dm-ambition 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ tmp
21
+
22
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Dan Kubb
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,23 @@
1
+ .gitignore
2
+ History.txt
3
+ LICENSE
4
+ Manifest.txt
5
+ README.txt
6
+ Rakefile
7
+ TODO
8
+ dm-ambition.gemspec
9
+ lib/dm-ambition.rb
10
+ lib/dm-ambition/collection.rb
11
+ lib/dm-ambition/model.rb
12
+ lib/dm-ambition/query.rb
13
+ lib/dm-ambition/query/filter_processor.rb
14
+ lib/dm-ambition/version.rb
15
+ spec/public/collection_spec.rb
16
+ spec/public/model_spec.rb
17
+ spec/public/shared/filter_shared_spec.rb
18
+ spec/semipublic/query_spec.rb
19
+ spec/spec.opts
20
+ spec/spec_helper.rb
21
+ tasks/hoe.rb
22
+ tasks/install.rb
23
+ tasks/spec.rb
data/README.rdoc ADDED
@@ -0,0 +1,118 @@
1
+ = dm-ambition
2
+
3
+ DataMapper::Ambition is a plugin that provides an Ambition-like API for
4
+ accessing DataMapper models.
5
+
6
+ == Installation
7
+
8
+ With Rubygems:
9
+
10
+ $ sudo gem install dm-ambition
11
+ $ irb -rubygems
12
+ >> require 'dm-core'
13
+ >> require 'dm-ambition'
14
+ => true
15
+
16
+ With git and local working copy:
17
+
18
+ $ git clone git://github.com/dkubb/dm-ambition.git
19
+ $ cd dm-ambition
20
+ $ rake build && sudo rake install
21
+ $ irb -rubygems
22
+ >> require 'dm-core'
23
+ >> require 'dm-ambition'
24
+ => true
25
+
26
+ == Examples
27
+
28
+ # with == operator
29
+ User.select { |u| u.name == 'Dan Kubb' }
30
+
31
+ # with =~ operator
32
+ User.select { |u| u.name =~ /Dan Kubb/ }
33
+
34
+ # with > operator
35
+ User.select { |u| u.id > 1 }
36
+
37
+ # with >= operator
38
+ User.select { |u| u.id >= 1 }
39
+
40
+ # with < operator
41
+ User.select { |u| u.id < 1 }
42
+
43
+ # with <= operator
44
+ User.select { |u| u.id <= 1 }
45
+
46
+ # with < operator
47
+ User.select { |u| u.id < 1 }
48
+
49
+ # with receiver.attribute.nil?
50
+ User.select { |u| u.id.nil? }
51
+
52
+ # with nil bind value
53
+ User.select { |u| u.id == nil }
54
+
55
+ # with true bind value
56
+ User.select { |u| u.admin == true }
57
+
58
+ # with false bind value
59
+ User.select { |u| u.admin == false }
60
+
61
+ # with AND conditions
62
+ User.select { |u| u.id == 1 && u.name == 'Dan Kubb' }
63
+
64
+ # with negated conditions
65
+ User.select { |u| !(u.id == 1) }
66
+
67
+ # with double-negated conditions
68
+ User.select { |u| !(!(u.id == 1)) }
69
+
70
+ # with receiver matching
71
+ User.select { |u| u == user }
72
+
73
+ # using send on receiver
74
+ User.select { |u| u.send(:name) == 'Dan Kubb' }
75
+
76
+ # with Array#include?
77
+ User.select { |u| user_ids.include?(u.id) }
78
+
79
+ # with Range#include?
80
+ User.select { |u| (1..10).include?(u.id) }
81
+
82
+ # with Hash#key?
83
+ User.select { |u| { 1 => 'Dan Kubb' }.key?(u.id) }
84
+
85
+ # with Hash#value?
86
+ User.select { |u| { 'Dan Kubb' => 1 }.value?(u.id) }
87
+
88
+ # with receiver and Array#include?
89
+ User.select { |u| users.include?(u) }
90
+
91
+ # with receiver and Hash#key?
92
+ User.select { |u| { user => 'Dan Kubb' }.key?(u) }
93
+
94
+ # with receiver and Hash#value?
95
+ User.select { |u| { 'Dan Kubb' => user }.value?(u) }
96
+
97
+ == License
98
+
99
+ Copyright (c) 2009 Dan Kubb
100
+
101
+ Permission is hereby granted, free of charge, to any person obtaining
102
+ a copy of this software and associated documentation files (the
103
+ "Software"), to deal in the Software without restriction, including
104
+ without limitation the rights to use, copy, modify, merge, publish,
105
+ distribute, sublicense, and/or sell copies of the Software, and to
106
+ permit persons to whom the Software is furnished to do so, subject to
107
+ the following conditions:
108
+
109
+ The above copyright notice and this permission notice shall be
110
+ included in all copies or substantial portions of the Software.
111
+
112
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
113
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
114
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
115
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
116
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
117
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
118
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require File.expand_path('../lib/dm-ambition/version', __FILE__)
5
+
6
+ begin
7
+ gem 'jeweler', '~> 1.4'
8
+ require 'jeweler'
9
+
10
+ Jeweler::Tasks.new do |gem|
11
+ gem.name = 'dm-ambition'
12
+ gem.summary = 'DataMapper plugin providing an Ambition-like API'
13
+ gem.description = gem.summary
14
+ gem.email = 'dan.kubb@gmail.com'
15
+ gem.homepage = 'http://github.com/dkubb/%s' % gem.name
16
+ gem.authors = [ 'Dan Kubb' ]
17
+
18
+ gem.version = DataMapper::Ambition::VERSION
19
+
20
+ gem.rubyforge_project = 'dm-ambition'
21
+
22
+ gem.add_dependency 'dm-core', '~> 0.10.2'
23
+ gem.add_dependency 'ParseTree', '~> 3.0.4'
24
+ gem.add_dependency 'ruby2ruby', '~> 1.2.4'
25
+
26
+ gem.add_development_dependency 'rspec', '~> 1.2.9'
27
+ gem.add_development_dependency 'yard', '~> 0.4.0'
28
+ end
29
+
30
+ Jeweler::GemcutterTasks.new
31
+
32
+ FileList['tasks/**/*.rake'].each { |task| import task }
33
+ rescue LoadError
34
+ puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
35
+ end
data/TODO ADDED
File without changes
@@ -0,0 +1,89 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{dm-ambition}
8
+ s.version = "0.10.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Dan Kubb"]
12
+ s.date = %q{2009-12-14}
13
+ s.description = %q{DataMapper plugin providing an Ambition-like API}
14
+ s.email = %q{dan.kubb@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc",
18
+ "TODO"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "Manifest.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "TODO",
28
+ "dm-ambition.gemspec",
29
+ "lib/dm-ambition.rb",
30
+ "lib/dm-ambition/collection.rb",
31
+ "lib/dm-ambition/model.rb",
32
+ "lib/dm-ambition/query.rb",
33
+ "lib/dm-ambition/query/filter_processor.rb",
34
+ "lib/dm-ambition/version.rb",
35
+ "spec/public/collection_spec.rb",
36
+ "spec/public/model_spec.rb",
37
+ "spec/public/shared/filter_shared_spec.rb",
38
+ "spec/rcov.opts",
39
+ "spec/semipublic/query_spec.rb",
40
+ "spec/spec.opts",
41
+ "spec/spec_helper.rb",
42
+ "tasks/ci.rake",
43
+ "tasks/clean.rake",
44
+ "tasks/heckle.rake",
45
+ "tasks/metrics.rake",
46
+ "tasks/spec.rake",
47
+ "tasks/yard.rake",
48
+ "tasks/yardstick.rake"
49
+ ]
50
+ s.homepage = %q{http://github.com/dkubb/dm-ambition}
51
+ s.rdoc_options = ["--charset=UTF-8"]
52
+ s.require_paths = ["lib"]
53
+ s.rubyforge_project = %q{dm-ambition}
54
+ s.rubygems_version = %q{1.3.5}
55
+ s.summary = %q{DataMapper plugin providing an Ambition-like API}
56
+ s.test_files = [
57
+ "spec/public/collection_spec.rb",
58
+ "spec/public/model_spec.rb",
59
+ "spec/public/shared/filter_shared_spec.rb",
60
+ "spec/semipublic/query_spec.rb",
61
+ "spec/spec_helper.rb"
62
+ ]
63
+
64
+ if s.respond_to? :specification_version then
65
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
66
+ s.specification_version = 3
67
+
68
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
69
+ s.add_runtime_dependency(%q<dm-core>, ["~> 0.10.2"])
70
+ s.add_runtime_dependency(%q<ParseTree>, ["~> 3.0.4"])
71
+ s.add_runtime_dependency(%q<ruby2ruby>, ["~> 1.2.4"])
72
+ s.add_development_dependency(%q<rspec>, ["~> 1.2.9"])
73
+ s.add_development_dependency(%q<yard>, ["~> 0.4.0"])
74
+ else
75
+ s.add_dependency(%q<dm-core>, ["~> 0.10.2"])
76
+ s.add_dependency(%q<ParseTree>, ["~> 3.0.4"])
77
+ s.add_dependency(%q<ruby2ruby>, ["~> 1.2.4"])
78
+ s.add_dependency(%q<rspec>, ["~> 1.2.9"])
79
+ s.add_dependency(%q<yard>, ["~> 0.4.0"])
80
+ end
81
+ else
82
+ s.add_dependency(%q<dm-core>, ["~> 0.10.2"])
83
+ s.add_dependency(%q<ParseTree>, ["~> 3.0.4"])
84
+ s.add_dependency(%q<ruby2ruby>, ["~> 1.2.4"])
85
+ s.add_dependency(%q<rspec>, ["~> 1.2.9"])
86
+ s.add_dependency(%q<yard>, ["~> 0.4.0"])
87
+ end
88
+ end
89
+
@@ -0,0 +1,73 @@
1
+ # TODO: update Collection#select in dm-core to return a Collection
2
+ # TODO: update Collection#reject in dm-core to return a Collection
3
+
4
+ module DataMapper
5
+ module Ambition
6
+ module Collection
7
+ def self.included(base)
8
+ base.send(:alias_method, :find_all, :select)
9
+ base.send(:alias_method, :find, :detect)
10
+ end
11
+
12
+ # TODO: add empty?
13
+ # TODO: add any? (handle with and without block)
14
+ # TODO: add all? (handle with and without block)
15
+ # TODO: add none? (handle with and without block) (add to LazyArray for < 1.9)
16
+ # TODO: add one? (handle with and without block) (add to LazyArray for < 1.9)
17
+
18
+ # TODO: document this
19
+ # @api public
20
+ def select(&block)
21
+ query = self.query.filter(&block)
22
+
23
+ if loaded?
24
+ new_collection(query, super)
25
+ else
26
+ collection = all(query)
27
+
28
+ if head.any?
29
+ collection.unshift(*head.select(&block))
30
+ end
31
+
32
+ if tail.any?
33
+ collection.concat(tail.select(&block))
34
+ end
35
+
36
+ collection
37
+ end
38
+ end
39
+
40
+ # TODO: document this
41
+ # @api public
42
+ def detect(&block)
43
+ if loaded?
44
+ super
45
+ else
46
+ head.detect(&block) || first(query.filter(&block))
47
+ end
48
+ end
49
+
50
+ # TODO: document this
51
+ # @api public
52
+ def reject(&block)
53
+ query = self.query.filter(true, &block)
54
+
55
+ if loaded?
56
+ new_collection(query, super)
57
+ else
58
+ collection = all(query)
59
+
60
+ if head.any?
61
+ collection.unshift(*head.reject(&block))
62
+ end
63
+
64
+ if tail.any?
65
+ collection.concat(tail.reject(&block))
66
+ end
67
+
68
+ collection
69
+ end
70
+ end
71
+ end # module Collection
72
+ end # module Ambition
73
+ end # module DataMapper
@@ -0,0 +1,28 @@
1
+ module DataMapper
2
+ module Ambition
3
+ module Model
4
+ def self.included(base)
5
+ base.send(:alias_method, :find_all, :select)
6
+ base.send(:alias_method, :find, :detect)
7
+ end
8
+
9
+ # TODO: document this
10
+ # @api public
11
+ def select(&block)
12
+ all.select(&block)
13
+ end
14
+
15
+ # TODO: document this
16
+ # @api public
17
+ def detect(&block)
18
+ all.detect(&block)
19
+ end
20
+
21
+ # TODO: document this
22
+ # @api public
23
+ def reject(&block)
24
+ all.reject(&block)
25
+ end
26
+ end # module Model
27
+ end # module Ambition
28
+ end # module DataMapper
@@ -0,0 +1,361 @@
1
+ require 'parse_tree'
2
+ require 'parse_tree_extensions'
3
+ require 'ruby2ruby'
4
+
5
+ module DataMapper
6
+ module Ambition
7
+ module Query
8
+ class FilterProcessor < SexpProcessor
9
+ attr_reader :conditions
10
+
11
+ def initialize(binding, model, negated = false)
12
+ super()
13
+
14
+ self.expected = Object # allow anything for now
15
+ self.auto_shift_type = true
16
+ self.default_method = :process_error
17
+
18
+ @binding = binding
19
+ @model = model
20
+ @receiver = nil
21
+
22
+ @container = DataMapper::Query::Conditions::Operation.new(:and)
23
+
24
+ if negated
25
+ @conditions = DataMapper::Query::Conditions::Operation.new(:not, @container)
26
+ else
27
+ @conditions = @container
28
+ end
29
+ end
30
+
31
+ def process_error(exp)
32
+ raise "DEBUG: calling process_#{exp.shift} with #{exp.inspect} (in process_error)"
33
+ end
34
+
35
+ 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
+ exp.shift
58
+ end
59
+
60
+ 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
69
+
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
75
+
76
+ if lhs.nil?
77
+ nil
78
+ else
79
+ evaluate_operator(operator, lhs, rhs)
80
+ end
81
+ else
82
+ raise "DEBUG: unhandled call: #{exp.inspect}"
83
+ end
84
+ end
85
+
86
+ 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
102
+ end
103
+
104
+ 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
120
+ end
121
+
122
+ 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
133
+ end
134
+
135
+ def process_lvar(exp)
136
+ var = exp.shift
137
+ var == @receiver ? @model : value(var)
138
+ end
139
+
140
+ 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)
152
+ end
153
+
154
+ def process_const(exp)
155
+ Object.const_get(exp.shift)
156
+ end
157
+
158
+ def process_match3(exp)
159
+ rhs = process(exp.shift)
160
+ lhs = process(exp.shift)
161
+
162
+ evaluate_operator(:=~, lhs, rhs)
163
+ end
164
+
165
+ def process_array(exp)
166
+ array = []
167
+ while sexp = exp.shift
168
+ array << process(sexp)
169
+ end
170
+ array
171
+ end
172
+
173
+ def process_hash(exp)
174
+ hash = {}
175
+ until exp.empty?
176
+ key = process(exp.shift)
177
+ value = process(exp.shift)
178
+
179
+ hash[key] = value
180
+ end
181
+ hash
182
+ end
183
+
184
+ def process_str(exp)
185
+ exp.shift
186
+ end
187
+
188
+ def process_lit(exp)
189
+ literal = exp.shift
190
+ exp.shift # FIXME: workaround for bug in ParseTree or SexpProcessor
191
+ literal
192
+ end
193
+
194
+ def process_true(exp)
195
+ true
196
+ end
197
+
198
+ def process_false(exp)
199
+ false
200
+ end
201
+
202
+ def process_nil(exp)
203
+ nil
204
+ end
205
+
206
+ def process_ivar(exp)
207
+ value(exp.shift)
208
+ end
209
+
210
+ def process_gvar(exp)
211
+ value(exp.shift)
212
+ end
213
+
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
218
+
219
+ if lhs == @model
220
+ if rhs.nil?
221
+ @model.properties[operator]
222
+ elsif rhs.kind_of?(DataMapper::Resource) && operator == :==
223
+ resource = rhs
224
+
225
+ if resource.repository == DataMapper.repository &&
226
+ resource.saved? &&
227
+ !resource.dirty? &&
228
+ (key = resource.key).all?
229
+
230
+ @model.key.zip(key) do |property, bind_value|
231
+ @container << DataMapper::Query::Conditions::Comparison.new(:eql, property, bind_value)
232
+ end
233
+
234
+ @container
235
+ end
236
+ 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)
332
+
333
+ else
334
+ raise "DEBUG: not handled: #{lhs.inspect} #{operator} #{rhs.inspect}"
335
+
336
+ end
337
+ end
338
+
339
+ # TODO: update dm-core internals to use the Ruby operators
340
+ # insted of the DM specific ones
341
+ def remap_operator(operator)
342
+ # remap Ruby to DM operators
343
+ case operator
344
+ when :in then :in
345
+ when :== then :eql
346
+ when :=~ then :regexp
347
+ when :> then :gt
348
+ when :>= then :gte
349
+ when :< then :lt
350
+ when :<= then :lte
351
+ else raise "DEBUG: unknown operator #{operator}"
352
+ end
353
+ end
354
+
355
+ def value(value)
356
+ eval(value.to_s, @binding)
357
+ end
358
+ end # class FilterProcessor
359
+ end # module Query
360
+ end # module Ambition
361
+ end # module DataMapper