mordor 0.3.0-java
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 +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +22 -0
- data/CHANGES.md +9 -0
- data/Gemfile +7 -0
- data/Guardfile +39 -0
- data/LICENSE +20 -0
- data/README.md +27 -0
- data/Rakefile +24 -0
- data/ext/mkrf_conf.rb +21 -0
- data/lib/mordor.rb +84 -0
- data/lib/mordor/collection.rb +50 -0
- data/lib/mordor/config.rb +91 -0
- data/lib/mordor/resource.rb +385 -0
- data/lib/mordor/version.rb +3 -0
- data/mordor.gemspec +38 -0
- data/spec/mordor/collection_spec.rb +159 -0
- data/spec/mordor/connection_spec.rb +143 -0
- data/spec/mordor/resource_spec.rb +519 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +41 -0
- data/tasks/github-gem.rake +376 -0
- metadata +158 -0
data/mordor.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require './lib/mordor/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'mordor'
|
5
|
+
|
6
|
+
# Do not set the version and date field manually, this is done by the release script
|
7
|
+
s.version = Mordor::VERSION
|
8
|
+
s.date = "2015-05-15"
|
9
|
+
|
10
|
+
s.summary = 'mordor'
|
11
|
+
s.description = <<-eos
|
12
|
+
Small gem to add MongoDB Resources, resources have attributes that translate into document fields. When an attribute is declared, finders for the attribute are added to the Resource automatically
|
13
|
+
eos
|
14
|
+
|
15
|
+
s.authors = ['Jan-Willem Koelewijn', 'Dirkjan Bussink']
|
16
|
+
s.email = ['janwillem.koelewijn@nedap.com', 'dirkjan.bussink@nedap.com']
|
17
|
+
s.homepage = 'http://www.nedap.com'
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'extlib'
|
20
|
+
s.add_runtime_dependency 'json'
|
21
|
+
s.add_runtime_dependency 'mongo', '< 2.0'
|
22
|
+
|
23
|
+
s.add_development_dependency 'rack-test'
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
s.add_development_dependency 'rspec', '~> 2.0', '< 2.99'
|
26
|
+
|
27
|
+
s.extensions << 'ext/mkrf_conf.rb'
|
28
|
+
|
29
|
+
if defined? JRUBY_VERSION
|
30
|
+
s.platform = 'java'
|
31
|
+
else
|
32
|
+
s.add_runtime_dependency('bson_ext')
|
33
|
+
end
|
34
|
+
|
35
|
+
# The files and test_files directives are set automatically by the release script.
|
36
|
+
# Do not change them by hand, but make sure to add the files to the git repository.
|
37
|
+
s.files = %w(.gitignore .travis.yml CHANGES.md Gemfile Guardfile LICENSE README.md Rakefile ext/mkrf_conf.rb lib/mordor.rb lib/mordor/collection.rb lib/mordor/config.rb lib/mordor/resource.rb lib/mordor/version.rb mordor.gemspec spec/mordor/collection_spec.rb spec/mordor/connection_spec.rb spec/mordor/resource_spec.rb spec/spec.opts spec/spec_helper.rb tasks/github-gem.rake)
|
38
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '/spec_helper.rb')
|
2
|
+
|
3
|
+
describe "with respect to collections" do
|
4
|
+
before :each do
|
5
|
+
class TestResource
|
6
|
+
include Mordor::Resource
|
7
|
+
|
8
|
+
attribute :first, :index => true
|
9
|
+
attribute :second, :index => true, :index_type => Mongo::ASCENDING
|
10
|
+
attribute :third, :finder_method => :find_by_third_attribute
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
after :each do
|
15
|
+
drop_db_collections
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "serialization" do
|
19
|
+
before :each do
|
20
|
+
5.times do |index|
|
21
|
+
res = TestResource.new(:first => "#{index}_first", :second => "#{index}_second", :third => "#{index}_third")
|
22
|
+
res.save.should be true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should correctly serialize a collection" do
|
27
|
+
collection = TestResource.all
|
28
|
+
collection.size.should == 5
|
29
|
+
|
30
|
+
json_collection = collection.to_json
|
31
|
+
json_collection.should_not be_nil
|
32
|
+
|
33
|
+
json_collection = JSON.parse(json_collection)
|
34
|
+
|
35
|
+
json_collection.size.should == 5
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "converting to array" do
|
40
|
+
before :each do
|
41
|
+
5.times do |index|
|
42
|
+
res = TestResource.new(:first => "#{index}_first", :second => "#{index}_second", :third => "#{index}_third")
|
43
|
+
res.save.should be true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be possible to convert a collection to an array" do
|
48
|
+
collection = TestResource.find_by_first("1_first")
|
49
|
+
collection.to_a.should be_a Array
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should be possible to convert multiple times after iterating using each" do
|
53
|
+
collection = TestResource.find_by_first("1_first")
|
54
|
+
collection.each do |resource|
|
55
|
+
resource.first
|
56
|
+
end
|
57
|
+
array1 = collection.to_a
|
58
|
+
array2 = collection.to_a
|
59
|
+
array1.size.should == array2.size
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should be possible to convert a collection to an array multiple times" do
|
63
|
+
collection = TestResource.find_by_first("1_first")
|
64
|
+
array1 = collection.to_a
|
65
|
+
array2 = collection.to_a
|
66
|
+
array1.size.should == array2.size
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should convert the collection to an array with the same size" do
|
70
|
+
collection = TestResource.find_by_first("1_first")
|
71
|
+
collection_size = collection.size
|
72
|
+
collection.to_a.size.should == collection_size
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "counting" do
|
77
|
+
|
78
|
+
before :each do
|
79
|
+
5.times do |index|
|
80
|
+
res = TestResource.new(:first => "#{index}_first", :second => "#{index}_second", :third => "#{index}_third")
|
81
|
+
res.save.should be true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should default to taking in account limits" do
|
86
|
+
TestResource.find({}, {:limit => 3}).count.should == 3
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not take in account limits when requested" do
|
90
|
+
TestResource.find({}, {:limit => 3}).count(false).should == 5
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should not take in account skips when requested" do
|
94
|
+
TestResource.find({}, {:skip => 2}).count(false).should == 5
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should not take in account skips and limits when requested" do
|
98
|
+
TestResource.find({}, {:skip => 1, :limit => 3}).count(false).should == 5
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should take in account skips by defaults" do
|
102
|
+
TestResource.find({}, {:skip => 2}).count.should == 3
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should take in account skips and limits by default" do
|
106
|
+
TestResource.find({}, {:skip => 1, :limit => 3}).count.should == 3
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "merging array based collection" do
|
111
|
+
before :each do
|
112
|
+
@first_collection = Mordor::Collection.new(TestResource, [TestResource.new(:first => "first", :second => "second", :third => "third")])
|
113
|
+
@second_collection = Mordor::Collection.new(TestResource, [TestResource.new(:first => "1st", :second => "2nd", :third => "3rd")])
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should not change original collections when no bang is used" do
|
117
|
+
first_size = @first_collection.size
|
118
|
+
second_size = @second_collection.size
|
119
|
+
|
120
|
+
new_collection = @first_collection.merge(@second_collection)
|
121
|
+
@first_collection.size.should == first_size
|
122
|
+
@second_collection.size.should == second_size
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should create collection with all elements from original collections" do
|
126
|
+
new_collection = @first_collection.merge(@second_collection)
|
127
|
+
new_collection.size.should == (@first_collection.size + @second_collection.size)
|
128
|
+
|
129
|
+
[@first_collection, @second_collection].each do |collection|
|
130
|
+
collection.each do |element|
|
131
|
+
new_collection.should include element
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should be possible to use the + as an alias" do
|
137
|
+
new_collection = @first_collection + @second_collection
|
138
|
+
new_collection.size.should == (@first_collection.size + @second_collection.size)
|
139
|
+
|
140
|
+
[@first_collection, @second_collection].each do |collection|
|
141
|
+
collection.each do |element|
|
142
|
+
new_collection.should include element
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should change the receiver of the merge! to have all elements" do
|
148
|
+
first_size = @first_collection.size
|
149
|
+
second_size = @second_collection.size
|
150
|
+
|
151
|
+
@first_collection.merge!(@second_collection)
|
152
|
+
@first_collection.size.should == (first_size + second_size)
|
153
|
+
|
154
|
+
@second_collection.each do |element|
|
155
|
+
@first_collection.should include element
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '/spec_helper.rb')
|
2
|
+
|
3
|
+
describe "connecting to mongo" do
|
4
|
+
before :each do
|
5
|
+
class TestResource
|
6
|
+
include Mordor::Resource
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'database connection' do
|
11
|
+
it "should have a mongo database " do
|
12
|
+
TestResource.database.should be_instance_of(Mongo::DB)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should select the correct database" do
|
16
|
+
database_name = "any_database_name"
|
17
|
+
Mordor::Config.use { |config| config[:database] = database_name }
|
18
|
+
|
19
|
+
TestResource.database.name.should == database_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "when credentials are provided" do
|
24
|
+
let(:credentials) { {:username => "A username", :password => "A password"} }
|
25
|
+
|
26
|
+
before :each do
|
27
|
+
Mordor::Config.use do |config|
|
28
|
+
config[:username] = credentials[:username]
|
29
|
+
config[:password] = credentials[:password]
|
30
|
+
end
|
31
|
+
|
32
|
+
@mock_db = double("db")
|
33
|
+
Mongo::Connection.stub(:new).and_return(double("connection", :db => @mock_db))
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should authenticate with username and password" do
|
37
|
+
@mock_db.should_receive(:authenticate).with(credentials[:username], credentials[:password])
|
38
|
+
TestResource.database
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "the Mongo database connection" do
|
43
|
+
before :each do
|
44
|
+
@mock_connection = double("connection", :db => double("db"))
|
45
|
+
end
|
46
|
+
|
47
|
+
after :each do
|
48
|
+
TestResource.database
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should connect with specified host" do
|
52
|
+
host = "any host IP or reachable hostname"
|
53
|
+
Mordor::Config.use { |config| config[:hostname] = host }
|
54
|
+
Mongo::Connection.should_receive(:new).with(host, anything).and_return(@mock_connection)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should connect on specified port" do
|
58
|
+
port = rand(10000)
|
59
|
+
Mordor::Config.use { |config| config[:port] = port }
|
60
|
+
Mongo::Connection.should_receive(:new).with(anything, port).and_return(@mock_connection)
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'setting connection pool options' do
|
64
|
+
it 'supports setting pool size' do
|
65
|
+
Mordor::Config.use { |config| config[:pool_size] = 7 }
|
66
|
+
Mongo::Connection.should_receive(:new).with('localhost', 27017, pool_size: 7).and_return(@mock_connection)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'supports setting pool timeout' do
|
70
|
+
Mordor::Config.use { |config| config[:pool_timeout] = 8 }
|
71
|
+
Mongo::Connection.should_receive(:new).with('localhost', 27017, pool_timeout: 8).and_return(@mock_connection)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'supports setting both' do
|
75
|
+
Mordor::Config.use { |config|
|
76
|
+
config[:pool_size] = 9
|
77
|
+
config[:pool_timeout] = 10
|
78
|
+
}
|
79
|
+
Mongo::Connection.should_receive(:new).with('localhost', 27017, pool_size: 9, pool_timeout: 10).and_return(@mock_connection)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "replica sets" do
|
85
|
+
before :each do
|
86
|
+
@mock_connection = double("connection", :db => double("db"))
|
87
|
+
Mordor::Config.use { |config| config[:hostname] = host_string }
|
88
|
+
end
|
89
|
+
|
90
|
+
after :each do
|
91
|
+
TestResource.database
|
92
|
+
end
|
93
|
+
|
94
|
+
let(:host_string){ 'localhost:27017, localhost:27018 ' }
|
95
|
+
let(:hosts_array){ host_string.split(',').map(&:strip) }
|
96
|
+
let(:replica_set_string){ 'sample replica set' }
|
97
|
+
|
98
|
+
it "creates a mongo replica set client when multiple hosts are provided" do
|
99
|
+
Mongo::MongoReplicaSetClient.should_receive(:new).with(hosts_array, anything).and_return(@mock_connection)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "creates a mongo replica set client with the correct replica set name if given" do
|
103
|
+
Mordor::Config.use do |config|
|
104
|
+
config[:replica_set] = replica_set_string
|
105
|
+
end
|
106
|
+
options = {:rs_name => replica_set_string, :refresh_mode => :sync}
|
107
|
+
|
108
|
+
Mongo::MongoReplicaSetClient.should_receive(:new).with(anything, options).and_return(@mock_connection)
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'setting connection pool options' do
|
112
|
+
it 'supports setting pool size' do
|
113
|
+
Mordor::Config.use do |config|
|
114
|
+
config[:pool_size] = 1
|
115
|
+
end
|
116
|
+
options = {:pool_size => 1, :refresh_mode => :sync}
|
117
|
+
|
118
|
+
Mongo::MongoReplicaSetClient.should_receive(:new).with(anything, options).and_return(@mock_connection)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'supports setting pool timeout' do
|
122
|
+
Mordor::Config.use do |config|
|
123
|
+
config[:pool_timeout] = 1
|
124
|
+
end
|
125
|
+
|
126
|
+
options = {:pool_timeout => 1, :refresh_mode => :sync}
|
127
|
+
Mongo::MongoReplicaSetClient.should_receive(:new).with(anything, options).and_return(@mock_connection)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'supports setting both' do
|
131
|
+
Mordor::Config.use do |config|
|
132
|
+
config[:pool_size] = 5
|
133
|
+
config[:pool_timeout] = 1
|
134
|
+
config[:replica_set] = replica_set_string
|
135
|
+
end
|
136
|
+
options = {:pool_size => 5, :pool_timeout => 1, :refresh_mode => :sync, :rs_name => replica_set_string}
|
137
|
+
|
138
|
+
Mongo::MongoReplicaSetClient.should_receive(:new).with(anything, options).and_return(@mock_connection)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,519 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '/spec_helper.rb')
|
2
|
+
|
3
|
+
describe "with respect to resources" do
|
4
|
+
before :each do
|
5
|
+
class TestResource
|
6
|
+
include Mordor::Resource
|
7
|
+
|
8
|
+
attribute :first, :index => true
|
9
|
+
attribute :second, :index => true, :index_type => Mongo::ASCENDING
|
10
|
+
attribute :third, :finder_method => :find_by_third_attribute
|
11
|
+
attribute :at
|
12
|
+
attribute :created_at, :timestamp => true
|
13
|
+
|
14
|
+
# Put this in here again to ensure the original method is still here
|
15
|
+
class_eval do
|
16
|
+
def self.ensure_indices
|
17
|
+
collection.ensure_index( indices.map{|index| [index.to_s, Mongo::DESCENDING]} ) if indices.any?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
after :each do
|
24
|
+
drop_db_collections
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should create accessor methods for all attributes" do
|
28
|
+
["first", "first=", "second", "second="].each{ |v| TestResource.public_instance_methods.map{|m| m.to_s}.should include(v) }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should create class level finder methods for all attributes" do
|
32
|
+
["find_by_first", "find_by_second"].each do |finder_method|
|
33
|
+
TestResource.methods.map{|m| m.to_s}.should include(finder_method)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should create finder methods with the supplied finder method name" do
|
38
|
+
TestResource.methods.map{|m| m.to_s}.should include "find_by_third_attribute"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should ensure indices when the option :index => true is given" do
|
42
|
+
TestResource.send(:indices).should include :first
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should default to descending indices" do
|
46
|
+
TestResource.send(:index_types).keys.should include :first
|
47
|
+
TestResource.send(:index_types)[:first].should == Mongo::DESCENDING
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be possible to set index type using the 'index_type' option" do
|
51
|
+
TestResource.send(:index_types).keys.should include :second
|
52
|
+
TestResource.send(:index_types)[:second].should == Mongo::ASCENDING
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should be possible to designate an attribute as a timestamp" do
|
56
|
+
TestResource.timestamped_attribute.should_not be_nil
|
57
|
+
TestResource.timestamped_attribute.should == :created_at
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should only be possible to have one attribute as a timestamp" do
|
61
|
+
lambda {
|
62
|
+
TestResource2.class_eval do
|
63
|
+
attribute :some_timestamp, :timestamp => true
|
64
|
+
attribute :another_timestamp, :timestamp => true
|
65
|
+
end
|
66
|
+
}.should raise_error
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should provide timestamped attribute as first attribute when creating a Resource" do
|
70
|
+
tr = TestResource.create({:first => 'first'})
|
71
|
+
tr.reload
|
72
|
+
tr.at.should_not be_nil
|
73
|
+
TestResource.get(tr._id).at.should_not == BSON::Timestamp.new(0,0)
|
74
|
+
end
|
75
|
+
|
76
|
+
context "with respect to replacing params" do
|
77
|
+
it "should correctly substitute non-alphanumeric characters in keys with underscores" do
|
78
|
+
options = {
|
79
|
+
"o*p#t>i_o@n)s" => "test"
|
80
|
+
}
|
81
|
+
result = TestResource.new.replace_params(options)
|
82
|
+
result.keys.first.should eql "o_p_t_i_o_n_s"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should correctly replace Date and DateTimes" do
|
86
|
+
options = {
|
87
|
+
"option" => Date.today,
|
88
|
+
"another" => DateTime.now
|
89
|
+
}
|
90
|
+
result = TestResource.new.replace_params(options)
|
91
|
+
result.each do |k, v|
|
92
|
+
v.should be_a Time
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should correctly replace BigDecimals" do
|
97
|
+
options = {
|
98
|
+
"option" => BigDecimal.new("1.00")
|
99
|
+
}
|
100
|
+
result = TestResource.new.replace_params(options)
|
101
|
+
result.each do |k,v|
|
102
|
+
v.should be_a Float
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should correctly replace BSON::Timestamps" do
|
107
|
+
options = {
|
108
|
+
"option" => BSON::Timestamp.new(324244, 12)
|
109
|
+
}
|
110
|
+
result = TestResource.new.replace_params(options)
|
111
|
+
result.each do |k, v|
|
112
|
+
v["seconds"].should == 324244
|
113
|
+
v["increment"].should == 12
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should correctly respond to to_hash" do
|
118
|
+
resource = TestResource.new({:first => "first", :second => "second", :third => "third"})
|
119
|
+
hash = resource.to_hash
|
120
|
+
hash.size.should == 5
|
121
|
+
hash[:first].should == "first"
|
122
|
+
hash[:second].should == "second"
|
123
|
+
hash[:third].should == "third"
|
124
|
+
hash[:at].should == ""
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "with respect to times and ranges" do
|
129
|
+
context "when DateTime days are given" do
|
130
|
+
it "should return a correct range" do
|
131
|
+
day = DateTime.civil(2012, 1, 19, 10, 0)
|
132
|
+
range = TestResource.send(:day_to_range, day)
|
133
|
+
range.first.should == DateTime.civil(2012, 1, 19).to_time.gmtime
|
134
|
+
range.last.should == DateTime.civil(2012, 1, 20).to_time.gmtime
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should return an Array of 2 Time objects" do
|
138
|
+
day = DateTime.civil(2012, 1, 19, 10, 0)
|
139
|
+
range = TestResource.send(:day_to_range, day)
|
140
|
+
range.first.should be_a Time
|
141
|
+
range.last.should be_a Time
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when Date days are given" do
|
146
|
+
it "should return a correct range" do
|
147
|
+
day = Date.parse("2012-1-19")
|
148
|
+
range = TestResource.send(:day_to_range, day)
|
149
|
+
range.first.should == Date.parse("2012-1-19").to_time.gmtime
|
150
|
+
range.last.should == Date.parse("2012-1-20").to_time.gmtime
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should return an Array of 2 Time objects" do
|
154
|
+
day = Date.parse("2012-1-19")
|
155
|
+
range = TestResource.send(:day_to_range, day)
|
156
|
+
range.first.should be_a Time
|
157
|
+
range.last.should be_a Time
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "when Time days are given" do
|
162
|
+
it "should return a correct range" do
|
163
|
+
day = DateTime.civil(2012, 1, 19, 10, 0).to_time
|
164
|
+
range = TestResource.send(:day_to_range, day)
|
165
|
+
range.first.should == DateTime.civil(2012, 1, 19).to_time.gmtime
|
166
|
+
range.last.should == DateTime.civil(2012, 1, 20).to_time.gmtime
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should return an Array of 2 Time objects" do
|
170
|
+
day = DateTime.civil(2012, 1, 19, 10, 0).to_time
|
171
|
+
range = TestResource.send(:day_to_range, day)
|
172
|
+
range.first.should be_a Time
|
173
|
+
range.last.should be_a Time
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when ranges are changed to queries" do
|
178
|
+
before :each do
|
179
|
+
@range = TestResource.send(:day_to_range, DateTime.civil(2012, 1, 19))
|
180
|
+
@query = TestResource.send(:date_range_to_query, @range)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should scope the query to the 'at' attribute" do
|
184
|
+
@query.size.should == 1
|
185
|
+
@query[:at].should be_a Hash
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should use the first of the range for the greater equal part" do
|
189
|
+
@query[:at][:$gte].should == @range.first
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should use the last of the range for the smaller than part" do
|
193
|
+
@query[:at][:$lt].should == @range.last
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "with respect to indices" do
|
199
|
+
before :each do
|
200
|
+
class TestResource2
|
201
|
+
include Mordor::Resource
|
202
|
+
end
|
203
|
+
|
204
|
+
[TestResource, TestResource2].each do |klass|
|
205
|
+
klass.class_eval do
|
206
|
+
def self.reset_ensure_count
|
207
|
+
@count = 0
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.ensure_count
|
211
|
+
@count ||= 0
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.ensure_count=(val)
|
215
|
+
@count = val
|
216
|
+
end
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
def self.do_ensure_index(attribute)
|
221
|
+
collection.ensure_index( [ [attribute.to_s, index_types[attribute]] ] )
|
222
|
+
end
|
223
|
+
|
224
|
+
def self.ensure_indices
|
225
|
+
indices.each do |index|
|
226
|
+
ensure_index(index)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.ensure_index(attribute)
|
231
|
+
self.ensure_count += 1
|
232
|
+
self.do_ensure_index(attribute)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should call ensure_index on the collection for each index when a query is performed" do
|
239
|
+
TestResource.create({:first => 'first', :second => 'second', :third => 'third'})
|
240
|
+
TestResource.reset_ensure_count
|
241
|
+
TestResource.all()
|
242
|
+
TestResource.ensure_count.should == 2 # For each index
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should call ensure_index on the collection whenever a resource is destroyed" do
|
246
|
+
resource = TestResource.create({:first => 'first', :second => 'second', :third => 'third'})
|
247
|
+
TestResource.reset_ensure_count
|
248
|
+
resource.destroy
|
249
|
+
TestResource.ensure_count.should == 2 # For each index
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should call ensure index for each index attribute on creation" do
|
253
|
+
TestResource2.class_eval do
|
254
|
+
attribute :test_attribute, :index => true
|
255
|
+
end
|
256
|
+
|
257
|
+
TestResource2.ensure_count.should == 1
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
context "with respect to creating" do
|
262
|
+
before :each do
|
263
|
+
@resource = TestResource.create({:first => "first", :second => "second", :third => "third"})
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should be possible to create a resource" do
|
267
|
+
@resource.should be_saved
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should be possible to retrieve created resources" do
|
271
|
+
res = TestResource.get(@resource._id)
|
272
|
+
res.should_not be_nil
|
273
|
+
res.first.should eql @resource.first
|
274
|
+
res.second.should eql @resource.second
|
275
|
+
res.third.should eql @resource.third
|
276
|
+
res._id.should eql @resource._id
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context "with respect to destroying" do
|
281
|
+
before :each do
|
282
|
+
@resource = TestResource.create({:first => "first", :second => "second", :third => "third"})
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should not create destroyed resources" do
|
286
|
+
@resource.should_not be_destroyed
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should be possible to destroy a resource" do
|
290
|
+
@resource.should_not be_destroyed
|
291
|
+
@resource.destroy
|
292
|
+
@resource.should be_destroyed
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should not be possible to retrieve a resource after it has been destroyed" do
|
296
|
+
@resource.destroy
|
297
|
+
res = TestResource.get(@resource._id)
|
298
|
+
res.should be_nil
|
299
|
+
end
|
300
|
+
|
301
|
+
it "should only destroy the current resource" do
|
302
|
+
resource2 = TestResource.create({:first => "first2", :second => "second2", :third => "third2"})
|
303
|
+
@resource.destroy
|
304
|
+
TestResource.get(resource2._id).should_not be_nil
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
context "with respect to saving and retrieving" do
|
309
|
+
it "should correctly save resources" do
|
310
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
311
|
+
resource.save.should be_true
|
312
|
+
resource._id.should_not be_nil
|
313
|
+
resource.collection.count.should == 1
|
314
|
+
resource.collection.find_one['_id'].should == resource._id
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should correctly update resources" do
|
318
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
319
|
+
resource.save.should be_true
|
320
|
+
resource._id.should_not be_nil
|
321
|
+
|
322
|
+
original_id = resource._id
|
323
|
+
|
324
|
+
resource.collection.count.should == 1
|
325
|
+
resource.collection.find_one['_id'].should == resource._id
|
326
|
+
|
327
|
+
resource.first = "third"
|
328
|
+
resource.save.should be_true
|
329
|
+
resource._id.should == original_id
|
330
|
+
resource.collection.find_one['first'].should == resource.first
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should be able to find resources by their ids" do
|
334
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
335
|
+
resource.save.should be_true
|
336
|
+
res = TestResource.find_by_id(resource._id)
|
337
|
+
res._id.should == resource._id
|
338
|
+
res.first.should == resource.first
|
339
|
+
res.second.should == resource.second
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should be able to find resources by their ids as strings" do
|
343
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
344
|
+
resource.save.should be_true
|
345
|
+
res = TestResource.find_by_id(resource._id.to_s)
|
346
|
+
res._id.should == resource._id
|
347
|
+
res.first.should == resource.first
|
348
|
+
res.second.should == resource.second
|
349
|
+
end
|
350
|
+
|
351
|
+
it "should be possible to find resources using queries" do
|
352
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
353
|
+
resource.save.should be_true
|
354
|
+
|
355
|
+
resource2 = TestResource.new({:first => "first", :second => "2nd"})
|
356
|
+
resource2.save.should be_true
|
357
|
+
|
358
|
+
collection = TestResource.find({:first => "first"})
|
359
|
+
collection.should_not be_nil
|
360
|
+
collection.size.should == 2
|
361
|
+
|
362
|
+
collection = TestResource.find({:second => "2nd"})
|
363
|
+
collection.should_not be_nil
|
364
|
+
collection.size.should == 1
|
365
|
+
end
|
366
|
+
|
367
|
+
it "should be possible to query with a limit" do
|
368
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
369
|
+
resource.save.should be_true
|
370
|
+
|
371
|
+
resource2 = TestResource.new({:first => "first", :second => "2nd"})
|
372
|
+
resource2.save.should be_true
|
373
|
+
|
374
|
+
collection = TestResource.find({:first => "first"}, :limit => 1)
|
375
|
+
collection.should_not be_nil
|
376
|
+
collection.size.should == 1
|
377
|
+
end
|
378
|
+
|
379
|
+
it "should be possible to retrieve all resources" do
|
380
|
+
TestResource.all.should_not be_nil
|
381
|
+
TestResource.all.size.should == 0
|
382
|
+
|
383
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
384
|
+
resource.save.should be_true
|
385
|
+
|
386
|
+
resource2 = TestResource.new({:first => "first", :second => "second"})
|
387
|
+
resource2.save.should be_true
|
388
|
+
|
389
|
+
collection = TestResource.all
|
390
|
+
collection.should_not be_nil
|
391
|
+
collection.size.should == 2
|
392
|
+
end
|
393
|
+
|
394
|
+
it "should be possible to limit the number of returned resources" do
|
395
|
+
TestResource.all.should_not be_nil
|
396
|
+
TestResource.all.size.should == 0
|
397
|
+
|
398
|
+
resource = TestResource.new({:first => "first", :second => "second"})
|
399
|
+
resource.save.should be_true
|
400
|
+
|
401
|
+
resource2 = TestResource.new({:first => "first", :second => "second"})
|
402
|
+
resource2.save.should be_true
|
403
|
+
|
404
|
+
collection = TestResource.all(:limit => 1)
|
405
|
+
collection.should_not be_nil
|
406
|
+
collection.size.should == 1
|
407
|
+
end
|
408
|
+
|
409
|
+
describe "with respect to passing extra query parameters to finder methods" do
|
410
|
+
before :each do
|
411
|
+
5.times do |i|
|
412
|
+
TestResource.create({:first => "first", :second => "second-#{i}", :third => "third-#{i}", :at => (Date.today).to_time})
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
it "should raise an argument exception if the :value option is omitted from a complex finder query" do
|
417
|
+
collection = TestResource.find_by_first("first")
|
418
|
+
collection.size.should == 5
|
419
|
+
|
420
|
+
lambda{ TestResource.find_by_first({:second => "second-2"})}.should raise_error
|
421
|
+
end
|
422
|
+
|
423
|
+
it "should be possible to add extra query clauses to the find_by_day method" do
|
424
|
+
collection = TestResource.find_by_day(Date.today)
|
425
|
+
collection.size.should == 5
|
426
|
+
|
427
|
+
collection = TestResource.find_by_day({:value => Date.today, :second => "second-1"})
|
428
|
+
collection.size.should == 1
|
429
|
+
resource = collection.first
|
430
|
+
resource.first.should == "first"
|
431
|
+
resource.at.should == Date.today.to_time
|
432
|
+
end
|
433
|
+
|
434
|
+
it "should be possible to add more complex query clauses to the find_by_day method" do
|
435
|
+
collection = TestResource.find_by_day(Date.today)
|
436
|
+
collection.size.should == 5
|
437
|
+
|
438
|
+
collection = TestResource.find_by_day({:value => Date.today, :second => {:$in => ["second-1", "second-2"]}})
|
439
|
+
collection.size.should == 2
|
440
|
+
collection.each do |res|
|
441
|
+
res.at.should == Date.today.to_time
|
442
|
+
["second-1", "second-2"].should include res.second
|
443
|
+
end
|
444
|
+
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should be possible to add extra query clauses to a finder method" do
|
448
|
+
collection = TestResource.find_by_first("first")
|
449
|
+
collection.size.should == 5
|
450
|
+
|
451
|
+
collection = TestResource.find_by_first({:value => "first", :second => "second-2"})
|
452
|
+
collection.size.should == 1
|
453
|
+
resource = collection.first
|
454
|
+
resource.first.should == "first"
|
455
|
+
resource.second.should == "second-2"
|
456
|
+
end
|
457
|
+
|
458
|
+
it "should be possible to add more complex query clauses to a finder method" do
|
459
|
+
collection = TestResource.find_by_first("first")
|
460
|
+
collection.size.should == 5
|
461
|
+
|
462
|
+
collection = TestResource.find_by_first({:value => "first", :second => {:$in => ["second-1", "second-2"]}})
|
463
|
+
collection.size.should == 2
|
464
|
+
collection.each do |res|
|
465
|
+
res.first.should == "first"
|
466
|
+
["second-1", "second-2"].should include res.second
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
context "with respect to retrieving by day" do
|
473
|
+
before :each do
|
474
|
+
class TestTimedResource
|
475
|
+
include Mordor::Resource
|
476
|
+
|
477
|
+
attribute :first
|
478
|
+
attribute :at
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
it "should be possible to retrieve a Resource by day" do
|
483
|
+
TestTimedResource.create({:first => "hallo", :at => DateTime.civil(2011, 11, 11, 11, 11)})
|
484
|
+
|
485
|
+
col = TestTimedResource.find_by_day(DateTime.civil(2011,11,11))
|
486
|
+
col.size.should == 1
|
487
|
+
col.first.first.should eql "hallo"
|
488
|
+
end
|
489
|
+
|
490
|
+
it "should not retrieve resources from other days" do
|
491
|
+
TestTimedResource.create({:first => "hallo", :at => DateTime.civil(2011, 11, 11, 11, 11)})
|
492
|
+
|
493
|
+
col = TestTimedResource.find_by_day(DateTime.civil(2011,11,10))
|
494
|
+
col.size.should == 0
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
context "with respect to collections" do
|
499
|
+
it "should correctly return a collection name" do
|
500
|
+
TestResource.collection_name.should == "testresources"
|
501
|
+
end
|
502
|
+
|
503
|
+
it "should be connected to a database" do
|
504
|
+
TestResource.database.should_not be_nil
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
context "with respect to not finding something" do
|
509
|
+
it "should just return an empty collection when a collection query doesn't return results" do
|
510
|
+
col = TestResource.find_by_day(DateTime.civil(2011, 11, 8))
|
511
|
+
col.size.should == 0
|
512
|
+
end
|
513
|
+
|
514
|
+
it "should return nil when an non existing id is queried" do
|
515
|
+
resource = TestResource.find_by_id('4eb8f3570e02e10cce000002')
|
516
|
+
resource.should be_nil
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|