mince 2.0.0.pre → 2.0.0.pre.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/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
|
|