dm-ambition 1.0.0 → 1.1.0.rc1

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.
@@ -1,184 +1,95 @@
1
- require 'parse_tree'
2
- require 'parse_tree_extensions'
3
- require 'ruby2ruby'
4
-
5
1
  module DataMapper
6
2
  module Ambition
7
3
  module Query
8
4
  class FilterProcessor < SexpProcessor
9
5
  attr_reader :conditions
10
6
 
11
- def initialize(binding, model, negated = false)
7
+ def initialize(binding, model)
12
8
  super()
13
9
 
14
10
  self.expected = Object # allow anything for now
15
11
  self.auto_shift_type = true
16
12
  self.default_method = :process_error
17
13
 
18
- @binding = binding
19
- @model = model
20
- @receiver = nil
21
-
22
- @container = DataMapper::Query::Conditions::Operation.new(:and)
14
+ @binding = binding
15
+ @model = model
23
16
 
24
- if negated
25
- @conditions = DataMapper::Query::Conditions::Operation.new(:not, @container)
26
- else
27
- @conditions = @container
28
- end
17
+ @conditions = @container = DataMapper::Query::Conditions::Operation.new(:and)
29
18
  end
30
19
 
31
20
  def process_error(exp)
32
- raise "DEBUG: calling process_#{exp.shift} with #{exp.inspect} (in process_error)"
21
+ raise ArgumentError, "calling process_#{exp.shift} with #{exp.inspect}"
33
22
  end
34
23
 
35
24
  def process_iter(exp)
36
- call_argslist = exp.shift
37
- raise "DEBUG: invalid: #{call_argslist.inspct}" if call_argslist != s(:call, nil, :proc, s(:arglist))
38
-
39
- # get the reciever
40
- @receiver = process(exp.shift)
41
-
42
- # process the Proc body
43
- result = process(exp.shift)
44
-
45
- if result.nil?
46
- raise 'DEBUG: conditions must be supplied'
47
- end
48
-
49
- unless result.equal?(@container)
50
- raise "DEBUG: invalid result processing body: #{result.inspect} (expected #{@container.inspect})"
51
- end
52
-
53
- result
54
- end
55
-
56
- def process_lasgn(exp)
57
25
  exp.shift
26
+ @receiver = process(exp.shift)
27
+ process(exp.shift)
58
28
  end
59
29
 
60
30
  def process_call(exp)
61
- if exp.size == 3
62
- lhs = process(exp.shift)
63
- operator = exp.shift
64
- rhs = process(exp.shift)
65
-
66
- if operator == :send
67
- operator = rhs.shift
68
- end
31
+ lhs = process(exp.shift)
32
+ operator = exp.shift
33
+ rhs = process(exp.shift)
69
34
 
70
- if rhs.size > 1
71
- raise "DEBUG: rhs.size should not be larger than 1, but was #{rhs.size}: #{rhs.inspect}"
72
- else
73
- rhs = rhs.first
74
- end
35
+ while [ :send, :__send__ ].include?(operator)
36
+ operator = rhs.shift
37
+ end
75
38
 
76
- if lhs.nil?
77
- nil
78
- else
79
- evaluate_operator(operator, lhs, rhs)
80
- end
39
+ if lhs.nil? && operator != :==
40
+ call_method(operator, *rhs)
81
41
  else
82
- raise "DEBUG: unhandled call: #{exp.inspect}"
42
+ evaluate_operator(operator, lhs, rhs.shift)
83
43
  end
84
44
  end
85
45
 
86
46
  def process_and(exp)
87
- parent = @container
88
-
89
- begin
90
- unless @container.kind_of?(DataMapper::Query::Conditions::AndOperation)
91
- parent << @container = DataMapper::Query::Conditions::Operation.new(:and)
92
- end
93
-
94
- while sexp = exp.shift
95
- process(sexp)
96
- end
97
- ensure
98
- @container = parent
99
- end
100
-
101
- @container
47
+ process_connective(exp, :and)
102
48
  end
103
49
 
104
50
  def process_or(exp)
105
- parent = @container
106
-
107
- begin
108
- unless @container.kind_of?(DataMapper::Query::Conditions::OrOperation)
109
- parent << @container = DataMapper::Query::Conditions::Operation.new(:or)
110
- end
111
-
112
- while sexp = exp.shift
113
- process(sexp)
114
- end
115
- ensure
116
- @container = parent
117
- end
118
-
119
- @container
51
+ process_connective(exp, :or)
120
52
  end
121
53
 
122
54
  def process_not(exp)
