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.
- data/.autotest +17 -14
- data/.gitignore +3 -1
- data/FAQ +6 -5
- data/History.txt +5 -50
- data/Manifest.txt +66 -76
- data/QUICKLINKS +1 -1
- data/README.txt +21 -15
- data/Rakefile +6 -7
- data/SPECS +2 -29
- data/TODO +1 -1
- data/deps.rip +2 -0
- data/dm-core.gemspec +11 -15
- data/lib/dm-core.rb +105 -110
- data/lib/dm-core/adapters.rb +135 -16
- data/lib/dm-core/adapters/abstract_adapter.rb +251 -181
- data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
- data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
- data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
- data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
- data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
- data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
- data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
- data/lib/dm-core/associations/many_to_many.rb +372 -90
- data/lib/dm-core/associations/many_to_one.rb +220 -73
- data/lib/dm-core/associations/one_to_many.rb +319 -255
- data/lib/dm-core/associations/one_to_one.rb +66 -53
- data/lib/dm-core/associations/relationship.rb +561 -156
- data/lib/dm-core/collection.rb +1101 -379
- data/lib/dm-core/core_ext/kernel.rb +12 -0
- data/lib/dm-core/core_ext/symbol.rb +10 -0
- data/lib/dm-core/identity_map.rb +4 -34
- data/lib/dm-core/migrations.rb +1283 -0
- data/lib/dm-core/model.rb +570 -369
- data/lib/dm-core/model/descendant_set.rb +81 -0
- data/lib/dm-core/model/hook.rb +45 -0
- data/lib/dm-core/model/is.rb +32 -0
- data/lib/dm-core/model/property.rb +247 -0
- data/lib/dm-core/model/relationship.rb +335 -0
- data/lib/dm-core/model/scope.rb +90 -0
- data/lib/dm-core/property.rb +808 -273
- data/lib/dm-core/property_set.rb +141 -98
- data/lib/dm-core/query.rb +1037 -483
- data/lib/dm-core/query/conditions/comparison.rb +872 -0
- data/lib/dm-core/query/conditions/operation.rb +221 -0
- data/lib/dm-core/query/direction.rb +43 -0
- data/lib/dm-core/query/operator.rb +84 -0
- data/lib/dm-core/query/path.rb +138 -0
- data/lib/dm-core/query/sort.rb +45 -0
- data/lib/dm-core/repository.rb +210 -94
- data/lib/dm-core/resource.rb +641 -421
- data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
- data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
- data/lib/dm-core/support/chainable.rb +22 -0
- data/lib/dm-core/support/deprecate.rb +12 -0
- data/lib/dm-core/support/logger.rb +13 -0
- data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
- data/lib/dm-core/transaction.rb +333 -92
- data/lib/dm-core/type.rb +98 -60
- data/lib/dm-core/types/boolean.rb +1 -1
- data/lib/dm-core/types/discriminator.rb +34 -20
- data/lib/dm-core/types/object.rb +7 -4
- data/lib/dm-core/types/paranoid_boolean.rb +11 -9
- data/lib/dm-core/types/paranoid_datetime.rb +11 -9
- data/lib/dm-core/types/serial.rb +3 -3
- data/lib/dm-core/types/text.rb +3 -4
- data/lib/dm-core/version.rb +1 -1
- data/script/performance.rb +102 -109
- data/script/profile.rb +169 -38
- data/spec/lib/adapter_helpers.rb +105 -0
- data/spec/lib/collection_helpers.rb +18 -0
- data/spec/lib/counter_adapter.rb +34 -0
- data/spec/lib/pending_helpers.rb +27 -0
- data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
- data/spec/public/associations/many_to_many_spec.rb +193 -0
- data/spec/public/associations/many_to_one_spec.rb +73 -0
- data/spec/public/associations/one_to_many_spec.rb +77 -0
- data/spec/public/associations/one_to_one_spec.rb +156 -0
- data/spec/public/collection_spec.rb +65 -0
- data/spec/public/migrations_spec.rb +359 -0
- data/spec/public/model/relationship_spec.rb +924 -0
- data/spec/public/model_spec.rb +159 -0
- data/spec/public/property_spec.rb +829 -0
- data/spec/public/resource_spec.rb +71 -0
- data/spec/public/sel_spec.rb +44 -0
- data/spec/public/setup_spec.rb +145 -0
- data/spec/public/shared/association_collection_shared_spec.rb +317 -0
- data/spec/public/shared/collection_shared_spec.rb +1670 -0
- data/spec/public/shared/finder_shared_spec.rb +1619 -0
- data/spec/public/shared/resource_shared_spec.rb +924 -0
- data/spec/public/shared/sel_shared_spec.rb +112 -0
- data/spec/public/transaction_spec.rb +129 -0
- data/spec/public/types/discriminator_spec.rb +130 -0
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
- data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
- data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
- data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
- data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
- data/spec/semipublic/associations/relationship_spec.rb +194 -0
- data/spec/semipublic/associations_spec.rb +177 -0
- data/spec/semipublic/collection_spec.rb +142 -0
- data/spec/semipublic/property_spec.rb +61 -0
- data/spec/semipublic/query/conditions_spec.rb +528 -0
- data/spec/semipublic/query/path_spec.rb +443 -0
- data/spec/semipublic/query_spec.rb +2626 -0
- data/spec/semipublic/resource_spec.rb +47 -0
- data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
- data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
- data/spec/spec.opts +3 -1
- data/spec/spec_helper.rb +80 -57
- data/tasks/ci.rb +19 -31
- data/tasks/dm.rb +43 -48
- data/tasks/doc.rb +8 -11
- data/tasks/gemspec.rb +5 -5
- data/tasks/hoe.rb +15 -16
- data/tasks/install.rb +8 -10
- metadata +74 -111
- data/lib/dm-core/associations.rb +0 -207
- data/lib/dm-core/associations/relationship_chain.rb +0 -81
- data/lib/dm-core/auto_migrations.rb +0 -105
- data/lib/dm-core/dependency_queue.rb +0 -32
- data/lib/dm-core/hook.rb +0 -11
- data/lib/dm-core/is.rb +0 -16
- data/lib/dm-core/logger.rb +0 -232
- data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
- data/lib/dm-core/migrator.rb +0 -29
- data/lib/dm-core/scope.rb +0 -58
- data/lib/dm-core/support.rb +0 -7
- data/lib/dm-core/support/array.rb +0 -13
- data/lib/dm-core/support/assertions.rb +0 -8
- data/lib/dm-core/support/errors.rb +0 -23
- data/lib/dm-core/support/kernel.rb +0 -11
- data/lib/dm-core/support/symbol.rb +0 -41
- data/lib/dm-core/type_map.rb +0 -80
- data/lib/dm-core/types.rb +0 -19
- data/script/all +0 -4
- data/spec/integration/association_spec.rb +0 -1382
- data/spec/integration/association_through_spec.rb +0 -203
- data/spec/integration/associations/many_to_many_spec.rb +0 -449
- data/spec/integration/associations/many_to_one_spec.rb +0 -163
- data/spec/integration/associations/one_to_many_spec.rb +0 -188
- data/spec/integration/auto_migrations_spec.rb +0 -413
- data/spec/integration/collection_spec.rb +0 -1073
- data/spec/integration/data_objects_adapter_spec.rb +0 -32
- data/spec/integration/dependency_queue_spec.rb +0 -46
- data/spec/integration/model_spec.rb +0 -197
- data/spec/integration/mysql_adapter_spec.rb +0 -85
- data/spec/integration/postgres_adapter_spec.rb +0 -731
- data/spec/integration/property_spec.rb +0 -253
- data/spec/integration/query_spec.rb +0 -514
- data/spec/integration/repository_spec.rb +0 -61
- data/spec/integration/resource_spec.rb +0 -513
- data/spec/integration/sqlite3_adapter_spec.rb +0 -352
- data/spec/integration/sti_spec.rb +0 -273
- data/spec/integration/strategic_eager_loading_spec.rb +0 -156
- data/spec/integration/transaction_spec.rb +0 -75
- data/spec/integration/type_spec.rb +0 -275
- data/spec/lib/logging_helper.rb +0 -18
- data/spec/lib/mock_adapter.rb +0 -27
- data/spec/lib/model_loader.rb +0 -100
- data/spec/lib/publicize_methods.rb +0 -28
- data/spec/models/content.rb +0 -16
- data/spec/models/vehicles.rb +0 -34
- data/spec/models/zoo.rb +0 -48
- data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
- data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
- data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
- data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
- data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
- data/spec/unit/associations/many_to_many_spec.rb +0 -32
- data/spec/unit/associations/many_to_one_spec.rb +0 -159
- data/spec/unit/associations/one_to_many_spec.rb +0 -393
- data/spec/unit/associations/one_to_one_spec.rb +0 -7
- data/spec/unit/associations/relationship_spec.rb +0 -71
- data/spec/unit/associations_spec.rb +0 -242
- data/spec/unit/auto_migrations_spec.rb +0 -111
- data/spec/unit/collection_spec.rb +0 -182
- data/spec/unit/data_mapper_spec.rb +0 -35
- data/spec/unit/identity_map_spec.rb +0 -126
- data/spec/unit/is_spec.rb +0 -80
- data/spec/unit/migrator_spec.rb +0 -33
- data/spec/unit/model_spec.rb +0 -321
- data/spec/unit/naming_conventions_spec.rb +0 -36
- data/spec/unit/property_set_spec.rb +0 -90
- data/spec/unit/property_spec.rb +0 -753
- data/spec/unit/query_spec.rb +0 -571
- data/spec/unit/repository_spec.rb +0 -93
- data/spec/unit/resource_spec.rb +0 -649
- data/spec/unit/scope_spec.rb +0 -142
- data/spec/unit/transaction_spec.rb +0 -493
- data/spec/unit/type_map_spec.rb +0 -114
- data/spec/unit/type_spec.rb +0 -119
|
@@ -0,0 +1,221 @@
|
|
|
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
|
+
|
|
43
|
+
# TODO: document
|
|
44
|
+
# @api semipublic
|
|
45
|
+
attr_reader :operands
|
|
46
|
+
|
|
47
|
+
# TODO: document
|
|
48
|
+
# @api private
|
|
49
|
+
def self.descendants
|
|
50
|
+
@descendants ||= Set.new
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# TODO: document
|
|
54
|
+
# @api private
|
|
55
|
+
def self.inherited(operation_class)
|
|
56
|
+
descendants << operation_class
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# TODO: document
|
|
60
|
+
# @api semipublic
|
|
61
|
+
def self.slug(slug = nil)
|
|
62
|
+
slug ? @slug = slug : @slug
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# TODO: document
|
|
66
|
+
# @api semipublic
|
|
67
|
+
def each
|
|
68
|
+
@operands.each { |*block_args| yield(*block_args) }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# TODO: document
|
|
72
|
+
# @api semipublic
|
|
73
|
+
def valid?
|
|
74
|
+
operands.all? do |operand|
|
|
75
|
+
if operand.respond_to?(:valid?)
|
|
76
|
+
operand.valid?
|
|
77
|
+
else
|
|
78
|
+
true
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# TODO: document
|
|
84
|
+
# @api semipublic
|
|
85
|
+
def <<(operand)
|
|
86
|
+
@operands << operand
|
|
87
|
+
self
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# TODO: document
|
|
91
|
+
# @api semipublic
|
|
92
|
+
def hash
|
|
93
|
+
@operands.sort_by { |operand| operand.hash }.hash
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# TODO: document
|
|
97
|
+
# @api semipublic
|
|
98
|
+
def ==(other)
|
|
99
|
+
if equal?(other)
|
|
100
|
+
return true
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
other_class = other.class
|
|
104
|
+
|
|
105
|
+
unless other_class.respond_to?(:slug) && other_class.slug == self.class.slug
|
|
106
|
+
return false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
unless other.respond_to?(:operands)
|
|
110
|
+
return false
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
cmp?(other, :==)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# TODO: document
|
|
117
|
+
# @api semipublic
|
|
118
|
+
def eql?(other)
|
|
119
|
+
if equal?(other)
|
|
120
|
+
return true
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
unless instance_of?(other.class)
|
|
124
|
+
return false
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
cmp?(other, :eql?)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# TODO: document
|
|
131
|
+
# @api semipublic
|
|
132
|
+
def inspect
|
|
133
|
+
"#<#{self.class} @operands=#{@operands.inspect}>"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
# TODO: document
|
|
139
|
+
# @api semipublic
|
|
140
|
+
def initialize(*operands)
|
|
141
|
+
@operands = operands
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# TODO: document
|
|
145
|
+
# @api semipublic
|
|
146
|
+
def initialize_copy(*)
|
|
147
|
+
@operands = @operands.map { |operand| operand.dup }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# TODO: document
|
|
151
|
+
# @api private
|
|
152
|
+
def cmp?(other, operator)
|
|
153
|
+
@operands.sort_by { |operand| operand.hash }.send(operator, other.operands.sort_by { |operand| operand.hash })
|
|
154
|
+
end
|
|
155
|
+
end # class AbstractOperation
|
|
156
|
+
|
|
157
|
+
module FlattenOperation
|
|
158
|
+
# TODO: document
|
|
159
|
+
# @api semipublic
|
|
160
|
+
def <<(operand)
|
|
161
|
+
if operand.kind_of?(self.class)
|
|
162
|
+
@operands.concat(operand.operands)
|
|
163
|
+
self
|
|
164
|
+
else
|
|
165
|
+
super
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end # module FlattenOperation
|
|
169
|
+
|
|
170
|
+
class AndOperation < AbstractOperation
|
|
171
|
+
include FlattenOperation
|
|
172
|
+
|
|
173
|
+
slug :and
|
|
174
|
+
|
|
175
|
+
# TODO: document
|
|
176
|
+
# @api semipublic
|
|
177
|
+
def matches?(record)
|
|
178
|
+
@operands.all? { |operand| operand.matches?(record) }
|
|
179
|
+
end
|
|
180
|
+
end # class AndOperation
|
|
181
|
+
|
|
182
|
+
class OrOperation < AbstractOperation
|
|
183
|
+
include FlattenOperation
|
|
184
|
+
|
|
185
|
+
slug :or
|
|
186
|
+
|
|
187
|
+
# TODO: document
|
|
188
|
+
# @api semipublic
|
|
189
|
+
def matches?(record)
|
|
190
|
+
@operands.any? { |operand| operand.matches?(record) }
|
|
191
|
+
end
|
|
192
|
+
end # class OrOperation
|
|
193
|
+
|
|
194
|
+
class NotOperation < AbstractOperation
|
|
195
|
+
slug :not
|
|
196
|
+
|
|
197
|
+
# TODO: document
|
|
198
|
+
# @api semipublic
|
|
199
|
+
def matches?(record)
|
|
200
|
+
not @operands.first.matches?(record)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# TODO: document
|
|
204
|
+
# @api semipublic
|
|
205
|
+
def <<(operand)
|
|
206
|
+
raise ArgumentError, "#{self.class} cannot have more than one operand" if @operands.size > 0
|
|
207
|
+
super
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
private
|
|
211
|
+
|
|
212
|
+
# TODO: document
|
|
213
|
+
# @api semipublic
|
|
214
|
+
def initialize(*operands)
|
|
215
|
+
raise InvalidOperation, "#{self.class} is a unary operator" if operands.size > 1
|
|
216
|
+
super
|
|
217
|
+
end
|
|
218
|
+
end # class NotOperation
|
|
219
|
+
end # module Conditions
|
|
220
|
+
end # class Query
|
|
221
|
+
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,84 @@
|
|
|
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
|
+
|
|
13
|
+
# TODO: document
|
|
14
|
+
# @api private
|
|
15
|
+
attr_reader :target
|
|
16
|
+
|
|
17
|
+
# TODO: document
|
|
18
|
+
# @api private
|
|
19
|
+
attr_reader :operator
|
|
20
|
+
|
|
21
|
+
# TODO: document
|
|
22
|
+
# @api private
|
|
23
|
+
def ==(other)
|
|
24
|
+
if equal?(other)
|
|
25
|
+
return true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
unless other.respond_to?(:target)
|
|
29
|
+
return false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
unless other.respond_to?(:operator)
|
|
33
|
+
return false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
cmp?(other, :==)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# TODO: document
|
|
40
|
+
# @api private
|
|
41
|
+
def eql?(other)
|
|
42
|
+
if equal?(other)
|
|
43
|
+
return true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
unless instance_of?(other.class)
|
|
47
|
+
return false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
cmp?(other, :eql?)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# TODO: document
|
|
54
|
+
# @api private
|
|
55
|
+
def hash
|
|
56
|
+
@target.hash
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# TODO: document
|
|
60
|
+
# @api private
|
|
61
|
+
def inspect
|
|
62
|
+
"#<#{self.class.name} @target=#{target.inspect} @operator=#{operator.inspect}>"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
# TODO: document
|
|
68
|
+
# @api private
|
|
69
|
+
def initialize(target, operator)
|
|
70
|
+
assert_kind_of 'operator', operator, Symbol
|
|
71
|
+
|
|
72
|
+
@target = target
|
|
73
|
+
@operator = operator
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# TODO: document
|
|
77
|
+
# @api private
|
|
78
|
+
def cmp?(other, operator)
|
|
79
|
+
target.send(operator, other.target) &&
|
|
80
|
+
self.operator.send(operator, other.operator)
|
|
81
|
+
end
|
|
82
|
+
end # class Operator
|
|
83
|
+
end # class Query
|
|
84
|
+
end # module DataMapper
|
|
@@ -0,0 +1,138 @@
|
|
|
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
|
+
|
|
14
|
+
# TODO: document
|
|
15
|
+
# @api semipublic
|
|
16
|
+
attr_reader :repository_name
|
|
17
|
+
|
|
18
|
+
# TODO: document
|
|
19
|
+
# @api semipublic
|
|
20
|
+
attr_reader :relationships
|
|
21
|
+
|
|
22
|
+
# TODO: document
|
|
23
|
+
# @api semipublic
|
|
24
|
+
attr_reader :model
|
|
25
|
+
|
|
26
|
+
# TODO: document
|
|
27
|
+
# @api semipublic
|
|
28
|
+
attr_reader :property
|
|
29
|
+
|
|
30
|
+
(Conditions::Comparison.slugs | [ :not ]).each do |slug|
|
|
31
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
32
|
+
def #{slug} # def eql
|
|
33
|
+
#{"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]})"
|
|
34
|
+
Operator.new(self, #{slug.inspect}) # Operator.new(self, :eql)
|
|
35
|
+
end # end
|
|
36
|
+
RUBY
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# TODO: document
|
|
40
|
+
# @api public
|
|
41
|
+
def kind_of?(klass)
|
|
42
|
+
super || (defined?(@property) && @property.kind_of?(klass))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# TODO: document
|
|
46
|
+
# @api public
|
|
47
|
+
def instance_of?(klass)
|
|
48
|
+
super || (defined?(@property) && @property.instance_of?(klass))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# TODO: document
|
|
52
|
+
# @api semipublic
|
|
53
|
+
def respond_to?(method, include_private = false)
|
|
54
|
+
super ||
|
|
55
|
+
(defined?(@property) && @property.respond_to?(method, include_private)) ||
|
|
56
|
+
@model.relationships(@repository_name).key?(method) ||
|
|
57
|
+
@model.properties(@repository_name).named?(method)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# TODO: document
|
|
61
|
+
# @api semipublic
|
|
62
|
+
def ==(other)
|
|
63
|
+
if equal?(other)
|
|
64
|
+
return true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
unless other.respond_to?(:relationships)
|
|
68
|
+
return false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
unless other.respond_to?(:property)
|
|
72
|
+
return false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
cmp?(other, :==)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# TODO: document
|
|
79
|
+
# @api semipublic
|
|
80
|
+
def eql?(other)
|
|
81
|
+
if equal?(other)
|
|
82
|
+
return true
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
unless instance_of?(other.class)
|
|
86
|
+
return false
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
cmp?(other, :eql?)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
# TODO: document
|
|
95
|
+
# @api semipublic
|
|
96
|
+
def initialize(relationships, property_name = nil)
|
|
97
|
+
assert_kind_of 'relationships', relationships, Array
|
|
98
|
+
assert_kind_of 'property_name', property_name, Symbol, NilClass
|
|
99
|
+
|
|
100
|
+
@relationships = relationships.dup
|
|
101
|
+
|
|
102
|
+
last_relationship = @relationships.last
|
|
103
|
+
@repository_name = last_relationship.relative_target_repository_name
|
|
104
|
+
@model = last_relationship.target_model
|
|
105
|
+
|
|
106
|
+
if property_name
|
|
107
|
+
@property = @model.properties(@repository_name)[property_name] ||
|
|
108
|
+
raise(ArgumentError, "Unknown property '#{property_name}' in #{@model}")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# TODO: document
|
|
113
|
+
# @api private
|
|
114
|
+
def cmp?(other, operator)
|
|
115
|
+
relationships.send(operator, other.relationships) &&
|
|
116
|
+
property.send(operator, other.property)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# TODO: document
|
|
120
|
+
# @api semipublic
|
|
121
|
+
def method_missing(method, *args)
|
|
122
|
+
if @property
|
|
123
|
+
return @property.send(method, *args)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if relationship = @model.relationships(@repository_name)[method]
|
|
127
|
+
return self.class.new(@relationships.dup << relationship)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
if @model.properties(@repository_name).named?(method)
|
|
131
|
+
return self.class.new(@relationships, method)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
raise NoMethodError, "undefined property or relationship '#{method}' on #{@model}"
|
|
135
|
+
end
|
|
136
|
+
end # class Path
|
|
137
|
+
end # class Query
|
|
138
|
+
end # module DataMapper
|