mince 2.0.0.pre.4 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|