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.

  1. ensures right contact columns present in db.
  2. 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

Popular posts from this blog

javascript - Karma not able to start PhantomJS on Windows - Error: spawn UNKNOWN -

Nuget pack csproj using nuspec -

c# - Display ASPX Popup control in RowDeleteing Event (ASPX Gridview) -