123
- parent = @container
124
-
125
- begin
126
- parent << @container = DataMapper::Query::Conditions::Operation.new(:not)
127
- process(exp.shift)
128
- ensure
129
- @container = parent
130
- end
131
-
132
- @container
55
+ process_connective(exp, :not)
133
56
  end
134
57
 
135
58
  def process_lvar(exp)
136
- var = exp.shift
137
- var == @receiver ? @model : value(var)
59
+ lvar = exp.shift
60
+ lvar.equal?(@receiver) ? @receiver : eval(lvar)
138
61
  end
139
62
 
140
63
  def process_arglist(exp)
141
- arglist = []
142
- while sexp = exp.shift
143
- arglist << process(sexp)
144
- end
145
- arglist
146
- end
147
-
148
- def process_colon2(exp)
149
- const = process(exp.shift)
150
-
151
- const.const_get(exp.shift)
64
+ process_array(exp)
152
65
  end
153
66
 
154
- def process_const(exp)
155
- Object.const_get(exp.shift)
67
+ def process_block(exp)
68
+ process_array(exp).last
156
69
  end
157
70
 
158
71
  def process_match3(exp)
159
- rhs = process(exp.shift)
160
- lhs = process(exp.shift)
72
+ evaluate_operator(:=~, process(exp.shift), process(exp.shift))
73
+ end
161
74
 
162
- evaluate_operator(:=~, lhs, rhs)
75
+ def process_colon2(exp)
76
+ process(exp.shift).const_get(exp.shift)
163
77
  end
164
78
 
165
- def process_array(exp)
166
- array = []
167
- while sexp = exp.shift
168
- array << process(sexp)
169
- end
170
- array
79
+ def process_const(exp)
80
+ Object.const_get(exp.shift)
171
81
  end
172
82
 
173
- def process_hash(exp)
174
- hash = {}
175
- until exp.empty?
176
- key = process(exp.shift)
177
- value = process(exp.shift)
83
+ def process_masgn(exp)
84
+ vars, values = process(exp.shift), process(exp.shift)
85
+ vars.zip(values) { |var, value| assign_value(var, value) }
86
+ values
87
+ end
178
88
 
179
- hash[key] = value
180
- end
181
- hash
89
+ def process_lasgn(exp)
90
+ var = exp.shift
91
+ return var if exp.empty?
92
+ assign_value(var, process(exp.shift))
182
93
  end
183
94
 
184
95
  def process_str(exp)
@@ -186,9 +97,15 @@ module DataMapper
186
97
  end
187
98
 
188
99
  def process_lit(exp)
189
- literal = exp.shift
190
- exp.shift # FIXME: workaround for bug in ParseTree or SexpProcessor
191
- literal
100
+ exp.shift
101
+ end
102
+
103
+ def process_ivar(exp)
104
+ eval(exp.shift)
105
+ end
106
+
107
+ def process_gvar(exp)
108
+ eval(exp.shift)
192
109
  end
193
110
 
194
111
  def process_true(exp)
@@ -203,158 +120,135 @@ module DataMapper
203
120
  nil
204
121
  end
205
122
 
206
- def process_ivar(exp)
207
- value(exp.shift)
123
+ def process_array(exp)
124
+ array = []
125
+ array << process(exp.shift) until exp.empty?
126
+ array
208
127
  end
209
128
 
210
- def process_gvar(exp)
211
- value(exp.shift)
129
+ def process_hash(exp)
130
+ hash = {}
131
+ hash[process(exp.shift)] = process(exp.shift) until exp.empty?
132
+ hash
212
133
  end
213
134
 
214
- def evaluate_operator(operator, lhs, rhs)
215
- if operator == :=~ && !lhs.kind_of?(Regexp) && !rhs.kind_of?(Regexp)
216
- raise "DEBUG: when using =~ operator one side should be a Regexp"
217
- end
135
+ private
218
136
 
219
- if lhs == @model
220
- if rhs.nil?
221
- @model.properties[operator]
222
- elsif rhs.kind_of?(DataMapper::Resource) && operator == :==
223
- resource = rhs
137
+ def process_connective(exp, operation)
138
+ parent, @container = @container, DataMapper::Query::Conditions::Operation.new(operation)
139
+ process(exp.shift) until exp.empty?
140
+ parent << @container
141
+ ensure
142
+ @container = parent
143
+ end
224
144
 
