Module: ActiveModel::AttributeMethods::ClassMethods

Defined in:
activemodel/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


238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'activemodel/lib/active_model/attribute_methods.rb', line 238

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'

233
234
235
236
# File 'activemodel/lib/active_model/attribute_methods.rb', line 233

def attribute_method_affix(*affixes)
  self.attribute_method_matchers += 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

158
159
160
161
# File 'activemodel/lib/active_model/attribute_methods.rb', line 158

def attribute_method_prefix(*prefixes)
  self.attribute_method_matchers += 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

195
196
197
198
# File 'activemodel/lib/active_model/attribute_methods.rb', line 195

def attribute_method_suffix(*suffixes)
  self.attribute_method_matchers += suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix }
  undefine_attribute_methods
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:

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

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'activemodel/lib/active_model/attribute_methods.rb', line 100

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_method(attr_name) ⇒ Object


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
313
314
315
316
317
# File 'activemodel/lib/active_model/attribute_methods.rb', line 285

def define_attribute_method(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

#define_attribute_methods(attr_names) ⇒ Object

Declares 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

281
282
283
# File 'activemodel/lib/active_model/attribute_methods.rb', line 281

def define_attribute_methods(attr_names)
  attr_names.each { |attr_name| define_attribute_method(attr_name) }
end

#generated_attribute_methodsObject

Returns true if the attribute methods defined have been generated.


327
328
329
330
331
332
333
# File 'activemodel/lib/active_model/attribute_methods.rb', line 327

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


320
321
322
323
324
# File 'activemodel/lib/active_model/attribute_methods.rb', line 320

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