mince 2.0.0.pre → 2.0.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mince/config.rb CHANGED
@@ -1,15 +1,31 @@
1
- module Mince
1
+ module Mince # :nodoc:
2
2
  require 'singleton'
3
3
 
4
+ # = Configuration for Mince
5
+ #
6
+ # Mince can be configured to interact with different mince supported data interfaces.
7
+ #
8
+ # Simply run the following to specify the data interface to use
9
+ # Mince::Config.interface = Mince::MyDb::Interface
10
+ #
11
+ # This is a singleton object in order to prevent multiple instances of this object from
12
+ # being used.
4
13
  class Config
5
14
  include Singleton
6
15
 
7
16
  attr_accessor :interface
8
17
 
18
+ # Sets the singleton's interface attribute so that you can change your storage strategy
19
+ # without changing all references to that class.
20
+ #
21
+ # @param [Class] interface the Mince Supported Interface class
9
22
  def self.interface=(interface)
10
23
  instance.interface = interface
11
24
  end
12
25
 
26
+ # Returns the interface that is configured to be used. Use this method instead of hard coding
27
+ # which mince interface to use throughout your code so that you can change mince interfaces
28
+ # as needed.
13
29
  def self.interface
14
30
  instance.interface
15
31
  end
@@ -0,0 +1,319 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+ require 'active_support/core_ext/object/instance_variables'
3
+ require 'active_support/core_ext/hash/slice'
4
+ require 'active_support/concern'
5
+
6
+ require_relative 'config'
7
+
8
+ module Mince # :nodoc:
9
+ # = DataModel
10
+ #
11
+ # Mince::DataModel is used as a mixin to easily mixin behavior into an object that
12
+ # you wish to act as a data model and interact with mince data interfaces.
13
+ #
14
+ # Simply mixin this module in order to get a wrapper class to interact with a mince
15
+ # interface for a specific collection
16
+ #
17
+ # Example:
18
+ # require 'mince/data_model'
19
+ #
20
+ # class UserDataModel
21
+ # include Mince::DataModel
22
+ #
23
+ # data_collection :users
24
+ # data_fields :username, :first_name, :last_name, :email, :password
25
+ # end
26
+ #
27
+ # To access methods on the data model do the following:
28
+ # user = User.new first_name: 'Matt' # initialize a user from some User class
29
+ # user.id = UserDataModel.store user # returns the id of the stored user
30
+ #
31
+ # UserDataModel.find 1 # => returns the data for the user with an id of 1
32
+ # UserDataModel.find_all # => returns all users
33
+ #
34
+ # View the docs for each method available
35
+ module DataModel
36
+ extend ActiveSupport::Concern
37
+
38
+ included do
39
+ # creates readonly attribute for id and model
40
+ attr_reader :id, :model
41
+ end
42
+
43
+ module ClassMethods # :nodoc:
44
+ # The interface configured by Mince::Config.interface=
45
+ #
46
+ # @returns [Class] interface class to be used by all mince data interactions
47
+ def interface
48
+ Config.interface
49
+ end
50
+
51
+ # Sets what data fields to accept
52
+ # Returns the fields
53
+ #
54
+ # * Calling multiple times will do an ammendment to the existing list of fields
55
+ # * Calling without any fields will simply return the current list of fields
56
+ #
57
+ # @param [*Array] *fields an array of fields to add to the data model
58
+ # @returns [Array] the fields defined for the data model
59
+ def data_fields(*fields)
60
+ create_data_fields(*fields) if fields.any?
61
+ @data_fields
62
+ end
63
+
64
+ # Sets the name of the data collection
65
+ # Returns the name of the data collection
66
+ #
67
+ # @param [String] collection_name the name of the collection to use
68
+ # @returns [String] returns the name of the collection
69
+ def data_collection(collection_name = nil)
70
+ set_data_collection(collection_name) if collection_name
71
+ @data_collection
72
+ end
73
+
74
+ # Stores the given model into the data store
75
+ #
76
+ # @param [Class] model a ruby class instance object to store into the data store
77
+ # @returns [id] returns the id of the newly stored object
78
+ def store(model)
79
+ new(model).tap do |p|
80
+ p.add_to_interface
81
+ end.id
82
+ end
83
+
84
+ # Updates the given model in the data store with the fields and values in model
85
+ #
86
+ # Uses model.id to find the record to update
87
+ #
88
+ # @param [Class] model a ruby class instance object to store into the data store
89
+ def update(model)
90
+ new(model).tap do |p|
91
+ p.replace_in_interface
92
+ end
93
+ end
94
+
95
+ # Updates a specific field with a value for the record with the given id
96
+ #
97
+ # @param [id] id the id of the record to update
98
+ # @param [Symbol] field the field to update
99
+ # @param [*] value the value to update the field with
100
+ def update_field_with_value(id, field, value)
101
+ interface.update_field_with_value(data_collection, id, field, value)
102
+ end
103
+
104
+ # Increments a field by a given amount
105
+ #
106
+ # Some databases provide very efficient algorithms for incrementing or decrementing a
107
+ # value.
108
+ #
109
+ # @param [id] id the id of the record to update
110
+ # @param [Symbol] field the field to update
111
+ # @param [Numeric] amount the amount to increment or decrement the field with
112
+ def increment_field_by_amount(id, field, amount)
113
+ interface.increment_field_by_amount(data_collection, id, field, amount)
114
+ end
115
+
116
+ # Removes a value from an field that is an array
117
+ #
118
+ # @param [id] id the id of the record to update
119
+ # @param [Symbol] field the field to update
120
+ # @param [*] value the value to update the field with
121
+ def remove_from_array(id, field, value)
122
+ interface.remove_from_array(data_collection, interface.primary_key, id, field, value)
123
+ end
124
+
125
+ # Pushes a value to an array field
126
+ #
127
+ # @param [id] id the id of the record to update
128
+ # @param [Symbol] field the field to update
129
+ # @param [*] value the value to update the field with
130
+ def push_to_array(id, field, value)
131
+ interface.push_to_array(data_collection, interface.primary_key, id, field, value)
132
+ end
133
+
134
+ # Returns a record that has the given id
135
+ #
136
+ # Returns nil if nothing found
137
+ #
138
+ # @param [id] id the id of the record to find
139
+ # @returns [HashWithIndifferentAccess, nil] a hash with the data for the record
140
+ def find(id)
141
+ translate_from_interface interface.find(data_collection, interface.primary_key, id)
142
+ end
143
+
144
+ # Deletes a field from all records in the collection
145
+ #
146
+ # @param [Symbol] field the field to remove
147
+ def delete_field(field)
148
+ interface.delete_field(data_collection, field)
149
+ end
150
+
151
+
152
+ # Deletes a field from all records that matches the field / value key pairs in the
153
+ # params provided in the collection
154
+ #
155
+ # This will only delete records that match all key/value pairs in the params hash.
156
+ #
157
+ # DataModel.delete_by_params(username: 'railsgrammer', first_name: 'Matt Simpson')
158
+ #
159
+ # @param [Hash] params the key/value pair hash to delete records by
160
+ def delete_by_params(params)
161
+ interface.delete_by_params(data_collection, params)
162
+ end
163
+
164
+ # Finds all records in the collection
165
+ #
166
+ # @returns [Array] all records in the collection, empty array if non found.
167
+ def all
168
+ translate_each_from_interface interface.find_all(data_collection)
169
+ end
170
+
171
+ # Returns all records in the collection that has the given field and value
172
+ #
173
+ # @param [Symbol] field the field to query for
174
+ # @param [*] value the value to query on the field for
175
+ # @returns [Array] the set of records matching the field / value pair
176
+ def all_by_field(field, value)
177
+ translate_each_from_interface interface.get_all_for_key_with_value(data_collection, field, value)
178
+ end
179
+
180
+ # Finds all recurds that match a set of key / value pairs
181
+ #
182
+ # @param [Hash] hash a hash of field / value pairs to query records for
183
+ # @returns [Array] the set of records matching all key / value pairs
184
+ def all_by_fields(hash)
185
+ translate_each_from_interface interface.get_by_params(data_collection, hash)
186
+ end
187
+
188
+ # Finds One record that matches all of the field / value pairs
189
+ #
190
+ # @param [Hash] hash the hash to query for
191
+ # @returns [Hash] a hash containing the data for the found record, nil is returned when nothing is found
192
+ def find_by_fields(hash)
193
+ translate_from_interface all_by_fields(hash).first
194
+ end
195
+
196
+ # Finds One record that matches a field / value pair
197
+ #
198
+ # @param [Symbol] field the field to query for
199
+ # @param [*] value the value to query on the field for
200
+ # @returns [Hash] a hash containing the data for the found record, nil is returned when nothing is found
201
+ def find_by_field(field, value)
202
+ translate_from_interface interface.get_for_key_with_value(data_collection, field, value)
203
+ end
204
+
205
+ # Finds all records where the field contains any of the values
206
+ #
207
+ # @param [Symbol] field the field to query against
208
+ # @param [Array] values an array of values to get records for
209
+ # @returns [Array] an array containing all records that match the criteria
210
+ def containing_any(field, values)
211
+ translate_each_from_interface interface.containing_any(data_collection, field, values)
212
+ end
213
+
214
+ # Finds all records where the field, which must be an array field, contains the value
215
+ #
216
+ # @param [Symbol] field the field to query against
217
+ # @param [*] value the value to query against
218
+ # @returns [Array] an array containing all records that match the criteria
219
+ def array_contains(field, value)
220
+ translate_each_from_interface interface.array_contains(data_collection, field, value)
221
+ end
222
+
223
+ # Deletes the entire collection from the database
224
+ def delete_collection
225
+ interface.delete_collection(data_collection)
226
+ end
227
+
228
+ private
229
+
230
+ def translate_from_interface(hash)
231
+ if hash
232
+ hash["id"] = hash[primary_key] if hash[primary_key]
233
+ HashWithIndifferentAccess.new hash
234
+ end
235
+ end
236
+
237
+ def translate_each_from_interface(data)
238
+ data.collect {|d| translate_from_interface(d) }
239
+ end
240
+
241
+ def primary_key
242
+ @primary_key ||= interface.primary_key
243
+ end
244
+
245
+ def set_data_collection(collection_name)
246
+ @data_collection = collection_name
247
+ end
248
+
249
+ def create_data_fields(*fields)
250
+ attr_accessor *fields
251
+ @data_fields = fields
252
+ end
253
+ end
254
+
255
+ def initialize(model)
256
+ @model = model
257
+ set_data_field_values
258
+ set_id
259
+ end
260
+
261
+ def interface
262
+ self.class.interface
263
+ end
264
+
265
+ def data_fields
266
+ self.class.data_fields
267
+ end
268
+
269
+ def data_collection
270
+ self.class.data_collection
271
+ end
272
+
273
+ def add_to_interface
274
+ interface.add(data_collection, attributes)
275
+ end
276
+
277
+ def replace_in_interface
278
+ interface.replace(data_collection, attributes)
279
+ end
280
+
281
+ private
282
+
283
+ def attributes
284
+ model_instance_values.merge(primary_key => id)
285
+ end
286
+
287
+ def model_instance_values
288
+ HashWithIndifferentAccess.new(model.instance_values).slice(*data_fields)
289
+ end
290
+
291
+ def set_id
292
+ @id = model_has_id? ? model.id : generated_id
293
+ end
294
+
295
+ def generated_id
296
+ interface.generate_unique_id(model)
297
+ end
298
+
299
+ def model_has_id?
300
+ model.respond_to?(:id) && model.id
301
+ end
302
+
303
+ def set_data_field_values
304
+ data_fields.each { |field| set_data_field_value(field) }
305
+ end
306
+
307
+ def primary_key
308
+ interface.primary_key
309
+ end
310
+
311
+ def set_data_field_value(field)
312
+ self.send("#{field}=", model.send(field)) if field_exists?(field)
313
+ end
314
+
315
+ def field_exists?(field)
316
+ model.respond_to?(field) && !model.send(field).nil?
317
+ end
318
+ end
319
+ end
@@ -0,0 +1,161 @@
1
+ require 'active_support'
2
+ require 'active_model'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'active_support/core_ext/object/instance_variables'
5
+
6
+ module Mince
7
+ # = Model
8
+ #
9
+ # The mince model is a module that provides standard model to data behavior to the Mince data model mixing for a specific model.
10
+ #
11
+ # Simply mixin this module in order to get a wrapper class to interact with a mince
12
+ # interface for a specific collection
13
+ #
14
+ # Example:
15
+ # require 'mince'
16
+ #
17
+ # class BookDataModel
18
+ # include Mince::DataModel
19
+ #
20
+ # data_collection :books
21
+ # data_fields :title, :publisher
22
+ # end
23
+ #
24
+ # class Book
25
+ # include Mince::Model
26
+ #
27
+ # data_model BookDataModel
28
+ # fields :title, :publisher
29
+ # end
30
+ #
31
+ # book = Book.new title: 'The World In Photographs', publisher: 'National Geographic'
32
+ # book.save
33
+ #
34
+ # View the docs for each method available
35
+ module Model
36
+ extend ActiveSupport::Concern
37
+
38
+ included do
39
+ include ActiveModel::Conversion
40
+ extend ActiveModel::Naming
41
+
42
+ attr_accessor :id
43
+ end
44
+
45
+ module ClassMethods
46
+ # Sets or returns the data model class for the model
47
+ def data_model(model=nil)
48
+ @data_model = model if model
49
+ @data_model
50
+ end
51
+
52
+ # Returns all models from the data model
53
+ def all
54
+ data_model.all.map{|a| new a }
55
+ end
56
+
57
+ # Returns a model that matches a given id, returns nil if none found
58
+ def find(id)
59
+ a = data_model.find(id)
60
+ new a if a
61
+ end
62
+
63
+ # Adds a field to the object. Takes options to indicate assignability. If `assignable`
64
+ # is set, the field will be assignable via
65
+ # model.field = 'foo'
66
+ # model.attributes = { field: 'foo' }
67
+ #
68
+ def field(field_name, options={})
69
+ if options[:assignable]
70
+ add_assignable_field(field_name)
71
+ else
72
+ add_readonly_field(field_name)
73
+ end
74
+ end
75
+
76
+ # Adds a read only field, values for these fields can only be set in the class itself
77
+ # or from the hash sent in to the initializer
78
+ def add_readonly_field(field_name)
79
+ fields << field_name
80
+ readonly_fields << field_name
81
+ attr_reader field_name
82
+ end
83
+
84
+ # Adds an assignable field, values for these fields can be set using the field writer
85
+ # or from the `attributes=` method.
86
+ def add_assignable_field(field_name)
87
+ fields << field_name
88
+ assignable_fields << field_name
89
+ attr_accessor field_name
90
+ end
91
+
92
+ # Adds multiple readonly fields to the fields array, and returns the current list
93
+ # of fields. If no fields are given, it just returns the current list of fields
94
+ def fields(*field_names)
95
+ @fields ||= []
96
+ field_names.each {|field_name| field(field_name) }
97
+ @fields
98
+ end
99
+
100
+ # Returns the list of assignable fields
101
+ def assignable_fields
102
+ @assignable_fields ||= []
103
+ end
104
+
105
+ # Returns the list of readonly fields
106
+ def readonly_fields
107
+ @readonly_fields ||= []
108
+ end
109
+ end
110
+
111
+ delegate :data_model, :assignable_fields, :readonly_fields, :fields, to: 'self.class'
112
+
113
+ # Sets values (for fields defined by calling .field or .fields) in the hash to the object
114
+ # includes assignable and non-assignable fields
115
+ def initialize(hash={})
116
+ @id = hash[:id]
117
+ readonly_fields.each do |field_name|
118
+ self.instance_variable_set("@#{field_name}", hash[field_name]) if hash[field_name]
119
+ end
120
+ self.attributes = hash
121
+ end
122
+
123
+ # Returns true if the record indicates that it has been persisted to a data model.
124
+ # Returns false otherwise.
125
+ def persisted?
126
+ !!id
127
+ end
128
+
129
+ # Saves the object to the data model. Stores if new, updates previous entry if it has already
130
+ # been saved.
131
+ def save
132
+ ensure_no_extra_fields
133
+ if persisted?
134
+ data_model.update(self)
135
+ else
136
+ @id = data_model.store(self)
137
+ end
138
+ end
139
+
140
+ # Sets values (for assignable fields only, defined by calling .field or .fields) in the hash
141
+ # to the object.
142
+ #
143
+ # Allows the proxy to have whitelisted attributes to be assigned from http requests.
144
+ def attributes=(hash={})
145
+ assignable_fields.each do |field|
146
+ send("#{field}=", hash[field]) if hash[field]
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ # Ensures that the data model has all of the fields that are trying to be saved. Raises an
153
+ # exception if the data model does not.
154
+ def ensure_no_extra_fields
155
+ extra_fields = (fields - data_model.data_fields)
156
+ if extra_fields.any?
157
+ raise "Tried to save a #{self.class.name} with fields not specified in #{data_model.name}: #{extra_fields.join(', ')}"
158
+ end
159
+ end
160
+ end
161
+ end
@@ -1,5 +1,42 @@
1
1
  require_relative '../../mince'
2
2
 
3
+ # = Shared example for a Mince Interface
4
+ #
5
+ # This is an Rspec Shared Example. It provides the ability to test
6
+ # shared behavior of objects without duplication.
7
+ #
8
+ # Use this shared example as documentation on what API a mince data interface
9
+ # must implement and as a specification and integration test while developing
10
+ # your mince data interface.
11
+ #
12
+ # == How to use
13
+ #
14
+ # For example. If I were writing a MySQL mince interface I would add mince
15
+ # as a gem dependency in my library (in your Gemfile or gemspec file). I would
16
+ # add Rspec, create a new spec file at spec/integration/mince_interface_spec.rb
17
+ # with the following contents
18
+ #
19
+ # require_relative '../../lib/my_mince_mysql'
20
+ # require 'mince/shared_examples/interface_example'
21
+ #
22
+ # describe 'Mince Interface with MySQL' do
23
+ # before do
24
+ # Mince::Config.interface = Mince::MyMinceMySQL::Interface
25
+ # end
26
+ #
27
+ # it_behaves_like 'a mince interface'
28
+ # end
29
+ #
30
+ # Run your spec
31
+ #
32
+ # bundle exec spec rspec/integration/mince_inerface_spect.rb
33
+ #
34
+ # Make the failures pass, when there are no failures your interface is fully
35
+ # supported. Be sure to submit your library when you've finished so others
36
+ # can use it.
37
+ #
38
+ # For a real examples, view the hashy_db, mingo, or mince_dynamo_db gems
39
+ #
3
40
  shared_examples_for 'a mince interface' do
4
41
  describe 'Mince Interface v2' do
5
42
  let(:interface) { Mince::Config.interface }
@@ -10,11 +47,15 @@ shared_examples_for 'a mince interface' do
10
47
  let(:data3) { { primary_key => 3, field_1: 'value 3', field_2: 9, shared_between_1_and_2: 'not the same as 1 and 2', :some_array => [1, 7]} }
11
48
 
12
49
  before do
13
- interface.set_data({})
50
+ interface.clear
14
51
 
15
52
  interface.insert(:some_collection, [data1, data2, data3])
16
53
  end
17
54
 
55
+ after do
56
+ interface.clear
57
+ end
58
+
18
59
  describe "Generating a primary key" do
19
60
  subject do
20
61
  (1..number_of_records).map do |salt|
@@ -40,7 +81,7 @@ shared_examples_for 'a mince interface' do
40
81
  it 'can delete a collection' do
41
82
  interface.delete_collection(:some_collection)
42
83
 
43
- interface.find_all(:some_collection).should == []
84
+ interface.find_all(:some_collection).to_a.should == []
44
85
  end
45
86
 
46
87
  it 'can delete records that match a given set of fields' do
@@ -48,85 +89,94 @@ shared_examples_for 'a mince interface' do
48
89
 
49
90
  interface.delete_by_params(:some_collection, params)
50
91
 
51
- interface.find_all(:some_collection).should == [data2, data3]
92
+ convert_each(interface.find_all(:some_collection)).should == convert_each([data2, data3])
52
93
  end
53
94
 
54
95
  it 'can write and read data to and from a collection' do
55
- data4 = {primary_key =>3, field_1: 'value 3', field_2: 9, shared_between_1_and_2: 'not the same as 1 and 2', :some_array => [1, 7]}
96
+ data4 = {primary_key =>4, field_1: 'value 3', field_2: 9, shared_between_1_and_2: 'not the same as 1 and 2', :some_array => [1, 7]}
56
97
 
57
98
  interface.add(:some_collection, data4)
58
- interface.find_all(:some_collection).should == [data1, data2, data3, data4]
99
+ convert_each(interface.find_all(:some_collection)).should == convert_each([data1, data2, data3, data4])
59
100
  end
60
101
 
61
102
  it 'can replace a record' do
62
103
  data2[:field_1] = 'value modified'
63
104
  interface.replace(:some_collection, data2)
64
105
 
65
- interface.find(:some_collection, primary_key, 2)[:field_1].should == 'value modified'
106
+ convert(interface.find(:some_collection, primary_key, 2))[:field_1].should == 'value modified'
66
107
  end
67
108
 
68
109
  it 'can update a field with a value on a specific record' do
69
110
  interface.update_field_with_value(:some_collection, 3, :field_2, '10')
70
-
71
- interface.find(:some_collection, primary_key, 3)[:field_2].should == '10'
111
+
112
+ convert(interface.find(:some_collection, primary_key, 3))[:field_2].should == '10'
72
113
  end
73
114
 
74
115
  it 'can increment a field with a given amount for a specific field' do
75
116
  interface.increment_field_by_amount(:some_collection, 1, :field_2, 3)
76
-
77
- interface.find(:some_collection, primary_key, 1)[:field_2].should == 6
117
+
118
+ convert(interface.find(:some_collection, primary_key, 1))[:field_2].should == 6
78
119
  end
79
120
 
80
121
  it 'can get one document' do
81
- interface.find(:some_collection, :field_1, 'value 1').should == data1
82
- interface.find(:some_collection, :field_2, 6).should == data2
122
+ convert(interface.find(:some_collection, :field_1, 'value 1')).should == convert(data1)
123
+ convert(interface.find(:some_collection, :field_2, 6)).should == convert(data2)
83
124
  end
84
125
 
85
126
  it 'can clear the data store' do
86
127
  interface.clear
87
128
 
88
- interface.find_all(:some_collection).should == []
129
+ interface.find_all(:some_collection).to_a.should == []
89
130
  end
90
131
 
91
132
  it 'can get all records of a specific key value' do
92
- interface.get_all_for_key_with_value(:some_collection, :shared_between_1_and_2, 'awesome_value').should == [data1, data2]
133
+ convert_each(interface.get_all_for_key_with_value(:some_collection, :shared_between_1_and_2, 'awesome_value')).should == convert_each([data1, data2])
93
134
  end
94
135
 
95
136
  it 'can get all records where a value includes any of a set of values' do
96
- interface.containing_any(:some_collection, :some_array, []).should == []
97
- interface.containing_any(:some_collection, :some_array, [7, 2, 3]).should == [data1, data3]
98
- interface.containing_any(:some_collection, primary_key, [1, 2, 5]).should == [data1, data2]
137
+ interface.containing_any(:some_collection, :some_array, []).to_a.should == []
138
+ convert_each(interface.containing_any(:some_collection, :some_array, [7, 2, 3])).should == convert_each([data1, data3])
139
+ convert_each(interface.containing_any(:some_collection, primary_key, [1, 2, 5])).should == convert_each([data1, data2])
99
140
  end
100
141
 
101
142
  it 'can get all records where the array includes a value' do
102
- interface.array_contains(:some_collection, :some_array, 1).should == [data1, data3]
103
- interface.array_contains(:some_collection, :some_array_2, 1).should == []
143
+ convert_each(interface.array_contains(:some_collection, :some_array, 1)).should == convert_each([data1, data3])
144
+ interface.array_contains(:some_collection, :some_array_2, 1).to_a.should == []
104
145
  end
105
146
 
106
147
  it 'can push a value to an array for a specific record' do
107
148
  interface.push_to_array(:some_collection, primary_key, 1, :field_3, 'add to existing array')
108
149
  interface.push_to_array(:some_collection, primary_key, 1, :new_field, 'add to new array')
109
150
 
110
- interface.find(:some_collection, primary_key, 1)[:field_3].should include('add to existing array')
111
- interface.find(:some_collection, primary_key, 1)[:new_field].should == ['add to new array']
151
+ record = convert(interface.find(:some_collection, primary_key, 1))
152
+ record[:field_3].should include('add to existing array')
153
+ record[:new_field].should == ['add to new array']
112
154
  end
113
155
 
114
156
  it 'can remove a value from an array for a specific record' do
115
157
  interface.remove_from_array(:some_collection, primary_key, 1, :field_3, 2)
116
158
 
117
- interface.find(:some_collection, primary_key, 1)[:field_3].should_not include(2)
159
+ convert(interface.find(:some_collection, primary_key, 1))[:field_3].should_not include(2)
118
160
  end
119
161
 
120
162
  it 'can get all records that match a given set of keys and values' do
121
- records = interface.get_by_params(:some_collection, field_1: 'value 1', shared_between_1_and_2: 'awesome_value')
163
+ records = interface.get_by_params(:some_collection, field_1: 'value 1', shared_between_1_and_2: 'awesome_value').to_a
122
164
  records.size.should be(1)
123
- records.first[primary_key].should == 1
124
- interface.find_all(:some_collection).size.should == 3
165
+ convert(records.first)[primary_key].should == 1
166
+ interface.find_all(:some_collection).to_a.size.should == 3
125
167
  end
126
168
 
127
169
  it 'can get a record for a specific key and value' do
128
- interface.get_for_key_with_value(:some_collection, :field_1, 'value 1').should == data1
170
+ convert(interface.get_for_key_with_value(:some_collection, :field_1, 'value 1')).should == convert(data1)
129
171
  end
130
172
  end
173
+
174
+ def convert(hash)
175
+ HashWithIndifferentAccess.new(hash)
176
+ end
177
+
178
+ def convert_each(ary)
179
+ ary.map{|a| convert(a) }
180
+ end
131
181
  end
132
182