modis 1.4.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ module ErrorsSpec
4
+ class MockModel
5
+ include Modis::Model
6
+
7
+ attribute :name, :string
8
+ end
9
+ end
10
+
11
+ describe Modis::Errors do
12
+ let(:model) { ErrorsSpec::MockModel.new }
13
+
14
+ it 'adds errors' do
15
+ model.errors.add(:name, 'is not valid')
16
+ expect(model.errors[:name]).to eq(['is not valid'])
17
+ end
18
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ module FindersSpec
4
+ class User
5
+ include Modis::Model
6
+ self.namespace = 'users'
7
+
8
+ attribute :name, :string
9
+ attribute :age, :integer
10
+ attribute :parent_default, :string, default: 'omg'
11
+ end
12
+
13
+ class Consumer < User
14
+ attribute :consumed, :boolean
15
+ end
16
+
17
+ class Producer < User
18
+ attribute :child_default, :string, default: 'derp'
19
+ end
20
+ end
21
+
22
+ describe Modis::Finder do
23
+ let!(:model) { FindersSpec::User.create!(name: 'Ian', age: 28) }
24
+ let(:found) { FindersSpec::User.find(model.id) }
25
+
26
+ it 'finds by ID' do
27
+ expect(found.id).to eq(model.id)
28
+ expect(found.name).to eq(model.name)
29
+ expect(found.age).to eq(model.age)
30
+ end
31
+
32
+ it 'finds multiple by ID' do
33
+ model1 = FindersSpec::User.create!(name: 'Ian', age: 28)
34
+ model2 = FindersSpec::User.create!(name: 'Tanya', age: 32)
35
+ model3 = FindersSpec::User.create!(name: 'Kyle', age: 35)
36
+ models = FindersSpec::User.find(model1.id, model2.id, model3.id)
37
+ expect(models).to eq([model1, model2, model3])
38
+ end
39
+
40
+ it 'raises an error a record could not be found' do
41
+ expect do
42
+ FindersSpec::User.find(model.id + 1)
43
+ end.to raise_error(Modis::RecordNotFound, "Couldn't find FindersSpec::User with id=#{model.id + 1}")
44
+
45
+ expect do
46
+ FindersSpec::User.find(model.id, model.id + 1)
47
+ end.to raise_error(Modis::RecordNotFound, "Couldn't find FindersSpec::User with id=#{model.id + 1}")
48
+ end
49
+
50
+ it 'does not flag an attribute as dirty on a found instance' do
51
+ expect(found.id_changed?).to be false
52
+ end
53
+
54
+ describe 'all' do
55
+ it 'returns all records' do
56
+ m2 = FindersSpec::User.create!(name: 'Tanya', age: 30)
57
+ m3 = FindersSpec::User.create!(name: 'Kyle', age: 32)
58
+ expect(FindersSpec::User.all).to eq([model, m2, m3])
59
+ end
60
+
61
+ it 'does not return a destroyed record' do
62
+ model.destroy
63
+ expect(FindersSpec::User.all).to eq([])
64
+ end
65
+ end
66
+
67
+ it 'identifies a found record as not being new' do
68
+ expect(found.new_record?).to be false
69
+ end
70
+
71
+ describe 'Single Table Inheritance' do
72
+ it 'returns the correct namespace' do
73
+ expect(FindersSpec::Consumer.namespace).to eq('users')
74
+ expect(FindersSpec::Consumer.absolute_namespace).to eq('modis:users')
75
+ expect(FindersSpec::Producer.namespace).to eq('users')
76
+ expect(FindersSpec::Producer.absolute_namespace).to eq('modis:users')
77
+ end
78
+
79
+ it 'returns instances of the correct class' do
80
+ FindersSpec::Consumer.create!(name: 'Kyle')
81
+ FindersSpec::Producer.create!(name: 'Tanya')
82
+
83
+ models = FindersSpec::User.all
84
+
85
+ ian = models.find { |model| model.name == 'Ian' }
86
+ kyle = models.find { |model| model.name == 'Kyle' }
87
+ tanya = models.find { |model| model.name == 'Tanya' }
88
+
89
+ expect(ian).to be_kind_of(FindersSpec::User)
90
+ expect(kyle).to be_kind_of(FindersSpec::Consumer)
91
+ expect(tanya).to be_kind_of(FindersSpec::Producer)
92
+
93
+ expect(FindersSpec::User.find(ian.id)).to be_kind_of(FindersSpec::User)
94
+ expect(FindersSpec::User.find(kyle.id)).to be_kind_of(FindersSpec::Consumer)
95
+ expect(FindersSpec::User.find(tanya.id)).to be_kind_of(FindersSpec::Producer)
96
+ end
97
+
98
+ it 'inherits attributes from the parent' do
99
+ consumer = FindersSpec::Consumer.create!(name: 'Kyle', consumed: true)
100
+ expect(consumer.attributes.keys.sort).to eq(%w(age consumed id name parent_default type))
101
+ end
102
+
103
+ it 'inherits default attribute values from the parent' do
104
+ producer = FindersSpec::Producer.create!(name: 'Kyle')
105
+ expect(producer.parent_default).to eq('omg')
106
+ expect(producer.child_default).to eq('derp')
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ module IndexSpec
4
+ class MockModel
5
+ include Modis::Model
6
+
7
+ attribute :name, :string
8
+ index :name
9
+ end
10
+ end
11
+
12
+ describe Modis::Index do
13
+ let!(:model) { IndexSpec::MockModel.create!(name: 'Ian') }
14
+
15
+ describe 'create' do
16
+ it 'adds a new model to the index' do
17
+ index = IndexSpec::MockModel.index_for(:name, 'Ian')
18
+ expect(index).to include(model.id)
19
+ end
20
+ end
21
+
22
+ describe 'update' do
23
+ before do
24
+ model.name = 'Kyle'
25
+ model.save!
26
+ end
27
+
28
+ it 'adds the model to the new index' do
29
+ index = IndexSpec::MockModel.index_for(:name, 'Kyle')
30
+ expect(index).to include(model.id)
31
+ end
32
+
33
+ it 'removes the model from the old index' do
34
+ index = IndexSpec::MockModel.index_for(:name, 'Ian')
35
+ expect(index).to_not include(model.id)
36
+ end
37
+ end
38
+
39
+ describe 'destroy' do
40
+ it 'removes a destroyed model id from the index' do
41
+ model.destroy
42
+ index = IndexSpec::MockModel.index_for(:name, 'Ian')
43
+ expect(index).to_not include(model.id)
44
+ end
45
+
46
+ it 'does not find a destroyed model' do
47
+ model.destroy
48
+ models = IndexSpec::MockModel.where(name: 'Ian')
49
+ expect(models).to be_empty
50
+ end
51
+ end
52
+
53
+ it 'finds by index' do
54
+ models = IndexSpec::MockModel.where(name: 'Ian')
55
+ expect(models.first.name).to eq('Ian')
56
+ end
57
+
58
+ it 'finds multiple matches' do
59
+ IndexSpec::MockModel.create!(name: 'Ian')
60
+ models = IndexSpec::MockModel.where(name: 'Ian')
61
+ expect(models.count).to eq(2)
62
+ end
63
+
64
+ it 'returns an empty array if there are no results' do
65
+ expect(IndexSpec::MockModel.where(name: 'Foo')).to be_empty
66
+ end
67
+
68
+ it 'raises an error when trying to query against multiple indexes' do
69
+ expect { IndexSpec::MockModel.where(name: 'Ian', age: 29) }.to raise_error(Modis::IndexError, 'Queries using multiple indexes is not currently supported.')
70
+ end
71
+
72
+ it 'indexes a nil value' do
73
+ model.name = nil
74
+ model.save!
75
+ expect(IndexSpec::MockModel.where(name: nil)).to include(model)
76
+ end
77
+
78
+ it 'distinguishes between nil and blank string values' do
79
+ model1 = IndexSpec::MockModel.create!(name: nil)
80
+ model2 = IndexSpec::MockModel.create!(name: "")
81
+ expect(IndexSpec::MockModel.where(name: nil)).to eq([model1])
82
+ expect(IndexSpec::MockModel.where(name: "")).to eq([model2])
83
+ end
84
+ end
@@ -0,0 +1,319 @@
1
+ require 'spec_helper'
2
+
3
+ module PersistenceSpec
4
+ class MockModel
5
+ include Modis::Model
6
+
7
+ attribute :name, :string, default: 'Ian'
8
+ attribute :age, :integer
9
+ validates :name, presence: true
10
+
11
+ before_create :test_before_create
12
+ after_create :test_after_create
13
+
14
+ before_update :test_before_update
15
+ after_update :test_after_update
16
+
17
+ before_save :test_before_save
18
+ after_save :test_after_save
19
+
20
+ def called_callbacks
21
+ @called_callbacks ||= []
22
+ end
23
+
24
+ def test_after_create
25
+ called_callbacks << :test_after_create
26
+ end
27
+
28
+ def test_before_create
29
+ called_callbacks << :test_before_create
30
+ end
31
+
32
+ def test_after_update
33
+ called_callbacks << :test_after_update
34
+ end
35
+
36
+ def test_before_update
37
+ called_callbacks << :test_before_update
38
+ end
39
+
40
+ def test_after_save
41
+ called_callbacks << :test_after_save
42
+ end
43
+
44
+ def test_before_save
45
+ called_callbacks << :test_before_save
46
+ end
47
+ end
48
+ end
49
+
50
+ describe Modis::Persistence do
51
+ let(:model) { PersistenceSpec::MockModel.new }
52
+
53
+ describe 'namespaces' do
54
+ it 'returns the namespace' do
55
+ expect(PersistenceSpec::MockModel.namespace).to eq('persistence_spec:mock_model')
56
+ end
57
+
58
+ it 'returns the absolute namespace' do
59
+ expect(PersistenceSpec::MockModel.absolute_namespace).to eq('modis:persistence_spec:mock_model')
60
+ end
61
+
62
+ it 'allows the namespace to be set explicitly' do
63
+ PersistenceSpec::MockModel.namespace = 'other'
64
+ expect(PersistenceSpec::MockModel.absolute_namespace).to eq('modis:other')
65
+ end
66
+
67
+ after { PersistenceSpec::MockModel.namespace = nil }
68
+ end
69
+
70
+ it 'returns a key' do
71
+ model.save!
72
+ expect(model.key).to eq('modis:persistence_spec:mock_model:1')
73
+ end
74
+
75
+ it 'returns a nil key if not saved' do
76
+ expect(model.key).to be_nil
77
+ end
78
+
79
+ it 'works with ActiveModel dirty tracking' do
80
+ expect { model.name = 'Kyle' }.to change(model, :changed).to(['name'])
81
+ expect(model.name_changed?).to be true
82
+ end
83
+
84
+ it 'resets dirty tracking when saved' do
85
+ model.name = 'Kyle'
86
+ expect(model.name_changed?).to be true
87
+ model.save!
88
+ expect(model.name_changed?).to be false
89
+ end
90
+
91
+ it 'resets dirty tracking when created' do
92
+ model = PersistenceSpec::MockModel.create!(name: 'Ian')
93
+ expect(model.name_changed?).to be false
94
+ end
95
+
96
+ it 'does not identify an attribute as changed if the value is the default' do
97
+ expect(model.class.attributes_with_defaults['name']).to eq('Ian')
98
+ expect(model.name).to eq('Ian')
99
+ expect(model.name_changed?).to be false
100
+ end
101
+
102
+ it 'is persisted' do
103
+ expect(model.persisted?).to be true
104
+ end
105
+
106
+ it 'does not track the ID if the underlying Redis command failed' do
107
+ redis = double(hmset: double(value: nil), sadd: nil)
108
+ expect(model.class).to receive(:transaction).and_yield(redis)
109
+ expect(redis).to receive(:pipelined).and_yield
110
+ model.save
111
+ expect { model.class.find(model.id) }.to raise_error(Modis::RecordNotFound)
112
+ end
113
+
114
+ it 'does not perform validation if validate: false' do
115
+ model.name = nil
116
+ expect(model.valid?).to be false
117
+ expect { model.save!(validate: false) }.to_not raise_error
118
+ model.reload
119
+ expect(model.name).to be_nil
120
+
121
+ expect(model.save(validate: false)).to be true
122
+ end
123
+
124
+ describe 'an existing record' do
125
+ it 'only updates dirty attributes' do
126
+ model.name = 'Ian'
127
+ model.age = 10
128
+ model.save!
129
+ model.age = 11
130
+ redis = double
131
+ expect(redis).to receive(:hmset).with("modis:persistence_spec:mock_model:1", ["age", "\v"]).and_return(double(value: 'OK'))
132
+ expect(model.class).to receive(:transaction).and_yield(redis)
133
+ expect(redis).to receive(:pipelined).and_yield
134
+ model.save!
135
+ expect(model.age).to eq(11)
136
+ end
137
+ end
138
+
139
+ describe 'reload' do
140
+ it 'reloads attributes' do
141
+ model.save!
142
+ model2 = model.class.find(model.id)
143
+ model2.name = 'Changed'
144
+ model2.save!
145
+ expect { model.reload }.to change(model, :name).to('Changed')
146
+ end
147
+
148
+ it 'resets dirty tracking' do
149
+ model.save!
150
+ model.name = 'Foo'
151
+ expect(model.name_changed?).to be true
152
+ model.reload
153
+ expect(model.name_changed?).to be false
154
+ end
155
+
156
+ it 'raises an error if the record has not been saved' do
157
+ expect { model.reload }.to raise_error(Modis::RecordNotFound, "Couldn't find PersistenceSpec::MockModel without an ID")
158
+ end
159
+ end
160
+
161
+ describe 'callbacks' do
162
+ it 'preserves dirty state for the duration of the callback life cycle'
163
+ it 'halts the chain if a callback returns false'
164
+
165
+ describe 'a new record' do
166
+ it 'calls the before_create callback' do
167
+ model.save!
168
+ expect(model.called_callbacks).to include(:test_before_create)
169
+ end
170
+
171
+ it 'calls the after create callback' do
172
+ model.save!
173
+ expect(model.called_callbacks).to include(:test_after_create)
174
+ end
175
+ end
176
+
177
+ describe 'an existing record' do
178
+ before { model.save! }
179
+
180
+ it 'calls the before_update callback' do
181
+ model.save!
182
+ expect(model.called_callbacks).to include(:test_before_update)
183
+ end
184
+
185
+ it 'calls the after update callback' do
186
+ model.save!
187
+ expect(model.called_callbacks).to include(:test_after_update)
188
+ end
189
+ end
190
+
191
+ it 'calls the before_save callback' do
192
+ model.save!
193
+ expect(model.called_callbacks).to include(:test_before_save)
194
+ end
195
+
196
+ it 'calls the after save callback' do
197
+ model.save!
198
+ expect(model.called_callbacks).to include(:test_after_save)
199
+ end
200
+ end
201
+
202
+ describe 'create' do
203
+ it 'resets dirty tracking' do
204
+ model = PersistenceSpec::MockModel.create(name: 'Ian')
205
+ expect(model.name_changed?).to be false
206
+ end
207
+
208
+ describe 'a valid model' do
209
+ it 'returns the created model' do
210
+ model = PersistenceSpec::MockModel.create(name: 'Ian')
211
+ expect(model.valid?).to be true
212
+ expect(model.new_record?).to be false
213
+ end
214
+ end
215
+
216
+ describe 'an invalid model' do
217
+ it 'returns the unsaved model' do
218
+ model = PersistenceSpec::MockModel.create(name: nil)
219
+ expect(model.valid?).to be false
220
+ expect(model.new_record?).to be true
221
+ end
222
+ end
223
+ end
224
+
225
+ describe 'update_attribute' do
226
+ it 'does not perform validation' do
227
+ model.name = nil
228
+ expect(model.valid?).to be false
229
+ model.name = 'Test'
230
+ model.update_attribute(:name, nil)
231
+ end
232
+
233
+ it 'invokes callbacks' do
234
+ model.update_attribute(:name, 'Derp')
235
+ expect(model.called_callbacks).to_not be_empty
236
+ end
237
+
238
+ it 'updates all dirty attributes' do
239
+ model.age = 29
240
+ model.update_attribute(:name, 'Derp')
241
+ model.reload
242
+ expect(model.age).to eq 29
243
+ end
244
+ end
245
+
246
+ describe 'update_attributes!' do
247
+ it 'updates the given attributes' do
248
+ model.update_attributes!(name: 'Derp', age: 29)
249
+ model.reload
250
+ expect(model.name).to eq 'Derp'
251
+ expect(model.age).to eq 29
252
+ end
253
+
254
+ it 'invokes callbacks' do
255
+ model.update_attributes!(name: 'Derp')
256
+ expect(model.called_callbacks).to_not be_empty
257
+ end
258
+
259
+ it 'updates all dirty attributes' do
260
+ model.age = 29
261
+ model.update_attributes!(name: 'Derp')
262
+ model.reload
263
+ expect(model.age).to eq 29
264
+ end
265
+
266
+ it 'raises an error if the model is invalid' do
267
+ expect do
268
+ model.update_attributes!(name: nil).to be false
269
+ end.to raise_error(Modis::RecordInvalid)
270
+ end
271
+ end
272
+
273
+ describe 'update_attributes' do
274
+ it 'updates the given attributes' do
275
+ model.update_attributes(name: 'Derp', age: 29)
276
+ model.reload
277
+ expect(model.name).to eq('Derp')
278
+ expect(model.age).to eq(29)
279
+ end
280
+
281
+ it 'invokes callbacks' do
282
+ model.update_attributes(name: 'Derp')
283
+ expect(model.called_callbacks).to_not be_empty
284
+ end
285
+
286
+ it 'updates all dirty attributes' do
287
+ model.age = 29
288
+ model.update_attributes(name: 'Derp')
289
+ model.reload
290
+ expect(model.age).to eq(29)
291
+ end
292
+
293
+ it 'returns false if the model is invalid' do
294
+ expect(model.update_attributes(name: nil)).to be false
295
+ end
296
+ end
297
+
298
+ describe 'YAML backward compatability' do
299
+ it 'loads a YAML serialized value' do
300
+ Modis.with_connection do |redis|
301
+ model.save!
302
+ key = model.class.key_for(model.id)
303
+ record = redis.hgetall(key)
304
+ record['age'] = YAML.dump(30)
305
+ redis.hmset(key, *record.to_a)
306
+ record = redis.hgetall(key)
307
+
308
+ expect(record['age']).to eq("--- 30\n...\n")
309
+
310
+ model.reload
311
+ expect(model.age).to eq(30)
312
+
313
+ model.save!(yaml_sucks: true)
314
+ record = redis.hgetall(key)
315
+ expect(record['age']).to eq("\x1E")
316
+ end
317
+ end
318
+ end
319
+ end