perpetuity 0.7.3 → 1.0.0.beta
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/CHANGELOG.md +10 -0
- data/Gemfile +3 -0
- data/README.md +18 -8
- data/lib/perpetuity.rb +4 -4
- data/lib/perpetuity/attribute.rb +9 -0
- data/lib/perpetuity/attribute_set.rb +4 -6
- data/lib/perpetuity/data_injectable.rb +1 -1
- data/lib/perpetuity/duplicator.rb +27 -0
- data/lib/perpetuity/mapper.rb +9 -14
- data/lib/perpetuity/version.rb +1 -1
- data/perpetuity.gemspec +0 -1
- data/spec/integration/associations_spec.rb +2 -6
- data/spec/integration/indexing_spec.rb +1 -1
- data/spec/integration/persistence_spec.rb +3 -2
- data/spec/integration/retrieval_spec.rb +5 -13
- data/spec/perpetuity/attribute_set_spec.rb +1 -1
- data/spec/perpetuity/attribute_spec.rb +6 -1
- data/spec/perpetuity/duplicator_spec.rb +35 -0
- data/spec/perpetuity/mapper_spec.rb +21 -2
- data/spec/perpetuity_spec.rb +2 -2
- data/spec/spec_helper.rb +8 -2
- data/spec/support/test_classes.rb +22 -26
- data/spec/support/test_classes/topic.rb +5 -0
- data/spec/support/test_classes/user.rb +1 -1
- metadata +7 -55
- data/lib/perpetuity/mongodb.rb +0 -230
- data/lib/perpetuity/mongodb/index.rb +0 -52
- data/lib/perpetuity/mongodb/nil_query.rb +0 -11
- data/lib/perpetuity/mongodb/query.rb +0 -33
- data/lib/perpetuity/mongodb/query_attribute.rb +0 -66
- data/lib/perpetuity/mongodb/query_expression.rb +0 -94
- data/lib/perpetuity/mongodb/query_intersection.rb +0 -16
- data/lib/perpetuity/mongodb/query_union.rb +0 -16
- data/lib/perpetuity/mongodb/serializer.rb +0 -174
- data/lib/perpetuity/validations.rb +0 -1
- data/lib/perpetuity/validations/length.rb +0 -36
- data/lib/perpetuity/validations/presence.rb +0 -14
- data/lib/perpetuity/validations/validation_set.rb +0 -28
- data/spec/integration/mongodb_spec.rb +0 -218
- data/spec/integration/validations_spec.rb +0 -17
- data/spec/perpetuity/mongodb/index_spec.rb +0 -44
- data/spec/perpetuity/mongodb/query_attribute_spec.rb +0 -58
- data/spec/perpetuity/mongodb/query_expression_spec.rb +0 -67
- data/spec/perpetuity/mongodb/query_intersection_spec.rb +0 -16
- data/spec/perpetuity/mongodb/query_spec.rb +0 -79
- data/spec/perpetuity/mongodb/query_union_spec.rb +0 -16
- data/spec/perpetuity/mongodb/serializer_spec.rb +0 -212
- data/spec/perpetuity/validations/length_spec.rb +0 -53
- data/spec/perpetuity/validations/presence_spec.rb +0 -30
- data/spec/perpetuity/validations_spec.rb +0 -87
@@ -1 +0,0 @@
|
|
1
|
-
require 'perpetuity/validations/validation_set'
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module Perpetuity
|
2
|
-
module Validations
|
3
|
-
class Length
|
4
|
-
def initialize attribute, options
|
5
|
-
@attribute = attribute
|
6
|
-
@at_least = nil
|
7
|
-
@at_most = nil
|
8
|
-
options.each do |option, value|
|
9
|
-
send option, value
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def pass? object
|
14
|
-
length = object.send(@attribute).length
|
15
|
-
|
16
|
-
return false unless @at_least.nil? or @at_least <= length
|
17
|
-
return false unless @at_most.nil? or @at_most >= length
|
18
|
-
|
19
|
-
true
|
20
|
-
end
|
21
|
-
|
22
|
-
def at_least value
|
23
|
-
@at_least = value
|
24
|
-
end
|
25
|
-
|
26
|
-
def at_most value
|
27
|
-
@at_most = value
|
28
|
-
end
|
29
|
-
|
30
|
-
def between range
|
31
|
-
at_least range.min
|
32
|
-
at_most range.max
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'perpetuity/validations/length'
|
2
|
-
require 'perpetuity/validations/presence'
|
3
|
-
require 'set'
|
4
|
-
|
5
|
-
module Perpetuity
|
6
|
-
class ValidationSet < Set
|
7
|
-
|
8
|
-
def valid? object
|
9
|
-
each do |validation|
|
10
|
-
return false unless validation.pass?(object)
|
11
|
-
end
|
12
|
-
|
13
|
-
true
|
14
|
-
end
|
15
|
-
|
16
|
-
def invalid? object
|
17
|
-
!valid? object
|
18
|
-
end
|
19
|
-
|
20
|
-
def present attribute
|
21
|
-
self << Perpetuity::Validations::Presence.new(attribute)
|
22
|
-
end
|
23
|
-
|
24
|
-
def length attribute, options = {}
|
25
|
-
self << Perpetuity::Validations::Length.new(attribute, options)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,218 +0,0 @@
|
|
1
|
-
require 'perpetuity/mongodb'
|
2
|
-
require 'date'
|
3
|
-
|
4
|
-
module Perpetuity
|
5
|
-
describe MongoDB do
|
6
|
-
let(:mongo) { MongoDB.new db: 'perpetuity_gem_test' }
|
7
|
-
let(:klass) { String }
|
8
|
-
subject { mongo }
|
9
|
-
|
10
|
-
it 'is not connected when instantiated' do
|
11
|
-
mongo.should_not be_connected
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'connects to its host' do
|
15
|
-
mongo.connect
|
16
|
-
mongo.should be_connected
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'connects automatically when accessing the database' do
|
20
|
-
mongo.database
|
21
|
-
mongo.should be_connected
|
22
|
-
end
|
23
|
-
|
24
|
-
describe 'initialization params' do
|
25
|
-
let(:host) { double('host') }
|
26
|
-
let(:port) { double('port') }
|
27
|
-
let(:db) { double('db') }
|
28
|
-
let(:pool_size) { double('pool size') }
|
29
|
-
let(:username) { double('username') }
|
30
|
-
let(:password) { double('password') }
|
31
|
-
let(:mongo) do
|
32
|
-
MongoDB.new(
|
33
|
-
host: host,
|
34
|
-
port: port,
|
35
|
-
db: db,
|
36
|
-
pool_size: pool_size,
|
37
|
-
username: username,
|
38
|
-
password: password
|
39
|
-
)
|
40
|
-
end
|
41
|
-
subject { mongo }
|
42
|
-
|
43
|
-
its(:host) { should == host }
|
44
|
-
its(:port) { should == port }
|
45
|
-
its(:db) { should == db }
|
46
|
-
its(:pool_size) { should == pool_size }
|
47
|
-
its(:username) { should == username }
|
48
|
-
its(:password) { should == password }
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'inserts documents into a collection' do
|
52
|
-
expect { mongo.insert klass, name: 'foo' }.to change { mongo.count klass }.by 1
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'inserts multiple documents into a collection' do
|
56
|
-
expect { mongo.insert klass, [{name: 'foo'}, {name: 'bar'}] }
|
57
|
-
.to change { mongo.count klass }.by 2
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'removes all documents from a collection' do
|
61
|
-
mongo.insert klass, {}
|
62
|
-
mongo.delete_all klass
|
63
|
-
mongo.count(klass).should == 0
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'counts the documents in a collection' do
|
67
|
-
mongo.delete_all klass
|
68
|
-
3.times do
|
69
|
-
mongo.insert klass, {}
|
70
|
-
end
|
71
|
-
mongo.count(klass).should == 3
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'counts the documents matching a query' do
|
75
|
-
mongo.delete_all klass
|
76
|
-
1.times { mongo.insert klass, { name: 'bar' } }
|
77
|
-
3.times { mongo.insert klass, { name: 'foo' } }
|
78
|
-
mongo.count(klass) { |o| o.name == 'foo' }.should == 3
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'gets the first document in a collection' do
|
82
|
-
value = {value: 1}
|
83
|
-
mongo.insert klass, value
|
84
|
-
mongo.first(klass)[:hypothetical_value].should == value['value']
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'gets all of the documents in a collection' do
|
88
|
-
values = [{value: 1}, {value: 2}]
|
89
|
-
mongo.should_receive(:retrieve).with(Object, mongo.nil_query, {})
|
90
|
-
.and_return(values)
|
91
|
-
mongo.all(Object).should == values
|
92
|
-
end
|
93
|
-
|
94
|
-
it 'retrieves by id if the id is a string' do
|
95
|
-
time = Time.now.utc
|
96
|
-
id = mongo.insert Object, {inserted: time}
|
97
|
-
|
98
|
-
object = mongo.retrieve(Object, mongo.query{|o| o.id == id.to_s }).first
|
99
|
-
retrieved_time = object["inserted"]
|
100
|
-
retrieved_time.to_f.should be_within(0.001).of time.to_f
|
101
|
-
end
|
102
|
-
|
103
|
-
describe 'serialization' do
|
104
|
-
let(:object) { Object.new }
|
105
|
-
let(:foo_attribute) { double('Attribute', name: :foo) }
|
106
|
-
let(:baz_attribute) { double('Attribute', name: :baz) }
|
107
|
-
let(:mapper) { double('Mapper',
|
108
|
-
mapped_class: Object,
|
109
|
-
mapper_registry: {},
|
110
|
-
attribute_set: Set[foo_attribute, baz_attribute],
|
111
|
-
data_source: mongo,
|
112
|
-
) }
|
113
|
-
|
114
|
-
before do
|
115
|
-
object.instance_variable_set :@foo, 'bar'
|
116
|
-
object.instance_variable_set :@baz, 'quux'
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'serializes objects' do
|
120
|
-
mongo.serialize(object, mapper).should == {
|
121
|
-
'foo' => 'bar',
|
122
|
-
'baz' => 'quux'
|
123
|
-
}
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'can serialize only modified attributes of objects' do
|
127
|
-
updated = object.dup
|
128
|
-
updated.instance_variable_set :@foo, 'foo'
|
129
|
-
|
130
|
-
serialized = mongo.serialize_changed_attributes(updated, object, mapper)
|
131
|
-
serialized.should == { 'foo' => 'foo' }
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
describe 'serializable objects' do
|
136
|
-
let(:serializable_values) { [nil, true, false, 1, 1.2, '', [], {}, Time.now] }
|
137
|
-
|
138
|
-
it 'can insert serializable values' do
|
139
|
-
serializable_values.each do |value|
|
140
|
-
mongo.insert(Object, {value: value}).should be_a Moped::BSON::ObjectId
|
141
|
-
mongo.can_serialize?(value).should be_true
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
it 'generates a new query DSL object' do
|
147
|
-
mongo.query { |object| object.whatever == 1 }.should respond_to :to_db
|
148
|
-
end
|
149
|
-
|
150
|
-
describe 'indexing' do
|
151
|
-
let(:collection) { Object }
|
152
|
-
let(:key) { 'object_id' }
|
153
|
-
|
154
|
-
before { mongo.index collection, key }
|
155
|
-
after { mongo.drop_collection collection }
|
156
|
-
|
157
|
-
it 'adds indexes for the specified key on the specified collection' do
|
158
|
-
indexes = mongo.indexes(collection).select{ |index| index.attribute == 'object_id' }
|
159
|
-
indexes.should_not be_empty
|
160
|
-
indexes.first.order.should be :ascending
|
161
|
-
end
|
162
|
-
|
163
|
-
it 'adds descending-order indexes' do
|
164
|
-
index = mongo.index collection, 'hash', order: :descending
|
165
|
-
index.order.should be :descending
|
166
|
-
end
|
167
|
-
|
168
|
-
it 'creates indexes on the database collection' do
|
169
|
-
mongo.delete_all collection
|
170
|
-
index = mongo.index collection, 'real_index', order: :descending, unique: true
|
171
|
-
mongo.activate_index! index
|
172
|
-
|
173
|
-
mongo.active_indexes(collection).should include index
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'removes indexes' do
|
177
|
-
mongo.drop_collection collection
|
178
|
-
index = mongo.index collection, 'real_index', order: :descending, unique: true
|
179
|
-
mongo.activate_index! index
|
180
|
-
mongo.remove_index index
|
181
|
-
mongo.active_indexes(collection).should_not include index
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
describe 'atomic operations' do
|
186
|
-
after(:all) { mongo.delete_all klass }
|
187
|
-
|
188
|
-
it 'increments the value of an attribute' do
|
189
|
-
id = mongo.insert klass, count: 1
|
190
|
-
mongo.increment klass, id, :count
|
191
|
-
mongo.increment klass, id, :count, 10
|
192
|
-
query = mongo.query { |o| o.id == id }
|
193
|
-
mongo.retrieve(klass, query).first['count'].should be == 12
|
194
|
-
mongo.increment klass, id, :count, -1
|
195
|
-
mongo.retrieve(klass, query).first['count'].should be == 11
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
describe 'operation errors' do
|
200
|
-
let(:data) { { foo: 'bar' } }
|
201
|
-
let(:index) { mongo.index Object, :foo, unique: true }
|
202
|
-
|
203
|
-
before do
|
204
|
-
mongo.delete_all Object
|
205
|
-
mongo.activate_index! index
|
206
|
-
end
|
207
|
-
|
208
|
-
after { mongo.drop_collection Object }
|
209
|
-
|
210
|
-
it 'raises an exception when insertion fails' do
|
211
|
-
mongo.insert Object, data
|
212
|
-
|
213
|
-
expect { mongo.insert Object, data }.to raise_error DuplicateKeyError,
|
214
|
-
'Tried to insert Object with duplicate unique index: foo => "bar"'
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'support/test_classes'
|
3
|
-
|
4
|
-
describe 'validations' do
|
5
|
-
let(:car_mapper) { Perpetuity[Car] }
|
6
|
-
|
7
|
-
it 'raises an exception when inserting an invalid object' do
|
8
|
-
car = Car.new
|
9
|
-
expect { car_mapper.insert car }.to raise_error
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'does not raise an exception when validations are met' do
|
13
|
-
car = Car.new
|
14
|
-
car.make = "Volkswagen"
|
15
|
-
expect { car_mapper.insert car }.not_to raise_error
|
16
|
-
end
|
17
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'perpetuity/mongodb/index'
|
2
|
-
|
3
|
-
module Perpetuity
|
4
|
-
class MongoDB
|
5
|
-
describe Index do
|
6
|
-
let(:attribute) { double(name: 'name') }
|
7
|
-
let(:index) { Index.new(Object, attribute) }
|
8
|
-
|
9
|
-
it 'is not active by default' do
|
10
|
-
index.should_not be_active
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'can be activated' do
|
14
|
-
index.activate!
|
15
|
-
index.should be_active
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'can be unique' do
|
19
|
-
index = Index.new(Object, attribute, unique: true)
|
20
|
-
index.should be_unique
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'is not unique by default' do
|
24
|
-
index.should_not be_unique
|
25
|
-
end
|
26
|
-
|
27
|
-
describe 'index ordering' do
|
28
|
-
it 'can be ordered in ascending order' do
|
29
|
-
index = Index.new(Object, attribute, order: :ascending)
|
30
|
-
index.order.should be :ascending
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'is ordered ascending by default' do
|
34
|
-
index.order.should be :ascending
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'can be ordered in descending order' do
|
38
|
-
index = Index.new(Object, attribute, order: :descending)
|
39
|
-
index.order.should be :descending
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
require 'perpetuity/mongodb/query_attribute'
|
2
|
-
|
3
|
-
module Perpetuity
|
4
|
-
describe MongoDB::QueryAttribute do
|
5
|
-
let(:attribute) { MongoDB::QueryAttribute.new :attribute_name }
|
6
|
-
subject { attribute }
|
7
|
-
|
8
|
-
its(:name) { should == :attribute_name }
|
9
|
-
|
10
|
-
it 'allows checking subattributes' do
|
11
|
-
attribute.title.name.should == :'attribute_name.title'
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'wraps .id subattribute in metadata' do
|
15
|
-
attribute.id.name.should == :'attribute_name.__metadata__.id'
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'wraps .klass subattribute in metadata' do
|
19
|
-
attribute.klass.name.should == :'attribute_name.__metadata__.class'
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'checks for equality' do
|
23
|
-
(attribute == 1).should be_a MongoDB::QueryExpression
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'checks for less than' do
|
27
|
-
(attribute < 1).should be_a MongoDB::QueryExpression
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'checks for <=' do
|
31
|
-
(attribute <= 1).should be_a MongoDB::QueryExpression
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'checks for greater than' do
|
35
|
-
(attribute > 1).should be_a MongoDB::QueryExpression
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'checks for >=' do
|
39
|
-
(attribute >= 1).should be_a MongoDB::QueryExpression
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'checks for inequality' do
|
43
|
-
(attribute != 1).should be_a MongoDB::QueryExpression
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'checks for regexp matches' do
|
47
|
-
(attribute =~ /value/).should be_a MongoDB::QueryExpression
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'checks for inclusion' do
|
51
|
-
(attribute.in [1, 2, 3]).should be_a MongoDB::QueryExpression
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'checks for its own truthiness' do
|
55
|
-
attribute.to_db.should == ((attribute != false) & (attribute != nil)).to_db
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'perpetuity/mongodb/query_expression'
|
2
|
-
|
3
|
-
module Perpetuity
|
4
|
-
describe MongoDB::QueryExpression do
|
5
|
-
let(:expression) { MongoDB::QueryExpression.new :attribute, :equals, :value }
|
6
|
-
subject { expression }
|
7
|
-
|
8
|
-
describe 'translation to Mongo expressions' do
|
9
|
-
it 'equality expression' do
|
10
|
-
expression.to_db.should == { attribute: :value }
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'less-than expression' do
|
14
|
-
expression.comparator = :less_than
|
15
|
-
expression.to_db.should == { attribute: { '$lt' => :value } }
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'less-than-or-equal-to expression' do
|
19
|
-
expression.comparator = :lte
|
20
|
-
expression.to_db.should == { attribute: { '$lte' => :value } }
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'greater-than expression' do
|
24
|
-
expression.comparator = :greater_than
|
25
|
-
expression.to_db.should == { attribute: { '$gt' => :value } }
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'greater-than-or-equal-to expression' do
|
29
|
-
expression.comparator = :gte
|
30
|
-
expression.to_db.should == { attribute: { '$gte' => :value } }
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'not-equal' do
|
34
|
-
expression.comparator = :not_equal
|
35
|
-
expression.to_db.should == { attribute: { '$ne' => :value } }
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'checks for inclusion' do
|
39
|
-
expression.comparator = :in
|
40
|
-
expression.to_db.should == { attribute: { '$in' => :value } }
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'checks for regexp matching' do
|
44
|
-
expression.comparator = :matches
|
45
|
-
expression.to_db.should == { attribute: :value }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
describe 'unions' do
|
50
|
-
let(:lhs) { MongoDB::QueryExpression.new :first, :equals, :one }
|
51
|
-
let(:rhs) { MongoDB::QueryExpression.new :second, :equals, :two }
|
52
|
-
|
53
|
-
it 'converts | to an $or query' do
|
54
|
-
(lhs | rhs).to_db.should == { '$or' => [{first: :one}, {second: :two}] }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
describe 'intersections' do
|
59
|
-
let(:lhs) { MongoDB::QueryExpression.new :first, :equals, :one }
|
60
|
-
let(:rhs) { MongoDB::QueryExpression.new :second, :equals, :two }
|
61
|
-
|
62
|
-
it 'converts & to an $and query' do
|
63
|
-
(lhs & rhs).to_db.should == { '$and' => [{first: :one}, {second: :two}] }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|