activemodel 3.0.0.beta3 → 3.0.0.beta4
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 +5 -0
- data/lib/active_model/attribute_methods.rb +1 -1
- data/lib/active_model/dirty.rb +5 -4
- data/lib/active_model/errors.rb +7 -7
- data/lib/active_model/locale/en.yml +10 -9
- data/lib/active_model/observing.rb +21 -7
- data/lib/active_model/serializers/json.rb +11 -4
- data/lib/active_model/serializers/xml.rb +59 -120
- data/lib/active_model/validations.rb +18 -10
- data/lib/active_model/validations/acceptance.rb +5 -3
- data/lib/active_model/validations/confirmation.rb +1 -1
- data/lib/active_model/validations/exclusion.rb +2 -2
- data/lib/active_model/validations/format.rb +1 -1
- data/lib/active_model/validations/inclusion.rb +2 -2
- data/lib/active_model/validations/length.rb +4 -4
- data/lib/active_model/validations/numericality.rb +18 -6
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/with.rb +53 -0
- data/lib/active_model/validator.rb +3 -0
- data/lib/active_model/version.rb +1 -1
- metadata +33 -5
data/CHANGELOG
CHANGED
data/lib/active_model/dirty.rb
CHANGED
@@ -124,11 +124,12 @@ module ActiveModel
|
|
124
124
|
@previously_changed
|
125
125
|
end
|
126
126
|
|
127
|
+
# Map of change <tt>attr => original value</tt>.
|
128
|
+
def changed_attributes
|
129
|
+
@changed_attributes ||= {}
|
130
|
+
end
|
131
|
+
|
127
132
|
private
|
128
|
-
# Map of change <tt>attr => original value</tt>.
|
129
|
-
def changed_attributes
|
130
|
-
@changed_attributes ||= {}
|
131
|
-
end
|
132
133
|
|
133
134
|
# Handle <tt>*_changed?</tt> for +method_missing+.
|
134
135
|
def attribute_changed?(attr)
|
data/lib/active_model/errors.rb
CHANGED
@@ -170,13 +170,13 @@ module ActiveModel
|
|
170
170
|
end
|
171
171
|
end
|
172
172
|
|
173
|
-
# Adds
|
174
|
-
# for the same attribute
|
175
|
-
#
|
176
|
-
# If no +
|
173
|
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
|
174
|
+
# <tt>on(attribute)</tt> for the same attribute. More than one error can be added to the same
|
175
|
+
# +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
|
176
|
+
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
|
177
177
|
#
|
178
|
-
# If +message+ is a
|
179
|
-
# If +message+ is a
|
178
|
+
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
|
179
|
+
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
|
180
180
|
def add(attribute, message = nil, options = {})
|
181
181
|
message ||= :invalid
|
182
182
|
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
|
@@ -223,7 +223,7 @@ module ActiveModel
|
|
223
223
|
else
|
224
224
|
attr_name = attribute.to_s.gsub('.', '_').humanize
|
225
225
|
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
|
226
|
-
options = { :default => "{
|
226
|
+
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
|
227
227
|
|
228
228
|
messages.each do |m|
|
229
229
|
full_messages << I18n.t(:"errors.format", options.merge(:message => m))
|
@@ -1,7 +1,7 @@
|
|
1
1
|
en:
|
2
2
|
errors:
|
3
3
|
# The default format use in full error messages.
|
4
|
-
format: "{
|
4
|
+
format: "%{attribute} %{message}"
|
5
5
|
|
6
6
|
# The values :model, :attribute and :value are always available for interpolation
|
7
7
|
# The value :count is available when applicable. Can be used for pluralization.
|
@@ -13,14 +13,15 @@ en:
|
|
13
13
|
accepted: "must be accepted"
|
14
14
|
empty: "can't be empty"
|
15
15
|
blank: "can't be blank"
|
16
|
-
too_long: "is too long (maximum is {
|
17
|
-
too_short: "is too short (minimum is {
|
18
|
-
wrong_length: "is the wrong length (should be {
|
16
|
+
too_long: "is too long (maximum is %{count} characters)"
|
17
|
+
too_short: "is too short (minimum is %{count} characters)"
|
18
|
+
wrong_length: "is the wrong length (should be %{count} characters)"
|
19
19
|
not_a_number: "is not a number"
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
not_an_integer: "must be an integer"
|
21
|
+
greater_than: "must be greater than %{count}"
|
22
|
+
greater_than_or_equal_to: "must be greater than or equal to %{count}"
|
23
|
+
equal_to: "must be equal to %{count}"
|
24
|
+
less_than: "must be less than %{count}"
|
25
|
+
less_than_or_equal_to: "must be less than or equal to %{count}"
|
25
26
|
odd: "must be odd"
|
26
27
|
even: "must be even"
|
@@ -1,17 +1,13 @@
|
|
1
|
-
require 'observer'
|
2
1
|
require 'singleton'
|
3
2
|
require 'active_support/core_ext/array/wrap'
|
4
3
|
require 'active_support/core_ext/module/aliasing'
|
5
4
|
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'active_support/core_ext/string/conversions'
|
6
6
|
|
7
7
|
module ActiveModel
|
8
8
|
module Observing
|
9
9
|
extend ActiveSupport::Concern
|
10
10
|
|
11
|
-
included do
|
12
|
-
extend Observable
|
13
|
-
end
|
14
|
-
|
15
11
|
module ClassMethods
|
16
12
|
# Activates the observers assigned. Examples:
|
17
13
|
#
|
@@ -41,6 +37,26 @@ module ActiveModel
|
|
41
37
|
observers.each { |o| instantiate_observer(o) }
|
42
38
|
end
|
43
39
|
|
40
|
+
def add_observer(observer)
|
41
|
+
unless observer.respond_to? :update
|
42
|
+
raise ArgumentError, "observer needs to respond to `update'"
|
43
|
+
end
|
44
|
+
@observer_instances ||= []
|
45
|
+
@observer_instances << observer
|
46
|
+
end
|
47
|
+
|
48
|
+
def notify_observers(*arg)
|
49
|
+
if defined? @observer_instances
|
50
|
+
for observer in @observer_instances
|
51
|
+
observer.update(*arg)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def count_observers
|
57
|
+
@observer_instances.size
|
58
|
+
end
|
59
|
+
|
44
60
|
protected
|
45
61
|
def instantiate_observer(observer) #:nodoc:
|
46
62
|
# string/symbol
|
@@ -56,7 +72,6 @@ module ActiveModel
|
|
56
72
|
# Notify observers when the observed class is subclassed.
|
57
73
|
def inherited(subclass)
|
58
74
|
super
|
59
|
-
changed
|
60
75
|
notify_observers :observed_class_inherited, subclass
|
61
76
|
end
|
62
77
|
end
|
@@ -70,7 +85,6 @@ module ActiveModel
|
|
70
85
|
# notify_observers(:after_save)
|
71
86
|
# end
|
72
87
|
def notify_observers(method)
|
73
|
-
self.class.changed
|
74
88
|
self.class.notify_observers(method, self)
|
75
89
|
end
|
76
90
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'active_support/json'
|
2
|
-
require 'active_support/core_ext/class/
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
3
|
|
4
4
|
module ActiveModel
|
5
5
|
module Serializers
|
@@ -10,7 +10,8 @@ module ActiveModel
|
|
10
10
|
included do
|
11
11
|
extend ActiveModel::Naming
|
12
12
|
|
13
|
-
|
13
|
+
class_attribute :include_root_in_json
|
14
|
+
self.include_root_in_json = true
|
14
15
|
end
|
15
16
|
|
16
17
|
# Returns a JSON string representing the model. Some configuration is
|
@@ -79,7 +80,11 @@ module ActiveModel
|
|
79
80
|
# "title": "So I was thinking"}]}
|
80
81
|
def encode_json(encoder)
|
81
82
|
hash = serializable_hash(encoder.options)
|
82
|
-
|
83
|
+
if include_root_in_json
|
84
|
+
custom_root = encoder.options && encoder.options[:root]
|
85
|
+
hash = { custom_root || self.class.model_name.element => hash }
|
86
|
+
end
|
87
|
+
|
83
88
|
ActiveSupport::JSON.encode(hash)
|
84
89
|
end
|
85
90
|
|
@@ -88,7 +93,9 @@ module ActiveModel
|
|
88
93
|
end
|
89
94
|
|
90
95
|
def from_json(json)
|
91
|
-
|
96
|
+
hash = ActiveSupport::JSON.decode(json)
|
97
|
+
hash = hash.values.first if include_root_in_json
|
98
|
+
self.attributes = hash
|
92
99
|
self
|
93
100
|
end
|
94
101
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'active_support/core_ext/array/wrap'
|
2
2
|
require 'active_support/core_ext/class/attribute_accessors'
|
3
|
+
require 'active_support/core_ext/array/conversions'
|
3
4
|
require 'active_support/core_ext/hash/conversions'
|
5
|
+
require 'active_support/core_ext/hash/slice'
|
4
6
|
|
5
7
|
module ActiveModel
|
6
8
|
module Serializers
|
@@ -12,68 +14,31 @@ module ActiveModel
|
|
12
14
|
class Attribute #:nodoc:
|
13
15
|
attr_reader :name, :value, :type
|
14
16
|
|
15
|
-
def initialize(name, serializable)
|
17
|
+
def initialize(name, serializable, raw_value=nil)
|
16
18
|
@name, @serializable = name, serializable
|
19
|
+
@value = value || @serializable.send(name)
|
17
20
|
@type = compute_type
|
18
|
-
@value = compute_value
|
19
21
|
end
|
20
22
|
|
21
|
-
|
22
|
-
# does not need to be escaped, as <tt>tag!</tt> escapes all values
|
23
|
-
# to ensure that valid XML is generated. For known binary
|
24
|
-
# values, it is at least an order of magnitude faster to
|
25
|
-
# Base64 encode binary values and directly put them in the
|
26
|
-
# output XML than to pass the original value or the Base64
|
27
|
-
# encoded value to the <tt>tag!</tt> method. It definitely makes
|
28
|
-
# no sense to Base64 encode the value and then give it to
|
29
|
-
# <tt>tag!</tt>, since that just adds additional overhead.
|
30
|
-
def needs_encoding?
|
31
|
-
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
|
32
|
-
end
|
33
|
-
|
34
|
-
def decorations(include_types = true)
|
23
|
+
def decorations
|
35
24
|
decorations = {}
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
if include_types && type != :string
|
42
|
-
decorations[:type] = type
|
43
|
-
end
|
44
|
-
|
45
|
-
if value.nil?
|
46
|
-
decorations[:nil] = true
|
47
|
-
end
|
48
|
-
|
25
|
+
decorations[:encoding] = 'base64' if type == :binary
|
26
|
+
decorations[:type] = type unless type == :string
|
27
|
+
decorations[:nil] = true if value.nil?
|
49
28
|
decorations
|
50
29
|
end
|
51
30
|
|
52
|
-
|
53
|
-
def compute_type
|
54
|
-
value = @serializable.send(name)
|
55
|
-
type = Hash::XML_TYPE_NAMES[value.class.name]
|
56
|
-
type ||= :string if value.respond_to?(:to_str)
|
57
|
-
type ||= :yaml
|
58
|
-
type
|
59
|
-
end
|
60
|
-
|
61
|
-
def compute_value
|
62
|
-
value = @serializable.send(name)
|
31
|
+
protected
|
63
32
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
33
|
+
def compute_type
|
34
|
+
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
|
35
|
+
type ||= :string if value.respond_to?(:to_str)
|
36
|
+
type ||= :yaml
|
37
|
+
type
|
38
|
+
end
|
70
39
|
end
|
71
40
|
|
72
41
|
class MethodAttribute < Attribute #:nodoc:
|
73
|
-
protected
|
74
|
-
def compute_type
|
75
|
-
Hash::XML_TYPE_NAMES[@serializable.send(name).class.name] || :string
|
76
|
-
end
|
77
42
|
end
|
78
43
|
|
79
44
|
attr_reader :options
|
@@ -92,104 +57,78 @@ module ActiveModel
|
|
92
57
|
# then because <tt>:except</tt> is set to a default value, the second
|
93
58
|
# level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
|
94
59
|
# <tt>:only</tt> is set, always delete <tt>:except</tt>.
|
95
|
-
def
|
96
|
-
|
97
|
-
|
60
|
+
def attributes_hash
|
61
|
+
attributes = @serializable.attributes
|
98
62
|
if options[:only].any?
|
99
|
-
|
63
|
+
attributes.slice(*options[:only])
|
100
64
|
elsif options[:except].any?
|
101
|
-
|
65
|
+
attributes.except(*options[:except])
|
66
|
+
else
|
67
|
+
attributes
|
102
68
|
end
|
103
|
-
|
104
|
-
attribute_names
|
105
69
|
end
|
106
70
|
|
107
71
|
def serializable_attributes
|
108
|
-
|
72
|
+
attributes_hash.map do |name, value|
|
73
|
+
self.class::Attribute.new(name, @serializable, value)
|
74
|
+
end
|
109
75
|
end
|
110
76
|
|
111
|
-
def
|
77
|
+
def serializable_methods
|
112
78
|
Array.wrap(options[:methods]).inject([]) do |methods, name|
|
113
|
-
methods << MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
|
79
|
+
methods << self.class::MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
|
114
80
|
methods
|
115
81
|
end
|
116
82
|
end
|
117
83
|
|
118
84
|
def serialize
|
119
|
-
|
120
|
-
|
121
|
-
if options[:namespace]
|
122
|
-
args << {:xmlns => options[:namespace]}
|
123
|
-
end
|
85
|
+
require 'builder' unless defined? ::Builder
|
124
86
|
|
125
|
-
|
126
|
-
|
127
|
-
end
|
87
|
+
options[:indent] ||= 2
|
88
|
+
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
|
128
89
|
|
129
|
-
builder
|
130
|
-
|
131
|
-
procs = options.delete(:procs)
|
132
|
-
options[:procs] = procs
|
133
|
-
add_procs
|
134
|
-
yield builder if block_given?
|
135
|
-
end
|
136
|
-
end
|
90
|
+
@builder = options[:builder]
|
91
|
+
@builder.instruct! unless options[:skip_instruct]
|
137
92
|
|
138
|
-
|
139
|
-
|
140
|
-
@builder ||= begin
|
141
|
-
require 'builder' unless defined? ::Builder
|
142
|
-
options[:indent] ||= 2
|
143
|
-
builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
|
93
|
+
root = (options[:root] || @serializable.class.model_name.element).to_s
|
94
|
+
root = ActiveSupport::XmlMini.rename_key(root, options)
|
144
95
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
end
|
149
|
-
|
150
|
-
builder
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def root
|
155
|
-
root = (options[:root] || @serializable.class.model_name.singular).to_s
|
156
|
-
reformat_name(root)
|
157
|
-
end
|
96
|
+
args = [root]
|
97
|
+
args << {:xmlns => options[:namespace]} if options[:namespace]
|
98
|
+
args << {:type => options[:type]} if options[:type] && !options[:skip_types]
|
158
99
|
|
159
|
-
|
160
|
-
|
100
|
+
@builder.tag!(*args) do
|
101
|
+
add_attributes_and_methods
|
102
|
+
add_extra_behavior
|
103
|
+
add_procs
|
104
|
+
yield @builder if block_given?
|
161
105
|
end
|
106
|
+
end
|
162
107
|
|
163
|
-
|
164
|
-
options.has_key?(:camelize) && options[:camelize]
|
165
|
-
end
|
108
|
+
private
|
166
109
|
|
167
|
-
|
168
|
-
|
169
|
-
dasherize? ? name.dasherize : name
|
170
|
-
end
|
110
|
+
def add_extra_behavior
|
111
|
+
end
|
171
112
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
attribute.decorations(!options[:skip_types])
|
178
|
-
)
|
179
|
-
end
|
113
|
+
def add_attributes_and_methods
|
114
|
+
(serializable_attributes + serializable_methods).each do |attribute|
|
115
|
+
key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
|
116
|
+
ActiveSupport::XmlMini.to_tag(key, attribute.value,
|
117
|
+
options.merge(attribute.decorations))
|
180
118
|
end
|
119
|
+
end
|
181
120
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
end
|
121
|
+
def add_procs
|
122
|
+
if procs = options.delete(:procs)
|
123
|
+
Array.wrap(procs).each do |proc|
|
124
|
+
if proc.arity == 1
|
125
|
+
proc.call(options)
|
126
|
+
else
|
127
|
+
proc.call(options, @serializable)
|
190
128
|
end
|
191
129
|
end
|
192
130
|
end
|
131
|
+
end
|
193
132
|
end
|
194
133
|
|
195
134
|
def to_xml(options = {}, &block)
|
@@ -29,7 +29,7 @@ module ActiveModel
|
|
29
29
|
# person.invalid?
|
30
30
|
# #=> false
|
31
31
|
# person.first_name = 'zoolander'
|
32
|
-
# person.valid?
|
32
|
+
# person.valid?
|
33
33
|
# #=> false
|
34
34
|
# person.invalid?
|
35
35
|
# #=> true
|
@@ -46,8 +46,14 @@ module ActiveModel
|
|
46
46
|
|
47
47
|
included do
|
48
48
|
extend ActiveModel::Translation
|
49
|
+
|
50
|
+
extend HelperMethods
|
51
|
+
include HelperMethods
|
52
|
+
|
49
53
|
define_callbacks :validate, :scope => :name
|
50
54
|
|
55
|
+
attr_accessor :validation_context
|
56
|
+
|
51
57
|
class_attribute :_validators
|
52
58
|
self._validators = Hash.new { |h,k| h[k] = [] }
|
53
59
|
end
|
@@ -117,7 +123,7 @@ module ActiveModel
|
|
117
123
|
options = args.last
|
118
124
|
if options.is_a?(Hash) && options.key?(:on)
|
119
125
|
options[:if] = Array.wrap(options[:if])
|
120
|
-
options[:if] << "
|
126
|
+
options[:if] << "validation_context == :#{options[:on]}"
|
121
127
|
end
|
122
128
|
set_callback(:validate, *args, &block)
|
123
129
|
end
|
@@ -133,11 +139,8 @@ module ActiveModel
|
|
133
139
|
_validators[attribute.to_sym]
|
134
140
|
end
|
135
141
|
|
136
|
-
|
137
|
-
|
138
|
-
def _merge_attributes(attr_names)
|
139
|
-
options = attr_names.extract_options!
|
140
|
-
options.merge(:attributes => attr_names.flatten)
|
142
|
+
def attribute_method?(attribute)
|
143
|
+
method_defined?(attribute)
|
141
144
|
end
|
142
145
|
end
|
143
146
|
|
@@ -147,15 +150,20 @@ module ActiveModel
|
|
147
150
|
end
|
148
151
|
|
149
152
|
# Runs all the specified validations and returns true if no errors were added otherwise false.
|
150
|
-
|
153
|
+
# Context can optionally be supplied to define which callbacks to test against (the context is
|
154
|
+
# defined on the validations using :on).
|
155
|
+
def valid?(context = nil)
|
156
|
+
current_context, self.validation_context = validation_context, context
|
151
157
|
errors.clear
|
152
158
|
_run_validate_callbacks
|
153
159
|
errors.empty?
|
160
|
+
ensure
|
161
|
+
self.validation_context = current_context
|
154
162
|
end
|
155
163
|
|
156
164
|
# Performs the opposite of <tt>valid?</tt>. Returns true if errors were added, false otherwise.
|
157
|
-
def invalid?
|
158
|
-
!valid?
|
165
|
+
def invalid?(context = nil)
|
166
|
+
!valid?(context)
|
159
167
|
end
|
160
168
|
|
161
169
|
# Hook method defining how an attribute value should be retieved. By default this is assumed
|
@@ -14,12 +14,14 @@ module ActiveModel
|
|
14
14
|
def setup(klass)
|
15
15
|
# Note: instance_methods.map(&:to_s) is important for 1.9 compatibility
|
16
16
|
# as instance_methods returns symbols unlike 1.8 which returns strings.
|
17
|
-
|
18
|
-
klass.
|
17
|
+
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
18
|
+
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
19
|
+
klass.send(:attr_reader, *attr_readers)
|
20
|
+
klass.send(:attr_writer, *attr_writers)
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
module
|
24
|
+
module HelperMethods
|
23
25
|
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
|
24
26
|
#
|
25
27
|
# class Person < ActiveRecord::Base
|
@@ -12,13 +12,13 @@ module ActiveModel
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
module
|
15
|
+
module HelperMethods
|
16
16
|
# Validates that the value of the specified attribute is not in a particular enumerable object.
|
17
17
|
#
|
18
18
|
# class Person < ActiveRecord::Base
|
19
19
|
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
|
20
20
|
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
|
21
|
-
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension {
|
21
|
+
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %{value} is not allowed"
|
22
22
|
# end
|
23
23
|
#
|
24
24
|
# Configuration options:
|
@@ -24,7 +24,7 @@ module ActiveModel
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
module
|
27
|
+
module HelperMethods
|
28
28
|
# Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided.
|
29
29
|
# You can require that the attribute matches the regular expression:
|
30
30
|
#
|
@@ -12,13 +12,13 @@ module ActiveModel
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
module
|
15
|
+
module HelperMethods
|
16
16
|
# Validates whether the value of the specified attribute is available in a particular enumerable object.
|
17
17
|
#
|
18
18
|
# class Person < ActiveRecord::Base
|
19
19
|
# validates_inclusion_of :gender, :in => %w( m f )
|
20
20
|
# validates_inclusion_of :age, :in => 0..99
|
21
|
-
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {
|
21
|
+
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %{value} is not included in the list"
|
22
22
|
# end
|
23
23
|
#
|
24
24
|
# Configuration options:
|
@@ -51,7 +51,7 @@ module ActiveModel
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
module
|
54
|
+
module HelperMethods
|
55
55
|
|
56
56
|
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
|
57
57
|
#
|
@@ -74,9 +74,9 @@ module ActiveModel
|
|
74
74
|
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
|
75
75
|
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
|
76
76
|
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
|
77
|
-
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is {
|
78
|
-
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is {
|
79
|
-
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be {
|
77
|
+
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %{count} characters)").
|
78
|
+
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %{count} characters)").
|
79
|
+
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %{count} characters)").
|
80
80
|
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
|
81
81
|
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
82
82
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
@@ -25,11 +25,18 @@ module ActiveModel
|
|
25
25
|
|
26
26
|
return if options[:allow_nil] && raw_value.nil?
|
27
27
|
|
28
|
-
unless value =
|
28
|
+
unless value = parse_raw_value_as_a_number(raw_value)
|
29
29
|
record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message])
|
30
30
|
return
|
31
31
|
end
|
32
32
|
|
33
|
+
if options[:only_integer]
|
34
|
+
unless value = parse_raw_value_as_an_integer(raw_value)
|
35
|
+
record.errors.add(attr_name, :not_an_integer, :value => raw_value, :default => options[:message])
|
36
|
+
return
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
33
40
|
options.slice(*CHECKS.keys).each do |option, option_value|
|
34
41
|
case option
|
35
42
|
when :odd, :even
|
@@ -44,14 +51,15 @@ module ActiveModel
|
|
44
51
|
record.errors.add(attr_name, option, :default => options[:message], :value => value, :count => option_value)
|
45
52
|
end
|
46
53
|
end
|
47
|
-
end
|
54
|
+
end
|
48
55
|
end
|
49
56
|
|
50
57
|
protected
|
51
58
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
59
|
+
def parse_raw_value_as_a_number(raw_value)
|
60
|
+
case raw_value
|
61
|
+
when /\A0[xX]/
|
62
|
+
nil
|
55
63
|
else
|
56
64
|
begin
|
57
65
|
Kernel.Float(raw_value)
|
@@ -61,9 +69,13 @@ module ActiveModel
|
|
61
69
|
end
|
62
70
|
end
|
63
71
|
|
72
|
+
def parse_raw_value_as_an_integer(raw_value)
|
73
|
+
raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
|
74
|
+
end
|
75
|
+
|
64
76
|
end
|
65
77
|
|
66
|
-
module
|
78
|
+
module HelperMethods
|
67
79
|
# Validates whether the value of the specified attribute is numeric by trying to convert it to
|
68
80
|
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
|
69
81
|
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
+
module HelperMethods
|
4
|
+
private
|
5
|
+
def _merge_attributes(attr_names)
|
6
|
+
options = attr_names.extract_options!
|
7
|
+
options.merge(:attributes => attr_names.flatten)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
3
11
|
module ClassMethods
|
4
12
|
|
5
13
|
# Passes the record off to the class or classes specified and allows them
|
@@ -75,5 +83,50 @@ module ActiveModel
|
|
75
83
|
end
|
76
84
|
end
|
77
85
|
end
|
86
|
+
|
87
|
+
# Passes the record off to the class or classes specified and allows them
|
88
|
+
# to add errors based on more complex conditions.
|
89
|
+
#
|
90
|
+
# class Person
|
91
|
+
# include ActiveModel::Validations
|
92
|
+
#
|
93
|
+
# validates :instance_validations
|
94
|
+
#
|
95
|
+
# def instance_validations
|
96
|
+
# validates_with MyValidator
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# Please consult the class method documentation for more information on
|
101
|
+
# creating your own validator.
|
102
|
+
#
|
103
|
+
# You may also pass it multiple classes, like so:
|
104
|
+
#
|
105
|
+
# class Person
|
106
|
+
# include ActiveModel::Validations
|
107
|
+
#
|
108
|
+
# validates :instance_validations, :on => :create
|
109
|
+
#
|
110
|
+
# def instance_validations
|
111
|
+
# validates_with MyValidator, MyOtherValidator
|
112
|
+
# end
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# Standard configuration options (:on, :if and :unless), which are
|
116
|
+
# available on the class version of validates_with, should instead be
|
117
|
+
# placed on the <tt>validates</tt> method as these are applied and tested
|
118
|
+
# in the callback
|
119
|
+
#
|
120
|
+
# If you pass any additional configuration options, they will be passed
|
121
|
+
# to the class and available as <tt>options</tt>, please refer to the
|
122
|
+
# class version of this method for more information
|
123
|
+
#
|
124
|
+
def validates_with(*args, &block)
|
125
|
+
options = args.extract_options!
|
126
|
+
args.each do |klass|
|
127
|
+
validator = klass.new(options, &block)
|
128
|
+
validator.validate(self)
|
129
|
+
end
|
130
|
+
end
|
78
131
|
end
|
79
132
|
end
|
@@ -88,6 +88,9 @@ module ActiveModel #:nodoc:
|
|
88
88
|
# klass.send :attr_accessor, :custom_attribute
|
89
89
|
# end
|
90
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.
|
91
94
|
#
|
92
95
|
class Validator
|
93
96
|
attr_reader :options
|
data/lib/active_model/version.rb
CHANGED
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 3
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 3.0.0.
|
9
|
+
- beta4
|
10
|
+
version: 3.0.0.beta4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- David Heinemeier Hansson
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-06-08 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -29,10 +29,38 @@ dependencies:
|
|
29
29
|
- 3
|
30
30
|
- 0
|
31
31
|
- 0
|
32
|
-
-
|
33
|
-
version: 3.0.0.
|
32
|
+
- beta4
|
33
|
+
version: 3.0.0.beta4
|
34
34
|
type: :runtime
|
35
35
|
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: builder
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 2
|
45
|
+
- 1
|
46
|
+
- 2
|
47
|
+
version: 2.1.2
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: i18n
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
- 4
|
60
|
+
- 1
|
61
|
+
version: 0.4.1
|
62
|
+
type: :runtime
|
63
|
+
version_requirements: *id003
|
36
64
|
description: A toolkit for building modeling frameworks like Active Record and Active Resource. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.
|
37
65
|
email: david@loudthinking.com
|
38
66
|
executables: []
|