Module: ActiveModel::AttributeMethods::ClassMethods

Defined in:
lib/active_model/attribute_methods.rb

Defined Under Namespace

Classes: AttributeMethodMatcher

Instance Method Summary collapse

Instance Method Details

#alias_attribute(new_name, old_name) ⇒ Object



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/active_model/attribute_methods.rb', line 233

def alias_attribute(new_name, old_name)
  attribute_method_matchers.each do |matcher|
    matcher_new = matcher.method_name(new_name).to_s
    matcher_old = matcher.method_name(old_name).to_s

    if matcher_new =~ COMPILABLE_REGEXP && matcher_old =~ COMPILABLE_REGEXP
      module_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{matcher_new}(*args)
          send(:#{matcher_old}, *args)
        end
      RUBY
    else
      define_method(matcher_new) do |*args|
        send(matcher_old, *args)
      end
    end
  end
end

#attribute_method_affix(*affixes) ⇒ Object

Declares a method available for all attributes with the given prefix and suffix. Uses method_missing and respond_to? to rewrite the method.

#{prefix}#{attr}#{suffix}(*args, &block)

to

#{prefix}attribute#{suffix}(#{attr}, *args, &block)

An #{prefix}attribute#{suffix} instance method must exist and accept at least the attr argument.

For example:

class Person

  include ActiveModel::AttributeMethods
  attr_accessor :name
  attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
  define_attribute_methods [:name]

  private

  def reset_attribute_to_default!(attr)
    ...
  end
end

person = Person.new
person.name                         # => 'Gem'
person.reset_name_to_default!
person.name                         # => 'Gemma'


228
229
230
231
# File 'lib/active_model/attribute_methods.rb', line 228

def attribute_method_affix(*affixes)
  attribute_method_matchers.concat(affixes.map { |affix| AttributeMethodMatcher.new :prefix => affix[:prefix], :suffix => affix[:suffix] })
  undefine_attribute_methods
end

#attribute_method_prefix(*prefixes) ⇒ Object

Declares a method available for all attributes with the given prefix. Uses method_missing and respond_to? to rewrite the method.

#{prefix}#{attr}(*args, &block)

to

#{prefix}attribute(#{attr}, *args, &block)

An instance method #{prefix}attribute must exist and accept at least the attr argument.

For example:

class Person

  include ActiveModel::AttributeMethods
  attr_accessor :name
  attribute_method_prefix 'clear_'
  define_attribute_methods [:name]

  private

  def clear_attribute(attr)
    send("#{attr}=", nil)
  end
end

person = Person.new
person.name = "Bob"
person.name          # => "Bob"
person.clear_name
person.name          # => nil


153
154
155
156
# File 'lib/active_model/attribute_methods.rb', line 153

def attribute_method_prefix(*prefixes)
  attribute_method_matchers.concat(prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix })
  undefine_attribute_methods
end

#attribute_method_suffix(*suffixes) ⇒ Object

Declares a method available for all attributes with the given suffix. Uses method_missing and respond_to? to rewrite the method.

#{attr}#{suffix}(*args, &block)

to

attribute#{suffix}(#{attr}, *args, &block)

An attribute#{suffix} instance method must exist and accept at least the attr argument.

For example:

class Person

  include ActiveModel::AttributeMethods
  attr_accessor :name
  attribute_method_suffix '_short?'
  define_attribute_methods [:name]

  private

  def attribute_short?(attr)
    send(attr).length < 5
  end
end

person = Person.new
person.name = "Bob"
person.name          # => "Bob"
person.name_short?   # => true


190
191
192
193
# File 'lib/active_model/attribute_methods.rb', line 190

def attribute_method_suffix(*suffixes)
  attribute_method_matchers.concat(suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix })
  undefine_attribute_methods
end

#attribute_methods_generated?Boolean

Returns true if the attribute methods defined have been generated.

Returns:

  • (Boolean)


332
333
334
# File 'lib/active_model/attribute_methods.rb', line 332

