mince 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+