activemodel 3.0.0.beta4 → 3.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +1 -39
- data/MIT-LICENSE +1 -1
- data/README +16 -200
- data/lib/active_model.rb +19 -28
- data/lib/active_model/attribute_methods.rb +27 -142
- data/lib/active_model/conversion.rb +1 -37
- data/lib/active_model/dirty.rb +12 -51
- data/lib/active_model/errors.rb +22 -146
- data/lib/active_model/lint.rb +14 -48
- data/lib/active_model/locale/en.yml +23 -26
- data/lib/active_model/naming.rb +5 -41
- data/lib/active_model/observing.rb +16 -35
- data/lib/active_model/serialization.rb +0 -57
- data/lib/active_model/serializers/json.rb +8 -13
- data/lib/active_model/serializers/xml.rb +123 -63
- data/lib/active_model/state_machine.rb +70 -0
- data/lib/active_model/state_machine/event.rb +62 -0
- data/lib/active_model/state_machine/machine.rb +75 -0
- data/lib/active_model/state_machine/state.rb +47 -0
- data/lib/active_model/state_machine/state_transition.rb +40 -0
- data/lib/active_model/test_case.rb +2 -0
- data/lib/active_model/validations.rb +62 -125
- data/lib/active_model/validations/acceptance.rb +18 -23
- data/lib/active_model/validations/confirmation.rb +10 -14
- data/lib/active_model/validations/exclusion.rb +13 -15
- data/lib/active_model/validations/format.rb +24 -26
- data/lib/active_model/validations/inclusion.rb +13 -15
- data/lib/active_model/validations/length.rb +65 -61
- data/lib/active_model/validations/numericality.rb +58 -76
- data/lib/active_model/validations/presence.rb +8 -8
- data/lib/active_model/validations/with.rb +22 -90
- data/lib/active_model/validations_repair_helper.rb +35 -0
- data/lib/active_model/version.rb +2 -3
- metadata +19 -63
- data/lib/active_model/callbacks.rb +0 -134
- data/lib/active_model/railtie.rb +0 -2
- data/lib/active_model/translation.rb +0 -60
- data/lib/active_model/validations/validates.rb +0 -108
- data/lib/active_model/validator.rb +0 -183
data/lib/active_model/railtie.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/hash/reverse_merge'
|
2
|
-
|
3
|
-
module ActiveModel
|
4
|
-
|
5
|
-
# ActiveModel::Translation provides integration between your object and
|
6
|
-
# the Rails internationalization (i18n) framework.
|
7
|
-
#
|
8
|
-
# A minimal implementation could be:
|
9
|
-
#
|
10
|
-
# class TranslatedPerson
|
11
|
-
# extend ActiveModel::Translation
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# TranslatedPerson.human_attribute_name('my_attribue')
|
15
|
-
# #=> "My attribute"
|
16
|
-
#
|
17
|
-
# This also provides the required class methods for hooking into the
|
18
|
-
# Rails internationalization API, including being able to define a
|
19
|
-
# class based i18n_scope and lookup_ancestors to find translations in
|
20
|
-
# parent classes.
|
21
|
-
module Translation
|
22
|
-
include ActiveModel::Naming
|
23
|
-
|
24
|
-
# Returns the i18n_scope for the class. Overwrite if you want custom lookup.
|
25
|
-
def i18n_scope
|
26
|
-
:activemodel
|
27
|
-
end
|
28
|
-
|
29
|
-
# When localizing a string, goes through the lookup returned by this method.
|
30
|
-
# Used in ActiveModel::Name#human, ActiveModel::Errors#full_messages and
|
31
|
-
# ActiveModel::Translation#human_attribute_name.
|
32
|
-
def lookup_ancestors
|
33
|
-
self.ancestors.select { |x| x.respond_to?(:model_name) }
|
34
|
-
end
|
35
|
-
|
36
|
-
# Transforms attributes names into a more human format, such as "First name" instead of "first_name".
|
37
|
-
#
|
38
|
-
# Person.human_attribute_name("first_name") # => "First name"
|
39
|
-
#
|
40
|
-
# Specify +options+ with additional translating options.
|
41
|
-
def human_attribute_name(attribute, options = {})
|
42
|
-
defaults = lookup_ancestors.map do |klass|
|
43
|
-
:"#{self.i18n_scope}.attributes.#{klass.model_name.underscore}.#{attribute}"
|
44
|
-
end
|
45
|
-
|
46
|
-
defaults << :"attributes.#{attribute}"
|
47
|
-
defaults << options.delete(:default) if options[:default]
|
48
|
-
defaults << attribute.to_s.humanize
|
49
|
-
|
50
|
-
options.reverse_merge! :count => 1, :default => defaults
|
51
|
-
I18n.translate(defaults.shift, options)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Model.human_name is deprecated. Use Model.model_name.human instead.
|
55
|
-
def human_name(*args)
|
56
|
-
ActiveSupport::Deprecation.warn("human_name has been deprecated, please use model_name.human instead", caller[0,5])
|
57
|
-
model_name.human(*args)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,108 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/hash/slice'
|
2
|
-
|
3
|
-
module ActiveModel
|
4
|
-
module Validations
|
5
|
-
module ClassMethods
|
6
|
-
# This method is a shortcut to all default validators and any custom
|
7
|
-
# validator classes ending in 'Validator'. Note that Rails default
|
8
|
-
# validators can be overridden inside specific classes by creating
|
9
|
-
# custom validator classes in their place such as PresenceValidator.
|
10
|
-
#
|
11
|
-
# Examples of using the default rails validators:
|
12
|
-
#
|
13
|
-
# validates :terms, :acceptance => true
|
14
|
-
# validates :password, :confirmation => true
|
15
|
-
# validates :username, :exclusion => { :in => %w(admin superuser) }
|
16
|
-
# validates :email, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create }
|
17
|
-
# validates :age, :inclusion => { :in => 0..9 }
|
18
|
-
# validates :first_name, :length => { :maximum => 30 }
|
19
|
-
# validates :age, :numericality => true
|
20
|
-
# validates :username, :presence => true
|
21
|
-
# validates :username, :uniqueness => true
|
22
|
-
#
|
23
|
-
# The power of the +validates+ method comes when using cusom validators
|
24
|
-
# and default validators in one call for a given attribute e.g.
|
25
|
-
#
|
26
|
-
# class EmailValidator < ActiveModel::EachValidator
|
27
|
-
# def validate_each(record, attribute, value)
|
28
|
-
# record.errors[attribute] << (options[:message] || "is not an email") unless
|
29
|
-
# value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
|
30
|
-
# end
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# class Person
|
34
|
-
# include ActiveModel::Validations
|
35
|
-
# attr_accessor :name, :email
|
36
|
-
#
|
37
|
-
# validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
|
38
|
-
# validates :email, :presence => true, :email => true
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# Validator classes my also exist within the class being validated
|
42
|
-
# allowing custom modules of validators to be included as needed e.g.
|
43
|
-
#
|
44
|
-
# class Film
|
45
|
-
# include ActiveModel::Validations
|
46
|
-
#
|
47
|
-
# class TitleValidator < ActiveModel::EachValidator
|
48
|
-
# def validate_each(record, attribute, value)
|
49
|
-
# record.errors[attribute] << "must start with 'the'" unless =~ /^the/i
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# validates :name, :title => true
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# The validators hash can also handle regular expressions, ranges and arrays:
|
57
|
-
#
|
58
|
-
# validates :email, :format => /@/
|
59
|
-
# validates :gender, :inclusion => %w(male female)
|
60
|
-
# validates :password, :length => 6..20
|
61
|
-
#
|
62
|
-
# Finally, the options :if, :unless, :on, :allow_blank and :allow_nil can be given
|
63
|
-
# to one specific validator:
|
64
|
-
#
|
65
|
-
# validates :password, :presence => { :if => :password_required? }, :confirmation => true
|
66
|
-
#
|
67
|
-
# Or to all at the same time:
|
68
|
-
#
|
69
|
-
# validates :password, :presence => true, :confirmation => true, :if => :password_required?
|
70
|
-
#
|
71
|
-
def validates(*attributes)
|
72
|
-
defaults = attributes.extract_options!
|
73
|
-
validations = defaults.slice!(:if, :unless, :on, :allow_blank, :allow_nil)
|
74
|
-
|
75
|
-
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
|
76
|
-
raise ArgumentError, "Attribute names must be symbols" if attributes.any?{ |attribute| !attribute.is_a?(Symbol) }
|
77
|
-
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
|
78
|
-
|
79
|
-
defaults.merge!(:attributes => attributes)
|
80
|
-
|
81
|
-
validations.each do |key, options|
|
82
|
-
begin
|
83
|
-
validator = const_get("#{key.to_s.camelize}Validator")
|
84
|
-
rescue NameError
|
85
|
-
raise ArgumentError, "Unknown validator: '#{key}'"
|
86
|
-
end
|
87
|
-
|
88
|
-
validates_with(validator, defaults.merge(_parse_validates_options(options)))
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
protected
|
93
|
-
|
94
|
-
def _parse_validates_options(options) #:nodoc:
|
95
|
-
case options
|
96
|
-
when TrueClass
|
97
|
-
{}
|
98
|
-
when Hash
|
99
|
-
options
|
100
|
-
when Regexp
|
101
|
-
{ :with => options }
|
102
|
-
when Range, Array
|
103
|
-
{ :in => options }
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
@@ -1,183 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/array/wrap'
|
2
|
-
require "active_support/core_ext/module/anonymous"
|
3
|
-
require 'active_support/core_ext/object/blank'
|
4
|
-
|
5
|
-
module ActiveModel #:nodoc:
|
6
|
-
# A simple base class that can be used along with
|
7
|
-
# +ActiveModel::Validations::ClassMethods.validates_with+
|
8
|
-
#
|
9
|
-
# class Person
|
10
|
-
# include ActiveModel::Validations
|
11
|
-
# validates_with MyValidator
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# class MyValidator < ActiveModel::Validator
|
15
|
-
# def validate(record)
|
16
|
-
# if some_complex_logic
|
17
|
-
# record.errors[:base] = "This record is invalid"
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# private
|
22
|
-
# def some_complex_logic
|
23
|
-
# # ...
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# Any class that inherits from ActiveModel::Validator must implement a method
|
28
|
-
# called <tt>validate</tt> which accepts a <tt>record</tt>.
|
29
|
-
#
|
30
|
-
# class Person
|
31
|
-
# include ActiveModel::Validations
|
32
|
-
# validates_with MyValidator
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# class MyValidator < ActiveModel::Validator
|
36
|
-
# def validate(record)
|
37
|
-
# record # => The person instance being validated
|
38
|
-
# options # => Any non-standard options passed to validates_with
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# To cause a validation error, you must add to the <tt>record<tt>'s errors directly
|
43
|
-
# from within the validators message
|
44
|
-
#
|
45
|
-
# class MyValidator < ActiveModel::Validator
|
46
|
-
# def validate(record)
|
47
|
-
# record.errors[:base] << "This is some custom error message"
|
48
|
-
# record.errors[:first_name] << "This is some complex validation"
|
49
|
-
# # etc...
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# To add behavior to the initialize method, use the following signature:
|
54
|
-
#
|
55
|
-
# class MyValidator < ActiveModel::Validator
|
56
|
-
# def initialize(record, options)
|
57
|
-
# super
|
58
|
-
# @my_custom_field = options[:field_name] || :first_name
|
59
|
-
# end
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# The easiest way to add custom validators for validating individual attributes
|
63
|
-
# is with the convenient ActiveModel::EachValidator for example:
|
64
|
-
#
|
65
|
-
# class TitleValidator < ActiveModel::EachValidator
|
66
|
-
# def validate_each(record, attribute, value)
|
67
|
-
# record.errors[attribute] << 'must be Mr. Mrs. or Dr.' unless ['Mr.', 'Mrs.', 'Dr.'].include?(value)
|
68
|
-
# end
|
69
|
-
# end
|
70
|
-
#
|
71
|
-
# This can now be used in combination with the +validates+ method
|
72
|
-
# (see ActiveModel::Validations::ClassMethods.validates for more on this)
|
73
|
-
#
|
74
|
-
# class Person
|
75
|
-
# include ActiveModel::Validations
|
76
|
-
# attr_accessor :title
|
77
|
-
#
|
78
|
-
# validates :title, :presence => true, :title => true
|
79
|
-
# end
|
80
|
-
#
|
81
|
-
# Validator may also define a +setup+ instance method which will get called
|
82
|
-
# with the class that using that validator as it's argument. This can be
|
83
|
-
# useful when there are prerequisites such as an attr_accessor being present
|
84
|
-
# for example:
|
85
|
-
#
|
86
|
-
# class MyValidator < ActiveModel::Validator
|
87
|
-
# def setup(klass)
|
88
|
-
# klass.send :attr_accessor, :custom_attribute
|
89
|
-
# end
|
90
|
-
# end
|
91
|
-
#
|
92
|
-
# This setup method is only called when used with validation macros or the
|
93
|
-
# class level <tt>validates_with</tt> method.
|
94
|
-
#
|
95
|
-
class Validator
|
96
|
-
attr_reader :options
|
97
|
-
|
98
|
-
# Returns the kind of the validator.
|
99
|
-
#
|
100
|
-
# == Examples
|
101
|
-
#
|
102
|
-
# PresenceValidator.kind #=> :presence
|
103
|
-
# UniquenessValidator.kind #=> :uniqueness
|
104
|
-
#
|
105
|
-
def self.kind
|
106
|
-
@kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
|
107
|
-
end
|
108
|
-
|
109
|
-
# Accepts options that will be made availible through the +options+ reader.
|
110
|
-
def initialize(options)
|
111
|
-
@options = options
|
112
|
-
end
|
113
|
-
|
114
|
-
# Return the kind for this validator.
|
115
|
-
def kind
|
116
|
-
self.class.kind
|
117
|
-
end
|
118
|
-
|
119
|
-
# Override this method in subclasses with validation logic, adding errors
|
120
|
-
# to the records +errors+ array where necessary.
|
121
|
-
def validate(record)
|
122
|
-
raise NotImplementedError
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
# EachValidator is a validator which iterates through the attributes given
|
127
|
-
# in the options hash invoking the validate_each method passing in the
|
128
|
-
# record, attribute and value.
|
129
|
-
#
|
130
|
-
# All ActiveModel validations are built on top of this Validator.
|
131
|
-
class EachValidator < Validator
|
132
|
-
attr_reader :attributes
|
133
|
-
|
134
|
-
# Returns a new validator instance. All options will be available via the
|
135
|
-
# +options+ reader, however the <tt>:attributes</tt> option will be removed
|
136
|
-
# and instead be made available through the +attributes+ reader.
|
137
|
-
def initialize(options)
|
138
|
-
@attributes = Array.wrap(options.delete(:attributes))
|
139
|
-
raise ":attributes cannot be blank" if @attributes.empty?
|
140
|
-
super
|
141
|
-
check_validity!
|
142
|
-
end
|
143
|
-
|
144
|
-
# Performs validation on the supplied record. By default this will call
|
145
|
-
# +validates_each+ to determine validity therefore subclasses should
|
146
|
-
# override +validates_each+ with validation logic.
|
147
|
-
def validate(record)
|
148
|
-
attributes.each do |attribute|
|
149
|
-
value = record.read_attribute_for_validation(attribute)
|
150
|
-
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
151
|
-
validate_each(record, attribute, value)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
# Override this method in subclasses with the validation logic, adding
|
156
|
-
# errors to the records +errors+ array where necessary.
|
157
|
-
def validate_each(record, attribute, value)
|
158
|
-
raise NotImplementedError
|
159
|
-
end
|
160
|
-
|
161
|
-
# Hook method that gets called by the initializer allowing verification
|
162
|
-
# that the arguments supplied are valid. You could for example raise an
|
163
|
-
# ArgumentError when invalid options are supplied.
|
164
|
-
def check_validity!
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# BlockValidator is a special EachValidator which receives a block on initialization
|
169
|
-
# and call this block for each attribute being validated. +validates_each+ uses this
|
170
|
-
# Validator.
|
171
|
-
class BlockValidator < EachValidator
|
172
|
-
def initialize(options, &block)
|
173
|
-
@block = block
|
174
|
-
super
|
175
|
-
end
|
176
|
-
|
177
|
-
private
|
178
|
-
|
179
|
-
def validate_each(record, attribute, value)
|
180
|
-
@block.call(record, attribute, value)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|