def attribute_methods_generated?
  @attribute_methods_generated ||= nil
end

#define_attr_method(name, value = nil, &block) ⇒ Object

Defines an “attribute” method (like inheritance_column or table_name). A new (class) method will be created with the given name. If a value is specified, the new method will return that value (as a string). Otherwise, the given block will be used to compute the value of the method.

The original method will be aliased, with the new name being prefixed with “original_”. This allows the new method to access the original value.

Example:

class Person

  include ActiveModel::AttributeMethods

  cattr_accessor :primary_key
  cattr_accessor :inheritance_column

  define_attr_method :primary_key, "sysid"
  define_attr_method( :inheritance_column ) do
    original_inheritance_column + "_id"
  end

end

Provides you with:

AttributePerson.primary_key
# => "sysid"
AttributePerson.inheritance_column = 'address'
AttributePerson.inheritance_column
# => 'address_id'


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/active_model/attribute_methods.rb', line 95

def define_attr_method(name, value=nil, &block)
  sing = singleton_class
  sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
    if method_defined?(:'original_#{name}')
      undef :'original_#{name}'
    end
    alias_method :'original_#{name}', :'#{name}'
  eorb
  if block_given?
    sing.send :define_method, name, &block
  else
    # If we can compile the method name, do it. Otherwise use define_method.
    # This is an important *optimization*, please don't change it. define_method
    # has slower dispatch and consumes more memory.
    if name =~ COMPILABLE_REGEXP
      sing.class_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end
      RUBY
    else
      value = value.to_s if value
      sing.send(:define_method, name) { value }
    end
  end
end

#define_attribute_methods(attr_names) ⇒ Object

Declares a the attributes that should be prefixed and suffixed by ActiveModel::AttributeMethods.

To use, pass in an array of attribute names (as strings or symbols), be sure to declare define_attribute_methods after you define any prefix, suffix or affix methods, or they will not hook in.

class Person

  include ActiveModel::AttributeMethods
  attr_accessor :name, :age, :address
  attribute_method_prefix 'clear_'

  # Call to define_attribute_methods must appear after the
  # attribute_method_prefix, attribute_method_suffix or
  # attribute_method_affix declares.
  define_attribute_methods [:name, :age, :address]

  private

  def clear_attribute(attr)
    ...
  end
end


276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/active_model/attribute_methods.rb', line 276

def define_attribute_methods(attr_names)
  return if attribute_methods_generated?
  attr_names.each do |attr_name|
    attribute_method_matchers.each do |matcher|
      unless instance_method_already_implemented?(matcher.method_name(attr_name))
        generate_method = "define_method_#{matcher.prefix}attribute#{matcher.suffix}"

        if respond_to?(generate_method)
          send(generate_method, attr_name)
        else
          method_name = matcher.method_name(attr_name)

          generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
            if method_defined?('#{method_name}')
              undef :'#{method_name}'
            end
          RUBY

          if method_name.to_s =~ COMPILABLE_REGEXP
            generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
              def #{method_name}(*args)
                send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
              end
            RUBY
          else
            generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
              define_method('#{method_name}') do |*args|
                send('#{matcher.method_missing_target}', '#{attr_name}', *args)
              end
            RUBY
          end
        end
      end
    end
  end
  @attribute_methods_generated = true
end

#generated_attribute_methodsObject

Returns true if the attribute methods defined have been generated.



323
324
325
326
327
328
329
# File 'lib/active_model/attribute_methods.rb', line 323

def generated_attribute_methods #:nodoc:
  @generated_attribute_methods ||= begin
    mod = Module.new
    include mod
    mod
  end
end

#undefine_attribute_methodsObject

Removes all the previously dynamically defined methods from the class



315
316
317
318
319
320
# File 'lib/active_model/attribute_methods.rb', line 315

def undefine_attribute_methods
  generated_attribute_methods.module_eval do
    instance_methods.each { |m| undef_method(m) }
  end
  @attribute_methods_generated = nil
end