active_delegate 0.2.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Gemfile.lock +11 -10
- data/Rakefile +1 -1
- data/lib/active_delegate.rb +14 -6
- data/lib/active_delegate/association/methods.rb +41 -0
- data/lib/active_delegate/associations.rb +21 -62
- data/lib/active_delegate/attribute/accessor.rb +102 -0
- data/lib/active_delegate/attribute/localize.rb +33 -0
- data/lib/active_delegate/attribute/methods.rb +48 -0
- data/lib/active_delegate/attribute/object.rb +135 -0
- data/lib/active_delegate/attributes.rb +130 -229
- data/lib/active_delegate/delegator.rb +60 -0
- data/lib/active_delegate/version.rb +1 -1
- metadata +15 -12
- data/lib/active_delegate/dirty.rb +0 -19
- data/lib/active_delegate/localized.rb +0 -26
- data/lib/active_delegate/read_write.rb +0 -17
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 7ef4539c2b70f925e9e83e293bd7fec0a560586526efa6c6b486f36b1675fa65
         | 
| 4 | 
            +
              data.tar.gz: ff272b205a8757d99b95cc45642b0f7c10cbb4171e63b3dd9149ab82389b03bd
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 620de75b4495080c73c9059ff361d2550ad2b2c031c19f68745f53b375dc1df119e18c33ab5f22027ae08cd9bb0e7b919740e87f0955f084133e580ce64fc638
         | 
