mark_mapper 0.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.rdoc +39 -0
- data/examples/attr_accessible.rb +24 -0
- data/examples/attr_protected.rb +24 -0
- data/examples/cache_key.rb +26 -0
- data/examples/custom_types.rb +26 -0
- data/examples/identity_map.rb +30 -0
- data/examples/identity_map/automatic.rb +2 -0
- data/examples/keys.rb +42 -0
- data/examples/modifiers/set.rb +27 -0
- data/examples/plugins.rb +40 -0
- data/examples/querying.rb +39 -0
- data/examples/sample_app.rb +43 -0
- data/examples/scopes.rb +56 -0
- data/examples/validating/embedded_docs.rb +31 -0
- data/lib/mark_mapper.rb +125 -0
- data/lib/mark_mapper/config.rb +90 -0
- data/lib/mark_mapper/connection.rb +60 -0
- data/lib/mark_mapper/criteria_hash.rb +194 -0
- data/lib/mark_mapper/document.rb +46 -0
- data/lib/mark_mapper/embedded_document.rb +32 -0
- data/lib/mark_mapper/exceptions.rb +33 -0
- data/lib/mark_mapper/extensions/array.rb +27 -0
- data/lib/mark_mapper/extensions/boolean.rb +45 -0
- data/lib/mark_mapper/extensions/date.rb +29 -0
- data/lib/mark_mapper/extensions/duplicable.rb +86 -0
- data/lib/mark_mapper/extensions/float.rb +18 -0
- data/lib/mark_mapper/extensions/hash.rb +26 -0
- data/lib/mark_mapper/extensions/integer.rb +27 -0
- data/lib/mark_mapper/extensions/kernel.rb +11 -0
- data/lib/mark_mapper/extensions/nil_class.rb +18 -0
- data/lib/mark_mapper/extensions/object.rb +30 -0
- data/lib/mark_mapper/extensions/object_id.rb +18 -0
- data/lib/mark_mapper/extensions/set.rb +20 -0
- data/lib/mark_mapper/extensions/string.rb +31 -0
- data/lib/mark_mapper/extensions/symbol.rb +87 -0
- data/lib/mark_mapper/extensions/time.rb +29 -0
- data/lib/mark_mapper/locale/en.yml +5 -0
- data/lib/mark_mapper/middleware/identity_map.rb +41 -0
- data/lib/mark_mapper/normalizers/criteria_hash_key.rb +17 -0
- data/lib/mark_mapper/normalizers/criteria_hash_value.rb +66 -0
- data/lib/mark_mapper/normalizers/fields_value.rb +26 -0
- data/lib/mark_mapper/normalizers/hash_key.rb +19 -0
- data/lib/mark_mapper/normalizers/integer.rb +19 -0
- data/lib/mark_mapper/normalizers/options_hash_value.rb +83 -0
- data/lib/mark_mapper/normalizers/sort_value.rb +55 -0
- data/lib/mark_mapper/options_hash.rb +103 -0
- data/lib/mark_mapper/pagination.rb +6 -0
- data/lib/mark_mapper/pagination/collection.rb +32 -0
- data/lib/mark_mapper/pagination/paginator.rb +46 -0
- data/lib/mark_mapper/plugins.rb +22 -0
- data/lib/mark_mapper/plugins/accessible.rb +61 -0
- data/lib/mark_mapper/plugins/active_model.rb +18 -0
- data/lib/mark_mapper/plugins/associations.rb +96 -0
- data/lib/mark_mapper/plugins/associations/base.rb +98 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_association.rb +63 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +35 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
- data/lib/mark_mapper/plugins/associations/collection.rb +29 -0
- data/lib/mark_mapper/plugins/associations/embedded_collection.rb +44 -0
- data/lib/mark_mapper/plugins/associations/in_array_proxy.rb +133 -0
- data/lib/mark_mapper/plugins/associations/many_association.rb +63 -0
- data/lib/mark_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
- data/lib/mark_mapper/plugins/associations/many_documents_proxy.rb +142 -0
- data/lib/mark_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
- data/lib/mark_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
- data/lib/mark_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
- data/lib/mark_mapper/plugins/associations/one_as_proxy.rb +22 -0
- data/lib/mark_mapper/plugins/associations/one_association.rb +48 -0
- data/lib/mark_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
- data/lib/mark_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
- data/lib/mark_mapper/plugins/associations/one_proxy.rb +95 -0
- data/lib/mark_mapper/plugins/associations/proxy.rb +138 -0
- data/lib/mark_mapper/plugins/associations/single_association.rb +46 -0
- data/lib/mark_mapper/plugins/caching.rb +21 -0
- data/lib/mark_mapper/plugins/callbacks.rb +42 -0
- data/lib/mark_mapper/plugins/clone.rb +24 -0
- data/lib/mark_mapper/plugins/counter_cache.rb +97 -0
- data/lib/mark_mapper/plugins/dirty.rb +61 -0
- data/lib/mark_mapper/plugins/document.rb +41 -0
- data/lib/mark_mapper/plugins/dumpable.rb +22 -0
- data/lib/mark_mapper/plugins/dynamic_querying.rb +45 -0
- data/lib/mark_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
- data/lib/mark_mapper/plugins/embedded_callbacks.rb +81 -0
- data/lib/mark_mapper/plugins/embedded_document.rb +53 -0
- data/lib/mark_mapper/plugins/equality.rb +23 -0
- data/lib/mark_mapper/plugins/identity_map.rb +144 -0
- data/lib/mark_mapper/plugins/indexable.rb +86 -0
- data/lib/mark_mapper/plugins/inspect.rb +16 -0
- data/lib/mark_mapper/plugins/keys.rb +470 -0
- data/lib/mark_mapper/plugins/keys/key.rb +134 -0
- data/lib/mark_mapper/plugins/keys/static.rb +45 -0
- data/lib/mark_mapper/plugins/logger.rb +18 -0
- data/lib/mark_mapper/plugins/modifiers.rb +140 -0
- data/lib/mark_mapper/plugins/pagination.rb +16 -0
- data/lib/mark_mapper/plugins/partial_updates.rb +77 -0
- data/lib/mark_mapper/plugins/persistence.rb +79 -0
- data/lib/mark_mapper/plugins/protected.rb +45 -0
- data/lib/mark_mapper/plugins/querying.rb +173 -0
- data/lib/mark_mapper/plugins/querying/decorated_markmapper_query.rb +75 -0
- data/lib/mark_mapper/plugins/rails.rb +79 -0
- data/lib/mark_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
- data/lib/mark_mapper/plugins/sci.rb +82 -0
- data/lib/mark_mapper/plugins/scopes.rb +28 -0
- data/lib/mark_mapper/plugins/serialization.rb +109 -0
- data/lib/mark_mapper/plugins/timestamps.rb +29 -0
- data/lib/mark_mapper/plugins/touch.rb +18 -0
- data/lib/mark_mapper/plugins/userstamps.rb +18 -0
- data/lib/mark_mapper/plugins/validations.rb +96 -0
- data/lib/mark_mapper/query.rb +278 -0
- data/lib/mark_mapper/railtie.rb +52 -0
- data/lib/mark_mapper/railtie/database.rake +65 -0
- data/lib/mark_mapper/translation.rb +10 -0
- data/lib/mark_mapper/version.rb +4 -0
- data/lib/rails/generators/mark_mapper/config/config_generator.rb +37 -0
- data/lib/rails/generators/mark_mapper/config/templates/marklogic.yml +19 -0
- data/lib/rails/generators/mark_mapper/model/model_generator.rb +40 -0
- data/lib/rails/generators/mark_mapper/model/templates/model.rb +17 -0
- data/spec/config/mark_mapper.yml +6 -0
- data/spec/examples_spec.rb +25 -0
- data/spec/functional/accessible_spec.rb +198 -0
- data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +64 -0
- data/spec/functional/associations/belongs_to_proxy_spec.rb +255 -0
- data/spec/functional/associations/in_array_proxy_spec.rb +349 -0
- data/spec/functional/associations/many_documents_as_proxy_spec.rb +230 -0
- data/spec/functional/associations/many_documents_proxy_spec.rb +968 -0
- data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +238 -0
- data/spec/functional/associations/many_embedded_proxy_spec.rb +288 -0
- data/spec/functional/associations/many_polymorphic_proxy_spec.rb +302 -0
- data/spec/functional/associations/one_as_proxy_spec.rb +489 -0
- data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +207 -0
- data/spec/functional/associations/one_embedded_proxy_spec.rb +100 -0
- data/spec/functional/associations/one_proxy_spec.rb +406 -0
- data/spec/functional/associations_spec.rb +48 -0
- data/spec/functional/caching_spec.rb +75 -0
- data/spec/functional/callbacks_spec.rb +330 -0
- data/spec/functional/counter_cache_spec.rb +235 -0
- data/spec/functional/dirty_spec.rb +316 -0
- data/spec/functional/document_spec.rb +310 -0
- data/spec/functional/dumpable_spec.rb +24 -0
- data/spec/functional/dynamic_querying_spec.rb +75 -0
- data/spec/functional/embedded_document_spec.rb +316 -0
- data/spec/functional/equality_spec.rb +20 -0
- data/spec/functional/extensions_spec.rb +16 -0
- data/spec/functional/identity_map_spec.rb +483 -0
- data/spec/functional/keys_spec.rb +339 -0
- data/spec/functional/logger_spec.rb +20 -0
- data/spec/functional/modifiers_spec.rb +446 -0
- data/spec/functional/options_hash_spec.rb +41 -0
- data/spec/functional/pagination_spec.rb +89 -0
- data/spec/functional/partial_updates_spec.rb +530 -0
- data/spec/functional/protected_spec.rb +199 -0
- data/spec/functional/querying_spec.rb +984 -0
- data/spec/functional/rails_spec.rb +55 -0
- data/spec/functional/sci_spec.rb +374 -0
- data/spec/functional/scopes_spec.rb +204 -0
- data/spec/functional/static_keys_spec.rb +153 -0
- data/spec/functional/timestamps_spec.rb +97 -0
- data/spec/functional/touch_spec.rb +125 -0
- data/spec/functional/userstamps_spec.rb +46 -0
- data/spec/functional/validations_spec.rb +416 -0
- data/spec/quality_spec.rb +51 -0
- data/spec/spec_helper.rb +150 -0
- data/spec/support/matchers.rb +15 -0
- data/spec/support/models.rb +256 -0
- data/spec/symbol_operator_spec.rb +70 -0
- data/spec/symbol_spec.rb +9 -0
- data/spec/unit/associations/base_spec.rb +146 -0
- data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
- data/spec/unit/associations/many_association_spec.rb +64 -0
- data/spec/unit/associations/one_association_spec.rb +48 -0
- data/spec/unit/associations/proxy_spec.rb +103 -0
- data/spec/unit/clone_spec.rb +79 -0
- data/spec/unit/config_generator_spec.rb +24 -0
- data/spec/unit/criteria_hash_spec.rb +218 -0
- data/spec/unit/document_spec.rb +251 -0
- data/spec/unit/dynamic_finder_spec.rb +125 -0
- data/spec/unit/embedded_document_spec.rb +676 -0
- data/spec/unit/equality_spec.rb +38 -0
- data/spec/unit/exceptions_spec.rb +12 -0
- data/spec/unit/extensions_spec.rb +368 -0
- data/spec/unit/identity_map_middleware_spec.rb +134 -0
- data/spec/unit/inspect_spec.rb +47 -0
- data/spec/unit/key_spec.rb +276 -0
- data/spec/unit/keys_spec.rb +155 -0
- data/spec/unit/mark_mapper_spec.rb +37 -0
- data/spec/unit/model_generator_spec.rb +45 -0
- data/spec/unit/normalizers/criteria_hash_key_spec.rb +37 -0
- data/spec/unit/normalizers/criteria_hash_value_spec.rb +200 -0
- data/spec/unit/normalizers/fields_value_spec.rb +45 -0
- data/spec/unit/normalizers/hash_key_spec.rb +15 -0
- data/spec/unit/normalizers/integer_spec.rb +24 -0
- data/spec/unit/normalizers/options_hash_value_spec.rb +99 -0
- data/spec/unit/normalizers/sort_value_spec.rb +98 -0
- data/spec/unit/options_hash_spec.rb +64 -0
- data/spec/unit/pagination/collection_spec.rb +30 -0
- data/spec/unit/pagination/paginator_spec.rb +118 -0
- data/spec/unit/pagination_spec.rb +11 -0
- data/spec/unit/plugins_spec.rb +89 -0
- data/spec/unit/query_spec.rb +837 -0
- data/spec/unit/rails_compatibility_spec.rb +40 -0
- data/spec/unit/rails_reflect_on_association_spec.rb +118 -0
- data/spec/unit/rails_spec.rb +188 -0
- data/spec/unit/serialization_spec.rb +169 -0
- data/spec/unit/serializers/json_serializer_spec.rb +218 -0
- data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
- data/spec/unit/time_zones_spec.rb +44 -0
- data/spec/unit/translation_spec.rb +27 -0
- data/spec/unit/validations_spec.rb +588 -0
- metadata +307 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mark_mapper/normalizers/sort_value'
|
3
|
+
|
4
|
+
describe MarkMapper::Normalizers::SortValue do
|
5
|
+
let(:key_normalizer) {
|
6
|
+
MarkMapper::Normalizers::HashKey.new({:id => :_id})
|
7
|
+
}
|
8
|
+
|
9
|
+
subject {
|
10
|
+
described_class.new({
|
11
|
+
:key_normalizer => key_normalizer,
|
12
|
+
})
|
13
|
+
}
|
14
|
+
|
15
|
+
it "raises exception if missing key normalizer" do
|
16
|
+
expect {
|
17
|
+
described_class.new
|
18
|
+
}.to raise_error(ArgumentError, "Missing required key :key_normalizer")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "defaults to nil" do
|
22
|
+
subject.call(nil).should eq(nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "works with natural order ascending" do
|
26
|
+
subject.call('$natural' => 1).should eq('$natural' => 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "works with natural order descending" do
|
30
|
+
subject.call('$natural' => -1).should eq('$natural' => -1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "converts single ascending field (string)" do
|
34
|
+
subject.call('foo asc').should eq([['foo', 1]])
|
35
|
+
subject.call('foo ASC').should eq([['foo', 1]])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "converts single descending field (string)" do
|
39
|
+
subject.call('foo desc').should eq([['foo', -1]])
|
40
|
+
subject.call('foo DESC').should eq([['foo', -1]])
|
41
|
+
end
|
42
|
+
|
43
|
+
it "converts multiple fields (string)" do
|
44
|
+
subject.call('foo desc, bar asc').should eq([['foo', -1], ['bar', 1]])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "converts multiple fields and default no direction to ascending (string)" do
|
48
|
+
subject.call('foo desc, bar, baz').should eq([['foo', -1], ['bar', 1], ['baz', 1]])
|
49
|
+
end
|
50
|
+
|
51
|
+
it "converts symbol" do
|
52
|
+
subject.call(:name).should eq([['name', 1]])
|
53
|
+
end
|
54
|
+
|
55
|
+
it "converts operator" do
|
56
|
+
subject.call(:foo.desc).should eq([['foo', -1]])
|
57
|
+
end
|
58
|
+
|
59
|
+
it "converts array of operators" do
|
60
|
+
subject.call([:foo.desc, :bar.asc]).should eq([['foo', -1], ['bar', 1]])
|
61
|
+
end
|
62
|
+
|
63
|
+
it "converts array of symbols" do
|
64
|
+
subject.call([:first_name, :last_name]).should eq([['first_name', 1], ['last_name', 1]])
|
65
|
+
end
|
66
|
+
|
67
|
+
it "works with array and one string element" do
|
68
|
+
subject.call(['foo, bar desc']).should eq([['foo', 1], ['bar', -1]])
|
69
|
+
end
|
70
|
+
|
71
|
+
it "works with array of single array" do
|
72
|
+
subject.call([['foo', -1]]).should eq([['foo', -1]])
|
73
|
+
end
|
74
|
+
|
75
|
+
it "works with array of multiple arrays" do
|
76
|
+
subject.call([['foo', -1], ['bar', 1]]).should eq([['foo', -1], ['bar', 1]])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "compacts nil values in array" do
|
80
|
+
subject.call([nil, :foo.desc]).should eq([['foo', -1]])
|
81
|
+
end
|
82
|
+
|
83
|
+
it "converts array with mix of values" do
|
84
|
+
subject.call([:foo.desc, 'bar']).should eq([['foo', -1], ['bar', 1]])
|
85
|
+
end
|
86
|
+
|
87
|
+
it "converts keys based on key normalizer" do
|
88
|
+
subject.call([:id.asc]).should eq([['_id', 1]])
|
89
|
+
end
|
90
|
+
|
91
|
+
it "doesn't convert keys like :sort to :order via key normalizer" do
|
92
|
+
subject.call(:order.asc).should eq([['order', 1]])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "converts string with $natural correctly" do
|
96
|
+
subject.call('$natural desc').should eq([['$natural', -1]])
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MarkMapper::OptionsHash do
|
4
|
+
describe "#initialize_copy" do
|
5
|
+
before do
|
6
|
+
@original = described_class.new(:fields => {:name => true}, :sort => :name, :limit => 10)
|
7
|
+
@cloned = @original.clone
|
8
|
+
end
|
9
|
+
|
10
|
+
it "duplicates source hash" do
|
11
|
+
@cloned.source.should_not equal(@original.source)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "clones duplicable? values" do
|
15
|
+
@cloned[:fields].should_not equal(@original[:fields])
|
16
|
+
@cloned[:sort].should_not equal(@original[:sort])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#fields?" do
|
21
|
+
it "returns true if fields have been selected" do
|
22
|
+
described_class.new(:fields => :name).fields?.should be(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns false if no fields have been selected" do
|
26
|
+
described_class.new.fields?.should be(false)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#merge" do
|
31
|
+
before do
|
32
|
+
@o1 = described_class.new(:skip => 5, :sort => :name)
|
33
|
+
@o2 = described_class.new(:limit => 10, :skip => 15)
|
34
|
+
@merged = @o1.merge(@o2)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "overrides options in first with options in second" do
|
38
|
+
@merged.should == described_class.new(:limit => 10, :skip => 15, :sort => :name)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns new instance and not change either of the merged" do
|
42
|
+
@o1[:skip].should == 5
|
43
|
+
@o2[:sort].should be_nil
|
44
|
+
@merged.should_not equal(@o1)
|
45
|
+
@merged.should_not equal(@o2)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#merge!" do
|
50
|
+
before do
|
51
|
+
@o1 = described_class.new(:skip => 5, :sort => :name)
|
52
|
+
@o2 = described_class.new(:limit => 10, :skip => 15)
|
53
|
+
@merged = @o1.merge!(@o2)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "overrides options in first with options in second" do
|
57
|
+
@merged.should == described_class.new(:limit => 10, :skip => 15, :sort => :name)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "just updates the first" do
|
61
|
+
@merged.should equal(@o1)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MarkMapper::Pagination::Collection do
|
4
|
+
context "Object decorated with Collection with paginator set" do
|
5
|
+
before do
|
6
|
+
@object = [1, 2, 3, 4]
|
7
|
+
@object_id = @object.object_id
|
8
|
+
@paginator = MarkMapper::Pagination::Paginator.new(20, 2, 10)
|
9
|
+
end
|
10
|
+
subject { MarkMapper::Pagination::Collection.new(@object, @paginator) }
|
11
|
+
|
12
|
+
it "knows paginator" do
|
13
|
+
subject.paginator.should == @paginator
|
14
|
+
end
|
15
|
+
|
16
|
+
[:total_entries, :current_page, :per_page, :total_pages, :out_of_bounds?,
|
17
|
+
:previous_page, :next_page, :skip, :limit, :offset].each do |method|
|
18
|
+
it "delegates #{method} to paginator" do
|
19
|
+
subject.send(method).should == @paginator.send(method)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "does not interfere with other methods on the object" do
|
24
|
+
@object.object_id.should == @object_id
|
25
|
+
@object.should == [1, 2, 3, 4]
|
26
|
+
@object.size.should == 4
|
27
|
+
@object.select { |o| o > 2 }.should == [3, 4]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MarkMapper::Pagination::Paginator do
|
4
|
+
describe "#initialize" do
|
5
|
+
context "with total and page" do
|
6
|
+
before { @paginator = described_class.new(20, 2) }
|
7
|
+
subject { @paginator }
|
8
|
+
|
9
|
+
it "sets total" do
|
10
|
+
subject.total_entries.should == 20
|
11
|
+
end
|
12
|
+
|
13
|
+
it "sets page" do
|
14
|
+
subject.current_page.should == 2
|
15
|
+
end
|
16
|
+
|
17
|
+
it "defaults per_page to 25" do
|
18
|
+
subject.per_page.should == 25
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "with total, page and per_page" do
|
23
|
+
before { @paginator = described_class.new(20, 2, 10) }
|
24
|
+
subject { @paginator }
|
25
|
+
|
26
|
+
it "sets total" do
|
27
|
+
subject.total_entries.should == 20
|
28
|
+
end
|
29
|
+
|
30
|
+
it "sets page" do
|
31
|
+
subject.current_page.should == 2
|
32
|
+
end
|
33
|
+
|
34
|
+
it "sets per_page" do
|
35
|
+
subject.per_page.should == 10
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with string values for total, page and per_page" do
|
40
|
+
before { @paginator = described_class.new('20', '2', '10') }
|
41
|
+
subject { @paginator }
|
42
|
+
|
43
|
+
it "sets total" do
|
44
|
+
subject.total_entries.should == 20
|
45
|
+
end
|
46
|
+
|
47
|
+
it "sets page" do
|
48
|
+
subject.current_page.should == 2
|
49
|
+
end
|
50
|
+
|
51
|
+
it "sets per_page" do
|
52
|
+
subject.per_page.should == 10
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with page less than 1" do
|
57
|
+
before { @paginator = described_class.new(20, -2, 10) }
|
58
|
+
subject { @paginator }
|
59
|
+
|
60
|
+
it "sets page to 1" do
|
61
|
+
subject.current_page.should == 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "aliases limit to per_page" do
|
67
|
+
described_class.new(30, 2, 30).limit.should == 30
|
68
|
+
end
|
69
|
+
|
70
|
+
it "knows total number of pages" do
|
71
|
+
described_class.new(43, 2, 7).total_pages.should == 7
|
72
|
+
described_class.new(40, 2, 10).total_pages.should == 4
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#out_of_bounds?" do
|
76
|
+
it "returns true if current_page is greater than total_pages" do
|
77
|
+
described_class.new(2, 3, 1).should be_out_of_bounds
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns false if current page is less than total_pages" do
|
81
|
+
described_class.new(2, 1, 1).should_not be_out_of_bounds
|
82
|
+
end
|
83
|
+
|
84
|
+
it "returns false if current page equals total_pages" do
|
85
|
+
described_class.new(2, 2, 1).should_not be_out_of_bounds
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#previous_page" do
|
90
|
+
it "returns nil if there is no page less than current" do
|
91
|
+
described_class.new(2, 1, 1).previous_page.should be_nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it "returns number less than current page if there is one" do
|
95
|
+
described_class.new(2, 2, 1).previous_page.should == 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#next_page" do
|
100
|
+
it "returns nil if no page greater than current page" do
|
101
|
+
described_class.new(2, 2, 1).next_page.should be_nil
|
102
|
+
end
|
103
|
+
|
104
|
+
it "returns number greater than current page if there is one" do
|
105
|
+
described_class.new(2, 1, 1).next_page.should == 2
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#skip" do
|
110
|
+
it "works" do
|
111
|
+
described_class.new(30, 3, 10).skip.should == 20
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns aliased to offset for will paginate" do
|
115
|
+
described_class.new(30, 3, 10).offset.should == 20
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Plugins" do
|
4
|
+
it "should default plugins to empty array" do
|
5
|
+
Class.new { extend MarkMapper::Plugins }.plugins.should == []
|
6
|
+
end
|
7
|
+
|
8
|
+
context "a plugin" do
|
9
|
+
module MyConcern
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
attr_accessor :from_concern
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def class_foo
|
18
|
+
'class_foo'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def instance_foo
|
23
|
+
'instance_foo'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
@document = Class.new do
|
29
|
+
extend MarkMapper::Plugins
|
30
|
+
plugin MyConcern
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should include instance methods" do
|
35
|
+
@document.new.instance_foo.should == 'instance_foo'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should extend class methods" do
|
39
|
+
@document.class_foo.should == 'class_foo'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should pass model to configure" do
|
43
|
+
@document.new.should respond_to(:from_concern)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should add plugin to plugins" do
|
47
|
+
@document.plugins.should include(MyConcern)
|
48
|
+
end
|
49
|
+
|
50
|
+
context "Document" do
|
51
|
+
before do
|
52
|
+
MarkMapper::Document.plugins.delete(MyConcern)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should allow plugins on Document' do
|
56
|
+
MarkMapper::Document.plugin(MyConcern)
|
57
|
+
Doc().should respond_to(:class_foo)
|
58
|
+
Doc().new.should respond_to(:instance_foo)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should add plugins to classes that include Document before they are added' do
|
62
|
+
article = Doc()
|
63
|
+
MarkMapper::Document.plugin(MyConcern)
|
64
|
+
article.should respond_to(:class_foo)
|
65
|
+
article.new.should respond_to(:instance_foo)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "EmbeddedDocument" do
|
70
|
+
before do
|
71
|
+
MarkMapper::EmbeddedDocument.plugins.delete(MyConcern)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should allow plugins on EmbeddedDocument' do
|
75
|
+
MarkMapper::EmbeddedDocument.plugin(MyConcern)
|
76
|
+
article = EDoc()
|
77
|
+
article.should respond_to(:class_foo)
|
78
|
+
article.new.should respond_to(:instance_foo)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should add plugins to classes that include EmbeddedDocument before they are added' do
|
82
|
+
article = EDoc()
|
83
|
+
MarkMapper::EmbeddedDocument.plugin(MyConcern)
|
84
|
+
article.should respond_to(:class_foo)
|
85
|
+
article.new.should respond_to(:instance_foo)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,837 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MarkMapper::Query do
|
4
|
+
before do
|
5
|
+
@chris = { "_id" => 'chris', "age" => 26, "name" => 'Chris' }
|
6
|
+
@steve = { "_id" => 'steve', "age" => 29, "name" => 'Steve' }
|
7
|
+
@john = { "_id" => 'john', "age" => 28, "name" => 'John' }
|
8
|
+
|
9
|
+
@collection = @database.collection("users")
|
10
|
+
@collection.drop
|
11
|
+
@collection.insert(@chris)
|
12
|
+
@collection.insert(@steve)
|
13
|
+
@collection.insert(@john)
|
14
|
+
end
|
15
|
+
|
16
|
+
context "#initialize" do
|
17
|
+
before { @query = described_class.new(@collection) }
|
18
|
+
subject { @query }
|
19
|
+
|
20
|
+
it "defaults options to options hash" do
|
21
|
+
@query.options.should be_instance_of(MarkMapper::OptionsHash)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "defaults criteria to criteria hash" do
|
25
|
+
@query.criteria.should be_instance_of(MarkMapper::CriteriaHash)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "#initialize_copy" do
|
30
|
+
before do
|
31
|
+
@original = described_class.new(@collection)
|
32
|
+
@cloned = @original.clone
|
33
|
+
end
|
34
|
+
|
35
|
+
it "duplicates options" do
|
36
|
+
@cloned.options.should_not equal(@original.options)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "duplicates criteria" do
|
40
|
+
@cloned.criteria.should_not equal(@original.criteria)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "#[]=" do
|
45
|
+
before { @query = described_class.new(@collection) }
|
46
|
+
subject { @query }
|
47
|
+
|
48
|
+
it "sets key on options for option" do
|
49
|
+
subject[:skip] = 1
|
50
|
+
subject[:skip].should == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
it "sets key on criteria for criteria" do
|
54
|
+
subject[:foo] = 'bar'
|
55
|
+
subject[:foo].should == 'bar'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "#find_each" do
|
60
|
+
it "returns a cursor" do
|
61
|
+
cursor = described_class.new(@collection).find_each
|
62
|
+
cursor.should be_instance_of(MarkLogic::Cursor)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "works with and normalize criteria" do
|
66
|
+
cursor = described_class.new(@collection).find_each(:id.eq => ['john'])
|
67
|
+
cursor.to_a.should == [@john]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "works with and normalize options" do
|
71
|
+
cursor = described_class.new(@collection).find_each(:order => :name.asc)
|
72
|
+
cursor.to_a.should == [@chris, @john, @steve]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "yields elements to a block if given" do
|
76
|
+
yielded_elements = Set.new
|
77
|
+
described_class.new(@collection).find_each { |doc| yielded_elements << doc }
|
78
|
+
yielded_elements.should == [@chris, @john, @steve].to_set
|
79
|
+
end
|
80
|
+
|
81
|
+
it "is Ruby-like and returns a reset cursor if a block is given" do
|
82
|
+
cursor = described_class.new(@collection).find_each {}
|
83
|
+
cursor.should be_instance_of(MarkLogic::Cursor)
|
84
|
+
cursor.next.should be_instance_of(Hash)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "#find_one" do
|
89
|
+
it "works with and normalize criteria" do
|
90
|
+
described_class.new(@collection).find_one(:id.eq => ['john']).should == @john
|
91
|
+
end
|
92
|
+
|
93
|
+
it "works with and normalize options" do
|
94
|
+
described_class.new(@collection).find_one(:order => :age.desc).should == @steve
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "#find" do
|
99
|
+
before do
|
100
|
+
@query = described_class.new(@collection)
|
101
|
+
end
|
102
|
+
subject { @query }
|
103
|
+
|
104
|
+
it "works with single id" do
|
105
|
+
@query.find('chris').should == @chris
|
106
|
+
end
|
107
|
+
|
108
|
+
it "works with multiple ids" do
|
109
|
+
@query.find('chris', 'john').should =~ [@chris, @john]
|
110
|
+
end
|
111
|
+
|
112
|
+
it "works with array of one id" do
|
113
|
+
@query.find(['chris']).should == [@chris]
|
114
|
+
end
|
115
|
+
|
116
|
+
it "works with array of ids" do
|
117
|
+
@query.find(['chris', 'john']).should =~ [@chris, @john]
|
118
|
+
end
|
119
|
+
|
120
|
+
it "ignores those not found" do
|
121
|
+
@query.find('john', 'frank').should == [@john]
|
122
|
+
end
|
123
|
+
|
124
|
+
it "returns nil for nil" do
|
125
|
+
@query.find(nil).should be_nil
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns nil for *nil" do
|
129
|
+
@query.find(*nil).should be_nil
|
130
|
+
end
|
131
|
+
|
132
|
+
it "normalizes if using object id" do
|
133
|
+
id = @collection.insert(:name => 'Frank')
|
134
|
+
@query.object_ids([:_id])
|
135
|
+
doc = @query.find(id.to_s)
|
136
|
+
doc['name'].should == 'Frank'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "#per_page" do
|
141
|
+
it "defaults to 25" do
|
142
|
+
described_class.new(@collection).per_page.should == 25
|
143
|
+
end
|
144
|
+
|
145
|
+
it "is changeable and chainable" do
|
146
|
+
query = described_class.new(@collection)
|
147
|
+
query.per_page(10).per_page.should == 10
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "#paginate" do
|
152
|
+
before do
|
153
|
+
@query = described_class.new(@collection).sort(:age).per_page(1)
|
154
|
+
end
|
155
|
+
subject { @query }
|
156
|
+
|
157
|
+
it "defaults to page 1" do
|
158
|
+
subject.paginate.should == [@chris]
|
159
|
+
end
|
160
|
+
|
161
|
+
it "works with other pages" do
|
162
|
+
subject.paginate(:page => 2).should == [@john]
|
163
|
+
subject.paginate(:page => 3).should == [@steve]
|
164
|
+
end
|
165
|
+
|
166
|
+
it "works with string page number" do
|
167
|
+
subject.paginate(:page => '2').should == [@john]
|
168
|
+
end
|
169
|
+
|
170
|
+
it "allows changing per_page" do
|
171
|
+
subject.paginate(:per_page => 2).should == [@chris, @john]
|
172
|
+
end
|
173
|
+
|
174
|
+
it "decorates return value" do
|
175
|
+
docs = subject.paginate
|
176
|
+
docs.should respond_to(:paginator)
|
177
|
+
docs.should respond_to(:total_entries)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "does not modify the original query" do
|
181
|
+
subject.paginate(:name => 'John')
|
182
|
+
subject[:page].should be_nil
|
183
|
+
subject[:per_page].should be_nil
|
184
|
+
subject[:name].should be_nil
|
185
|
+
end
|
186
|
+
|
187
|
+
it "allows total_entries overrides" do
|
188
|
+
docs = subject.paginate(:total_entries => 1)
|
189
|
+
docs.total_entries.should == 1
|
190
|
+
end
|
191
|
+
|
192
|
+
context "with options" do
|
193
|
+
before do
|
194
|
+
@result = @query.sort(:age).paginate(:age.gt => 27, :per_page => 10)
|
195
|
+
end
|
196
|
+
subject { @result }
|
197
|
+
|
198
|
+
it "only returns matching" do
|
199
|
+
subject.should == [@john, @steve]
|
200
|
+
end
|
201
|
+
|
202
|
+
it "correctly counts matching" do
|
203
|
+
subject.total_entries.should == 2
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "#all" do
|
209
|
+
it "works with no arguments" do
|
210
|
+
docs = described_class.new(@collection).all
|
211
|
+
docs.size.should == 3
|
212
|
+
docs.should include(@john)
|
213
|
+
docs.should include(@steve)
|
214
|
+
docs.should include(@chris)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "works with and normalize criteria" do
|
218
|
+
docs = described_class.new(@collection).all(:id.eq => ['steve'])
|
219
|
+
docs.should == [@steve]
|
220
|
+
end
|
221
|
+
|
222
|
+
it "works with and normalize options" do
|
223
|
+
docs = described_class.new(@collection).all(:order => :name.asc)
|
224
|
+
docs.should == [@chris, @john, @steve]
|
225
|
+
end
|
226
|
+
|
227
|
+
it "does not modify original query object" do
|
228
|
+
query = described_class.new(@collection)
|
229
|
+
query.all(:name => 'Steve')
|
230
|
+
query[:name].should be_nil
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "#first" do
|
235
|
+
it "works with and normalize criteria" do
|
236
|
+
described_class.new(@collection).first(:age.lt => 27).should == @chris
|
237
|
+
end
|
238
|
+
|
239
|
+
it "works with and normalize options" do
|
240
|
+
described_class.new(@collection).first(:age.le => 29, :order => :name.desc).should == @steve
|
241
|
+
end
|
242
|
+
|
243
|
+
it "does not modify original query object" do
|
244
|
+
query = described_class.new(@collection)
|
245
|
+
query.first(:name => 'Steve')
|
246
|
+
query[:name].should be_nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
context "#last" do
|
251
|
+
it "works with and normalize criteria" do
|
252
|
+
described_class.new(@collection).last(:age.le => 29, :order => :name.asc).should == @steve
|
253
|
+
end
|
254
|
+
|
255
|
+
it "works with and normalize options" do
|
256
|
+
described_class.new(@collection).last(:age.le => 26, :order => :name.desc).should == @chris
|
257
|
+
end
|
258
|
+
|
259
|
+
it "uses _id if a sort key is not specified" do
|
260
|
+
described_class.new(@collection).last.should == [@steve, @chris, @john].sort {|a, b| a["_id"] <=> b["_id"] }.last
|
261
|
+
end
|
262
|
+
|
263
|
+
it "does not modify original query object" do
|
264
|
+
query = described_class.new(@collection)
|
265
|
+
query.last(:name => 'Steve')
|
266
|
+
query[:name].should be_nil
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context "#count" do
|
271
|
+
it "works with no arguments" do
|
272
|
+
described_class.new(@collection).count.should == 3
|
273
|
+
end
|
274
|
+
|
275
|
+
it "works with and normalize criteria" do
|
276
|
+
described_class.new(@collection).count(:age.le => 28).should == 2
|
277
|
+
end
|
278
|
+
|
279
|
+
it "does not modify original query object" do
|
280
|
+
query = described_class.new(@collection)
|
281
|
+
query.count(:name => 'Steve')
|
282
|
+
query[:name].should be_nil
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context "#size" do
|
287
|
+
it "works just like count without options" do
|
288
|
+
described_class.new(@collection).size.should == 3
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# context "#distinct" do
|
293
|
+
# before do
|
294
|
+
# # same age as John
|
295
|
+
# @mark = { "_id" => 'mark', "age" => 28, "name" => 'Mark' }
|
296
|
+
# @collection.insert(@mark)
|
297
|
+
# end
|
298
|
+
|
299
|
+
# it "works with just a key" do
|
300
|
+
# described_class.new(@collection).distinct(:age).sort.should == [26, 28, 29]
|
301
|
+
# end
|
302
|
+
|
303
|
+
# it "works with criteria" do
|
304
|
+
# described_class.new(@collection).distinct(:age, :age.gt => 26).sort.should == [28, 29]
|
305
|
+
# end
|
306
|
+
|
307
|
+
# it "does not modify the original query object" do
|
308
|
+
# query = described_class.new(@collection)
|
309
|
+
# query.distinct(:age, :name => 'Mark').should == [28]
|
310
|
+
# query[:name].should be_nil
|
311
|
+
# end
|
312
|
+
# end
|
313
|
+
|
314
|
+
context "#remove" do
|
315
|
+
it "works with no arguments" do
|
316
|
+
lambda { described_class.new(@collection).remove }.should change { @collection.count }.by(-3)
|
317
|
+
end
|
318
|
+
|
319
|
+
it "works with and normalize criteria" do
|
320
|
+
lambda { described_class.new(@collection).remove(:age.le => 28) }.should change { @collection.count }
|
321
|
+
end
|
322
|
+
|
323
|
+
it "does not modify original query object" do
|
324
|
+
query = described_class.new(@collection)
|
325
|
+
query.remove(:name => 'Steve')
|
326
|
+
query[:name].should be_nil
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# context "#update" do
|
331
|
+
# before do
|
332
|
+
# @query = described_class.new(@collection).where('_id' => 'john')
|
333
|
+
# end
|
334
|
+
|
335
|
+
# it "works with document" do
|
336
|
+
# @query.update('$set' => {'age' => 29})
|
337
|
+
# doc = @query.first('_id' => 'john')
|
338
|
+
# doc['age'].should be(29)
|
339
|
+
# end
|
340
|
+
|
341
|
+
# it "works with document and driver options" do
|
342
|
+
# @query.update({'$set' => {'age' => 30}}, :multi => true)
|
343
|
+
# @query.each do |doc|
|
344
|
+
# doc['age'].should be(30)
|
345
|
+
# end
|
346
|
+
# end
|
347
|
+
# end
|
348
|
+
|
349
|
+
context "#[]" do
|
350
|
+
it "returns value if key in criteria (symbol)" do
|
351
|
+
described_class.new(@collection, :count => 1)[:count].should == 1
|
352
|
+
end
|
353
|
+
|
354
|
+
it "returns value if key in criteria (string)" do
|
355
|
+
described_class.new(@collection, :count => 1)['count'].should == 1
|
356
|
+
end
|
357
|
+
|
358
|
+
it "returns nil if key not in criteria" do
|
359
|
+
described_class.new(@collection)[:count].should be_nil
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context "#[]=" do
|
364
|
+
before { @query = described_class.new(@collection) }
|
365
|
+
|
366
|
+
it "sets the value of the given criteria key" do
|
367
|
+
@query[:count] = 1
|
368
|
+
@query[:count].should == 1
|
369
|
+
end
|
370
|
+
|
371
|
+
it "overwrites value if key already exists" do
|
372
|
+
@query[:count] = 1
|
373
|
+
@query[:count] = 2
|
374
|
+
@query[:count].should == 2
|
375
|
+
end
|
376
|
+
|
377
|
+
it "normalizes value" do
|
378
|
+
now = Time.now
|
379
|
+
@query[:published_at] = now
|
380
|
+
@query[:published_at].should == now.utc
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# context "#fields" do
|
385
|
+
# before { @query = described_class.new(@collection) }
|
386
|
+
# subject { @query }
|
387
|
+
|
388
|
+
# it "works" do
|
389
|
+
# subject.fields(:name).first(:id => 'john').keys.should == ['_id', 'name']
|
390
|
+
# end
|
391
|
+
|
392
|
+
# it "returns new instance of query" do
|
393
|
+
# new_query = subject.fields(:name)
|
394
|
+
# new_query.should_not equal(subject)
|
395
|
+
# subject[:fields].should be_nil
|
396
|
+
# end
|
397
|
+
|
398
|
+
# it "works with hash" do
|
399
|
+
# subject.fields(:name => 0).
|
400
|
+
# first(:id => 'john').keys.sort.
|
401
|
+
# should == ['_id', 'age']
|
402
|
+
# end
|
403
|
+
# end
|
404
|
+
|
405
|
+
# context "#ignore" do
|
406
|
+
# before { @query = described_class.new(@collection) }
|
407
|
+
# subject { @query }
|
408
|
+
|
409
|
+
# it "includes a list of keys to ignore" do
|
410
|
+
# new_query = subject.ignore(:name).first(:id => 'john')
|
411
|
+
# new_query.keys.should == ['_id', 'age']
|
412
|
+
# end
|
413
|
+
# end
|
414
|
+
|
415
|
+
# context "#only" do
|
416
|
+
# before { @query = described_class.new(@collection) }
|
417
|
+
# subject { @query }
|
418
|
+
|
419
|
+
# it "includes a list of keys with others excluded" do
|
420
|
+
# new_query = subject.only(:name).first(:id => 'john')
|
421
|
+
# new_query.keys.should == ['_id', 'name']
|
422
|
+
# end
|
423
|
+
|
424
|
+
# end
|
425
|
+
|
426
|
+
context "#skip" do
|
427
|
+
before { @query = described_class.new(@collection) }
|
428
|
+
subject { @query }
|
429
|
+
|
430
|
+
it "works" do
|
431
|
+
subject.skip(2).all(:order => :age).should == [@steve]
|
432
|
+
end
|
433
|
+
|
434
|
+
it "sets skip option" do
|
435
|
+
subject.skip(5).options[:skip].should == 5
|
436
|
+
end
|
437
|
+
|
438
|
+
it "overrides existing skip" do
|
439
|
+
subject.skip(5).skip(10).options[:skip].should == 10
|
440
|
+
end
|
441
|
+
|
442
|
+
it "returns nil for nil" do
|
443
|
+
subject.skip.options[:skip].should be_nil
|
444
|
+
end
|
445
|
+
|
446
|
+
it "returns new instance of query" do
|
447
|
+
new_query = subject.skip(2)
|
448
|
+
new_query.should_not equal(subject)
|
449
|
+
subject[:skip].should be_nil
|
450
|
+
end
|
451
|
+
|
452
|
+
it "aliases to offset" do
|
453
|
+
subject.offset(5).options[:skip].should == 5
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
context "#limit" do
|
458
|
+
before { @query = described_class.new(@collection) }
|
459
|
+
subject { @query }
|
460
|
+
|
461
|
+
it "works" do
|
462
|
+
subject.limit(2).all(:order => :age).should == [@chris, @john]
|
463
|
+
end
|
464
|
+
|
465
|
+
it "sets limit option" do
|
466
|
+
subject.limit(5).options[:limit].should == 5
|
467
|
+
end
|
468
|
+
|
469
|
+
it "overwrites existing limit" do
|
470
|
+
subject.limit(5).limit(15).options[:limit].should == 15
|
471
|
+
end
|
472
|
+
|
473
|
+
it "returns new instance of query" do
|
474
|
+
new_query = subject.limit(2)
|
475
|
+
new_query.should_not equal(subject)
|
476
|
+
subject[:limit].should be_nil
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
context "#sort" do
|
481
|
+
before { @query = described_class.new(@collection) }
|
482
|
+
subject { @query }
|
483
|
+
|
484
|
+
it "works" do
|
485
|
+
subject.sort(:age).all.should == [@chris, @john, @steve]
|
486
|
+
subject.sort(:age.desc).all.should == [@steve, @john, @chris]
|
487
|
+
end
|
488
|
+
|
489
|
+
it "works with symbol operators" do
|
490
|
+
subject.sort(:foo.asc, :bar.desc).options[:sort].should == [['foo', 1], ['bar', -1]]
|
491
|
+
end
|
492
|
+
|
493
|
+
it "works with string" do
|
494
|
+
subject.sort('foo, bar desc').options[:sort].should == [['foo', 1], ['bar', -1]]
|
495
|
+
end
|
496
|
+
|
497
|
+
it "works with just a symbol" do
|
498
|
+
subject.sort(:foo).options[:sort].should == [['foo', 1]]
|
499
|
+
end
|
500
|
+
|
501
|
+
it "works with symbol descending" do
|
502
|
+
subject.sort(:foo.desc).options[:sort].should == [['foo', -1]]
|
503
|
+
end
|
504
|
+
|
505
|
+
it "works with multiple symbols" do
|
506
|
+
subject.sort(:foo, :bar).options[:sort].should == [['foo', 1], ['bar', 1]]
|
507
|
+
end
|
508
|
+
|
509
|
+
it "returns new instance of query" do
|
510
|
+
new_query = subject.sort(:name)
|
511
|
+
new_query.should_not equal(subject)
|
512
|
+
subject[:sort].should be_nil
|
513
|
+
end
|
514
|
+
|
515
|
+
it "is aliased to order" do
|
516
|
+
subject.order(:foo).options[:sort].should == [['foo', 1]]
|
517
|
+
subject.order(:foo, :bar).options[:sort].should == [['foo', 1], ['bar', 1]]
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
context "#reverse" do
|
522
|
+
before { @query = described_class.new(@collection) }
|
523
|
+
subject { @query }
|
524
|
+
|
525
|
+
it "works" do
|
526
|
+
subject.sort(:age).reverse.all.should == [@steve, @john, @chris]
|
527
|
+
end
|
528
|
+
|
529
|
+
it "does not error if no sort provided" do
|
530
|
+
expect {
|
531
|
+
subject.reverse
|
532
|
+
}.to_not raise_error
|
533
|
+
end
|
534
|
+
|
535
|
+
it "reverses the sort order" do
|
536
|
+
subject.sort('foo asc, bar desc').
|
537
|
+
reverse.options[:sort].should == [['foo', -1], ['bar', 1]]
|
538
|
+
end
|
539
|
+
|
540
|
+
it "returns new instance of query" do
|
541
|
+
sorted_query = subject.sort(:name)
|
542
|
+
new_query = sorted_query.reverse
|
543
|
+
new_query.should_not equal(sorted_query)
|
544
|
+
sorted_query[:sort].should == [['name', 1]]
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
context "#amend" do
|
549
|
+
it "normalizes and update options" do
|
550
|
+
described_class.new(@collection).amend(:order => :age.desc).options[:sort].should == [['age', -1]]
|
551
|
+
end
|
552
|
+
|
553
|
+
it "works with simple stuff" do
|
554
|
+
described_class.new(@collection).
|
555
|
+
amend(:foo => 'bar').
|
556
|
+
amend(:baz => 'wick').
|
557
|
+
criteria.source.should eq(:foo => 'bar', :baz => 'wick')
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
context "#where" do
|
562
|
+
before { @query = described_class.new(@collection) }
|
563
|
+
subject { @query }
|
564
|
+
|
565
|
+
it "works" do
|
566
|
+
subject.where(:age.lt => 29).where(:name => 'Chris').all.should == [@chris]
|
567
|
+
end
|
568
|
+
|
569
|
+
# it "works with literal regexp" do
|
570
|
+
# subject.where(:name => /^c/i).all.should == [@chris]
|
571
|
+
# end
|
572
|
+
|
573
|
+
it "updates criteria" do
|
574
|
+
subject.
|
575
|
+
where(:moo => 'cow').
|
576
|
+
where(:foo => 'bar').
|
577
|
+
criteria.source.should eq(:foo => 'bar', :moo => 'cow')
|
578
|
+
end
|
579
|
+
|
580
|
+
it "gets normalized" do
|
581
|
+
subject.
|
582
|
+
where(:moo => 'cow').
|
583
|
+
where(:foo.eq => ['bar']).
|
584
|
+
criteria.source.should eq(:moo => 'cow', :foo => {:$eq => ['bar']})
|
585
|
+
end
|
586
|
+
|
587
|
+
it "normalizes merged criteria" do
|
588
|
+
subject.
|
589
|
+
where(:foo => 'bar').
|
590
|
+
where(:foo => 'baz').
|
591
|
+
criteria.source.should eq({:foo => {:$eq => %w[bar baz]}})
|
592
|
+
end
|
593
|
+
|
594
|
+
it "returns new instance of query" do
|
595
|
+
new_query = subject.where(:name => 'John')
|
596
|
+
new_query.should_not equal(subject)
|
597
|
+
subject[:name].should be_nil
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
context "#filter" do
|
602
|
+
before { @query = described_class.new(@collection) }
|
603
|
+
subject { @query }
|
604
|
+
|
605
|
+
it "works the same as where" do
|
606
|
+
subject.filter(:age.lt => 29).filter(:name => 'Chris').all.should == [@chris]
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
context "#empty?" do
|
611
|
+
it "returns true if empty" do
|
612
|
+
@collection.remove
|
613
|
+
described_class.new(@collection).should be_empty
|
614
|
+
end
|
615
|
+
|
616
|
+
it "returns false if not empty" do
|
617
|
+
described_class.new(@collection).should_not be_empty
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
context "#exists?" do
|
622
|
+
it "returns true if found" do
|
623
|
+
described_class.new(@collection).exists?(:name => 'John').should be(true)
|
624
|
+
end
|
625
|
+
|
626
|
+
it "returns false if not found" do
|
627
|
+
described_class.new(@collection).exists?(:name => 'Billy Bob').should be(false)
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
context "#exist?" do
|
632
|
+
it "returns true if found" do
|
633
|
+
described_class.new(@collection).exist?(:name => 'John').should be(true)
|
634
|
+
end
|
635
|
+
|
636
|
+
it "returns false if not found" do
|
637
|
+
described_class.new(@collection).exist?(:name => 'Billy Bob').should be(false)
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
context "#include?" do
|
642
|
+
it "returns true if included" do
|
643
|
+
described_class.new(@collection).include?(@john).should be(true)
|
644
|
+
end
|
645
|
+
|
646
|
+
it "returns false if not included" do
|
647
|
+
described_class.new(@collection).include?(['_id', 'frankyboy']).should be(false)
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
context "#to_a" do
|
652
|
+
it "returns all documents the query matches" do
|
653
|
+
described_class.new(@collection).sort(:name).to_a.
|
654
|
+
should == [@chris, @john, @steve]
|
655
|
+
|
656
|
+
described_class.new(@collection).where(:name => 'John').sort(:name).to_a.
|
657
|
+
should == [@john]
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
context "#each" do
|
662
|
+
it "iterates through matching documents" do
|
663
|
+
docs = []
|
664
|
+
described_class.new(@collection).sort(:name).each do |doc|
|
665
|
+
docs << doc
|
666
|
+
end
|
667
|
+
docs.should == [@chris, @john, @steve]
|
668
|
+
end
|
669
|
+
|
670
|
+
it "returns a working enumerator" do
|
671
|
+
query = described_class.new(@collection)
|
672
|
+
query.each.methods.map(&:to_sym).include?(:group_by).should be(true)
|
673
|
+
query.each.next.should be_instance_of(Hash)
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
context "enumerables" do
|
678
|
+
it "works" do
|
679
|
+
query = described_class.new(@collection).sort(:name)
|
680
|
+
query.map { |doc| doc['name'] }.should == %w(Chris John Steve)
|
681
|
+
query.collect { |doc| doc['name'] }.should == %w(Chris John Steve)
|
682
|
+
query.detect { |doc| doc['name'] == 'John' }.should == @john
|
683
|
+
query.min { |a, b| a['age'] <=> b['age'] }.should == @chris
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
# context "#object_ids" do
|
688
|
+
# before { @query = described_class.new(@collection) }
|
689
|
+
# subject { @query }
|
690
|
+
|
691
|
+
# it "sets criteria's object_ids" do
|
692
|
+
# expect(subject.criteria).to receive(:object_ids=).with([:foo, :bar])
|
693
|
+
# subject.object_ids(:foo, :bar)
|
694
|
+
# end
|
695
|
+
|
696
|
+
# it "returns current object ids if keys argument is empty" do
|
697
|
+
# subject.object_ids(:foo, :bar)
|
698
|
+
# subject.object_ids.should == [:foo, :bar]
|
699
|
+
# end
|
700
|
+
# end
|
701
|
+
|
702
|
+
context "#merge" do
|
703
|
+
it "overwrites options" do
|
704
|
+
query1 = described_class.new(@collection, :skip => 5, :limit => 5)
|
705
|
+
query2 = described_class.new(@collection, :skip => 10, :limit => 10)
|
706
|
+
new_query = query1.merge(query2)
|
707
|
+
new_query.options[:skip].should == 10
|
708
|
+
new_query.options[:limit].should == 10
|
709
|
+
end
|
710
|
+
|
711
|
+
it "merges criteria" do
|
712
|
+
query1 = described_class.new(@collection, :foo => 'bar')
|
713
|
+
query2 = described_class.new(@collection, :foo => 'baz', :fent => 'wick')
|
714
|
+
new_query = query1.merge(query2)
|
715
|
+
new_query.criteria[:fent].should == 'wick'
|
716
|
+
new_query.criteria[:foo].should == {:$eq => %w[bar baz]}
|
717
|
+
end
|
718
|
+
|
719
|
+
it "does not affect either of the merged queries" do
|
720
|
+
query1 = described_class.new(@collection, :foo => 'bar', :limit => 5)
|
721
|
+
query2 = described_class.new(@collection, :foo => 'baz', :limit => 10)
|
722
|
+
new_query = query1.merge(query2)
|
723
|
+
query1[:foo].should == 'bar'
|
724
|
+
query1[:limit].should == 5
|
725
|
+
query2[:foo].should == 'baz'
|
726
|
+
query2[:limit].should == 10
|
727
|
+
end
|
728
|
+
end
|
729
|
+
|
730
|
+
context "Criteria/option auto-detection" do
|
731
|
+
it "knows :conditions are criteria" do
|
732
|
+
query = described_class.new(@collection, :conditions => {:foo => 'bar'})
|
733
|
+
query.criteria.source.should eq(:foo => 'bar')
|
734
|
+
query.options.keys.should_not include(:conditions)
|
735
|
+
end
|
736
|
+
|
737
|
+
{
|
738
|
+
:fields => ['foo'],
|
739
|
+
:sort => [['foo', 1]],
|
740
|
+
:skip => 0,
|
741
|
+
:limit => 0
|
742
|
+
}.each do |option, value|
|
743
|
+
it "knows #{option} is an option" do
|
744
|
+
query = described_class.new(@collection, option => value)
|
745
|
+
query.options[option].should == value
|
746
|
+
query.criteria.keys.should_not include(option)
|
747
|
+
end
|
748
|
+
end
|
749
|
+
|
750
|
+
it "knows select is an option and remove it from options" do
|
751
|
+
query = described_class.new(@collection, :select => 'foo')
|
752
|
+
query.options[:fields].should == ['foo']
|
753
|
+
query.criteria.keys.should_not include(:select)
|
754
|
+
query.options.keys.should_not include(:select)
|
755
|
+
end
|
756
|
+
|
757
|
+
it "knows order is an option and remove it from options" do
|
758
|
+
query = described_class.new(@collection, :order => 'foo')
|
759
|
+
query.options[:sort].should == [['foo', 1]]
|
760
|
+
query.criteria.keys.should_not include(:order)
|
761
|
+
query.options.keys.should_not include(:order)
|
762
|
+
end
|
763
|
+
|
764
|
+
it "knows offset is an option and remove it from options" do
|
765
|
+
query = described_class.new(@collection, :offset => 0)
|
766
|
+
query.options[:skip].should == 0
|
767
|
+
query.criteria.keys.should_not include(:offset)
|
768
|
+
query.options.keys.should_not include(:offset)
|
769
|
+
end
|
770
|
+
|
771
|
+
it "works with full range of things" do
|
772
|
+
query = described_class.new(@collection, {
|
773
|
+
:foo => 'bar',
|
774
|
+
:baz => true,
|
775
|
+
:sort => [['foo', 1]],
|
776
|
+
:fields => ['foo', 'baz'],
|
777
|
+
:limit => 10,
|
778
|
+
:skip => 10,
|
779
|
+
})
|
780
|
+
query.criteria.source.should eq(:foo => 'bar', :baz => true)
|
781
|
+
query.options.source.should eq({
|
782
|
+
:sort => [['foo', 1]],
|
783
|
+
:fields => ['foo', 'baz'],
|
784
|
+
:limit => 10,
|
785
|
+
:skip => 10,
|
786
|
+
})
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
# it "inspects pretty" do
|
791
|
+
# inspect = described_class.new(@collection, :baz => 'wick', :foo => 'bar').inspect
|
792
|
+
# inspect.should == '#<MarkMapper::Query baz: "wick", foo: "bar">'
|
793
|
+
# end
|
794
|
+
|
795
|
+
it "delegates simple? to criteria" do
|
796
|
+
query = described_class.new(@collection)
|
797
|
+
expect(query.criteria).to receive(:simple?)
|
798
|
+
query.simple?
|
799
|
+
end
|
800
|
+
|
801
|
+
it "delegates fields? to options" do
|
802
|
+
query = described_class.new(@collection)
|
803
|
+
expect(query.options).to receive(:fields?)
|
804
|
+
query.fields?
|
805
|
+
end
|
806
|
+
|
807
|
+
# context "#explain" do
|
808
|
+
# before { @query = described_class.new(@collection) }
|
809
|
+
# subject { @query }
|
810
|
+
|
811
|
+
# it "works" do
|
812
|
+
# explain = subject.where(:age.lt => 28).explain
|
813
|
+
# explain['cursor'].should == 'BasicCursor'
|
814
|
+
# explain['nscanned'].should == 3
|
815
|
+
# end
|
816
|
+
# end
|
817
|
+
|
818
|
+
context "Transforming documents" do
|
819
|
+
before do
|
820
|
+
transformer = lambda { |doc| @user_class.new(doc['_id'], doc['name'], doc['age']) }
|
821
|
+
@user_class = Struct.new(:id, :name, :age)
|
822
|
+
@query = described_class.new(@collection, :transformer => transformer)
|
823
|
+
end
|
824
|
+
|
825
|
+
it "works with find_one" do
|
826
|
+
result = @query.find_one('_id' => 'john')
|
827
|
+
result.should be_instance_of(@user_class)
|
828
|
+
end
|
829
|
+
|
830
|
+
it "works with find_each" do
|
831
|
+
results = @query.find_each
|
832
|
+
results.each do |result|
|
833
|
+
result.should be_instance_of(@user_class)
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|
837
|
+
end
|