datamapper-dm-core 0.9.11 → 0.10.0

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 (192) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -39
  5. data/Manifest.txt +67 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +16 -15
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/dm-core.gemspec +11 -15
  12. data/lib/dm-core/adapters/abstract_adapter.rb +182 -185
  13. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  14. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  15. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  16. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  19. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  20. data/lib/dm-core/adapters.rb +135 -16
  21. data/lib/dm-core/associations/many_to_many.rb +372 -90
  22. data/lib/dm-core/associations/many_to_one.rb +220 -73
  23. data/lib/dm-core/associations/one_to_many.rb +319 -255
  24. data/lib/dm-core/associations/one_to_one.rb +66 -53
  25. data/lib/dm-core/associations/relationship.rb +560 -158
  26. data/lib/dm-core/collection.rb +1104 -381
  27. data/lib/dm-core/core_ext/kernel.rb +12 -0
  28. data/lib/dm-core/core_ext/symbol.rb +10 -0
  29. data/lib/dm-core/identity_map.rb +4 -34
  30. data/lib/dm-core/migrations.rb +1283 -0
  31. data/lib/dm-core/model/descendant_set.rb +81 -0
  32. data/lib/dm-core/model/hook.rb +45 -0
  33. data/lib/dm-core/model/is.rb +32 -0
  34. data/lib/dm-core/model/property.rb +248 -0
  35. data/lib/dm-core/model/relationship.rb +335 -0
  36. data/lib/dm-core/model/scope.rb +90 -0
  37. data/lib/dm-core/model.rb +570 -369
  38. data/lib/dm-core/property.rb +753 -280
  39. data/lib/dm-core/property_set.rb +141 -98
  40. data/lib/dm-core/query/conditions/comparison.rb +814 -0
  41. data/lib/dm-core/query/conditions/operation.rb +247 -0
  42. data/lib/dm-core/query/direction.rb +43 -0
  43. data/lib/dm-core/query/operator.rb +42 -0
  44. data/lib/dm-core/query/path.rb +102 -0
  45. data/lib/dm-core/query/sort.rb +45 -0
  46. data/lib/dm-core/query.rb +974 -492
  47. data/lib/dm-core/repository.rb +147 -107
  48. data/lib/dm-core/resource.rb +644 -429
  49. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  50. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  51. data/lib/dm-core/support/chainable.rb +20 -0
  52. data/lib/dm-core/support/deprecate.rb +12 -0
  53. data/lib/dm-core/support/equalizer.rb +23 -0
  54. data/lib/dm-core/support/logger.rb +13 -0
  55. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  56. data/lib/dm-core/transaction.rb +333 -92
  57. data/lib/dm-core/type.rb +98 -60
  58. data/lib/dm-core/types/boolean.rb +1 -1
  59. data/lib/dm-core/types/discriminator.rb +34 -20
  60. data/lib/dm-core/types/object.rb +7 -4
  61. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  62. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  63. data/lib/dm-core/types/serial.rb +3 -3
  64. data/lib/dm-core/types/text.rb +3 -4
  65. data/lib/dm-core/version.rb +1 -1
  66. data/lib/dm-core.rb +106 -110
  67. data/script/performance.rb +102 -109
  68. data/script/profile.rb +169 -38
  69. data/spec/lib/adapter_helpers.rb +105 -0
  70. data/spec/lib/collection_helpers.rb +18 -0
  71. data/spec/lib/counter_adapter.rb +34 -0
  72. data/spec/lib/pending_helpers.rb +27 -0
  73. data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
  74. data/spec/public/associations/many_to_many_spec.rb +193 -0
  75. data/spec/public/associations/many_to_one_spec.rb +73 -0
  76. data/spec/public/associations/one_to_many_spec.rb +77 -0
  77. data/spec/public/associations/one_to_one_spec.rb +156 -0
  78. data/spec/public/collection_spec.rb +65 -0
  79. data/spec/public/model/relationship_spec.rb +924 -0
  80. data/spec/public/model_spec.rb +159 -0
  81. data/spec/public/property_spec.rb +829 -0
  82. data/spec/public/resource_spec.rb +71 -0
  83. data/spec/public/sel_spec.rb +44 -0
  84. data/spec/public/setup_spec.rb +145 -0
  85. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  86. data/spec/public/shared/collection_shared_spec.rb +1723 -0
  87. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  88. data/spec/public/shared/resource_shared_spec.rb +924 -0
  89. data/spec/public/shared/sel_shared_spec.rb +112 -0
  90. data/spec/public/transaction_spec.rb +129 -0
  91. data/spec/public/types/discriminator_spec.rb +130 -0
  92. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  93. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  94. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  95. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  96. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  97. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  99. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  100. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  101. data/spec/semipublic/associations_spec.rb +177 -0
  102. data/spec/semipublic/collection_spec.rb +142 -0
  103. data/spec/semipublic/property_spec.rb +61 -0
  104. data/spec/semipublic/query/conditions_spec.rb +528 -0
  105. data/spec/semipublic/query/path_spec.rb +443 -0
  106. data/spec/semipublic/query_spec.rb +2626 -0
  107. data/spec/semipublic/resource_spec.rb +47 -0
  108. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  109. data/spec/spec.opts +3 -1
  110. data/spec/spec_helper.rb +80 -57
  111. data/tasks/ci.rb +19 -31
  112. data/tasks/dm.rb +43 -48
  113. data/tasks/doc.rb +8 -11
  114. data/tasks/gemspec.rb +5 -5
  115. data/tasks/hoe.rb +15 -16
  116. data/tasks/install.rb +8 -10
  117. metadata +72 -93
  118. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  119. data/lib/dm-core/associations.rb +0 -207
  120. data/lib/dm-core/auto_migrations.rb +0 -105
  121. data/lib/dm-core/dependency_queue.rb +0 -32
  122. data/lib/dm-core/hook.rb +0 -11
  123. data/lib/dm-core/is.rb +0 -16
  124. data/lib/dm-core/logger.rb +0 -232
  125. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  126. data/lib/dm-core/migrator.rb +0 -29
  127. data/lib/dm-core/scope.rb +0 -58
  128. data/lib/dm-core/support/array.rb +0 -13
  129. data/lib/dm-core/support/assertions.rb +0 -8
  130. data/lib/dm-core/support/errors.rb +0 -23
  131. data/lib/dm-core/support/kernel.rb +0 -11
  132. data/lib/dm-core/support/symbol.rb +0 -41
  133. data/lib/dm-core/support.rb +0 -7
  134. data/lib/dm-core/type_map.rb +0 -80
  135. data/lib/dm-core/types.rb +0 -19
  136. data/script/all +0 -4
  137. data/spec/integration/association_spec.rb +0 -1382
  138. data/spec/integration/association_through_spec.rb +0 -203
  139. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  140. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  141. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  142. data/spec/integration/auto_migrations_spec.rb +0 -413
  143. data/spec/integration/collection_spec.rb +0 -1073
  144. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  145. data/spec/integration/dependency_queue_spec.rb +0 -46
  146. data/spec/integration/model_spec.rb +0 -197
  147. data/spec/integration/mysql_adapter_spec.rb +0 -85
  148. data/spec/integration/postgres_adapter_spec.rb +0 -731
  149. data/spec/integration/property_spec.rb +0 -253
  150. data/spec/integration/query_spec.rb +0 -514
  151. data/spec/integration/repository_spec.rb +0 -61
  152. data/spec/integration/resource_spec.rb +0 -513
  153. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  154. data/spec/integration/sti_spec.rb +0 -273
  155. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  156. data/spec/integration/transaction_spec.rb +0 -75
  157. data/spec/integration/type_spec.rb +0 -275
  158. data/spec/lib/logging_helper.rb +0 -18
  159. data/spec/lib/mock_adapter.rb +0 -27
  160. data/spec/lib/model_loader.rb +0 -100
  161. data/spec/lib/publicize_methods.rb +0 -28
  162. data/spec/models/content.rb +0 -16
  163. data/spec/models/vehicles.rb +0 -34
  164. data/spec/models/zoo.rb +0 -48
  165. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  166. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  167. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  168. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  169. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  170. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  171. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  172. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  173. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  174. data/spec/unit/associations/relationship_spec.rb +0 -71
  175. data/spec/unit/associations_spec.rb +0 -242
  176. data/spec/unit/auto_migrations_spec.rb +0 -111
  177. data/spec/unit/collection_spec.rb +0 -182
  178. data/spec/unit/data_mapper_spec.rb +0 -35
  179. data/spec/unit/identity_map_spec.rb +0 -126
  180. data/spec/unit/is_spec.rb +0 -80
  181. data/spec/unit/migrator_spec.rb +0 -33
  182. data/spec/unit/model_spec.rb +0 -321
  183. data/spec/unit/naming_conventions_spec.rb +0 -36
  184. data/spec/unit/property_set_spec.rb +0 -90
  185. data/spec/unit/property_spec.rb +0 -753
  186. data/spec/unit/query_spec.rb +0 -571
  187. data/spec/unit/repository_spec.rb +0 -93
  188. data/spec/unit/resource_spec.rb +0 -649
  189. data/spec/unit/scope_spec.rb +0 -142
  190. data/spec/unit/transaction_spec.rb +0 -493
  191. data/spec/unit/type_map_spec.rb +0 -114
  192. data/spec/unit/type_spec.rb +0 -119
