mince 2.0.0.pre.4 → 2.0.1
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/data_model.rb +23 -9
- data/lib/mince/model.rb +24 -126
- data/lib/mince/model/data_model.rb +31 -0
- data/lib/mince/model/fields.rb +89 -0
- data/lib/mince/model/finders.rb +32 -0
- data/lib/mince/model/persistence.rb +45 -0
- data/lib/mince/shared_examples/interface_example.rb +8 -13
- data/lib/mince/version.rb +1 -1
- data/spec/integration/mince_data_model_spec.rb +42 -0
- data/spec/integration/mince_model_spec.rb +106 -0
- data/spec/support/shared_examples/modeL_data_model_example.rb +16 -0
- data/spec/support/shared_examples/model_finders_example.rb +59 -0
- data/spec/units/mince/data_model_spec.rb +20 -5
- data/spec/units/mince/model/data_model_spec.rb +14 -0
- data/spec/units/mince/model/finders_spec.rb +14 -0
- data/spec/units/mince/model_spec.rb +10 -0
- metadata +27 -13
- data/spec/integration/simple_mince_data_model_spec.rb +0 -55
data/lib/mince/data_model.rb
CHANGED
@@ -8,7 +8,7 @@ require_relative 'config'
|
|
8
8
|
module Mince # :nodoc:
|
9
9
|
# = DataModel
|
10
10
|
#
|
11
|
-
# Mince::DataModel is used as a mixin to easily mixin behavior into an object that
|
11
|
+
# Mince::DataModel is used as a mixin to easily mixin behavior into an object that
|
12
12
|
# you wish to act as a data model and interact with mince data interfaces.
|
13
13
|
#
|
14
14
|
# Simply mixin this module in order to get a wrapper class to interact with a mince
|
@@ -103,7 +103,7 @@ module Mince # :nodoc:
|
|
103
103
|
|
104
104
|
# Increments a field by a given amount
|
105
105
|
#
|
106
|
-
# Some databases provide very efficient algorithms for incrementing or decrementing a
|
106
|
+
# Some databases provide very efficient algorithms for incrementing or decrementing a
|
107
107
|
# value.
|
108
108
|
#
|
109
109
|
# @param [id] id the id of the record to update
|
@@ -119,7 +119,7 @@ module Mince # :nodoc:
|
|
119
119
|
# @param [Symbol] field the field to update
|
120
120
|
# @param [*] value the value to update the field with
|
121
121
|
def remove_from_array(id, field, value)
|
122
|
-
interface.remove_from_array(data_collection,
|
122
|
+
interface.remove_from_array(data_collection, id, field, value)
|
123
123
|
end
|
124
124
|
|
125
125
|
# Pushes a value to an array field
|
@@ -128,7 +128,7 @@ module Mince # :nodoc:
|
|
128
128
|
# @param [Symbol] field the field to update
|
129
129
|
# @param [*] value the value to update the field with
|
130
130
|
def push_to_array(id, field, value)
|
131
|
-
interface.push_to_array(data_collection,
|
131
|
+
interface.push_to_array(data_collection, id, field, value)
|
132
132
|
end
|
133
133
|
|
134
134
|
# Returns a record that has the given id
|
@@ -138,7 +138,7 @@ module Mince # :nodoc:
|
|
138
138
|
# @param [id] id the id of the record to find
|
139
139
|
# @returns [HashWithIndifferentAccess, nil] a hash with the data for the record
|
140
140
|
def find(id)
|
141
|
-
translate_from_interface interface.find(data_collection,
|
141
|
+
translate_from_interface interface.find(data_collection, id)
|
142
142
|
end
|
143
143
|
|
144
144
|
# Deletes a field from all records in the collection
|
@@ -149,7 +149,7 @@ module Mince # :nodoc:
|
|
149
149
|
end
|
150
150
|
|
151
151
|
|
152
|
-
# Deletes
|
152
|
+
# Deletes all records that matches the field / value key pairs in the
|
153
153
|
# params provided in the collection
|
154
154
|
#
|
155
155
|
# This will only delete records that match all key/value pairs in the params hash.
|
@@ -225,11 +225,23 @@ module Mince # :nodoc:
|
|
225
225
|
interface.delete_collection(data_collection)
|
226
226
|
end
|
227
227
|
|
228
|
-
|
228
|
+
# Generates a new id to be used for a primary key
|
229
|
+
def generate_unique_id(seed)
|
230
|
+
interface.generate_unique_id(seed)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Adds a new record with provided data hash
|
234
|
+
#
|
235
|
+
# @param [Hash] data to add to the data collection
|
236
|
+
# @returns [Hash] the data that was added to the data collection, including the uniquely generated id
|
237
|
+
def add(hash)
|
238
|
+
hash = HashWithIndifferentAccess.new(hash.merge(primary_key => generate_unique_id(hash)))
|
239
|
+
interface.add(data_collection, hash).first
|
240
|
+
end
|
229
241
|
|
230
242
|
def translate_from_interface(hash)
|
231
243
|
if hash
|
232
|
-
hash["id"] = hash[primary_key] if hash[primary_key]
|
244
|
+
hash["id"] = hash[primary_key] if hash[primary_key] && (primary_key != :id || primary_key != 'id')
|
233
245
|
HashWithIndifferentAccess.new hash
|
234
246
|
end
|
235
247
|
end
|
@@ -238,6 +250,8 @@ module Mince # :nodoc:
|
|
238
250
|
data.collect {|d| translate_from_interface(d) }
|
239
251
|
end
|
240
252
|
|
253
|
+
private
|
254
|
+
|
241
255
|
def primary_key
|
242
256
|
@primary_key ||= interface.primary_key
|
243
257
|
end
|
@@ -293,7 +307,7 @@ module Mince # :nodoc:
|
|
293
307
|
end
|
294
308
|
|
295
309
|
def generated_id
|
296
|
-
|
310
|
+
self.class.generate_unique_id(model)
|
297
311
|
end
|
298
312
|
|
299
313
|
def model_has_id?
|
data/lib/mince/model.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
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
1
|
module Mince
|
2
|
+
require_relative 'model/fields'
|
3
|
+
require_relative 'model/persistence'
|
4
|
+
require_relative 'model/finders'
|
5
|
+
require_relative 'model/data_model'
|
6
|
+
|
7
7
|
# = Model
|
8
8
|
#
|
9
9
|
# The mince model is a module that provides standard model to data behavior to the Mince data model mixing for a specific model.
|
@@ -31,131 +31,29 @@ module Mince
|
|
31
31
|
# book = Book.new title: 'The World In Photographs', publisher: 'National Geographic'
|
32
32
|
# book.save
|
33
33
|
#
|
34
|
-
#
|
34
|
+
# By including this module, you are including DataModel, Fields, Persistence, and Finders.
|
35
|
+
#
|
36
|
+
# However, you can choose which modules you would like by including those modules individually
|
37
|
+
# so that you can have flexibility an lighter weight help to implement your models.
|
38
|
+
#
|
39
|
+
# class AvailableBook
|
40
|
+
# include Mince::Model::DataModel
|
41
|
+
#
|
42
|
+
# data_collection :books
|
43
|
+
#
|
44
|
+
# def self.all
|
45
|
+
# data_collection.all_by_fields(library: "St. Louis - Downtown", available: true).map{ |a| new(a) }
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
35
49
|
module Model
|
36
50
|
extend ActiveSupport::Concern
|
37
51
|
|
38
52
|
included do
|
39
|
-
include
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
53
|
+
include DataModel
|
54
|
+
include Fields
|
55
|
+
include Persistence
|
56
|
+
include Finders
|
159
57
|
end
|
160
58
|
end
|
161
59
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mince
|
2
|
+
module Model
|
3
|
+
require 'active_support'
|
4
|
+
|
5
|
+
module DataModel
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
# Sets or returns the data model class for the model
|
10
|
+
def data_model(model=nil)
|
11
|
+
@data_model = model if model
|
12
|
+
@data_model
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
# Ensures that the data model has all of the fields that are trying to be saved. Raises an
|
19
|
+
# exception if the data model does not.
|
20
|
+
#
|
21
|
+
# Not sure if this is where this method should live, it requires both
|
22
|
+
# Mince::Model::DataModel and Mince::Model::Fields.
|
23
|
+
def ensure_no_extra_fields
|
24
|
+
extra_fields = (fields - data_model.data_fields)
|
25
|
+
if extra_fields.any?
|
26
|
+
raise "Tried to save a #{self.class.name} with fields not specified in #{data_model.name}: #{extra_fields.join(', ')}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Mince
|
2
|
+
module Model
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/module/delegation'
|
5
|
+
require 'active_support/core_ext/object/instance_variables'
|
6
|
+
require_relative 'data_model'
|
7
|
+
|
8
|
+
module Fields
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
include Mince::Model::DataModel
|
13
|
+
|
14
|
+
attr_accessor :id
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Adds a field to the object. Takes options to indicate assignability. If `assignable`
|
19
|
+
# is set, the field will be assignable via
|
20
|
+
# model.field = 'foo'
|
21
|
+
# model.attributes = { field: 'foo' }
|
22
|
+
#
|
23
|
+
def field(field_name, options={})
|
24
|
+
if options[:assignable]
|
25
|
+
add_assignable_field(field_name)
|
26
|
+
else
|
27
|
+
add_readonly_field(field_name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds a read only field, values for these fields can only be set in the class itself
|
32
|
+
# or from the hash sent in to the initializer
|
33
|
+
def add_readonly_field(field_name)
|
34
|
+
fields << field_name
|
35
|
+
readonly_fields << field_name
|
36
|
+
attr_reader field_name
|
37
|
+
end
|
38
|
+
|
39
|
+
# Adds an assignable field, values for these fields can be set using the field writer
|
40
|
+
# or from the `attributes=` method.
|
41
|
+
def add_assignable_field(field_name)
|
42
|
+
fields << field_name
|
43
|
+
assignable_fields << field_name
|
44
|
+
attr_accessor field_name
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds multiple readonly fields to the fields array, and returns the current list
|
48
|
+
# of fields. If no fields are given, it just returns the current list of fields
|
49
|
+
def fields(*field_names)
|
50
|
+
@fields ||= []
|
51
|
+
field_names.each {|field_name| field(field_name) }
|
52
|
+
@fields
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the list of assignable fields
|
56
|
+
def assignable_fields
|
57
|
+
@assignable_fields ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the list of readonly fields
|
61
|
+
def readonly_fields
|
62
|
+
@readonly_fields ||= []
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
delegate :assignable_fields, :readonly_fields, :fields, to: 'self.class'
|
67
|
+
|
68
|
+
# Sets values (for fields defined by calling .field or .fields) in the hash to the object
|
69
|
+
# includes assignable and non-assignable fields
|
70
|
+
def initialize(hash={})
|
71
|
+
@id = hash[:id]
|
72
|
+
readonly_fields.each do |field_name|
|
73
|
+
self.instance_variable_set("@#{field_name}", hash[field_name]) if hash[field_name]
|
74
|
+
end
|
75
|
+
self.attributes = hash
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sets values (for assignable fields only, defined by calling .field or .fields) in the hash
|
79
|
+
# to the object.
|
80
|
+
#
|
81
|
+
# Allows the proxy to have whitelisted attributes to be assigned from http requests.
|
82
|
+
def attributes=(hash={})
|
83
|
+
assignable_fields.each do |field|
|
84
|
+
send("#{field}=", hash[field]) if hash[field]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mince
|
2
|
+
module Model
|
3
|
+
require 'active_support'
|
4
|
+
require_relative 'data_model'
|
5
|
+
|
6
|
+
module Finders
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
include Mince::Model::DataModel
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# Sets or returns the data model class for the model
|
15
|
+
def find_by_fields(*fields)
|
16
|
+
raise 'not implemented'
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns all models from the data model
|
20
|
+
def all
|
21
|
+
data_model.all.map{|a| new a }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a model that matches a given id, returns nil if none found
|
25
|
+
def find(id)
|
26
|
+
a = data_model.find(id)
|
27
|
+
new a if a
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Mince
|
2
|
+
module Model
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_model'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
6
|
+
require_relative 'data_model'
|
7
|
+
|
8
|
+
module Persistence
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
include Mince::Model::DataModel
|
13
|
+
include ActiveModel::Conversion
|
14
|
+
extend ActiveModel::Naming
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Creates a record with the given field values
|
19
|
+
def create(data)
|
20
|
+
new(data).tap(&:save)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
delegate :data_model, to: 'self.class'
|
25
|
+
|
26
|
+
# Returns true if the record indicates that it has been persisted to a data model.
|
27
|
+
# Returns false otherwise.
|
28
|
+
def persisted?
|
29
|
+
!!id
|
30
|
+
end
|
31
|
+
|
32
|
+
# Saves the object to the data model. Stores if new, updates previous entry if it has already
|
33
|
+
# been saved.
|
34
|
+
def save
|
35
|
+
ensure_no_extra_fields if self.respond_to?(:ensure_no_extra_fields)
|
36
|
+
|
37
|
+
if persisted?
|
38
|
+
data_model.update(self)
|
39
|
+
else
|
40
|
+
@id = data_model.store(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -103,24 +103,19 @@ shared_examples_for 'a mince interface' do
|
|
103
103
|
data2[:field_1] = 'value modified'
|
104
104
|
interface.replace(:some_collection, data2)
|
105
105
|
|
106
|
-
convert(interface.find(:some_collection,
|
106
|
+
convert(interface.find(:some_collection, 2))[:field_1].should == 'value modified'
|
107
107
|
end
|
108
108
|
|
109
109
|
it 'can update a field with a value on a specific record' do
|
110
110
|
interface.update_field_with_value(:some_collection, 3, :field_2, '10')
|
111
111
|
|
112
|
-
convert(interface.find(:some_collection,
|
112
|
+
convert(interface.find(:some_collection, 3))[:field_2].should == '10'
|
113
113
|
end
|
114
114
|
|
115
115
|
it 'can increment a field with a given amount for a specific field' do
|
116
116
|
interface.increment_field_by_amount(:some_collection, 1, :field_2, 3)
|
117
117
|
|
118
|
-
convert(interface.find(:some_collection,
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'can get one document' do
|
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)
|
118
|
+
convert(interface.find(:some_collection, 1))[:field_2].should == 6
|
124
119
|
end
|
125
120
|
|
126
121
|
it 'can clear the data store' do
|
@@ -145,18 +140,18 @@ shared_examples_for 'a mince interface' do
|
|
145
140
|
end
|
146
141
|
|
147
142
|
it 'can push a value to an array for a specific record' do
|
148
|
-
interface.push_to_array(:some_collection,
|
149
|
-
interface.push_to_array(:some_collection,
|
143
|
+
interface.push_to_array(:some_collection, 1, :field_3, 'add to existing array')
|
144
|
+
interface.push_to_array(:some_collection, 1, :new_field, 'add to new array')
|
150
145
|
|
151
|
-
record = convert(interface.find(:some_collection,
|
146
|
+
record = convert(interface.find(:some_collection, 1))
|
152
147
|
record[:field_3].should include('add to existing array')
|
153
148
|
record[:new_field].should == ['add to new array']
|
154
149
|
end
|
155
150
|
|
156
151
|
it 'can remove a value from an array for a specific record' do
|
157
|
-
interface.remove_from_array(:some_collection,
|
152
|
+
interface.remove_from_array(:some_collection, 1, :field_3, 2)
|
158
153
|
|
159
|
-
convert(interface.find(:some_collection,
|
154
|
+
convert(interface.find(:some_collection, 1))[:field_3].should_not include(2)
|
160
155
|
end
|
161
156
|
|
162
157
|
it 'can get all records that match a given set of keys and values' do
|
data/lib/mince/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'mince'
|
2
|
+
require 'hashy_db'
|
3
|
+
|
4
|
+
describe 'A mince data model integration spec' do
|
5
|
+
let(:attributes) { { brand: brand, price: price, type: type, color: color } }
|
6
|
+
let(:brand) { mock }
|
7
|
+
let(:price) { mock }
|
8
|
+
let(:type) { mock }
|
9
|
+
let(:color) { mock }
|
10
|
+
let(:primary_key) { Mince::HashyDb::Interface.primary_key}
|
11
|
+
|
12
|
+
let(:data_model_klass) do
|
13
|
+
Class.new do
|
14
|
+
include Mince::DataModel
|
15
|
+
|
16
|
+
data_collection :guitars
|
17
|
+
data_fields :brand, :price, :type, :color
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
before do
|
22
|
+
Mince::Config.interface = Mince::HashyDb::Interface
|
23
|
+
Mince::HashyDb::Interface.clear
|
24
|
+
end
|
25
|
+
|
26
|
+
after do
|
27
|
+
Mince::HashyDb::Interface.clear
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can insert data directly to the data collection' do
|
31
|
+
result = data_model_klass.add attributes
|
32
|
+
|
33
|
+
all = data_model_klass.all
|
34
|
+
all.size.should == 1
|
35
|
+
[all.first, result].each do |object|
|
36
|
+
%w(brand price type color).each do |field|
|
37
|
+
object[field.to_sym].should == attributes[field.to_sym]
|
38
|
+
end
|
39
|
+
object[primary_key].should_not be_nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'mince'
|
2
|
+
require 'hashy_db'
|
3
|
+
|
4
|
+
describe 'A mince model integration spec' do
|
5
|
+
subject { model_klass.new attributes }
|
6
|
+
|
7
|
+
let(:attributes) { { brand: brand, price: price, type: type, color: color } }
|
8
|
+
let(:brand) { mock }
|
9
|
+
let(:price) { mock }
|
10
|
+
let(:type) { mock }
|
11
|
+
let(:color) { mock }
|
12
|
+
|
13
|
+
before do
|
14
|
+
Mince::Config.interface = Mince::HashyDb::Interface
|
15
|
+
Mince::HashyDb::Interface.clear
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
Mince::HashyDb::Interface.clear
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'a model with a limited set of mince model mixins' do
|
23
|
+
let(:model_klass) do
|
24
|
+
Class.new do
|
25
|
+
include Mince::Model::Fields
|
26
|
+
|
27
|
+
data_model(
|
28
|
+
Class.new do
|
29
|
+
include Mince::DataModel
|
30
|
+
|
31
|
+
data_collection :guitars
|
32
|
+
data_fields :brand, :price, :type, :color
|
33
|
+
end
|
34
|
+
)
|
35
|
+
fields :brand, :price, :type, :color
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'is initialized with the correct data' do
|
40
|
+
subject.brand.should == brand
|
41
|
+
subject.price.should == price
|
42
|
+
subject.type.should == type
|
43
|
+
subject.color.should == color
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'cannot be persisted to the mince data interface' do
|
47
|
+
subject.respond_to?(:save).should be_false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'a model with all mince model mixins' do
|
52
|
+
let(:model_klass) do
|
53
|
+
Class.new do
|
54
|
+
include Mince::Model
|
55
|
+
|
56
|
+
data_model(
|
57
|
+
Class.new do
|
58
|
+
include Mince::DataModel
|
59
|
+
|
60
|
+
data_collection :guitars
|
61
|
+
data_fields :brand, :price, :type, :color
|
62
|
+
end
|
63
|
+
)
|
64
|
+
fields :brand, :price, :type, :color
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'is initialized with the correct data' do
|
69
|
+
subject.brand.should == brand
|
70
|
+
subject.price.should == price
|
71
|
+
subject.type.should == type
|
72
|
+
subject.color.should == color
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'can be persisted to the mince data interface' do
|
76
|
+
subject.save
|
77
|
+
|
78
|
+
raw_record = Mince::Config.interface.find(:guitars, subject.id)
|
79
|
+
model_record = model_klass.find(subject.id)
|
80
|
+
raw_record[:brand].should == brand
|
81
|
+
model_record.brand.should == brand
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when not all fields are defined in the data model' do
|
85
|
+
let(:model_klass) do
|
86
|
+
Class.new do
|
87
|
+
include Mince::Model
|
88
|
+
|
89
|
+
data_model(
|
90
|
+
Class.new do
|
91
|
+
include Mince::DataModel
|
92
|
+
|
93
|
+
data_collection :guitars
|
94
|
+
data_fields :brand, :price, :type
|
95
|
+
end
|
96
|
+
)
|
97
|
+
fields :brand, :price, :type, :color
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'raises an exception to provide feedback about the missing field' do
|
102
|
+
expect { subject.save }.to raise_exception(RuntimeError, "Tried to save a with fields not specified in : color")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
shared_examples_for 'a model using mince model data model' do
|
2
|
+
before do
|
3
|
+
begin
|
4
|
+
klass
|
5
|
+
rescue NameError
|
6
|
+
raise "You must define a `klass` spec variable to use the mince model finders shared example"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'can set and get the data model' do
|
11
|
+
data_model = mock 'data model'
|
12
|
+
klass.data_model(data_model)
|
13
|
+
klass.data_model.should == data_model
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
shared_examples_for 'a model using mince model finders' do
|
2
|
+
before do
|
3
|
+
begin
|
4
|
+
klass
|
5
|
+
rescue NameError
|
6
|
+
raise "You must define a `klass` spec variable to use the mince model finders shared example"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'getting all models' do
|
11
|
+
subject { klass.all }
|
12
|
+
|
13
|
+
let(:model) { mock }
|
14
|
+
let(:models) { [model] }
|
15
|
+
let(:data) { [datum] }
|
16
|
+
let(:datum) { mock }
|
17
|
+
|
18
|
+
before do
|
19
|
+
klass.data_model.stub(all: data)
|
20
|
+
klass.stub(:new).with(datum).and_return(model)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns an array of all models' do
|
24
|
+
subject.should == models
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'finding a model by id' do
|
29
|
+
subject { klass.find(id) }
|
30
|
+
|
31
|
+
let(:id) { mock 'id' }
|
32
|
+
|
33
|
+
before do
|
34
|
+
klass.data_model.stub(:find).with(id).and_return(data)
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when it exists' do
|
38
|
+
let(:data) { mock 'data' }
|
39
|
+
let(:model) { mock 'model' }
|
40
|
+
|
41
|
+
before do
|
42
|
+
klass.stub(:new).with(data).and_return(model)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns the model' do
|
46
|
+
subject.should == model
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when it does not exist' do
|
51
|
+
let(:data) { nil }
|
52
|
+
|
53
|
+
it 'returns nothing' do
|
54
|
+
subject.should be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -31,6 +31,21 @@ describe Mince::DataModel, 'Mixin' do
|
|
31
31
|
Mince::Config.stub(:interface => interface)
|
32
32
|
end
|
33
33
|
|
34
|
+
describe "inserting data" do
|
35
|
+
let(:expected_hash) { data_field_attributes.merge(primary_key => unique_id) }
|
36
|
+
|
37
|
+
before do
|
38
|
+
interface.stub(:add).and_return([expected_hash])
|
39
|
+
interface.stub(:generate_unique_id).with(data_field_attributes).and_return(unique_id)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'adds the data to the db store with the generated id' do
|
43
|
+
interface.should_receive(:add).with(collection_name, HashWithIndifferentAccess.new(expected_hash)).and_return([expected_hash])
|
44
|
+
|
45
|
+
described_class.add(data_field_attributes).should == expected_hash
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
34
49
|
describe "storing a data model" do
|
35
50
|
let(:model) { mock 'a model', instance_values: data_field_attributes }
|
36
51
|
|
@@ -53,7 +68,7 @@ describe Mince::DataModel, 'Mixin' do
|
|
53
68
|
|
54
69
|
it 'can delete the collection' do
|
55
70
|
interface.should_receive(:delete_collection).with(collection_name)
|
56
|
-
|
71
|
+
|
57
72
|
described_class.delete_collection
|
58
73
|
end
|
59
74
|
|
@@ -90,7 +105,7 @@ describe Mince::DataModel, 'Mixin' do
|
|
90
105
|
|
91
106
|
describe 'updating a specific field for a data model' do
|
92
107
|
let(:data_model_id) { '1234567' }
|
93
|
-
|
108
|
+
|
94
109
|
it 'has the data store update the field' do
|
95
110
|
interface.should_receive(:update_field_with_value).with(collection_name, data_model_id, :some_field, 'some value')
|
96
111
|
|
@@ -112,7 +127,7 @@ describe Mince::DataModel, 'Mixin' do
|
|
112
127
|
let(:data_model_id) { '1234567' }
|
113
128
|
|
114
129
|
it 'replaces the data model in the db store' do
|
115
|
-
interface.should_receive(:push_to_array).with(collection_name,
|
130
|
+
interface.should_receive(:push_to_array).with(collection_name, data_model_id, :array_field, 'some value')
|
116
131
|
|
117
132
|
described_class.push_to_array(data_model_id, :array_field, 'some value')
|
118
133
|
end
|
@@ -135,7 +150,7 @@ describe Mince::DataModel, 'Mixin' do
|
|
135
150
|
let(:data_model_id) { '1234567' }
|
136
151
|
|
137
152
|
it 'removes the value from the array' do
|
138
|
-
interface.should_receive(:remove_from_array).with(collection_name,
|
153
|
+
interface.should_receive(:remove_from_array).with(collection_name, data_model_id, :array_field, 'some value')
|
139
154
|
|
140
155
|
described_class.remove_from_array(data_model_id, :array_field, 'some value')
|
141
156
|
end
|
@@ -220,7 +235,7 @@ describe Mince::DataModel, 'Mixin' do
|
|
220
235
|
let(:data_model) { {primary_key => 'id', :id => 'id' } }
|
221
236
|
|
222
237
|
it 'returns the data model from the data store' do
|
223
|
-
interface.should_receive(:find).with(collection_name,
|
238
|
+
interface.should_receive(:find).with(collection_name, 'id').and_return(data_model)
|
224
239
|
|
225
240
|
described_class.find(data_model[:id]).should == HashWithIndifferentAccess.new(data_model)
|
226
241
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative '../../../../lib/mince/model/data_model'
|
2
|
+
require_relative '../../../support/shared_examples/model_data_model_example'
|
3
|
+
|
4
|
+
describe Mince::Model::DataModel do
|
5
|
+
let(:klass) do
|
6
|
+
Class.new do
|
7
|
+
include Mince::Model::DataModel
|
8
|
+
|
9
|
+
data_model Class.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it_behaves_like 'a model using mince model data model'
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative '../../../../lib/mince/model/finders'
|
2
|
+
require_relative '../../../support/shared_examples/model_finders_example'
|
3
|
+
|
4
|
+
describe Mince::Model::Finders do
|
5
|
+
let(:klass) do
|
6
|
+
Class.new do
|
7
|
+
include Mince::Model::Finders
|
8
|
+
|
9
|
+
data_model Class.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it_behaves_like 'a model using mince model finders'
|
14
|
+
end
|
@@ -94,6 +94,15 @@ describe Mince::Model do
|
|
94
94
|
end
|
95
95
|
|
96
96
|
describe "Query Methods:" do
|
97
|
+
it 'provides a way to easily create a new record' do
|
98
|
+
model = mock 'model'
|
99
|
+
data = mock 'data'
|
100
|
+
klass.stub(:new).with(data).and_return(model)
|
101
|
+
model.should_receive(:save)
|
102
|
+
|
103
|
+
klass.create(data)
|
104
|
+
end
|
105
|
+
|
97
106
|
describe 'finding a model by id' do
|
98
107
|
subject { klass.find(id) }
|
99
108
|
|
@@ -126,3 +135,4 @@ describe Mince::Model do
|
|
126
135
|
end
|
127
136
|
end
|
128
137
|
end
|
138
|
+
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mince
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.1
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Matt Simpson
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2013-02-26 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -145,17 +145,17 @@ dependencies:
|
|
145
145
|
requirement: !ruby/object:Gem::Requirement
|
146
146
|
none: false
|
147
147
|
requirements:
|
148
|
-
- -
|
148
|
+
- - ~>
|
149
149
|
- !ruby/object:Gem::Version
|
150
|
-
version: 2.0
|
150
|
+
version: '2.0'
|
151
151
|
type: :development
|
152
152
|
prerelease: false
|
153
153
|
version_requirements: !ruby/object:Gem::Requirement
|
154
154
|
none: false
|
155
155
|
requirements:
|
156
|
-
- -
|
156
|
+
- - ~>
|
157
157
|
- !ruby/object:Gem::Version
|
158
|
-
version: 2.0
|
158
|
+
version: '2.0'
|
159
159
|
- !ruby/object:Gem::Dependency
|
160
160
|
name: rb-fsevent
|
161
161
|
requirement: !ruby/object:Gem::Requirement
|
@@ -180,16 +180,25 @@ extensions: []
|
|
180
180
|
extra_rdoc_files: []
|
181
181
|
files:
|
182
182
|
- lib/mince.rb
|
183
|
-
- lib/mince/version.rb
|
184
183
|
- lib/mince/config.rb
|
185
184
|
- lib/mince/data_model.rb
|
186
185
|
- lib/mince/model.rb
|
186
|
+
- lib/mince/version.rb
|
187
187
|
- lib/mince/shared_examples/interface_example.rb
|
188
|
+
- lib/mince/model/data_model.rb
|
189
|
+
- lib/mince/model/fields.rb
|
190
|
+
- lib/mince/model/finders.rb
|
191
|
+
- lib/mince/model/persistence.rb
|
192
|
+
- spec/integration/mince_data_model_spec.rb
|
193
|
+
- spec/integration/mince_model_spec.rb
|
194
|
+
- spec/support/shared_examples/modeL_data_model_example.rb
|
195
|
+
- spec/support/shared_examples/model_finders_example.rb
|
196
|
+
- spec/units/mince/model/data_model_spec.rb
|
197
|
+
- spec/units/mince/model/finders_spec.rb
|
188
198
|
- spec/units/mince/config_spec.rb
|
189
199
|
- spec/units/mince/interface_example_spec.rb
|
190
200
|
- spec/units/mince/data_model_spec.rb
|
191
201
|
- spec/units/mince/model_spec.rb
|
192
|
-
- spec/integration/simple_mince_data_model_spec.rb
|
193
202
|
homepage: https://github.com/coffeencoke/mince
|
194
203
|
licenses: []
|
195
204
|
post_install_message:
|
@@ -205,19 +214,24 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
205
214
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
206
215
|
none: false
|
207
216
|
requirements:
|
208
|
-
- - ! '
|
217
|
+
- - ! '>='
|
209
218
|
- !ruby/object:Gem::Version
|
210
|
-
version:
|
219
|
+
version: '0'
|
211
220
|
requirements: []
|
212
221
|
rubyforge_project: mince
|
213
|
-
rubygems_version: 1.8.
|
222
|
+
rubygems_version: 1.8.25
|
214
223
|
signing_key:
|
215
224
|
specification_version: 3
|
216
225
|
summary: Library to interact with mince interfacing data libraries
|
217
226
|
test_files:
|
227
|
+
- spec/integration/mince_data_model_spec.rb
|
228
|
+
- spec/integration/mince_model_spec.rb
|
229
|
+
- spec/support/shared_examples/modeL_data_model_example.rb
|
230
|
+
- spec/support/shared_examples/model_finders_example.rb
|
231
|
+
- spec/units/mince/model/data_model_spec.rb
|
232
|
+
- spec/units/mince/model/finders_spec.rb
|
218
233
|
- spec/units/mince/config_spec.rb
|
219
234
|
- spec/units/mince/interface_example_spec.rb
|
220
235
|
- spec/units/mince/data_model_spec.rb
|
221
236
|
- spec/units/mince/model_spec.rb
|
222
|
-
- spec/integration/simple_mince_data_model_spec.rb
|
223
237
|
has_rdoc: true
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'mince'
|
2
|
-
require 'hashy_db'
|
3
|
-
|
4
|
-
describe 'A simple mince data model integration spec' do
|
5
|
-
before do
|
6
|
-
Mince::Config.interface = Mince::HashyDb::Interface
|
7
|
-
Mince::HashyDb::Interface.clear
|
8
|
-
end
|
9
|
-
|
10
|
-
after do
|
11
|
-
Mince::HashyDb::Interface.clear
|
12
|
-
end
|
13
|
-
|
14
|
-
describe 'a model' do
|
15
|
-
subject { model_klass.new attributes }
|
16
|
-
|
17
|
-
let(:model_klass) do
|
18
|
-
Class.new do
|
19
|
-
include Mince::Model
|
20
|
-
|
21
|
-
data_model(
|
22
|
-
Class.new do
|
23
|
-
include Mince::DataModel
|
24
|
-
|
25
|
-
data_collection :guitars
|
26
|
-
data_fields :brand, :price, :type, :color
|
27
|
-
end
|
28
|
-
)
|
29
|
-
fields :brand, :price, :type, :color
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
let(:attributes) { { brand: brand, price: price, type: type, color: color } }
|
34
|
-
let(:brand) { mock }
|
35
|
-
let(:price) { mock }
|
36
|
-
let(:type) { mock }
|
37
|
-
let(:color) { mock }
|
38
|
-
|
39
|
-
it 'is initialized with the correct data' do
|
40
|
-
subject.brand.should == brand
|
41
|
-
subject.price.should == price
|
42
|
-
subject.type.should == type
|
43
|
-
subject.color.should == color
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'can be persisted to the mince data interface' do
|
47
|
-
subject.save
|
48
|
-
|
49
|
-
raw_record = Mince::Config.interface.find(:guitars, Mince::HashyDb::Interface.primary_key, subject.id)
|
50
|
-
model_record = model_klass.find(subject.id)
|
51
|
-
raw_record[:brand].should == brand
|
52
|
-
model_record.brand.should == brand
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|