activemodel-3.2.16/ 0000755 0001750 0001750 00000000000 12247654710 013433 5 ustar ondrej ondrej activemodel-3.2.16/CHANGELOG.md 0000644 0001750 0001750 00000004222 12247654710 015244 0 ustar ondrej ondrej ## Rails 3.2.15 (Oct 16, 2013) ##
* No changes.
## Rails 3.2.14 (Jul 22, 2013) ##
* No changes.
## Rails 3.2.13 (Mar 18, 2013) ##
* Specify type of singular association during serialization *Steve Klabnik*
## Rails 3.2.12 (Feb 11, 2013) ##
* Fix issue with `attr_protected` where malformed input could circumvent protection.
CVE-2013-0276
*joernchen*
## Rails 3.2.11 (Jan 8, 2013) ##
* No changes.
## Rails 3.2.10 (Jan 2, 2013) ##
* No changes.
## Rails 3.2.9 (Nov 12, 2012) ##
* Due to a change in builder, nil values and empty strings now generates
closed tags, so instead of this:
It generates this:
*Carlos Antonio da Silva*
## Rails 3.2.8 (Aug 9, 2012) ##
* No changes.
## Rails 3.2.7 (Jul 26, 2012) ##
* `validates_inclusion_of` and `validates_exclusion_of` now accept `:within` option as alias of `:in` as documented.
* Fix the the backport of the object dup with the ruby 1.9.3p194.
## Rails 3.2.6 (Jun 12, 2012) ##
* No changes.
## Rails 3.2.4 (May 31, 2012) ##
* No changes.
## Rails 3.2.3 (March 30, 2012) ##
* No changes.
## Rails 3.2.2 (March 1, 2012) ##
* No changes.
## Rails 3.2.1 (January 26, 2012) ##
* No changes.
## Rails 3.2.0 (January 20, 2012) ##
* Deprecated `define_attr_method` in `ActiveModel::AttributeMethods`, because this only existed to
support methods like `set_table_name` in Active Record, which are themselves being deprecated. *Jon Leighton*
* Add ActiveModel::Errors#added? to check if a specific error has been added *Martin Svalin*
* Add ability to define strict validation(with :strict => true option) that always raises exception when fails *Bogdan Gusiev*
* Deprecate "Model.model_name.partial_path" in favor of "model.to_partial_path" *Grant Hutchins, Peter Jaros*
* Provide mass_assignment_sanitizer as an easy API to replace the sanitizer behavior. Also support both :logger (default) and :strict sanitizer behavior *Bogdan Gusiev*
Please check [3-1-stable](https://github.com/rails/rails/blob/3-1-stable/activemodel/CHANGELOG.md) for previous changes.
activemodel-3.2.16/README.rdoc 0000644 0001750 0001750 00000012400 12247654710 015236 0 ustar ondrej ondrej = Active Model -- model interfaces for Rails
Active Model provides a known set of interfaces for usage in model classes.
They allow for Action Pack helpers to interact with non-ActiveRecord models,
for example. Active Model also helps building custom ORMs for use outside of
the Rails framework.
Prior to Rails 3.0, if a plugin or gem developer wanted to have an object
interact with Action Pack helpers, it was required to either copy chunks of
code from Rails, or monkey patch entire helpers to make them handle objects
that did not exactly conform to the Active Record interface. This would result
in code duplication and fragile applications that broke on upgrades.
Active Model solves this. You can include functionality from the following
modules:
* Add attribute magic to objects
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
person.clear_name
person.clear_age
{Learn more}[link:classes/ActiveModel/AttributeMethods.html]
* Callbacks for certain operations
class Person
extend ActiveModel::Callbacks
define_model_callbacks :create
def create
run_callbacks :create do
# Your create action methods here
end
end
end
This generates +before_create+, +around_create+ and +after_create+
class methods that wrap your create method.
{Learn more}[link:classes/ActiveModel/Callbacks.html]
* Tracking value changes
The ActiveModel::Dirty module allows for tracking attribute changes:
person = Person.new
person.name # => nil
person.changed? # => false
person.name = 'bob'
person.changed? # => true
person.changed # => ['name']
person.changes # => { 'name' => [nil, 'bob'] }
person.name = 'robert'
person.save
person.previous_changes # => {'name' => ['bob, 'robert']}
{Learn more}[link:classes/ActiveModel/Dirty.html]
* Adding +errors+ interface to objects
Exposing error messages allows objects to interact with Action Pack
helpers seamlessly.
class Person
def initialize
@errors = ActiveModel::Errors.new(self)
end
attr_accessor :name
attr_reader :errors
def validate!
errors.add(:name, "can not be nil") if name.nil?
end
def ErrorsPerson.human_attribute_name(attr, options = {})
"Name"
end
end
person.errors.full_messages
# => ["Name can not be nil"]
person.errors.full_messages
# => ["Name can not be nil"]
{Learn more}[link:classes/ActiveModel/Errors.html]
* Model name introspection
class NamedPerson
extend ActiveModel::Naming
end
NamedPerson.model_name # => "NamedPerson"
NamedPerson.model_name.human # => "Named person"
{Learn more}[link:classes/ActiveModel/Naming.html]
* Observer support
ActiveModel::Observers allows your object to implement the Observer
pattern in a Rails App and take advantage of all the standard observer
functions.
{Learn more}[link:classes/ActiveModel/Observer.html]
* Making objects serializable
ActiveModel::Serialization provides a standard interface for your object
to provide +to_json+ or +to_xml+ serialization.
s = SerialPerson.new
s.serializable_hash # => {"name"=>nil}
s.to_json # => "{\"name\":null}"
s.to_xml # => "\n "My attribute"
{Learn more}[link:classes/ActiveModel/Translation.html]
* Validation support
class Person
include ActiveModel::Validations
attr_accessor :first_name, :last_name
validates_each :first_name, :last_name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
end
end
person = Person.new
person.first_name = 'zoolander'
person.valid? # => false
{Learn more}[link:classes/ActiveModel/Validations.html]
* Custom validators
class Person
include ActiveModel::Validations
validates_with HasNameValidator
attr_accessor :name
end
class HasNameValidator < ActiveModel::Validator
def validate(record)
record.errors[:name] = "must exist" if record.name.blank?
end
end
p = ValidatorPerson.new
p.valid? # => false
p.errors.full_messages # => ["Name must exist"]
p.name = "Bob"
p.valid? # => true
{Learn more}[link:classes/ActiveModel/Validator.html]
== Download and installation
The latest version of Active Model can be installed with RubyGems:
% [sudo] gem install activemodel
Source code can be downloaded as part of the Rails project on GitHub
* https://github.com/rails/rails/tree/3-2-stable/activemodel
== License
Active Model is released under the MIT license.
== Support
API documentation is at
* http://api.rubyonrails.org
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
activemodel-3.2.16/checksums.yaml.gz 0000444 0001750 0001750 00000000413 12247654710 016717 0 ustar ondrej ondrej #Re;R0"c˒%R08=%Zvy{YK{jd-XהVat_a_"Z0kٞkkaO+MAIͷRn0#c322eoB.&[:w2N!I0r2MVf=0DyZT 'VmO }}ЈFzvն4:0kHP?^#B.? activemodel-3.2.16/lib/ 0000755 0001750 0001750 00000000000 12247654710 014201 5 ustar ondrej ondrej activemodel-3.2.16/lib/active_model/ 0000755 0001750 0001750 00000000000 12247654710 016634 5 ustar ondrej ondrej activemodel-3.2.16/lib/active_model/validations.rb 0000644 0001750 0001750 00000017437 12247654710 021512 0 ustar ondrej ondrej require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/except'
require 'active_model/errors'
require 'active_model/validations/callbacks'
require 'active_model/validator'
module ActiveModel
# == Active Model Validations
#
# Provides a full validation framework to your objects.
#
# A minimal implementation could be:
#
# class Person
# include ActiveModel::Validations
#
# attr_accessor :first_name, :last_name
#
# validates_each :first_name, :last_name do |record, attr, value|
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
# end
# end
#
# Which provides you with the full standard validation stack that you
# know from Active Record:
#
# person = Person.new
# person.valid? # => true
# person.invalid? # => false
#
# person.first_name = 'zoolander'
# person.valid? # => false
# person.invalid? # => true
# person.errors # => #["starts with z."]}>
#
# Note that ActiveModel::Validations automatically adds an +errors+ method
# to your instances initialized with a new ActiveModel::Errors object, so
# there is no need for you to do this manually.
#
module Validations
extend ActiveSupport::Concern
include ActiveSupport::Callbacks
included do
extend ActiveModel::Translation
extend HelperMethods
include HelperMethods
attr_accessor :validation_context
define_callbacks :validate, :scope => :name
class_attribute :_validators
self._validators = Hash.new { |h,k| h[k] = [] }
end
module ClassMethods
# Validates each attribute against a block.
#
# class Person
# include ActiveModel::Validations
#
# attr_accessor :first_name, :last_name
#
# validates_each :first_name, :last_name do |record, attr, value|
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
# end
# end
#
# Options:
# * :on - Specifies the context where this validation is active
# (e.g. :on => :create or :on => :custom_validation_context)
# * :allow_nil - Skip validation if attribute is +nil+.
# * :allow_blank - Skip validation if attribute is blank.
# * :if - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. :if => :allow_validation,
# or :if => Proc.new { |user| user.signup_step > 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine if the validation should
# not occur (e.g. :unless => :skip_validation, or
# :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_each(*attr_names, &block)
options = attr_names.extract_options!.symbolize_keys
validates_with BlockValidator, options.merge(:attributes => attr_names.flatten), &block
end
# Adds a validation method or block to the class. This is useful when
# overriding the +validate+ instance method becomes too unwieldy and
# you're looking for more descriptive declaration of your validations.
#
# This can be done with a symbol pointing to a method:
#
# class Comment
# include ActiveModel::Validations
#
# validate :must_be_friends
#
# def must_be_friends
# errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee)
# end
# end
#
# With a block which is passed with the current record to be validated:
#
# class Comment
# include ActiveModel::Validations
#
# validate do |comment|
# comment.must_be_friends
# end
#
# def must_be_friends
# errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee)
# end
# end
#
# Or with a block where self points to the current record to be validated:
#
# class Comment
# include ActiveModel::Validations
#
# validate do
# errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee)
# end
# end
#
def validate(*args, &block)
options = args.extract_options!
if options.key?(:on)
options = options.dup
options[:if] = Array.wrap(options[:if])
options[:if].unshift("validation_context == :#{options[:on]}")
end
args << options
set_callback(:validate, *args, &block)
end
# List all validators that are being used to validate the model using
# +validates_with+ method.
def validators
_validators.values.flatten.uniq
end
# List all validators that being used to validate a specific attribute.
def validators_on(*attributes)
attributes.map do |attribute|
_validators[attribute.to_sym]
end.flatten
end
# Check if method is an attribute method or not.
def attribute_method?(attribute)
method_defined?(attribute)
end
# Copy validators on inheritance.
def inherited(base)
dup = _validators.dup
base._validators = dup.each { |k, v| dup[k] = v.dup }
super
end
end
# Clean the +Errors+ object if instance is duped
def initialize_dup(other) # :nodoc:
@errors = nil
super if defined?(super)
end
# Backport dup from 1.9 so that #initialize_dup gets called
unless Object.respond_to?(:initialize_dup, true)
def dup # :nodoc:
copy = super
copy.initialize_dup(self)
copy
end
end
# Returns the +Errors+ object that holds all information about attribute error messages.
def errors
@errors ||= Errors.new(self)
end
# Runs all the specified validations and returns true if no errors were added
# otherwise false. Context can optionally be supplied to define which callbacks
# to test against (the context is defined on the validations using :on).
def valid?(context = nil)
current_context, self.validation_context = validation_context, context
errors.clear
run_validations!
ensure
self.validation_context = current_context
end
# Performs the opposite of valid?. Returns true if errors were added,
# false otherwise.
def invalid?(context = nil)
!valid?(context)
end
# Hook method defining how an attribute value should be retrieved. By default
# this is assumed to be an instance named after the attribute. Override this
# method in subclasses should you need to retrieve the value for a given
# attribute differently:
#
# class MyClass
# include ActiveModel::Validations
#
# def initialize(data = {})
# @data = data
# end
#
# def read_attribute_for_validation(key)
# @data[key]
# end
# end
#
alias :read_attribute_for_validation :send
protected
def run_validations!
run_callbacks :validate
errors.empty?
end
end
end
Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
filename = File.basename(path)
require "active_model/validations/#{filename}"
end
activemodel-3.2.16/lib/active_model/mass_assignment_security.rb 0000644 0001750 0001750 00000021020 12247654710 024276 0 ustar ondrej ondrej require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/array/wrap'
require 'active_model/mass_assignment_security/permission_set'
require 'active_model/mass_assignment_security/sanitizer'
module ActiveModel
# = Active Model Mass-Assignment Security
module MassAssignmentSecurity
extend ActiveSupport::Concern
included do
class_attribute :_accessible_attributes
class_attribute :_protected_attributes
class_attribute :_active_authorizer
class_attribute :_mass_assignment_sanitizer
self.mass_assignment_sanitizer = :logger
end
# Mass assignment security provides an interface for protecting attributes
# from end-user assignment. For more complex permissions, mass assignment security
# may be handled outside the model by extending a non-ActiveRecord class,
# such as a controller, with this behavior.
#
# For example, a logged in user may need to assign additional attributes depending
# on their role:
#
# class AccountsController < ApplicationController
# include ActiveModel::MassAssignmentSecurity
#
# attr_accessible :first_name, :last_name
# attr_accessible :first_name, :last_name, :plan_id, :as => :admin
#
# def update
# ...
# @account.update_attributes(account_params)
# ...
# end
#
# protected
#
# def account_params
# role = admin ? :admin : :default
# sanitize_for_mass_assignment(params[:account], role)
# end
#
# end
#
# = Configuration options
#
# * mass_assignment_sanitizer - Defines sanitize method. Possible values are:
# * :logger (default) - writes filtered attributes to logger
# * :strict - raise ActiveModel::MassAssignmentSecurity::Error on any protected attribute update
#
# You can specify your own sanitizer object eg. MySanitizer.new.
# See ActiveModel::MassAssignmentSecurity::LoggerSanitizer for example implementation.
#
#
module ClassMethods
# Attributes named in this macro are protected from mass-assignment
# whenever attributes are sanitized before assignment. A role for the
# attributes is optional, if no role is provided then :default is used.
# A role can be defined by using the :as option.
#
# Mass-assignment to these attributes will simply be ignored, to assign
# to them you can use direct writer methods. This is meant to protect
# sensitive attributes from being overwritten by malicious users
# tampering with URLs or forms. Example:
#
# class Customer
# include ActiveModel::MassAssignmentSecurity
#
# attr_accessor :name, :credit_rating
#
# attr_protected :credit_rating, :last_login
# attr_protected :last_login, :as => :admin
#
# def assign_attributes(values, options = {})
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
# send("#{k}=", v)
# end
# end
# end
#
# When using the :default role:
#
# customer = Customer.new
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default)
# customer.name # => "David"
# customer.credit_rating # => nil
# customer.last_login # => nil
#
# customer.credit_rating = "Average"
# customer.credit_rating # => "Average"
#
# And using the :admin role:
#
# customer = Customer.new
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin)
# customer.name # => "David"
# customer.credit_rating # => "Excellent"
# customer.last_login # => nil
#
# To start from an all-closed default and enable attributes as needed,
# have a look at +attr_accessible+.
#
# Note that using Hash#except or Hash#slice in place of
# +attr_protected+ to sanitize attributes provides basically the same
# functionality, but it makes a bit tricky to deal with nested attributes.
def attr_protected(*args)
options = args.extract_options!
role = options[:as] || :default
self._protected_attributes = protected_attributes_configs.dup
Array.wrap(role).each do |name|
self._protected_attributes[name] = self.protected_attributes(name) + args
end
self._active_authorizer = self._protected_attributes
end
# Specifies a white list of model attributes that can be set via
# mass-assignment.
#
# Like +attr_protected+, a role for the attributes is optional,
# if no role is provided then :default is used. A role can be defined by
# using the :as option.
#
# This is the opposite of the +attr_protected+ macro: Mass-assignment
# will only set attributes in this list, to assign to the rest of
# attributes you can use direct writer methods. This is meant to protect
# sensitive attributes from being overwritten by malicious users
# tampering with URLs or forms. If you'd rather start from an all-open
# default and restrict attributes as needed, have a look at
# +attr_protected+.
#
# class Customer
# include ActiveModel::MassAssignmentSecurity
#
# attr_accessor :name, :credit_rating
#
# attr_accessible :name
# attr_accessible :name, :credit_rating, :as => :admin
#
# def assign_attributes(values, options = {})
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
# send("#{k}=", v)
# end
# end
# end
#
# When using the :default role:
#
# customer = Customer.new
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default)
# customer.name # => "David"
# customer.credit_rating # => nil
#
# customer.credit_rating = "Average"
# customer.credit_rating # => "Average"
#
# And using the :admin role:
#
# customer = Customer.new
# customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin)
# customer.name # => "David"
# customer.credit_rating # => "Excellent"
#
# Note that using Hash#except or Hash#slice in place of
# +attr_accessible+ to sanitize attributes provides basically the same
# functionality, but it makes a bit tricky to deal with nested attributes.
def attr_accessible(*args)
options = args.extract_options!
role = options[:as] || :default
self._accessible_attributes = accessible_attributes_configs.dup
Array.wrap(role).each do |name|
self._accessible_attributes[name] = self.accessible_attributes(name) + args
end
self._active_authorizer = self._accessible_attributes
end
def protected_attributes(role = :default)
protected_attributes_configs[role]
end
def accessible_attributes(role = :default)
accessible_attributes_configs[role]
end
def active_authorizers
self._active_authorizer ||= protected_attributes_configs
end
alias active_authorizer active_authorizers
def attributes_protected_by_default
[]
end
def mass_assignment_sanitizer=(value)
self._mass_assignment_sanitizer = if value.is_a?(Symbol)
const_get(:"#{value.to_s.camelize}Sanitizer").new(self)
else
value
end
end
private
def protected_attributes_configs
self._protected_attributes ||= begin
Hash.new { |h,k| h[k] = BlackList.new(attributes_protected_by_default) }
end
end
def accessible_attributes_configs
self._accessible_attributes ||= begin
Hash.new { |h,k| h[k] = WhiteList.new }
end
end
end
protected
def sanitize_for_mass_assignment(attributes, role = nil)
_mass_assignment_sanitizer.sanitize(attributes, mass_assignment_authorizer(role))
end
def mass_assignment_authorizer(role)
self.class.active_authorizer[role || :default]
end
end
end
activemodel-3.2.16/lib/active_model/railtie.rb 0000644 0001750 0001750 00000000046 12247654710 020612 0 ustar ondrej ondrej require "active_model"
require "rails" activemodel-3.2.16/lib/active_model/observer_array.rb 0000644 0001750 0001750 00000010241 12247654710 022204 0 ustar ondrej ondrej require 'set'
module ActiveModel
# Stores the enabled/disabled state of individual observers for
# a particular model class.
class ObserverArray < Array
attr_reader :model_class
def initialize(model_class, *args)
@model_class = model_class
super(*args)
end
# Returns true if the given observer is disabled for the model class.
def disabled_for?(observer)
disabled_observers.include?(observer.class)
end
# Disables one or more observers. This supports multiple forms:
#
# ORM.observers.disable :user_observer
# # => disables the UserObserver
#
# User.observers.disable AuditTrail
# # => disables the AuditTrail observer for User notifications.
# # Other models will still notify the AuditTrail observer.
#
# ORM.observers.disable :observer_1, :observer_2
# # => disables Observer1 and Observer2 for all models.
#
# ORM.observers.disable :all
# # => disables all observers for all models.
#
# User.observers.disable :all do
# # all user observers are disabled for
# # just the duration of the block
# end
def disable(*observers, &block)
set_enablement(false, observers, &block)
end
# Enables one or more observers. This supports multiple forms:
#
# ORM.observers.enable :user_observer
# # => enables the UserObserver
#
# User.observers.enable AuditTrail
# # => enables the AuditTrail observer for User notifications.
# # Other models will not be affected (i.e. they will not
# # trigger notifications to AuditTrail if previously disabled)
#
# ORM.observers.enable :observer_1, :observer_2
# # => enables Observer1 and Observer2 for all models.
#
# ORM.observers.enable :all
# # => enables all observers for all models.
#
# User.observers.enable :all do
# # all user observers are enabled for
# # just the duration of the block
# end
#
# Note: all observers are enabled by default. This method is only
# useful when you have previously disabled one or more observers.
def enable(*observers, &block)
set_enablement(true, observers, &block)
end
protected
def disabled_observers
@disabled_observers ||= Set.new
end
def observer_class_for(observer)
return observer if observer.is_a?(Class)
if observer.respond_to?(:to_sym) # string/symbol
observer.to_s.camelize.constantize
else
raise ArgumentError, "#{observer} was not a class or a " +
"lowercase, underscored class name as expected."
end
end
def start_transaction
disabled_observer_stack.push(disabled_observers.dup)
each_subclass_array do |array|
array.start_transaction
end
end
def disabled_observer_stack
@disabled_observer_stack ||= []
end
def end_transaction
@disabled_observers = disabled_observer_stack.pop
each_subclass_array do |array|
array.end_transaction
end
end
def transaction
start_transaction
begin
yield
ensure
end_transaction
end
end
def each_subclass_array
model_class.descendants.each do |subclass|
yield subclass.observers
end
end
def set_enablement(enabled, observers)
if block_given?
transaction do
set_enablement(enabled, observers)
yield
end
else
observers = ActiveModel::Observer.descendants if observers == [:all]
observers.each do |obs|
klass = observer_class_for(obs)
unless klass < ActiveModel::Observer
raise ArgumentError.new("#{obs} does not refer to a valid observer")
end
if enabled
disabled_observers.delete(klass)
else
disabled_observers << klass
end
end
each_subclass_array do |array|
array.set_enablement(enabled, observers)
end
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/ 0000755 0001750 0001750 00000000000 12247654710 021151 5 ustar ondrej ondrej activemodel-3.2.16/lib/active_model/validations/exclusion.rb 0000644 0001750 0001750 00000007741 12247654710 023520 0 ustar ondrej ondrej require 'active_support/core_ext/range'
module ActiveModel
# == Active Model Exclusion Validator
module Validations
class ExclusionValidator < EachValidator
ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
"and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
unless [:include?, :call].any? { |method| delimiter.respond_to?(method) }
raise ArgumentError, ERROR_MESSAGE
end
end
def validate_each(record, attribute, value)
exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
if exclusions.send(inclusion_method(exclusions), value)
record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(:value => value))
end
end
private
def delimiter
@delimiter ||= options[:in] || options[:within]
end
# In Ruby 1.9 Range#include? on non-numeric ranges checks all possible
# values in the range for equality, so it may be slow for large ranges. The new
# Range#cover? uses the previous logic of comparing a value with the
# range endpoints.
def inclusion_method(enumerable)
enumerable.is_a?(Range) ? :cover? : :include?
end
end
module HelperMethods
# Validates that the value of the specified attribute is not in a particular
# enumerable object.
#
# class Person < ActiveRecord::Base
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %{value} is not allowed"
# validates_exclusion_of :password, :in => lambda { |p| [p.username, p.first_name] },
# :message => "should not be the same as your username or first name"
# end
#
# Configuration options:
# * :in - An enumerable object of items that the value shouldn't be
# part of. This can be supplied as a proc or lambda which returns an enumerable.
# If the enumerable is a range the test is performed with Range#cover?
# (backported in Active Support for 1.8), otherwise with include?.
# * :within - A synonym(or alias) for :in
# * :message - Specifies a custom error message (default is: "is reserved").
# * :allow_nil - If set to true, skips this validation if the attribute
# is +nil+ (default is +false+).
# * :allow_blank - If set to true, skips this validation if the
# attribute is blank (default is +false+).
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :if - Specifies a method, proc or string to call to determine if the
# validation should occur (e.g. :if => :allow_validation, or
# :if => Proc.new { |user| user.signup_step > 2 }). The method, proc
# or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine if
# the validation should not occur (e.g. :unless => :skip_validation,
# or :unless => Proc.new { |user| user.signup_step <= 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
def validates_exclusion_of(*attr_names)
validates_with ExclusionValidator, _merge_attributes(attr_names)
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/with.rb 0000644 0001750 0001750 00000011443 12247654710 022454 0 ustar ondrej ondrej module ActiveModel
module Validations
module HelperMethods
private
def _merge_attributes(attr_names)
options = attr_names.extract_options!
options.merge(:attributes => attr_names.flatten)
end
end
class WithValidator < EachValidator
def validate_each(record, attr, val)
method_name = options[:with]
if record.method(method_name).arity == 0
record.send method_name
else
record.send method_name, attr
end
end
end
module ClassMethods
# Passes the record off to the class or classes specified and allows them
# to add errors based on more complex conditions.
#
# class Person
# include ActiveModel::Validations
# validates_with MyValidator
# end
#
# class MyValidator < ActiveModel::Validator
# def validate(record)
# if some_complex_logic
# record.errors.add :base, "This record is invalid"
# end
# end
#
# private
# def some_complex_logic
# # ...
# end
# end
#
# You may also pass it multiple classes, like so:
#
# class Person
# include ActiveModel::Validations
# validates_with MyValidator, MyOtherValidator, :on => :create
# end
#
# Configuration options:
# * :on - Specifies when this validation is active
# (:create or :update
# * :if - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. :if => :allow_validation,
# or :if => Proc.new { |user| user.signup_step > 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine
# if the validation should not occur (e.g. :unless => :skip_validation,
# or :unless => Proc.new { |user| user.signup_step <= 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
# If you pass any additional configuration options, they will be passed
# to the class and available as options:
#
# class Person
# include ActiveModel::Validations
# validates_with MyValidator, :my_custom_key => "my custom value"
# end
#
# class MyValidator < ActiveModel::Validator
# def validate(record)
# options[:my_custom_key] # => "my custom value"
# end
# end
def validates_with(*args, &block)
options = args.extract_options!
args.each do |klass|
validator = klass.new(options, &block)
validator.setup(self) if validator.respond_to?(:setup)
if validator.respond_to?(:attributes) && !validator.attributes.empty?
validator.attributes.each do |attribute|
_validators[attribute.to_sym] << validator
end
else
_validators[nil] << validator
end
validate(validator, options)
end
end
end
# Passes the record off to the class or classes specified and allows them
# to add errors based on more complex conditions.
#
# class Person
# include ActiveModel::Validations
#
# validate :instance_validations
#
# def instance_validations
# validates_with MyValidator
# end
# end
#
# Please consult the class method documentation for more information on
# creating your own validator.
#
# You may also pass it multiple classes, like so:
#
# class Person
# include ActiveModel::Validations
#
# validate :instance_validations, :on => :create
#
# def instance_validations
# validates_with MyValidator, MyOtherValidator
# end
# end
#
# Standard configuration options (:on, :if and :unless), which are
# available on the class version of validates_with, should instead be
# placed on the validates method as these are applied and tested
# in the callback.
#
# If you pass any additional configuration options, they will be passed
# to the class and available as options, please refer to the
# class version of this method for more information.
def validates_with(*args, &block)
options = args.extract_options!
args.each do |klass|
validator = klass.new(options, &block)
validator.validate(self)
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/format.rb 0000644 0001750 0001750 00000011557 12247654710 022777 0 ustar ondrej ondrej module ActiveModel
# == Active Model Format Validator
module Validations
class FormatValidator < EachValidator
def validate_each(record, attribute, value)
if options[:with]
regexp = option_call(record, :with)
record_error(record, attribute, :with, value) if value.to_s !~ regexp
elsif options[:without]
regexp = option_call(record, :without)
record_error(record, attribute, :without, value) if value.to_s =~ regexp
end
end
def check_validity!
unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
end
check_options_validity(options, :with)
check_options_validity(options, :without)
end
private
def option_call(record, name)
option = options[name]
option.respond_to?(:call) ? option.call(record) : option
end
def record_error(record, attribute, name, value)
record.errors.add(attribute, :invalid, options.except(name).merge!(:value => value))
end
def check_options_validity(options, name)
option = options[name]
if option && !option.is_a?(Regexp) && !option.respond_to?(:call)
raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
end
end
end
module HelperMethods
# Validates whether the value of the specified attribute is of the correct form,
# going by the regular expression provided. You can require that the attribute
# matches the regular expression:
#
# class Person < ActiveRecord::Base
# validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
# end
#
# Alternatively, you can require that the specified attribute does _not_ match
# the regular expression:
#
# class Person < ActiveRecord::Base
# validates_format_of :email, :without => /NOSPAM/
# end
#
# You can also provide a proc or lambda which will determine the regular
# expression that will be used to validate the attribute.
#
# class Person < ActiveRecord::Base
# # Admin can have number as a first letter in their screen name
# validates_format_of :screen_name,
# :with => lambda{ |person| person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\Z/i : /\A[a-z][a-z0-9_\-]*\Z/i }
# end
#
# Note: use \A and \Z to match the start and end of the string,
# ^ and $ match the start/end of a line.
#
# You must pass either :with or :without as an option. In
# addition, both must be a regular expression or a proc or lambda, or else an
# exception will be raised.
#
# Configuration options:
# * :message - A custom error message (default is: "is invalid").
# * :allow_nil - If set to true, skips this validation if the attribute
# is +nil+ (default is +false+).
# * :allow_blank - If set to true, skips this validation if the
# attribute is blank (default is +false+).
# * :with - Regular expression that if the attribute matches will
# result in a successful validation. This can be provided as a proc or lambda
# returning regular expression which will be called at runtime.
# * :without - Regular expression that if the attribute does not match
# will result in a successful validation. This can be provided as a proc or
# lambda returning regular expression which will be called at runtime.
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :if - Specifies a method, proc or string to call to determine if the
# validation should occur (e.g. :if => :allow_validation, or
# :if => Proc.new { |user| user.signup_step > 2 }). The method, proc
# or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine if
# the validation should not occur (e.g. :unless => :skip_validation,
# or :unless => Proc.new { |user| user.signup_step <= 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
def validates_format_of(*attr_names)
validates_with FormatValidator, _merge_attributes(attr_names)
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/length.rb 0000644 0001750 0001750 00000013677 12247654710 022775 0 ustar ondrej ondrej require "active_support/core_ext/string/encoding"
module ActiveModel
# == Active Model Length Validator
module Validations
class LengthValidator < EachValidator
MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
def initialize(options)
if range = (options.delete(:in) || options.delete(:within))
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
options[:minimum], options[:maximum] = range.begin, range.end
options[:maximum] -= 1 if range.exclude_end?
end
super
end
def check_validity!
keys = CHECKS.keys & options.keys
if keys.empty?
raise ArgumentError, 'Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option.'
end
keys.each do |key|
value = options[key]
unless value.is_a?(Integer) && value >= 0
raise ArgumentError, ":#{key} must be a nonnegative Integer"
end
end
end
def validate_each(record, attribute, value)
value = tokenize(value)
value_length = value.respond_to?(:length) ? value.length : value.to_s.length
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
next if value_length.send(validity_check, check_value)
errors_options = options.except(*RESERVED_OPTIONS)
errors_options[:count] = check_value
default_message = options[MESSAGES[key]]
errors_options[:message] ||= default_message if default_message
record.errors.add(attribute, MESSAGES[key], errors_options)
end
end
private
def tokenize(value)
if value.kind_of?(String)
if options[:tokenizer]
options[:tokenizer].call(value)
elsif !value.encoding_aware?
value.mb_chars
end
end || value
end
end
module HelperMethods
# Validates that the specified attribute matches the length restrictions supplied.
# Only one option can be used at a time:
#
# class Person < ActiveRecord::Base
# validates_length_of :first_name, :maximum => 30
# validates_length_of :last_name, :maximum => 30, :message => "less than 30 if you don't mind"
# validates_length_of :fax, :in => 7..32, :allow_nil => true
# validates_length_of :phone, :in => 7..32, :allow_blank => true
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
# validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters"
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me."
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.",
# :tokenizer => lambda { |str| str.scan(/\w+/) }
# end
#
# Configuration options:
# * :minimum - The minimum size of the attribute.
# * :maximum - The maximum size of the attribute.
# * :is - The exact size of the attribute.
# * :within - A range specifying the minimum and maximum size of the
# attribute.
# * :in - A synonym(or alias) for :within.
# * :allow_nil - Attribute may be +nil+; skip validation.
# * :allow_blank - Attribute may be blank; skip validation.
# * :too_long - The error message if the attribute goes over the
# maximum (default is: "is too long (maximum is %{count} characters)").
# * :too_short - The error message if the attribute goes under the
# minimum (default is: "is too short (min is %{count} characters)").
# * :wrong_length - The error message if using the :is method
# and the attribute is the wrong size (default is: "is the wrong length
# should be %{count} characters)").
# * :message - The error message to use for a :minimum,
# :maximum, or :is violation. An alias of the appropriate
# too_long/too_short/wrong_length message.
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :if - Specifies a method, proc or string to call to determine if
# the validation should occur (e.g. :if => :allow_validation, or
# :if => Proc.new { |user| user.signup_step > 2 }). The method, proc
# or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine
# if the validation should not occur (e.g. :unless => :skip_validation,
# or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
# * :tokenizer - Specifies how to split up the attribute string.
# (e.g. :tokenizer => lambda {|str| str.scan(/\w+/)} to count words
# as in above example). Defaults to lambda{ |value| value.split(//) } which counts individual characters.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
def validates_length_of(*attr_names)
validates_with LengthValidator, _merge_attributes(attr_names)
end
alias_method :validates_size_of, :validates_length_of
end
end
end
activemodel-3.2.16/lib/active_model/validations/confirmation.rb 0000644 0001750 0001750 00000006015 12247654710 024170 0 ustar ondrej ondrej module ActiveModel
# == Active Model Confirmation Validator
module Validations
class ConfirmationValidator < EachValidator
def validate_each(record, attribute, value)
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
record.errors.add(attribute, :confirmation, options)
end
end
def setup(klass)
klass.send(:attr_accessor, *attributes.map do |attribute|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
end.compact)
end
end
module HelperMethods
# Encapsulates the pattern of wanting to validate a password or email
# address field with a confirmation. For example:
#
# Model:
# class Person < ActiveRecord::Base
# validates_confirmation_of :user_name, :password
# validates_confirmation_of :email_address,
# :message => "should match confirmation"
# end
#
# View:
# <%= password_field "person", "password" %>
# <%= password_field "person", "password_confirmation" %>
#
# The added +password_confirmation+ attribute is virtual; it exists only
# as an in-memory attribute for validating the password. To achieve this,
# the validation adds accessors to the model for the confirmation
# attribute.
#
# NOTE: This check is performed only if +password_confirmation+ is not
# +nil+, and by default only on save. To require confirmation, make sure
# to add a presence check for the confirmation attribute:
#
# validates_presence_of :password_confirmation, :if => :password_changed?
#
# Configuration options:
# * :message - A custom error message (default is: "doesn't match
# confirmation").
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :if - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. :if => :allow_validation,
# or :if => Proc.new { |user| user.signup_step > 2 }). The
# method, proc or string should return or evaluate to a true or false
# value.
# * :unless - Specifies a method, proc or string to call to
# determine if the validation should not occur (e.g.
# :unless => :skip_validation, or
# :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
def validates_confirmation_of(*attr_names)
validates_with ConfirmationValidator, _merge_attributes(attr_names)
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/numericality.rb 0000644 0001750 0001750 00000013406 12247654710 024207 0 ustar ondrej ondrej module ActiveModel
# == Active Model Numericality Validator
module Validations
class NumericalityValidator < EachValidator
CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
:equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
:odd => :odd?, :even => :even? }.freeze
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
def check_validity!
keys = CHECKS.keys - [:odd, :even]
options.slice(*keys).each do |option, value|
next if value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
end
end
def validate_each(record, attr_name, value)
before_type_cast = "#{attr_name}_before_type_cast"
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
raw_value ||= value
return if options[:allow_nil] && raw_value.nil?
unless value = parse_raw_value_as_a_number(raw_value)
record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
return
end
if options[:only_integer]
unless value = parse_raw_value_as_an_integer(raw_value)
record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
return
end
end
options.slice(*CHECKS.keys).each do |option, option_value|
case option
when :odd, :even
unless value.to_i.send(CHECKS[option])
record.errors.add(attr_name, option, filtered_options(value))
end
else
option_value = option_value.call(record) if option_value.is_a?(Proc)
option_value = record.send(option_value) if option_value.is_a?(Symbol)
unless value.send(CHECKS[option], option_value)
record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value))
end
end
end
end
protected
def parse_raw_value_as_a_number(raw_value)
case raw_value
when /\A0[xX]/
nil
else
begin
Kernel.Float(raw_value)
rescue ArgumentError, TypeError
nil
end
end
end
def parse_raw_value_as_an_integer(raw_value)
raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
end
def filtered_options(value)
options.except(*RESERVED_OPTIONS).merge!(:value => value)
end
end
module HelperMethods
# Validates whether the value of the specified attribute is numeric by trying to convert it to
# a float with Kernel.Float (if only_integer is false) or applying it to the regular expression
# /\A[\+\-]?\d+\Z/ (if only_integer is set to true).
#
# class Person < ActiveRecord::Base
# validates_numericality_of :value, :on => :create
# end
#
# Configuration options:
# * :message - A custom error message (default is: "is not a number").
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :only_integer - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
# * :allow_nil - Skip validation if attribute is +nil+ (default is
# +false+). Notice that for fixnum and float columns empty strings are
# converted to +nil+.
# * :greater_than - Specifies the value must be greater than the
# supplied value.
# * :greater_than_or_equal_to - Specifies the value must be greater
# than or equal the supplied value.
# * :equal_to - Specifies the value must be equal to the supplied value.
# * :less_than - Specifies the value must be less than the supplied
# value.
# * :less_than_or_equal_to - Specifies the value must be less than
# or equal the supplied value.
# * :odd - Specifies the value must be an odd number.
# * :even - Specifies the value must be an even number.
# * :if - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. :if => :allow_validation,
# or :if => Proc.new { |user| user.signup_step > 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine
# if the validation should not occur (e.g. :unless => :skip_validation,
# or :unless => Proc.new { |user| user.signup_step <= 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
#
# The following checks can also be supplied with a proc or a symbol which corresponds to a method:
# * :greater_than
# * :greater_than_or_equal_to
# * :equal_to
# * :less_than
# * :less_than_or_equal_to
#
# For example:
#
# class Person < ActiveRecord::Base
# validates_numericality_of :width, :less_than => Proc.new { |person| person.height }
# validates_numericality_of :width, :greater_than => :minimum_weight
# end
def validates_numericality_of(*attr_names)
validates_with NumericalityValidator, _merge_attributes(attr_names)
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/callbacks.rb 0000644 0001750 0001750 00000003520 12247654710 023415 0 ustar ondrej ondrej require 'active_support/callbacks'
module ActiveModel
module Validations
module Callbacks
# == Active Model Validation callbacks
#
# Provides an interface for any class to have before_validation and
# after_validation callbacks.
#
# First, extend ActiveModel::Callbacks from the class you are creating:
#
# class MyModel
# include ActiveModel::Validations::Callbacks
#
# before_validation :do_stuff_before_validation
# after_validation :do_stuff_after_validation
# end
#
# Like other before_* callbacks if before_validation returns false
# then valid? will not be called.
extend ActiveSupport::Concern
included do
include ActiveSupport::Callbacks
define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
end
module ClassMethods
def before_validation(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
options[:if] = Array.wrap(options[:if])
options[:if].unshift("self.validation_context == :#{options[:on]}")
end
set_callback(:validation, :before, *args, &block)
end
def after_validation(*args, &block)
options = args.extract_options!
options[:prepend] = true
options[:if] = Array.wrap(options[:if])
options[:if] << "!halted"
options[:if].unshift("self.validation_context == :#{options[:on]}") if options[:on]
set_callback(:validation, :after, *(args << options), &block)
end
end
protected
# Overwrite run validations to include callbacks.
def run_validations!
run_callbacks(:validation) { super }
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/presence.rb 0000644 0001750 0001750 00000004323 12247654710 023304 0 ustar ondrej ondrej require 'active_support/core_ext/object/blank'
module ActiveModel
# == Active Model Presence Validator
module Validations
class PresenceValidator < EachValidator
def validate(record)
record.errors.add_on_blank(attributes, options)
end
end
module HelperMethods
# Validates that the specified attributes are not blank (as defined by
# Object#blank?). Happens by default on save. Example:
#
# class Person < ActiveRecord::Base
# validates_presence_of :first_name
# end
#
# The first_name attribute must be in the object and it cannot be blank.
#
# If you want to validate the presence of a boolean field (where the real values
# are true and false), you will want to use validates_inclusion_of :field_name,
# :in => [true, false].
#
# This is due to the way Object#blank? handles boolean values:
# false.blank? # => true.
#
# Configuration options:
# * :message - A custom error message (default is: "can't be blank").
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :if - Specifies a method, proc or string to call to determine if
# the validation should occur (e.g. :if => :allow_validation, or
# :if => Proc.new { |user| user.signup_step > 2 }). The method, proc
# or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine
# if the validation should not occur (e.g. :unless => :skip_validation,
# or :unless => Proc.new { |user| user.signup_step <= 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/validates.rb 0000644 0001750 0001750 00000012523 12247654710 023455 0 ustar ondrej ondrej require 'active_support/core_ext/hash/slice'
module ActiveModel
# == Active Model validates method
module Validations
module ClassMethods
# This method is a shortcut to all default validators and any custom
# validator classes ending in 'Validator'. Note that Rails default
# validators can be overridden inside specific classes by creating
# custom validator classes in their place such as PresenceValidator.
#
# Examples of using the default rails validators:
#
# validates :terms, :acceptance => true
# validates :password, :confirmation => true
# validates :username, :exclusion => { :in => %w(admin superuser) }
# validates :email, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create }
# validates :age, :inclusion => { :in => 0..9 }
# validates :first_name, :length => { :maximum => 30 }
# validates :age, :numericality => true
# validates :username, :presence => true
# validates :username, :uniqueness => true
#
# The power of the +validates+ method comes when using custom validators
# and default validators in one call for a given attribute e.g.
#
# class EmailValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
# record.errors.add attribute, (options[:message] || "is not an email") unless
# value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
# end
# end
#
# class Person
# include ActiveModel::Validations
# attr_accessor :name, :email
#
# validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
# validates :email, :presence => true, :email => true
# end
#
# Validator classes may also exist within the class being validated
# allowing custom modules of validators to be included as needed e.g.
#
# class Film
# include ActiveModel::Validations
#
# class TitleValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
# record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i
# end
# end
#
# validates :name, :title => true
# end
#
# Additionally validator classes may be in another namespace and still used within any class.
#
# validates :name, :'film/title' => true
#
# The validators hash can also handle regular expressions, ranges,
# arrays and strings in shortcut form, e.g.
#
# validates :email, :format => /@/
# validates :gender, :inclusion => %w(male female)
# validates :password, :length => 6..20
#
# When using shortcut form, ranges and arrays are passed to your
# validator's initializer as +options[:in]+ while other types including
# regular expressions and strings are passed as +options[:with]+
#
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+ and +:strict+
# can be given to one specific validator, as a hash:
#
# validates :password, :presence => { :if => :password_required? }, :confirmation => true
#
# Or to all at the same time:
#
# validates :password, :presence => true, :confirmation => true, :if => :password_required?
#
def validates(*attributes)
defaults = attributes.extract_options!
validations = defaults.slice!(*_validates_default_keys)
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
defaults.merge!(:attributes => attributes)
validations.each do |key, options|
key = "#{key.to_s.camelize}Validator"
begin
validator = key.include?('::') ? key.constantize : const_get(key)
rescue NameError
raise ArgumentError, "Unknown validator: '#{key}'"
end
validates_with(validator, defaults.merge(_parse_validates_options(options)))
end
end
# This method is used to define validation that cannot be corrected by end
# user and is considered exceptional. So each validator defined with bang
# or :strict option set to true will always raise
# ActiveModel::StrictValidationFailed instead of adding error
# when validation fails.
# See validates for more information about validation itself.
def validates!(*attributes)
options = attributes.extract_options!
options[:strict] = true
validates(*(attributes << options))
end
protected
# When creating custom validators, it might be useful to be able to specify
# additional default keys. This can be done by overwriting this method.
def _validates_default_keys
[:if, :unless, :on, :allow_blank, :allow_nil , :strict]
end
def _parse_validates_options(options) #:nodoc:
case options
when TrueClass
{}
when Hash
options
when Range, Array
{ :in => options }
else
{ :with => options }
end
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/inclusion.rb 0000644 0001750 0001750 00000007446 12247654710 023514 0 ustar ondrej ondrej require 'active_support/core_ext/range'
module ActiveModel
# == Active Model Inclusion Validator
module Validations
class InclusionValidator < EachValidator
ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
"and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
unless [:include?, :call].any?{ |method| delimiter.respond_to?(method) }
raise ArgumentError, ERROR_MESSAGE
end
end
def validate_each(record, attribute, value)
exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
unless exclusions.send(inclusion_method(exclusions), value)
record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(:value => value))
end
end
private
def delimiter
@delimiter ||= options[:in] || options[:within]
end
# In Ruby 1.9 Range#include? on non-numeric ranges checks all possible
# values in the range for equality, so it may be slow for large ranges. The new
# Range#cover? uses the previous logic of comparing a value with the
# range endpoints.
def inclusion_method(enumerable)
enumerable.is_a?(Range) ? :cover? : :include?
end
end
module HelperMethods
# Validates whether the value of the specified attribute is available in a
# particular enumerable object.
#
# class Person < ActiveRecord::Base
# validates_inclusion_of :gender, :in => %w( m f )
# validates_inclusion_of :age, :in => 0..99
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %{value} is not included in the list"
# validates_inclusion_of :states, :in => lambda{ |person| STATES[person.country] }
# end
#
# Configuration options:
# * :in - An enumerable object of available items. This can be
# supplied as a proc or lambda which returns an enumerable. If the enumerable
# is a range the test is performed with Range#cover?
# (backported in Active Support for 1.8), otherwise with include?.
# * :within - A synonym(or alias) for :in
# * :message - Specifies a custom error message (default is: "is not
# included in the list").
# * :allow_nil - If set to true, skips this validation if the attribute
# is +nil+ (default is +false+).
# * :allow_blank - If set to true, skips this validation if the
# attribute is blank (default is +false+).
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :if - Specifies a method, proc or string to call to determine if
# the validation should occur (e.g. :if => :allow_validation, or
# :if => Proc.new { |user| user.signup_step > 2 }). The method, proc
# or string should return or evaluate to a true or false value.
# * :unless - Specifies a method, proc or string to call to determine
# if the validation should not occur (e.g. :unless => :skip_validation,
# or :unless => Proc.new { |user| user.signup_step <= 2 }). The method,
# proc or string should return or evaluate to a true or false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
def validates_inclusion_of(*attr_names)
validates_with InclusionValidator, _merge_attributes(attr_names)
end
end
end
end
activemodel-3.2.16/lib/active_model/validations/acceptance.rb 0000644 0001750 0001750 00000006037 12247654710 023572 0 ustar ondrej ondrej module ActiveModel
# == Active Model Acceptance Validator
module Validations
class AcceptanceValidator < EachValidator
def initialize(options)
super(options.reverse_merge(:allow_nil => true, :accept => "1"))
end
def validate_each(record, attribute, value)
unless value == options[:accept]
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
end
end
def setup(klass)
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
klass.send(:attr_reader, *attr_readers)
klass.send(:attr_writer, *attr_writers)
end
end
module HelperMethods
# Encapsulates the pattern of wanting to validate the acceptance of a
# terms of service check box (or similar agreement). Example:
#
# class Person < ActiveRecord::Base
# validates_acceptance_of :terms_of_service
# validates_acceptance_of :eula, :message => "must be abided"
# end
#
# If the database column does not exist, the +terms_of_service+ attribute
# is entirely virtual. This check is performed only if +terms_of_service+
# is not +nil+ and by default on save.
#
# Configuration options:
# * :message - A custom error message (default is: "must be
# accepted").
# * :on - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are :create
# and :update.
# * :allow_nil - Skip validation if attribute is +nil+ (default
# is true).
# * :accept - Specifies value that is considered accepted.
# The default value is a string "1", which makes it easy to relate to
# an HTML checkbox. This should be set to +true+ if you are validating
# a database column, since the attribute is typecast from "1" to +true+
# before validation.
# * :if - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. :if => :allow_validation,
# or :if => Proc.new { |user| user.signup_step > 2 }). The
# method, proc or string should return or evaluate to a true or false
# value.
# * :unless - Specifies a method, proc or string to call to
# determine if the validation should not occur (for example,
# :unless => :skip_validation, or
# :unless => Proc.new { |user| user.signup_step <= 2 }).
# The method, proc or string should return or evaluate to a true or
# false value.
# * :strict - Specifies whether validation should be strict.
# See ActiveModel::Validation#validates! for more information.
def validates_acceptance_of(*attr_names)
validates_with AcceptanceValidator, _merge_attributes(attr_names)
end
end
end
end
activemodel-3.2.16/lib/active_model/serializers/ 0000755 0001750 0001750 00000000000 12247654710 021170 5 ustar ondrej ondrej activemodel-3.2.16/lib/active_model/serializers/json.rb 0000644 0001750 0001750 00000010322 12247654710 022464 0 ustar ondrej ondrej require 'active_support/json'
require 'active_support/core_ext/class/attribute'
module ActiveModel
module Serializers
# == Active Model JSON Serializer
module JSON
extend ActiveSupport::Concern
include ActiveModel::Serialization
included do
extend ActiveModel::Naming
class_attribute :include_root_in_json
self.include_root_in_json = true
end
# Returns a hash representing the model. Some configuration can be
# passed through +options+.
#
# The option include_root_in_json controls the top-level behavior
# of +as_json+. If true (the default) +as_json+ will emit a single root
# node named after the object's type. For example:
#
# user = User.find(1)
# user.as_json
# # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true} }
#
# ActiveRecord::Base.include_root_in_json = false
# user.as_json
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true}
#
# This behavior can also be achieved by setting the :root option to +false+ as in:
#
# user = User.find(1)
# user.as_json(root: false)
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true}
#
# The remainder of the examples in this section assume include_root_in_json is set to
# false.
#
# Without any +options+, the returned Hash will include all the model's
# attributes. For example:
#
# user = User.find(1)
# user.as_json
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true}
#
# The :only and :except options can be used to limit the attributes
# included, and work similar to the +attributes+ method. For example:
#
# user.as_json(:only => [ :id, :name ])
# # => {"id": 1, "name": "Konata Izumi"}
#
# user.as_json(:except => [ :id, :created_at, :age ])
# # => {"name": "Konata Izumi", "awesome": true}
#
# To include the result of some method calls on the model use :methods:
#
# user.as_json(:methods => :permalink)
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true,
# "permalink": "1-konata-izumi"}
#
# To include associations use :include:
#
# user.as_json(:include => :posts)
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true,
# "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
# {"id": 2, author_id: 1, "title": "So I was thinking"}]}
#
# Second level and higher order associations work as well:
#
# user.as_json(:include => { :posts => {
# :include => { :comments => {
# :only => :body } },
# :only => :title } })
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
# "created_at": "2006/08/01", "awesome": true,
# "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
# "title": "Welcome to the weblog"},
# {"comments": [{"body": "Don't think too hard"}],
# "title": "So I was thinking"}]}
def as_json(options = nil)
root = include_root_in_json
root = options[:root] if options.try(:key?, :root)
if root
root = self.class.model_name.element if root == true
{ root => serializable_hash(options) }
else
serializable_hash(options)
end
end
def from_json(json, include_root=include_root_in_json)
hash = ActiveSupport::JSON.decode(json)
hash = hash.values.first if include_root
self.attributes = hash
self
end
end
end
end
activemodel-3.2.16/lib/active_model/serializers/xml.rb 0000644 0001750 0001750 00000014702 12247654710 022321 0 ustar ondrej ondrej require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/hash/conversions'
require 'active_support/core_ext/hash/slice'
module ActiveModel
module Serializers
# == Active Model XML Serializer
module Xml
extend ActiveSupport::Concern
include ActiveModel::Serialization
included do
extend ActiveModel::Naming
end
class Serializer #:nodoc:
class Attribute #:nodoc:
attr_reader :name, :value, :type
def initialize(name, serializable, value)
@name, @serializable = name, serializable
value = value.in_time_zone if value.respond_to?(:in_time_zone)
@value = value
@type = compute_type
end
def decorations
decorations = {}
decorations[:encoding] = 'base64' if type == :binary
decorations[:type] = (type == :string) ? nil : type
decorations[:nil] = true if value.nil?
decorations
end
protected
def compute_type
return if value.nil?
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
type ||= :string if value.respond_to?(:to_str)
type ||= :yaml
type
end
end
class MethodAttribute < Attribute #:nodoc:
end
attr_reader :options
def initialize(serializable, options = nil)
@serializable = serializable
@options = options ? options.dup : {}
end
def serializable_hash
@serializable.serializable_hash(@options.except(:include))
end
def serializable_collection
methods = Array.wrap(options[:methods]).map(&:to_s)
serializable_hash.map do |name, value|
name = name.to_s
if methods.include?(name)
self.class::MethodAttribute.new(name, @serializable, value)
else
self.class::Attribute.new(name, @serializable, value)
end
end
end
def serialize
require 'builder' unless defined? ::Builder
options[:indent] ||= 2
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
@builder = options[:builder]
@builder.instruct! unless options[:skip_instruct]
root = (options[:root] || @serializable.class.model_name.element).to_s
root = ActiveSupport::XmlMini.rename_key(root, options)
args = [root]
args << {:xmlns => options[:namespace]} if options[:namespace]
args << {:type => options[:type]} if options[:type] && !options[:skip_types]
@builder.tag!(*args) do
add_attributes_and_methods
add_includes
add_extra_behavior
add_procs
yield @builder if block_given?
end
end
private
def add_extra_behavior
end
def add_attributes_and_methods
serializable_collection.each do |attribute|
key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
ActiveSupport::XmlMini.to_tag(key, attribute.value,
options.merge(attribute.decorations))
end
end
def add_includes
@serializable.send(:serializable_add_includes, options) do |association, records, opts|
add_associations(association, records, opts)
end
end
# TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
def add_associations(association, records, opts)
merged_options = opts.merge(options.slice(:builder, :indent))
merged_options[:skip_instruct] = true
if records.is_a?(Enumerable)
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
type = options[:skip_types] ? { } : {:type => "array"}
association_name = association.to_s.singularize
merged_options[:root] = association_name
if records.empty?
@builder.tag!(tag, type)
else
@builder.tag!(tag, type) do
records.each do |record|
if options[:skip_types]
record_type = {}
else
record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
record_type = {:type => record_class}
end
record.to_xml merged_options.merge(record_type)
end
end
end
else
merged_options[:root] = association.to_s
unless records.class.to_s.underscore == association.to_s
merged_options[:type] = records.class.name
end
records.to_xml merged_options
end
end
def add_procs
if procs = options.delete(:procs)
Array.wrap(procs).each do |proc|
if proc.arity == 1
proc.call(options)
else
proc.call(options, @serializable)
end
end
end
end
end
# Returns XML representing the model. Configuration can be
# passed through +options+.
#
# Without any +options+, the returned XML string will include all the model's
# attributes. For example:
#
# user = User.find(1)
# user.to_xml
#
#
#
# 1
# David
# 16
# 2011-01-30T22:29:23Z
#
#
# The :only and :except options can be used to limit the attributes
# included, and work similar to the +attributes+ method.
#
# To include the result of some method calls on the model use :methods.
#
# To include associations use :include.
#
# For further documentation see activerecord/lib/active_record/serializers/xml_serializer.xml.
def to_xml(options = {}, &block)
Serializer.new(self, options).serialize(&block)
end
def from_xml(xml)
self.attributes = Hash.from_xml(xml).values.first
self
end
end
end
end
activemodel-3.2.16/lib/active_model/test_case.rb 0000644 0001750 0001750 00000000477 12247654710 021143 0 ustar ondrej ondrej module ActiveModel #:nodoc:
class TestCase < ActiveSupport::TestCase #:nodoc:
def with_kcode(kcode)
if RUBY_VERSION < '1.9'
orig_kcode, $KCODE = $KCODE, kcode
begin
yield
ensure
$KCODE = orig_kcode
end
else
yield
end
end
end
end
activemodel-3.2.16/lib/active_model/observing.rb 0000644 0001750 0001750 00000017743 12247654710 021173 0 ustar ondrej ondrej require 'singleton'
require 'active_model/observer_array'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/enumerable'
require 'active_support/descendants_tracker'
module ActiveModel
module Observing
extend ActiveSupport::Concern
included do
extend ActiveSupport::DescendantsTracker
end
module ClassMethods
# == Active Model Observers Activation
#
# Activates the observers assigned. Examples:
#
# class ORM
# include ActiveModel::Observing
# end
#
# # Calls PersonObserver.instance
# ORM.observers = :person_observer
#
# # Calls Cacher.instance and GarbageCollector.instance
# ORM.observers = :cacher, :garbage_collector
#
# # Same as above, just using explicit class references
# ORM.observers = Cacher, GarbageCollector
#
# Note: Setting this does not instantiate the observers yet.
# +instantiate_observers+ is called during startup, and before
# each development request.
def observers=(*values)
observers.replace(values.flatten)
end
# Gets an array of observers observing this model.
# The array also provides +enable+ and +disable+ methods
# that allow you to selectively enable and disable observers.
# (see ActiveModel::ObserverArray.enable and
# ActiveModel::ObserverArray.disable for more on this)
def observers
@observers ||= ObserverArray.new(self)
end
# Gets the current observer instances.
def observer_instances
@observer_instances ||= []
end
# Instantiate the global observers.
def instantiate_observers
observers.each { |o| instantiate_observer(o) }
end
# Add a new observer to the pool.
# The new observer needs to respond to 'update', otherwise it
# raises an +ArgumentError+ exception.
def add_observer(observer)
unless observer.respond_to? :update
raise ArgumentError, "observer needs to respond to `update'"
end
observer_instances << observer
end
# Notify list of observers of a change.
def notify_observers(*arg)
observer_instances.each { |observer| observer.update(*arg) }
end
# Total number of observers.
def count_observers
observer_instances.size
end
protected
def instantiate_observer(observer) #:nodoc:
# string/symbol
if observer.respond_to?(:to_sym)
observer.to_s.camelize.constantize.instance
elsif observer.respond_to?(:instance)
observer.instance
else
raise ArgumentError,
"#{observer} must be a lowercase, underscored class name (or an " +
"instance of the class itself) responding to the instance " +
"method. Example: Person.observers = :big_brother # calls " +
"BigBrother.instance"
end
end
# Notify observers when the observed class is subclassed.
def inherited(subclass)
super
notify_observers :observed_class_inherited, subclass
end
end
private
# Fires notifications to model's observers
#
# def save
# notify_observers(:before_save)
# ...
# notify_observers(:after_save)
# end
def notify_observers(method)
self.class.notify_observers(method, self)
end
end
# == Active Model Observers
#
# Observer classes respond to life cycle callbacks to implement trigger-like
# behavior outside the original class. This is a great way to reduce the
# clutter that normally comes when the model class is burdened with
# functionality that doesn't pertain to the core responsibility of the
# class. Example:
#
# class CommentObserver < ActiveModel::Observer
# def after_save(comment)
# Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
# end
# end
#
# This Observer sends an email when a Comment#save is finished.
#
# class ContactObserver < ActiveModel::Observer
# def after_create(contact)
# contact.logger.info('New contact added!')
# end
#
# def after_destroy(contact)
# contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
# end
# end
#
# This Observer uses logger to log when specific callbacks are triggered.
#
# == Observing a class that can't be inferred
#
# Observers will by default be mapped to the class with which they share a
# name. So CommentObserver will be tied to observing Comment, ProductManagerObserver
# to ProductManager, and so on. If you want to name your observer differently than
# the class you're interested in observing, you can use the Observer.observe
# class method which takes either the concrete class (Product) or a symbol for that
# class (:product):
#
# class AuditObserver < ActiveModel::Observer
# observe :account
#
# def after_update(account)
# AuditTrail.new(account, "UPDATED")
# end
# end
#
# If the audit observer needs to watch more than one kind of object, this can be
# specified with multiple arguments:
#
# class AuditObserver < ActiveModel::Observer
# observe :account, :balance
#
# def after_update(record)
# AuditTrail.new(record, "UPDATED")
# end
# end
#
# The AuditObserver will now act on both updates to Account and Balance by treating
# them both as records.
#
# If you're using an Observer in a Rails application with Active Record, be sure to
# read about the necessary configuration in the documentation for
# ActiveRecord::Observer.
#
class Observer
include Singleton
extend ActiveSupport::DescendantsTracker
class << self
# Attaches the observer to the supplied model classes.
def observe(*models)
models.flatten!
models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
redefine_method(:observed_classes) { models }
end
# Returns an array of Classes to observe.
#
# You can override this instead of using the +observe+ helper.
#
# class AuditObserver < ActiveModel::Observer
# def self.observed_classes
# [Account, Balance]
# end
# end
def observed_classes
Array.wrap(observed_class)
end
# The class observed by default is inferred from the observer's class name:
# assert_equal Person, PersonObserver.observed_class
def observed_class
if observed_class_name = name[/(.*)Observer/, 1]
observed_class_name.constantize
else
nil
end
end
end
# Start observing the declared classes and their subclasses.
def initialize
observed_classes.each { |klass| add_observer!(klass) }
end
def observed_classes #:nodoc:
self.class.observed_classes
end
# Send observed_method(object) if the method exists and
# the observer is enabled for the given object's class.
def update(observed_method, object, &block) #:nodoc:
return unless respond_to?(observed_method)
return if disabled_for?(object)
send(observed_method, object, &block)
end
# Special method sent by the observed class when it is inherited.
# Passes the new subclass.
def observed_class_inherited(subclass) #:nodoc:
self.class.observe(observed_classes + [subclass])
add_observer!(subclass)
end
protected
def add_observer!(klass) #:nodoc:
klass.add_observer(self)
end
def disabled_for?(object)
klass = object.class
return false unless klass.respond_to?(:observers)
klass.observers.disabled_for?(self)
end
end
end
activemodel-3.2.16/lib/active_model/serialization.rb 0000644 0001750 0001750 00000010320 12247654710 022032 0 ustar ondrej ondrej require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/array/wrap'
module ActiveModel
# == Active Model Serialization
#
# Provides a basic serialization to a serializable_hash for your object.
#
# A minimal implementation could be:
#
# class Person
#
# include ActiveModel::Serialization
#
# attr_accessor :name
#
# def attributes
# {'name' => name}
# end
#
# end
#
# Which would provide you with:
#
# person = Person.new
# person.serializable_hash # => {"name"=>nil}
# person.name = "Bob"
# person.serializable_hash # => {"name"=>"Bob"}
#
# You need to declare some sort of attributes hash which contains the attributes
# you want to serialize and their current value.
#
# Most of the time though, you will want to include the JSON or XML
# serializations. Both of these modules automatically include the
# ActiveModel::Serialization module, so there is no need to explicitly
# include it.
#
# So a minimal implementation including XML and JSON would be:
#
# class Person
#
# include ActiveModel::Serializers::JSON
# include ActiveModel::Serializers::Xml
#
# attr_accessor :name
#
# def attributes
# {'name' => name}
# end
#
# end
#
# Which would provide you with:
#
# person = Person.new
# person.serializable_hash # => {"name"=>nil}
# person.as_json # => {"name"=>nil}
# person.to_json # => "{\"name\":null}"
# person.to_xml # => "\n {"name"=>"Bob"}
# person.as_json # => {"name"=>"Bob"}
# person.to_json # => "{\"name\":\"Bob\"}"
# person.to_xml # => "\n:only, :except and :methods .
module Serialization
def serializable_hash(options = nil)
options ||= {}
attribute_names = attributes.keys.sort
if only = options[:only]
attribute_names &= Array.wrap(only).map(&:to_s)
elsif except = options[:except]
attribute_names -= Array.wrap(except).map(&:to_s)
end
hash = {}
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) }
method_names.each { |n| hash[n] = send(n) }
serializable_add_includes(options) do |association, records, opts|
hash[association] = if records.is_a?(Enumerable)
records.map { |a| a.serializable_hash(opts) }
else
records.serializable_hash(opts)
end
end
hash
end
private
# Hook method defining how an attribute value should be retrieved for
# serialization. By default this is assumed to be an instance named after
# the attribute. Override this method in subclasses should you need to
# retrieve the value for a given attribute differently:
#
# class MyClass
# include ActiveModel::Validations
#
# def initialize(data = {})
# @data = data
# end
#
# def read_attribute_for_serialization(key)
# @data[key]
# end
# end
#
alias :read_attribute_for_serialization :send
# Add associations specified via the :include option.
#
# Expects a block that takes as arguments:
# +association+ - name of the association
# +records+ - the association record(s) to be serialized
# +opts+ - options for the association records
def serializable_add_includes(options = {}) #:nodoc:
return unless include = options[:include]
unless include.is_a?(Hash)
include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
end
include.each do |association, opts|
if records = send(association)
yield association, records, opts
end
end
end
end
end
activemodel-3.2.16/lib/active_model/conversion.rb 0000644 0001750 0001750 00000004442 12247654710 021352 0 ustar ondrej ondrej require 'active_support/concern'
require 'active_support/inflector'
module ActiveModel
# == Active Model Conversions
#
# Handles default conversions: to_model, to_key, to_param, and to_partial_path.
#
# Let's take for example this non-persisted object.
#
# class ContactMessage
# include ActiveModel::Conversion
#
# # ContactMessage are never persisted in the DB
# def persisted?
# false
# end
# end
#
# cm = ContactMessage.new
# cm.to_model == self # => true
# cm.to_key # => nil
# cm.to_param # => nil
# cm.to_path # => "contact_messages/contact_message"
#
module Conversion
extend ActiveSupport::Concern
# If your object is already designed to implement all of the Active Model
# you can use the default :to_model implementation, which simply
# returns self.
#
# If your model does not act like an Active Model object, then you should
# define :to_model yourself returning a proxy object that wraps
# your object with Active Model compliant methods.
def to_model
self
end
# Returns an Enumerable of all key attributes if any is set, regardless
# if the object is persisted or not.
#
# Note the default implementation uses persisted? just because all objects
# in Ruby 1.8.x responds to :id.
def to_key
persisted? ? [id] : nil
end
# Returns a string representing the object's key suitable for use in URLs,
# or nil if persisted? is false.
def to_param
persisted? ? to_key.join('-') : nil
end
# Returns a string identifying the path associated with the object.
# ActionPack uses this to find a suitable partial to represent the object.
def to_partial_path
self.class._to_partial_path
end
module ClassMethods #:nodoc:
# Provide a class level cache for the to_path. This is an
# internal method and should not be accessed directly.
def _to_partial_path #:nodoc:
@_to_partial_path ||= begin
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self))
collection = ActiveSupport::Inflector.tableize(self)
"#{collection}/#{element}".freeze
end
end
end
end
end
activemodel-3.2.16/lib/active_model/errors.rb 0000644 0001750 0001750 00000027475 12247654710 020514 0 ustar ondrej ondrej # -*- coding: utf-8 -*-
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/ordered_hash'
module ActiveModel
# == Active Model Errors
#
# Provides a modified +OrderedHash+ that you can include in your object
# for handling error messages and interacting with Action Pack helpers.
#
# A minimal implementation could be:
#
# class Person
#
# # Required dependency for ActiveModel::Errors
# extend ActiveModel::Naming
#
# def initialize
# @errors = ActiveModel::Errors.new(self)
# end
#
# attr_accessor :name
# attr_reader :errors
#
# def validate!
# errors.add(:name, "can not be nil") if name == nil
# end
#
# # The following methods are needed to be minimally implemented
#
# def read_attribute_for_validation(attr)
# send(attr)
# end
#
# def Person.human_attribute_name(attr, options = {})
# attr
# end
#
# def Person.lookup_ancestors
# [self]
# end
#
# end
#
# The last three methods are required in your object for Errors to be
# able to generate error messages correctly and also handle multiple
# languages. Of course, if you extend your object with ActiveModel::Translation
# you will not need to implement the last two. Likewise, using
# ActiveModel::Validations will handle the validation related methods
# for you.
#
# The above allows you to do:
#
# p = Person.new
# p.validate! # => ["can not be nil"]
# p.errors.full_messages # => ["name can not be nil"]
# # etc..
class Errors
include Enumerable
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
attr_reader :messages
# Pass in the instance of the object that is using the errors object.
#
# class Person
# def initialize
# @errors = ActiveModel::Errors.new(self)
# end
# end
def initialize(base)
@base = base
@messages = ActiveSupport::OrderedHash.new
end
def initialize_dup(other)
@messages = other.messages.dup
end
# Backport dup from 1.9 so that #initialize_dup gets called
unless Object.respond_to?(:initialize_dup, true)
def dup # :nodoc:
copy = super
copy.initialize_dup(self)
copy
end
end
# Clear the messages
def clear
messages.clear
end
# Do the error messages include an error with key +error+?
def include?(error)
(v = messages[error]) && v.any?
end
alias :has_key? :include?
# Get messages for +key+
def get(key)
messages[key]
end
# Set messages for +key+ to +value+
def set(key, value)
messages[key] = value
end
# Delete messages for +key+
def delete(key)
messages.delete(key)
end
# When passed a symbol or a name of a method, returns an array of errors
# for the method.
#
# p.errors[:name] # => ["can not be nil"]
# p.errors['name'] # => ["can not be nil"]
def [](attribute)
get(attribute.to_sym) || set(attribute.to_sym, [])
end
# Adds to the supplied attribute the supplied error message.
#
# p.errors[:name] = "must be set"
# p.errors[:name] # => ['must be set']
def []=(attribute, error)
self[attribute] << error
end
# Iterates through each error key, value pair in the error messages hash.
# Yields the attribute and the error for that attribute. If the attribute
# has more than one error message, yields once for each error message.
#
# p.errors.add(:name, "can't be blank")
# p.errors.each do |attribute, errors_array|
# # Will yield :name and "can't be blank"
# end
#
# p.errors.add(:name, "must be specified")
# p.errors.each do |attribute, errors_array|
# # Will yield :name and "can't be blank"
# # then yield :name and "must be specified"
# end
def each
messages.each_key do |attribute|
self[attribute].each { |error| yield attribute, error }
end
end
# Returns the number of error messages.
#
# p.errors.add(:name, "can't be blank")
# p.errors.size # => 1
# p.errors.add(:name, "must be specified")
# p.errors.size # => 2
def size
values.flatten.size
end
# Returns all message values
def values
messages.values
end
# Returns all message keys
def keys
messages.keys
end
# Returns an array of error messages, with the attribute name included
#
# p.errors.add(:name, "can't be blank")
# p.errors.add(:name, "must be specified")
# p.errors.to_a # => ["name can't be blank", "name must be specified"]
def to_a
full_messages
end
# Returns the number of error messages.
# p.errors.add(:name, "can't be blank")
# p.errors.count # => 1
# p.errors.add(:name, "must be specified")
# p.errors.count # => 2
def count
to_a.size
end
# Returns true if no errors are found, false otherwise.
# If the error message is a string it can be empty.
def empty?
all? { |k, v| v && v.empty? && !v.is_a?(String) }
end
alias_method :blank?, :empty?
# Returns an xml formatted representation of the Errors hash.
#
# p.errors.add(:name, "can't be blank")
# p.errors.add(:name, "must be specified")
# p.errors.to_xml
# # =>
# #
# #
# # name can't be blank
# # name must be specified
# #
def to_xml(options={})
to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
end
# Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
def as_json(options=nil)
to_hash
end
def to_hash
messages.dup
end
# Adds +message+ to the error messages on +attribute+. More than one error can be added to the same
# +attribute+.
# If no +message+ is supplied, :invalid is assumed.
#
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
# If +message+ is a proc, it will be called, allowing for things like Time.now to be used within an error.
def add(attribute, message = nil, options = {})
message = normalize_message(attribute, message, options)
if options[:strict]
raise ActiveModel::StrictValidationFailed, full_message(attribute, message)
end
self[attribute] << message
end
# Will add an error message to each of the attributes in +attributes+ that is empty.
def add_on_empty(attributes, options = {})
[attributes].flatten.each do |attribute|
value = @base.send(:read_attribute_for_validation, attribute)
is_empty = value.respond_to?(:empty?) ? value.empty? : false
add(attribute, :empty, options) if value.nil? || is_empty
end
end
# Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
def add_on_blank(attributes, options = {})
[attributes].flatten.each do |attribute|
value = @base.send(:read_attribute_for_validation, attribute)
add(attribute, :blank, options) if value.blank?
end
end
# Returns true if an error on the attribute with the given message is present, false otherwise.
# +message+ is treated the same as for +add+.
# p.errors.add :name, :blank
# p.errors.added? :name, :blank # => true
def added?(attribute, message = nil, options = {})
message = normalize_message(attribute, message, options)
self[attribute].include? message
end
# Returns all the full error messages in an array.
#
# class Company
# validates_presence_of :name, :address, :email
# validates_length_of :name, :in => 5..30
# end
#
# company = Company.create(:address => '123 First St.')
# company.errors.full_messages # =>
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
def full_messages
map { |attribute, message| full_message(attribute, message) }
end
# Returns a full message for a given attribute.
#
# company.errors.full_message(:name, "is invalid") # =>
# "Name is invalid"
def full_message(attribute, message)
return message if attribute == :base
attr_name = attribute.to_s.gsub('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
I18n.t(:"errors.format", {
:default => "%{attribute} %{message}",
:attribute => attr_name,
:message => message
})
end
# Translates an error message in its default scope
# (activemodel.errors.messages).
#
# Error messages are first looked up in models.MODEL.attributes.ATTRIBUTE.MESSAGE,
# if it's not there, it's looked up in models.MODEL.MESSAGE and if that is not
# there also, it returns the translation of the default message
# (e.g. activemodel.errors.messages.MESSAGE). The translated model name,
# translated attribute name and the value are available for interpolation.
#
# When using inheritance in your models, it will check all the inherited
# models too, but only if the model itself hasn't been found. Say you have
# class Admin < User; end and you wanted the translation for
# the :blank error message for the title attribute,
# it looks for these translations:
#
# * activemodel.errors.models.admin.attributes.title.blank
# * activemodel.errors.models.admin.blank
# * activemodel.errors.models.user.attributes.title.blank
# * activemodel.errors.models.user.blank
# * any default you provided through the +options+ hash (in the activemodel.errors scope)
# * activemodel.errors.messages.blank
# * errors.attributes.title.blank
# * errors.messages.blank
#
def generate_message(attribute, type = :invalid, options = {})
type = options.delete(:message) if options[:message].is_a?(Symbol)
if @base.class.respond_to?(:i18n_scope)
defaults = @base.class.lookup_ancestors.map do |klass|
[ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
:"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
end
else
defaults = []
end
defaults << options.delete(:message)
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
defaults << :"errors.attributes.#{attribute}.#{type}"
defaults << :"errors.messages.#{type}"
defaults.compact!
defaults.flatten!
key = defaults.shift
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
options = {
:default => defaults,
:model => @base.class.model_name.human,
:attribute => @base.class.human_attribute_name(attribute),
:value => value
}.merge(options)
I18n.translate(key, options)
end
private
def normalize_message(attribute, message, options)
message ||= :invalid
if message.is_a?(Symbol)
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
elsif message.is_a?(Proc)
message.call
else
message
end
end
end
class StrictValidationFailed < StandardError
end
end
activemodel-3.2.16/lib/active_model/callbacks.rb 0000644 0001750 0001750 00000010714 12247654710 021103 0 ustar ondrej ondrej require 'active_support/core_ext/array/wrap'
require 'active_support/callbacks'
module ActiveModel
# == Active Model Callbacks
#
# Provides an interface for any class to have Active Record like callbacks.
#
# Like the Active Record methods, the callback chain is aborted as soon as
# one of the methods in the chain returns false.
#
# First, extend ActiveModel::Callbacks from the class you are creating:
#
# class MyModel
# extend ActiveModel::Callbacks
# end
#
# Then define a list of methods that you want callbacks attached to:
#
# define_model_callbacks :create, :update
#
# This will provide all three standard callbacks (before, around and after) for
# both the :create and :update methods. To implement, you need to wrap the methods
# you want callbacks on in a block so that the callbacks get a chance to fire:
#
# def create
# run_callbacks :create do
# # Your create action methods here
# end
# end
#
# Then in your class, you can use the +before_create+, +after_create+ and +around_create+
# methods, just as you would in an Active Record module.
#
# before_create :action_before_create
#
# def action_before_create
# # Your code here
# end
#
# You can choose not to have all three callbacks by passing a hash to the
# define_model_callbacks method.
#
# define_model_callbacks :create, :only => [:after, :before]
#
# Would only create the after_create and before_create callback methods in your
# class.
module Callbacks
def self.extended(base)
base.class_eval do
include ActiveSupport::Callbacks
end
end
# define_model_callbacks accepts the same options define_callbacks does, in case
# you want to overwrite a default. Besides that, it also accepts an :only option,
# where you can choose if you want all types (before, around or after) or just some.
#
# define_model_callbacks :initializer, :only => :after
#
# Note, the :only => hash will apply to all callbacks defined on
# that method call. To get around this you can call the define_model_callbacks
# method as many times as you need.
#
# define_model_callbacks :create, :only => :after
# define_model_callbacks :update, :only => :before
# define_model_callbacks :destroy, :only => :around
#
# Would create +after_create+, +before_update+ and +around_destroy+ methods only.
#
# You can pass in a class to before_, after_ and around_, in which
# case the callback will call that class's _ method passing the object
# that the callback is being called on.
#
# class MyModel
# extend ActiveModel::Callbacks
# define_model_callbacks :create
#
# before_create AnotherClass
# end
#
# class AnotherClass
# def self.before_create( obj )
# # obj is the MyModel instance that the callback is being called on
# end
# end
#
def define_model_callbacks(*callbacks)
options = callbacks.extract_options!
options = {
:terminator => "result == false",
:scope => [:kind, :name],
:only => [:before, :around, :after]
}.merge(options)
types = Array.wrap(options.delete(:only))
callbacks.each do |callback|
define_callbacks(callback, options)
types.each do |type|
send("_define_#{type}_model_callback", self, callback)
end
end
end
def _define_before_model_callback(klass, callback) #:nodoc:
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.before_#{callback}(*args, &block)
set_callback(:#{callback}, :before, *args, &block)
end
CALLBACK
end
def _define_around_model_callback(klass, callback) #:nodoc:
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.around_#{callback}(*args, &block)
set_callback(:#{callback}, :around, *args, &block)
end
CALLBACK
end
def _define_after_model_callback(klass, callback) #:nodoc:
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.after_#{callback}(*args, &block)
options = args.extract_options!
options[:prepend] = true
options[:if] = Array.wrap(options[:if]) << "!halted && value != false"
set_callback(:#{callback}, :after, *(args << options), &block)
end
CALLBACK
end
end
end
activemodel-3.2.16/lib/active_model/attribute_methods.rb 0000644 0001750 0001750 00000040300 12247654710 022704 0 ustar ondrej ondrej require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/class/attribute'
require 'active_support/deprecation'
module ActiveModel
class MissingAttributeError < NoMethodError
end
# == Active Model Attribute Methods
#
# ActiveModel::AttributeMethods 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 to:
#
# * include ActiveModel::AttributeMethods in your object
# * Call each Attribute Method module method you want to add, such as
# attribute_method_suffix or attribute_method_prefix
# * Call define_attribute_methods 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
#
# Note that whenever you include ActiveModel::AttributeMethods in your class,
# it requires you to implement an attributes method which returns a hash
# with each attribute name in your model as hash key and the attribute value as
# hash value.
#
# Hash keys must be strings.
#
module AttributeMethods
extend ActiveSupport::Concern
NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
included do
class_attribute :attribute_method_matchers, :instance_writer => false
self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
end
module ClassMethods
def define_attr_method(name, value=nil, deprecation_warning = true, &block) #:nodoc:
# This deprecation_warning param is for internal use so that we can silence
# the warning from Active Record, because we are implementing more specific
# messages there instead.
#
# It doesn't apply to the original_#{name} method as we want to warn if
# people are calling that regardless.
if deprecation_warning
ActiveSupport::Deprecation.warn("define_attr_method is deprecated and will be removed without replacement.")
end
sing = singleton_class
sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
remove_possible_method :'original_#{name}'
remove_possible_method :'_original_#{name}'
alias_method :'_original_#{name}', :'#{name}'
define_method :'original_#{name}' do
ActiveSupport::Deprecation.warn(
"This method is generated by ActiveModel::AttributeMethods::ClassMethods#define_attr_method, " \
"which is deprecated and will be removed."
)
send(:'_original_#{name}')
end
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 =~ 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
# 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
def attribute_method_prefix(*prefixes)
self.attribute_method_matchers += prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix }
undefine_attribute_methods
end
# 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
def attribute_method_suffix(*suffixes)
self.attribute_method_matchers += suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix }
undefine_attribute_methods
end
# 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'
def attribute_method_affix(*affixes)
self.attribute_method_matchers += affixes.map { |affix| AttributeMethodMatcher.new :prefix => affix[:prefix], :suffix => affix[:suffix] }
undefine_attribute_methods
end
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
define_optimized_call self, matcher_new, matcher_old
end
end
# 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
def define_attribute_methods(attr_names)
attr_names.each { |attr_name| define_attribute_method(attr_name) }
end
def define_attribute_method(attr_name)
attribute_method_matchers.each do |matcher|
method_name = matcher.method_name(attr_name)
unless instance_method_already_implemented?(method_name)
generate_method = "define_method_#{matcher.method_missing_target}"
if respond_to?(generate_method, true)
send(generate_method, attr_name)
else
define_optimized_call generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s
end
end
end
attribute_method_matchers_cache.clear
end
# Removes all the previously dynamically defined methods from the class
def undefine_attribute_methods
generated_attribute_methods.module_eval do
instance_methods.each { |m| undef_method(m) }
end
attribute_method_matchers_cache.clear
end
# Returns true if the attribute methods defined have been generated.
def generated_attribute_methods #:nodoc:
@generated_attribute_methods ||= begin
mod = Module.new
include mod
mod
end
end
protected
def instance_method_already_implemented?(method_name)
generated_attribute_methods.method_defined?(method_name)
end
private
# The methods +method_missing+ and +respond_to?+ of this module are
# invoked often in a typical rails, both of which invoke the method
# +match_attribute_method?+. The latter method iterates through an
# array doing regular expression matches, which results in a lot of
# object creations. Most of the times it returns a +nil+ match. As the
# match result is always the same given a +method_name+, this cache is
# used to alleviate the GC, which ultimately also speeds up the app
# significantly (in our case our test suite finishes 10% faster with
# this cache).
def attribute_method_matchers_cache #:nodoc:
@attribute_method_matchers_cache ||= {}
end
def attribute_method_matcher(method_name) #:nodoc:
if attribute_method_matchers_cache.key?(method_name)
attribute_method_matchers_cache[method_name]
else
# Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix
# will match every time.
matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
match = nil
matchers.detect { |method| match = method.match(method_name) }
attribute_method_matchers_cache[method_name] = match
end
end
# Define a method `name` in `mod` that dispatches to `send`
# using the given `extra` args. This fallbacks `define_method`
# and `send` if the given names cannot be compiled.
def define_optimized_call(mod, name, send, *extra) #:nodoc:
if name =~ NAME_COMPILABLE_REGEXP
defn = "def #{name}(*args)"
else
defn = "define_method(:'#{name}') do |*args|"
end
extra = (extra.map(&:inspect) << "*args").join(", ")
if send =~ CALL_COMPILABLE_REGEXP
target = "#{send}(#{extra})"
else
target = "send(:'#{send}', #{extra})"
end
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
#{defn}
#{target}
end
RUBY
end
class AttributeMethodMatcher
attr_reader :prefix, :suffix, :method_missing_target
AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name)
def initialize(options = {})
options.symbolize_keys!
if options[:prefix] == '' || options[:suffix] == ''
ActiveSupport::Deprecation.warn(
"Specifying an empty prefix/suffix for an attribute method is no longer " \
"necessary. If the un-prefixed/suffixed version of the method has not been " \
"defined when `define_attribute_methods` is called, it will be defined " \
"automatically."
)
end
@prefix, @suffix = options[:prefix] || '', options[:suffix] || ''
@regex = /\A(#{Regexp.escape(@prefix)})(.+?)(#{Regexp.escape(@suffix)})\z/
@method_missing_target = "#{@prefix}attribute#{@suffix}"
@method_name = "#{prefix}%s#{suffix}"
end
def match(method_name)
if @regex =~ method_name
AttributeMethodMatch.new(method_missing_target, $2, method_name)
else
nil
end
end
def method_name(attr_name)
@method_name % attr_name
end
def plain?
prefix.empty? && suffix.empty?
end
end
end
# Allows access to the object attributes, which are held in the
# @attributes hash, as though they were first-class methods. So a
# Person class with a name attribute can use Person#name and Person#name=
# and never directly use the attributes hash -- except for multiple assigns
# with ActiveRecord#attributes=. A Milestone class can also ask
# Milestone#completed? to test that the completed attribute is not +nil+
# or 0.
#
# It's also possible to instantiate related objects, so a Client class
# belonging to the clients table with a +master_id+ foreign key can
# instantiate master through Client#master.
def method_missing(method, *args, &block)
if respond_to_without_attributes?(method, true)
super
else
match = match_attribute_method?(method.to_s)
match ? attribute_missing(match, *args, &block) : super
end
end
# attribute_missing is like method_missing, but for attributes. When method_missing is
# called we check to see if there is a matching attribute method. If so, we call
# attribute_missing to dispatch the attribute. This method can be overloaded to
# customise the behaviour.
def attribute_missing(match, *args, &block)
__send__(match.target, match.attr_name, *args, &block)
end
# A Person object with a name attribute can ask person.respond_to?(:name),
# person.respond_to?(:name=), and person.respond_to?(:name?)
# which will all return +true+.
alias :respond_to_without_attributes? :respond_to?
def respond_to?(method, include_private_methods = false)
if super
true
elsif !include_private_methods && super(method, true)
# If we're here then we haven't found among non-private methods
# but found among all methods. Which means that the given method is private.
false
else
!match_attribute_method?(method.to_s).nil?
end
end
protected
def attribute_method?(attr_name)
respond_to_without_attributes?(:attributes) && attributes.include?(attr_name)
end
private
# Returns a struct representing the matching attribute method.
# The struct's attributes are prefix, base and suffix.
def match_attribute_method?(method_name)
match = self.class.send(:attribute_method_matcher, method_name)
match && attribute_method?(match.attr_name) ? match : nil
end
def missing_attribute(attr_name, stack)
raise ActiveModel::MissingAttributeError, "missing attribute: #{attr_name}", stack
end
end
end
activemodel-3.2.16/lib/active_model/dirty.rb 0000644 0001750 0001750 00000011417 12247654710 020320 0 ustar ondrej ondrej require 'active_model/attribute_methods'
require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext/object/duplicable'
module ActiveModel
# == Active Model Dirty
#
# Provides a way to track changes in your object in the same way as
# Active Record does.
#
# The requirements for implementing ActiveModel::Dirty are:
#
# * include ActiveModel::Dirty in your object
# * Call define_attribute_methods passing each method you want to
# track
# * Call attr_name_will_change! before each change to the tracked
# attribute
#
# If you wish to also track previous changes on save or update, you need to
# add
#
# @previously_changed = changes
#
# inside of your save or update method.
#
# A minimal implementation could be:
#
# class Person
#
# include ActiveModel::Dirty
#
# define_attribute_methods [:name]
#
# def name
# @name
# end
#
# def name=(val)
# name_will_change! unless val == @name
# @name = val
# end
#
# def save
# @previously_changed = changes
# @changed_attributes.clear
# end
#
# end
#
# == Examples:
#
# A newly instantiated object is unchanged:
# person = Person.find_by_name('Uncle Bob')
# person.changed? # => false
#
# Change the name:
# person.name = 'Bob'
# person.changed? # => true
# person.name_changed? # => true
# person.name_was # => 'Uncle Bob'
# person.name_change # => ['Uncle Bob', 'Bob']
# person.name = 'Bill'
# person.name_change # => ['Uncle Bob', 'Bill']
#
# Save the changes:
# person.save
# person.changed? # => false
# person.name_changed? # => false
#
# Assigning the same value leaves the attribute unchanged:
# person.name = 'Bill'
# person.name_changed? # => false
# person.name_change # => nil
#
# Which attributes have changed?
# person.name = 'Bob'
# person.changed # => ['name']
# person.changes # => { 'name' => ['Bill', 'Bob'] }
#
# If an attribute is modified in-place then make use of [attribute_name]_will_change!
# to mark that the attribute is changing. Otherwise ActiveModel can't track changes to
# in-place attributes.
#
# person.name_will_change!
# person.name << 'y'
# person.name_change # => ['Bill', 'Billy']
module Dirty
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods
included do
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
attribute_method_affix :prefix => 'reset_', :suffix => '!'
end
# Returns true if any attribute have unsaved changes, false otherwise.
# person.changed? # => false
# person.name = 'bob'
# person.changed? # => true
def changed?
changed_attributes.any?
end
# List of attributes with unsaved changes.
# person.changed # => []
# person.name = 'bob'
# person.changed # => ['name']
def changed
changed_attributes.keys
end
# Map of changed attrs => [original value, new value].
# person.changes # => {}
# person.name = 'bob'
# person.changes # => { 'name' => ['bill', 'bob'] }
def changes
HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
end
# Map of attributes that were changed when the model was saved.
# person.name # => 'bob'
# person.name = 'robert'
# person.save
# person.previous_changes # => {'name' => ['bob, 'robert']}
def previous_changes
@previously_changed
end
# Map of change attr => original value.
def changed_attributes
@changed_attributes ||= {}
end
private
# Handle *_changed? for +method_missing+.
def attribute_changed?(attr)
changed_attributes.include?(attr)
end
# Handle *_change for +method_missing+.
def attribute_change(attr)
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
end
# Handle *_was for +method_missing+.
def attribute_was(attr)
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
# Handle *_will_change! for +method_missing+.
def attribute_will_change!(attr)
begin
value = __send__(attr)
value = value.duplicable? ? value.clone : value
rescue TypeError, NoMethodError
end
changed_attributes[attr] = value unless changed_attributes.include?(attr)
end
# Handle reset_*! for +method_missing+.
def reset_attribute!(attr)
__send__("#{attr}=", changed_attributes[attr]) if attribute_changed?(attr)
end
end
end
activemodel-3.2.16/lib/active_model/validator.rb 0000644 0001750 0001750 00000014123 12247654710 021147 0 ustar ondrej ondrej require 'active_support/core_ext/array/wrap'
require "active_support/core_ext/module/anonymous"
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/inclusion'
module ActiveModel #:nodoc:
# == Active Model Validator
#
# A simple base class that can be used along with
# ActiveModel::Validations::ClassMethods.validates_with
#
# class Person
# include ActiveModel::Validations
# validates_with MyValidator
# end
#
# class MyValidator < ActiveModel::Validator
# def validate(record)
# if some_complex_logic
# record.errors[:base] = "This record is invalid"
# end
# end
#
# private
# def some_complex_logic
# # ...
# end
# end
#
# Any class that inherits from ActiveModel::Validator must implement a method
# called validate which accepts a record.
#
# class Person
# include ActiveModel::Validations
# validates_with MyValidator
# end
#
# class MyValidator < ActiveModel::Validator
# def validate(record)
# record # => The person instance being validated
# options # => Any non-standard options passed to validates_with
# end
# end
#
# To cause a validation error, you must add to the record's errors directly
# from within the validators message
#
# class MyValidator < ActiveModel::Validator
# def validate(record)
# record.errors.add :base, "This is some custom error message"
# record.errors.add :first_name, "This is some complex validation"
# # etc...
# end
# end
#
# To add behavior to the initialize method, use the following signature:
#
# class MyValidator < ActiveModel::Validator
# def initialize(options)
# super
# @my_custom_field = options[:field_name] || :first_name
# end
# end
#
# The easiest way to add custom validators for validating individual attributes
# is with the convenient ActiveModel::EachValidator. For example:
#
# class TitleValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
# record.errors.add attribute, 'must be Mr. Mrs. or Dr.' unless value.in?(['Mr.', 'Mrs.', 'Dr.'])
# end
# end
#
# This can now be used in combination with the +validates+ method
# (see ActiveModel::Validations::ClassMethods.validates for more on this)
#
# class Person
# include ActiveModel::Validations
# attr_accessor :title
#
# validates :title, :presence => true
# end
#
# Validator may also define a +setup+ instance method which will get called
# with the class that using that validator as its argument. This can be
# useful when there are prerequisites such as an +attr_accessor+ being present
# for example:
#
# class MyValidator < ActiveModel::Validator
# def setup(klass)
# klass.send :attr_accessor, :custom_attribute
# end
# end
#
# This setup method is only called when used with validation macros or the
# class level validates_with method.
#
class Validator
attr_reader :options
# Returns the kind of the validator. Examples:
#
# PresenceValidator.kind # => :presence
# UniquenessValidator.kind # => :uniqueness
#
def self.kind
@kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
end
# Accepts options that will be made available through the +options+ reader.
def initialize(options)
@options = options.freeze
end
# Return the kind for this validator.
def kind
self.class.kind
end
# Override this method in subclasses with validation logic, adding errors
# to the records +errors+ array where necessary.
def validate(record)
raise NotImplementedError, "Subclasses must implement a validate(record) method."
end
end
# +EachValidator+ is a validator which iterates through the attributes given
# in the options hash invoking the validate_each method passing in the
# record, attribute and value.
#
# All Active Model validations are built on top of this validator.
class EachValidator < Validator
attr_reader :attributes
# Returns a new validator instance. All options will be available via the
# +options+ reader, however the :attributes option will be removed
# and instead be made available through the +attributes+ reader.
def initialize(options)
@attributes = Array.wrap(options.delete(:attributes))
raise ":attributes cannot be blank" if @attributes.empty?
super
check_validity!
end
# Performs validation on the supplied record. By default this will call
# +validates_each+ to determine validity therefore subclasses should
# override +validates_each+ with validation logic.
def validate(record)
attributes.each do |attribute|
value = record.read_attribute_for_validation(attribute)
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
validate_each(record, attribute, value)
end
end
# Override this method in subclasses with the validation logic, adding
# errors to the records +errors+ array where necessary.
def validate_each(record, attribute, value)
raise NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method"
end
# Hook method that gets called by the initializer allowing verification
# that the arguments supplied are valid. You could for example raise an
# +ArgumentError+ when invalid options are supplied.
def check_validity!
end
end
# +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
# and call this block for each attribute being validated. +validates_each+ uses this validator.
class BlockValidator < EachValidator
def initialize(options, &block)
@block = block
super
end
private
def validate_each(record, attribute, value)
@block.call(record, attribute, value)
end
end
end
activemodel-3.2.16/lib/active_model/mass_assignment_security/ 0000755 0001750 0001750 00000000000 12247654710 023756 5 ustar ondrej ondrej activemodel-3.2.16/lib/active_model/mass_assignment_security/permission_set.rb 0000644 0001750 0001750 00000001232 12247654710 027344 0 ustar ondrej ondrej require 'set'
module ActiveModel
module MassAssignmentSecurity
class PermissionSet < Set
def +(values)
super(values.map(&:to_s))
end
def include?(key)
super(remove_multiparameter_id(key))
end
def deny?(key)
raise NotImplementedError, "#deny?(key) suppose to be overwritten"
end
protected
def remove_multiparameter_id(key)
key.to_s.gsub(/\(.+/m, '')
end
end
class WhiteList < PermissionSet
def deny?(key)
!include?(key)
end
end
class BlackList < PermissionSet
def deny?(key)
include?(key)
end
end
end
end
activemodel-3.2.16/lib/active_model/mass_assignment_security/sanitizer.rb 0000644 0001750 0001750 00000003212 12247654710 026311 0 ustar ondrej ondrej require 'active_support/core_ext/module/delegation'
module ActiveModel
module MassAssignmentSecurity
class Sanitizer
def initialize(target=nil)
end
# Returns all attributes not denied by the authorizer.
def sanitize(attributes, authorizer)
sanitized_attributes = attributes.reject { |key, value| authorizer.deny?(key) }
debug_protected_attribute_removal(attributes, sanitized_attributes)
sanitized_attributes
end
protected
def debug_protected_attribute_removal(attributes, sanitized_attributes)
removed_keys = attributes.keys - sanitized_attributes.keys
process_removed_attributes(removed_keys) if removed_keys.any?
end
def process_removed_attributes(attrs)
raise NotImplementedError, "#process_removed_attributes(attrs) suppose to be overwritten"
end
end
class LoggerSanitizer < Sanitizer
delegate :logger, :to => :@target
def initialize(target)
@target = target
super
end
def logger?
@target.respond_to?(:logger) && @target.logger
end
def process_removed_attributes(attrs)
logger.debug "WARNING: Can't mass-assign protected attributes: #{attrs.join(', ')}" if logger?
end
end
class StrictSanitizer < Sanitizer
def process_removed_attributes(attrs)
return if (attrs - insensitive_attributes).empty?
raise ActiveModel::MassAssignmentSecurity::Error, "Can't mass-assign protected attributes: #{attrs.join(', ')}"
end
def insensitive_attributes
['id']
end
end
class Error < StandardError
end
end
end
activemodel-3.2.16/lib/active_model/secure_password.rb 0000644 0001750 0001750 00000005573 12247654710 022403 0 ustar ondrej ondrej module ActiveModel
module SecurePassword
extend ActiveSupport::Concern
module ClassMethods
# Adds methods to set and authenticate against a BCrypt password.
# This mechanism requires you to have a password_digest attribute.
#
# Validations for presence of password, confirmation of password (using
# a "password_confirmation" attribute) are automatically added.
# You can add more validations by hand if need be.
#
# You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use has_secure_password:
#
# gem 'bcrypt-ruby', '~> 3.0.0'
#
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
#
# # Schema: User(name:string, password_digest:string)
# class User < ActiveRecord::Base
# has_secure_password
# end
#
# user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch")
# user.save # => false, password required
# user.password = "mUc3m00RsqyRe"
# user.save # => false, confirmation doesn't match
# user.password_confirmation = "mUc3m00RsqyRe"
# user.save # => true
# user.authenticate("notright") # => false
# user.authenticate("mUc3m00RsqyRe") # => user
# User.find_by_name("david").try(:authenticate, "notright") # => nil
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
def has_secure_password
# Load bcrypt-ruby only when has_secure_password is used.
# This is to avoid ActiveModel (and by extension the entire framework) being dependent on a binary library.
gem 'bcrypt-ruby', '~> 3.0.0'
require 'bcrypt'
attr_reader :password
validates_confirmation_of :password
validates_presence_of :password_digest
include InstanceMethodsOnActivation
if respond_to?(:attributes_protected_by_default)
def self.attributes_protected_by_default
super + ['password_digest']
end
end
end
end
module InstanceMethodsOnActivation
# Returns self if the password is correct, otherwise false.
def authenticate(unencrypted_password)
if BCrypt::Password.new(password_digest) == unencrypted_password
self
else
false
end
end
# Encrypts the password into the password_digest attribute.
def password=(unencrypted_password)
@password = unencrypted_password
unless unencrypted_password.blank?
self.password_digest = BCrypt::Password.create(unencrypted_password)
end
end
end
end
end
activemodel-3.2.16/lib/active_model/locale/ 0000755 0001750 0001750 00000000000 12247654710 020073 5 ustar ondrej ondrej activemodel-3.2.16/lib/active_model/locale/en.yml 0000644 0001750 0001750 00000002216 12247654710 021221 0 ustar ondrej ondrej en:
errors:
# The default format to use in full error messages.
format: "%{attribute} %{message}"
# The values :model, :attribute and :value are always available for interpolation
# The value :count is available when applicable. Can be used for pluralization.
messages:
inclusion: "is not included in the list"
exclusion: "is reserved"
invalid: "is invalid"
confirmation: "doesn't match confirmation"
accepted: "must be accepted"
empty: "can't be empty"
blank: "can't be blank"
too_long: "is too long (maximum is %{count} characters)"
too_short: "is too short (minimum is %{count} characters)"
wrong_length: "is the wrong length (should be %{count} characters)"
not_a_number: "is not a number"
not_an_integer: "must be an integer"
greater_than: "must be greater than %{count}"
greater_than_or_equal_to: "must be greater than or equal to %{count}"
equal_to: "must be equal to %{count}"
less_than: "must be less than %{count}"
less_than_or_equal_to: "must be less than or equal to %{count}"
odd: "must be odd"
even: "must be even"
activemodel-3.2.16/lib/active_model/naming.rb 0000644 0001750 0001750 00000014516 12247654710 020441 0 ustar ondrej ondrej require 'active_support/inflector'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/module/introspection'
require 'active_support/deprecation'
module ActiveModel
class Name < String
attr_reader :singular, :plural, :element, :collection, :partial_path,
:singular_route_key, :route_key, :param_key, :i18n_key
alias_method :cache_key, :collection
deprecate :partial_path => "ActiveModel::Name#partial_path is deprecated. Call #to_partial_path on model instances directly instead."
def initialize(klass, namespace = nil, name = nil)
name ||= klass.name
raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if name.blank?
super(name)
@unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace
@klass = klass
@singular = _singularize(self).freeze
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
@human = ActiveSupport::Inflector.humanize(@element).freeze
@collection = ActiveSupport::Inflector.tableize(self).freeze
@partial_path = "#{@collection}/#{@element}".freeze
@param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
@i18n_key = self.underscore.to_sym
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
@singular_route_key = ActiveSupport::Inflector.singularize(@route_key).freeze
@route_key << "_index" if @plural == @singular
@route_key.freeze
end
# Transform the model name into a more humane format, using I18n. By default,
# it will underscore then humanize the class name
#
# BlogPost.model_name.human # => "Blog post"
#
# Specify +options+ with additional translating options.
def human(options={})
return @human unless @klass.respond_to?(:lookup_ancestors) &&
@klass.respond_to?(:i18n_scope)
defaults = @klass.lookup_ancestors.map do |klass|
klass.model_name.i18n_key
end
defaults << options[:default] if options[:default]
defaults << @human
options = {:scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults}.merge(options.except(:default))
I18n.translate(defaults.shift, options)
end
private
def _singularize(string, replacement='_')
ActiveSupport::Inflector.underscore(string).tr('/', replacement)
end
end
# == Active Model Naming
#
# Creates a +model_name+ method on your object.
#
# To implement, just extend ActiveModel::Naming in your object:
#
# class BookCover
# extend ActiveModel::Naming
# end
#
# BookCover.model_name # => "BookCover"
# BookCover.model_name.human # => "Book cover"
#
# BookCover.model_name.i18n_key # => :book_cover
# BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
#
# Providing the functionality that ActiveModel::Naming provides in your object
# is required to pass the Active Model Lint test. So either extending the provided
# method below, or rolling your own is required.
module Naming
# Returns an ActiveModel::Name object for module. It can be
# used to retrieve all kinds of naming-related information.
def model_name
@_model_name ||= begin
namespace = self.parents.detect do |n|
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
end
ActiveModel::Name.new(self, namespace)
end
end
# Returns the plural class name of a record or class. Examples:
#
# ActiveModel::Naming.plural(post) # => "posts"
# ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
def self.plural(record_or_class)
model_name_from_record_or_class(record_or_class).plural
end
# Returns the singular class name of a record or class. Examples:
#
# ActiveModel::Naming.singular(post) # => "post"
# ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
def self.singular(record_or_class)
model_name_from_record_or_class(record_or_class).singular
end
# Identifies whether the class name of a record or class is uncountable. Examples:
#
# ActiveModel::Naming.uncountable?(Sheep) # => true
# ActiveModel::Naming.uncountable?(Post) => false
def self.uncountable?(record_or_class)
plural(record_or_class) == singular(record_or_class)
end
# Returns string to use while generating route names. It differs for
# namespaced models regarding whether it's inside isolated engine.
#
# For isolated engine:
# ActiveModel::Naming.route_key(Blog::Post) #=> post
#
# For shared engine:
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_post
def self.singular_route_key(record_or_class)
model_name_from_record_or_class(record_or_class).singular_route_key
end
# Returns string to use while generating route names. It differs for
# namespaced models regarding whether it's inside isolated engine.
#
# For isolated engine:
# ActiveModel::Naming.route_key(Blog::Post) #=> posts
#
# For shared engine:
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
#
# The route key also considers if the noun is uncountable and, in
# such cases, automatically appends _index.
def self.route_key(record_or_class)
model_name_from_record_or_class(record_or_class).route_key
end
# Returns string to use for params names. It differs for
# namespaced models regarding whether it's inside isolated engine.
#
# For isolated engine:
# ActiveModel::Naming.param_key(Blog::Post) #=> post
#
# For shared engine:
# ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
def self.param_key(record_or_class)
model_name_from_record_or_class(record_or_class).param_key
end
private
def self.model_name_from_record_or_class(record_or_class)
(record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
end
def self.convert_to_model(object)
object.respond_to?(:to_model) ? object.to_model : object
end
end
end
activemodel-3.2.16/lib/active_model/translation.rb 0000644 0001750 0001750 00000004443 12247654710 021524 0 ustar ondrej ondrej require 'active_support/core_ext/hash/reverse_merge'
module ActiveModel
# == Active Model Translation
#
# Provides integration between your object and the Rails internationalization
# (i18n) framework.
#
# A minimal implementation could be:
#
# class TranslatedPerson
# extend ActiveModel::Translation
# end
#
# TranslatedPerson.human_attribute_name('my_attribute')
# # => "My attribute"
#
# This also provides the required class methods for hooking into the
# Rails internationalization API, including being able to define a
# class based +i18n_scope+ and +lookup_ancestors+ to find translations in
# parent classes.
module Translation
include ActiveModel::Naming
# Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
def i18n_scope
:activemodel
end
# When localizing a string, it goes through the lookup returned by this
# method, which is used in ActiveModel::Name#human,
# ActiveModel::Errors#full_messages and
# ActiveModel::Translation#human_attribute_name.
def lookup_ancestors
self.ancestors.select { |x| x.respond_to?(:model_name) }
end
# Transforms attribute names into a more human format, such as "First name"
# instead of "first_name".
#
# Person.human_attribute_name("first_name") # => "First name"
#
# Specify +options+ with additional translating options.
def human_attribute_name(attribute, options = {})
defaults = []
parts = attribute.to_s.split(".")
attribute = parts.pop
namespace = parts.join("/") unless parts.empty?
if namespace
lookup_ancestors.each do |klass|
defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
end
defaults << :"#{self.i18n_scope}.attributes.#{namespace}.#{attribute}"
else
lookup_ancestors.each do |klass|
defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
end
end
defaults << :"attributes.#{attribute}"
defaults << options.delete(:default) if options[:default]
defaults << attribute.humanize
options.reverse_merge! :count => 1, :default => defaults
I18n.translate(defaults.shift, options)
end
end
end
activemodel-3.2.16/lib/active_model/version.rb 0000644 0001750 0001750 00000000254 12247654710 020647 0 ustar ondrej ondrej module ActiveModel
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
TINY = 16
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
activemodel-3.2.16/lib/active_model/lint.rb 0000644 0001750 0001750 00000012213 12247654710 020126 0 ustar ondrej ondrej module ActiveModel
module Lint
# == Active Model Lint Tests
#
# You can test whether an object is compliant with the Active Model API by
# including ActiveModel::Lint::Tests in your TestCase. It will include
# tests that tell you whether your object is fully compliant, or if not,
# which aspects of the API are not implemented.
#
# These tests do not attempt to determine the semantic correctness of the
# returned values. For instance, you could implement valid? to always
# return true, and the tests would pass. It is up to you to ensure that
# the values are semantically meaningful.
#
# Objects you pass in are expected to return a compliant object from a
# call to to_model. It is perfectly fine for to_model to return self.
module Tests
# == Responds to to_key
#
# Returns an Enumerable of all (primary) key attributes
# or nil if model.persisted? is false
def test_to_key
assert model.respond_to?(:to_key), "The model should respond to to_key"
def model.persisted?() false end
assert model.to_key.nil?, "to_key should return nil when `persisted?` returns false"
end
# == Responds to to_param
#
# Returns a string representing the object's key suitable for use in URLs
# or nil if model.persisted? is false.
#
# Implementers can decide to either raise an exception or provide a default
# in case the record uses a composite primary key. There are no tests for this
# behavior in lint because it doesn't make sense to force any of the possible
# implementation strategies on the implementer. However, if the resource is
# not persisted?, then to_param should always return nil.
def test_to_param
assert model.respond_to?(:to_param), "The model should respond to to_param"
def model.to_key() [1] end
def model.persisted?() false end
assert model.to_param.nil?, "to_param should return nil when `persisted?` returns false"
end
# == Responds to to_partial_path
#
# Returns a string giving a relative path. This is used for looking up
# partials. For example, a BlogPost model might return "blog_posts/blog_post"
#
def test_to_partial_path
assert model.respond_to?(:to_partial_path), "The model should respond to to_partial_path"
assert_kind_of String, model.to_partial_path
end
# == Responds to valid?
#
# Returns a boolean that specifies whether the object is in a valid or invalid
# state.
def test_valid?
assert model.respond_to?(:valid?), "The model should respond to valid?"
assert_boolean model.valid?, "valid?"
end
# == Responds to persisted?
#
# Returns a boolean that specifies whether the object has been persisted yet.
# This is used when calculating the URL for an object. If the object is
# not persisted, a form for that object, for instance, will be POSTed to the
# collection. If it is persisted, a form for the object will be PUT to the
# URL for the object.
def test_persisted?
assert model.respond_to?(:persisted?), "The model should respond to persisted?"
assert_boolean model.persisted?, "persisted?"
end
# == Naming
#
# Model.model_name must return a string with some convenience methods:
# :human, :singular, and :plural. Check ActiveModel::Naming for more information.
#
def test_model_naming
assert model.class.respond_to?(:model_name), "The model should respond to model_name"
model_name = model.class.model_name
assert_kind_of String, model_name
assert_kind_of String, model_name.human
assert_kind_of String, model_name.singular
assert_kind_of String, model_name.plural
end
# == Errors Testing
#
# Returns an object that has :[] and :full_messages defined on it. See below
# for more details.
#
# Returns an Array of Strings that are the errors for the attribute in
# question. If localization is used, the Strings should be localized
# for the current locale. If no error is present, this method should
# return an empty Array.
def test_errors_aref
assert model.respond_to?(:errors), "The model should respond to errors"
assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
end
# Returns an Array of all error messages for the object. Each message
# should contain information about the field, if applicable.
def test_errors_full_messages
assert model.respond_to?(:errors), "The model should respond to errors"
assert model.errors.full_messages.is_a?(Array), "errors#full_messages should return an Array"
end
private
def model
assert @model.respond_to?(:to_model), "The object should respond_to to_model"
@model.to_model
end
def assert_boolean(result, name)
assert result == true || result == false, "#{name} should be a boolean"
end
end
end
end
activemodel-3.2.16/lib/active_model.rb 0000644 0001750 0001750 00000004162 12247654710 017164 0 ustar ondrej ondrej #--
# Copyright (c) 2004-2011 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'active_support'
require 'active_model/version'
module ActiveModel
extend ActiveSupport::Autoload
autoload :AttributeMethods
autoload :BlockValidator, 'active_model/validator'
autoload :Callbacks
autoload :Conversion
autoload :Dirty
autoload :EachValidator, 'active_model/validator'
autoload :Errors
autoload :Lint
autoload :MassAssignmentSecurity
autoload :Name, 'active_model/naming'
autoload :Naming
autoload :Observer, 'active_model/observing'
autoload :Observing
autoload :SecurePassword
autoload :Serialization
autoload :TestCase
autoload :Translation
autoload :Validations
autoload :Validator
module Serializers
extend ActiveSupport::Autoload
autoload :JSON
autoload :Xml
end
end
require 'active_support/i18n'
I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
activemodel-3.2.16/metadata.yml 0000644 0001750 0001750 00000006236 12247654710 015745 0 ustar ondrej ondrej --- !ruby/object:Gem::Specification
name: activemodel
version: !ruby/object:Gem::Version
version: 3.2.16
platform: ruby
authors:
- David Heinemeier Hansson
autorequire:
bindir: bin
cert_chain: []
date: 2013-12-03 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: activesupport
requirement: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
version: 3.2.16
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
version: 3.2.16
- !ruby/object:Gem::Dependency
name: builder
requirement: !ruby/object:Gem::Requirement
requirements:
- - "~>"
- !ruby/object:Gem::Version
version: 3.0.0
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - "~>"
- !ruby/object:Gem::Version
version: 3.0.0
description: A toolkit for building modeling frameworks like Active Record and Active
Resource. Rich support for attributes, callbacks, validations, observers, serialization,
internationalization, and testing.
email: david@loudthinking.com
executables: []
extensions: []
extra_rdoc_files: []
files:
- CHANGELOG.md
- MIT-LICENSE
- README.rdoc
- lib/active_model/attribute_methods.rb
- lib/active_model/callbacks.rb
- lib/active_model/conversion.rb
- lib/active_model/dirty.rb
- lib/active_model/errors.rb
- lib/active_model/lint.rb
- lib/active_model/locale/en.yml
- lib/active_model/mass_assignment_security/permission_set.rb
- lib/active_model/mass_assignment_security/sanitizer.rb
- lib/active_model/mass_assignment_security.rb
- lib/active_model/naming.rb
- lib/active_model/observer_array.rb
- lib/active_model/observing.rb
- lib/active_model/railtie.rb
- lib/active_model/secure_password.rb
- lib/active_model/serialization.rb
- lib/active_model/serializers/json.rb
- lib/active_model/serializers/xml.rb
- lib/active_model/test_case.rb
- lib/active_model/translation.rb
- lib/active_model/validations/acceptance.rb
- lib/active_model/validations/callbacks.rb
- lib/active_model/validations/confirmation.rb
- lib/active_model/validations/exclusion.rb
- lib/active_model/validations/format.rb
- lib/active_model/validations/inclusion.rb
- lib/active_model/validations/length.rb
- lib/active_model/validations/numericality.rb
- lib/active_model/validations/presence.rb
- lib/active_model/validations/validates.rb
- lib/active_model/validations/with.rb
- lib/active_model/validations.rb
- lib/active_model/validator.rb
- lib/active_model/version.rb
- lib/active_model.rb
homepage: http://www.rubyonrails.org
licenses:
- MIT
metadata: {}
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 1.8.7
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubyforge_project:
rubygems_version: 2.0.2
signing_key:
specification_version: 4
summary: A toolkit for building modeling frameworks (part of Rails).
test_files: []
activemodel-3.2.16/MIT-LICENSE 0000644 0001750 0001750 00000002062 12247654710 015067 0 ustar ondrej ondrej Copyright (c) 2004-2011 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.