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.
@@ -1,5 +1,6 @@
1
1
  require 'mince'
2
2
  require 'hashy_db'
3
+ require 'active_support/core_ext/numeric/time'
3
4
 
4
5
  describe 'A mince model integration spec' do
5
6
  subject { model_klass.new attributes }
@@ -7,6 +7,34 @@ shared_examples_for 'a model using mince model finders' do
7
7
  end
8
8
  end
9
9
 
10
+ describe 'finding by one field' do
11
+ subject { klass.find_by_field(field, value) }
12
+
13
+ let(:field) { mock }
14
+ let(:value) { mock }
15
+
16
+ before do
17
+ klass.data_model.stub(:find_by_field).with(field, value).and_return(data)
18
+ end
19
+
20
+ context 'when a record exists' do
21
+ let(:data) { mock }
22
+ let(:model) { mock }
23
+
24
+ before do
25
+ klass.stub(:new).with(data).and_return(model)
26
+ end
27
+
28
+ it { should == model }
29
+ end
30
+
31
+ context 'when a record does not exist' do
32
+ let(:data) { nil }
33
+
34
+ it { should be_nil }
35
+ end
36
+ end
37
+
10
38
  describe 'getting all models' do
11
39
  subject { klass.all }
12
40
 
@@ -20,8 +48,115 @@ shared_examples_for 'a model using mince model finders' do
20
48
  klass.stub(:new).with(datum).and_return(model)
21
49
  end
22
50
 
23
- it 'returns an array of all models' do
24
- subject.should == models
51
+ it { should == models }
52
+ end
53
+
54
+ describe 'finding all by fields by a given hash' do
55
+ subject { klass.all_by_fields(hash) }
56
+
57
+ let(:hash) { mock }
58
+
59
+ before do
60
+ klass.data_model.stub(:all_by_fields).with(hash).and_return(data)
61
+ end
62
+
63
+ context 'when a record exists' do
64
+ let(:data) { [mock] }
65
+ let(:model) { mock }
66
+
67
+ before do
68
+ klass.stub(:new).with(data.first).and_return(model)
69
+ end
70
+
71
+ it { should == [model] }
72
+ end
73
+
74
+ context 'when a record does not exist' do
75
+ let(:data) { [] }
76
+
77
+ it { should be_empty }
78
+ end
79
+ end
80
+
81
+ describe 'finding all by fields by a given field value pair' do
82
+ subject { klass.all_by_field(field, value) }
83
+
84
+ let(:field) { mock }
85
+ let(:value) { mock }
86
+
87
+ before do
88
+ klass.data_model.stub(:all_by_field).with(field, value).and_return(data)
89
+ end
90
+
91
+ context 'when a record exists' do
92
+ let(:data) { [mock] }
93
+ let(:model) { mock }
94
+
95
+ before do
96
+ klass.stub(:new).with(data.first).and_return(model)
97
+ end
98
+
99
+ it { should == [model] }
100
+ end
101
+
102
+ context 'when a record does not exist' do
103
+ let(:data) { [] }
104
+
105
+ it { should be_empty }
106
+ end
107
+ end
108
+
109
+ describe 'finding by fields' do
110
+ subject { klass.find_by_fields(hash) }
111
+
112
+ let(:hash) { mock }
113
+
114
+ before do
115
+ klass.data_model.stub(:find_by_fields).with(hash).and_return(data)
116
+ end
117
+
118
+ context 'when a record exists' do
119
+ let(:data) { mock }
120
+ let(:model) { mock }
121
+
122
+ before do
123
+ klass.stub(:new).with(data).and_return(model)
124
+ end
125
+
126
+ it { should == model }
127
+ end
128
+
129
+ context 'when a record does not exist' do
130
+ let(:data) { nil }
131
+
132
+ it { should be_nil }
133
+ end
134
+ end
135
+
136
+ describe 'finding or initializing by fields' do
137
+ subject { klass.find_or_initialize_by(hash) }
138
+
139
+ let(:hash) { mock }
140
+
141
+ before do
142
+ klass.stub(:find_by_fields).with(hash).and_return(model)
143
+ end
144
+
145
+ context 'when a record exists' do
146
+ let(:model) { mock }
147
+
148
+ it { should == model }
149
+ end
150
+
151
+ context 'when a record does not exist' do
152
+ let(:model) { nil }
153
+ let(:new_model) { mock }
154
+
155
+ before do
156
+ klass.stub(:new).with(hash).and_return(new_model)
157
+ end
158
+
159
+ it { should == new_model }
25
160
  end
