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,157 @@
1
+ require 'forwardable'
2
+ require 'data_mapper/support/ordered_set'
3
+ # TODO: can I use Equalizer without introducing a dependency on dm-core?
4
+ # require 'data_mapper/support/equalizer'
5
+
6
+ module DataMapper
7
+ module Validation
8
+
9
+ class RuleSet
10
+ # Holds a collection of Rule instances to be run against
11
+ # Resources to validate the Resources in a specific context
12
+
13
+ extend Equalizer
14
+ extend Forwardable
15
+ include Enumerable
16
+
17
+ # @api public
18
+ attr_reader :rules
19
+
20
+ # @api private
21
+ attr_reader :attribute_index
22
+
23
+ # @api public
24
+ attr_accessor :optimize
25
+
26
+ equalize :rules
27
+
28
+ # @api public
29
+ def_delegators :attribute_index, :[], :fetch
30
+
31
+ # @api public
32
+ def_delegators :rules, :each, :empty?
33
+
34
+ def initialize(optimize = false)
35
+ @optimize = optimize
36
+
37
+ @rules = OrderedSet.new
38
+ @attribute_index = Hash.new { |h,k| h[k] = [] }
39
+ end
40
+
41
+ def <<(rule)
42
+ unless rules.include?(rule)
43
+ rules << rule
44
+ attribute_index[rule.attribute_name] << rule
45
+ end
46
+ self
47
+ end
48
+
49
+ # Execute all rules in this context against the resource.
50
+ #
51
+ # @param [Object] resource
52
+ # the resource to be validated
53
+ #
54
+ # @return [Array(Violation)]
55
+ # an Array of Violations
56
+ def validate(resource)
57
+ rules = rules_for_resource(resource)
58
+ rules.map { |rule| rule.validate(resource) }.compact
59
+ end
60
+
61
+ # Assimilate all the rules from another RuleSet into the receiver
62
+ #
63
+ # @param [RuleSet, Array] rules
64
+ # The other RuleSet whose rules are to be assimilated
65
+ #
66
+ # @return [RuleSet]
67
+ # +self+, the receiver
68
+ def concat(rules)
69
+ rules.each { |rule| self << rule.dup }
70
+ self
71
+ end
72
+
73
+ def inspect
74
+ "#<#{ self.class } {#{ rules.map { |e| e.inspect }.join( ', ' ) }}>"
75
+ end
76
+
77
+ private
78
+
79
+ def rules_for_resource(resource)
80
+ executable_rules = rules.entries.select { |r| r.execute?(resource) }
81
+
82
+ # By default we start the list with the full set of executable
83
+ # rules.
84
+ #
85
+ # In the case of a new Resource or regular ruby class instance,
86
+ # everything needs to be validated completely, and no eager-loading
87
+ # logic should apply.
88
+ #
89
+ # @see #optimized_rules_for_persisted_resource
90
+ if resource.kind_of?(DataMapper::Resource)
91
+ if optimize && !resource.new?
92
+ optimized_rules =
93
+ optimized_rules_for_persisted_resource(resource, executable_rules)
94
+ load_validated_properties(resource, optimized_rules)
95
+ optimized_rules
96
+ else
97
+ load_validated_properties(resource, executable_rules)
98
+ executable_rules
99
+ end
100
+ else
101
+ executable_rules
102
+ end
103
+ end
104
+
105
+ # Only validate dirty properties on persisted Resources.
106
+ # Eager load lazy properties that are not yet loaded.
107
+ #
108
+ # In the case of a DM::Resource that isn't new, we optimize:
109
+ #
110
+ # 1. Eager-load all lazy, not-yet-loaded properties that need
111
+ # validation, all at once.
112
+ #
113
+ # 2. Limit run rules to
114
+ # - those applied to dirty attributes only,
115
+ # - those that should always run (presence/absence)
116
+ # - those that don't reference any real properties (attribute-less
117
+ # block rules, validations in virtual attributes)
118
+ def optimized_rules_for_persisted_resource(resource, executable_rules)
119
+ attrs = resource.attributes(:name)
120
+ # TODO: update Resource#dirty_attributes to accept :name arg
121
+ dirty_attrs = Hash[resource.dirty_attributes.map { |p, value| [p.name, value] }]
122
+ rules = executable_rules.select { |r|
123
+ !attrs.include?(r.attribute_name) || dirty_attrs.include?(r.attribute_name)
124
+ }
125
+
126
+ # Finally include any rules that should always run or don't
127
+ # reference any real properties (field-less block vaildators).
128
+ rules |= all_rules.select do |v|
129
+ # TODO: extract this to a #always_validate? interface on Rule
130
+ # instead of a #kind_of? test here in RuleSet
131
+ v.kind_of?(Rule::Block) ||
132
+ v.kind_of?(Rule::Method) ||
133
+ v.kind_of?(Rule::Presence) ||
134
+ v.kind_of?(Rule::Absence)
135
+ end
136
+
137
+ rules
138
+ end
139
+
140
+ # Load all lazy, not-yet-loaded properties that need validation,
141
+ # all at once.
142
+ def load_validated_properties(resource, rules)
143
+ properties = resource.model.properties
144
+
145
+ properties_to_load = rules.map { |rule|
146
+ properties[rule.attribute_name]
147
+ }.compact.select { |property|
148
+ # TODO: isn't Property#loaded? sufficient?
149
+ property.lazy? && !property.loaded?(resource)
150
+ }
151
+
152
+ resource.__send__(:eager_load, properties_to_load)
153
+ end
154
+ end # class RuleSet
155
+
156
+ end # module Validation
157
+ end # module DataMapper
@@ -0,0 +1,19 @@
1
+ # TODO: remove Object#try_call
2
+ class Object
3
+ # If receiver is callable, calls it and returns result.
4
+ # If not, just returns receiver itself
5
+ #
6
+ # @return [Object]
7
+ # @api private
8
+ def try_call(*args)
9
+ if self.respond_to?(:call)
10
+ self.call(*args)
11
+ else
12
+ self
13
+ end
14
+ end
15
+
16
+ def validatable?
17
+ false
18
+ end
19
+ end
@@ -0,0 +1,434 @@
1
+ module DataMapper; module Validation
2
+ # TITLE:
3
+ #
4
+ # OrderedHash (originally Dictionary)
5
+ #
6
+ # AUTHORS:
7
+ #
8
+ # - Jan Molic
9
+ # - Thomas Sawyer
10
+ #
11
+ # CREDIT:
12
+ #
13
+ # - Andrew Johnson (merge, to_a, inspect, shift and Hash[])
14
+ # - Jeff Sharpe (reverse and reverse!)
15
+ # - Thomas Leitner (has_key? and key?)
16
+ #
17
+ # LICENSE:
18
+ #
19
+ # Copyright (c) 2005 Jan Molic, Thomas Sawyer
20
+ #
21
+ # Ruby License
22
+ #
23
+ # This module is free software. You may use, modify, and/or redistribute this
24
+ # software under the same terms as Ruby.
25
+ #
26
+ # This program is distributed in the hope that it will be useful, but WITHOUT
27
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
28
+ # FOR A PARTICULAR PURPOSE.
29
+ #
30
+ # Originally ported from OrderHash 2.0, Copyright (c) 2005 jan molic
31
+ #
32
+ # LOG:
33
+ #
34
+ # - 2007.10.31 trans
35
+ # Fixed initialize so the constructor blocks correctly effected dictionary
36
+ # rather then just the internal hash.
37
+
38
+ # = Dictionary
39
+ #
40
+ # The Dictionary class is a Hash that preserves order.
41
+ # So it has some array-like extensions also. By defualt
42
+ # a Dictionary object preserves insertion order, but any
43
+ # order can be specified including alphabetical key order.
44
+ #
45
+ # == Usage
46
+ #
47
+ # Just require this file and use Dictionary instead of Hash.
48
+ #
49
+ # # You can do simply
50
+ # hsh = Dictionary.new
51
+ # hsh['z'] = 1
52
+ # hsh['a'] = 2
53
+ # hsh['c'] = 3
54
+ # p hsh.keys #=> ['z','a','c']
55
+ #
56
+ # # or using Dictionary[] method
57
+ # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
58
+ # p hsh.keys #=> ['z','a','c']
59
+ #
60
+ # # but this doesn't preserve order
61
+ # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
62
+ # p hsh.keys #=> ['a','c','z']
63
+ #
64
+ # # Dictionary has useful extensions: push, pop and unshift
65
+ # p hsh.push('to_end', 15) #=> true, key added
66
+ # p hsh.push('to_end', 30) #=> false, already - nothing happen
67
+ # p hsh.unshift('to_begin', 50) #=> true, key added
68
+ # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
69
+ # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
70
+ # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
71
+ # p hsh.keys #=> ["to_begin", "a", "c", "z"]
72
+ # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
73
+ #
74
+ # == Usage Notes
75
+ #
76
+ # * You can use #order_by to set internal sort order.
77
+ # * #<< takes a two element [k,v] array and inserts.
78
+ # * Use ::auto which creates Dictionay sub-entries as needed.
79
+ # * And ::alpha which creates a new Dictionary sorted by key.
80
+ class OrderedHash
81
+
82
+ include Enumerable
83
+
84
+ class << self
85
+ #--
86
+ # TODO is this needed? Doesn't the super class do this?
87
+ #++
88
+ def [](*args)
89
+ hsh = new
90
+ if Hash === args[0]
91
+ hsh.replace(args[0])
92
+ elsif (args.size % 2) != 0
93
+ raise ArgumentError, "odd number of elements for Hash"
94
+ else
95
+ while !args.empty?
96
+ hsh[args.shift] = args.shift
97
+ end
98
+ end
99
+ hsh
100
+ end
101
+
102
+ # Like #new but the block sets the order.
103
+ #
104
+ def new_by(*args, &blk)
105
+ new(*args).order_by(&blk)
106
+ end
107
+
108
+ # Alternate to #new which creates a dictionary sorted by key.
109
+ #
110
+ # d = Dictionary.alpha
111
+ # d["z"] = 1
112
+ # d["y"] = 2
113
+ # d["x"] = 3
114
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
115
+ #
116
+ # This is equivalent to:
117
+ #
118
+ # Dictionary.new.order_by { |key,value| key }
119
+ def alpha(*args, &block)
120
+ new(*args, &block).order_by_key
121
+ end
122
+
123
+ # Alternate to #new which auto-creates sub-dictionaries as needed.
124
+ #
125
+ # d = Dictionary.auto
126
+ # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
127
+ #
128
+ def auto(*args)
129
+ #AutoDictionary.new(*args)
130
+ leet = lambda { |hsh, key| hsh[key] = new(&leet) }
131
+ new(*args, &leet)
132
+ end
133
+ end
134
+
135
+ # New Dictiionary.
136
+ def initialize(*args, &blk)
137
+ @order = []
138
+ @order_by = nil
139
+ if blk
140
+ dict = self # This ensure autmatic key entry effect the
141
+ oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash.
142
+ @hash = Hash.new(*args, &oblk)
143
+ else
144
+ @hash = Hash.new(*args)
145
+ end
146
+ end
147
+
148
+ def order
149
+ reorder if @order_by
150
+ @order
151
+ end
152
+
153
+ # Keep dictionary sorted by a specific sort order.
154
+ def order_by( &block )
155
+ @order_by = block
156
+ order
157
+ self
158
+ end
159
+
160
+ # Keep dictionary sorted by key.
161
+ #
162
+ # d = Dictionary.new.order_by_key
163
+ # d["z"] = 1
164
+ # d["y"] = 2
165
+ # d["x"] = 3
166
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
167
+ #
168
+ # This is equivalent to:
169
+ #
170
+ # Dictionary.new.order_by { |key,value| key }
171
+ #
172
+ # The initializer Dictionary#alpha also provides this.
173
+ def order_by_key
174
+ @order_by = lambda { |k,v| k }
175
+ order
176
+ self
177
+ end
178
+
179
+ # Keep dictionary sorted by value.
180
+ #
181
+ # d = Dictionary.new.order_by_value
182
+ # d["z"] = 1
183
+ # d["y"] = 2
184
+ # d["x"] = 3
185
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
186
+ #
187
+ # This is equivalent to:
188
+ #
189
+ # Dictionary.new.order_by { |key,value| value }
190
+ def order_by_value
191
+ @order_by = lambda { |k,v| v }
192
+ order
193
+ self
194
+ end
195
+
196
+ #
197
+ def reorder
198
+ if @order_by
199
+ assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
200
+ @order = assoc.collect{ |k,v| k }
201
+ end
202
+ @order
203
+ end
204
+
205
+ def ==(hsh2)
206
+ if hsh2.is_a?( Dictionary )
207
+ @order == hsh2.order &&
208
+ @hash == hsh2.instance_variable_get("@hash")
209
+ else
210
+ false
211
+ end
212
+ end
213
+
214
+ def [] k
215
+ @hash[ k ]
216
+ end
217
+
218
+ def fetch(k, *a, &b)
219
+ @hash.fetch(k, *a, &b)
220
+ end
221
+
222
+ # Store operator.
223
+ #
224
+ # h[key] = value
225
+ #
226
+ # Or with additional index.
227
+ #
228
+ # h[key,index] = value
229
+ def []=(k, i=nil, v=nil)
230
+ if v
231
+ insert(i,k,v)
232
+ else
233
+ store(k,i)
234
+ end
235
+ end
236
+
237
+ def insert( i,k,v )
238
+ @order.insert( i,k )
239
+ @hash.store( k,v )
240
+ end
241
+
242
+ def store( a,b )
243
+ @order.push( a ) unless @hash.has_key?( a )
244
+ @hash.store( a,b )
245
+ end
246
+
247
+ def clear
248
+ @order = []
249
+ @hash.clear
250
+ end
251
+
252
+ def delete( key )
253
+ @order.delete( key )
254
+ @hash.delete( key )
255
+ end
256
+
257
+ def each_key
258
+ order.each { |k| yield( k ) }
259
+ self
260
+ end
261
+
262
+ def each_value
263
+ order.each { |k| yield( @hash[k] ) }
264
+ self
265
+ end
266
+
267
+ def each
268
+ order.each { |k| yield( k,@hash[k] ) }
269
+ self
270
+ end
271
+ alias each_pair each
272
+
273
+ def delete_if
274
+ order.clone.each { |k| delete k if yield(k,@hash[k]) }
275
+ self
276
+ end
277
+
278
+ def values
279
+ ary = []
280
+ order.each { |k| ary.push @hash[k] }
281
+ ary
282
+ end
283
+
284
+ def keys
285
+ order
286
+ end
287
+
288
+ def invert
289
+ hsh2 = self.class.new
290
+ order.each { |k| hsh2[@hash[k]] = k }
291
+ hsh2
292
+ end
293
+
294
+ def reject( &block )
295
+ self.dup.delete_if(&block)
296
+ end
297
+
298
+ def reject!( &block )
299
+ hsh2 = reject(&block)
300
+ self == hsh2 ? nil : hsh2
301
+ end
302
+
303
+ def replace( hsh2 )
304
+ @order = hsh2.order
305
+ @hash = hsh2.hash
306
+ end
307
+
308
+ def shift
309
+ key = order.first
310
+ key ? [key,delete(key)] : super
311
+ end
312
+
313
+ def unshift( k,v )
314
+ unless @hash.include?( k )
315
+ @order.unshift( k )
316
+ @hash.store( k,v )
317
+ true
318
+ else
319
+ false
320
+ end
321
+ end
322
+
323
+ def <<(kv)
324
+ push( *kv )
325
+ end
326
+
327
+ def push( k,v )
328
+ unless @hash.include?( k )
329
+ @order.push( k )
330
+ @hash.store( k,v )
331
+ true
332
+ else
333
+ false
334
+ end
335
+ end
336
+
337
+ def pop
338
+ key = order.last
339
+ key ? [key,delete(key)] : nil
340
+ end
341
+
342
+ def inspect
343
+ ary = []
344
+ each {|k,v| ary << k.inspect + "=>" + v.inspect}
345
+ '{' + ary.join(", ") + '}'
346
+ end
347
+
348
+ def dup
349
+ a = []
350
+ each{ |k,v| a << k; a << v }
351
+ self.class[*a]
352
+ end
353
+
354
+ def update( hsh2 )
355
+ hsh2.each { |k,v| self[k] = v }
356
+ reorder
357
+ self
358
+ end
359
+ alias :merge! update
360
+
361
+ def merge( hsh2 )
362
+ self.dup.update(hsh2)
363
+ end
364
+
365
+ def select
366
+ ary = []
367
+ each { |k,v| ary << [k,v] if yield k,v }
368
+ ary
369
+ end
370
+
371
+ def reverse!
372
+ @order.reverse!
373
+ self
374
+ end
375
+
376
+ def reverse
377
+ dup.reverse!
378
+ end
379
+
380
+ def first
381
+ @hash[order.first]
382
+ end
383
+
384
+ def last
385
+ @hash[order.last]
386
+ end
387
+
388
+ def length
389
+ @order.length
390
+ end
391
+ alias :size :length
392
+
393
+ def empty?
394
+ @hash.empty?
395
+ end
396
+
397
+ def has_key?(key)
398
+ @hash.has_key?(key)
399
+ end
400
+
401
+ def key?(key)
402
+ @hash.key?(key)
403
+ end
404
+
405
+ def to_a
406
+ ary = []
407
+ each { |k,v| ary << [k,v] }
408
+ ary
409
+ end
410
+
411
+ def to_json
412
+ buf = "["
413
+ map do |k,v|
414
+ buf << k.to_json
415
+ buf << ", "
416
+ buf << v.to_json
417
+ end.join(", ")
418
+ buf << "]"
419
+ buf
420
+ end
421
+
422
+ def to_s
423
+ self.to_a.to_s
424
+ end
425
+
426
+ def to_hash
427
+ @hash.dup
428
+ end
429
+
430
+ def to_h
431
+ @hash.dup
432
+ end
433
+ end
434
+ end; end
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module Validation
3
+ VERSION = '1.3.0.beta'
4
+ end
5
+ end