Class: ActiveModel::Hints

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/active_model/hints.rb

Overview

Active Model Hints

p = Person.new p.hints p.hints

more documentation needed

Constant Summary collapse

CALLBACKS_OPTIONS =
[:if, :unless, :on, :allow_nil, :allow_blank, :strict]
MESSAGES_FOR_VALIDATORS =
%w(confirmation acceptance presence uniqueness format associated numericality)
VALIDATORS_WITHOUT_MAIN_KEYS =
%w(exclusion format inclusion length numericality)
MESSAGES_FOR_OPTIONS =

and these? validates_with validates_each

%w(within in is minimum maximum greater_than greater_than_or_equal_to equal_to less_than less_than_or_equal_to odd even only_integer)
OPTIONS_THAT_WE_DONT_USE_YET =
{
  :acceptance => :acceptance

}
VALIDATORS_THAT_WE_DONT_KNOW_WHAT_TO_DO_WITH =
%w(validates_associated)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base) ⇒ Hints

Pass in the instance of the object that is using the errors object.

class Person
  def initialize
    @errors = ActiveModel::Errors.new(self)
  end
end


38
39
40
41
42
43
44
# File 'lib/active_model/hints.rb', line 38

def initialize(base)
  @base     = base
  @messages = ActiveSupport::OrderedHash.new
  @base.attributes.keys.each do |a|
    @messages[a.to_sym] = hints_for(a.to_sym)
  end
end

Instance Attribute Details

#messagesObject (readonly)

Should virtual element for

validates :email, :confirmation => true
validates :email_confirmation, :presence => true

also have a hint?



29
30
31
# File 'lib/active_model/hints.rb', line 29

def messages
  @messages
end

Instance Method Details

#[](attribute) ⇒ Object

When passed a symbol or a name of a method, returns an array of hints for the method.

p.hints[:name]   # => ["can not be nil"]
p.hints['name']  # => ["can not be nil"]


117
118
119
# File 'lib/active_model/hints.rb', line 117

def [](attribute)
  get(attribute.to_sym) || set(attribute.to_sym, [])
end

#[]=(attribute, hint) ⇒ Object

Adds to the supplied attribute the supplied hint message.

p.hints[:name] = "must be set"
p.hints[:name] # => ['must be set']


125
126
127
# File 'lib/active_model/hints.rb', line 125

def []=(attribute, hint)
  self[attribute] << hint
end

#add(attribute, message = nil, options = {}) ⇒ Object

Adds message to the hint messages on attribute. More than one hint can be added to the same attribute. If no message is supplied, :invalid is assumed.

If message is a symbol, it will be translated using the appropriate scope (see translate_hint). If message is a proc, it will be called, allowing for things like Time.now to be used within an hint.



224
225
226
227
228
229
230
231
# File 'lib/active_model/hints.rb', line 224

def add(attribute, message = nil, options = {})
  message = normalize_message(attribute, message, options)
  if options[:strict]
    raise ActiveModel::StrictValidationFailed, full_message(attribute, message)
  end

  self[attribute] << message
end

#add_on_blank(attributes, options = {}) ⇒ Object