26
161
  end
27
162
 
@@ -42,17 +177,13 @@ shared_examples_for 'a model using mince model finders' do
42
177
  klass.stub(:new).with(data).and_return(model)
43
178
  end
44
179
 
45
- it 'returns the model' do
46
- subject.should == model
47
- end
180
+ it { should == model }
48
181
  end
49
182
 
50
183
  context 'when it does not exist' do
51
184
  let(:data) { nil }
52
185
 
53
- it 'returns nothing' do
54
- subject.should be_nil
55
- end
186
+ it { should be_nil }
56
187
  end
57
188
  end
58
189
  end
@@ -0,0 +1,79 @@
1
+ require_relative '../../../../lib/mince/data_model'
2
+
3
+ module Mince
4
+ describe DataModel do
5
+ subject { klass.new(model) }
6
+
7
+ let(:model) { mock fields: fields}
8
+ let(:fields) { [:username, :email] }
9
+ let(:klass) do
10
+ Class.new do
11
+ include Mince::DataModel
12
+
13
+ data_collection :users
14
+ end
15
+ end
16
+ let(:id) { mock }
17
+
18
+ before do
19
+ klass.stub(:generate_unique_id).with(model).and_return(id)
20
+ end
21
+
22
+ context 'when the data model is infering fields' do
23
+ let(:primary_key) { mock }
24
+ let(:interface) { mock }
25
+ let(:model_instance_values) { { username: 'coffeencoke' } }
26
+
27
+ before do
28
+ klass.stub(infer_fields?: true, interface: interface, primary_key: primary_key)
29
+ model.stub(instance_values: model_instance_values)
30
+ end
31
+
32
+ its(:infer_fields?) { should be_true }
33
+
34
+ it 'can store the model' do
35
+ expected_attributes = HashWithIndifferentAccess.new(model_instance_values.merge(primary_key => id))
36
+ interface.should_receive(:add).with(klass.data_collection, expected_attributes)
37
+
38
+ subject.create.should == id
39
+ end
40
+ end
41
+
42
+ context 'when the data model is not infering fields' do
43
+ before do
44
+ klass.stub(infer_fields?: false)
45
+ end
46
+
47
+ its(:infer_fields?) { should be_false }
48
+ end
49
+ end
50
+
51
+ describe DataModel, 'Class Methods:' do
52
+ subject { klass }
53
+
54
+ context 'when the data model is infering fields' do
55
+ let(:klass) do
56
+ Class.new do
57
+ include Mince::DataModel
58
+
59
+ data_collection :users
60
+ infer_fields_from_model
61
+ end
62
+ end
63
+
64
+ its(:infer_fields?) { should be_true }
65
+ end
66
+
67
+ context 'when the data model is not infering fields' do
68
+ let(:klass) do
69
+ Class.new do
70
+ include Mince::DataModel
71
+
72
+ data_collection :users
73
+ end
74
+ end
75
+
76
+ its(:infer_fields?) { should be_false }
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,149 @@
1
+ require_relative '../../../../lib/mince/data_model/timestamps'
2
+
3
+ module Mince::DataModel
4
+ describe Timestamps, 'Mixin' do
5
+ let(:klass) do
6
+ Class.new do
7
+ include Mince::DataModel
8
+ include Timestamps
9
+
10
+ data_collection :users
11
+ data_fields :username, :emails
12
+ end
13
+ end
14
+ let(:utc_now) { mock 'now' }
15
+ let(:interface) { mock 'interface' }
16
+ let(:data_collection) { mock 'collection name' }
17
+ let(:id) { '1' }
18
+ let(:primary_key) { :id }
19
+
20
+ before do
21
+ Time.stub_chain('now.utc' => utc_now)
22
+ klass.stub(
23
+ interface: interface,
24
+ data_collection: data_collection,
25
+ primary_key: primary_key,
26
+ generate_unique_id: id
27
+ ) # because it's mixed in
28
+ end
29
+
30
+ describe 'adding a record from a hash' do
31
+ let(:hash_to_add) { { username: 'joe' } }
32
+ let(:expected_hash) do
33
+ HashWithIndifferentAccess.new(
34
+ hash_to_add.merge(
35
+ primary_key => id,
36
+ created_at: utc_now,
37
+ updated_at: utc_now
38
+ )
39
+ )
40
+ end
41
+
42
+ before do
43
+ interface.stub(:add).with(data_collection, expected_hash).and_return([expected_hash])
44
+ end
45
+
46
+ it 'sets the created and updated values' do
47
+ klass.add(hash_to_add).should == expected_hash
48
+ end
49
+ end
50
+
51
+ describe 'adding a record from a model' do
52
+ subject { klass.new(model) }
53
+ let(:model) { mock 'model', instance_values: instance_values }
54
+ let(:instance_values) { HashWithIndifferentAccess.new username: "joe", emails: ["joedawg@test.com"] }
55
+ let(:attributes_plus_timestamps) { instance_values.merge(created_at: utc_now, updated_at: utc_now, primary_key => id) }
56
+
57
+ before do
58
+ interface.stub(:add)
59
+ end
60
+
61
+ it 'sets the created value' do
62
+ subject.create
63
+
64
+ subject.attributes.should == attributes_plus_timestamps
65
+ end
66
+ end
67
+
68
+ describe 'updating a record' do
69
+ subject { klass.new(model) }
70
+ let(:model) { mock 'model', instance_values.merge(instance_values: instance_values) }
71
+ let(:instance_values) { HashWithIndifferentAccess.new username: "joe", emails: ["joedawg@test.com"], updated_at: 'old value', created_at: 'created time', primary_key => id }
72
+ let(:attributes_plus_timestamp) { instance_values.merge(updated_at: utc_now) }
73
+
74
+ before do
75
+ interface.stub(:replace)
76
+ end
77
+
78
+ it 'sets the updated value' do
79
+ subject.update
80
+
81
+ subject.attributes.should == attributes_plus_timestamp
82
+ end
83
+ end
84
+
85
+ describe 'updating a single field' do
86
+ subject { klass.new(model) }
87
+ let(:field_to_update) { :username }
88
+ let(:new_value) { mock 'new value' }
89
+
90
+ before do
91
+ interface.stub(:update_field_with_value).with(data_collection, id, field_to_update, new_value)
92
+ end
93
+
94
+ it 'sets the updated value' do
95
+ interface.should_receive(:update_field_with_value).with(data_collection, id, :updated_at, utc_now)
96
+
97
+ klass.update_field_with_value(id, field_to_update, new_value)
98
+ end
99
+ end
100
+
101
+ describe 'incrementing a single field' do
102
+ subject { klass.new(model) }
103
+ let(:field_to_update) { :username }
104
+ let(:amount) { mock :amount }
105
+
106
+ before do
107
+ interface.stub(:increment_field_by_amount).with(data_collection, id, field_to_update, amount)
108
+ end
109
+
110
+ it 'sets the updated value' do
111
+ interface.should_receive(:update_field_with_value).with(data_collection, id, :updated_at, utc_now)
112
+
113
+ klass.increment_field_by_amount(id, field_to_update, amount)
114
+ end
115
+ end
116
+
117
+ describe 'adding a value to an array field' do
118
+ subject { klass.new(model) }
119
+ let(:field_to_update) { :username }
120
+ let(:value) { mock :value }
121
+
122
+ before do
123
+ interface.stub(:push_to_array).with(data_collection, id, field_to_update, value)
124
+ end
125
+
126
+ it 'sets the updated value' do
127
+ interface.should_receive(:update_field_with_value).with(data_collection, id, :updated_at, utc_now)
128
+
129
+ klass.push_to_array(id, field_to_update, value)
130
+ end
131
+ end
132
+
133
+ describe 'removing a value from an array field' do
134
+ subject { klass.new(model) }
135
+ let(:field_to_update) { :username }
136
+ let(:value) { mock :value }
137
+
138
+ before do
139
+ interface.stub(:remove_from_array).with(data_collection, id, field_to_update, value)
140
+ end
141
+
142
+ it 'sets the updated value' do
143
+ interface.should_receive(:update_field_with_value).with(data_collection, id, :updated_at, utc_now)
144
+
145
+ klass.remove_from_array(id, field_to_update, value)
146
+ end
147
+ end
148
+ end
149
+ end
@@ -1,7 +1,80 @@
1
1
  require_relative '../../../lib/mince/data_model'
