dm-ambition 0.10.2

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/.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