perpetuity 0.1
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.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/Guardfile +24 -0
- data/README.md +142 -0
- data/Rakefile +6 -0
- data/lib/perpetuity/attribute.rb +17 -0
- data/lib/perpetuity/attribute_set.rb +23 -0
- data/lib/perpetuity/config.rb +11 -0
- data/lib/perpetuity/data_injectable.rb +21 -0
- data/lib/perpetuity/mapper.rb +195 -0
- data/lib/perpetuity/mongodb/query.rb +19 -0
- data/lib/perpetuity/mongodb/query_attribute.rb +49 -0
- data/lib/perpetuity/mongodb/query_expression.rb +55 -0
- data/lib/perpetuity/mongodb.rb +102 -0
- data/lib/perpetuity/retrieval.rb +82 -0
- data/lib/perpetuity/validations/length.rb +36 -0
- data/lib/perpetuity/validations/presence.rb +14 -0
- data/lib/perpetuity/validations/validation_set.rb +27 -0
- data/lib/perpetuity/validations.rb +1 -0
- data/lib/perpetuity/version.rb +3 -0
- data/lib/perpetuity.rb +23 -0
- data/perpetuity.gemspec +25 -0
- data/spec/perpetuity/attribute_set_spec.rb +19 -0
- data/spec/perpetuity/attribute_spec.rb +19 -0
- data/spec/perpetuity/config_spec.rb +13 -0
- data/spec/perpetuity/data_injectable_spec.rb +26 -0
- data/spec/perpetuity/mapper_spec.rb +51 -0
- data/spec/perpetuity/mongodb/query_attribute_spec.rb +42 -0
- data/spec/perpetuity/mongodb/query_expression_spec.rb +49 -0
- data/spec/perpetuity/mongodb/query_spec.rb +37 -0
- data/spec/perpetuity/mongodb_spec.rb +91 -0
- data/spec/perpetuity/retrieval_spec.rb +58 -0
- data/spec/perpetuity/validations/length_spec.rb +53 -0
- data/spec/perpetuity/validations/presence_spec.rb +30 -0
- data/spec/perpetuity/validations_spec.rb +87 -0
- data/spec/perpetuity_spec.rb +293 -0
- data/spec/test_classes.rb +93 -0
- metadata +181 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'perpetuity/data_injectable'
|
2
|
+
require 'perpetuity/mapper'
|
3
|
+
|
4
|
+
module Perpetuity
|
5
|
+
class Retrieval
|
6
|
+
include DataInjectable
|
7
|
+
include Enumerable
|
8
|
+
attr_accessor :sort_attribute, :sort_direction, :result_limit, :result_page, :quantity_per_page
|
9
|
+
|
10
|
+
def initialize klass, criteria, data_source = Perpetuity.configuration.data_source
|
11
|
+
@class = klass
|
12
|
+
@criteria = criteria
|
13
|
+
@data_source = data_source
|
14
|
+
end
|
15
|
+
|
16
|
+
def sort attribute=:name
|
17
|
+
retrieval = clone
|
18
|
+
retrieval.sort_attribute = attribute
|
19
|
+
retrieval.sort_direction = :ascending
|
20
|
+
|
21
|
+
retrieval
|
22
|
+
end
|
23
|
+
|
24
|
+
def reverse
|
25
|
+
retrieval = clone
|
26
|
+
retrieval.sort_direction = retrieval.sort_direction == :descending ? :ascending : :descending
|
27
|
+
|
28
|
+
retrieval
|
29
|
+
end
|
30
|
+
|
31
|
+
def page page
|
32
|
+
retrieval = clone
|
33
|
+
retrieval.result_page = page
|
34
|
+
retrieval.quantity_per_page = 20
|
35
|
+
retrieval
|
36
|
+
end
|
37
|
+
|
38
|
+
def per_page per
|
39
|
+
retrieval = clone
|
40
|
+
retrieval.quantity_per_page = per
|
41
|
+
retrieval
|
42
|
+
end
|
43
|
+
|
44
|
+
def each &block
|
45
|
+
to_a.each(&block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_a
|
49
|
+
options = {
|
50
|
+
attribute: sort_attribute,
|
51
|
+
direction: sort_direction,
|
52
|
+
limit: result_limit || quantity_per_page,
|
53
|
+
page: result_page
|
54
|
+
}
|
55
|
+
results = @data_source.retrieve(@class, @criteria, options)
|
56
|
+
objects = []
|
57
|
+
results.each do |result|
|
58
|
+
object = @class.new
|
59
|
+
inject_data object, Mapper.new.unserialize(result)
|
60
|
+
|
61
|
+
objects << object
|
62
|
+
end
|
63
|
+
|
64
|
+
objects
|
65
|
+
end
|
66
|
+
|
67
|
+
def [] index
|
68
|
+
to_a[index]
|
69
|
+
end
|
70
|
+
|
71
|
+
def empty?
|
72
|
+
to_a.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def limit lim
|
76
|
+
retrieval = clone
|
77
|
+
retrieval.result_limit = lim
|
78
|
+
|
79
|
+
retrieval
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,36 @@
|
|
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
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'perpetuity/validations/length'
|
2
|
+
require 'perpetuity/validations/presence'
|
3
|
+
|
4
|
+
module Perpetuity
|
5
|
+
class ValidationSet < Set
|
6
|
+
|
7
|
+
def valid? object
|
8
|
+
each do |validation|
|
9
|
+
return false unless validation.pass?(object)
|
10
|
+
end
|
11
|
+
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def invalid? object
|
16
|
+
!valid? object
|
17
|
+
end
|
18
|
+
|
19
|
+
def present attribute
|
20
|
+
self << Perpetuity::Validations::Presence.new(attribute)
|
21
|
+
end
|
22
|
+
|
23
|
+
def length attribute, options = {}
|
24
|
+
self << Perpetuity::Validations::Length.new(attribute, options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'perpetuity/validations/validation_set'
|
data/lib/perpetuity.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "perpetuity/version"
|
2
|
+
require "perpetuity/retrieval"
|
3
|
+
require "perpetuity/mongodb"
|
4
|
+
require "perpetuity/config"
|
5
|
+
require "perpetuity/mapper"
|
6
|
+
|
7
|
+
module Perpetuity
|
8
|
+
def self.configure &block
|
9
|
+
configuration.instance_exec(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configuration
|
13
|
+
@@configuration ||= Configuration.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.generate_mapper_for klass, &block
|
17
|
+
Mapper.generate_for klass, &block
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.[] klass
|
21
|
+
Mapper[klass]
|
22
|
+
end
|
23
|
+
end
|
data/perpetuity.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "perpetuity/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "perpetuity"
|
7
|
+
s.version = Perpetuity::VERSION
|
8
|
+
s.authors = ["Jamie Gaskins"]
|
9
|
+
s.email = ["jgaskins@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/jgaskins/perpetuity.git"
|
11
|
+
s.summary = %q{Persistence library allowing persistence of Ruby objects}
|
12
|
+
s.description = %q{Persistence library allowing persistence of Ruby objects without adding persistence concerns to domain objects.}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
s.add_development_dependency "rake"
|
21
|
+
s.add_development_dependency "rspec", "~> 2.8.0"
|
22
|
+
s.add_development_dependency "guard-rspec"
|
23
|
+
s.add_runtime_dependency "mongo"
|
24
|
+
s.add_runtime_dependency "bson_ext"
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'perpetuity/attribute_set'
|
2
|
+
|
3
|
+
module Perpetuity
|
4
|
+
describe AttributeSet do
|
5
|
+
it 'contains attributes' do
|
6
|
+
attribute = double('Attribute')
|
7
|
+
subject << attribute
|
8
|
+
|
9
|
+
subject.first.should eq attribute
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can access attributes by name' do
|
13
|
+
user_attribute = double('Attribute', name: :user)
|
14
|
+
subject << user_attribute
|
15
|
+
|
16
|
+
subject[:user].should eq user_attribute
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'perpetuity/attribute'
|
2
|
+
|
3
|
+
module Perpetuity
|
4
|
+
describe Attribute do
|
5
|
+
subject { Attribute.new :article, Object }
|
6
|
+
it 'has a name' do
|
7
|
+
subject.name.should == :article
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'has a type' do
|
11
|
+
subject.type.should == Object
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'can be embedded' do
|
15
|
+
attribute = Attribute.new :article, Object, embedded: true
|
16
|
+
attribute.should be_embedded
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'perpetuity/data_injectable'
|
2
|
+
|
3
|
+
module Perpetuity
|
4
|
+
describe DataInjectable do
|
5
|
+
let(:klass) { Class.new }
|
6
|
+
let(:object) { klass.new }
|
7
|
+
|
8
|
+
before { klass.extend DataInjectable }
|
9
|
+
|
10
|
+
it 'injects data into an object' do
|
11
|
+
klass.inject_data object, { a: 1, b: 2 }
|
12
|
+
object.instance_variable_get(:@a).should eq 1
|
13
|
+
object.instance_variable_get(:@b).should eq 2
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'injects an id' do
|
17
|
+
klass.inject_data object, { id: 1 }
|
18
|
+
object.id.should eq 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'injects a specified id' do
|
22
|
+
klass.give_id_to object, 2
|
23
|
+
object.id.should eq 2
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'perpetuity/mapper'
|
2
|
+
|
3
|
+
module Perpetuity
|
4
|
+
describe Mapper do
|
5
|
+
let(:mapper) do
|
6
|
+
Mapper.new do
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { mapper }
|
11
|
+
|
12
|
+
it { should be_a Mapper }
|
13
|
+
|
14
|
+
it 'has correct attributes' do
|
15
|
+
Mapper.new { attribute :name, String }.attributes.should eq [:name]
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns an empty attribute list when no attributes have been assigned' do
|
19
|
+
Mapper.new.attributes.should be_empty
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'can have embedded attributes' do
|
23
|
+
mapper_with_embedded_attrs = Mapper.new { attribute :comments, Array, embedded: true }
|
24
|
+
mapper_with_embedded_attrs.attribute_set[:comments].should be_embedded
|
25
|
+
end
|
26
|
+
|
27
|
+
its(:mapped_class) { should eq Object }
|
28
|
+
|
29
|
+
context 'with unserializable attributes' do
|
30
|
+
let(:unserializable_object) { 1.to_c }
|
31
|
+
let(:serialized_attrs) do
|
32
|
+
[ Marshal.dump(unserializable_object) ]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'serializes attributes' do
|
36
|
+
object = Object.new
|
37
|
+
object.stub(sub_objects: [unserializable_object])
|
38
|
+
mapper.attribute :sub_objects, Array, embedded: true
|
39
|
+
mapper.attributes_for(object)[:sub_objects].should eq serialized_attrs
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'unserializes attributes' do
|
43
|
+
let(:comments) { mapper.unserialize(serialized_attrs) }
|
44
|
+
subject { comments.first }
|
45
|
+
|
46
|
+
it { should be_a Complex }
|
47
|
+
it { should eq unserializable_object }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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 'checks for equality' do
|
11
|
+
(attribute == 1).should be_a MongoDB::QueryExpression
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'checks for less than' do
|
15
|
+
(attribute < 1).should be_a MongoDB::QueryExpression
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'checks for <=' do
|
19
|
+
(attribute <= 1).should be_a MongoDB::QueryExpression
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'checks for greater than' do
|
23
|
+
(attribute > 1).should be_a MongoDB::QueryExpression
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'checks for >=' do
|
27
|
+
(attribute >= 1).should be_a MongoDB::QueryExpression
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'checks for inequality' do
|
31
|
+
attribute.not_equal?(1).should be_a MongoDB::QueryExpression
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'checks for regexp matches' do
|
35
|
+
(attribute =~ /value/).should be_a MongoDB::QueryExpression
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'checks for inclusion' do
|
39
|
+
(attribute.in [1, 2, 3]).should be_a MongoDB::QueryExpression
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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{name == 'Jamie'}.to_db.should == {name: 'Jamie'}
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'generates Mongo less-than expressions' do
|
12
|
+
query.new{quantity < 10}.to_db.should == {quantity: { '$lt' => 10}}
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'generates Mongo less-than-or-equal expressions' do
|
16
|
+
query.new{quantity <= 10}.to_db.should == {quantity: { '$lte' => 10}}
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'generates Mongo greater-than expressions' do
|
20
|
+
query.new{quantity > 10}.to_db.should == {quantity: { '$gt' => 10}}
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'generates Mongo greater-than-or-equal expressions' do
|
24
|
+
query.new{quantity >= 10}.to_db.should == {quantity: { '$gte' => 10}}
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'generates Mongo inequality expressions' do
|
28
|
+
query.new{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{name =~ /Jamie/}.to_db.should == {name: /Jamie/}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'perpetuity/mongodb'
|
2
|
+
|
3
|
+
module Perpetuity
|
4
|
+
describe MongoDB do
|
5
|
+
let(:mongo) { MongoDB.new db: 'perpetuity_gem_test' }
|
6
|
+
let(:klass) { String }
|
7
|
+
subject { mongo }
|
8
|
+
|
9
|
+
it 'is not connected when instantiated' do
|
10
|
+
mongo.should_not be_connected
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'connects to its host' do
|
14
|
+
connection = double('connection')
|
15
|
+
Mongo::Connection.stub(new: connection)
|
16
|
+
mongo.connect
|
17
|
+
mongo.should be_connected
|
18
|
+
mongo.connection.should == connection
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'connects automatically when accessing the database' do
|
22
|
+
mongo.database
|
23
|
+
mongo.should be_connected
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'initialization params' do
|
27
|
+
let(:host) { double('host') }
|
28
|
+
let(:port) { double('port') }
|
29
|
+
let(:db) { double('db') }
|
30
|
+
let(:pool_size) { double('pool size') }
|
31
|
+
let(:username) { double('username') }
|
32
|
+
let(:password) { double('password') }
|
33
|
+
let(:mongo) do
|
34
|
+
MongoDB.new(
|
35
|
+
host: host,
|
36
|
+
port: port,
|
37
|
+
db: db,
|
38
|
+
pool_size: pool_size,
|
39
|
+
username: username,
|
40
|
+
password: password
|
41
|
+
)
|
42
|
+
end
|
43
|
+
subject { mongo }
|
44
|
+
|
45
|
+
its(:host) { should == host }
|
46
|
+
its(:port) { should == port }
|
47
|
+
its(:db) { should == db }
|
48
|
+
its(:pool_size) { should == pool_size }
|
49
|
+
its(:username) { should == username }
|
50
|
+
its(:password) { should == password }
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'uses the selected database' do
|
54
|
+
mongo.database.name.should == 'perpetuity_gem_test'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'removes all documents from a collection' do
|
58
|
+
mongo.insert klass, {}
|
59
|
+
mongo.delete_all klass
|
60
|
+
mongo.count(klass).should == 0
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'counts the documents in a collection' do
|
64
|
+
mongo.delete_all klass
|
65
|
+
3.times do
|
66
|
+
mongo.insert klass, {}
|
67
|
+
end
|
68
|
+
mongo.count(klass).should == 3
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'gets the first document in a collection' do
|
72
|
+
value = {value: 1}
|
73
|
+
mongo.insert klass, value
|
74
|
+
mongo.first(klass)[:hypothetical_value].should == value['value']
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'gets all of the documents in a collection' do
|
78
|
+
values = [{value: 1}, {value: 2}]
|
79
|
+
mongo.should_receive(:retrieve).with(Object, {}, {})
|
80
|
+
.and_return(values)
|
81
|
+
mongo.all(Object).should == values
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'retrieves by id if the id is a string' do
|
85
|
+
time = Time.now.utc
|
86
|
+
id = mongo.insert Article, {inserted: time}
|
87
|
+
objects = mongo.retrieve(Article, id: id.to_s).to_a
|
88
|
+
objects.map{|i| i["inserted"].to_f}.first.should be_within(0.001).of time.to_f
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'perpetuity/retrieval'
|
2
|
+
|
3
|
+
module Perpetuity
|
4
|
+
describe Retrieval do
|
5
|
+
let(:data_source) { double('data_source') }
|
6
|
+
let(:retrieval) { Perpetuity::Retrieval.new Object, {}, data_source }
|
7
|
+
subject { retrieval }
|
8
|
+
|
9
|
+
it "sorts the results" do
|
10
|
+
sorted = retrieval.sort(:name)
|
11
|
+
sorted.sort_attribute.should == :name
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reverses the sort order of the results" do
|
15
|
+
sorted = retrieval.sort(:name).reverse
|
16
|
+
sorted.sort_direction.should == :descending
|
17
|
+
end
|
18
|
+
|
19
|
+
it "limits the result set" do
|
20
|
+
retrieval.limit(1).result_limit.should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'indicates whether it includes a specific item' do
|
24
|
+
subject.stub(to_a: [1])
|
25
|
+
subject.should include 1
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'can be empty' do
|
29
|
+
retrieval.stub(to_a: [])
|
30
|
+
retrieval.should be_empty
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'pagination' do
|
34
|
+
let(:paginated) { retrieval.page(2) }
|
35
|
+
it 'paginates data' do
|
36
|
+
paginated.result_page.should == 2
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'defaults to 20 items per page' do
|
40
|
+
paginated.quantity_per_page.should == 20
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'sets the number of items per page' do
|
44
|
+
paginated.per_page(50).quantity_per_page.should == 50
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'retrieves data from the data source' do
|
49
|
+
return_data = { id: 0, a: 1, b: 2 }
|
50
|
+
options = { attribute: nil, direction: nil, limit: nil, page: nil }
|
51
|
+
data_source.should_receive(:retrieve).with(Object, {}, options).
|
52
|
+
and_return([return_data])
|
53
|
+
results = retrieval.to_a
|
54
|
+
|
55
|
+
results.map(&:id).should == [0]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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
|