validation_hints 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/active_model/hints.rb +203 -13
- data/lib/validation_hints/version.rb +1 -1
- metadata +1 -1
data/lib/active_model/hints.rb
CHANGED
@@ -22,6 +22,9 @@ module ActiveModel
|
|
22
22
|
def initialize(base)
|
23
23
|
@base = base
|
24
24
|
@messages = ActiveSupport::OrderedHash.new
|
25
|
+
@base.attributes.keys.each do |a|
|
26
|
+
@messages[a.to_sym] = validation_hints_for(a.to_sym)
|
27
|
+
end
|
25
28
|
end
|
26
29
|
|
27
30
|
def initialize_dup(other)
|
@@ -42,34 +45,223 @@ module ActiveModel
|
|
42
45
|
messages.clear
|
43
46
|
end
|
44
47
|
|
48
|
+
# Do the hint messages include an hint with key +hint+?
|
49
|
+
def include?(hint)
|
50
|
+
(v = messages[hint]) && v.any?
|
51
|
+
end
|
52
|
+
alias :has_key? :include?
|
53
|
+
|
54
|
+
# Get messages for +key+
|
55
|
+
def get(key)
|
56
|
+
messages[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
# Set messages for +key+ to +value+
|
60
|
+
def set(key, value)
|
61
|
+
messages[key] = value
|
62
|
+
end
|
63
|
+
|
64
|
+
# Delete messages for +key+
|
65
|
+
def delete(key)
|
66
|
+
messages.delete(key)
|
67
|
+
end
|
68
|
+
|
69
|
+
# When passed a symbol or a name of a method, returns an array of hints
|
70
|
+
# for the method.
|
71
|
+
#
|
72
|
+
# p.hints[:name] # => ["can not be nil"]
|
73
|
+
# p.hints['name'] # => ["can not be nil"]
|
74
|
+
def [](attribute)
|
75
|
+
get(attribute.to_sym) || set(attribute.to_sym, [])
|
76
|
+
end
|
77
|
+
|
78
|
+
# Adds to the supplied attribute the supplied hint message.
|
79
|
+
#
|
80
|
+
# p.hints[:name] = "must be set"
|
81
|
+
# p.hints[:name] # => ['must be set']
|
82
|
+
def []=(attribute, hint)
|
83
|
+
self[attribute] << hint
|
84
|
+
end
|
85
|
+
|
86
|
+
# Iterates through each hint key, value pair in the hint messages hash.
|
87
|
+
# Yields the attribute and the hint for that attribute. If the attribute
|
88
|
+
# has more than one hint message, yields once for each hint message.
|
89
|
+
#
|
90
|
+
# p.hints.add(:name, "can't be blank")
|
91
|
+
# p.hints.each do |attribute, hints_array|
|
92
|
+
# # Will yield :name and "can't be blank"
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# p.hints.add(:name, "must be specified")
|
96
|
+
# p.hints.each do |attribute, hints_array|
|
97
|
+
# # Will yield :name and "can't be blank"
|
98
|
+
# # then yield :name and "must be specified"
|
99
|
+
# end
|
100
|
+
def each
|
101
|
+
messages.each_key do |attribute|
|
102
|
+
self[attribute].each { |hint| yield attribute, hint }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the number of error messages.
|
107
|
+
#
|
108
|
+
# p.hints.add(:name, "can't be blank")
|
109
|
+
# p.hints.size # => 1
|
110
|
+
# p.hints.add(:name, "must be specified")
|
111
|
+
# p.hints.size # => 2
|
112
|
+
def size
|
113
|
+
values.flatten.size
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns all message values
|
117
|
+
def values
|
118
|
+
messages.values
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns all message keys
|
122
|
+
def keys
|
123
|
+
messages.keys
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns an array of hint messages, with the attribute name included
|
127
|
+
#
|
128
|
+
# p.hints.add(:name, "can't be blank")
|
129
|
+
# p.hints.add(:name, "must be specified")
|
130
|
+
# p.hints.to_a # => ["name can't be blank", "name must be specified"]
|
131
|
+
def to_a
|
132
|
+
full_messages
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the number of hint messages.
|
136
|
+
# p.hints.add(:name, "can't be blank")
|
137
|
+
# p.hints.count # => 1
|
138
|
+
# p.hints.add(:name, "must be specified")
|
139
|
+
# p.hints.count # => 2
|
140
|
+
def count
|
141
|
+
to_a.size
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns true if no hints are found, false otherwise.
|
145
|
+
# If the hint message is a string it can be empty.
|
146
|
+
def empty?
|
147
|
+
all? { |k, v| v && v.empty? && !v.is_a?(String) }
|
148
|
+
end
|
149
|
+
alias_method :blank?, :empty?
|
150
|
+
|
151
|
+
# Returns an xml formatted representation of the hints hash.
|
152
|
+
#
|
153
|
+
# p.hints.add(:name, "can't be blank")
|
154
|
+
# p.hints.add(:name, "must be specified")
|
155
|
+
# p.hints.to_xml
|
156
|
+
# # =>
|
157
|
+
# # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
158
|
+
# # <hints>
|
159
|
+
# # <hint>name can't be blank</hint>
|
160
|
+
# # <hint>name must be specified</hint>
|
161
|
+
# # </hints>
|
162
|
+
def to_xml(options={})
|
163
|
+
to_a.to_xml options.reverse_merge(:root => "hints", :skip_types => true)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
|
167
|
+
def as_json(options=nil)
|
168
|
+
to_hash
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_hash
|
172
|
+
messages.dup
|
173
|
+
end
|
174
|
+
|
175
|
+
# Adds +message+ to the hint messages on +attribute+. More than one hint can be added to the same
|
176
|
+
# +attribute+.
|
177
|
+
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
|
178
|
+
#
|
179
|
+
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_hint+).
|
180
|
+
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an hint.
|
181
|
+
def add(attribute, message = nil, options = {})
|
182
|
+
message = normalize_message(attribute, message, options)
|
183
|
+
if options[:strict]
|
184
|
+
raise ActiveModel::StrictValidationFailed, full_message(attribute, message)
|
185
|
+
end
|
186
|
+
|
187
|
+
self[attribute] << message
|
188
|
+
end
|
189
|
+
|
190
|
+
# Will add an hint message to each of the attributes in +attributes+ that is empty.
|
191
|
+
def add_on_empty(attributes, options = {})
|
192
|
+
[attributes].flatten.each do |attribute|
|
193
|
+
value = @base.send(:read_attribute_for_validation, attribute)
|
194
|
+
is_empty = value.respond_to?(:empty?) ? value.empty? : false
|
195
|
+
add(attribute, :empty, options) if value.nil? || is_empty
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Will add an hint message to each of the attributes in +attributes+ that is blank (using Object#blank?).
|
200
|
+
def add_on_blank(attributes, options = {})
|
201
|
+
[attributes].flatten.each do |attribute|
|
202
|
+
value = @base.send(:read_attribute_for_validation, attribute)
|
203
|
+
add(attribute, :blank, options) if value.blank?
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Returns true if an hint on the attribute with the given message is present, false otherwise.
|
208
|
+
# +message+ is treated the same as for +add+.
|
209
|
+
# p.hints.add :name, :blank
|
210
|
+
# p.hints.added? :name, :blank # => true
|
211
|
+
def added?(attribute, message = nil, options = {})
|
212
|
+
message = normalize_message(attribute, message, options)
|
213
|
+
self[attribute].include? message
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns all the full hint messages in an array.
|
217
|
+
#
|
218
|
+
# class Company
|
219
|
+
# validates_presence_of :name, :address, :email
|
220
|
+
# validates_length_of :name, :in => 5..30
|
221
|
+
# end
|
222
|
+
#
|
223
|
+
# company = Company.create(:address => '123 First St.')
|
224
|
+
# company.hints.full_messages # =>
|
225
|
+
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
|
226
|
+
def full_messages
|
227
|
+
map { |attribute, message| full_message(attribute, message) }
|
228
|
+
end
|
229
|
+
|
230
|
+
# Returns a full message for a given attribute.
|
231
|
+
#
|
232
|
+
# company.hints.full_message(:name, "is invalid") # =>
|
233
|
+
# "Name is invalid"
|
234
|
+
def full_message(attribute, message)
|
235
|
+
return message if attribute == :base
|
236
|
+
attr_name = attribute.to_s.gsub('.', '_').humanize
|
237
|
+
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
|
238
|
+
I18n.t(:"hints.format", {
|
239
|
+
:default => "%{attribute} %{message}",
|
240
|
+
:attribute => attr_name,
|
241
|
+
:message => message
|
242
|
+
})
|
243
|
+
end
|
244
|
+
|
45
245
|
def validation_hints_for(attribute)
|
46
246
|
result = Array.new
|
47
247
|
@base.class.validators_on(attribute).map do |v|
|
48
|
-
puts "** ** ** ** V " + v.class.to_s.split('::').last.downcase.gsub('validator','')
|
248
|
+
# puts "** ** ** ** V " + v.class.to_s.split('::').last.downcase.gsub('validator','')
|
49
249
|
# check for validators that have no options
|
50
250
|
validator = v.class.to_s.split('::').last.downcase.gsub('validator','')
|
51
251
|
if MESSAGES_FOR_VALIDATORS.include?(validator)
|
52
252
|
result << generate_message(attribute, validator)
|
53
253
|
end
|
54
254
|
v.options.each do |o|
|
55
|
-
puts "** ** ** ** O " + o.inspect
|
255
|
+
# puts "** ** ** ** O " + o.inspect
|
56
256
|
if MESSAGES_FOR_OPTIONS.include?(o.first.to_s)
|
57
257
|
result << generate_message(attribute, [ validator, o.first.to_s ].join('.'), { :count => o.last } )
|
58
258
|
end
|
59
259
|
end
|
60
260
|
end
|
61
|
-
|
62
|
-
# key = v.class.to_s.underscore.gsub('/','.')
|
63
|
-
# puts "************#{v.inspect}"
|
64
|
-
# key = [v.qualifier, key].join('.') if v.respond_to?(:qualifier)
|
65
|
-
# [ key, v.options.except(*CALLBACKS_OPTIONS).keys.map do |o|
|
66
|
-
# key + "." + o.to_s
|
67
|
-
# end ].flatten
|
68
261
|
result
|
69
262
|
end
|
70
263
|
|
71
264
|
def generate_message(attribute, type, options = {})
|
72
|
-
|
73
265
|
if @base.class.respond_to?(:i18n_scope)
|
74
266
|
defaults = @base.class.lookup_ancestors.map do |klass|
|
75
267
|
[ :"#{@base.class.i18n_scope}.hints.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
|
@@ -93,12 +285,10 @@ module ActiveModel
|
|
93
285
|
:model => @base.class.model_name.human,
|
94
286
|
:attribute => @base.class.human_attribute_name(attribute),
|
95
287
|
}.merge(options)
|
96
|
-
puts "*" + File.basename(__FILE__) + ": " + "ATTR #{attribute}, OPTIONS #{options.inspect} "
|
97
|
-
# [ key, options ]
|
288
|
+
# puts "*" + File.basename(__FILE__) + ": " + "ATTR #{attribute}, OPTIONS #{options.inspect} "
|
98
289
|
I18n.translate(key, options)
|
99
290
|
end
|
100
291
|
|
101
|
-
|
102
292
|
end
|
103
293
|
|
104
294
|
end
|