activemodel 3.0.0.beta4 → 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +1 -39
  2. data/MIT-LICENSE +1 -1
  3. data/README +16 -200
  4. data/lib/active_model.rb +19 -28
  5. data/lib/active_model/attribute_methods.rb +27 -142
  6. data/lib/active_model/conversion.rb +1 -37
  7. data/lib/active_model/dirty.rb +12 -51
  8. data/lib/active_model/errors.rb +22 -146
  9. data/lib/active_model/lint.rb +14 -48
  10. data/lib/active_model/locale/en.yml +23 -26
  11. data/lib/active_model/naming.rb +5 -41
  12. data/lib/active_model/observing.rb +16 -35
  13. data/lib/active_model/serialization.rb +0 -57
  14. data/lib/active_model/serializers/json.rb +8 -13
  15. data/lib/active_model/serializers/xml.rb +123 -63
  16. data/lib/active_model/state_machine.rb +70 -0
  17. data/lib/active_model/state_machine/event.rb +62 -0
  18. data/lib/active_model/state_machine/machine.rb +75 -0
  19. data/lib/active_model/state_machine/state.rb +47 -0
  20. data/lib/active_model/state_machine/state_transition.rb +40 -0
  21. data/lib/active_model/test_case.rb +2 -0
  22. data/lib/active_model/validations.rb +62 -125
  23. data/lib/active_model/validations/acceptance.rb +18 -23
  24. data/lib/active_model/validations/confirmation.rb +10 -14
  25. data/lib/active_model/validations/exclusion.rb +13 -15
  26. data/lib/active_model/validations/format.rb +24 -26
  27. data/lib/active_model/validations/inclusion.rb +13 -15
  28. data/lib/active_model/validations/length.rb +65 -61
  29. data/lib/active_model/validations/numericality.rb +58 -76
  30. data/lib/active_model/validations/presence.rb +8 -8
  31. data/lib/active_model/validations/with.rb +22 -90
  32. data/lib/active_model/validations_repair_helper.rb +35 -0
  33. data/lib/active_model/version.rb +2 -3
  34. metadata +19 -63
  35. data/lib/active_model/callbacks.rb +0 -134
  36. data/lib/active_model/railtie.rb +0 -2
  37. data/lib/active_model/translation.rb +0 -60
  38. data/lib/active_model/validations/validates.rb +0 -108
  39. data/lib/active_model/validator.rb +0 -183
