sbf-dm-validations 1.3.0.beta

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.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +468 -0
  5. data/.travis.yml +51 -0
  6. data/Gemfile +60 -0
  7. data/LICENSE +21 -0
  8. data/README.rdoc +122 -0
  9. data/Rakefile +4 -0
  10. data/dm-validations.gemspec +20 -0
  11. data/lib/data_mapper/core.rb +1 -0
  12. data/lib/data_mapper/support/assertions.rb +1 -0
  13. data/lib/data_mapper/support/equalizer.rb +1 -0
  14. data/lib/data_mapper/support/ordered_set.rb +2 -0
  15. data/lib/data_mapper/validation/backward.rb +205 -0
  16. data/lib/data_mapper/validation/context.rb +57 -0
  17. data/lib/data_mapper/validation/contextual_rule_set.rb +210 -0
  18. data/lib/data_mapper/validation/exceptions.rb +7 -0
  19. data/lib/data_mapper/validation/inferred.rb +264 -0
  20. data/lib/data_mapper/validation/macros.rb +449 -0
  21. data/lib/data_mapper/validation/message_transformer.rb +111 -0
  22. data/lib/data_mapper/validation/model_extensions.rb +17 -0
  23. data/lib/data_mapper/validation/resource_extensions.rb +131 -0
  24. data/lib/data_mapper/validation/rule/absence.rb +31 -0
  25. data/lib/data_mapper/validation/rule/acceptance.rb +49 -0
  26. data/lib/data_mapper/validation/rule/block.rb +37 -0
  27. data/lib/data_mapper/validation/rule/confirmation.rb +47 -0
  28. data/lib/data_mapper/validation/rule/format/proc.rb +34 -0
  29. data/lib/data_mapper/validation/rule/format/regexp.rb +51 -0
  30. data/lib/data_mapper/validation/rule/format.rb +86 -0
  31. data/lib/data_mapper/validation/rule/formats/email.rb +54 -0
  32. data/lib/data_mapper/validation/rule/formats/url.rb +13 -0
  33. data/lib/data_mapper/validation/rule/length/equal.rb +48 -0
  34. data/lib/data_mapper/validation/rule/length/maximum.rb +50 -0
  35. data/lib/data_mapper/validation/rule/length/minimum.rb +50 -0
  36. data/lib/data_mapper/validation/rule/length/range.rb +50 -0
  37. data/lib/data_mapper/validation/rule/length.rb +96 -0
  38. data/lib/data_mapper/validation/rule/method.rb +42 -0
  39. data/lib/data_mapper/validation/rule/numericalness/equal.rb +34 -0
  40. data/lib/data_mapper/validation/rule/numericalness/greater_than.rb +34 -0
  41. data/lib/data_mapper/validation/rule/numericalness/greater_than_or_equal.rb +34 -0
  42. data/lib/data_mapper/validation/rule/numericalness/integer.rb +41 -0
  43. data/lib/data_mapper/validation/rule/numericalness/less_than.rb +34 -0
  44. data/lib/data_mapper/validation/rule/numericalness/less_than_or_equal.rb +34 -0
  45. data/lib/data_mapper/validation/rule/numericalness/not_equal.rb +34 -0
  46. data/lib/data_mapper/validation/rule/numericalness/numeric.rb +68 -0
  47. data/lib/data_mapper/validation/rule/numericalness.rb +91 -0
  48. data/lib/data_mapper/validation/rule/presence.rb +52 -0
  49. data/lib/data_mapper/validation/rule/primitive_type.rb +32 -0
  50. data/lib/data_mapper/validation/rule/uniqueness.rb +64 -0
  51. data/lib/data_mapper/validation/rule/within/range/bounded.rb +29 -0
  52. data/lib/data_mapper/validation/rule/within/range/unbounded_begin.rb +29 -0
  53. data/lib/data_mapper/validation/rule/within/range/unbounded_end.rb +29 -0
  54. data/lib/data_mapper/validation/rule/within/range.rb +55 -0
  55. data/lib/data_mapper/validation/rule/within/set.rb +45 -0
  56. data/lib/data_mapper/validation/rule/within.rb +32 -0
  57. data/lib/data_mapper/validation/rule.rb +232 -0
  58. data/lib/data_mapper/validation/rule_set.rb +157 -0
  59. data/lib/data_mapper/validation/support/object.rb +19 -0
  60. data/lib/data_mapper/validation/support/ordered_hash.rb +434 -0
  61. data/lib/data_mapper/validation/version.rb +5 -0
  62. data/lib/data_mapper/validation/violation.rb +136 -0
  63. data/lib/data_mapper/validation/violation_set.rb +115 -0
  64. data/lib/data_mapper/validation.rb +105 -0
  65. data/lib/dm-validations.rb +24 -0
  66. data/spec/data_mapper/validation/resource_extensions/save_spec.rb +56 -0
  67. data/spec/data_mapper/validation/resource_extensions/validate_spec.rb +103 -0
  68. data/spec/fixtures/barcode.rb +40 -0
  69. data/spec/fixtures/basketball_court.rb +58 -0
  70. data/spec/fixtures/basketball_player.rb +34 -0
  71. data/spec/fixtures/beta_tester_account.rb +33 -0
  72. data/spec/fixtures/bill_of_landing.rb +47 -0
  73. data/spec/fixtures/boat_dock.rb +26 -0
  74. data/spec/fixtures/city.rb +24 -0
  75. data/spec/fixtures/company.rb +93 -0
  76. data/spec/fixtures/corporate_world.rb +39 -0
  77. data/spec/fixtures/country.rb +24 -0
  78. data/spec/fixtures/ethernet_frame.rb +56 -0
  79. data/spec/fixtures/event.rb +44 -0
  80. data/spec/fixtures/g3_concert.rb +57 -0
  81. data/spec/fixtures/integer_dumped_as_string_property.rb +24 -0
  82. data/spec/fixtures/jabberwock.rb +27 -0
  83. data/spec/fixtures/kayak.rb +28 -0
  84. data/spec/fixtures/lernean_hydra.rb +39 -0
  85. data/spec/fixtures/llama_spaceship.rb +15 -0
  86. data/spec/fixtures/mathematical_function.rb +34 -0
  87. data/spec/fixtures/memory_object.rb +35 -0
  88. data/spec/fixtures/mittelschnauzer.rb +39 -0
  89. data/spec/fixtures/motor_launch.rb +21 -0
  90. data/spec/fixtures/multibyte.rb +16 -0
  91. data/spec/fixtures/page.rb +32 -0
  92. data/spec/fixtures/phone_number.rb +28 -0
  93. data/spec/fixtures/pirogue.rb +28 -0
  94. data/spec/fixtures/programming_language.rb +83 -0
  95. data/spec/fixtures/reservation.rb +38 -0
  96. data/spec/fixtures/scm_operation.rb +56 -0
  97. data/spec/fixtures/sms_message.rb +22 -0
  98. data/spec/fixtures/udp_packet.rb +49 -0
  99. data/spec/integration/absent_field_validator/absent_field_validator_spec.rb +90 -0
  100. data/spec/integration/absent_field_validator/spec_helper.rb +7 -0
  101. data/spec/integration/acceptance_validator/acceptance_validator_spec.rb +196 -0
  102. data/spec/integration/acceptance_validator/spec_helper.rb +7 -0
  103. data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +57 -0
  104. data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +49 -0
  105. data/spec/integration/automatic_validation/inferred_boolean_properties_validation_spec.rb +100 -0
  106. data/spec/integration/automatic_validation/inferred_float_property_validation_spec.rb +45 -0
  107. data/spec/integration/automatic_validation/inferred_format_validation_spec.rb +35 -0
  108. data/spec/integration/automatic_validation/inferred_integer_properties_validation_spec.rb +70 -0
  109. data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +142 -0
  110. data/spec/integration/automatic_validation/inferred_presence_validation_spec.rb +45 -0
  111. data/spec/integration/automatic_validation/inferred_primitive_validation_spec.rb +22 -0
  112. data/spec/integration/automatic_validation/inferred_uniqueness_validation_spec.rb +48 -0
  113. data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +35 -0
  114. data/spec/integration/automatic_validation/spec_helper.rb +57 -0
  115. data/spec/integration/block_validator/spec_helper.rb +5 -0
  116. data/spec/integration/conditional_validation/if_condition_spec.rb +63 -0
  117. data/spec/integration/conditional_validation/spec_helper.rb +5 -0
  118. data/spec/integration/confirmation_validator/confirmation_validator_spec.rb +76 -0
  119. data/spec/integration/confirmation_validator/spec_helper.rb +5 -0
  120. data/spec/integration/datamapper_models/association_validation_spec.rb +29 -0
  121. data/spec/integration/datamapper_models/inheritance_spec.rb +82 -0
  122. data/spec/integration/dirty_attributes/dirty_attributes_spec.rb +13 -0
  123. data/spec/integration/duplicated_validations/duplicated_validations_spec.rb +24 -0
  124. data/spec/integration/duplicated_validations/spec_helper.rb +5 -0
  125. data/spec/integration/format_validator/email_format_validator_spec.rb +139 -0
  126. data/spec/integration/format_validator/format_validator_spec.rb +64 -0
  127. data/spec/integration/format_validator/regexp_validator_spec.rb +33 -0
  128. data/spec/integration/format_validator/spec_helper.rb +5 -0
  129. data/spec/integration/format_validator/url_format_validator_spec.rb +91 -0
  130. data/spec/integration/length_validator/default_value_spec.rb +14 -0
  131. data/spec/integration/length_validator/equality_spec.rb +83 -0
  132. data/spec/integration/length_validator/error_message_spec.rb +22 -0
  133. data/spec/integration/length_validator/maximum_spec.rb +47 -0
  134. data/spec/integration/length_validator/minimum_spec.rb +54 -0
  135. data/spec/integration/length_validator/range_spec.rb +87 -0
  136. data/spec/integration/length_validator/spec_helper.rb +7 -0
  137. data/spec/integration/method_validator/method_validator_spec.rb +243 -0
  138. data/spec/integration/method_validator/spec_helper.rb +5 -0
  139. data/spec/integration/numeric_validator/equality_with_float_type_spec.rb +65 -0
  140. data/spec/integration/numeric_validator/equality_with_integer_type_spec.rb +41 -0
  141. data/spec/integration/numeric_validator/float_type_spec.rb +90 -0
  142. data/spec/integration/numeric_validator/gt_with_float_type_spec.rb +37 -0
  143. data/spec/integration/numeric_validator/gte_with_float_type_spec.rb +36 -0
  144. data/spec/integration/numeric_validator/integer_only_true_spec.rb +91 -0
  145. data/spec/integration/numeric_validator/integer_type_spec.rb +86 -0
  146. data/spec/integration/numeric_validator/lt_with_float_type_spec.rb +37 -0
  147. data/spec/integration/numeric_validator/lte_with_float_type_spec.rb +37 -0
  148. data/spec/integration/numeric_validator/spec_helper.rb +5 -0
  149. data/spec/integration/primitive_validator/primitive_validator_spec.rb +112 -0
  150. data/spec/integration/primitive_validator/spec_helper.rb +5 -0
  151. data/spec/integration/pure_ruby_objects/plain_old_ruby_object_validation_spec.rb +118 -0
  152. data/spec/integration/required_field_validator/association_spec.rb +69 -0
  153. data/spec/integration/required_field_validator/boolean_type_value_spec.rb +164 -0
  154. data/spec/integration/required_field_validator/date_type_value_spec.rb +127 -0
  155. data/spec/integration/required_field_validator/datetime_type_value_spec.rb +127 -0
  156. data/spec/integration/required_field_validator/float_type_value_spec.rb +131 -0
  157. data/spec/integration/required_field_validator/integer_type_value_spec.rb +99 -0
  158. data/spec/integration/required_field_validator/plain_old_ruby_object_spec.rb +35 -0
  159. data/spec/integration/required_field_validator/shared_examples.rb +27 -0
  160. data/spec/integration/required_field_validator/spec_helper.rb +7 -0
  161. data/spec/integration/required_field_validator/string_type_value_spec.rb +167 -0
  162. data/spec/integration/required_field_validator/text_type_value_spec.rb +49 -0
  163. data/spec/integration/shared/default_validation_context.rb +13 -0
  164. data/spec/integration/shared/valid_and_invalid_model.rb +35 -0
  165. data/spec/integration/uniqueness_validator/spec_helper.rb +5 -0
  166. data/spec/integration/uniqueness_validator/uniqueness_validator_spec.rb +116 -0
  167. data/spec/integration/within_validator/spec_helper.rb +5 -0
  168. data/spec/integration/within_validator/within_validator_spec.rb +168 -0
  169. data/spec/public/resource_spec.rb +113 -0
  170. data/spec/spec_helper.rb +28 -0
  171. data/spec/unit/contextual_validators/emptiness_spec.rb +50 -0
  172. data/spec/unit/contextual_validators/execution_spec.rb +48 -0
  173. data/spec/unit/contextual_validators/spec_helper.rb +37 -0
  174. data/spec/unit/generic_validator/equality_operator_spec.rb +26 -0
  175. data/spec/unit/generic_validator/optional_spec.rb +54 -0
  176. data/spec/unit/validators/within_validator_spec.rb +23 -0
  177. data/spec/unit/violation_set/adding_spec.rb +54 -0
  178. data/spec/unit/violation_set/emptiness_spec.rb +38 -0
  179. data/spec/unit/violation_set/enumerable_spec.rb +32 -0
  180. data/spec/unit/violation_set/reading_spec.rb +35 -0
  181. data/spec/unit/violation_set/respond_to_spec.rb +15 -0
  182. data/tasks/spec.rake +21 -0
  183. data/tasks/yard.rake +9 -0
  184. data/tasks/yardstick.rake +19 -0
  185. metadata +245 -0