| 7 | 
            +
              data.tar.gz: 1c4e5f6b8f3cc93212b027d47e3098c1bd96a5c9b34bca9d4f00204892f8ab8a72e7dae65e407dc4d186860a3ba88448b2c814b5308068949418df5047ce57a0
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,28 +1,29 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                active_delegate (0.1. | 
| 4 | 
            +
                active_delegate (0.1.2)
         | 
| 5 5 | 
             
                  activerecord (~> 5.0)
         | 
| 6 6 | 
             
                  i18n (~> 0.8)
         | 
| 7 7 |  | 
| 8 8 | 
             
            GEM
         | 
| 9 9 | 
             
              remote: https://rubygems.org/
         | 
| 10 10 | 
             
              specs:
         | 
| 11 | 
            -
                activemodel (5.1. | 
| 12 | 
            -
                  activesupport (= 5.1. | 
| 13 | 
            -
                activerecord (5.1. | 
| 14 | 
            -
                  activemodel (= 5.1. | 
| 15 | 
            -
                  activesupport (= 5.1. | 
| 11 | 
            +
                activemodel (5.1.4)
         | 
| 12 | 
            +
                  activesupport (= 5.1.4)
         | 
| 13 | 
            +
                activerecord (5.1.4)
         | 
| 14 | 
            +
                  activemodel (= 5.1.4)
         | 
| 15 | 
            +
                  activesupport (= 5.1.4)
         | 
| 16 16 | 
             
                  arel (~> 8.0)
         | 
| 17 | 
            -
                activesupport (5.1. | 
| 17 | 
            +
                activesupport (5.1.4)
         | 
| 18 18 | 
             
                  concurrent-ruby (~> 1.0, >= 1.0.2)
         | 
| 19 19 | 
             
                  i18n (~> 0.7)
         | 
| 20 20 | 
             
                  minitest (~> 5.1)
         | 
| 21 21 | 
             
                  tzinfo (~> 1.1)
         | 
| 22 22 | 
             
                arel (8.0.0)
         | 
| 23 23 | 
             
                concurrent-ruby (1.0.5)
         | 
| 24 | 
            -
                i18n (0. | 
| 25 | 
            -
             | 
| 24 | 
            +
                i18n (0.9.0)
         | 
| 25 | 
            +
                  concurrent-ruby (~> 1.0)
         | 
| 26 | 
            +
                minitest (5.10.3)
         | 
| 26 27 | 
             
                rake (10.5.0)
         | 
| 27 28 | 
             
                thread_safe (0.3.6)
         | 
| 28 29 | 
             
                tzinfo (1.2.3)
         | 
| @@ -38,4 +39,4 @@ DEPENDENCIES | |
| 38 39 | 
             
              rake (~> 10.0)
         | 
| 39 40 |  | 
| 40 41 | 
             
            BUNDLED WITH
         | 
| 41 | 
            -
               1. | 
| 42 | 
            +
               1.15.4
         | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/lib/active_delegate.rb
    CHANGED
    
    | @@ -12,23 +12,31 @@ module ActiveDelegate | |
| 12 12 | 
             
                # Delegate associations
         | 
| 13 13 | 
             
                def delegate_associations(*args)
         | 
| 14 14 | 
             
                  options = args.extract_options!
         | 
| 15 | 
            -
                   | 
| 15 | 
            +
                  options = options.reverse_merge(only: args)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  Associations.new(self, options).call
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                # Delegate association
         | 
| 21 | 
            +
                def delegate_association(association, options = {})
         | 
| 22 | 
            +
                  options = options.merge(only: association)
         | 
| 23 | 
            +
                  Associations.new(self, options).call
         | 
| 16 24 | 
             
                end
         | 
| 17 25 |  | 
| 18 26 | 
             
                # Delegate attributes
         | 
| 19 27 | 
             
                def delegate_attributes(*args)
         | 
| 20 28 | 
             
                  options = args.extract_options!
         | 
| 21 | 
            -
                  options = options. | 
| 29 | 
            +
                  options = options.reverse_merge(only: args).except(:cast_type, :alias)
         | 
| 22 30 |  | 
| 23 | 
            -
                  Attributes.new(self, options)
         | 
| 31 | 
            +
                  Attributes.new(self, options).call
         | 
| 24 32 | 
             
                end
         | 
| 25 33 |  | 
| 26 34 | 
             
                # Delegate attribute
         | 
| 27 | 
            -
                def delegate_attribute(attribute, cast_type, options={})
         | 
| 35 | 
            +
                def delegate_attribute(attribute, cast_type, options = {})
         | 
| 28 36 | 
             
                  options = options.except(:only, :except)
         | 
| 29 | 
            -
                  options = options.merge(only:  | 
| 37 | 
            +
                  options = options.merge(only: attribute, cast_type: cast_type)
         | 
| 30 38 |  | 
| 31 | 
            -
                  Attributes.new(self, options)
         | 
| 39 | 
            +
                  Attributes.new(self, options).call
         | 
| 32 40 | 
             
                end
         | 
| 33 41 | 
             
              end
         | 
| 34 42 | 
             
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            module ActiveDelegate
         | 
| 2 | 
            +
              module Association
         | 
| 3 | 
            +
                class Methods
         | 
| 4 | 
            +
                  attr_reader :association_name, :association_class
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  # Initialize association methods
         | 
| 7 | 
            +
                  def initialize(association_name, association_class)
         | 
| 8 | 
            +
                    @association_name  = association_name
         | 
| 9 | 
            +
                    @association_class = association_class
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Get delegatable methods
         | 
| 13 | 
            +
                  def delegatable
         | 
| 14 | 
            +
                    delegatable = suffixed + prefixed
         | 
| 15 | 
            +
                    delegatable & association_class.instance_methods
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  private
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Get method prefixes
         | 
| 21 | 
            +
                  def prefixes
         | 
| 22 | 
            +
                    ['build_']
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  # Get method suffixes
         | 
| 26 | 
            +
                  def suffixes
         | 
| 27 | 
            +
                    ['', '=', '_attributes', '_attributes=']
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Get prefixed methods
         | 
| 31 | 
            +
                  def prefixed
         | 
| 32 | 
            +
                    prefixes.map { |s| :"#{s}#{attribute}" }
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  # Get suffixed methods
         | 
| 36 | 
            +
                  def suffixed
         | 
| 37 | 
            +
                    suffixes.map { |s| :"#{attribute}#{s}" }
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -1,70 +1,29 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
                # Initialize assoctiations
         | 
| 4 | 
            -
                def initialize(model, options)
         | 
| 5 | 
            -
                  @model   = model
         | 
| 6 | 
            -
                  @options = default_options.merge(options)
         | 
| 1 | 
            +
            require 'active_delegate/delegator'
         | 
| 2 | 
            +
            require 'active_delegate/association/methods'
         | 
| 7 3 |  | 
| 8 | 
            -
             | 
| 4 | 
            +
            module ActiveDelegate
         | 
| 5 | 
            +
              class Associations < Delegator
         | 
| 6 | 
            +
                # Get all associations
         | 
| 7 | 
            +
                def association_reflections
         | 
| 8 | 
            +
                  association_class.reflect_on_all_associations
         | 
| 9 9 | 
             
                end
         | 
| 10 10 |  | 
| 11 | 
            -
                 | 
| 12 | 
            -
             | 
| 13 | 
            -
                   | 
| 14 | 
            -
             | 
| 15 | 
            -
                    { except: [], only: [], allow_nil: false, to: [] }
         | 
| 16 | 
            -
                  end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                  # Get association reflection
         | 
| 19 | 
            -
                  def association_reflection
         | 
| 20 | 
            -
                    assoc_name = @options.fetch(:to)
         | 
| 21 | 
            -
                    reflection = @model.reflect_on_association(assoc_name)
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                    return reflection unless reflection.nil?
         | 
| 24 | 
            -
                    raise "#{@model.name} don't have the association #{assoc_name}"
         | 
| 25 | 
            -
                  end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                  # Get model association class
         | 
| 28 | 
            -
                  def association_class
         | 
| 29 | 
            -
                    association_reflection.klass
         | 
| 30 | 
            -
                  end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  # Get all associations
         | 
| 33 | 
            -
                  def association_reflections
         | 
| 34 | 
            -
                    association_class.reflect_on_all_associations
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                  # Get singular model association names
         | 
| 38 | 
            -
                  def association_names
         | 
| 39 | 
            -
                    association_reflections.map(&:name)
         | 
| 40 | 
            -
                  end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                  # Get delegatable associations
         | 
| 43 | 
            -
                  def delegatable_associations
         | 
| 44 | 
            -
                    associations = association_names.map(&:to_sym)
         | 
| 45 | 
            -
                    associations = associations & @options[:only].to_a   if @options[:only].present?
         | 
| 46 | 
            -
                    associations = associations - @options[:except].to_a if @options[:except].present?
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                    associations.map(&:to_sym)
         | 
| 49 | 
            -
                  end
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                  # Check if association is collection
         | 
| 52 | 
            -
                  def collection_association?(association)
         | 
| 53 | 
            -
                    collections = association_reflections.select(&:collection?).map(&:name)
         | 
| 54 | 
            -
                    association.in? collections
         | 
| 55 | 
            -
                  end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                  # Delegate associations
         | 
| 58 | 
            -
                  def delegate_associations
         | 
| 59 | 
            -
                    options = { to: @options[:to], allow_nil: @options[:allow_nil] }
         | 
| 11 | 
            +
                # Get singular model association names
         | 
| 12 | 
            +
                def association_names
         | 
| 13 | 
            +
                  association_reflections.map(&:name)
         | 
| 14 | 
            +
                end
         | 
| 60 15 |  | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 16 | 
            +
                # Get delegatable associations
         | 
| 17 | 
            +
                def delegatable_associations
         | 
| 18 | 
            +
                  delegation_args(association_names)
         | 
| 19 | 
            +
                end
         | 
| 64 20 |  | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 21 | 
            +
                # Delegate associations
         | 
| 22 | 
            +
                def call
         | 
| 23 | 
            +
                  delegatable_associations.each do |association|
         | 
| 24 | 
            +
                    methods = Association::Methods.new(association, association_class)
         | 
| 25 | 
            +
                    model.delegate(*methods.delegatable, delegation_options)
         | 
| 68 26 | 
             
                  end
         | 
| 27 | 
            +
                end
         | 
| 69 28 | 
             
              end
         | 
| 70 29 | 
             
            end
         | 
| @@ -0,0 +1,102 @@ | |
| 1 | 
            +
            module ActiveDelegate
         | 
| 2 | 
            +
              module Attribute
         | 
| 3 | 
            +
                class Accessor
         | 
| 4 | 
            +
                  attr_reader :record, :options
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  # Initialize attribute
         | 
| 7 | 
            +
                  def initialize(record, options = {})
         | 
| 8 | 
            +
                    @record  = record
         | 
| 9 | 
            +
                    @options = options
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Get default value
         | 
| 13 | 
            +
                  def default_value
         | 
| 14 | 
            +
                    options[:default]
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  # Get association name
         | 
| 18 | 
            +
                  def association_name
         | 
| 19 | 
            +
                    options[:association].to_sym
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  # Get association attribute name
         | 
| 23 | 
            +
                  def attribute_name
         | 
| 24 | 
            +
                    options[:attribute].to_sym
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # Get record attribute type
         | 
| 28 | 
            +
                  def read_type
         | 
| 29 | 
            +
                    options[:read_type]
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  # Get association attribute type
         | 
| 33 | 
            +
                  def write_type
         | 
| 34 | 
            +
                    options[:write_type]
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  # Check if should cast value
         | 
| 38 | 
            +
                  def type_cast?
         | 
| 39 | 
            +
                    read_type != write_type
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  # Get associated value
         | 
| 43 | 
            +
                  def association_record
         | 
| 44 | 
            +
                    record.send(association_name)
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  # Get association attribute value
         | 
| 48 | 
            +
                  def attribute_value(*args)
         | 
| 49 | 
            +
                    association_record.try(attribute_name, *args)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  # Get record attribute type caster
         | 
| 53 | 
            +
                  def read_type_caster
         | 
| 54 | 
            +
                    lookup_type_caster(read_type)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  # Get association attribute type caster
         | 
| 58 | 
            +
                  def write_type_caster
         | 
| 59 | 
            +
                    lookup_type_caster(write_type)
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  # Cast value for reading
         | 
| 63 | 
            +
                  def cast_read_value(value)
         | 
| 64 | 
            +
                    read_type_caster.cast(value)
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  # Cast value for writing
         | 
| 68 | 
            +
                  def cast_write_value(value)
         | 
| 69 | 
            +
                    write_type_caster.cast(value)
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  # Prepare association attribute value for writing
         | 
| 73 | 
            +
                  def normalize_value(value)
         | 
| 74 | 
            +
                    value = cast_read_value(value)
         | 
| 75 | 
            +
                    cast_write_value(value)
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  # Read and cast value
         | 
| 79 | 
            +
                  def read(*args)
         | 
| 80 | 
            +
                    value = attribute_value(*args) || default_value
         | 
| 81 | 
            +
                    type_cast? ? cast_read_value(value) : value
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  # Cast and write value
         | 
| 85 | 
            +
                  def write(value)
         | 
| 86 | 
            +
                    value = normalize_value(value) if type_cast?
         | 
| 87 | 
            +
                    association_record.send(:"#{attribute_name}=", value)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  private
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  # Get type caster class
         | 
| 93 | 
            +
                  def lookup_type_caster(type_caster)
         | 
| 94 | 
            +
                    if type_caster.is_a?(Symbol)
         | 
| 95 | 
            +
                      ActiveRecord::Type.lookup(type_caster)
         | 
| 96 | 
            +
                    else
         | 
| 97 | 
            +
                      type_caster
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module ActiveDelegate
         | 
| 2 | 
            +
              module Attribute
         | 
| 3 | 
            +
                class Localize
         | 
| 4 | 
            +
                  attr_reader :attribute_name, :association_instance
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  # Initialize attribute methods
         | 
| 7 | 
            +
                  def initialize(attribute_name, association_class)
         | 
| 8 | 
            +
                    @attribute_name       = attribute_name
         | 
| 9 | 
            +
                    @association_instance = association_class.new
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Get localized attributes
         | 
| 13 | 
            +
                  def attributes
         | 
| 14 | 
            +
                    localized = suffixes.map { |s| :"#{attribute_name}#{s}" }
         | 
| 15 | 
            +
                    localized & association_instance.methods
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  private
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Get localized method suffixes
         | 
| 21 | 
            +
                  def suffixes
         | 
| 22 | 
            +
                    @suffixes ||= I18n.available_locales.map do |locale|
         | 
| 23 | 
            +
                      "_#{normalize_locale(locale)}"
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # Normalize locale
         | 
| 28 | 
            +
                  def normalize_locale(locale)
         | 
| 29 | 
            +
                    locale.to_s.downcase.sub('-', '_').to_s
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            module ActiveDelegate
         | 
| 2 | 
            +
              module Attribute
         | 
| 3 | 
            +
                class Methods
         | 
| 4 | 
            +
                  attr_reader :attribute_name, :association_instance
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  # Initialize attribute methods
         | 
| 7 | 
            +
                  def initialize(attribute_name, association_class)
         | 
| 8 | 
            +
                    @attribute_name       = attribute_name
         | 
| 9 | 
            +
                    @association_instance = association_class.new
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Get accessor methods
         | 
| 13 | 
            +
                  def accessors
         | 
| 14 | 
            +
                    suffix_attribute(accessor_suffixes)
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  # Get dirty methods for attributes
         | 
| 18 | 
            +
                  def dirty
         | 
| 19 | 
            +
                    suffix_attribute(dirty_suffixes)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  # Get delegatable methods
         | 
| 23 | 
            +
                  def delegatable
         | 
| 24 | 
            +
                    accessors + dirty
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  private
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  # Get accessor method suffixes
         | 
| 30 | 
            +
                  def accessor_suffixes
         | 
| 31 | 
            +
                    ['', '=', '?']
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  # Get dirty method suffixes
         | 
| 35 | 
            +
                  def dirty_suffixes
         | 
| 36 | 
            +
                    @dirty_suffixes ||= Class.new do
         | 
| 37 | 
            +
                      include ::ActiveModel::Dirty
         | 
| 38 | 
            +
                    end.attribute_method_matchers.map(&:suffix).select { |m| m =~ /\A_/ }
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  # Generate suffixed array of symbols
         | 
| 42 | 
            +
                  def suffix_attribute(suffixes)
         | 
| 43 | 
            +
                    delegatable = suffixes.map { |s| :"#{attribute_name}#{s}" }
         | 
| 44 | 
            +
                    delegatable & association_instance.methods
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| @@ -0,0 +1,135 @@ | |
| 1 | 
            +
            require 'active_delegate/attribute/methods'
         | 
| 2 | 
            +
            require 'active_delegate/attribute/localize'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module ActiveDelegate
         | 
| 5 | 
            +
              module Attribute
         | 
| 6 | 
            +
                class Object
         | 
| 7 | 
            +
                  attr_reader :attribute_name, :association_class, :options
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  # Initialize attribute methods
         | 
| 10 | 
            +
                  def initialize(attribute_name, association_class, options = {})
         | 
| 11 | 
            +
                    @attribute_name    = attribute_name
         | 
| 12 | 
            +
                    @association_class = association_class
         | 
| 13 | 
            +
                    @options           = options
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # Check if should define attribute
         | 
| 17 | 
            +
                  def define?
         | 
| 18 | 
            +
                    options[:define] || in_option?(:define)
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  # Check if should localize attribute
         | 
| 22 | 
            +
                  def localize?
         | 
| 23 | 
            +
                    options[:localized] || in_option?(:localized)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  # Check if should define attribute finders
         | 
| 27 | 
            +
                  def finder?
         | 
| 28 | 
            +
                    options[:finder] || in_option?(:finder)
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  # Check if should define attribute scopes
         | 
| 32 | 
            +
                  def scope?
         | 
| 33 | 
            +
                    options[:scope] || in_option?(:scope)
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  # Get attribute prefix
         | 
| 37 | 
            +
                  def prefix
         | 
| 38 | 
            +
                    options[:prefix]
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  # Get attribute default
         | 
| 42 | 
            +
                  def default
         | 
| 43 | 
            +
                    options.fetch :default, association_class.column_defaults[unprefixed.to_s]
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  # Get read type or fallback to write type
         | 
| 47 | 
            +
                  def read_type
         | 
| 48 | 
            +
                    options.fetch :cast_type, write_type
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  # Get write type from associated model
         | 
| 52 | 
            +
                  def write_type
         | 
| 53 | 
            +
                    association_class.type_for_attribute(unprefixed.to_s)
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  # Get unprefixed attribute
         | 
| 57 | 
            +
                  def unprefixed
         | 
| 58 | 
            +
                    remove_prefix(attribute_name)
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  # Get prefixed attribute
         | 
| 62 | 
            +
                  def prefixed
         | 
| 63 | 
            +
                    add_prefix(attribute_name)
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  # Check if attribute is prefixed
         | 
| 67 | 
            +
                  def prefixed?
         | 
| 68 | 
            +
                    unprefixed != prefixed
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  # Get aliased attribute
         | 
| 72 | 
            +
                  def aliased
         | 
| 73 | 
            +
                    options[:alias] || prefixed
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  # Check if attribute is aliased
         | 
| 77 | 
            +
                  def aliased?
         | 
| 78 | 
            +
                    prefixed != aliased
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  # Get method aliases
         | 
| 82 | 
            +
                  def aliases
         | 
| 83 | 
            +
                    return {} unless aliased?
         | 
| 84 | 
            +
                    Hash[delegatable_methods.map { |m| generate_alias(m) }]
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  # Get localized attributes
         | 
| 88 | 
            +
                  def localized
         | 
| 89 | 
            +
                    @localized ||= Localize.new(unprefixed, association_class).attributes
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  # Check if attributes has localized methods
         | 
| 93 | 
            +
                  def localized?
         | 
| 94 | 
            +
                    localized.any?
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  # Get delegatable attributes
         | 
| 98 | 
            +
                  def delegatable_attributes
         | 
| 99 | 
            +
                    @delegatable_attributes ||= [unprefixed, *localized].map { |a| add_prefix(a) }
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  # Get delegatable attribute methods
         | 
| 103 | 
            +
                  def delegatable_methods
         | 
| 104 | 
            +
                    @delegatable_methods ||= [unprefixed, *localized].flat_map do |method_name|
         | 
| 105 | 
            +
                      Methods.new(method_name, association_class).delegatable
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  private
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  # Prefix attribute
         | 
| 112 | 
            +
                  def add_prefix(attr_name)
         | 
| 113 | 
            +
                    prefix.present? ? :"#{prefix}_#{attr_name}" : attr_name
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  # Unprefix attribute
         | 
| 117 | 
            +
                  def remove_prefix(attr_name)
         | 
| 118 | 
            +
                    attr_name.to_s.sub("#{prefix}_", '').to_sym
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  # Generate alias method
         | 
| 122 | 
            +
                  def generate_alias(method_name)
         | 
| 123 | 
            +
                    old_name = method_name.to_s.sub(unprefixed.to_s, prefixed.to_s)
         | 
| 124 | 
            +
                    new_name = method_name.to_s.sub(unprefixed.to_s, aliased.to_s)
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                    [new_name.to_sym, old_name.to_sym]
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  # Check if attribute is in option
         | 
| 130 | 
            +
                  def in_option?(key)
         | 
| 131 | 
            +
                    attribute_name.in? Array(options[key])
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
            end
         | 
| @@ -1,274 +1,175 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
              autoload :Localized, 'active_delegate/localized'
         | 
| 5 | 
            -
             | 
| 6 | 
            -
              class Attributes
         | 
| 7 | 
            -
                # Initialize attributes
         | 
| 8 | 
            -
                def initialize(model, options)
         | 
| 9 | 
            -
                  @model   = model
         | 
| 10 | 
            -
                  @options = default_options.merge(options)
         | 
| 1 | 
            +
            require 'active_delegate/delegator'
         | 
| 2 | 
            +
            require 'active_delegate/attribute/object'
         | 
| 3 | 
            +
            require 'active_delegate/attribute/accessor'
         | 
| 11 4 |  | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 5 | 
            +
            module ActiveDelegate
         | 
| 6 | 
            +
              class Attributes < Delegator
         | 
| 7 | 
            +
                # Get default options
         | 
| 8 | 
            +
                def default_options
         | 
| 9 | 
            +
                  {
         | 
| 10 | 
            +
                    except:    [],
         | 
| 11 | 
            +
                    only:      [],
         | 
| 12 | 
            +
                    localized: false,
         | 
| 13 | 
            +
                    define:    true,
         | 
| 14 | 
            +
                    finder:    false,
         | 
| 15 | 
            +
                    scope:     false,
         | 
| 16 | 
            +
                    to:        nil,
         | 
| 17 | 
            +
                    prefix:    nil,
         | 
| 18 | 
            +
                    allow_nil: false
         | 
| 19 | 
            +
                  }
         | 
| 15 20 | 
             
                end
         | 
| 16 21 |  | 
| 17 | 
            -
                 | 
| 18 | 
            -
             | 
| 19 | 
            -
                   | 
| 20 | 
            -
                   | 
| 21 | 
            -
             | 
| 22 | 
            -
                      except: [], only: [], allow_nil: false, to: [],
         | 
| 23 | 
            -
                      prefix: nil, localized: false, finder: false,
         | 
| 24 | 
            -
                      scope: false, cast: false
         | 
| 25 | 
            -
                    }
         | 
| 26 | 
            -
                  end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                  # Get association reflection
         | 
| 29 | 
            -
                  def association_reflection
         | 
| 30 | 
            -
                    assoc_name = @options.fetch(:to)
         | 
| 31 | 
            -
                    reflection = @model.reflect_on_association(assoc_name)
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                    return reflection unless reflection.nil?
         | 
| 34 | 
            -
                    raise "#{@model.name} don't have the association #{assoc_name}"
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                  # Get model association class
         | 
| 38 | 
            -
                  def association_class
         | 
| 39 | 
            -
                    association_reflection.klass
         | 
| 40 | 
            -
                  end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                  # Get association attribute names
         | 
| 43 | 
            -
                  def association_attribute_names
         | 
| 44 | 
            -
                    association_class.attribute_names
         | 
| 45 | 
            -
                  end
         | 
| 46 | 
            -
             | 
| 47 | 
            -
                  # Default excluded attributes
         | 
| 48 | 
            -
                  def default_excluded_attributes
         | 
| 49 | 
            -
                    assoc_as  = association_reflection.options[:as]
         | 
| 50 | 
            -
                    poly_attr = [:"#{assoc_as}_type", :"#{assoc_as}_id"] if assoc_as.present?
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                    [:id, :created_at, :updated_at] + poly_attr.to_a
         | 
| 53 | 
            -
                  end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                  # Get delegatable attributes
         | 
| 56 | 
            -
                  def delegatable_attributes
         | 
| 57 | 
            -
                    attributes = association_attribute_names.map(&:to_sym)
         | 
| 58 | 
            -
                    attributes = attributes & @options[:only].to_a   if @options[:only].present?
         | 
| 59 | 
            -
                    attributes = attributes - @options[:except].to_a if @options[:except].present?
         | 
| 60 | 
            -
                    attributes = attributes - default_excluded_attributes
         | 
| 61 | 
            -
             | 
| 62 | 
            -
                    attributes.map(&:to_sym)
         | 
| 63 | 
            -
                  end
         | 
| 22 | 
            +
                # Get attribute options
         | 
| 23 | 
            +
                def attribute_options
         | 
| 24 | 
            +
                  keys = %i[cast_type default define alias localized finder scope]
         | 
| 25 | 
            +
                  options.select { |k, _v| k.in? keys }.merge(prefix: delegation_prefix)
         | 
| 26 | 
            +
                end
         | 
| 64 27 |  | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 28 | 
            +
                # Get association table
         | 
| 29 | 
            +
                def association_table
         | 
| 30 | 
            +
                  association_class.table_name
         | 
| 31 | 
            +
                end
         | 
| 69 32 |  | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 33 | 
            +
                # Default excluded attributes
         | 
| 34 | 
            +
                def excluded_attributes
         | 
| 35 | 
            +
                  excluded  = %i[id created_at updated_at]
         | 
| 72 36 |  | 
| 73 | 
            -
                   | 
| 74 | 
            -
                   | 
| 75 | 
            -
                    attributes = delegatable_attributes + localized_attributes
         | 
| 76 | 
            -
                    readwrite  = ReadWrite.readwrite_methods(attributes)
         | 
| 77 | 
            -
                    dirty      = Dirty.dirty_methods(attributes)
         | 
| 78 | 
            -
                    methods    = readwrite + dirty
         | 
| 37 | 
            +
                  sti_col   = association_class.inheritance_column
         | 
| 38 | 
            +
                  excluded << sti_col.to_sym if sti_col.present?
         | 
| 79 39 |  | 
| 80 | 
            -
             | 
| 81 | 
            -
                   | 
| 40 | 
            +
                  assoc_as  = association_reflection.options[:as]
         | 
| 41 | 
            +
                  excluded += [:"#{assoc_as}_type", :"#{assoc_as}_id"] if assoc_as.present?
         | 
| 82 42 |  | 
| 83 | 
            -
                   | 
| 84 | 
            -
             | 
| 85 | 
            -
                    options = { to: @options[:to], allow_nil: @options[:allow_nil], prefix: @options[:prefix] }
         | 
| 86 | 
            -
                    @model.delegate(*delegatable_methods, options)
         | 
| 87 | 
            -
                  end
         | 
| 43 | 
            +
                  excluded
         | 
| 44 | 
            +
                end
         | 
| 88 45 |  | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 46 | 
            +
                # Get delegatable attributes
         | 
| 47 | 
            +
                def delegatable_attributes
         | 
| 48 | 
            +
                  attributes  = delegation_args(association_class.attribute_names)
         | 
| 49 | 
            +
                  attributes -= excluded_attributes
         | 
| 92 50 |  | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
                        end
         | 
| 98 | 
            -
                      EOM
         | 
| 99 | 
            -
                    end
         | 
| 51 | 
            +
                  attributes.map! do |attribute_name|
         | 
| 52 | 
            +
                    ActiveDelegate::Attribute::Object.new(
         | 
| 53 | 
            +
                      attribute_name, association_class, attribute_options
         | 
| 54 | 
            +
                    )
         | 
| 100 55 | 
             
                  end
         | 
| 101 56 |  | 
| 102 | 
            -
                   | 
| 103 | 
            -
             | 
| 104 | 
            -
                    prefix = @options[:prefix]
         | 
| 105 | 
            -
                    prefix.is_a?(TrueClass) ? @options[:to] : prefix
         | 
| 106 | 
            -
                  end
         | 
| 57 | 
            +
                  attributes.reject { |a| model.has_attribute?(a.prefixed) }
         | 
| 58 | 
            +
                end
         | 
| 107 59 |  | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
                  end
         | 
| 60 | 
            +
                # Delegate attributes
         | 
| 61 | 
            +
                def call
         | 
| 62 | 
            +
                  redefine_build_association(association_name)
         | 
| 112 63 |  | 
| 113 | 
            -
                   | 
| 114 | 
            -
             | 
| 115 | 
            -
                     | 
| 116 | 
            -
                      attributes.map { |a| :"#{attribute_prefix}_#{a}" }
         | 
| 117 | 
            -
                    else
         | 
| 118 | 
            -
                      attributes
         | 
| 119 | 
            -
                    end
         | 
| 120 | 
            -
                  end
         | 
| 64 | 
            +
                  delegatable_attributes.each do |attribute|
         | 
| 65 | 
            +
                    delegate_methods(attribute.delegatable_methods)
         | 
| 66 | 
            +
                    define_model_class_methods(attribute)
         | 
| 121 67 |  | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
                    @options.fetch :default, association_class.column_defaults["#{attribute}"]
         | 
| 68 | 
            +
                    define_attribute_methods(attribute)
         | 
| 69 | 
            +
                    define_attribute_queries(attribute)
         | 
| 125 70 | 
             
                  end
         | 
| 71 | 
            +
                end
         | 
| 126 72 |  | 
| 127 | 
            -
             | 
| 128 | 
            -
                  def attribute_cast_type(attribute)
         | 
| 129 | 
            -
                    @options.fetch :cast_type, association_class.type_for_attribute("#{attribute}")
         | 
| 130 | 
            -
                  end
         | 
| 73 | 
            +
                private
         | 
| 131 74 |  | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 75 | 
            +
                # Redefine build association method
         | 
| 76 | 
            +
                def redefine_build_association(assoc_name)
         | 
| 77 | 
            +
                  model.class_eval do
         | 
| 78 | 
            +
                    class_eval <<-EOM, __FILE__, __LINE__ + 1
         | 
| 79 | 
            +
                      def #{assoc_name}
         | 
| 80 | 
            +
                        super || send(:build_#{assoc_name})
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
                    EOM
         | 
| 135 83 | 
             
                  end
         | 
| 84 | 
            +
                end
         | 
| 136 85 |  | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 86 | 
            +
                # Redefine attribute accessor methods
         | 
| 87 | 
            +
                def redefine_attribute_accessors(method_name, attribute)
         | 
| 88 | 
            +
                  attr_options = {
         | 
| 89 | 
            +
                    association: association_name,
         | 
| 90 | 
            +
                    attribute:   attribute.unprefixed,
         | 
| 91 | 
            +
                    read_type:   attribute.read_type,
         | 
| 92 | 
            +
                    write_type:  attribute.write_type,
         | 
| 93 | 
            +
                    default:     attribute.default
         | 
| 94 | 
            +
                  }
         | 
| 141 95 |  | 
| 142 | 
            -
                   | 
| 143 | 
            -
             | 
| 144 | 
            -
                    @options[:finder] || Array(@options[:finder]).include?(attribute)
         | 
| 96 | 
            +
                  model.send(:redefine_method, method_name) do |*args|
         | 
| 97 | 
            +
                    ActiveDelegate::Attribute::Accessor.new(self, attr_options).read(*args)
         | 
| 145 98 | 
             
                  end
         | 
| 146 99 |  | 
| 147 | 
            -
                  #  | 
| 148 | 
            -
             | 
| 149 | 
            -
                    @options[:scope] || Array(@options[:scope]).include?(attribute)
         | 
| 100 | 
            +
                  model.send(:redefine_method, :"#{method_name}=") do |value|
         | 
| 101 | 
            +
                    ActiveDelegate::Attribute::Accessor.new(self, attr_options).write(value)
         | 
| 150 102 | 
             
                  end
         | 
| 103 | 
            +
                end
         | 
| 151 104 |  | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 105 | 
            +
                # Delegate attribute methods
         | 
| 106 | 
            +
                def delegate_methods(methods)
         | 
| 107 | 
            +
                  model.delegate(*methods, delegation_options)
         | 
| 108 | 
            +
                end
         | 
| 156 109 |  | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 110 | 
            +
                # Define model method keeping old values
         | 
| 111 | 
            +
                def define_model_method(method, *attributes)
         | 
| 112 | 
            +
                  attributes = model.try(method).to_a.concat(attributes).uniq
         | 
| 113 | 
            +
                  model.send(:define_singleton_method, method) { attributes }
         | 
| 114 | 
            +
                end
         | 
| 159 115 |  | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 116 | 
            +
                # Store attribute names in model class methods
         | 
| 117 | 
            +
                def define_model_class_methods(attribute)
         | 
| 118 | 
            +
                  method_name = :"#{association_table}_attribute_names"
         | 
| 119 | 
            +
                  define_model_method(method_name, attribute.prefixed)
         | 
| 162 120 |  | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 121 | 
            +
                  method_name = :"#{association_table}_localized_attribute_names"
         | 
| 122 | 
            +
                  define_model_method(method_name, attribute.prefixed) if attribute.localized?
         | 
| 123 | 
            +
                end
         | 
| 166 124 |  | 
| 167 | 
            -
             | 
| 168 | 
            -
             | 
| 125 | 
            +
                # Define delegated attribute methods
         | 
| 126 | 
            +
                def define_attribute_methods(attribute)
         | 
| 127 | 
            +
                  if attribute.define?
         | 
| 128 | 
            +
                    model.attribute(attribute.aliased, attribute.read_type)
         | 
| 169 129 | 
             
                  end
         | 
| 170 130 |  | 
| 171 | 
            -
                   | 
| 172 | 
            -
             | 
| 173 | 
            -
                    existing  = @model.attribute_names.map(&:to_sym)
         | 
| 174 | 
            -
                    undefined = attributes.reject { |a| a.in? existing }
         | 
| 175 | 
            -
             | 
| 176 | 
            -
                    undefined.each do |attrib|
         | 
| 177 | 
            -
                      attr_name = unprefix_attribute(attrib)
         | 
| 178 | 
            -
             | 
| 179 | 
            -
                      define_attribute_default_value(attrib, attr_name)
         | 
| 180 | 
            -
                      define_attribute_type_cast(attrib, attr_name)
         | 
| 181 | 
            -
                      define_attribute_and_alias(attrib, attr_name)
         | 
| 182 | 
            -
                      define_attribute_finders_and_scopes(attrib, attr_name)
         | 
| 183 | 
            -
                    end
         | 
| 131 | 
            +
                  attribute.delegatable_attributes.each do |method_name|
         | 
| 132 | 
            +
                    redefine_attribute_accessors(method_name, attribute)
         | 
| 184 133 | 
             
                  end
         | 
| 185 134 |  | 
| 186 | 
            -
                   | 
| 187 | 
            -
             | 
| 188 | 
            -
                    attr_default = attribute_default(attr_name)
         | 
| 189 | 
            -
             | 
| 190 | 
            -
                    unless attr_default.nil?
         | 
| 191 | 
            -
                      attr_assoc = @options[:to]
         | 
| 192 | 
            -
                      attr_cattr = :"_attribute_#{attrib}_default"
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                      @model.send(:define_singleton_method, attr_cattr) { attr_default }
         | 
| 195 | 
            -
             | 
| 196 | 
            -
                      @model.class_eval do
         | 
| 197 | 
            -
                        class_eval <<-EOM, __FILE__, __LINE__ + 1
         | 
| 198 | 
            -
                          def #{attrib}
         | 
| 199 | 
            -
                            send(:#{attr_assoc}).try(:#{attr_name}) || self.class.send(:#{attr_cattr})
         | 
| 200 | 
            -
                          end
         | 
| 201 | 
            -
                        EOM
         | 
| 202 | 
            -
                      end
         | 
| 203 | 
            -
                    end
         | 
| 135 | 
            +
                  attribute.aliases.each do |alias_name, method_name|
         | 
| 136 | 
            +
                    model.alias_method(alias_name, method_name)
         | 
| 204 137 | 
             
                  end
         | 
| 138 | 
            +
                end
         | 
| 205 139 |  | 
| 206 | 
            -
             | 
| 207 | 
            -
             | 
| 208 | 
            -
             | 
| 209 | 
            -
                     | 
| 210 | 
            -
             | 
| 211 | 
            -
                    if needs_type_cast?(attr_name)
         | 
| 212 | 
            -
                      @model.class_eval do
         | 
| 213 | 
            -
                        class_eval <<-EOM, __FILE__, __LINE__ + 1
         | 
| 214 | 
            -
                          def #{attrib}
         | 
| 215 | 
            -
                            assoc_value = send(:#{attr_assoc}).try(:#{attr_name})
         | 
| 216 | 
            -
                            self.class.type_for_attribute('#{attrib}').cast(assoc_value) ||
         | 
| 217 | 
            -
                            self.class.try(:#{attr_cattr})
         | 
| 218 | 
            -
                          end
         | 
| 219 | 
            -
             | 
| 220 | 
            -
                          def #{attrib}=(value)
         | 
| 221 | 
            -
                            assoc_value = self.class.type_for_attribute('#{attrib}').cast(value)
         | 
| 222 | 
            -
                            send(:#{attr_assoc}).send(:#{attr_name}=, assoc_value)
         | 
| 223 | 
            -
                          end
         | 
| 224 | 
            -
                        EOM
         | 
| 225 | 
            -
                      end
         | 
| 226 | 
            -
                    end
         | 
| 140 | 
            +
                # Define attribute finder methods
         | 
| 141 | 
            +
                def define_attribute_finders(attr_method:, attr_column:, assoc_name:, table_name:)
         | 
| 142 | 
            +
                  model.send(:define_singleton_method, :"find_by_#{attr_method}") do |value|
         | 
| 143 | 
            +
                    joins(assoc_name).find_by(table_name => { attr_column => value })
         | 
| 227 144 | 
             
                  end
         | 
| 228 145 |  | 
| 229 | 
            -
                  #  | 
| 230 | 
            -
             | 
| 231 | 
            -
                    cast_type  = attribute_cast_type(attr_name)
         | 
| 232 | 
            -
                    attr_alias = @options[:alias]
         | 
| 233 | 
            -
             | 
| 234 | 
            -
                    @model.attribute(attrib, cast_type)
         | 
| 235 | 
            -
             | 
| 236 | 
            -
                    unless attr_alias.nil?
         | 
| 237 | 
            -
                      @model.attribute(attr_alias, cast_type)
         | 
| 238 | 
            -
                      @model.alias_attribute(attr_alias, attrib)
         | 
| 239 | 
            -
                    end
         | 
| 146 | 
            +
                  model.send(:define_singleton_method, :"find_by_#{attr_method}!") do |value|
         | 
| 147 | 
            +
                    joins(assoc_name).find_by!(table_name => { attr_column => value })
         | 
| 240 148 | 
             
                  end
         | 
| 149 | 
            +
                end
         | 
| 241 150 |  | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 244 | 
            -
             | 
| 245 | 
            -
                     | 
| 246 | 
            -
                    attr_args  = [@options[:alias] || attrib, attr_name, attr_assoc, attr_table]
         | 
| 247 | 
            -
             | 
| 248 | 
            -
                    define_attribute_finder_methods(*attr_args) if define_finders?(attr_name)
         | 
| 249 | 
            -
                    define_attribute_scope_methods(*attr_args) if define_scopes?(attr_name)
         | 
| 151 | 
            +
                # Define attribute scope methods
         | 
| 152 | 
            +
                def define_attribute_scopes(attr_method:, attr_column:, assoc_name:, table_name:)
         | 
| 153 | 
            +
                  model.send(:define_singleton_method, :"with_#{attr_method}") do |*args|
         | 
| 154 | 
            +
                    joins(assoc_name).where(table_name => { attr_column => args })
         | 
| 250 155 | 
             
                  end
         | 
| 251 156 |  | 
| 252 | 
            -
                  #  | 
| 253 | 
            -
             | 
| 254 | 
            -
                    @model.send(:define_singleton_method, :"find_by_#{attrib}") do |value|
         | 
| 255 | 
            -
                      joins(attr_assoc).find_by(attr_table => { attr_name => value })
         | 
| 256 | 
            -
                    end
         | 
| 257 | 
            -
             | 
| 258 | 
            -
                    @model.send(:define_singleton_method, :"find_by_#{attrib}!") do |value|
         | 
| 259 | 
            -
                      joins(attr_assoc).find_by!(attr_table => { attr_name => value })
         | 
| 260 | 
            -
                    end
         | 
| 157 | 
            +
                  model.send(:define_singleton_method, :"without_#{attr_method}") do |*args|
         | 
| 158 | 
            +
                    joins(assoc_name).where.not(table_name => { attr_column => args })
         | 
| 261 159 | 
             
                  end
         | 
| 160 | 
            +
                end
         | 
| 262 161 |  | 
| 263 | 
            -
             | 
| 264 | 
            -
             | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
                     | 
| 268 | 
            -
             | 
| 269 | 
            -
                     | 
| 270 | 
            -
             | 
| 271 | 
            -
             | 
| 272 | 
            -
                   | 
| 162 | 
            +
                # Define attribute finders and scopes
         | 
| 163 | 
            +
                def define_attribute_queries(attribute)
         | 
| 164 | 
            +
                  attr_options = {
         | 
| 165 | 
            +
                    assoc_name:  association_name,
         | 
| 166 | 
            +
                    table_name:  association_table,
         | 
| 167 | 
            +
                    attr_method: attribute.aliased,
         | 
| 168 | 
            +
                    attr_column: attribute.unprefixed
         | 
| 169 | 
            +
                  }
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                  define_attribute_finders(attr_options) if attribute.finder?
         | 
| 172 | 
            +
                  define_attribute_scopes(attr_options)  if attribute.scope?
         | 
| 173 | 
            +
                end
         | 
| 273 174 | 
             
              end
         | 
| 274 175 | 
             
            end
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            module ActiveDelegate
         | 
| 2 | 
            +
              class Delegator
         | 
| 3 | 
            +
                attr_reader :model, :options
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                # Initialize delegator
         | 
| 6 | 
            +
                def initialize(model, options)
         | 
| 7 | 
            +
                  @model   = model
         | 
| 8 | 
            +
                  @options = default_options.merge(options.symbolize_keys)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                # Get default options
         | 
| 12 | 
            +
                def default_options
         | 
| 13 | 
            +
                  {
         | 
| 14 | 
            +
                    except:    [],
         | 
| 15 | 
            +
                    only:      [],
         | 
| 16 | 
            +
                    to:        nil,
         | 
| 17 | 
            +
                    allow_nil: false
         | 
| 18 | 
            +
                  }
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                # Get delegation options
         | 
| 22 | 
            +
                def delegation_options
         | 
| 23 | 
            +
                  options.select { |k, _v| k.in? %i[to allow_nil prefix] }
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                # Get delegation arguments
         | 
| 27 | 
            +
                def delegation_args(available = [])
         | 
| 28 | 
            +
                  included   = Array(options[:only]).map(&:to_sym)
         | 
| 29 | 
            +
                  excluded   = Array(options[:except]).map(&:to_sym)
         | 
| 30 | 
            +
                  available  = Array(available).map(&:to_sym)
         | 
| 31 | 
            +
                  available &= included if included.any?
         | 
| 32 | 
            +
                  available -= excluded if excluded.any?
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  available
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                # Get delegation prefix
         | 
| 38 | 
            +
                def delegation_prefix
         | 
| 39 | 
            +
                  prefix = options[:prefix]
         | 
| 40 | 
            +
                  prefix == true ? association_name : prefix
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                # Get association name
         | 
| 44 | 
            +
                def association_name
         | 
| 45 | 
            +
                  @options[:to]
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                # Get association reflection
         | 
| 49 | 
            +
                def association_reflection
         | 
| 50 | 
            +
                  reflection = @model.reflect_on_association(association_name)
         | 
| 51 | 
            +
                  return reflection unless reflection.nil?
         | 
| 52 | 
            +
                  raise "#{@model.name} don't have the association #{association_name}"
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                # Get model association class
         | 
| 56 | 
            +
                def association_class
         | 
| 57 | 
            +
                  association_reflection.klass
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: active_delegate
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Jonian Guveli
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2018-10-01 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activerecord
         | 
| @@ -53,33 +53,33 @@ dependencies: | |
| 53 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 54 | 
             
                    version: '1.14'
         | 
| 55 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            -
              name:  | 
| 56 | 
            +
              name: minitest
         | 
| 57 57 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 58 | 
             
                requirements:
         | 
| 59 59 | 
             
                - - "~>"
         | 
| 60 60 | 
             
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            -
                    version: ' | 
| 61 | 
            +
                    version: '5.0'
         | 
| 62 62 | 
             
              type: :development
         | 
| 63 63 | 
             
              prerelease: false
         | 
| 64 64 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 65 | 
             
                requirements:
         | 
| 66 66 | 
             
                - - "~>"
         | 
| 67 67 | 
             
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            -
                    version: ' | 
| 68 | 
            +
                    version: '5.0'
         | 
| 69 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            -
              name:  | 
| 70 | 
            +
              name: rake
         | 
| 71 71 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 72 | 
             
                requirements:
         | 
| 73 73 | 
             
                - - "~>"
         | 
| 74 74 | 
             
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            -
                    version: ' | 
| 75 | 
            +
                    version: '10.0'
         | 
| 76 76 | 
             
              type: :development
         | 
| 77 77 | 
             
              prerelease: false
         | 
| 78 78 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 79 | 
             
                requirements:
         | 
| 80 80 | 
             
                - - "~>"
         | 
| 81 81 | 
             
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            -
                    version: ' | 
| 82 | 
            +
                    version: '10.0'
         | 
| 83 83 | 
             
            description: Stores and retrieves delegatable data through attributes on an ActiveRecord
         | 
| 84 84 | 
             
              class, with support for translatable attributes.
         | 
| 85 85 | 
             
            email:
         | 
| @@ -94,11 +94,14 @@ files: | |
| 94 94 | 
             
            - README.md
         | 
| 95 95 | 
             
            - Rakefile
         | 
| 96 96 | 
             
            - lib/active_delegate.rb
         | 
| 97 | 
            +
            - lib/active_delegate/association/methods.rb
         | 
| 97 98 | 
             
            - lib/active_delegate/associations.rb
         | 
| 99 | 
            +
            - lib/active_delegate/attribute/accessor.rb
         | 
| 100 | 
            +
            - lib/active_delegate/attribute/localize.rb
         | 
| 101 | 
            +
            - lib/active_delegate/attribute/methods.rb
         | 
| 102 | 
            +
            - lib/active_delegate/attribute/object.rb
         | 
| 98 103 | 
             
            - lib/active_delegate/attributes.rb
         | 
| 99 | 
            -
            - lib/active_delegate/ | 
| 100 | 
            -
            - lib/active_delegate/localized.rb
         | 
| 101 | 
            -
            - lib/active_delegate/read_write.rb
         | 
| 104 | 
            +
            - lib/active_delegate/delegator.rb
         | 
| 102 105 | 
             
            - lib/active_delegate/version.rb
         | 
| 103 106 | 
             
            homepage: https://github.com/hardpixel/active-delegate
         | 
| 104 107 | 
             
            licenses:
         | 
| @@ -120,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 120 123 | 
             
                  version: '0'
         | 
| 121 124 | 
             
            requirements: []
         | 
| 122 125 | 
             
            rubyforge_project: 
         | 
| 123 | 
            -
            rubygems_version: 2. | 
| 126 | 
            +
            rubygems_version: 2.7.7
         | 
| 124 127 | 
             
            signing_key: 
         | 
| 125 128 | 
             
            specification_version: 4
         | 
| 126 129 | 
             
            summary: Delegate ActiveRecord model attributes and associations
         | 
| @@ -1,19 +0,0 @@ | |
| 1 | 
            -
            module ActiveDelegate
         | 
| 2 | 
            -
              module Dirty
         | 
| 3 | 
            -
                class << self
         | 
| 4 | 
            -
                  # Get dirty methods for attributes
         | 
| 5 | 
            -
                  def dirty_methods(attributes)
         | 
| 6 | 
            -
                    @dirty_methods = attributes.to_a.flat_map do |attribute|
         | 
| 7 | 
            -
                      method_suffixes.map { |suffix| "#{attribute}#{suffix}" }
         | 
| 8 | 
            -
                    end
         | 
| 9 | 
            -
                  end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  # Get method suffixes
         | 
| 12 | 
            -
                  def method_suffixes
         | 
| 13 | 
            -
                    @method_suffixes ||= Class.new do
         | 
| 14 | 
            -
                      include ::ActiveModel::Dirty
         | 
| 15 | 
            -
                    end.attribute_method_matchers.map(&:suffix).select { |m| m =~ /\A_/ }
         | 
| 16 | 
            -
                  end
         | 
| 17 | 
            -
                end
         | 
| 18 | 
            -
              end
         | 
| 19 | 
            -
            end
         | 
| @@ -1,26 +0,0 @@ | |
| 1 | 
            -
            require 'i18n'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module ActiveDelegate
         | 
| 4 | 
            -
              module Localized
         | 
| 5 | 
            -
                class << self
         | 
| 6 | 
            -
                  # Get localized methods for attributes
         | 
| 7 | 
            -
                  def localized_methods(attributes)
         | 
| 8 | 
            -
                    @localized_methods = attributes.to_a.flat_map do |attribute|
         | 
| 9 | 
            -
                      method_suffixes.map { |suffix| "#{attribute}#{suffix}" }
         | 
| 10 | 
            -
                    end
         | 
| 11 | 
            -
                  end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  # Get method suffixes
         | 
| 14 | 
            -
                  def method_suffixes
         | 
| 15 | 
            -
                    @method_suffixes ||= I18n.available_locales.map do |locale|
         | 
| 16 | 
            -
                      "_#{normalize_locale(locale)}"
         | 
| 17 | 
            -
                    end
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                  # Normalize locale
         | 
| 21 | 
            -
                  def normalize_locale(locale)
         | 
| 22 | 
            -
                    "#{locale.to_s.downcase.sub("-", "_")}".freeze
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
                end
         | 
| 25 | 
            -
              end
         | 
| 26 | 
            -
            end
         | 
| @@ -1,17 +0,0 @@ | |
| 1 | 
            -
            module ActiveDelegate
         | 
| 2 | 
            -
              module ReadWrite
         | 
| 3 | 
            -
                class << self
         | 
| 4 | 
            -
                  # Get readwrite methods for attributes
         | 
| 5 | 
            -
                  def readwrite_methods(attributes)
         | 
| 6 | 
            -
                    @readwrite_methods = attributes.to_a.flat_map do |attribute|
         | 
| 7 | 
            -
                      method_suffixes.map { |suffix| "#{attribute}#{suffix}" }
         | 
| 8 | 
            -
                    end
         | 
| 9 | 
            -
                  end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  # Get method suffixes
         | 
| 12 | 
            -
                  def method_suffixes
         | 
| 13 | 
            -
                    ['', '=', '?']
         | 
| 14 | 
            -
                  end
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
              end
         | 
| 17 | 
            -
            end
         |