@@ -1,2 +0,0 @@
1
- require "active_model"
2
- require "rails"
@@ -1,60 +0,0 @@
1
- require 'active_support/core_ext/hash/reverse_merge'
2
-
3
- module ActiveModel
4
-
5
- # ActiveModel::Translation provides integration between your object and
6
- # the Rails internationalization (i18n) framework.
7
- #
8
- # A minimal implementation could be:
9
- #
10
- # class TranslatedPerson
11
- # extend ActiveModel::Translation
12
- # end
13
- #
14
- # TranslatedPerson.human_attribute_name('my_attribue')
15
- # #=> "My attribute"
16
- #
17
- # This also provides the required class methods for hooking into the
18
- # Rails internationalization API, including being able to define a
19
- # class based i18n_scope and lookup_ancestors to find translations in
20
- # parent classes.
21
- module Translation
22
- include ActiveModel::Naming
23
-
24
- # Returns the i18n_scope for the class. Overwrite if you want custom lookup.
25
- def i18n_scope
26
- :activemodel
27
- end
28
-
29
- # When localizing a string, goes through the lookup returned by this method.
30
- # Used in ActiveModel::Name#human, ActiveModel::Errors#full_messages and
31
- # ActiveModel::Translation#human_attribute_name.
32
- def lookup_ancestors
33
- self.ancestors.select { |x| x.respond_to?(:model_name) }
34
- end
35
-
36
- # Transforms attributes names into a more human format, such as "First name" instead of "first_name".
37
- #
38
- # Person.human_attribute_name("first_name") # => "First name"
39
- #
40
- # Specify +options+ with additional translating options.
41
- def human_attribute_name(attribute, options = {})
42
- defaults = lookup_ancestors.map do |klass|
43
- :"#{self.i18n_scope}.attributes.#{klass.model_name.underscore}.#{attribute}"
44
- end
45
-
46
- defaults << :"attributes.#{attribute}"
47
- defaults << options.delete(:default) if options[:default]
48
- defaults << attribute.to_s.humanize
49
-
50
- options.reverse_merge! :count => 1, :default => defaults
51
- I18n.translate(defaults.shift, options)
52
- end
53
-
54
- # Model.human_name is deprecated. Use Model.model_name.human instead.
55
- def human_name(*args)
56
- ActiveSupport::Deprecation.warn("human_name has been deprecated, please use model_name.human instead", caller[0,5])
57
- model_name.human(*args)
58
- end
59
- end
60
- end
@@ -1,108 +0,0 @@
1
- require 'active_support/core_ext/hash/slice'
2
-
3
- module ActiveModel
4
- module Validations
5
- module ClassMethods
6
- # This method is a shortcut to all default validators and any custom
7
- # validator classes ending in 'Validator'. Note that Rails default
8
- # validators can be overridden inside specific classes by creating
9
- # custom validator classes in their place such as PresenceValidator.
10
- #
11
- # Examples of using the default rails validators:
12
- #
13
- # validates :terms, :acceptance => true
14
- # validates :password, :confirmation => true
15
- # validates :username, :exclusion => { :in => %w(admin superuser) }
16
- # validates :email, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create }
17
- # validates :age, :inclusion => { :in => 0..9 }
18
- # validates :first_name, :length => { :maximum => 30 }
19
- # validates :age, :numericality => true
20
- # validates :username, :presence => true
21
- # validates :username, :uniqueness => true
22
- #
23
- # The power of the +validates+ method comes when using cusom validators
24
- # and default validators in one call for a given attribute e.g.
25
- #
26
- # class EmailValidator < ActiveModel::EachValidator
27
- # def validate_each(record, attribute, value)
28
- # record.errors[attribute] << (options[:message] || "is not an email") unless
29
- # value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
30
- # end
31
- # end
32
- #
33
- # class Person
34
- # include ActiveModel::Validations
35
- # attr_accessor :name, :email
36
- #
37
- # validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
38
- # validates :email, :presence => true, :email => true
39
- # end
40
- #
41
- # Validator classes my also exist within the class being validated
42
- # allowing custom modules of validators to be included as needed e.g.
43
- #
44
- # class Film
45
- # include ActiveModel::Validations
46
- #
47
- # class TitleValidator < ActiveModel::EachValidator
48
- # def validate_each(record, attribute, value)
49
- # record.errors[attribute] << "must start with 'the'" unless =~ /^the/i
50
- # end
51
- # end
52
- #
53
- # validates :name, :title => true
54
- # end
55
- #
56
- # The validators hash can also handle regular expressions, ranges and arrays:
57
- #
58
- # validates :email, :format => /@/
59
- # validates :gender, :inclusion => %w(male female)
60
- # validates :password, :length => 6..20
61
- #
62
- # Finally, the options :if, :unless, :on, :allow_blank and :allow_nil can be given
63
- # to one specific validator:
64
- #
65
- # validates :password, :presence => { :if => :password_required? }, :confirmation => true
66
- #
67
- # Or to all at the same time:
68
- #
69
- # validates :password, :presence => true, :confirmation => true, :if => :password_required?
70
- #
71
- def validates(*attributes)
72
- defaults = attributes.extract_options!
73
- validations = defaults.slice!(:if, :unless, :on, :allow_blank, :allow_nil)
74
-
75
- raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
76
- raise ArgumentError, "Attribute names must be symbols" if attributes.any?{ |attribute| !attribute.is_a?(Symbol) }
77
- raise ArgumentError, "You need to supply at least one validation" if validations.empty?
78
-
79
- defaults.merge!(:attributes => attributes)
80
-
81
- validations.each do |key, options|
82
- begin
83
- validator = const_get("#{key.to_s.camelize}Validator")
84
- rescue NameError
85
- raise ArgumentError, "Unknown validator: '#{key}'"
86
- end
87
-
88
- validates_with(validator, defaults.merge(_parse_validates_options(options)))
89
- end
90
- end
91
-
92
- protected
93
-
94
- def _parse_validates_options(options) #:nodoc:
95
- case options
96
- when TrueClass
97
- {}
98
- when Hash
99
- options
100
- when Regexp
101
- { :with => options }
102
- when Range, Array
103
- { :in => options }
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,183 +0,0 @@
1
- require 'active_support/core_ext/array/wrap'
2
- require "active_support/core_ext/module/anonymous"
3
- require 'active_support/core_ext/object/blank'
4
-
5
- module ActiveModel #:nodoc:
6
- # A simple base class that can be used along with
7
- # +ActiveModel::Validations::ClassMethods.validates_with+
8
- #
9
- # class Person
10
- # include ActiveModel::Validations
11
- # validates_with MyValidator
12
- # end
13
- #
14
- # class MyValidator < ActiveModel::Validator
15
- # def validate(record)
16
- # if some_complex_logic
17
- # record.errors[:base] = "This record is invalid"
18
- # end
19
- # end
20
- #
21
- # private
22
- # def some_complex_logic
23
- # # ...
24
- # end
25
- # end
26
- #
27
- # Any class that inherits from ActiveModel::Validator must implement a method
28
- # called <tt>validate</tt> which accepts a <tt>record</tt>.
29
- #
30
- # class Person
31
- # include ActiveModel::Validations
32
- # validates_with MyValidator
33
- # end
34
- #
35
- # class MyValidator < ActiveModel::Validator
36
- # def validate(record)
37
- # record # => The person instance being validated
38
- # options # => Any non-standard options passed to validates_with
39
- # end
40
- # end
41
- #
42
- # To cause a validation error, you must add to the <tt>record<tt>'s errors directly
43
- # from within the validators message
44
- #
45
- # class MyValidator < ActiveModel::Validator
46
- # def validate(record)
47
- # record.errors[:base] << "This is some custom error message"
48
- # record.errors[:first_name] << "This is some complex validation"
49
- # # etc...
50
- # end
51
- # end
52
- #
53
- # To add behavior to the initialize method, use the following signature:
54
- #
55
- # class MyValidator < ActiveModel::Validator
56
- # def initialize(record, options)
57
- # super
58
- # @my_custom_field = options[:field_name] || :first_name
59
- # end
60
- # end
61
- #
62
- # The easiest way to add custom validators for validating individual attributes
63
- # is with the convenient ActiveModel::EachValidator for example:
64
- #
65
- # class TitleValidator < ActiveModel::EachValidator
66
- # def validate_each(record, attribute, value)
67
- # record.errors[attribute] << 'must be Mr. Mrs. or Dr.' unless ['Mr.', 'Mrs.', 'Dr.'].include?(value)
68
- # end
69
- # end
70
- #
71
- # This can now be used in combination with the +validates+ method
72
- # (see ActiveModel::Validations::ClassMethods.validates for more on this)
73
- #
74
- # class Person
75
- # include ActiveModel::Validations
76
- # attr_accessor :title
77
- #
78
- # validates :title, :presence => true, :title => true
79
- # end
80
- #
81
- # Validator may also define a +setup+ instance method which will get called
82
- # with the class that using that validator as it's argument. This can be
83
- # useful when there are prerequisites such as an attr_accessor being present
84
- # for example:
85
- #
86
- # class MyValidator < ActiveModel::Validator
87
- # def setup(klass)
88
- # klass.send :attr_accessor, :custom_attribute
89
- # end
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.
94
- #
95
- class Validator
96
- attr_reader :options
97
-
98
- # Returns the kind of the validator.
99
- #
100
- # == Examples
101
- #
102
- # PresenceValidator.kind #=> :presence
103
- # UniquenessValidator.kind #=> :uniqueness
104
- #
105
- def self.kind
106
- @kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
107
- end
108
-
109
- # Accepts options that will be made availible through the +options+ reader.
110
- def initialize(options)
111
- @options = options
112
- end
113
-
114
- # Return the kind for this validator.
115
- def kind
116
- self.class.kind
117
- end
118
-
119
- # Override this method in subclasses with validation logic, adding errors
120
- # to the records +errors+ array where necessary.
121
- def validate(record)
122
- raise NotImplementedError
123
- end
124
- end
125
-
126
- # EachValidator is a validator which iterates through the attributes given
127
- # in the options hash invoking the validate_each method passing in the
128
- # record, attribute and value.
129
- #
130
- # All ActiveModel validations are built on top of this Validator.
131
- class EachValidator < Validator
132
- attr_reader :attributes
133
-
134
- # Returns a new validator instance. All options will be available via the
135
- # +options+ reader, however the <tt>:attributes</tt> option will be removed
136
- # and instead be made available through the +attributes+ reader.
137
- def initialize(options)
138
- @attributes = Array.wrap(options.delete(:attributes))
139
- raise ":attributes cannot be blank" if @attributes.empty?
140
- super
141
- check_validity!
142
- end
143
-
144
- # Performs validation on the supplied record. By default this will call
145
- # +validates_each+ to determine validity therefore subclasses should
146
- # override +validates_each+ with validation logic.
147
- def validate(record)
148
- attributes.each do |attribute|
149
- value = record.read_attribute_for_validation(attribute)
150
- next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
151
- validate_each(record, attribute, value)
152
- end
153
- end
154
-
155
- # Override this method in subclasses with the validation logic, adding
156
- # errors to the records +errors+ array where necessary.
157
- def validate_each(record, attribute, value)
158
- raise NotImplementedError
159
- end
160
-
161
- # Hook method that gets called by the initializer allowing verification
162
- # that the arguments supplied are valid. You could for example raise an
163
- # ArgumentError when invalid options are supplied.
164
- def check_validity!
165
- end
166
- end
167
-
168
- # BlockValidator is a special EachValidator which receives a block on initialization
169
- # and call this block for each attribute being validated. +validates_each+ uses this
170
- # Validator.
171
- class BlockValidator < EachValidator
172
- def initialize(options, &block)
173
- @block = block
174
- super
175
- end
176
-
177
- private
178
-
179
- def validate_each(record, attribute, value)
180
- @block.call(record, attribute, value)
181
- end
182
- end
183
- end