Will add an hint message to each of the attributes in attributes that is blank (using Object#blank?).



243
244
245
246
247
248
# File 'lib/active_model/hints.rb', line 243

def add_on_blank(attributes, options = {})
  [attributes].flatten.each do |attribute|
    value = @base.send(:read_attribute_for_validation, attribute)
    add(attribute, :blank, options) if value.blank?
  end
end

#add_on_empty(attributes, options = {}) ⇒ Object

Will add an hint message to each of the attributes in attributes that is empty.



234
235
236
237
238
239
240
# File 'lib/active_model/hints.rb', line 234

def add_on_empty(attributes, options = {})
  [attributes].flatten.each do |attribute|
    value = @base.send(:read_attribute_for_validation, attribute)
    is_empty = value.respond_to?(:empty?) ? value.empty? : false
    add(attribute, :empty, options) if value.nil? || is_empty
  end
end

#added?(attribute, message = nil, options = {}) ⇒ Boolean

Returns true if an hint on the attribute with the given message is present, false otherwise. message is treated the same as for add.

p.hints.add :name, :blank
p.hints.added? :name, :blank # => true

Returns:

  • (Boolean)


254
255
256
257
# File 'lib/active_model/hints.rb', line 254

def added?(attribute, message = nil, options = {})
  message = normalize_message(attribute, message, options)
  self[attribute].include? message
end

#as_json(options = nil) ⇒ Object

Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.



210
211
212
# File 'lib/active_model/hints.rb', line 210

def as_json(options=nil)
  to_hash
end

#clearObject

Clear the messages



87
88
89
# File 'lib/active_model/hints.rb', line 87

def clear
  messages.clear
end

#countObject

Returns the number of hint messages.

p.hints.add(:name, "can't be blank")
p.hints.count # => 1
p.hints.add(:name, "must be specified")
p.hints.count # => 2


183
184
185
# File 'lib/active_model/hints.rb', line 183

def count
  to_a.size
end

#delete(key) ⇒ Object

Delete messages for key



108
109
110
# File 'lib/active_model/hints.rb', line 108

def delete(key)
  messages.delete(key)
end

#dupObject

:nodoc:



79
80
81
82
83
# File 'lib/active_model/hints.rb', line 79

def dup # :nodoc:
  copy = super
  copy.initialize_dup(self)
  copy
end

#eachObject

Iterates through each hint key, value pair in the hint messages hash. Yields the attribute and the hint for that attribute. If the attribute has more than one hint message, yields once for each hint message.

p.hints.add(:name, "can't be blank")
p.hints.each do |attribute, hints_array|
  # Will yield :name and "can't be blank"
end

p.hints.add(:name, "must be specified")
p.hints.each do |attribute, hints_array|
  # Will yield :name and "can't be blank"
  # then yield :name and "must be specified"
end


143
144
145
146
147
# File 'lib/active_model/hints.rb', line 143

def each
  messages.each_key do |attribute|
    self[attribute].each { |hint| yield attribute, hint }
  end
end

#empty?Boolean Also known as: blank?

Returns true if no hints are found, false otherwise. If the hint message is a string it can be empty.

Returns:

  • (Boolean)


189
190
191
# File 'lib/active_model/hints.rb', line 189

def empty?
  all? { |k, v| v && v.empty? && !v.is_a?(String) }
end

#full_message(attribute, message) ⇒ Object

Returns a full message for a given attribute.

company.hints.full_message(:name, "is invalid")  # =>
  "Name is invalid"


277
278
279
280
281
282
283
284
285
286
# File 'lib/active_model/hints.rb', line 277

def full_message(attribute, message)
  return message if attribute == :base
  attr_name = attribute.to_s.gsub('.', '_').humanize
  attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
  I18n.t(:"hints.format", {
      :default   => "%{attribute} %{message}",
      :attribute => attr_name,
      :message   => message
    })
end

#full_messagesObject

Returns all the full hint messages in an array.

class Company
  validates_presence_of :name, :address, :email
  validates_length_of :name, :in => 5..30
end

company = Company.create(:address => '123 First St.')
company.hints.full_messages # =>
  ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]


269
270
271
# File 'lib/active_model/hints.rb', line 269

def full_messages
  map { |attribute, message| full_message(attribute, message) }
end

#full_messages_for(attribute) ⇒ Object



69
70
71
# File 'lib/active_model/hints.rb', line 69

def full_messages_for(attribute)
  hints_for(attribute).map { |message| full_message(attribute, message) }
end

#generate_message(attribute, type, options = {}) ⇒ Object



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/active_model/hints.rb', line 288

def generate_message(attribute, type, options = {})
  #options.delete(:message) if options[:message].is_a?(Symbol)
  if @base.class.respond_to?(:i18n_scope)
    defaults = @base.class.lookup_ancestors.map do |klass|
      [ :"#{@base.class.i18n_scope}.hints.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
        :"#{@base.class.i18n_scope}.hints.models.#{klass.model_name.i18n_key}.#{type}" ]
    end
  else
    defaults = []
  end

  defaults << options[:message] # defaults << options.delete(:message)
  defaults << :"#{@base.class.i18n_scope}.hints.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
  defaults << :"hints.attributes.#{attribute}.#{type}"
  defaults << :"hints.messages.#{type}"

  defaults.compact!
  defaults.flatten!

  key = defaults.shift

  options = {
    :default => defaults,
    :model => @base.class.model_name.human,
    :attribute => @base.class.human_attribute_name(attribute),
  }.merge(options)
  I18n.translate(key, options)
