perpetuity 0.7.3 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile +3 -0
  4. data/README.md +18 -8
  5. data/lib/perpetuity.rb +4 -4
  6. data/lib/perpetuity/attribute.rb +9 -0
  7. data/lib/perpetuity/attribute_set.rb +4 -6
  8. data/lib/perpetuity/data_injectable.rb +1 -1
  9. data/lib/perpetuity/duplicator.rb +27 -0
  10. data/lib/perpetuity/mapper.rb +9 -14
  11. data/lib/perpetuity/version.rb +1 -1
  12. data/perpetuity.gemspec +0 -1
  13. data/spec/integration/associations_spec.rb +2 -6
  14. data/spec/integration/indexing_spec.rb +1 -1
  15. data/spec/integration/persistence_spec.rb +3 -2
  16. data/spec/integration/retrieval_spec.rb +5 -13
  17. data/spec/perpetuity/attribute_set_spec.rb +1 -1
  18. data/spec/perpetuity/attribute_spec.rb +6 -1
  19. data/spec/perpetuity/duplicator_spec.rb +35 -0
  20. data/spec/perpetuity/mapper_spec.rb +21 -2
  21. data/spec/perpetuity_spec.rb +2 -2
  22. data/spec/spec_helper.rb +8 -2
  23. data/spec/support/test_classes.rb +22 -26
  24. data/spec/support/test_classes/topic.rb +5 -0
  25. data/spec/support/test_classes/user.rb +1 -1
  26. metadata +7 -55
  27. data/lib/perpetuity/mongodb.rb +0 -230
  28. data/lib/perpetuity/mongodb/index.rb +0 -52
  29. data/lib/perpetuity/mongodb/nil_query.rb +0 -11
  30. data/lib/perpetuity/mongodb/query.rb +0 -33
  31. data/lib/perpetuity/mongodb/query_attribute.rb +0 -66
  32. data/lib/perpetuity/mongodb/query_expression.rb +0 -94
  33. data/lib/perpetuity/mongodb/query_intersection.rb +0 -16
  34. data/lib/perpetuity/mongodb/query_union.rb +0 -16
  35. data/lib/perpetuity/mongodb/serializer.rb +0 -174
  36. data/lib/perpetuity/validations.rb +0 -1
  37. data/lib/perpetuity/validations/length.rb +0 -36
  38. data/lib/perpetuity/validations/presence.rb +0 -14
  39. data/lib/perpetuity/validations/validation_set.rb +0 -28
  40. data/spec/integration/mongodb_spec.rb +0 -218
  41. data/spec/integration/validations_spec.rb +0 -17
  42. data/spec/perpetuity/mongodb/index_spec.rb +0 -44
  43. data/spec/perpetuity/mongodb/query_attribute_spec.rb +0 -58
  44. data/spec/perpetuity/mongodb/query_expression_spec.rb +0 -67
  45. data/spec/perpetuity/mongodb/query_intersection_spec.rb +0 -16
  46. data/spec/perpetuity/mongodb/query_spec.rb +0 -79
  47. data/spec/perpetuity/mongodb/query_union_spec.rb +0 -16
  48. data/spec/perpetuity/mongodb/serializer_spec.rb +0 -212
  49. data/spec/perpetuity/validations/length_spec.rb +0 -53
  50. data/spec/perpetuity/validations/presence_spec.rb +0 -30
  51. data/spec/perpetuity/validations_spec.rb +0 -87