2
2
  require 'digest'
3
3
 
4
- describe Mince::DataModel, 'Mixin' do
4
+ describe Mince::DataModel do
5
+ let(:klass_object){ described_class.new(model) }
6
+
7
+ let(:described_class) { klass }
8
+ let(:klass) do
9
+ Class.new do
10
+ include Mince::DataModel
11
+
12
+ data_collection :guitars
13
+ data_fields :brand, :price
14
+ data_fields :type, :color
15
+ end
16
+ end
17
+
18
+ let(:data_collection) { :guitars }
19
+ let(:data_field_attributes) do
20
+ {
21
+ brand: 'a brand everyone knows',
22
+ price: 'a price you save up for',
23
+ type: 'the kind you want',
24
+ color: 'should be your favorite'
25
+ }
26
+ end
27
+ let(:model) { mock instance_values: data_field_attributes }
28
+
29
+ let(:interface) { mock 'mince data interface class', generate_unique_id: unique_id, primary_key: primary_key }
30
+ let(:unique_id) { mock 'id' }
31
+ let(:primary_key) { "custom_id" }
32
+
33
+ before do
34
+ Mince::Config.stub(:interface => interface)
35
+ end
36
+
37
+ describe 'creating' do
38
+ let(:attributes_with_id) { attributes.merge(primary_key => unique_id) }
39
+ let(:attributes) { HashWithIndifferentAccess.new data_field_attributes }
40
+
41
+ before do
42
+ interface.stub(:add)
43
+ end
44
+
45
+ it 'adds the data model to the db store' do
46
+ interface.should_receive(:add).with(data_collection, attributes_with_id)
47
+
48
+ klass_object.create
49
+ end
50
+
51
+ it 'generates a unique id' do
52
+ interface.should_receive(:generate_unique_id).with(model).and_return(unique_id)
53
+
54
+ klass_object.create
55
+ end
56
+
57
+ it 'returns the id' do
58
+ klass_object.create.should == unique_id
59
+ end
60
+ end
61
+
62
+ describe 'updating' do
63
+ let(:attributes) { HashWithIndifferentAccess.new data_field_attributes }
64
+
65
+ before do
66
+ data_field_attributes.merge!(primary_key => unique_id)
67
+ end
68
+
69
+ it 'replaces all data in the data collection with the current state of the model' do
70
+ interface.should_receive(:replace).with(data_collection, attributes)
71
+
72
+ klass_object.update
73
+ end
74
+ end
75
+ end
76
+
77
+ describe Mince::DataModel, 'Class Methods' do
5
78
  let(:described_class) { klass }
