Skip to content

Commit

Permalink
Adding ActiveModel::AttributeMethods documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
mikel committed Jan 17, 2010
1 parent e1c15d9 commit 8834b26
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 23 deletions.
25 changes: 23 additions & 2 deletions activemodel/README
Expand Up @@ -12,9 +12,30 @@ Active Model provides a known set of interfaces that your objects can implement
to then present a common interface to the Action Pack helpers. You can include
functionality from the following modules:

* Adding attribute magic to your objects

Add prefixes and suffixes to defined attribute methods...

class Person
include ActiveModel::AttributeMethods

attribute_method_prefix 'clear_'
define_attribute_methods [:name, :age]

attr_accessor :name, :age

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

...gives you clear_name, clear_age.

{Learn more}[link:classes/ActiveModel/AttributeMethods.html]

* Adding callbacks to your objects

class MyClass
class Person
extend ActiveModel::Callbacks
define_model_callbacks :create

Expand All @@ -32,7 +53,7 @@ functionality from the following modules:

* For classes that already look like an Active Record object

class MyClass
class Person
include ActiveModel::Conversion
end

Expand Down
141 changes: 120 additions & 21 deletions activemodel/lib/active_model/attribute_methods.rb
Expand Up @@ -5,10 +5,51 @@ module ActiveModel
class MissingAttributeError < NoMethodError
end

# <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and suffixes
# to your methods as well as handling the creation of Active Record like class methods
# such as +table_name+.
#
# The requirements to implement ActiveModel::AttributeMethods are:
#
# * <tt>include ActiveModel::AttributeMethods</tt> in your object
# * Call each Attribute Method module method you want to add, such as
# attribute_method_suffix or attribute_method_prefix
# * Call <tt>define_attribute_methods</tt> after the other methods are
# called.
# * Define the various generic +_attribute+ methods that you have declared
#
# A minimal implementation could be:
#
# class Person
#
# include ActiveModel::AttributeMethods
#
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
# attribute_method_suffix '_contrived?'
# attribute_method_prefix 'clear_'
# define_attribute_methods ['name']
#
# attr_accessor :name
#
# private
#
# def attribute_contrived?(attr)
# true
# end
#
# def clear_attribute(attr)
# send("#{attr}=", nil)
# end
#
# def reset_attribute_to_default!(attr)
# send("#{attr}=", "Default Name")
# end
#
# end
#
module AttributeMethods
extend ActiveSupport::Concern

# Declare and check for suffixed attribute methods.
module ClassMethods
# Defines an "attribute" method (like +inheritance_column+ or
# +table_name+). A new (class) method will be created with the
Expand All @@ -22,12 +63,27 @@ module ClassMethods
#
# Example:
#
# class A < ActiveRecord::Base
# 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
#
# Provivdes you with:
#
# AttributePerson.primary_key
# # => "sysid"
# AttributePerson.inheritance_column = 'address'
# AttributePerson.inheritance_column
# # => 'address_id'
def define_attr_method(name, value=nil, &block)
sing = metaclass
sing.send :alias_method, "original_#{name}", name
Expand All @@ -54,19 +110,25 @@ def define_attr_method(name, value=nil, &block)
#
# For example:
#
# class Person < ActiveRecord::Base
# class Person
#
# include ActiveModel::AttributeMethods
# attr_accessor :name
# attribute_method_prefix 'clear_'
# define_attribute_methods [:name]
#
# private
# def clear_attribute(attr)
# ...
# end
#
# def clear_attribute(attr)
# send("#{attr}=", nil)
# end
# end
#
# person = Person.find(1)
# person.name # => 'Gem'
# person = Person.new
# person.name = "Bob"
# person.name # => "Bob"
# person.clear_name
# person.name # => ''
# person.name # => nil
def attribute_method_prefix(*prefixes)
attribute_method_matchers.concat(prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix })
undefine_attribute_methods
Expand All @@ -86,18 +148,24 @@ def attribute_method_prefix(*prefixes)
#
# For example:
#
# class Person < ActiveRecord::Base
# class Person
#
# include ActiveModel::AttributeMethods
# attr_accessor :name
# attribute_method_suffix '_short?'
# define_attribute_methods [:name]
#
# private
# def attribute_short?(attr)
# ...
# end
#
# def attribute_short?(attr)
# send(attr).length < 5
# end
# end
#
# person = Person.find(1)
# person.name # => 'Gem'
# person.name_short? # => true
# person = Person.new
# person.name = "Bob"
# person.name # => "Bob"
# person.name_short? # => true
def attribute_method_suffix(*suffixes)
attribute_method_matchers.concat(suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix })
undefine_attribute_methods
Expand All @@ -118,16 +186,21 @@ def attribute_method_suffix(*suffixes)
#
# For example:
#
# class Person < ActiveRecord::Base
# 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
#
# def reset_attribute_to_default!(attr)
# ...
# end
# end
#
# person = Person.find(1)
# person = Person.new
# person.name # => 'Gem'
# person.reset_name_to_default!
# person.name # => 'Gemma'
Expand All @@ -146,6 +219,30 @@ def #{matcher.method_name(new_name)}(*args)
end
end

# 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
def define_attribute_methods(attr_names)
return if attribute_methods_generated?
attr_names.each do |attr_name|
Expand All @@ -168,6 +265,7 @@ def #{matcher.method_name(attr_name)}(*args)
@attribute_methods_generated = true
end

# Removes all the preiously dynamically defined methods from the class
def undefine_attribute_methods
generated_attribute_methods.module_eval do
instance_methods.each { |m| undef_method(m) }
Expand All @@ -183,6 +281,7 @@ def generated_attribute_methods #:nodoc:
end
end

# Returns true if the attribute methods defined have been generated.
def attribute_methods_generated?
@attribute_methods_generated ||= nil
end
Expand Down

0 comments on commit 8834b26

Please sign in to comment.