Rails/Ruby, mixing modules and remaining DRY -
i trying write more modular code rails apps, have started playing more including modules in classes. have basic understanding of function, finding hard keep them flexible while remaining dry.
here current example.
i have module called contactable. has 2 basic functions.
- ensures right contact columns present in db.
- validates fields.
here
module contactable extend activesupport::concern error = 'please ensure necessary fields in place' included required_database_fields.map { |rdf| raise "#{rdf} not included. #{error}" unless column_names.include?(rdf)} required_input_fields.map { |rif| validates rif.to_sym, presence: true} end end
i contactable comprised of 3 other modules (phoneable, emailable , addressable) contain arrays of columns require , fields validate against. 1 working on right 'addressable'
module addressable extend activesupport::concern error = 'please ensure necessary fields in place' required_database_fields = %w{address1 address2 address3 town county country postcode} required_input_fields = %w{address1 postcode} included required_database_fields.map { |rdf| raise "#{rdf} not included. #{error}" unless column_names.include?(rdf)} required_input_fields.map { |rif| validates rif.to_sym, presence: true} end end
obviously here there duplication. however, if include module contactable avoid need of repetition means contactable include phoneable , emailable also. in cases might not want validate or require these traits. there way can achieve flexibility?
you can this:
add /app/models/concerns/fields_validator.rb
module fieldsvalidator extend activesupport::concern class_methods def validate_required_attributes required_attributes.each |a| puts "adds validation #{a}" validates(a.to_sym, presence: true) end end def load_required_attributes(*_attrs) puts "loading attrs: #{_attrs.to_s}" @required_attributes ||=[] @required_attributes += _attrs @required_attributes.uniq! end def required_attributes @required_attributes end end end
add /app/models/concerns/contact.rb
module contact extend activesupport::concern include fieldsvalidator included puts "include contact..." load_required_attributes(:product_details, :observations, :offer_details) end end
add /app/models/concerns/address.rb
module address extend activesupport::concern include fieldsvalidator included puts "include address..." load_required_attributes(:sku, :amount, :observations) end end
in model...
class promotion < activerecord::base include address include contact validate_required_attributes end
the output:
include address... loading attrs: [:sku, :amount, :observations] include contact... loading attrs: [:product_details, :observations, :offer_details] adds validation sku adds validation amount adds validation observations adds validation product_details adds validation offer_details
to check working...
promotion.new.save! "activerecord::recordinvalid: validation failed: sku can't blank, amount can't blank, observations can't blank, product details can't blank, offer details can't blank"
considerations:
keep modules inside custom namespace. have problems existent
addressable
module. example:module myapp module addressable # code... end end class promotion < activerecord::base include myapp::addressable validate_required_attributes end
you need load attributes first , apply validations. if don`t that, repeat validations if modules share attributes.
- the shared logic goes in
fieldsvalidator
module
Comments
Post a Comment