justinfrench/formtastic — pull request #1098 by justinfrench (2014-11-08 11:58:53+00:00)
Title: [WIP] Support for `enum`

This is a really early work-in-progress for supporting ActiveRecord `enum`, as requested in #1059 by @grzesiek and @timoschilling.

ActiveRecord let's you defined an `enum` attribute like this:

```ruby
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ] # or enum status: { active: 0, archived: 1 }
end
```

You're expected to have a `status` column in your database to match. Under the hood, this will define a bunch of methods. The interesting ones for our implementation are:

```ruby
Conversation.statuses # => { "active" => 0, "archived" => 1 }
Conversation#status # => "active" or "archived"
Conversation#defined_enums # => { "status" => { "active" => 0, "archived" => 1 } }
```

The big pieces in supporting `enum` (so far) seem to be:

- [ ] automatically using `SelectInput` or similar on `enum` attributes (like we do for `belongs_to` associations)
- [x] automatically deriving the collection of options from the `enum` definition (like we do for `belongs_to` associations)
- [x] converting the `enum` options Hash into something we can use
- [ ] correctly marking the right options as checked or selected
- [ ] humanizing the option names ("active" => "Active", "on_hold" => "On hold")
- [ ] allowing for I18n translations of the option names
- [ ] documentation & testing

At this point, my biggest concern is around marking an option as selected or checked. I've managed to build an Array in a format that Formtastic expects (`[["active",0], ["archived",1]]`), but I don't think this is going be enough in practice, because `Conversation#status` would normally be compared with `0` and `1` (the value in the DB) for marking something as selected or checked, but `enum` modifies `Conversation#status` to return `"active"` or `"archived" ` instead.

So instead of an Array `[["active",0], ["archived",1]]`, I think we might need to build-up a collection of ActiveRecord-like objects that Formtastic also knows how to work with already. Something like this:

```ruby
class EnumOption
def initialize(name, database_value)
@name = name
@database_value = database_value
end

def id
@database_value
end

def to_label
@name.humanize # I18n goes here too
end

# Similar to `EnumOption.new("active", 0) == Conversation.status`
def==(comparision_string)
@name == string
end
end
```

We'd use it something like this:

```ruby
def collection_from_enum
# ...
enum_options_hash.map { |name, integer| EnumOption.new(name, integer) } # this is the interesting line
end
```

I'll need to look into the Rails and Formtastic internals a bit more to see if this will work out.

Everything else is pretty straightforward. I'm also playing around with a new style of spec that's more of a unit test, as the existing specs are more like integration tests, and they're starting to frustrate me.

Feel free to chime in if you have thoughts on the direction of this PR so far, or this spec format.