validation_hints 0.0.6 → 0.0.7
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/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
|