Module: ActiveModel::AttributeMethods::ClassMethods

Defined in:
activemodel/lib/active_model/attribute_methods.rb

Defined Under Namespace

Classes: AttributeMethodPattern

Instance Method Summary collapse

Instance Method Details

#alias_attribute(new_name, old_name) ⇒ Object

Allows you to make aliases for attributes.

class Person
  include ActiveModel::AttributeMethods

  attr_accessor :name
  attribute_method_suffix '_short?'
  define_attribute_methods :name

  alias_attribute :nickname, :name

  private

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

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

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'activemodel/lib/active_model/attribute_methods.rb', line 209

def alias_attribute(new_name, old_name)
  self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
  ActiveSupport::CodeGenerator.batch(self, __FILE__, __LINE__) do |code_generator|
    attribute_method_patterns.each do |pattern|
      method_name = pattern.method_name(new_name).to_s
      target_name = pattern.method_name(old_name).to_s
      parameters = pattern.parameters

      mangled_name = target_name
      unless NAME_COMPILABLE_REGEXP.match?(target_name)
        mangled_name = "__temp__#{target_name.unpack1("h*")}"
      end

      code_generator.define_cached_method(method_name, as: mangled_name, namespace: :alias_attribute) do |batch|
        body = if CALL_COMPILABLE_REGEXP.match?(target_name)
          "self.#{target_name}(#{parameters || ''})"
        else
          call_args = [":'#{target_name}'"]
          call_args << parameters if parameters
          "send(#{call_args.join(", ")})"
        end

        modifier = pattern.parameters == FORWARD_PARAMETERS ? "ruby2_keywords " : ""

        batch <<
          "#{modifier}def #{mangled_name}(#{parameters || ''})" <<
          body <<
          "end"
      end
    end
  end
end

#attribute_alias(name) ⇒ Object

Returns the original name for the alias name


248
249
250
# File 'activemodel/lib/active_model/attribute_methods.rb', line 248

def attribute_alias(name)
  attribute_aliases[name.to_s]
end

#attribute_alias?(new_name) ⇒ Boolean

Is new_name an alias?

Returns:

  • (Boolean)

243
244
245
# File 'activemodel/lib/active_model/attribute_methods.rb', line 243

def attribute_alias?(new_name)
  attribute_aliases.key? new_name.to_s
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.

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)
    send("#{attr}=", 'Default Name')
  end
end

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

180
181
182
183
# File 'activemodel/lib/active_model/attribute_methods.rb', line 180

def attribute_method_affix(*affixes)
  self.attribute_method_patterns += affixes.map! { |affix| AttributeMethodPattern.new(**affix) }
  undefine_attribute_methods
end

#attribute_method_prefix(*prefixes, parameters: nil) ⇒ 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.

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

109
110
111
112
# File 'activemodel/lib/active_model/attribute_methods.rb', line 109

def attribute_method_prefix(*prefixes, parameters: nil)
  self.attribute_method_patterns += prefixes.map! { |prefix| AttributeMethodPattern.new(prefix: prefix, parameters: parameters) }
  undefine_attribute_methods
end

#attribute_method_suffix(*suffixes, parameters: nil) ⇒ 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.

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

144
145
146
147
# File 'activemodel/lib/active_model/attribute_methods.rb', line 144

def attribute_method_suffix(*suffixes, parameters: nil)
  self.attribute_method_patterns += suffixes.map! { |suffix| AttributeMethodPattern.new(suffix: suffix, parameters: parameters) }
  undefine_attribute_methods
end

#define_attribute_method(attr_name, _owner: generated_attribute_methods) ⇒ Object

Declares an attribute that should be prefixed and suffixed by ActiveModel::AttributeMethods.

To use, pass an attribute name (as string or symbol). Be sure to declare define_attribute_method after you define any prefix, suffix or affix method, or they will not hook in.

class Person
  include ActiveModel::AttributeMethods

  attr_accessor :name
  attribute_method_suffix '_short?'

  # Call to define_attribute_method must appear after the
  # attribute_method_prefix, attribute_method_suffix or
  # attribute_method_affix declarations.
  define_attribute_method :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

311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'activemodel/lib/active_model/attribute_methods.rb', line 311

def define_attribute_method(attr_name, _owner: generated_attribute_methods)
  ActiveSupport::CodeGenerator.batch(_owner, __FILE__, __LINE__) do |owner|
    attribute_method_patterns.each do |pattern|
      method_name = pattern.method_name(attr_name)

      unless instance_method_already_implemented?(method_name)
        generate_method = "define_method_#{pattern.proxy_target}"

        if respond_to?(generate_method, true)
          send(generate_method, attr_name.to_s, owner: owner)
        else
          define_proxy_call(owner, method_name, pattern.proxy_target, pattern.parameters, attr_name.to_s, namespace: :active_model_proxy)
        end
      end
    end
    attribute_method_patterns_cache.clear
  end
end

#define_attribute_methods(*attr_names) ⇒ Object

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

To use, pass 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 declarations.
  define_attribute_methods :name, :age, :address

  private

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

276
277
278
279
280
# File 'activemodel/lib/active_model/attribute_methods.rb', line 276

def define_attribute_methods(*attr_names)
  ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
    attr_names.flatten.each { |attr_name| define_attribute_method(attr_name, _owner: owner) }
  end
end

#undefine_attribute_methodsObject

Removes all the previously dynamically defined methods from the class.

class Person
  include ActiveModel::AttributeMethods

  attr_accessor :name
  attribute_method_suffix '_short?'
  define_attribute_method :name

  private

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

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

Person.undefine_attribute_methods

person.name_short? # => NoMethodError

353
354
355
356
357
358
# File 'activemodel/lib/active_model/attribute_methods.rb', line 353

def undefine_attribute_methods
  generated_attribute_methods.module_eval do
    undef_method(*instance_methods)
  end
  attribute_method_patterns_cache.clear
end