end

#get(key) ⇒ Object

Get messages for key



98
99
100
# File 'lib/active_model/hints.rb', line 98

def get(key)
  messages[key]
end

#hints_for(attribute) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/active_model/hints.rb', line 46

def hints_for(attribute)
  result = Array.new
  @base.class.validators_on(attribute).map do |v|
    validator = v.class.to_s.split('::').last.underscore.gsub('_validator','')
    if v.options[:message].is_a?(Symbol)
      message_key =  [validator, v.options[:message]].join('.') # if a message was supplied as a symbol, we use it instead
      result << generate_message(attribute, message_key, v.options)
    else
      message_key =  validator
      message_key =  [validator, ".must_be_a_number"].join('.') if validator == 'numericality' # create an option for numericality; the way YAML works a key (numericality) with subkeys (greater_than, etc etc) can not have a string itself. So we create a subkey for numericality
      result << generate_message(attribute, message_key, v.options) unless VALIDATORS_WITHOUT_MAIN_KEYS.include?(validator)
      v.options.each do |o|
        if MESSAGES_FOR_OPTIONS.include?(o.first.to_s)
          count = o.last
          count = (o.last.to_sentence if %w(inclusion exclusion).include?(validator)) rescue o.last
          result << generate_message(attribute, [ validator, o.first.to_s ].join('.'), { :count => count } )
        end
      end
    end
  end
  result
end

#include?(hint) ⇒ Boolean Also known as: has_key?

Do the hint messages include an hint with key hint?

Returns:

  • (Boolean)


92
93
94
# File 'lib/active_model/hints.rb', line 92

def include?(hint)
  (v = messages[hint]) && v.any?
end

#initialize_dup(other) ⇒ Object



73
74
75
# File 'lib/active_model/hints.rb', line 73

def initialize_dup(other)
  @messages = other.messages.dup
end

#keysObject

Returns all message keys



165
166
167
# File 'lib/active_model/hints.rb', line 165

def keys
  messages.keys
end

#set(key, value) ⇒ Object

Set messages for key to value



103
104
105
# File 'lib/active_model/hints.rb', line 103

def set(key, value)
  messages[key] = value
end

#sizeObject

Returns the number of error messages.

p.hints.add(:name, "can't be blank")
p.hints.size # => 1
p.hints.add(:name, "must be specified")
p.hints.size # => 2


155
156
157
# File 'lib/active_model/hints.rb', line 155

def size
  values.flatten.size
end

#to_aObject

Returns an array of hint messages, with the attribute name included

p.hints.add(:name, "can't be blank")
p.hints.add(:name, "must be specified")
p.hints.to_a # => ["name can't be blank", "name must be specified"]


174
175
176
# File 'lib/active_model/hints.rb', line 174

def to_a
  full_messages
end

#to_hashObject



214
215
216
# File 'lib/active_model/hints.rb', line 214

def to_hash
  messages.dup
end

#to_xml(options = {}) ⇒ Object

Returns an xml formatted representation of the hints hash.

p.hints.add(:name, "can't be blank")
p.hints.add(:name, "must be specified")
p.hints.to_xml
# =>
#  <?xml version=\"1.0\" encoding=\"UTF-8\"?>
#  <hints>
#    <hint>name can't be blank</hint>
#    <hint>name must be specified</hint>
#  </hints>


205
206
207
# File 'lib/active_model/hints.rb', line 205

def to_xml(options={})
  to_a.to_xml options.reverse_merge(:root => "hints", :skip_types => true)
end

#valuesObject

Returns all message values



160
161
162
# File 'lib/active_model/hints.rb', line 160

def values
  messages.values
end