6
79
 
7
80
  let(:collection_name) { :guitars }
@@ -19,50 +92,67 @@ describe Mince::DataModel, 'Mixin' do
19
92
  include Mince::DataModel
20
93
 
21
94
  data_collection :guitars
22
- data_fields :brand, :price, :type, :color
95
+ data_fields :brand, :price
96
+ data_fields :type, :color
23
97
  end
24
98
  end
25
99
 
26
100
  let(:interface) { mock 'mince data interface class', generate_unique_id: unique_id, primary_key: primary_key }
27
101
  let(:unique_id) { mock 'id' }
28
102
  let(:primary_key) { "custom_id" }
103
+ let(:fields) { [:brand, :type, :color, :price] }
29
104
 
30
105
  before do
31
106
  Mince::Config.stub(:interface => interface)
32
107
  end
33
108
 
109
+ it 'knows when timestamps are not included' do
110
+ klass.timestamps?.should be_false
111
+ end
112
+
113
+ it 'knows when timestamps are included' do
114
+ klass.send(:include, Mince::DataModel::Timestamps)
115
+
116
+ klass.timestamps?.should be_true
117
+ end
118
+
119
+ it 'knows the fields it has' do
120
+ klass.data_fields.should =~ fields
121
+ end
122
+
123
+ it 'does not duplicate data fields' do
124
+ pending
125
+ klass.data_fields(:brand)
126
+
127
+ klass.data_fields.should =~ fields
128
+ end
129
+
34
130
  describe "inserting data" do