225
- if resource.repository == DataMapper.repository &&
226
- resource.saved? &&
227
- !resource.dirty? &&
228
- (key = resource.key).all?
145
+ def evaluate_operator(operator, lhs, rhs)
146
+ if lhs.equal?(@receiver) then evaluate_receiver_source(operator, lhs, rhs)
147
+ elsif rhs.equal?(@receiver) then evaluate_receiver_target(operator, lhs, rhs)
148
+ elsif lhs.kind_of?(DataMapper::Property) then evaluate_property_source(operator, lhs, rhs)
149
+ elsif rhs.kind_of?(DataMapper::Property) then evaluate_property_target(operator, lhs, rhs)
150
+ else
151
+ lhs.send(operator, *Array(rhs))
152
+ end
153
+ end
229
154
 
230
- @model.key.zip(key) do |property, bind_value|
231
- @container << DataMapper::Query::Conditions::Comparison.new(:eql, property, bind_value)
232
- end
155
+ def evaluate_receiver_source(operator, lhs, rhs)
156
+ if rhs.nil? && @model.properties.named?(operator)
157
+ @model.properties[operator]
158
+ else
159
+ resources = operator == :== ? [ rhs ] : []
160
+ key = @model.key
161
+ add_condition(DataMapper::Query.target_conditions(resources, key, key))
162
+ end
163
+ end
233
164
 
234
- @container
165
+ def evaluate_receiver_target(operator, lhs, rhs)
166
+ resources = case lhs
167
+ when Hash
168
+ case operator
169
+ when :key?, :has_key?, :include?, :member? then lhs.keys.sort
170
+ when :value?, :has_value? then lhs.values.sort
171
+ end
172
+ when Enumerable
173
+ case operator
174
+ when :include?, :member? then lhs
175
+ end
176
+ when Resource
177
+ case operator
178
+ when :==, :eql? then lhs
235
179
  end
236
180
  else
237
- raise "DEBUG: cannot call #{@model.name}.#{operator} with #{rhs.inspect}"
238
- end
239
-
240
- elsif rhs == @model
241
- if @model.key.size > 1
242
- raise 'Until OR conditions are added can only match resources with single keys'
243
- end
244
-
245
- resources = case lhs
246
- when Array
247
- case operator
248
- when :include?, :member? then lhs
249
- end
250
-
251
- when Hash
252
- case operator
253
- when :key?, :has_key?, :include?, :member? then lhs.keys
254
- when :value?, :has_value? then lhs.values
255
- end
256
- end
257
-
258
- unless resources
259
- raise "DEBUG: cannot call #{lhs.class}##{operator} with #{rhs.inspect}"
260
- end
261
-
262
- unless resources.all? { |r| r.kind_of?(DataMapper::Resource) }
263
- raise 'cannot compare against a non-resource'
264
- end
265
-
266
- property = @model.key.first
267
- bind_value = resources.map { |r| r.key.first }.sort
268
-
269
- evaluate_operator(:include?, bind_value, property)
270
-
271
- elsif lhs.kind_of?(DataMapper::Property)
272
- property = lhs
273
- bind_value = rhs
274
-
275
- # TODO: throw an exception if the operator is :== and the value is an Array
276
- # - this prevents conditions like { |u| u.val == [ 1, 2, 3 ] }
277
-
278
- if operator == :nil? && bind_value.nil?
279
- operator = :==
280
- bind_value = nil
281
- end
282
-
283
- operator = remap_operator(operator)
284
-
285
- @container << DataMapper::Query::Conditions::Comparison.new(operator, property, bind_value)
286
- @container
287
-
288
- elsif rhs.kind_of?(DataMapper::Property)
289
- property = rhs
290
- bind_value = lhs
291
-
292
- # TODO: throw an exception if the operator is :== and the bind value is an Array
293
- # - this prevents conditions like { |u| [ 1, 2, 3 ] == u.val }
294
-
295
- case bind_value
296
- when Array
297
- case operator
298
- when :include?, :member?
299
- operator = :in
300
- else
301
- raise "DEBUG: cannot call Array##{operator} with #{bind_value.inspect}"
302
- end
303
-
304
- when Range
305
- case operator
306
- when :include?, :member?, :===
307
- operator = :in
308
- else
309
- raise "DEBUG: cannot call Range##{operator} with #{bind_value.inspect}"
310
- end
311
-
312
- when Hash
313
- case operator
314
- when :key?, :has_key?, :include?, :member?
315
- operator = :in
316
- bind_value = bind_value.keys
317
- when :value?, :has_value?
318
- operator = :in
319
- bind_value = bind_value.values
320
- else
321
- raise "DEBUG: cannot call Hash##{operator} with #{bind_value.inspect}"
322
- end
323
- end
324
-
325
- operator = remap_operator(operator)
326
-
327
- @container << DataMapper::Query::Conditions::Comparison.new(operator, property, bind_value)
328
- @container
329
-
330
- elsif lhs.respond_to?(operator)
331
- lhs.send(operator, *[ rhs ].compact)
181
+ []
182
+ end
332
183
 
