dm-mongo-adapter 0.2.0.pre1

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 (65) hide show
  1. data/.gitignore +9 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +130 -0
  4. data/Rakefile +36 -0
  5. data/TODO +33 -0
  6. data/VERSION.yml +5 -0
  7. data/bin/console +31 -0
  8. data/dm-mongo-adapter.gemspec +154 -0
  9. data/lib/mongo_adapter.rb +71 -0
  10. data/lib/mongo_adapter/adapter.rb +255 -0
  11. data/lib/mongo_adapter/aggregates.rb +21 -0
  12. data/lib/mongo_adapter/conditions.rb +100 -0
  13. data/lib/mongo_adapter/embedded_model.rb +187 -0
  14. data/lib/mongo_adapter/embedded_resource.rb +134 -0
  15. data/lib/mongo_adapter/embedments/one_to_many.rb +139 -0
  16. data/lib/mongo_adapter/embedments/one_to_one.rb +53 -0
  17. data/lib/mongo_adapter/embedments/relationship.rb +258 -0
  18. data/lib/mongo_adapter/migrations.rb +45 -0
  19. data/lib/mongo_adapter/model.rb +89 -0
  20. data/lib/mongo_adapter/model/embedment.rb +215 -0
  21. data/lib/mongo_adapter/modifier.rb +85 -0
  22. data/lib/mongo_adapter/query.rb +252 -0
  23. data/lib/mongo_adapter/query/java_script.rb +145 -0
  24. data/lib/mongo_adapter/resource.rb +147 -0
  25. data/lib/mongo_adapter/types/date.rb +28 -0
  26. data/lib/mongo_adapter/types/date_time.rb +28 -0
  27. data/lib/mongo_adapter/types/db_ref.rb +65 -0
  28. data/lib/mongo_adapter/types/discriminator.rb +32 -0
  29. data/lib/mongo_adapter/types/object_id.rb +72 -0
  30. data/lib/mongo_adapter/types/objects.rb +31 -0
  31. data/script/performance.rb +260 -0
  32. data/spec/legacy/README +6 -0
  33. data/spec/legacy/adapter_shared_spec.rb +299 -0
  34. data/spec/legacy/adapter_spec.rb +174 -0
  35. data/spec/legacy/associations_spec.rb +140 -0
  36. data/spec/legacy/embedded_resource_spec.rb +157 -0
  37. data/spec/legacy/embedments_spec.rb +177 -0
  38. data/spec/legacy/modifier_spec.rb +81 -0
  39. data/spec/legacy/property_spec.rb +51 -0
  40. data/spec/legacy/spec_helper.rb +3 -0
  41. data/spec/legacy/sti_spec.rb +53 -0
  42. data/spec/lib/cleanup_models.rb +32 -0
  43. data/spec/lib/raw_connections.rb +11 -0
  44. data/spec/public/embedded_collection_spec.rb +61 -0
  45. data/spec/public/embedded_resource_spec.rb +220 -0
  46. data/spec/public/model/embedment_spec.rb +186 -0
  47. data/spec/public/model_spec.rb +37 -0
  48. data/spec/public/resource_spec.rb +564 -0
  49. data/spec/public/shared/model_embedments_spec.rb +338 -0
  50. data/spec/public/shared/object_id_shared_spec.rb +56 -0
  51. data/spec/public/types/df_ref_spec.rb +6 -0
  52. data/spec/public/types/discriminator_spec.rb +118 -0
  53. data/spec/public/types/embedded_array_spec.rb +55 -0
  54. data/spec/public/types/embedded_hash_spec.rb +83 -0
  55. data/spec/public/types/object_id_spec.rb +6 -0
  56. data/spec/rcov.opts +6 -0
  57. data/spec/semipublic/embedded_model_spec.rb +43 -0
  58. data/spec/semipublic/model/embedment_spec.rb +42 -0
  59. data/spec/semipublic/resource_spec.rb +70 -0
  60. data/spec/spec.opts +4 -0
  61. data/spec/spec_helper.rb +45 -0
  62. data/tasks/spec.rake +37 -0
  63. data/tasks/yard.rake +9 -0
  64. data/tasks/yardstick.rake +21 -0
  65. metadata +215 -0