@@ -0,0 +1,111 @@
1
+ module DataMapper
2
+ module Validation
3
+ # Transforms Violations to error message strings.
4
+ #
5
+ # @abstract
6
+ # Subclass and override {#transform} to implement a custom message
7
+ # transformer. Use {Violation.message_transformer=} to set a new
8
+ # message transformer or pass the transformer to {Violation#message}.
9
+ class MessageTransformer
10
+
11
+ # Transforms the specified Violation to an error message string.
12
+ #
13
+ # @param [Violation] violation
14
+ # The Violation to transform.
15
+ #
16
+ # @return [String]
17
+ # The transformed message.
18
+ #
19
+ # @raise [ArgumentError]
20
+ # +violation+ is +nil+.
21
+ def transform(violation)
22
+ raise NotImplementedError, "#{self.class}#transform must be implemented"
23
+ end
24
+
25
+
26
+ class Default < self
27
+ @error_messages = {
28
+ :absent => '%s must be absent',
29
+ :inclusion => '%s must be one of %s',
30
+ :invalid => '%s has an invalid format',
31
+ :confirmation => '%s does not match the confirmation',
32
+ :accepted => '%s is not accepted',
33
+ :nil => '%s must not be nil',
34
+ :blank => '%s must not be blank',
35
+ :length_between => '%s must be between %s and %s characters long',
36
+ :too_long => '%s must be at most %s characters long',
37
+ :too_short => '%s must be at least %s characters long',
38
+ :wrong_length => '%s must be %s characters long',
39
+ :taken => '%s is already taken',
40
+ :not_a_number => '%s must be a number',
41
+ :not_an_integer => '%s must be an integer',
42
+ :greater_than => '%s must be greater than %s',
43
+ :greater_than_or_equal_to => '%s must be greater than or equal to %s',
44
+ :equal_to => '%s must be equal to %s',
45
+ :not_equal_to => '%s must not be equal to %s',
46
+ :less_than => '%s must be less than %s',
47
+ :less_than_or_equal_to => '%s must be less than or equal to %s',
48
+ :value_between => '%s must be between %s and %s',
49
+ :primitive => '%s must be of type %s'
50
+ }
51
+
52
+ class << self
53
+ # Gets the hash of error messages used to transform violations.
54
+ #
55
+ # @return [Hash{Symbol=>String}]
56
+ attr_reader :error_messages
57
+ end
58
+
59
+ # Merges the specified +error_messages+ hash into the internal hash of
60
+ # error messages.
61
+ #
62
+ # @param [Hash{Symbol=>String}] error_messages
63
+ # The error messages to be merged.
64
+ def self.error_messages=(error_messages)
65
+ unless error_messages.is_a?(Hash)
66
+ raise ArgumentError, "+error_messages+ must be a hash"
67
+ end
68
+
69
+ self.error_messages.merge!(error_messages)
70
+ end
71
+
72
+ def self.error_message(violation_type, attribute_name, violation_values)
73
+ if message = self.error_messages[violation_type]
74
+ attribute_name = DataMapper::Inflector.humanize(attribute_name)
75
+ message % [attribute_name, *violation_values].flatten
76
+ else
77
+ violation_type.to_s
78
+ end
79
+ end
80
+
81
+ def transform(violation)
82
+ raise ArgumentError, "+violation+ must be specified" if violation.nil?
83
+
84
+ attribute_name = violation.attribute_name
85
+
86
+ self.class.error_message(violation.type, attribute_name, violation.values)
87
+ end
88
+ end # class Default
89
+
90
+ class DefaultI18n < self
91
+ def transform(violation)
92
+ raise ArgumentError, "+violation+ must be specified" if violation.nil?
93
+
94
+ resource = violation.resource
95
+ model_name = resource.model.model_name
96
+ attribute_name = violation.attribute_name
97
+
98
+ options = {
99
+ :model => ::I18n.translate("models.#{model_name}"),
100
+ :attribute => ::I18n.translate("attributes.#{model_name}.#{attribute_name}"),
101
+ :value => resource.validation_property_value(attribute_name)
102
+ }.merge(violation.info)
103
+
104
+ ::I18n.translate("errors.#{violation.type}", options)
105
+ end
106
+ end # class I18n
107
+
108
+ end # class MessageTransformer
109
+
110
+ end # module Validation
111
+ end # module DataMapper
@@ -0,0 +1,17 @@
1
+ module DataMapper
2
+ module Validation
3
+ module ModelExtensions
4
+
5
+ # @api public
6
+ def create(attributes = {}, *args)
7
+ resource = new(attributes)
8
+ resource.save(*args)
9
+ resource
10
+ end
11
+
12
+ end # module ModelExtensions
13
+ end # module Validation
14
+
15
+ Model.append_extensions Validation::ModelExtensions
16
+
17
+ end # module DataMapper
@@ -0,0 +1,131 @@
1
+ require 'data_mapper/core'
2
+ require 'data_mapper/validation'
3
+ require 'data_mapper/validation/context'
4
+
5
+ module DataMapper
6
+ module Validation
7
+
8
+ module ResourceExtensions
9
+
10
+ # TODO: contemplate reworking dm-core to allow hooks to be fired even if
11
+ # the hooked event does not transpire (eg., `before :save` when clean)
12
+ # def self.included(model)
13
+ # model.before :save, :validate_or_halt
14
+ # end
15
+
16
+ # Ensures the object is valid for the context provided, and otherwise
17
+ # throws :halt and returns false.
18
+ #
19
+ # @param [Symbol] context
20
+ # context for which to validate the Resource, defaulting to
21
+ # #default_validation_context
22
+ #
23
+ # @return [Boolean]
24
+ # whether the Resource was persisted successfully
25
+ #
26
+ # TODO: fix this to not change the method signature of #save
27
+ # TODO: add support for skipping validations by passing nil
28
+ #
29
+ # @api public
30
+ def save(context_name = default_validation_context)
31
+ validation_rules.assert_valid_context(context_name)
32
+
33
+ Context.in_context(context_name) { super() }
34
+ end
35
+
36
+ # @api private
37
+ def save_self(*)
38
+ return super unless dirty_self? || new?
39
+ if Context.any? && !valid?(validation_rules.current_context)
40
+ false
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ # Ensures the object is valid for the context provided, and otherwise
47
+ # throws :halt and returns false.
48
+ #
49
+ # @param [Hash] attributes
50
+ # attribute names and values which will be set on this Resource
51
+ # @param [Symbol] context
52
+ # context for which to validate the Resource, defaulting to
53
+ # #default_validation_context
54
+ #
55
+ # @return [Boolean]
56
+ # whether the Resource attributes were set and persisted successfully
57
+ #
58
+ # TODO: fix this to not change the method signature of #update
59
+ #
60
+ # @api public
61
+ def update(attributes = {}, context_name = default_validation_context)
62
+ validation_rules.assert_valid_context(context_name)
63
+
64
+ Context.in_context(context_name) { super(attributes) }
65
+ end
66
+
67
+ # @api public
68
+ def validate(context_name = default_validation_context)
69
+ super
70
+ #validate_parents # if model.validation_rules.validate_parents?
71
+ #validate_children # if model.validation_rules.validate_children?
72
+
73
+ self
74
+ end
75
+
76
+ private
77
+
78
+ # @api private
79
+ def validate_parents
80
+ parent_relationships.each do |relationship|
81
+ next unless relationship.loaded?(self)
82
+ validate_parent_relationship(relationship)
83
+ end
84
+ end
85
+
86
+ # @api private
87
+ def validate_children
88
+ child_relationships.each do |relationship|
89
+ next unless relationship.loaded?(self)
90
+ validate_child_relationship(relationship)
91
+ end
92
+ end
93
+
94
+ # @api private
95
+ def validate_parent_relationship(relationship)
96
+ relationship_name = relationship.name
97
+ parent_model = relationship.target_model
98
+ context_name = parent_model.validation_rules.current_context
99
+ parent_resource = relationship.get(self)
100
+
101
+ parent_violations = parent_resource.validation_violations(context_name)
102
+ parent_violations.each { |v| errors[relationship_name] << v }
103
+ end
104
+
105
+ # @api private
106
+ def validate_child_relationship(relationship)
107
+ relationship_name = relationship.name
108
+ child_model = relationship.target_model
109
+ context_name = child_model.validation_rules.current_context
110
+ child_collection = relationship.get_collection(self)
111
+
112
+ child_collection.each do |child_resource|
113
+ child_violations = child_resource.validation_violations(context_name)
114
+ child_violations.each { |v| errors[relationship_name] << v }
115
+ end
116
+ end
117
+
118
+ # @see note at self.included, above
119
+ #
120
+ # @api private
121
+ # def validate_or_halt
122
+ # throw :halt if Context.any? && !valid?(validation_rules.current_context)
123
+ # end
124
+
125
+ end # module Resource
126
+ end # module Validation
127
+
128
+ Model.append_inclusions Validation
129
+ Model.append_inclusions Validation::ResourceExtensions
130
+
131
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule'
4
+
5
+ module DataMapper
6
+ module Validation
7
+ class Rule
8
+
9
+ class Absence < Rule
10
+
11
+ def initialize(attribute_name, options = {})
12
+ super
13
+
14
+ @allow_nil = false
15
+ @allow_blank = false
16
+ end
17
+
18
+ def valid?(resource)
19
+ value = resource.validation_property_value(attribute_name)
20
+ DataMapper::Ext.blank?(value)
21
+ end
22
+
23
+ def violation_type(resource)
24
+ :absent
25
+ end
26
+
27
+ end # class Absence
28
+
29
+ end # class Rule
30
+ end # module Validation
31
+ end # module DataMapper
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule'
4
+
5
+ module DataMapper
6
+ module Validation
7
+ class Rule
8
+
9
+ # TODO: update this to inherit from Rule::Within::Set
10
+ class Acceptance < Rule
11
+
12
+ EQUALIZE_ON = superclass::EQUALIZE_ON.dup << :accept
13
+
14
+ equalize *EQUALIZE_ON
15
+
16
+ DEFAULT_ACCEPTED_VALUES = [ '1', 1, 'true', true, 't' ]
17
+
18
+ attr_reader :accept
19
+
20
+ def initialize(attribute_name, options = {})
21
+ super
22
+
23
+ @accept = Array(options.fetch(:accept, DEFAULT_ACCEPTED_VALUES))
24
+
25
+ allow_nil! unless defined?(@allow_nil)
26
+ end
27
+
28
+ def valid?(resource)
29
+ value = resource.validation_property_value(attribute_name)
30
+ return true if exempt_value?(value)
31
+ accept.include?(value)
32
+ end
33
+
34
+ def violation_type(resource)
35
+ :accepted
36
+ end
37
+
38
+ private
39
+
40
+ # TODO: isn't this superfluous considering Rule#optional?
41
+ def exempt_value?(value)
42
+ allow_nil? && value.nil?
43
+ end
44
+
45
+ end # class Acceptance
46
+
47
+ end # class Rule
48
+ end # module Validation
49
+ end # module DataMapper
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule'
4
+
5
+ module DataMapper
6
+ module Validation
7
+ class Rule
8
+
9
+ class Block < Rule
10
+
11
+ attr_reader :block
12
+
13
+ def initialize(attribute_name, options, &block)
14
+ unless block_given?
15
+ raise ArgumentError, 'cannot initialize a Block validator without a block'
16
+ end
17
+
18
+ super
19
+
20
+ @block = block
21
+ end
22
+
23
+ def validate(resource)
24
+ result, error_message = resource.instance_eval(&self.block)
25
+
26
+ if result
27
+ nil
28
+ else
29
+ Violation.new(resource, error_message, self)
30
+ end
31
+ end
32
+
33
+ end # class Block
34
+
35
+ end # class Rule
36
+ end # module Validation
37
+ end # module DataMapper
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule'
4
+
5
+ module DataMapper
6
+ module Validation
7
+ class Rule
8
+
9
+ class Confirmation < Rule
10
+
11
+ EQUALIZE_ON = superclass::EQUALIZE_ON.dup << :confirmation_attribute
12
+
13
+ attr_reader :confirmation_attribute
14
+
15
+
16
+ def initialize(attribute_name, options = {})
17
+ super
18
+
19
+ @confirm_attribute_name = options.fetch(:confirm) do
20
+ :"#{attribute_name}_confirmation"
21
+ end
22
+
23
+ allow_nil! unless defined?(@allow_nil)
24
+ allow_blank! unless defined?(@allow_blank)
25
+ end
26
+
27
+ def valid?(resource)
28
+ value = resource.validation_property_value(attribute_name)
29
+ return true if optional?(value)
30
+
31
+ if resource.model.properties.named?(attribute_name)
32
+ return true unless resource.attribute_dirty?(attribute_name)
33
+ end
34
+
35
+ confirm_value = resource.instance_variable_get("@#{@confirm_attribute_name}")
36
+ value == confirm_value
37
+ end
38
+
39
+ def violation_type(resource)
40
+ :confirmation
41
+ end
42
+
43
+ end # class Confirmation
44
+
45
+ end # class Rule
46
+ end # module Validation
47
+ end # module DataMapper
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule/format'
4
+
5
+ module DataMapper
6
+ module Validation
7
+ class Rule
8
+ module Format
9
+
10
+ class Proc < Rule
11
+
12
+ include Format
13
+
14
+ EQUALIZE_ON = superclass::EQUALIZE_ON.dup << :format
15
+
16
+ equalize *EQUALIZE_ON
17
+
18
+
19
+ def valid?(resource)
20
+ value = resource.validation_property_value(attribute_name)
21
+ return true if optional?(value)
22
+
23
+ self.format.call(value)
24
+ # rescue ::Encoding::CompatibilityError
25
+ # # This is to work around a bug in jruby - see formats/email.rb
26
+ # false
27
+ end
28
+
29
+ end # class Proc
30
+
31
+ end # module Format
32
+ end # class Rule
33
+ end # module Validation
34
+ end # module DataMapper
@@ -0,0 +1,51 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule/format'
4
+
5
+ module DataMapper
6
+ module Validation
7
+ class Rule
8
+ module Format
9
+
10
+ class Regexp < Rule
11
+
12
+ include Format
13
+
14
+ EQUALIZE_ON = superclass::EQUALIZE_ON.dup << :format << :format_name
15
+
16
+ equalize *EQUALIZE_ON
17
+
18
+
19
+ attr_reader :format_name
20
+
21
+ def initialize(attribute_name, options = {})
22
+ super
23
+
24
+ @format_name = options.fetch(:format_name, nil)
25
+ end
26
+
27
+ def valid?(resource)
28
+ value = resource.validation_property_value(attribute_name)
29
+ return true if optional?(value)
30
+
31
+ value ? value.to_s =~ self.format : false
32
+ rescue ::Encoding::CompatibilityError
33
+ # This is to work around a bug in jruby - see formats/email.rb
34
+ false
35
+ end
36
+
37
+ # TODO: integrate format into error message key?
38
+ # def error_message_args
39
+ # if format_name.is_a?(Symbol)
40
+ # [ :"invalid_#{format_name}", attribute_name ]
41
+ # else
42
+ # [ :invalid, attribute_name ]
43
+ # end
44
+ # end
45
+
46
+ end # class Regexp
47
+
48
+ end # module Format
49
+ end # class Rule
50
+ end # module Validation
51
+ end # module DataMapper
@@ -0,0 +1,86 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule'
4
+
5
+ require 'data_mapper/validation/rule/formats/email'
6
+ require 'data_mapper/validation/rule/formats/url'
7
+
8
+ module DataMapper
9
+ module Validation
10
+ class UnknownValidationFormat < ::ArgumentError; end
11
+
12
+ class Rule
13
+
14
+ module Format
15
+
16
+ FORMATS = {
17
+ :email_address => Formats::EmailAddress,
18
+ :url => Formats::Url
19
+ }
20
+ # TODO: evaluate re-implementing custom error messages per format type
21
+ # previously these strings were wrapped in lambdas, which were, at one
22
+ # point, invoked with #try_call with the humanized attribute name and value
23
+ FORMAT_MESSAGES = {
24
+ :email_address => '%s is not a valid email address',
25
+ :url => '%s is not a valid URL',
26
+ }
27
+
28
+
29
+ def self.rules_for(attribute_name, options)
30
+ Array(new(attribute_name, options))
31
+ end
32
+
33
+ # @raise [UnknownValidationFormat]
34
+ # if the :as (or :with) option is a Symbol that is not a key in FORMATS,
35
+ # or if the provided format is not a Regexp, Symbol or Proc
36
+ def self.new(attribute_name, options)
37
+ format = options.delete(:as) || options.delete(:with)
38
+
39
+ case format
40
+ when Symbol
41
+ regexp = FORMATS.fetch(format) do
42
+ raise UnknownValidationFormat, "No such predefined format '#{format}'"
43
+ end
44
+ self::Regexp.new(attribute_name, options.merge(:format => regexp, :format_name => format))
45
+ when ::Regexp
46
+ self::Regexp.new(attribute_name, options.merge(:format => format))
47
+ when ::Proc
48
+ self::Proc.new(attribute_name, options.merge(:format => format))
49
+ else
50
+ raise UnknownValidationFormat, "Expected a Regexp, Symbol, or Proc format. Got: #{format.inspect}"
51
+ end
52
+ end
53
+
54
+
55
+ attr_reader :format
56
+
57
+ def initialize(attribute_name, options)
58
+ super
59
+
60
+ @format = options.fetch(:format)
61
+
62
+ allow_nil! unless defined?(@allow_nil)
63
+ allow_blank! unless defined?(@allow_blank)
64
+ end
65
+
66
+ def violation_type(resource)
67
+ :invalid
68
+ end
69
+
70
+ # TODO: integrate format into error message key?
71
+ # def error_message_args
72
+ # if format.is_a?(Symbol)
73
+ # [ :"invalid_#{format}", attribute_name ]
74
+ # else
75
+ # [ :invalid, attribute_name ]
76
+ # end
77
+ # end
78
+
79
+ end # class Format
80
+
81
+ end # class Rule
82
+ end # module Validation
83
+ end # module DataMapper
84
+
85
+ require 'data_mapper/validation/rule/format/proc'
86
+ require 'data_mapper/validation/rule/format/regexp'
@@ -0,0 +1,54 @@
1
+ # encoding: UTF-8
2
+
3
+ module DataMapper
4
+ module Validation
5
+ class Rule
6
+ module Formats
7
+
8
+ # Almost RFC2822 (No attribution reference available).
9
+ #
10
+ # This differs in that it does not allow local domains (test@localhost).
11
+ # 99% of the time you do not want to allow these email addresses
12
+ # in a public web application.
13
+ EmailAddress = begin
14
+ if (RUBY_VERSION == '1.9.2' && RUBY_ENGINE == 'jruby' && JRUBY_VERSION <= '1.6.3') || RUBY_VERSION == '1.9.3'
15
+ # There is an obscure bug in jruby 1.6 that prevents matching
16
+ # on unicode properties here. Remove this logic branch once
17
+ # a stable jruby release fixes this.
18
+ #
19
+ # http://jira.codehaus.org/browse/JRUBY-5622
20
+ #
21
+ # There is a similar bug in preview releases of 1.9.3
22
+ #
23
+ # http://redmine.ruby-lang.org/issues/5126
24
+ letter = 'a-zA-Z'
25
+ else
26
+ letter = 'a-zA-Z\p{L}' # Changed from RFC2822 to include unicode chars
27
+ end
28
+ digit = '0-9'
29
+ atext = "[#{letter}#{digit}\!\#\$\%\&\'\*+\/\=\?\^\_\`\{\|\}\~\-]"
30
+ dot_atom_text = "#{atext}+([.]#{atext}*)+"
31
+ dot_atom = dot_atom_text
32
+ no_ws_ctl = '\x01-\x08\x11\x12\x14-\x1f\x7f'
33
+ qtext = "[^#{no_ws_ctl}\\x0d\\x22\\x5c]" # Non-whitespace, non-control character except for \ and "
34
+ text = '[\x01-\x09\x11\x12\x14-\x7f]'
35
+ quoted_pair = "(\\x5c#{text})"
36
+ qcontent = "(?:#{qtext}|#{quoted_pair})"
37
+ quoted_string = "[\"]#{qcontent}+[\"]"
38
+ atom = "#{atext}+"
39
+ word = "(?:#{atom}|#{quoted_string})"
40
+ obs_local_part = "#{word}([.]#{word})*"
41
+ local_part = "(?:#{dot_atom}|#{quoted_string}|#{obs_local_part})"
42
+ dtext = "[#{no_ws_ctl}\\x21-\\x5a\\x5e-\\x7e]"
43
+ dcontent = "(?:#{dtext}|#{quoted_pair})"
44
+ domain_literal = "\\[#{dcontent}+\\]"
45
+ obs_domain = "#{atom}([.]#{atom})+"
46
+ domain = "(?:#{dot_atom}|#{domain_literal}|#{obs_domain})"
47
+ addr_spec = "#{local_part}\@#{domain}"
48
+ pattern = /\A#{addr_spec}\z/u
49
+ end
50
+
51
+ end # module Formats
52
+ end # class Rule
53
+ end # module Validation
54
+ end # module DataMapper
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ module DataMapper
4
+ module Validation
5
+ class Rule
6
+ module Formats
7
+
8
+ Url = %r{\Ahttps?://[a-z\d](?:[-.]?[a-z\d])*\.[a-z]{2,6}(?::\d{1,5})?/?.*\z}ix.freeze
9
+
10
+ end # module Formats
11
+ end # class Rule
12
+ end # module Validation
13
+ end # module DataMapper