333
- else
334
- raise "DEBUG: not handled: #{lhs.inspect} #{operator} #{rhs.inspect}"
184
+ key = @model.key
185
+ add_condition(DataMapper::Query.target_conditions(resources, key, key))
186
+ end
187
+
188
+ def evaluate_property_source(operator, lhs, rhs)
189
+ operator = operator == :nil? ? :eql : remap_operator(operator)
190
+ add_condition(DataMapper::Query::Conditions::Comparison.new(operator, lhs, rhs))
191
+ end
192
+
193
+ def evaluate_property_target(operator, lhs, rhs)
194
+ bind_value = lhs
335
195
 
196
+ operator = case bind_value
197
+ when Hash
198
+ case operator
199
+ when :key?, :has_key?, :include?, :member?
200
+ bind_value = bind_value.keys.sort
201
+ :in
202
+ when :value?, :has_value?
203
+ bind_value = bind_value.values.sort
204
+ :in
205
+ end
206
+ when Range
207
+ case operator
208
+ when :include?, :member?, :===
209
+ :in
210
+ end
211
+ when Enumerable
212
+ case operator
213
+ when :include?, :member?
214
+ bind_value = bind_value.sort
215
+ :in
216
+ end
217
+ else
218
+ remap_operator(operator)
336
219
  end
220
+
221
+ add_condition(DataMapper::Query::Conditions::Comparison.new(operator, rhs, bind_value))
337
222
  end
338
223
 
339
- # TODO: update dm-core internals to use the Ruby operators
340
- # insted of the DM specific ones
341
224
  def remap_operator(operator)
342
225
  # remap Ruby to DM operators
343
226
  case operator
344
- when :in then :in
345
227
  when :== then :eql
346
228
  when :=~ then :regexp
347
229
  when :> then :gt
348
230
  when :>= then :gte
349
231
  when :< then :lt
350
232
  when :<= then :lte
351
- else raise "DEBUG: unknown operator #{operator}"
352
233
  end
353
234
  end
354
235
 
355
- def value(value)
356
- eval(value.to_s, @binding)
236
+ def eval(value, binding = @binding)
237
+ super(value.to_s, binding)
238
+ end
239
+
240
+ def call_method(name, *args)
241
+ eval("method(#{name.inspect})").call(*args)
357
242
  end
243
+
244
+ def assign_value(var, value)
245
+ eval("#{var} = #{value.inspect}")
246
+ end
247
+
248
+ def add_condition(condition)
249
+ @container << condition
250
+ end
251
+
358
252
  end # class FilterProcessor
359
253
  end # module Query
360
254
  end # module Ambition
@@ -5,19 +5,18 @@ module DataMapper
5
5
 
6
6
  # TODO: spec and document this
7
7
  # @api semipublic
8
- def filter(negated = false, &block)
8
+ def filter(&block)
9
9
  # TODO: benchmark Marshal versus just building the sexp on demand
10
10
 
11
11
  # deep clone the sexp for multiple re-use
12
12
  sexp = Marshal.load(@@sexps[block.to_s] ||= Marshal.dump(block.to_sexp))
13
13
 
14
- processor = FilterProcessor.new(block.binding, model, negated)
14
+ processor = FilterProcessor.new(block.binding, model)
15
15
  processor.process(sexp)
16
16
 
17
- merge(:conditions => processor.conditions)
17
+ self.class.new(repository, model, options.merge(:conditions => conditions & processor.conditions))
18
18
  end
19
+
19
20
  end # module Query
20
21
  end # module Ambition
21
22
  end # module DataMapper
22
-
23
- require Pathname(__FILE__).dirname.expand_path / 'query' / 'filter_processor'
@@ -1,5 +1,5 @@
1
1
  module DataMapper
2
2
  module Ambition
3
- VERSION = '1.0.0'
3
+ VERSION = '1.1.0.rc1'
4
4
  end
5
5
  end
data/lib/dm-ambition.rb CHANGED
@@ -1,6 +1,13 @@
1
+ require 'dm-core'
2
+ require 'sourcify'
3
+ require 'ruby2ruby'
4
+
1
5
  require 'dm-ambition/collection'
2
6
  require 'dm-ambition/model'
7
+
3
8
  require 'dm-ambition/query'
9
+ require 'dm-ambition/query/filter_processor'
10
+
4
11
  require 'dm-ambition/version'
5
12
 
6
13
  module DataMapper