modis 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +15 -0
- data/lib/modis/attributes.rb +136 -0
- data/lib/modis/configuration.rb +12 -0
- data/lib/modis/errors.rb +14 -0
- data/lib/modis/finders.rb +43 -0
- data/lib/modis/model.rb +50 -0
- data/lib/modis/persistence.rb +162 -0
- data/lib/modis/transaction.rb +13 -0
- data/lib/modis/version.rb +3 -0
- data/lib/modis.rb +27 -0
- data/lib/tasks/spec.rake +18 -0
- data/redis_model.gemspec +23 -0
- data/spec/attributes_spec.rb +179 -0
- data/spec/errors_spec.rb +18 -0
- data/spec/finders_spec.rb +84 -0
- data/spec/persistence_spec.rb +266 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/simplecov_helper.rb +19 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/transaction_spec.rb +14 -0
- data/spec/validations_spec.rb +49 -0
- metadata +123 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AttributesSpec
|
4
|
+
class MockModel
|
5
|
+
include Modis::Model
|
6
|
+
|
7
|
+
attribute :name, :string, default: 'Janet'
|
8
|
+
attribute :age, :integer, default: 60
|
9
|
+
attribute :percentage, :float
|
10
|
+
attribute :created_at, :timestamp
|
11
|
+
attribute :flag, :boolean
|
12
|
+
attribute :array, :array
|
13
|
+
attribute :hash, :hash
|
14
|
+
attribute :hash_strict, :hash, strict: true
|
15
|
+
attribute :hash_not_strict, :hash, strict: false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Modis::Attributes do
|
20
|
+
let(:model) { AttributesSpec::MockModel.new }
|
21
|
+
|
22
|
+
it 'defines attributes' do
|
23
|
+
model.name = 'bar'
|
24
|
+
model.name.should == 'bar'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'applies an default value' do
|
28
|
+
model.name.should eq 'Janet'
|
29
|
+
model.age.should eq 60
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does not mark an attribute with a default as dirty' do
|
33
|
+
model.name_changed?.should be_false
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises an error for an unsupported attribute type' do
|
37
|
+
expect do
|
38
|
+
class AttributesSpec::MockModel
|
39
|
+
attribute :unsupported, :symbol
|
40
|
+
end
|
41
|
+
end.to raise_error(Modis::UnsupportedAttributeType)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'assigns attributes' do
|
45
|
+
model.assign_attributes(name: 'bar')
|
46
|
+
model.name.should eq 'bar'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'does not attempt to assign attributes that are not defined on the model' do
|
50
|
+
model.assign_attributes(missing_attr: 'derp')
|
51
|
+
model.respond_to?(:missing_attr).should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'allows an attribute to be nilled' do
|
55
|
+
model.name = nil
|
56
|
+
model.save!
|
57
|
+
model.class.find(model.id).name.should be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ':string type' do
|
61
|
+
it 'is coerced' do
|
62
|
+
model.name = 'Ian'
|
63
|
+
model.save!
|
64
|
+
found = AttributesSpec::MockModel.find(model.id)
|
65
|
+
found.name.should eq 'Ian'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ':integer type' do
|
70
|
+
it 'is coerced' do
|
71
|
+
model.age = 18
|
72
|
+
model.save!
|
73
|
+
found = AttributesSpec::MockModel.find(model.id)
|
74
|
+
found.age.should eq 18
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe ':float type' do
|
79
|
+
it 'is coerced' do
|
80
|
+
model.percentage = 18.6
|
81
|
+
model.save!
|
82
|
+
found = AttributesSpec::MockModel.find(model.id)
|
83
|
+
found.percentage.should eq 18.6
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'coerces a string representation to Float' do
|
87
|
+
model.percentage = '18.6'
|
88
|
+
model.save!
|
89
|
+
found = AttributesSpec::MockModel.find(model.id)
|
90
|
+
found.percentage.should eq 18.6
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe ':timestamp type' do
|
95
|
+
it 'is coerced' do
|
96
|
+
now = Time.now
|
97
|
+
model.created_at = now
|
98
|
+
model.save!
|
99
|
+
found = AttributesSpec::MockModel.find(model.id)
|
100
|
+
found.created_at.should be_kind_of(Time)
|
101
|
+
found.created_at.to_s.should eq now.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'coerces a string representation to Time' do
|
105
|
+
now = Time.now
|
106
|
+
model.created_at = now.to_s
|
107
|
+
model.save!
|
108
|
+
found = AttributesSpec::MockModel.find(model.id)
|
109
|
+
found.created_at.should be_kind_of(Time)
|
110
|
+
found.created_at.to_s.should eq now.to_s
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe ':boolean type' do
|
115
|
+
it 'is coerced' do
|
116
|
+
model.flag = 'true'
|
117
|
+
model.save!
|
118
|
+
found = AttributesSpec::MockModel.find(model.id)
|
119
|
+
found.flag.should eq true
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'raises an error if assigned a non-boolean value' do
|
123
|
+
expect { model.flag = 'unf!' }.to raise_error(Modis::AttributeCoercionError)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe ':array type' do
|
128
|
+
it 'is coerced' do
|
129
|
+
model.array = [1, 2, 3]
|
130
|
+
model.save!
|
131
|
+
found = AttributesSpec::MockModel.find(model.id)
|
132
|
+
found.array.should eq [1, 2, 3]
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'raises an error when assigned another type' do
|
136
|
+
expect { model.array = {foo: :bar} }.to raise_error(Modis::AttributeCoercionError)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'does not raise an error when assigned a JSON array string' do
|
140
|
+
expect { model.array = "[1,2,3]" }.to_not raise_error
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'does not raise an error when a JSON string does not deserialize to an Array' do
|
144
|
+
expect { model.array = "{\"foo\":\"bar\"}" }.to raise_error(Modis::AttributeCoercionError)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe ':hash type' do
|
149
|
+
it 'is coerced' do
|
150
|
+
model.hash = {foo: :bar}
|
151
|
+
model.save!
|
152
|
+
found = AttributesSpec::MockModel.find(model.id)
|
153
|
+
found.hash.should eq({'foo' => 'bar'})
|
154
|
+
end
|
155
|
+
|
156
|
+
describe 'strict: true' do
|
157
|
+
it 'raises an error if the value cannot be decoded on assignment' do
|
158
|
+
expect { model.hash_strict = "test" }.to raise_error(MultiJson::ParseError)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'does not raise an error if the value can be decoded on assignment' do
|
162
|
+
expect { model.hash_strict = "{\"foo\":\"bar\"}" }.to_not raise_error
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'strict: false' do
|
167
|
+
it 'returns the value if it cannot be decoded' do
|
168
|
+
model.write_attribute(:hash_not_strict, "test")
|
169
|
+
model.save!
|
170
|
+
found = AttributesSpec::MockModel.find(model.id)
|
171
|
+
found.hash_not_strict.should eq "test"
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'does not raise an error when assigned another type' do
|
175
|
+
expect { model.hash_not_strict = "test" }.to_not raise_error
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/spec/errors_spec.rb
ADDED
@@ -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
|
+
model.errors[:name].should eq ['is not valid']
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,84 @@
|
|
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
|
+
end
|
11
|
+
|
12
|
+
class Consumer < User
|
13
|
+
end
|
14
|
+
|
15
|
+
class Producer < User
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Modis::Finders do
|
20
|
+
let!(:model) { FindersSpec::User.create!(name: 'Ian', age: 28) }
|
21
|
+
let(:found) { FindersSpec::User.find(model.id) }
|
22
|
+
|
23
|
+
it 'finds by ID' do
|
24
|
+
found.id.should eq model.id
|
25
|
+
found.name.should eq model.name
|
26
|
+
found.age.should eq model.age
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'raises an error if the record could not be found' do
|
30
|
+
expect do
|
31
|
+
FindersSpec::User.find(model.id + 1)
|
32
|
+
end.to raise_error(Modis::RecordNotFound, "Couldn't find FindersSpec::User with id=#{model.id + 1}")
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'does not flag an attribute as dirty on a found instance' do
|
36
|
+
found.id_changed?.should be_false
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'all' do
|
40
|
+
it 'returns all records' do
|
41
|
+
m2 = FindersSpec::User.create!(name: 'Tanya', age: 30)
|
42
|
+
m3 = FindersSpec::User.create!(name: 'Kyle', age: 32)
|
43
|
+
|
44
|
+
FindersSpec::User.all.should == [model, m2, m3]
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not return a destroyed record' do
|
48
|
+
model.destroy
|
49
|
+
FindersSpec::User.all.should == []
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'identifies a found record as not being new' do
|
54
|
+
found.new_record?.should be_false
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'Single Table Inheritance' do
|
58
|
+
it 'returns the correct namespace' do
|
59
|
+
FindersSpec::Consumer.namespace.should eq 'users'
|
60
|
+
FindersSpec::Consumer.absolute_namespace.should eq 'modis:users'
|
61
|
+
FindersSpec::Producer.namespace.should eq 'users'
|
62
|
+
FindersSpec::Producer.absolute_namespace.should eq 'modis:users'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns instances of the correct class' do
|
66
|
+
FindersSpec::Consumer.create!(name: 'Kyle')
|
67
|
+
FindersSpec::Producer.create!(name: 'Tanya')
|
68
|
+
|
69
|
+
models = FindersSpec::User.all
|
70
|
+
|
71
|
+
ian = models.find { |model| model.name == 'Ian' }
|
72
|
+
kyle = models.find { |model| model.name == 'Kyle' }
|
73
|
+
tanya = models.find { |model| model.name == 'Tanya' }
|
74
|
+
|
75
|
+
ian.should be_kind_of(FindersSpec::User)
|
76
|
+
kyle.should be_kind_of(FindersSpec::Consumer)
|
77
|
+
tanya.should be_kind_of(FindersSpec::Producer)
|
78
|
+
|
79
|
+
FindersSpec::User.find(ian.id).should be_kind_of(FindersSpec::User)
|
80
|
+
FindersSpec::User.find(kyle.id).should be_kind_of(FindersSpec::Consumer)
|
81
|
+
FindersSpec::User.find(tanya.id).should be_kind_of(FindersSpec::Producer)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,266 @@
|
|
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
|
+
PersistenceSpec::MockModel.namespace.should eq 'persistence_spec:mock_model'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns the absolute namespace' do
|
59
|
+
PersistenceSpec::MockModel.absolute_namespace.should 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
|
+
PersistenceSpec::MockModel.absolute_namespace.should 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
|
+
model.key.should eq 'modis:persistence_spec:mock_model:1'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns a nil key if not saved' do
|
76
|
+
model.key.should 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
|
+
model.name_changed?.should be_true
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'resets dirty tracking when saved' do
|
85
|
+
model.name = 'Kyle'
|
86
|
+
model.name_changed?.should be_true
|
87
|
+
model.save!
|
88
|
+
model.name_changed?.should be_false
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'resets dirty tracking when created' do
|
92
|
+
model = PersistenceSpec::MockModel.create!(name: 'Ian')
|
93
|
+
model.name_changed?.should be_false
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'is persisted' do
|
97
|
+
model.persisted?.should be_true
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'does not track the ID if the underlying Redis command failed'
|
101
|
+
|
102
|
+
it 'does not perform validation if validate: false' do
|
103
|
+
model.name = nil
|
104
|
+
model.valid?.should be_false
|
105
|
+
expect { model.save!(validate: false) }.to_not raise_error
|
106
|
+
model.reload
|
107
|
+
model.name.should be_nil
|
108
|
+
|
109
|
+
model.save(validate: false).should be_true
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'an existing record' do
|
113
|
+
it 'only updates dirty attributes'
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'reload' do
|
117
|
+
it 'reloads attributes' do
|
118
|
+
model.save!
|
119
|
+
model2 = model.class.find(model.id)
|
120
|
+
model2.name = 'Changed'
|
121
|
+
model2.save!
|
122
|
+
expect { model.reload }.to change(model, :name).to('Changed')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'resets dirty tracking' do
|
126
|
+
model.save!
|
127
|
+
model.name = 'Foo'
|
128
|
+
model.name_changed?.should be_true
|
129
|
+
model.reload
|
130
|
+
model.name_changed?.should be_false
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'raises an error if the record has not been saved' do
|
134
|
+
expect { model.reload }.to raise_error(Modis::RecordNotFound, "Couldn't find PersistenceSpec::MockModel without an ID")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe 'callbacks' do
|
139
|
+
it 'preserves dirty state for the duration of the callback life cycle'
|
140
|
+
it 'halts the chain if a callback returns false'
|
141
|
+
|
142
|
+
describe 'a new record' do
|
143
|
+
it 'calls the before_create callback' do
|
144
|
+
model.save!
|
145
|
+
model.called_callbacks.should include(:test_before_create)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'calls the after create callback' do
|
149
|
+
model.save!
|
150
|
+
model.called_callbacks.should include(:test_after_create)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe 'an existing record' do
|
155
|
+
before { model.save! }
|
156
|
+
|
157
|
+
it 'calls the before_update callback' do
|
158
|
+
model.save!
|
159
|
+
model.called_callbacks.should include(:test_before_update)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'calls the after update callback' do
|
163
|
+
model.save!
|
164
|
+
model.called_callbacks.should include(:test_after_update)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'calls the before_save callback' do
|
169
|
+
model.save!
|
170
|
+
model.called_callbacks.should include(:test_before_save)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'calls the after save callback' do
|
174
|
+
model.save!
|
175
|
+
model.called_callbacks.should include(:test_after_save)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'create' do
|
180
|
+
it 'resets dirty tracking' do
|
181
|
+
model = PersistenceSpec::MockModel.create(name: 'Ian')
|
182
|
+
model.name_changed?.should be_false
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'a valid model' do
|
186
|
+
it 'returns the created model'
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'an invalid model' do
|
190
|
+
it 'returns the unsaved model'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe 'update_attribute' do
|
195
|
+
it 'does not perform validation' do
|
196
|
+
model.name = nil
|
197
|
+
model.valid?.should be_false
|
198
|
+
model.name = 'Test'
|
199
|
+
model.update_attribute(:name, nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'invokes callbacks' do
|
203
|
+
model.update_attribute(:name, 'Derp')
|
204
|
+
model.called_callbacks.should_not be_empty
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'updates all dirty attributes' do
|
208
|
+
model.age = 29
|
209
|
+
model.update_attribute(:name, 'Derp')
|
210
|
+
model.reload
|
211
|
+
model.age.should eq 29
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe 'update_attributes!' do
|
216
|
+
it 'updates the given attributes' do
|
217
|
+
model.update_attributes!(name: 'Derp', age: 29)
|
218
|
+
model.reload
|
219
|
+
model.name.should eq 'Derp'
|
220
|
+
model.age.should eq 29
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'invokes callbacks' do
|
224
|
+
model.update_attributes!(name: 'Derp')
|
225
|
+
model.called_callbacks.should_not be_empty
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'updates all dirty attributes' do
|
229
|
+
model.age = 29
|
230
|
+
model.update_attributes!(name: 'Derp')
|
231
|
+
model.reload
|
232
|
+
model.age.should eq 29
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'raises an error if the model is invalid' do
|
236
|
+
expect do
|
237
|
+
model.update_attributes!(name: nil).should be_false
|
238
|
+
end.to raise_error(Modis::RecordInvalid)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe 'update_attributes' do
|
243
|
+
it 'updates the given attributes' do
|
244
|
+
model.update_attributes(name: 'Derp', age: 29)
|
245
|
+
model.reload
|
246
|
+
model.name.should eq 'Derp'
|
247
|
+
model.age.should eq 29
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'invokes callbacks' do
|
251
|
+
model.update_attributes(name: 'Derp')
|
252
|
+
model.called_callbacks.should_not be_empty
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'updates all dirty attributes' do
|
256
|
+
model.age = 29
|
257
|
+
model.update_attributes(name: 'Derp')
|
258
|
+
model.reload
|
259
|
+
model.age.should eq 29
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'returns false if the model is invalid' do
|
263
|
+
model.update_attributes(name: nil).should be_false
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
begin
|
2
|
+
require './spec/support/simplecov_helper'
|
3
|
+
include SimpleCovHelper
|
4
|
+
start_simple_cov('unit')
|
5
|
+
rescue LoadError
|
6
|
+
puts "Coverage disabled."
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'modis'
|
10
|
+
|
11
|
+
Modis.configure do |config|
|
12
|
+
config.namespace = 'modis'
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.after :each do
|
17
|
+
keys = Modis.redis.keys "#{Modis.config.namespace}:*"
|
18
|
+
Modis.redis.del *keys unless keys.empty?
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require './spec/support/simplecov_quality_formatter'
|
3
|
+
|
4
|
+
module SimpleCovHelper
|
5
|
+
def start_simple_cov(name)
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter '/spec/'
|
8
|
+
command_name name
|
9
|
+
|
10
|
+
if ENV['TRAVIS']
|
11
|
+
require 'coveralls'
|
12
|
+
formatter SimpleCov::Formatter::MultiFormatter[SimpleCov::Formatter::QualityFormatter,
|
13
|
+
Coveralls::SimpleCov::Formatter]
|
14
|
+
else
|
15
|
+
formatter SimpleCov::Formatter::QualityFormatter
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TransactionSpec
|
4
|
+
class MockModel
|
5
|
+
include Modis::Model
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Modis::Transaction do
|
10
|
+
it 'yields the block in a transaction' do
|
11
|
+
Modis.redis.should_receive(:multi).and_yield
|
12
|
+
TransactionSpec::MockModel.transaction {}
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'validations' do
|
4
|
+
class TestModel
|
5
|
+
include Modis::Model
|
6
|
+
attribute :name, :string
|
7
|
+
validates :name, presence: true
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:model) { TestModel.new }
|
11
|
+
|
12
|
+
it 'responds to valid?' do
|
13
|
+
model.name = nil
|
14
|
+
model.valid?.should be_false
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets errors on the model' do
|
18
|
+
model.name = nil
|
19
|
+
model.valid?
|
20
|
+
model.errors[:name].should eq ["can't be blank"]
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'save' do
|
24
|
+
it 'returns true if the model is valid' do
|
25
|
+
model.name = "Ian"
|
26
|
+
model.save.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns false if the model is invalid' do
|
30
|
+
model.name = nil
|
31
|
+
model.save.should be_false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'save!' do
|
36
|
+
it 'raises an error if the model is invalid' do
|
37
|
+
model.name = nil
|
38
|
+
expect do
|
39
|
+
model.save!.should be_false
|
40
|
+
end.to raise_error(Modis::RecordInvalid)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'create!' do
|
45
|
+
it 'raises an error if the record is not valid' do
|
46
|
+
expect { TestModel.create!(name: nil) }.to raise_error(Modis::RecordInvalid)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|