@@ -1,16 +0,0 @@
1
- require 'perpetuity/mongodb/query_intersection'
2
- require 'perpetuity/mongodb/query_expression'
3
-
4
- module Perpetuity
5
- class MongoDB
6
- describe QueryIntersection do
7
- let(:lhs) { QueryExpression.new :first, :equals, 'one' }
8
- let(:rhs) { QueryExpression.new :second, :equals, 'two' }
9
- let(:intersection) { QueryIntersection.new lhs, rhs }
10
-
11
- it 'returns a Mongo representation of the union of 2 expressions' do
12
- intersection.to_db.should be == { '$and' => [{first: 'one'}, {second: 'two'}] }
13
- end
14
- end
15
- end
16
- end
@@ -1,79 +0,0 @@
1
- require 'perpetuity/mongodb/query'
2
-
3
- module Perpetuity
4
- describe MongoDB::Query do
5
- let(:query) { MongoDB::Query }
6
-
7
- it 'generates Mongo equality expressions' do
8
- query.new{ |user| user.name == 'Jamie' }.to_db.should == {name: 'Jamie'}
9
- end
10
-
11
- it 'generates Mongo less-than expressions' do
12
- query.new{ |v| v.quantity < 10 }.to_db.should == {quantity: { '$lt' => 10}}
13
- end
14
-
15
- it 'generates Mongo less-than-or-equal expressions' do
16
- query.new{ |v| v.quantity <= 10 }.to_db.should == {quantity: { '$lte' => 10}}
17
- end
18
-
19
- it 'generates Mongo greater-than expressions' do
20
- query.new{ |v| v.quantity > 10 }.to_db.should == {quantity: { '$gt' => 10}}
21
- end
22
-
23
- it 'generates Mongo greater-than-or-equal expressions' do
24
- query.new{ |v| v.quantity >= 10 }.to_db.should == {quantity: { '$gte' => 10}}
25
- end
26
-
27
- it 'generates Mongo inequality expressions' do
28
- query.new{ |user| user.name.not_equal? 'Jamie' }.to_db.should == {
29
- name: {'$ne' => 'Jamie'}
30
- }
31
- end
32
-
33
- it 'generates Mongo regexp expressions' do
34
- query.new{ |user| user.name =~ /Jamie/ }.to_db.should == {name: /Jamie/}
35
- end
36
-
37
- describe 'negated queries' do
38
- it 'negates an equality query' do
39
- q = query.new { |user| user.name == 'Jamie' }
40
- q.negate.to_db.should == { name: { '$ne' => 'Jamie' } }
41
- end
42
-
43
- it 'negates a not-equal query' do
44
- q = query.new { |account| account.balance != 10 }
45
- q.negate.to_db.should == { balance: { '$not' => { '$ne' => 10 } } }
46
- end
47
-
48
- it 'negates a less-than query' do
49
- q = query.new { |account| account.balance < 10 }
50
- q.negate.to_db.should == { balance: { '$not' => { '$lt' => 10 } } }
51
- end
52
-
53
- it 'negates a less-than-or-equal query' do
54
- q = query.new { |account| account.balance <= 10 }
55
- q.negate.to_db.should == { balance: { '$not' => { '$lte' => 10 } } }
56
- end
57
-
58
- it 'negates a greater-than query' do
59
- q = query.new { |account| account.balance > 10 }
60
- q.negate.to_db.should == { balance: { '$not' => { '$gt' => 10 } } }
61
- end
62
-
63
- it 'negates a greater-than-or-equal query' do
64
- q = query.new { |account| account.balance >= 10 }
65
- q.negate.to_db.should == { balance: { '$not' => { '$gte' => 10 } } }
66
- end
67
-
68
- it 'negates a regex query' do
69
- q = query.new { |account| account.name =~ /Jamie/ }
70
- q.negate.to_db.should == { name: { '$not' => /Jamie/ } }
71
- end
72
-
73
- it 'negates a inclusion query' do
74
- q = query.new { |article| article.tags.in ['tag1', 'tag2'] }
75
- q.negate.to_db.should == { tags: { '$not' => { '$in' => ['tag1', 'tag2'] } } }
76
- end
77
- end
78
- end
79
- end
@@ -1,16 +0,0 @@
1
- require 'perpetuity/mongodb/query_union'
2
- require 'perpetuity/mongodb/query_expression'
3
-
4
- module Perpetuity
5
- class MongoDB
6
- describe QueryUnion do
7
- let(:lhs) { QueryExpression.new :first, :equals, 'one' }
8
- let(:rhs) { QueryExpression.new :second, :equals, 'two' }
9
- let(:union) { QueryUnion.new lhs, rhs }
10
-
11
- it 'returns the proper union of two expressions' do
12
- union.to_db.should be == { '$or' => [{first: 'one'}, {second: 'two'}] }
13
- end
14
- end
15
- end
16
- end
@@ -1,212 +0,0 @@
1
- require 'perpetuity/mongodb/serializer'
2
- require 'perpetuity/mapper'
3
- require 'perpetuity/mapper_registry'
4
- require 'support/test_classes/book'
5
- require 'support/test_classes/user'
6
- require 'support/test_classes/car'
7
-
8
- module Perpetuity
9
- class MongoDB
10
- describe Serializer do
11
- let(:dave) { User.new('Dave') }
12
- let(:andy) { User.new('Andy') }
13
- let(:authors) { [dave, andy] }
14
- let(:book) { Book.new('The Pragmatic Programmer', authors) }
15
- let(:mapper_registry) { MapperRegistry.new }
16
- let(:book_mapper) do
17
- registry = mapper_registry
18
- Class.new(Perpetuity::Mapper) do
19
- map Book, registry
20
- attribute :title
21
- attribute :authors
22
- end.new(registry)
23
- end
24
- let(:user_mapper) do
25
- registry = mapper_registry
26
- Class.new(Perpetuity::Mapper) do
27
- map User, registry
28
- attribute :name
29
- end.new(registry)
30
- end
31
- let(:data_source) { double('Data Source') }
32
- let(:serializer) { Serializer.new(book_mapper) }
33
-
34
- before do
35
- serializer.give_id_to dave, 1
36
- serializer.give_id_to andy, 2
37
- end
38
-
39
- it 'serializes an array of non-embedded attributes as references' do
40
- user_mapper.stub(data_source: data_source)
41
- book_mapper.stub(data_source: data_source)
42
- data_source.should_receive(:can_serialize?).with(book.title).and_return true
43
- data_source.should_receive(:can_serialize?).with(dave).and_return false
44
- data_source.should_receive(:can_serialize?).with(andy).and_return false
45
- serializer.serialize(book).should be == {
46
- 'title' => book.title,
47
- 'authors' => [
48
- {
49
- '__metadata__' => {
50
- 'class' => 'User',
51
- 'id' => user_mapper.id_for(dave)
52
- }
53
- },
54
- {
55
- '__metadata__' => {
56
- 'class' => 'User',
57
- 'id' => user_mapper.id_for(andy)
58
- }
59
- }
60
- ]
61
- }
62
- end
63
-
64
- it 'can serialize only changed attributes' do
65
- book = Book.new('Original Title')
66
- updated_book = book.dup
67
- updated_book.title = 'New Title'
68
- book_mapper.stub(data_source: data_source)
69
- data_source.stub(:can_serialize?).with('New Title') { true }
70
- data_source.stub(:can_serialize?).with('Original Title') { true }
71
- serializer.serialize_changes(updated_book, book).should == {
72
- 'title' => 'New Title'
73
- }
74
- end
75
-
76
- context 'with objects that have hashes as attributes' do
77
- let(:name_data) { {first_name: 'Jamie', last_name: 'Gaskins'} }
78
- let(:serialized_data) { { 'name' => name_data } }
79
- let(:user) { User.new(name_data) }
80
- let(:user_serializer) { Serializer.new(user_mapper) }
81
-
82
- before do
83
- user_mapper.stub(data_source: data_source)
84
- book_mapper.stub(data_source: data_source)
85
- data_source.stub(:can_serialize?).with(name_data) { true }
86
- end
87
-
88
- it 'serializes' do
89
- user_serializer.serialize(user).should be == serialized_data
90
- end
91
-
92
- it 'unserializes' do
93
- user_serializer.unserialize(serialized_data).name.should be == user.name
94
- end
95
- end
96
-
97
- describe 'with an array of references' do
98
- let(:author) { Reference.new(User, 1) }
99
- let(:title) { 'title' }
100
- let(:book) { Book.new(title, [author]) }
101
-
102
- before do
103
- user_mapper.stub(data_source: data_source)
104
- book_mapper.stub(data_source: data_source)
105
- end
106
-
107
- it 'passes the reference unserialized' do
108
- data_source.should_receive(:can_serialize?).with('title') { true }
109
- serializer.serialize(book).should == {
110
- 'title' => title,
111
- 'authors' => [{
112
- '__metadata__' => {
113
- 'class' => author.klass.to_s,
114
- 'id' => author.id
115
- }
116
- }]
117
- }
118
- end
119
- end
120
-
121
- context 'with uninitialized attributes' do
122
- let(:car_model) { 'Corvette' }
123
- let(:car) { Car.new(model: car_model) }
124
- let(:mapper) do
125
- registry = mapper_registry
126
- Class.new(Mapper) do
127
- map Car, registry
128
-
129
- attribute :make
130
- attribute :model
131
- end.new(registry)
132
- end
133
- let(:serializer) { Serializer.new(mapper) }
134
-
135
-
136
- it 'does not persist uninitialized attributes' do
137
- mapper.stub data_source: data_source
138
- data_source.should_receive(:can_serialize?).with(car_model) { true }
139
-
140
- serializer.serialize(car).should == { 'model' => car_model }
141
- end
142
- end
143
-
144
- context 'with marshaled data' do
145
- let(:unserializable_value) { 1..10 }
146
-
147
- it 'stores metadata with marshal information' do
148
- book = Book.new(unserializable_value)
149
-
150
- book_mapper.stub(data_source: data_source)
151
- data_source.stub(:can_serialize?).with(book.title) { false }
152
-
153
- serializer.serialize(book).should == {
154
- 'title' => {
155
- '__marshaled__' => true,
156
- 'value' => Marshal.dump(unserializable_value)
157
- },
158
- 'authors' => []
159
- }
160
- end
161
-
162
- it 'stores marshaled attributes within arrays' do
163
- book = Book.new([unserializable_value])
164
- book_mapper.stub(data_source: data_source)
165
- data_source.stub(:can_serialize?).with(book.title.first) { false }
166
-
167
- serializer.serialize(book).should == {
168
- 'title' => [{
169
- '__marshaled__' => true,
170
- 'value' => Marshal.dump(unserializable_value)
171
- }],
172
- 'authors' => []
173
- }
174
- end
175
-
176
- it 'unmarshals data that has been marshaled by the serializer' do
177
- data = {
178
- 'title' => {
179
- '__marshaled__' => true,
180
- 'value' => Marshal.dump(unserializable_value),
181
- }
182
- }
183
- serializer.unserialize(data).title.should be_a unserializable_value.class
184
- end
185
-
186
- it 'does not unmarshal data not marshaled by the serializer' do
187
- data = { 'title' => Marshal.dump(unserializable_value) }
188
-
189
- serializer.unserialize(data).title.should be_a String
190
- end
191
- end
192
-
193
- it 'unserializes a hash of primitives' do
194
- time = Time.now
195
- serialized_data = {
196
- 'number' => 1,
197
- 'string' => 'hello',
198
- 'boolean' => true,
199
- 'float' => 7.5,
200
- 'time' => time
201
- }
202
-
203
- object = serializer.unserialize(serialized_data)
204
- object.instance_variable_get(:@number).should == 1
205
- object.instance_variable_get(:@string).should == 'hello'
206
- object.instance_variable_get(:@boolean).should == true
207
- object.instance_variable_get(:@float).should == 7.5
208
- object.instance_variable_get(:@time).should == time
209
- end
210
- end
211
- end
212
- end
@@ -1,53 +0,0 @@
1
- require 'perpetuity/validations/length'
2
-
3
- module Perpetuity
4
- module Validations
5
- describe Length do
6
- let(:length) { Length.new :to_s, {}}
7
-
8
- describe 'minimum length' do
9
- before { length.at_least 4 }
10
-
11
- it 'invalidates' do
12
- length.pass?('abc').should be false
13
- end
14
-
15
- it 'validates' do
16
- length.pass?('abcd').should be true
17
- end
18
- end
19
-
20
- describe 'maximum length' do
21
- before { length.at_most 4 }
22
-
23
- it 'validates' do
24
- length.pass?('abcd').should be true
25
- end
26
-
27
- it 'invalidates' do
28
- length.pass?('abcde').should be false
29
- end
30
- end
31
-
32
- describe 'ranges' do
33
- before { length.between 4..5 }
34
-
35
- it 'invalidates values too short' do
36
- length.pass?('abc').should be false
37
- end
38
-
39
- it 'validates lengths at the low end' do
40
- length.pass?('abcd').should be true
41
- end
42
-
43
- it 'validates lengths at the high end' do
44
- length.pass?('abcde').should be true
45
- end
46
-
47
- it 'invalidates values too long' do
48
- length.pass?('abcdef').should be false
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,30 +0,0 @@
1
- require 'perpetuity/validations/presence'
2
-
3
- module Perpetuity
4
- module Validations
5
- describe Presence do
6
- let(:presence) { Presence.new :to_s }
7
- let(:valid) { double('valid object') }
8
- let(:invalid) { double('invalid object') }
9
-
10
- it 'validates values that are present' do
11
- presence.pass?(valid).should be_true
12
- end
13
-
14
- it 'invalidates nil values' do
15
- invalid.stub(to_s: nil)
16
- presence.pass?(invalid).should be_false
17
- end
18
-
19
- it 'invalidates empty strings' do
20
- invalid.stub(to_s: '')
21
- presence.pass?(invalid).should be_false
22
- end
23
-
24
- it 'invalidates strings with only whitespace' do
25
- invalid.stub(to_s: ' ')
26
- presence.pass?(invalid).should be_false
27
- end
28
- end
29
- end
30
- end
@@ -1,87 +0,0 @@
1
- require 'perpetuity/validations'
2
-
3
- module Perpetuity
4
- describe ValidationSet do
5
- let(:validation_set) { ValidationSet.new }
6
- it 'is empty when created' do
7
- validation_set.should be_empty
8
- end
9
-
10
- it 'can add validations' do
11
- v = double('validation')
12
- validation_set << v
13
-
14
- validation_set.first.should == v
15
- end
16
-
17
- it 'validates an object' do
18
- v = double('validation')
19
- v.stub(pass?: true)
20
- validation_set << v
21
-
22
- validation_set.should be_valid(double('object'))
23
- end
24
-
25
- it 'invalidates an object' do
26
- v = double('validation')
27
- v.stub(pass?: false)
28
- validation_set << v
29
-
30
- validation_set.should_not be_valid(double('object'))
31
- end
32
-
33
- describe 'validation types' do
34
- let(:valid_object) { double('valid object') }
35
- let(:invalid_object) { double('invalid object') }
36
-
37
- it 'validates presence of an attribute' do
38
- valid_object.stub(email: 'me@example.com')
39
- validation_set.present :email
40
-
41
- validation_set.count.should eq 1
42
- validation_set.should be_valid(valid_object)
43
- end
44
-
45
- it 'validates length of an attribute' do
46
- valid_object.stub(email: 'me@example.com')
47
- validation_set.length :email, at_most: 14
48
- validation_set.length :email, at_least: 14
49
-
50
- validation_set.count.should eq 2
51
- validation_set.should be_valid(valid_object)
52
- end
53
-
54
- it 'validates length within a range' do
55
- valid_object.stub(email: 'me@example.com')
56
- validation_set.length :email, between: (14..14)
57
-
58
- validation_set.should have(1).items
59
- validation_set.should be_valid(valid_object)
60
- end
61
-
62
- it 'invalidates length within a range' do
63
- invalid_object.stub(email: 'me@example.com')
64
- validation_set.length :email, between: (0..13)
65
-
66
- validation_set.should have(1).items
67
- validation_set.should be_invalid(invalid_object)
68
- end
69
-
70
- it 'invalidates when attribute is too short' do
71
- invalid_object.stub(email: 'foo')
72
- validation_set.length :email, at_least: 4
73
-
74
- validation_set.count.should eq 1
75
- validation_set.should be_invalid(invalid_object)
76
- end
77
-
78
- it 'invalidates when attribute is too long' do
79
- invalid_object.stub(email: 'me@example.com')
80
- subject.length :email, at_most: 4
81
-
82
- subject.count.should eq 1
83
- subject.should be_invalid(invalid_object)
84
- end
85
- end
86
- end
87
- end