Skip to content

Instantly share code, notes, and snippets.

@caesarsol
Created May 15, 2015 09:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caesarsol/c9bcf6267a210eae0c71 to your computer and use it in GitHub Desktop.
Save caesarsol/c9bcf6267a210eae0c71 to your computer and use it in GitHub Desktop.
ChoicesFields: model macro to define helper functions on AR model attributes that have pre-determined values
module ChoicesFields
extend ActiveSupport::Concern
module ClassMethods
# Class macro that defines useful function for choice fields (<option> tag fields).
#
# Will define:
# ::fieldname_choices_hash
# Returns the hash given to the `choices_field` macro function.
#
# ::fieldname_choices
# Returns the "machine" values, used primarily for AR validation with:
# validates :a_field, inclusion: { within: self.a_field_choices }
# WARNING: Use `choice_field` before `validates` in your AR model definition.
# TODO: Add a convenient custom validator that reads the *_choices automatically.
#
# ::fieldname_options
# Returns an array of arrays, ordered to use with `select_tag` or `f.select`.
#
# #fieldname_humanized
# Will read `#fieldname` and return the associated human-redable value.
#
# See engines/business/spec/models/concern_choices_fields_spec.rb for details.
#
def choices_field(fieldname, choices_hash)
fieldname = fieldname.to_sym
fieldname_choices_hash = "#{fieldname}_choices_hash".to_sym
fieldname_choices = "#{fieldname}_choices".to_sym
fieldname_options = "#{fieldname}_options".to_sym
fieldname_humanized = "#{fieldname}_humanized".to_sym
choices_hash = choices_hash.with_indifferent_access
choices_keys = choices_hash.keys
choices_select = choices_hash.to_a.map(&:reverse)
## Class Methods ##
# Hash defining choices: machine => human_readable
define_singleton_method fieldname_choices_hash do
choices_hash
end
# Choices for validation
define_singleton_method fieldname_choices do
choices_keys
end
# Options for select
define_singleton_method fieldname_options do
choices_select
end
## Instance Methods ##
# Humanized name of current field
define_method fieldname_humanized do
choices_hash[self.send(fieldname)]
end
end
end
end
RSpec.describe ChoicesFields do
before :example do
class C
attr_accessor :a_field
include Business::Concerns::ChoicesFields
choices_field :a_field, {
'hello' => "Hello",
'world' => "World",
}
end
@it = C.new
end
it "defines correctly Class method ::fieldname_choices_hash" do
expect(@it.class.a_field_choices_hash).to eq({
'hello' => "Hello",
'world' => "World",
})
end
it "defines correctly Class method ::fieldname_choices" do
expect(@it.class.a_field_choices).to eq(['hello', 'world'])
end
it "defines correctly Class method ::fieldname_options" do
expect(@it.class.a_field_options).to eq([
["Hello", 'hello'],
["World", 'world'],
])
end
it "defines correctly Instance method #fieldname_humanized" do
@it.a_field = 'hello'
expect(@it.a_field_humanized).to eq("Hello")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment