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 +17 -1
- data/lib/mince/data_model.rb +319 -0
- data/lib/mince/model.rb +161 -0
- data/lib/mince/shared_examples/interface_example.rb +76 -26
- data/lib/mince/version.rb +1 -1
- data/lib/mince.rb +130 -2
- data/spec/integration/simple_mince_data_model_spec.rb +55 -0
- data/spec/units/mince/data_model_spec.rb +228 -0
- data/spec/units/mince/model_spec.rb +128 -0
- metadata +52 -46
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
|
data/lib/mince/model.rb
ADDED
@@ -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.
|
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 =>
|
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)
|
111
|
-
|
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
|
|