ViewComponent / view_component

A framework for building reusable, testable & encapsulated view components in Ruby on Rails.
https://viewcomponent.org
MIT License
3.31k stars 435 forks source link

DSL for initializer attribute validation #1479

Closed wooly closed 2 years ago

wooly commented 2 years ago

Feature request

DSL for describing the type/shape of attributes

Motivation

I'm building out a component library and have come across multiple cases where I have to pass an attribute that has a particular shape or can be selected from a particular set of values. I'm wondering if there's a simple DSL for articulating this that would raise some error or a warning if the attribute was bad.

I've come back to rails dev from working with react for a long time, and was inspired by the prop-types library which does something similar. Similar maybe but in a different context is apipie for describing API methods using a decorator pattern.

As an example, say I had an icon component that had a variant attribute that could be either light or regular:

class Icon < ViewComponent::Base
  def initialize(variant:)
    @variant = variant
  end
end

It would be useful if I could do something like the following:

class Icon < ViewComponent::Base
  attribute :variant, one_of(:light, :regular)

  def initialize(variant:)
    @variant = variant
  end
end

So this would raise an AttributeMismatchError: Icon.new(variant: :pyjamas)

Sorc96 commented 2 years ago

Check out dry-initializer and dry-types. There has been a short discussion about this in #639, where multiple people mentioned dry-initializer.

Your example could look like this:

module Types
  include Dry.Types
end

class ApplicationComponent < ViewComponent::Base
  include Dry::Initializer
end

class Icon < ApplicationComponent
  option :variant, Types::Symbol.enum(:light, :regular)
end

I'm honestly not a fan of the fact that every other Ruby library reinvents its own type system. I would much prefer having the option to just use one type library everywhere and dry-types works really well.

Of course, using ActiveModel::Attributes is also an option.

joelhawksley commented 2 years ago

@wooly thanks for sharing your thoughts. I agree that something like this would be useful for ViewComponent, but I don't think that it belongs in the project as a matter of responsibility. All of the proposals I've seen for something like this over the years generally fall into the category of not being coupled to ViewComponent at all! And I think that's for the better ❤️

wooly commented 2 years ago

Thanks both for the input, I probably should have been more diligent in looking at the issue history to see if anyone had asked the same question before.

I'll close this as answered!