@@ -0,0 +1,72 @@
1
+ module DataMapper
2
+ module Mongo
3
+ module Types
4
+ # Each object (document) stored in Mongo DB has an _id field as its
5
+ # first attribute. This is an object id. It must be unique for each
6
+ # member of a collection (this is enforced if the collection has an _id
7
+ # index, which is the case by default).
8
+ #
9
+ # The database will assign an _id if an object being inserted into a
10
+ # collection does not have one.
11
+ #
12
+ # The _id may be of any type as long as it is a unique value.
13
+ #
14
+ # @see http://www.mongodb.org/display/DOCS/Object+IDs
15
+ #
16
+ # @api public
17
+ class ObjectID < DataMapper::Type
18
+ primitive ::Object
19
+ key true
20
+ field "_id"
21
+ required false
22
+
23
+ # Returns the ObjectID as a string; suitable for use in a Resource
24
+ #
25
+ # @return [String]
26
+ #
27
+ # @api public
28
+ def self.load(value, property)
29
+ typecast(value, property)
30
+ end
31
+
32
+ # Returns the ObjectID as a Mongo::ObjectID; suitable to be passed to
33
+ # the Mongo library
34
+ #
35
+ # @return [Mongo::ObjectID] The dumped ID.
36
+ #
37
+ # @api public
38
+ def self.dump(value, property)
39
+ case value
40
+ when NilClass
41
+ nil
42
+ when String
43
+ ::Mongo::ObjectID.from_string(value)
44
+ when ::Mongo::ObjectID
45
+ value
46
+ else
47
+ raise ArgumentError.new('+value+ must be nil, String or ObjectID')
48
+ end
49
+ end
50
+
51
+ # Returns the ObjectID as a string
52
+ #
53
+ # @return [String]
54
+ #
55
+ # @api semipublic
56
+ def self.typecast(value, property)
57
+ case value
58
+ when NilClass
59
+ nil
60
+ when String
61
+ value
62
+ when ::Mongo::ObjectID
63
+ value.to_s
64
+ else
65
+ raise ArgumentError.new('+value+ must be nil, String or ObjectID')
66
+ end
67
+ end
68
+
69
+ end # ObjectID
70
+ end # Types
71
+ end # Mongo
72
+ end # DataMapper
@@ -0,0 +1,31 @@
1
+ module DataMapper
2
+ module Mongo
3
+ module Types
4
+ class EmbeddedArray < DataMapper::Type
5
+ primitive Object
6
+ end
7
+
8
+ class EmbeddedHash < DataMapper::Type
9
+ primitive Object
10
+
11
+ # @api public
12
+ def self.load(value, property)
13
+ typecast(value, property)
14
+ end
15
+
16
+ # @api semipublic
17
+ def self.typecast(value, property)
18
+ case value
19
+ when NilClass
20
+ nil
21
+ when Hash
22
+ value.to_mash.symbolize_keys
23
+ when Array
24
+ value.empty? ? Mash.new : [value].to_mash.symbolize_keys
25
+ end
26
+ end
27
+ end #EmbeddedHash
28
+
29
+ end # Types
30
+ end # Mongo
31
+ end # DataMapper
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+ require 'rubygems'
5
+
6
+ gem 'addressable', '~>2.0'
7
+ gem 'faker', '~>0.3.1'
8
+ gem 'rbench', '~>0.2.3'
9
+
10
+ require 'addressable/uri'
11
+ require 'faker'
12
+ require 'rbench'
13
+ require 'dm-core'
14
+
15
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'mongo_adapter'))
16
+
17
+ TIMES = ENV.key?('x') ? ENV['x'].to_i : 10_000
18
+
19
+ configuration = { :hostname => 'localhost', :database => 'dm_core_test', :username => 'root', :password => '' }
20
+
21
+ DataMapper.setup(:default, configuration.merge(:adapter => "mongo").except(:username, :password))
22
+ DataMapper.setup(:mysql, configuration.merge(:adapter => "mysql"))
23
+
24
+ class User
25
+ include DataMapper::Resource
26
+
27
+ def self.default_repository_name; :mysql; end
28
+
29
+ property :id, Serial
30
+ property :name, String
31
+ property :email, String
32
+ property :about, Text, :lazy => false
33
+ property :created_on, Time
34
+ end
35
+
36
+ class Exhibit
37
+ include DataMapper::Resource
38
+
39
+ def self.default_repository_name; :mysql; end
40
+
41
+ property :id, Serial
42
+ property :name, String
43
+ property :user_id, Integer
44
+ property :notes, Text, :lazy => false
45
+ property :created_on, Time
46
+ end
47
+
48
+ User.has User.n, :exhibits
49
+ Exhibit.belongs_to :user
50
+
51
+ module Mongo
52
+ class User
53
+ include DataMapper::Mongo::Resource
54
+
55
+ def self.default_storage_name; 'user'; end
56
+
57
+ property :id, DataMapper::Mongo::Types::ObjectID
58
+ property :name, String
59
+ property :email, String
60
+ property :about, Text, :lazy => false
61
+ property :created_on, Time
62
+ end
63
+
64
+ class Exhibit
65
+ include DataMapper::Mongo::Resource
66
+
67
+ def self.default_storage_name; 'exhibit'; end
68
+
69
+ property :id, DataMapper::Mongo::Types::ObjectID
70
+ property :name, String
71
+ property :user_id, Integer
72
+ property :notes, Text, :lazy => false
73
+ property :created_on, Time
74
+
75
+ belongs_to :user
76
+ end
77
+
78
+ User.has User.n, :exhibits
79
+ Exhibit.belongs_to :user
80
+ end
81
+
82
+ def touch_attributes(*exhibits)
83
+ exhibits.flatten.each do |exhibit|
84
+ exhibit.id
85
+ exhibit.name
86
+ exhibit.created_on
87
+ end
88
+ end
89
+
90
+ def touch_relationships(*exhibits)
91
+ exhibits.flatten.each do |exhibit|
92
+ exhibit.id
93
+ exhibit.name
94
+ exhibit.created_on
95
+ exhibit.user
96
+ end
97
+ end
98
+
99
+ root = File.dirname(__FILE__)
100
+
101
+ dump_dir = root / 'dumps'
102
+
103
+ dump_dir_mysql = dump_dir / 'mysql'
104
+ dump_dir_mongo = dump_dir / 'mongo'
105
+
106
+ FileUtils.mkdir_p(dump_dir_mysql) unless File.exist?(dump_dir_mysql)
107
+ FileUtils.mkdir_p(dump_dir_mongo) unless File.exist?(dump_dir_mongo)
108
+
109
+ dump_sql = dump_dir / 'mysql' / 'dump.sql'
110
+ dump_mongo_users = dump_dir_mongo / configuration[:database] / 'users.bson'
111
+ dump_mongo_exhibits = dump_dir_mongo / configuration[:database] / 'exhibits.bson'
112
+
113
+ mongo_user_ids = []
114
+ mongo_exhibit_ids = []
115
+
116
+ mysql_user_ids = []
117
+ mysql_exhibit_ids = []
118
+
119
+ puts 'Clearing databases...'
120
+
121
+ User.auto_migrate!
122
+ Exhibit.auto_migrate!
123
+
124
+ Mongo::User.auto_migrate!
125
+ Mongo::Exhibit.auto_migrate!
126
+
127
+ # pre-compute the insert statements and fake data compilation,
128
+ # so the benchmmysqlks below show the actual runtime for the execute
129
+ # method, minus the setup steps
130
+
131
+ # Using the same paragraphs for all exhibits because it is very slow
132
+ # to generate unique paragraphs for all exhibits.
133
+ notes = Faker::Lorem.paragraphs.join($/)
134
+ today = Time.now
135
+
136
+ puts 'Generating data for benchmarking...'
137
+
138
+ c = configuration
139
+
140
+ if File.exist?(dump_sql) && File.exist?(dump_mongo_users) && File.exist?(dump_mongo_exhibits)
141
+ puts "Found db dumps, importing..."
142
+
143
+ `mysql -u #{c[:username]} #{"-p#{ c[:password]}" unless c[:password].blank?} --database #{c[:database]} < #{dump_sql}`
144
+ `mongorestore -d #{c[:database]} -c users --dir #{dump_mongo_users}`
145
+ `mongorestore -d #{c[:database]} -c exhibits --dir #{dump_mongo_exhibits}`
146
+
147
+ mysql_user_ids = User.all.map(&:id)
148
+ mysql_exhibit_ids = Exhibit.all.map(&:id)
149
+
150
+ mongo_user_ids = Mongo::User.all.map(&:id)
151
+ mongo_exhibit_ids = Mongo::Exhibit.all.map(&:id)
152
+ else
153
+ FileUtils.rm(dump_sql) if File.exist?(dump_sql)
154
+ FileUtils.rm_r(dump_mongo_users) if File.exist?(dump_mongo_users)
155
+ FileUtils.rm_r(dump_mongo_exhibits) if File.exist?(dump_mongo_exhibits)
156
+
157
+ puts 'Inserting 10,000 users and exhibits...'
158
+ 10_000.times do
159
+ user_properties = {
160
+ :created_on => today,
161
+ :name => Faker::Name.name,
162
+ :email => Faker::Internet.email }
163
+
164
+ exhibit_properties = {
165
+ :created_on => today,
166
+ :name => Faker::Company.name,
167
+ :notes => notes
168
+ }
169
+
170
+ mongo_user = Mongo::User.create(user_properties)
171
+ mongo_exhibit = Mongo::Exhibit.create(exhibit_properties.merge(:user_id => mongo_user.id))
172
+
173
+ mongo_user_ids << mongo_user.id.to_s
174
+ mongo_exhibit_ids << mongo_exhibit.id.to_s
175
+
176
+ mysql_user = User.new(user_properties)
177
+ mysql_user.save!
178
+ mysql_exhibit = Exhibit.new(exhibit_properties.merge(:user_id => mysql_user.id))
179
+ mysql_exhibit.save!
180
+
181
+ mysql_user_ids << mysql_user.id
182
+ mysql_exhibit_ids << mysql_exhibit.id
183
+ end
184
+
185
+ # mysql dump
186
+ `mysqldump -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{dump_sql}`
187
+ # mongo dump
188
+ `mongodump -d #{c[:database]} -c users -o #{dump_dir_mongo}`
189
+ `mongodump -d #{c[:database]} -c exhibits -o #{dump_dir_mongo}`
190
+ end
191
+
192
+ RBench.run(TIMES) do
193
+ column :times
194
+ column :mysql, :title => 'MySQL Adapter'
195
+ column :mongo, :title => "Mongo Adapter"
196
+ column :diff, :compare => [:mysql, :mongo]
197
+
198
+ report 'Model#id', (TIMES * 100).ceil do
199
+ mysql_obj = Exhibit.get(mysql_exhibit_ids.first)
200
+ mongo_obj = Mongo::Exhibit.get(mongo_exhibit_ids.first)
201
+
202
+ mysql { mysql_obj.id }
203
+ mongo { mongo_obj.id }
204
+ end
205
+
206
+ report 'Model.get specific (not cached)' do
207
+ mysql { touch_attributes(Exhibit.get(mysql_exhibit_ids.first)) }
208
+ mongo { touch_attributes(Mongo::Exhibit.get(mongo_exhibit_ids.first)) }
209
+ end
210
+
211
+ report 'Model.get specific (cached)' do
212
+ Exhibit.repository(:mysql) { mysql { touch_attributes(Exhibit.get(mysql_exhibit_ids.first)) } }
213
+ Mongo::Exhibit.repository(:default) { mongo { touch_attributes(Mongo::Exhibit.get(mongo_exhibit_ids.first)) } }
214
+ end
215
+
216
+ report 'Model.first' do
217
+ mysql { touch_attributes(Exhibit.first) }
218
+ mongo { touch_attributes(Mongo::Exhibit.first) }
219
+ end
220
+
221
+ report 'Model.all limit(100)', (TIMES / 100).ceil do
222
+ mysql { touch_attributes(Exhibit.all(:limit => 100)) }
223
+ mongo { touch_attributes(Mongo::Exhibit.all(:limit => 100)) }
224
+ end
225
+
226
+ report 'Model.all limit(10,000)', (TIMES / 1000).ceil do
227
+ mysql { touch_attributes(Exhibit.all(:limit => 10_000)) }
228
+ mongo { touch_attributes(Mongo::Exhibit.all(:limit => 10_000)) }
229
+ end
230
+
231
+ exhibit = {
232
+ :name => Faker::Company.name,
233
+ :notes => Faker::Lorem.paragraphs.join($/),
234
+ :created_on => today
235
+ }
236
+
237
+ report 'Model.create' do
238
+ mysql { Exhibit.create(exhibit) }
239
+ mongo { Mongo::Exhibit.create(exhibit) }
240
+ end
241
+
242
+ report 'Resource#attributes=' do
243
+ attrs_first = { :name => 'sam', :notes => 'foo bar' }
244
+ attrs_second = { :name => 'tom', :notes => 'foo bar' }
245
+ mysql { exhibit = Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
246
+ mongo { exhibit = Mongo::Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
247
+ end
248
+
249
+ report 'Resource#update' do
250
+ mysql { Exhibit.get(mysql_user_ids.first).update(:name => 'bob') }
251
+ mongo { Mongo::Exhibit.get(mongo_exhibit_ids.first).update(:name => 'bob') }
252
+ end
253
+
254
+ report 'Resource#destroy' do
255
+ mysql { Exhibit.first.destroy }
256
+ mongo { Mongo::Exhibit.first.destroy }
257
+ end
258
+
259
+ summary 'Total'
260
+ end
@@ -0,0 +1,6 @@
1
+ This directory contains the old specs, written for 0.1.0. They are retained
2
+ here while the specs are separated into spec/{public,semipublic}.
3
+
4
+ Examples marked as @done indicate those for which there are equivalents in
5
+ spec/{public,semipublic}. Once all specs are moved (or rewritten, as
6
+ appropriate), the legacy specs will be removed.
@@ -0,0 +1,299 @@
1
+ share_examples_for 'An Adapter' do
2
+
3
+ def self.adapter_supports?(*methods)
4
+ methods.all? do |method|
5
+ # TODO: figure out a way to see if the instance method is only inherited
6
+ # from the Abstract Adapter, and not defined in it's class. If that is
7
+ # the case return false
8
+
9
+ # CRUD methods can be inherited from parent class
10
+ described_type.instance_methods.any? { |instance_method| method.to_s == instance_method.to_s }
11
+ end
12
+ end
13
+
14
+ before :all do
15
+ # create all tables and constraints before each spec
16
+ if @repository.respond_to?(:auto_migrate!)
17
+ Heffalump.auto_migrate!
18
+ end
19
+ end
20
+
21
+ if adapter_supports?(:create)
22
+ describe '#create' do
23
+ it 'should not raise any errors' do
24
+ lambda {
25
+ Heffalump.create(:color => 'peach')
26
+ }.should_not raise_error
27
+ end
28
+
29
+ it 'should set the identity field for the resource' do
30
+ heffalump = Heffalump.new(:color => 'peach')
31
+ heffalump.id.should be_nil
32
+ heffalump.save
33
+ heffalump.id.should_not be_nil
34
+ end
35
+ end
36
+ else
37
+ it 'needs to support #create'
38
+ end
39
+
40
+ if adapter_supports?(:read)
41
+ describe '#read' do
42
+ before :all do
43
+ @heffalump = Heffalump.create(:color => 'brownish hue')
44
+ #just going to borrow this, so I can check the return values
45
+ @query = Heffalump.all.query
46
+ end
47
+
48
+ it 'should not raise any errors' do
49
+ lambda {
50
+ Heffalump.all()
51
+ }.should_not raise_error
52
+ end
53
+
54
+ it 'should return stuff' do
55
+ Heffalump.all.should be_include(@heffalump)
56
+ end
57
+ end
58
+ else
59
+ it 'needs to support #read'
60
+ end
61
+
62
+ if adapter_supports?(:update)
63
+ describe '#update' do
64
+ before do
65
+ @heffalump = Heffalump.create(:color => 'indigo')
66
+ end
67
+
68
+ it 'should not raise any errors' do
69
+ lambda {
70
+ @heffalump.color = 'violet'
71
+ @heffalump.save
72
+ }.should_not raise_error
73
+ end
74
+
75
+ it 'should not alter the identity field' do
76
+ id = @heffalump.id
77
+ @heffalump.color = 'violet'
78
+ @heffalump.save
79
+ @heffalump.id.should == id
80
+ end
81
+
82
+ it 'should update altered fields' do
83
+ @heffalump.color = 'violet'
84
+ @heffalump.save
85
+ Heffalump.get(*@heffalump.key).color.should == 'violet'
86
+ end
87
+
88
+ it 'should not alter other fields' do
89
+ color = @heffalump.color
90
+ @heffalump.num_spots = 3
91
+ @heffalump.save
92
+ Heffalump.get(*@heffalump.key).color.should == color
93
+ end
94
+ end
95
+ else
96
+ it 'needs to support #update'
97
+ end
98
+
99
+ if adapter_supports?(:delete)
100
+ describe '#delete' do
101
+ before do
102
+ @heffalump = Heffalump.create(:color => 'forest green')
103
+ end
104
+
105
+ it 'should not raise any errors' do
106
+ lambda {
107
+ @heffalump.destroy
108
+ }.should_not raise_error
109
+ end
110
+
111
+ it 'should delete the requested resource' do
112
+ id = @heffalump.id
113
+ @heffalump.destroy
114
+ Heffalump.get(id).should be_nil
115
+ end
116
+ end
117
+ else
118
+ it 'needs to support #delete'
119
+ end
120
+
121
+ if adapter_supports?(:read, :create)
122
+ describe 'query matching' do
123
+ before :all do
124
+ @red = Heffalump.create(:color => 'red')
125
+ @two = Heffalump.create(:num_spots => 2)
126
+ @five = Heffalump.create(:num_spots => 5)
127
+ end
128
+
129
+ describe 'conditions' do
130
+ describe 'eql' do
131
+ it 'should be able to search for objects included in an inclusive range of values' do
132
+ Heffalump.all(:num_spots => 1..5).should be_include(@five)
133
+ end
134
+
135
+ it 'should be able to search for objects included in an exclusive range of values' do
136
+ Heffalump.all(:num_spots => 1...6).should be_include(@five)
137
+ end
138
+
139
+ it 'should not be able to search for values not included in an inclusive range of values' do
140
+ Heffalump.all(:num_spots => 1..4).should_not be_include(@five)
141
+ end
142
+
143
+ it 'should not be able to search for values not included in an exclusive range of values' do
144
+ Heffalump.all(:num_spots => 1...5).should_not be_include(@five)
145
+ end
146
+ end
147
+
148
+ describe 'not' do
149
+ it 'should be able to search for objects with not equal value' do
150
+ Heffalump.all(:color.not => 'red').should_not be_include(@red)
151
+ end
152
+
153
+ it 'should include objects that are not like the value' do
154
+ Heffalump.all(:color.not => 'black').should be_include(@red)
155
+ end
156
+
157
+ it 'should be able to search for objects with not nil value' do
158
+ Heffalump.all(:color.not => nil).should be_include(@red)
159
+ end
160
+
161
+ it 'should not include objects with a nil value' do
162
+ Heffalump.all(:color.not => nil).should_not be_include(@two)
163
+ end
164
+
165
+ it 'should be able to search for object with a nil value using required properties' do
166
+ Heffalump.all(:id.not => nil).should == [ @red, @two, @five ]
167
+ end
168
+
169
+ it 'should be able to search for objects not in an empty list (match all)' do
170
+ Heffalump.all(:color.not => []).should == [ @red, @two, @five ]
171
+ end
172
+
173
+ it 'should be able to search for objects in an empty list and another OR condition (match none on the empty list)' do
174
+ Heffalump.all(:conditions => DataMapper::Query::Conditions::Operation.new(
175
+ :or,
176
+ DataMapper::Query::Conditions::Comparison.new(:in, Heffalump.properties[:color], []),
177
+ DataMapper::Query::Conditions::Comparison.new(:in, Heffalump.properties[:num_spots], [5]))).should == [ @five ]
178
+ end
179
+
180
+ it 'should be able to search for objects not included in an array of values' do
181
+ Heffalump.all(:num_spots.not => [ 1, 3, 5, 7 ]).should be_include(@two)
182
+ end
183
+
184
+ it 'should be able to search for objects not included in an array of values' do
185
+ Heffalump.all(:num_spots.not => [ 1, 3, 5, 7 ]).should_not be_include(@five)
186
+ end
187
+
188
+ it 'should be able to search for objects not included in an inclusive range of values' do
189
+ Heffalump.all(:num_spots.not => 1..4).should be_include(@five)
190
+ end
191
+
192
+ it 'should be able to search for objects not included in an exclusive range of values' do
193
+ Heffalump.all(:num_spots.not => 1...5).should be_include(@five)
194
+ end
195
+
196
+ it 'should not be able to search for values not included in an inclusive range of values' do
197
+ Heffalump.all(:num_spots.not => 1..5).should_not be_include(@five)
198
+ end
199
+
200
+ it 'should not be able to search for values not included in an exclusive range of values' do
201
+ Heffalump.all(:num_spots.not => 1...6).should_not be_include(@five)
202
+ end
203
+ end
204
+
205
+ describe 'like' do
206
+ it 'should be able to search for objects that match value' do
207
+ Heffalump.all(:color.like => '%ed').should be_include(@red)
208
+ end
209
+
210
+ it 'should not search for objects that do not match the value' do
211
+ Heffalump.all(:color.like => '%blak%').should_not be_include(@red)
212
+ end
213
+ end
214
+
215
+ describe 'regexp' do
216
+ before do
217
+ if (defined?(DataMapper::Adapters::Sqlite3Adapter) && @adapter.kind_of?(DataMapper::Adapters::Sqlite3Adapter) ||
218
+ defined?(DataMapper::Adapters::SqlserverAdapter) && @adapter.kind_of?(DataMapper::Adapters::SqlserverAdapter))
219
+ pending 'delegate regexp matches to same system that the InMemory and YAML adapters use'
220
+ end
221
+ end
222
+
223
+ it 'should be able to search for objects that match value' do
224
+ Heffalump.all(:color => /ed/).should be_include(@red)
225
+ end
226
+
227
+ it 'should not be able to search for objects that do not match the value' do
228
+ Heffalump.all(:color => /blak/).should_not be_include(@red)
229
+ end
230
+
231
+ it 'should be able to do a negated search for objects that match value' do
232
+ Heffalump.all(:color.not => /blak/).should be_include(@red)
233
+ end
234
+
235
+ it 'should not be able to do a negated search for objects that do not match value' do
236
+ Heffalump.all(:color.not => /ed/).should_not be_include(@red)
237
+ end
238
+
239
+ end
240
+
241
+ describe 'gt' do
242
+ it 'should be able to search for objects with value greater than' do
243
+ Heffalump.all(:num_spots.gt => 1).should be_include(@two)
244
+ end
245
+
246
+ it 'should not find objects with a value less than' do
247
+ Heffalump.all(:num_spots.gt => 3).should_not be_include(@two)
248
+ end
249
+ end
250
+
251
+ describe 'gte' do
252
+ it 'should be able to search for objects with value greater than' do
253
+ Heffalump.all(:num_spots.gte => 1).should be_include(@two)
254
+ end
255
+
256
+ it 'should be able to search for objects with values equal to' do
257
+ Heffalump.all(:num_spots.gte => 2).should be_include(@two)
258
+ end
259
+
260
+ it 'should not find objects with a value less than' do
261
+ Heffalump.all(:num_spots.gte => 3).should_not be_include(@two)
262
+ end
263
+ end
264
+
265
+ describe 'lt' do
266
+ it 'should be able to search for objects with value less than' do
267
+ Heffalump.all(:num_spots.lt => 3).should be_include(@two)
268
+ end
269
+
270
+ it 'should not find objects with a value less than' do
271
+ Heffalump.all(:num_spots.gt => 2).should_not be_include(@two)
272
+ end
273
+ end
274
+
275
+ describe 'lte' do
276
+ it 'should be able to search for objects with value less than' do
277
+ Heffalump.all(:num_spots.lte => 3).should be_include(@two)
278
+ end
279
+
280
+ it 'should be able to search for objects with values equal to' do
281
+ Heffalump.all(:num_spots.lte => 2).should be_include(@two)
282
+ end
283
+
284
+ it 'should not find objects with a value less than' do
285
+ Heffalump.all(:num_spots.lte => 1).should_not be_include(@two)
286
+ end
287
+ end
288
+ end
289
+
290
+ describe 'limits' do
291
+ it 'should be able to limit the objects' do
292
+ Heffalump.all(:limit => 2).length.should == 2
293
+ end
294
+ end
295
+ end
296
+ else
297
+ it 'needs to support #read and #create to test query matching'
298
+ end
299
+ end