mince 2.1.0 → 2.2.0
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.
- checksums.yaml +4 -4
- data/README.md +8 -1
- data/lib/mince/data_model.rb +56 -13
- data/lib/mince/data_model/timestamps.rb +60 -0
- data/lib/mince/model.rb +1 -1
- data/lib/mince/model/data_model.rb +5 -2
- data/lib/mince/model/finders.rb +25 -3
- data/lib/mince/version.rb +1 -1
- data/spec/integration/inferred_data_fields_spec.rb +62 -0
- data/spec/integration/mince_data_model_spec.rb +52 -0
- data/spec/integration/mince_model_spec.rb +1 -0
- data/spec/support/shared_examples/model_finders_example.rb +139 -8
- data/spec/units/mince/data_model/inferred_fields_spec.rb +79 -0
- data/spec/units/mince/data_model/timestamps_spec.rb +149 -0
- data/spec/units/mince/data_model_spec.rb +116 -23
- data/spec/units/mince/model_spec.rb +107 -66
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d691deea536ba372175b37a2c0172b19b444de75
|
4
|
+
data.tar.gz: 6273bb8e9b649e122f162ce7c95df290486cf961
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b840fbc99a80dc42e94767738877a0fa5fa71b9ae20fbe9bcda8bdb9e33b265dcabe7761c17d9d8180d68b0f8ecceca2f57168a4315743b2dc48b08ece356bb
|
7
|
+
data.tar.gz: 5eb6ee075cc5808d319a4689ba47740f711b82344c37394fa02f641d2eb2555f9055d95a23a02d2990608b27a084e5d151a89c4ba4bfc2d674298f5521ab1861
|
data/README.md
CHANGED
@@ -65,10 +65,11 @@ Link | Description
|
|
65
65
|
----|-----
|
66
66
|
[API Docs](http://rdoc.info/github/coffeencoke/mince/master/frames) | API docs for Mince
|
67
67
|
[Existing Interfaces](https://github.com/coffeencoke/mince/wiki/Existing-interfaces) | List of supported database interfaces that can be used with Mince
|
68
|
+
[Mince Migrator](http://coffeencoke.github.io/mince_migrator) | Database migrations library for Mince
|
68
69
|
[Usage with Rails](https://github.com/coffeencoke/mince/wiki/Usage-with-rails) | More information on how to use Mince with Rails
|
69
70
|
[Fancy Mixins](https://github.com/coffeencoke/mince/wiki/Fancy-mixins) | We've written a few mixins that provide some standard behavior to your models while using Mince
|
70
71
|
[Development](https://github.com/coffeencoke/mince/wiki/Development) | Help by contributing
|
71
|
-
[Mailing List](https://groups.google.com/forum/?fromgroups#!forum/mince_dev)
|
72
|
+
[Mailing List](https://groups.google.com/forum/?fromgroups#!forum/mince_dev) | Mailing list for Mince
|
72
73
|
[Travis CI](https://travis-ci.org/#!/coffeencoke/mince) | Check out the build status of Mince
|
73
74
|
[Why Multitier Architecture?](https://github.com/coffeencoke/mince/wiki/Why-multitier-architecture%3F) | Discussion about why to use multi tier architecture as apposed to others, such as Active Record
|
74
75
|
|
@@ -90,6 +91,12 @@ Helena Converse | [@n3rdgir1](https://twitter.com/n3rdgir1) | [@n3rdgir1](https:
|
|
90
91
|
|
91
92
|
If you've been missed on this list, let us know by creating an issue or sending one of us a message.
|
92
93
|
|
94
|
+
# License
|
95
|
+
|
96
|
+
Copyright 2013 Matt Simpson
|
97
|
+
|
98
|
+
MIT - view LICENSE.txt in the source for details
|
99
|
+
|
93
100
|
# Version 1
|
94
101
|
|
95
102
|
Looking for support for Version 1? Put an issue in, or send one of us a message.
|
data/lib/mince/data_model.rb
CHANGED
@@ -33,6 +33,8 @@ module Mince # :nodoc:
|
|
33
33
|
#
|
34
34
|
# View the docs for each method available
|
35
35
|
module DataModel
|
36
|
+
require_relative 'data_model/timestamps'
|
37
|
+
|
36
38
|
extend ActiveSupport::Concern
|
37
39
|
|
38
40
|
included do
|
@@ -48,6 +50,16 @@ module Mince # :nodoc:
|
|
48
50
|
Config.interface
|
49
51
|
end
|
50
52
|
|
53
|
+
# Allows models to define fields without the data model needing to define them also
|
54
|
+
def infer_fields_from_model
|
55
|
+
@infer_fields_from_model = true
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns true if the data model is infering fields from the model
|
59
|
+
def infer_fields?
|
60
|
+
!!@infer_fields_from_model
|
61
|
+
end
|
62
|
+
|
51
63
|
# Sets what data fields to accept
|
52
64
|
# Returns the fields
|
53
65
|
#
|
@@ -57,6 +69,7 @@ module Mince # :nodoc:
|
|
57
69
|
# @param [*Array] *fields an array of fields to add to the data model
|
58
70
|
# @returns [Array] the fields defined for the data model
|
59
71
|
def data_fields(*fields)
|
72
|
+
@data_fields ||= []
|
60
73
|
create_data_fields(*fields) if fields.any?
|
61
74
|
@data_fields
|
62
75
|
end
|
@@ -76,9 +89,7 @@ module Mince # :nodoc:
|
|
76
89
|
# @param [Class] model a ruby class instance object to store into the data store
|
77
90
|
# @returns [id] returns the id of the newly stored object
|
78
91
|
def store(model)
|
79
|
-
new(model).
|
80
|
-
p.add_to_interface
|
81
|
-
end.id
|
92
|
+
new(model).create
|
82
93
|
end
|
83
94
|
|
84
95
|
# Updates the given model in the data store with the fields and values in model
|
@@ -87,9 +98,7 @@ module Mince # :nodoc:
|
|
87
98
|
#
|
88
99
|
# @param [Class] model a ruby class instance object to store into the data store
|
89
100
|
def update(model)
|
90
|
-
new(model).
|
91
|
-
p.replace_in_interface
|
92
|
-
end
|
101
|
+
new(model).update
|
93
102
|
end
|
94
103
|
|
95
104
|
# Updates a specific field with a value for the record with the given id
|
@@ -99,6 +108,7 @@ module Mince # :nodoc:
|
|
99
108
|
# @param [*] value the value to update the field with
|
100
109
|
def update_field_with_value(id, field, value)
|
101
110
|
interface.update_field_with_value(data_collection, id, field, value)
|
111
|
+
set_update_timestamp_for(data_collection, id) if timestamps?
|
102
112
|
end
|
103
113
|
|
104
114
|
# Increments a field by a given amount
|
@@ -111,6 +121,7 @@ module Mince # :nodoc:
|
|
111
121
|
# @param [Numeric] amount the amount to increment or decrement the field with
|
112
122
|
def increment_field_by_amount(id, field, amount)
|
113
123
|
interface.increment_field_by_amount(data_collection, id, field, amount)
|
124
|
+
set_update_timestamp_for(data_collection, id) if timestamps?
|
114
125
|
end
|
115
126
|
|
116
127
|
# Removes a value from an field that is an array
|
@@ -120,6 +131,7 @@ module Mince # :nodoc:
|
|
120
131
|
# @param [*] value the value to update the field with
|
121
132
|
def remove_from_array(id, field, value)
|
122
133
|
interface.remove_from_array(data_collection, id, field, value)
|
134
|
+
set_update_timestamp_for(data_collection, id) if timestamps?
|
123
135
|
end
|
124
136
|
|
125
137
|
# Pushes a value to an array field
|
@@ -129,6 +141,7 @@ module Mince # :nodoc:
|
|
129
141
|
# @param [*] value the value to update the field with
|
130
142
|
def push_to_array(id, field, value)
|
131
143
|
interface.push_to_array(data_collection, id, field, value)
|
144
|
+
set_update_timestamp_for(data_collection, id) if timestamps?
|
132
145
|
end
|
133
146
|
|
134
147
|
# Returns a record that has the given id
|
@@ -236,9 +249,15 @@ module Mince # :nodoc:
|
|
236
249
|
# @returns [Hash] the data that was added to the data collection, including the uniquely generated id
|
237
250
|
def add(hash)
|
238
251
|
hash = HashWithIndifferentAccess.new(hash.merge(primary_key => generate_unique_id(hash)))
|
252
|
+
add_timestamps_to_hash(hash) if timestamps?
|
239
253
|
interface.add(data_collection, hash).first
|
240
254
|
end
|
241
255
|
|
256
|
+
# Returns true if the data model uses the Timestamps mixin
|
257
|
+
def timestamps?
|
258
|
+
include? Mince::DataModel::Timestamps
|
259
|
+
end
|
260
|
+
|
242
261
|
def translate_from_interface(hash)
|
243
262
|
if hash
|
244
263
|
hash["id"] = hash[primary_key] if hash[primary_key] && (primary_key != :id || primary_key != 'id')
|
@@ -250,19 +269,19 @@ module Mince # :nodoc:
|
|
250
269
|
data.collect {|d| translate_from_interface(d) }
|
251
270
|
end
|
252
271
|
|
253
|
-
private
|
254
|
-
|
255
272
|
def primary_key
|
256
273
|
@primary_key ||= interface.primary_key
|
257
274
|
end
|
258
275
|
|
276
|
+
private
|
277
|
+
|
259
278
|
def set_data_collection(collection_name)
|
260
279
|
@data_collection = collection_name
|
261
280
|
end
|
262
281
|
|
263
282
|
def create_data_fields(*fields)
|
264
283
|
attr_accessor *fields
|
265
|
-
@data_fields
|
284
|
+
@data_fields += fields
|
266
285
|
end
|
267
286
|
end
|
268
287
|
|
@@ -272,14 +291,36 @@ module Mince # :nodoc:
|
|
272
291
|
set_id
|
273
292
|
end
|
274
293
|
|
294
|
+
def create
|
295
|
+
update_timestamps if timestamps?
|
296
|
+
add_to_interface
|
297
|
+
id
|
298
|
+
end
|
299
|
+
|
300
|
+
def update
|
301
|
+
update_timestamps if timestamps?
|
302
|
+
replace_in_interface
|
303
|
+
end
|
304
|
+
|
305
|
+
def timestamps?
|
306
|
+
self.class.timestamps?
|
307
|
+
end
|
308
|
+
|
275
309
|
def interface
|
276
310
|
self.class.interface
|
277
311
|
end
|
278
312
|
|
279
313
|
def data_fields
|
314
|
+
if infer_fields?
|
315
|
+
self.class.data_fields *model.fields
|
316
|
+
end
|
280
317
|
self.class.data_fields
|
281
318
|
end
|
282
319
|
|
320
|
+
def infer_fields?
|
321
|
+
self.class.infer_fields?
|
322
|
+
end
|
323
|
+
|
283
324
|
def data_collection
|
284
325
|
self.class.data_collection
|
285
326
|
end
|
@@ -292,12 +333,14 @@ module Mince # :nodoc:
|
|
292
333
|
interface.replace(data_collection, attributes)
|
293
334
|
end
|
294
335
|
|
295
|
-
private
|
296
|
-
|
297
336
|
def attributes
|
298
|
-
model_instance_values.merge(primary_key => id)
|
337
|
+
model_instance_values.merge(primary_key => id).tap do |hash|
|
338
|
+
hash.merge!(timestamp_attributes) if timestamps?
|
339
|
+
end
|
299
340
|
end
|
300
341
|
|
342
|
+
private
|
343
|
+
|
301
344
|
def model_instance_values
|
302
345
|
HashWithIndifferentAccess.new(model.instance_values).slice(*data_fields)
|
303
346
|
end
|
@@ -319,7 +362,7 @@ module Mince # :nodoc:
|
|
319
362
|
end
|
320
363
|
|
321
364
|
def primary_key
|
322
|
-
|
365
|
+
self.class.primary_key
|
323
366
|
end
|
324
367
|
|
325
368
|
def set_data_field_value(field)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Mince
|
2
|
+
module DataModel
|
3
|
+
require 'active_support/concern'
|
4
|
+
require_relative '../data_model'
|
5
|
+
|
6
|
+
# = Timestamps
|
7
|
+
#
|
8
|
+
# Timestamps can be mixed into your DataModel classes in order to provide with fields
|
9
|
+
# to store when records are created and updated.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
# require 'mince/data_model'
|
13
|
+
#
|
14
|
+
# Class UserDataModel
|
15
|
+
# include Mince::DataModel
|
16
|
+
# include Mince::DataModel::Timestamps
|
17
|
+
#
|
18
|
+
# data_collection :users
|
19
|
+
# data_fields :username
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# UserDataModel.add username: 'coffeencoke'
|
23
|
+
# data_model = UserDataModel.find_by_field :username, 'coffeencoke'
|
24
|
+
# data_model.created_at # => todo: returns date time in utc
|
25
|
+
# data_model.updated_at # => todo: returns date time in utc
|
26
|
+
#
|
27
|
+
# Whenever a database persisting message is called for a record, the updated_at
|
28
|
+
# timestamp will be updated.
|
29
|
+
module Timestamps
|
30
|
+
include Mince::DataModel
|
31
|
+
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
|
34
|
+
included do
|
35
|
+
data_fields :created_at, :updated_at
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods # :nodoc:
|
39
|
+
def add_timestamps_to_hash(hash)
|
40
|
+
now = Time.now.utc
|
41
|
+
hash.merge!(created_at: now, updated_at: now)
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_update_timestamp_for(data_collection, id)
|
45
|
+
interface.update_field_with_value(data_collection, id, :updated_at, Time.now.utc)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_timestamps
|
50
|
+
now = Time.now.utc
|
51
|
+
self.created_at = now unless created_at
|
52
|
+
self.updated_at = now
|
53
|
+
end
|
54
|
+
|
55
|
+
def timestamp_attributes
|
56
|
+
{ created_at: created_at, updated_at: updated_at }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/mince/model.rb
CHANGED
@@ -33,7 +33,7 @@ module Mince
|
|
33
33
|
#
|
34
34
|
# By including this module, you are including DataModel, Fields, Persistence, and Finders.
|
35
35
|
#
|
36
|
-
# However, you can choose which modules you would like by including those modules individually
|
36
|
+
# However, you can choose which modules you would like by including those modules individually
|
37
37
|
# so that you can have flexibility an lighter weight help to implement your models.
|
38
38
|
#
|
39
39
|
# class AvailableBook
|
@@ -21,11 +21,14 @@ module Mince
|
|
21
21
|
# Not sure if this is where this method should live, it requires both
|
22
22
|
# Mince::Model::DataModel and Mince::Model::Fields.
|
23
23
|
def ensure_no_extra_fields
|
24
|
-
|
25
|
-
if extra_fields.any?
|
24
|
+
if !data_model.infer_fields? && extra_fields.any?
|
26
25
|
raise "Tried to save a #{self.class.name} with fields not specified in #{data_model.name}: #{extra_fields.join(', ')}"
|
27
26
|
end
|
28
27
|
end
|
28
|
+
|
29
|
+
def extra_fields
|
30
|
+
@extra_fields ||= (fields - data_model.data_fields)
|
31
|
+
end
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
data/lib/mince/model/finders.rb
CHANGED
@@ -11,9 +11,31 @@ module Mince
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
#
|
15
|
-
def
|
16
|
-
|
14
|
+
# Finds a model for the given field value pair
|
15
|
+
def find_by_field(field, value)
|
16
|
+
d = data_model.find_by_field(field, value)
|
17
|
+
new d if d
|
18
|
+
end
|
19
|
+
|
20
|
+
# Finds a model for the given hash
|
21
|
+
def find_by_fields(hash)
|
22
|
+
d = data_model.find_by_fields(hash)
|
23
|
+
new d if d
|
24
|
+
end
|
25
|
+
|
26
|
+
# Finds all fields that match the given field value pair
|
27
|
+
def all_by_field(field, value)
|
28
|
+
data_model.all_by_field(field, value).map{|a| new(a) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Finds all fields that match the given hash
|
32
|
+
def all_by_fields(hash)
|
33
|
+
data_model.all_by_fields(hash).map{|a| new(a) }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Finds or initializes a model for the given hash
|
37
|
+
def find_or_initialize_by(hash)
|
38
|
+
find_by_fields(hash) || new(hash)
|
17
39
|
end
|
18
40
|
|
19
41
|
# Returns all models from the data model
|
data/lib/mince/version.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'debugger'
|
2
|
+
require 'mince'
|
3
|
+
require 'hashy_db'
|
4
|
+
require 'active_support/core_ext/numeric/time'
|
5
|
+
|
6
|
+
describe 'A data model with inferred fields' do
|
7
|
+
subject { model_klass.new attributes }
|
8
|
+
|
9
|
+
let(:attributes) { { brand: brand, price: price, type: type, color: color } }
|
10
|
+
let(:brand) { mock }
|
11
|
+
let(:price) { mock }
|
12
|
+
let(:type) { mock }
|
13
|
+
let(:color) { mock }
|
14
|
+
|
15
|
+
before do
|
16
|
+
Mince::Config.interface = Mince::HashyDb::Interface
|
17
|
+
Mince::HashyDb::Interface.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
after do
|
21
|
+
Mince::HashyDb::Interface.clear
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'a model with a limited set of mince model mixins' do
|
25
|
+
let(:model_klass) do
|
26
|
+
Class.new do
|
27
|
+
include Mince::Model
|
28
|
+
|
29
|
+
data_model(
|
30
|
+
Class.new do
|
31
|
+
include Mince::DataModel
|
32
|
+
|
33
|
+
data_collection :guitars
|
34
|
+
infer_fields_from_model
|
35
|
+
end
|
36
|
+
)
|
37
|
+
field :brand, assignable: true
|
38
|
+
field :price, assignable: true
|
39
|
+
field :type, assignable: true
|
40
|
+
field :color, assignable: true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'is initialized with the correct data' do
|
45
|
+
subject.brand.should == brand
|
46
|
+
subject.price.should == price
|
47
|
+
subject.type.should == type
|
48
|
+
subject.color.should == color
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'persists the data when saved' do
|
52
|
+
subject.save
|
53
|
+
|
54
|
+
all = model_klass.all
|
55
|
+
all.size.should == 1
|
56
|
+
all.first.brand.should == brand
|
57
|
+
all.first.price.should == price
|
58
|
+
all.first.type.should == type
|
59
|
+
all.first.color.should == color
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -39,4 +39,56 @@ describe 'A mince data model integration spec' do
|
|
39
39
|
object[primary_key].should_not be_nil
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
describe 'a mince data model with timestamps' do
|
44
|
+
subject { data_model_klass.new brand: mock }
|
45
|
+
|
46
|
+
let(:data_model_klass) do
|
47
|
+
Class.new do
|
48
|
+
include Mince::DataModel
|
49
|
+
include Mince::DataModel::Timestamps
|
50
|
+
|
51
|
+
data_collection :guitars
|
52
|
+
data_fields :brand
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'provides a data field for the timestamps' do
|
57
|
+
subject.created_at.should be_nil
|
58
|
+
subject.updated_at.should be_nil
|
59
|
+
subject.data_fields.should =~ [:brand, :updated_at, :created_at]
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when the record is created' do
|
63
|
+
let(:persisted_data_model) { data_model_klass.find_by_field :brand, brand }
|
64
|
+
let(:brand) { 'Gibson' }
|
65
|
+
|
66
|
+
before do
|
67
|
+
data_model_klass.add brand: brand
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'sets the created at timestamp' do
|
71
|
+
(persisted_data_model[:created_at] > 10.seconds.ago.utc && persisted_data_model[:created_at] < 10.seconds.from_now.utc).should be_true
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'sets the updated at timestamp' do
|
75
|
+
(persisted_data_model[:updated_at] > 10.seconds.ago.utc && persisted_data_model[:updated_at] < 10.seconds.from_now.utc).should be_true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when the record is updated' do
|
80
|
+
let(:persisted_data_model) { data_model_klass.find_by_field :brand, brand }
|
81
|
+
let(:brand) { 'Gibson' }
|
82
|
+
|
83
|
+
before do
|
84
|
+
data_model_klass.add brand: brand
|
85
|
+
data_model_klass.update_field_with_value persisted_data_model[:id], :updated_at, (Time.now - 10000).utc
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'updates the updated at timestamp' do
|
89
|
+
persisted_data_model = data_model_klass.find_by_field(:brand, brand)
|
90
|
+
(persisted_data_model[:updated_at] > 10.seconds.ago.utc && persisted_data_model[:updated_at] < 10.seconds.from_now.utc).should be_true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
42
94
|
end
|