dynamini 1.10.2 → 1.10.4
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/Gemfile.lock +5 -5
- data/dynamini.gemspec +2 -2
- data/lib/dynamini/base.rb +12 -170
- data/lib/dynamini/client_interface.rb +61 -0
- data/lib/dynamini/dirty.rb +38 -0
- data/lib/dynamini/increment.rb +44 -0
- data/lib/dynamini/querying.rb +11 -26
- data/lib/dynamini/test_client.rb +2 -2
- data/lib/dynamini/type_handler.rb +58 -0
- data/spec/dynamini/base_spec.rb +0 -284
- data/spec/dynamini/client_interface_spec.rb +10 -0
- data/spec/dynamini/dirty_spec.rb +144 -0
- data/spec/dynamini/increment_spec.rb +88 -0
- data/spec/dynamini/type_handler_spec.rb +79 -0
- metadata +36 -24
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dynamini::Dirty do
|
4
|
+
|
5
|
+
let(:model_attributes) {
|
6
|
+
{
|
7
|
+
name: 'Widget',
|
8
|
+
price: 9.99,
|
9
|
+
id: 'abcd1234',
|
10
|
+
hash_key: '009'
|
11
|
+
}
|
12
|
+
}
|
13
|
+
let(:dirty_model) { Dynamini::Base.new(model_attributes) }
|
14
|
+
let(:model) { Dynamini::Base.new(model_attributes, false) }
|
15
|
+
|
16
|
+
|
17
|
+
describe '.new' do
|
18
|
+
|
19
|
+
|
20
|
+
it 'should append all initial attrs to @changed, including hash_key' do
|
21
|
+
expect(dirty_model.changed).to eq(model_attributes.keys.map(&:to_s).delete_if { |k, v| k == 'id' })
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should not include the primary key in the changes' do
|
25
|
+
expect(dirty_model.changes[:id]).to be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#__was' do
|
30
|
+
|
31
|
+
context 'nonexistent attribute' do
|
32
|
+
it 'should raise an error' do
|
33
|
+
expect { Dynamini::Base.new.thing_was }.to raise_error ArgumentError
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'after saving' do
|
38
|
+
it 'should clear all _was values' do
|
39
|
+
model = Dynamini::Base.new
|
40
|
+
model.new_val = 'new'
|
41
|
+
model.save
|
42
|
+
expect(model.new_val_was).to eq('new')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'new record' do
|
47
|
+
|
48
|
+
subject(:model) { Dynamini::Base.new(baz: 'baz') }
|
49
|
+
it { is_expected.to respond_to(:baz_was) }
|
50
|
+
|
51
|
+
context 'handled attribute with default' do
|
52
|
+
it 'should return the default value' do
|
53
|
+
Dynamini::Base.handle(:num, :integer, default: 2)
|
54
|
+
expect(model.num_was).to eq(2)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'handled attribute with no default' do
|
59
|
+
it 'should return nil' do
|
60
|
+
Dynamini::Base.handle(:num, :integer)
|
61
|
+
expect(model.num_was).to be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'newly assigned attribute' do
|
66
|
+
it 'should return nil' do
|
67
|
+
model.new_attribute = 'hello'
|
68
|
+
expect(model.new_attribute_was).to be_nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'previously saved record' do
|
74
|
+
subject(:model) { Dynamini::Base.new({baz: 'baz', nil_val: nil}, false) }
|
75
|
+
context 'unchanged attribute' do
|
76
|
+
it 'should return the current value' do
|
77
|
+
expect(model.baz_was).to eq('baz')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'newly assigned attribute or attribute changed from explicit nil' do
|
82
|
+
it 'should return nil' do
|
83
|
+
model.nil_val = 'no longer nil'
|
84
|
+
model.new_val = 'new'
|
85
|
+
expect(model.nil_val_was).to be_nil
|
86
|
+
expect(model.new_val_was).to be_nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'attribute changed from value to value' do
|
91
|
+
it 'should return the old value' do
|
92
|
+
model.baz = 'baz2'
|
93
|
+
expect(model.baz_was).to eq('baz')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#changes' do
|
100
|
+
it 'should not return the hash key or range key' do
|
101
|
+
Dynamini::Base.set_range_key(:range_key)
|
102
|
+
model.instance_variable_set(:@changes, {id: 'test_hash_key', range_key: "test_range_key"})
|
103
|
+
expect(model.changes).to eq({})
|
104
|
+
Dynamini::Base.set_range_key(nil)
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'no change detected' do
|
108
|
+
it 'should return an empty hash' do
|
109
|
+
expect(model.changes).to eq({})
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'attribute changed' do
|
114
|
+
before { model.price = 1 }
|
115
|
+
it 'should include the changed attribute' do
|
116
|
+
expect(model.changes['price']).to eq([9.99, 1])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'attribute created' do
|
121
|
+
before { model.foo = 'bar' }
|
122
|
+
it 'should include the created attribute' do
|
123
|
+
expect(model.changes['foo']).to eq([nil, 'bar'])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'attribute changed twice' do
|
128
|
+
before do
|
129
|
+
model.foo = 'bar'
|
130
|
+
model.foo = 'baz'
|
131
|
+
end
|
132
|
+
it 'should only include one copy of the changed attribute' do
|
133
|
+
expect(model.changes['foo']).to eq(['bar', 'baz'])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#changed' do
|
139
|
+
it 'should stringify the keys of changes' do
|
140
|
+
allow(model).to receive(:changes).and_return({'price' => [1, 2], 'name' => ['a', 'b']})
|
141
|
+
expect(model.changed).to eq(['price', 'name'])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dynamini::Increment do
|
4
|
+
|
5
|
+
let(:model_attributes) {
|
6
|
+
{
|
7
|
+
name: 'Widget',
|
8
|
+
price: 9.99,
|
9
|
+
id: 'abcd1234',
|
10
|
+
hash_key: '009'
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
subject(:model) { Dynamini::Base.create!(model_attributes) }
|
15
|
+
|
16
|
+
describe '.increment!' do
|
17
|
+
context 'when incrementing a nil value' do
|
18
|
+
it 'should save' do
|
19
|
+
expect(model.class.client).to receive(:update_item).with(
|
20
|
+
table_name: 'bases',
|
21
|
+
key: {id: model_attributes[:id]},
|
22
|
+
attribute_updates: hash_including(
|
23
|
+
"foo" => {
|
24
|
+
value: 5,
|
25
|
+
action: 'ADD'
|
26
|
+
}
|
27
|
+
)
|
28
|
+
)
|
29
|
+
model.increment!(foo: 5)
|
30
|
+
end
|
31
|
+
it 'should update the value' do
|
32
|
+
model.increment!(foo: 5)
|
33
|
+
expect(Dynamini::Base.find('abcd1234').foo.to_i).to eq 5
|
34
|
+
end
|
35
|
+
end
|
36
|
+
context 'when incrementing a numeric value' do
|
37
|
+
it 'should save' do
|
38
|
+
expect(model).to receive(:read_attribute).and_return(9.99)
|
39
|
+
expect(model.class.client).to receive(:update_item).with(
|
40
|
+
table_name: 'bases',
|
41
|
+
key: {id: model_attributes[:id]},
|
42
|
+
attribute_updates: hash_including(
|
43
|
+
"price" => {
|
44
|
+
value: 5,
|
45
|
+
action: 'ADD'
|
46
|
+
}
|
47
|
+
)
|
48
|
+
)
|
49
|
+
model.increment!(price: 5)
|
50
|
+
|
51
|
+
end
|
52
|
+
it 'should sum the values' do
|
53
|
+
expect(model).to receive(:read_attribute).and_return(9.99)
|
54
|
+
model.increment!(price: 5)
|
55
|
+
expect(Dynamini::Base.find('abcd1234').price).to eq 14.99
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context 'when incrementing a non-numeric value' do
|
59
|
+
it 'should raise an error and not save' do
|
60
|
+
expect(model).to receive(:read_attribute).and_return('hello')
|
61
|
+
expect { model.increment!(price: 5) }.to raise_error(StandardError)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
context 'when incrementing with a non-numeric value' do
|
65
|
+
it 'should raise an error and not save' do
|
66
|
+
expect { model.increment!(foo: 'bar') }.to raise_error(StandardError)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
context 'when incrementing multiple values' do
|
70
|
+
it 'should create/sum both values' do
|
71
|
+
allow(model).to receive(:read_attribute).and_return(9.99)
|
72
|
+
model.increment!(price: 5, baz: 6)
|
73
|
+
found_model = Dynamini::Base.find('abcd1234')
|
74
|
+
expect(found_model.price).to eq 14.99
|
75
|
+
expect(found_model.baz).to eq 6
|
76
|
+
end
|
77
|
+
end
|
78
|
+
context 'when incrementing a new record' do
|
79
|
+
it 'should save the record and init the values and timestamps' do
|
80
|
+
Dynamini::Base.new(id: 1, foo: 'bar').increment!(baz: 1)
|
81
|
+
found_model = Dynamini::Base.find(1)
|
82
|
+
expect(found_model.baz).to eq 1
|
83
|
+
expect(found_model.created_at).to_not be_nil
|
84
|
+
expect(found_model.updated_at).to_not be_nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dynamini::TypeHandler do
|
4
|
+
describe '.handle' do
|
5
|
+
|
6
|
+
class HandledClass < Dynamini::Base;
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'when reading the handled attirubte' do
|
10
|
+
before { HandledClass.handle :price, :integer, default: 9 }
|
11
|
+
it 'should return the proper format' do
|
12
|
+
object = HandledClass.new(price: "1")
|
13
|
+
expect(object.price).to eq(1)
|
14
|
+
end
|
15
|
+
it 'should return the default value if not assigned' do
|
16
|
+
object = HandledClass.new
|
17
|
+
expect(object.price).to eq(9)
|
18
|
+
end
|
19
|
+
it 'should return an array with formated item if handled' do
|
20
|
+
object = HandledClass.new(price: ["1", "2"])
|
21
|
+
expect(object.price).to eq([1, 2])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when writing the handled attribute' do
|
26
|
+
before { HandledClass.handle :price, :float, default: 9 }
|
27
|
+
it 'should convert the value to handled format' do
|
28
|
+
object = HandledClass.new(price: "1")
|
29
|
+
expect(object.attributes[:price]).to eq(1.0)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class HandleModel < Dynamini::Base
|
36
|
+
handle :price, :float, default: 10
|
37
|
+
handle :start_date, :time
|
38
|
+
handle :int_list, :integer
|
39
|
+
handle :sym_list, :symbol
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:handle_model) { HandleModel.new }
|
43
|
+
|
44
|
+
it 'should create getters and setters' do
|
45
|
+
expect(handle_model).to_not receive(:method_missing)
|
46
|
+
handle_model.price = 1
|
47
|
+
handle_model.price
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should retrieve price as a float' do
|
51
|
+
handle_model.price = '5.2'
|
52
|
+
expect(handle_model.price).to be_a(Float)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should default price to 0 if not set' do
|
56
|
+
expect(handle_model.price).to eq 10
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should store times as floats' do
|
60
|
+
handle_model.start_date = Time.now
|
61
|
+
expect(handle_model.attributes[:start_date]).to be_a(Float)
|
62
|
+
expect(handle_model.attributes[:start_date] > 1_000_000_000).to be_truthy
|
63
|
+
expect(handle_model.start_date).to be_a(Time)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should reject bad data' do
|
67
|
+
expect { handle_model.int_list = {a: 1} }.to raise_error NoMethodError
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should save casted arrays' do
|
71
|
+
handle_model.int_list = [12, 24, 48]
|
72
|
+
expect(handle_model.int_list).to eq([12, 24, 48])
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should retrieve casted arrays' do
|
76
|
+
handle_model.sym_list = ['foo', 'bar', 'baz']
|
77
|
+
expect(handle_model.sym_list).to eq([:foo, :bar, :baz])
|
78
|
+
end
|
79
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamini
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.10.
|
4
|
+
version: 1.10.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Ward
|
@@ -15,110 +15,110 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
|
-
date: 2015-01-
|
18
|
+
date: 2015-01-11 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activemodel
|
22
22
|
requirement: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3'
|
27
|
-
- - <
|
27
|
+
- - "<"
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '5.0'
|
30
30
|
type: :runtime
|
31
31
|
prerelease: false
|
32
32
|
version_requirements: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
|
-
- -
|
34
|
+
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: '3'
|
37
|
-
- - <
|
37
|
+
- - "<"
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '5.0'
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: aws-sdk
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
|
-
- - ~>
|
44
|
+
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '2'
|
47
47
|
type: :runtime
|
48
48
|
prerelease: false
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - ~>
|
51
|
+
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '2'
|
54
54
|
- !ruby/object:Gem::Dependency
|
55
55
|
name: rspec
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - ~>
|
58
|
+
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '3'
|
61
61
|
type: :development
|
62
62
|
prerelease: false
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- - ~>
|
65
|
+
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '3'
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
69
|
name: pry
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - ~>
|
72
|
+
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '0'
|
75
75
|
type: :development
|
76
76
|
prerelease: false
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
|
-
- - ~>
|
79
|
+
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '0'
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
83
|
name: fuubar
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
|
-
- - ~>
|
86
|
+
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '2'
|
89
89
|
type: :development
|
90
90
|
prerelease: false
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- - ~>
|
93
|
+
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '2'
|
96
96
|
- !ruby/object:Gem::Dependency
|
97
97
|
name: guard-rspec
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
|
-
- -
|
100
|
+
- - ">="
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '0'
|
103
103
|
type: :development
|
104
104
|
prerelease: false
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
|
-
- -
|
107
|
+
- - ">="
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: guard-shell
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
|
-
- -
|
114
|
+
- - ">="
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '0'
|
117
117
|
type: :development
|
118
118
|
prerelease: false
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
|
-
- -
|
121
|
+
- - ">="
|
122
122
|
- !ruby/object:Gem::Version
|
123
123
|
version: '0'
|
124
124
|
description: |-
|
@@ -130,9 +130,9 @@ executables: []
|
|
130
130
|
extensions: []
|
131
131
|
extra_rdoc_files: []
|
132
132
|
files:
|
133
|
-
- .gitignore
|
134
|
-
- .rspec
|
135
|
-
- .travis.yml
|
133
|
+
- ".gitignore"
|
134
|
+
- ".rspec"
|
135
|
+
- ".travis.yml"
|
136
136
|
- Gemfile
|
137
137
|
- Gemfile.lock
|
138
138
|
- Guardfile
|
@@ -144,13 +144,21 @@ files:
|
|
144
144
|
- lib/dynamini.rb
|
145
145
|
- lib/dynamini/base.rb
|
146
146
|
- lib/dynamini/batch_operations.rb
|
147
|
+
- lib/dynamini/client_interface.rb
|
147
148
|
- lib/dynamini/configuration.rb
|
149
|
+
- lib/dynamini/dirty.rb
|
150
|
+
- lib/dynamini/increment.rb
|
148
151
|
- lib/dynamini/querying.rb
|
149
152
|
- lib/dynamini/test_client.rb
|
153
|
+
- lib/dynamini/type_handler.rb
|
150
154
|
- spec/dynamini/base_spec.rb
|
151
155
|
- spec/dynamini/batch_operations_spec.rb
|
156
|
+
- spec/dynamini/client_interface_spec.rb
|
157
|
+
- spec/dynamini/dirty_spec.rb
|
158
|
+
- spec/dynamini/increment_spec.rb
|
152
159
|
- spec/dynamini/querying_spec.rb
|
153
160
|
- spec/dynamini/test_client_spec.rb
|
161
|
+
- spec/dynamini/type_handler_spec.rb
|
154
162
|
- spec/spec_helper.rb
|
155
163
|
homepage: https://github.com/47colborne/dynamini
|
156
164
|
licenses:
|
@@ -162,23 +170,27 @@ require_paths:
|
|
162
170
|
- lib
|
163
171
|
required_ruby_version: !ruby/object:Gem::Requirement
|
164
172
|
requirements:
|
165
|
-
- -
|
173
|
+
- - ">="
|
166
174
|
- !ruby/object:Gem::Version
|
167
175
|
version: '0'
|
168
176
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
177
|
requirements:
|
170
|
-
- -
|
178
|
+
- - ">="
|
171
179
|
- !ruby/object:Gem::Version
|
172
180
|
version: '0'
|
173
181
|
requirements: []
|
174
182
|
rubyforge_project:
|
175
|
-
rubygems_version: 2.
|
183
|
+
rubygems_version: 2.4.6
|
176
184
|
signing_key:
|
177
185
|
specification_version: 4
|
178
186
|
summary: DynamoDB interface
|
179
187
|
test_files:
|
180
188
|
- spec/dynamini/base_spec.rb
|
181
189
|
- spec/dynamini/batch_operations_spec.rb
|
190
|
+
- spec/dynamini/client_interface_spec.rb
|
191
|
+
- spec/dynamini/dirty_spec.rb
|
192
|
+
- spec/dynamini/increment_spec.rb
|
182
193
|
- spec/dynamini/querying_spec.rb
|
183
194
|
- spec/dynamini/test_client_spec.rb
|
195
|
+
- spec/dynamini/type_handler_spec.rb
|
184
196
|
- spec/spec_helper.rb
|