@@ -0,0 +1,247 @@
1
+ module DataMapper
2
+ class Query
3
+ module Conditions
4
+ class InvalidOperation < ArgumentError; end
5
+
6
+ class Operation
7
+ # TODO: document
8
+ # @api semipublic
9
+ def self.new(slug, *operands)
10
+ if klass = operation_class(slug)
11
+ klass.new(*operands)
12
+ else
13
+ raise "No Operation class for `#{slug.inspect}' has been defined"
14
+ end
15
+ end
16
+
17
+ # TODO: document
18
+ # @api semipublic
19
+ def self.operation_class(slug)
20
+ operation_classes[slug] ||= AbstractOperation.descendants.detect { |operation_class| operation_class.slug == slug }
21
+ end
22
+
23
+ # TODO: document
24
+ # @api private
25
+ def self.slugs
26
+ @slugs ||= AbstractOperation.descendants.map { |operation_class| operation_class.slug }
27
+ end
28
+
29
+ class << self
30
+ private
31
+
32
+ # TODO: document
33
+ # @api private
34
+ def operation_classes
35
+ @operation_classes ||= {}
36
+ end
37
+ end
38
+ end # class Operation
39
+
40
+ class AbstractOperation
41
+ include Enumerable
42
+ extend Equalizer
43
+
44
+ equalize :slug, :sorted_operands
45
+
46
+ # TODO: document
47
+ # @api semipublic
48
+ attr_reader :operands
49
+
50
+ # TODO: document
51
+ # @api private
52
+ def self.descendants
53
+ @descendants ||= Set.new
54
+ end
55
+
56
+ # TODO: document
57
+ # @api private
58
+ def self.inherited(operation_class)
59
+ descendants << operation_class
60
+ end
61
+
62
+ # TODO: document
63
+ # @api semipublic
64
+ def self.slug(slug = nil)
65
+ slug ? @slug = slug : @slug
66
+ end
67
+
68
+ # Return the comparison class slug
69
+ #
70
+ # @return [Symbol]
71
+ # the comparison class slug
72
+ #
73
+ # @api private
74
+ def slug
75
+ self.class.slug
76
+ end
77
+
78
+ # TODO: document
79
+ # @api semipublic
80
+ def each
81
+ @operands.each { |*block_args| yield(*block_args) }
82
+ end
83
+
84
+ # TODO: document
85
+ # @api semipublic
86
+ def valid?
87
+ operands.any? && operands.all? do |operand|
88
+ if operand.respond_to?(:valid?)
89
+ operand.valid?
90
+ else
91
+ true
92
+ end
93
+ end
94
+ end
95
+
96
+ # TODO: document
97
+ # @api semipublic
98
+ def <<(operand)
99
+ @operands << operand
100
+ self
101
+ end
102
+
103
+ # TODO: document
104
+ # @api semipublic
105
+ def inspect
106
+ "#<#{self.class} @operands=#{@operands.inspect}>"
107
+ end
108
+
109
+ # Return a list of operands in predictable order
110
+ #
111
+ # @return [Array<AbstractOperation>]
112
+ # list of operands sorted in deterministic order
113
+ #
114
+ # @api private
115
+ def sorted_operands
116
+ @operands.sort_by { |operand| operand.hash }
117
+ end
118
+
119
+ private
120
+
121
+ # TODO: document
122
+ # @api semipublic
123
+ def initialize(*operands)
124
+ @operands = operands
125
+ end
126
+
127
+ # TODO: document
128
+ # @api semipublic
129
+ def initialize_copy(*)
130
+ @operands = @operands.map { |operand| operand.dup }
131
+ end
132
+ end # class AbstractOperation
133
+
134
+ module FlattenOperation
135
+ # TODO: document
136
+ # @api semipublic
137
+ def <<(operand)
138
+ if operand.kind_of?(self.class)
139
+ @operands.concat(operand.operands)
140
+ self
141
+ else
142
+ super
143
+ end
144
+ end
145
+ end # module FlattenOperation
146
+
147
+ class AndOperation < AbstractOperation
148
+ include FlattenOperation
149
+
150
+ slug :and
151
+
152
+ # TODO: document
153
+ # @api semipublic
154
+ def matches?(record)
155
+ @operands.all? { |operand| operand.matches?(record) }
156
+ end
157
+ end # class AndOperation
158
+
159
+ class OrOperation < AbstractOperation
160
+ include FlattenOperation
161
+
162
+ slug :or
163
+
164
+ # TODO: document
165
+ # @api semipublic
166
+ def matches?(record)
167
+ @operands.any? { |operand| operand.matches?(record) }
168
+ end
169
+ end # class OrOperation
170
+
171
+ class NotOperation < AbstractOperation
172
+ slug :not
173
+
174
+ # TODO: document
175
+ # @api semipublic
176
+ def matches?(record)
177
+ not @operands.first.matches?(record)
178
+ end
179
+
180
+ # TODO: document
181
+ # @api semipublic
182
+ def <<(operand)
183
+ raise ArgumentError, "#{self.class} cannot have more than one operand" if @operands.size > 0
184
+ super
185
+ end
186
+
187
+ private
188
+
189
+ # TODO: document
190
+ # @api semipublic
191
+ def initialize(*operands)
192
+ raise InvalidOperation, "#{self.class} is a unary operator" if operands.size > 1
193
+ super
194
+ end
195
+ end # class NotOperation
196
+
197
+ class NullOperation < AbstractOperation
198
+ undef_method :<<
199
+
200
+ slug :null
201
+
202
+ # Match the record
203
+ #
204
+ # @param [Resource] record
205
+ # the resource to match
206
+ #
207
+ # @return [true]
208
+ # every record matches
209
+ #
210
+ # @api semipublic
211
+ def matches?(record)
212
+ true
213
+ end
214
+
215
+ # Test validity of the operation
216
+ #
217
+ # @return [true]
218
+ # always valid
219
+ #
220
+ # @api semipublic
221
+ def valid?
222
+ true
223
+ end
224
+
225
+ # Treat the operation the same as nil
226
+ #
227
+ # @return [true]
228
+ # should be treated as nil
229
+ #
230
+ # @api semipublic
231
+ def nil?
232
+ true
233
+ end
234
+
235
+ # Inspecting the operation should return the same as nil
236
+ #
237
+ # @return [String]
238
+ # return the string 'nil'
239
+ #
240
+ # @api semipublic
241
+ def inspect
242
+ 'nil'
243
+ end
244
+ end
245
+ end # module Conditions
246
+ end # class Query
247
+ end # module DataMapper
@@ -0,0 +1,43 @@
1
+ # TODO: rename this DM::Symbol::Direction
2
+
3
+ # TODO: add a method to convert it into a DM::Query::Sort object, eg:
4
+ # operator.sort_for(model)
5
+
6
+ # TODO: rename #target to #property_name
7
+
8
+ # TODO: make sure Query converts this into a DM::Query::Sort object
9
+ # immediately and passes that down to the Adapter
10
+
11
+ # TODO: remove #get method
12
+
13
+ module DataMapper
14
+ class Query
15
+ class Direction < Operator
16
+ extend Deprecate
17
+
18
+ deprecate :property, :target
19
+ deprecate :direction, :operator
20
+
21
+ # TODO: document
22
+ # @api private
23
+ def reverse!
24
+ @operator = @operator == :asc ? :desc : :asc
25
+ self
26
+ end
27
+
28
+ # TODO: document
29
+ # @api private
30
+ def get(resource)
31
+ Sort.new(target.get(resource), @operator == :asc)
32
+ end
33
+
34
+ private
35
+
36
+ # TODO: document
37
+ # @api private
38
+ def initialize(target, operator = :asc)
39
+ super
40
+ end
41
+ end # class Direction
42
+ end # class Query
43
+ end # module DataMapper
@@ -0,0 +1,42 @@
1
+ # TODO: rename this DM::Symbol::Operator
2
+
3
+ # TODO: add a method to convert it into a DM::Query::AbstractComparison object, eg:
4
+ # operator.comparison_for(repository, model)
5
+
6
+ # TODO: rename #target to #property_name
7
+
8
+ module DataMapper
9
+ class Query
10
+ class Operator
11
+ include Extlib::Assertions
12
+ extend Equalizer
13
+
14
+ equalize :target, :operator
15
+
16
+ # TODO: document
17
+ # @api private
18
+ attr_reader :target
19
+
20
+ # TODO: document
21
+ # @api private
22
+ attr_reader :operator
23
+
24
+ # TODO: document
25
+ # @api private
26
+ def inspect
27
+ "#<#{self.class.name} @target=#{target.inspect} @operator=#{operator.inspect}>"
28
+ end
29
+
30
+ private
31
+
32
+ # TODO: document
33
+ # @api private
34
+ def initialize(target, operator)
35
+ assert_kind_of 'operator', operator, Symbol
36
+
37
+ @target = target
38
+ @operator = operator
39
+ end
40
+ end # class Operator
41
+ end # class Query
42
+ end # module DataMapper
@@ -0,0 +1,102 @@
1
+ # TODO: instead of an Array of Path objects, create a Relationship
2
+ # on the fly using :through on the previous relationship, creating a
3
+ # chain. Query::Path could then be a thin wrapper that specifies extra
4
+ # conditions on the Relationships, like the target property o match
5
+ # on.
6
+
7
+ module DataMapper
8
+ class Query
9
+ class Path
10
+ instance_methods.each { |method| undef_method method unless %w[ __id__ __send__ send class dup object_id kind_of? instance_of? respond_to? equal? should should_not instance_variable_set instance_variable_get extend hash inspect ].include?(method.to_s) }
11
+
12
+ include Extlib::Assertions
13
+ extend Equalizer
14
+
15
+ equalize :relationships, :property
16
+
17
+ # TODO: document
18
+ # @api semipublic
19
+ attr_reader :repository_name
20
+
21
+ # TODO: document
22
+ # @api semipublic
23
+ attr_reader :relationships
24
+
25
+ # TODO: document
26
+ # @api semipublic
27
+ attr_reader :model
28
+
29
+ # TODO: document
30
+ # @api semipublic
31
+ attr_reader :property
32
+
33
+ (Conditions::Comparison.slugs | [ :not ]).each do |slug|
34
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
35
+ def #{slug} # def eql
36
+ #{"warn \"explicit use of '#{slug}' operator is deprecated (#{caller[0]})\"" if slug == :eql || slug == :in} # warn "explicit use of 'eql' operator is deprecated (#{caller[0]})"
37
+ Operator.new(self, #{slug.inspect}) # Operator.new(self, :eql)
38
+ end # end
39
+ RUBY
40
+ end
41
+
42
+ # TODO: document
43
+ # @api public
44
+ def kind_of?(klass)
45
+ super || (defined?(@property) ? @property.kind_of?(klass) : false)
46
+ end
47
+
48
+ # TODO: document
49
+ # @api public
50
+ def instance_of?(klass)
51
+ super || (defined?(@property) ? @property.instance_of?(klass) : false)
52
+ end
53
+
54
+ # TODO: document
55
+ # @api semipublic
56
+ def respond_to?(method, include_private = false)
57
+ super ||
58
+ (defined?(@property) && @property.respond_to?(method, include_private)) ||
59
+ @model.relationships(@repository_name).key?(method) ||
60
+ @model.properties(@repository_name).named?(method)
61
+ end
62
+
63
+ private
64
+
65
+ # TODO: document
66
+ # @api semipublic
67
+ def initialize(relationships, property_name = nil)
68
+ assert_kind_of 'relationships', relationships, Array
69
+ assert_kind_of 'property_name', property_name, Symbol, NilClass
70
+
71
+ @relationships = relationships.dup
72
+
73
+ last_relationship = @relationships.last
74
+ @repository_name = last_relationship.relative_target_repository_name
75
+ @model = last_relationship.target_model
76
+
77
+ if property_name
78
+ @property = @model.properties(@repository_name)[property_name] ||
79
+ raise(ArgumentError, "Unknown property '#{property_name}' in #{@model}")
80
+ end
81
+ end
82
+
83
+ # TODO: document
84
+ # @api semipublic
85
+ def method_missing(method, *args)
86
+ if @property
87
+ return @property.send(method, *args)
88
+ end
89
+
90
+ if relationship = @model.relationships(@repository_name)[method]
91
+ return self.class.new(@relationships.dup << relationship)
92
+ end
93
+
94
+ if @model.properties(@repository_name).named?(method)
95
+ return self.class.new(@relationships, method)
96
+ end
97
+
98
+ raise NoMethodError, "undefined property or relationship '#{method}' on #{@model}"
99
+ end
100
+ end # class Path
101
+ end # class Query
102
+ end # module DataMapper
@@ -0,0 +1,45 @@
1
+ # TODO: add #reverse and #reverse! methods
2
+
3
+ module DataMapper
4
+ class Query
5
+ class Sort
6
+ # TODO: document
7
+ # @api semipublic
8
+ attr_reader :value
9
+
10
+ # TODO: document
11
+ # @api semipublic
12
+ def direction
13
+ @ascending ? :ascending : :descending
14
+ end
15
+
16
+ # TODO: document
17
+ # @api private
18
+ def <=>(other)
19
+ other_value = other.value
20
+
21
+ cmp = case
22
+ when @value.nil? && other_value.nil?
23
+ 0
24
+ when @value.nil?
25
+ 1
26
+ when other_value.nil?
27
+ -1
28
+ else
29
+ @value <=> other_value
30
+ end
31
+
32
+ @ascending ? cmp : cmp * -1
33
+ end
34
+
35
+ private
36
+
37
+ # TODO: document
38
+ # @api private
39
+ def initialize(value, ascending = true)
40
+ @value = value
41
+ @ascending = ascending
42
+ end
43
+ end # class Sort
44
+ end # class Query
45
+ end # module DataMapper