35
- let(:expected_hash) { data_field_attributes.merge(primary_key => unique_id) }
131
+ let(:expected_hash) { HashWithIndifferentAccess.new(data_field_attributes.merge(primary_key => unique_id)) }
36
132
 
37
133
  before do
38
- interface.stub(:add).and_return([expected_hash])
134
+ interface.stub(:add).with(collection_name, expected_hash).and_return([expected_hash])
39
135
  interface.stub(:generate_unique_id).with(data_field_attributes).and_return(unique_id)
40
136
  end
41
137
 
42
138
  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
139
  described_class.add(data_field_attributes).should == expected_hash
46
140
  end
47
141
  end
48
142
 
49
143
  describe "storing a data model" do
50
- let(:model) { mock 'a model', instance_values: data_field_attributes }
144
+ let(:klass_object) { mock }
145
+ let(:model) { mock 'a model' }
146
+ let(:return_value) { mock }
51
147
 
52
148
  before do
53
- interface.stub(:add)
149
+ described_class.stub(:new).with(model).and_return(klass_object)
54
150
  end
55
151
 
56
- it 'generates a unique id using the model as a salt' do
57
- interface.should_receive(:generate_unique_id).with(model).and_return(unique_id)
152
+ it 'can create a record from a model' do
153
+ klass_object.should_receive(:create).and_return(return_value)
58
154
 
59
- described_class.store(model)
60
- end
61
-
62
- it 'adds the data model to the db store' do
63
- interface.should_receive(:add).with(collection_name, HashWithIndifferentAccess.new({primary_key => unique_id}).merge(data_field_attributes))
64
-
65
- described_class.store(model)
155
+ described_class.store(model).should == return_value
66
156
  end
67
157
  end
68
158
 
@@ -89,17 +179,18 @@ describe Mince::DataModel, 'Mixin' do
89
179
  end
90
180
 
91
181
  describe "updating a data model" do
92
- let(:data_model_id) { '1234567' }
93
- let(:model) { mock 'a model', id: data_model_id, instance_values: data_field_attributes }
182
+ let(:klass_object) { mock }
183
+ let(:model) { mock 'a model' }
184
+ let(:return_value) { mock }
94
185
 
95
186
  before do
96
- interface.stub(:replace)
187
+ described_class.stub(:new).with(model).and_return(klass_object)
97
188
  end
98
189
 
99
- it 'replaces the data model in the db store' do
100
- interface.should_receive(:replace).with(collection_name, HashWithIndifferentAccess.new({primary_key => data_model_id}).merge(data_field_attributes))
190
+ it 'can update a record for a model' do
191
+ klass_object.should_receive(:update).and_return(return_value)
101
192
 
102
- described_class.update(model)
193
+ described_class.update(model).should == return_value
103
194
  end
104
195
  end
105
196
 
@@ -241,3 +332,5 @@ describe Mince::DataModel, 'Mixin' do
241
332
  end
242
333
  end
243
334
  end
335
+
336
+