activemodel 3.0.0.beta3 → 3.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|