mongomodel 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/mongomodel/concerns/attribute_methods/nested.rb +4 -0
- data/lib/mongomodel/document/indexes.rb +8 -3
- data/lib/mongomodel/document/persistence.rb +12 -6
- data/lib/mongomodel/document/validations/uniqueness.rb +11 -11
- data/lib/mongomodel/support/configuration.rb +14 -3
- data/lib/mongomodel/support/scope.rb +7 -3
- data/lib/mongomodel/version.rb +1 -1
- data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +1 -1
- data/spec/mongomodel/concerns/attribute_methods/nested_spec.rb +29 -0
- data/spec/mongomodel/document/indexes_spec.rb +42 -30
- data/spec/mongomodel/document/persistence_spec.rb +15 -0
- data/spec/mongomodel/document/validations/uniqueness_spec.rb +26 -0
- data/spec/mongomodel/support/configuration_spec.rb +13 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d006082bc3f5bf2357182fecea49893a06e5994
|
4
|
+
data.tar.gz: d53384261c1968e770c65c4eb4e8faa32ade2c22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e97ed321bde11ad2f30311b4e80a8ea853894e720673d2634142b3e29e1f5088286185169d95fa9bd233aab9637a1d28f69c1b3bd9ef726f69d3b5f6f089a2ff
|
7
|
+
data.tar.gz: b787c2bdaa482398da09028b9093ac4514bc596a446a115d58879ab103fd8c116a078415ba0c85022bd9ca14638421a5dfd4ac4844a42d65b1e4bd59d5e9b1c1
|
@@ -59,6 +59,8 @@ module MongoModel
|
|
59
59
|
end
|
60
60
|
|
61
61
|
collection = send(property)
|
62
|
+
collection.clear if options[:clear]
|
63
|
+
|
62
64
|
attributes_collection.each_with_index do |attributes, index|
|
63
65
|
if collection[index]
|
64
66
|
collection[index].attributes = attributes
|
@@ -85,6 +87,8 @@ module MongoModel
|
|
85
87
|
end
|
86
88
|
|
87
89
|
association = send(association)
|
90
|
+
association.clear if options[:clear]
|
91
|
+
|
88
92
|
attributes_collection.each do |attributes|
|
89
93
|
association.build(attributes)
|
90
94
|
end
|
@@ -55,6 +55,7 @@ module MongoModel
|
|
55
55
|
def initialize(*keys)
|
56
56
|
options = keys.extract_options!
|
57
57
|
|
58
|
+
@name = options.delete(:name)
|
58
59
|
@unique = options.delete(:unique)
|
59
60
|
@min = options.delete(:min)
|
60
61
|
@max = options.delete(:max)
|
@@ -82,6 +83,7 @@ module MongoModel
|
|
82
83
|
|
83
84
|
def to_args
|
84
85
|
args = []
|
86
|
+
options = {}
|
85
87
|
|
86
88
|
if geo2d?
|
87
89
|
args << [[keys.keys.first, Mongo::GEO2D]]
|
@@ -92,11 +94,14 @@ module MongoModel
|
|
92
94
|
end
|
93
95
|
|
94
96
|
if geo2d? && @min && @max
|
95
|
-
|
96
|
-
|
97
|
-
args << { :unique => true }
|
97
|
+
options[:min] = @min
|
98
|
+
options[:max] = @max
|
98
99
|
end
|
99
100
|
|
101
|
+
options[:unique] = true if unique?
|
102
|
+
options[:name] = @name if @name
|
103
|
+
|
104
|
+
args << options if options.any?
|
100
105
|
args
|
101
106
|
end
|
102
107
|
|
@@ -102,14 +102,11 @@ module MongoModel
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def collection_name
|
105
|
-
|
106
|
-
@_collection_name || name.tableize.gsub(/\//, '.')
|
107
|
-
else
|
108
|
-
superclass.collection_name
|
109
|
-
end
|
105
|
+
@_collection_name || inferred_collection_name
|
110
106
|
end
|
111
107
|
|
112
108
|
def collection_name=(name)
|
109
|
+
@_collection = nil
|
113
110
|
@_collection_name = name
|
114
111
|
end
|
115
112
|
|
@@ -130,12 +127,21 @@ module MongoModel
|
|
130
127
|
end
|
131
128
|
|
132
129
|
def save_safely?
|
133
|
-
@_save_safely
|
130
|
+
defined?(@_save_safely) ? @_save_safely : true
|
134
131
|
end
|
135
132
|
|
136
133
|
def save_safely=(val)
|
137
134
|
@_save_safely = val
|
138
135
|
end
|
136
|
+
|
137
|
+
protected
|
138
|
+
def inferred_collection_name
|
139
|
+
if superclass.abstract_class?
|
140
|
+
name.tableize.gsub(/\//, '.')
|
141
|
+
else
|
142
|
+
superclass.collection_name
|
143
|
+
end
|
144
|
+
end
|
139
145
|
end
|
140
146
|
|
141
147
|
private
|
@@ -40,17 +40,16 @@ module MongoModel
|
|
40
40
|
def setup!(klass)
|
41
41
|
@klass = klass
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
klass.index *[lowercase_key] + Array.wrap(options[:scope]) << { :unique => true }
|
43
|
+
unless options[:index] == false
|
44
|
+
# Create unique indexes to deal with race condition
|
45
|
+
attributes.each do |attr_name|
|
46
|
+
if options[:case_sensitive]
|
47
|
+
klass.index *[attr_name] + Array.wrap(options[:scope]) << { :unique => true }
|
48
|
+
else
|
49
|
+
lowercase_key = "_lowercase_#{attr_name}"
|
50
|
+
klass.before_save { attributes[lowercase_key] = send(attr_name).downcase }
|
51
|
+
klass.index *[lowercase_key] + Array.wrap(options[:scope]) << { :unique => true }
|
52
|
+
end
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
@@ -93,6 +92,7 @@ module MongoModel
|
|
93
92
|
# * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
|
94
93
|
# * <tt>:scope</tt> - One or more properties by which to limit the scope of the uniqueness constraint.
|
95
94
|
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
|
95
|
+
# * <tt>:index</tt> - If set to false, disables the unique index constraint (default is +true+).
|
96
96
|
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
97
97
|
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
98
98
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
@@ -27,9 +27,12 @@ module MongoModel
|
|
27
27
|
options['password']
|
28
28
|
end
|
29
29
|
|
30
|
+
def replicas
|
31
|
+
options['replicas'] || []
|
32
|
+
end
|
33
|
+
|
30
34
|
def establish_connection
|
31
|
-
@
|
32
|
-
@database = @connection.db(database)
|
35
|
+
@database = connection.db(database)
|
33
36
|
@database.authenticate(username, password) if username.present?
|
34
37
|
@database
|
35
38
|
end
|
@@ -39,8 +42,16 @@ module MongoModel
|
|
39
42
|
establish_connection
|
40
43
|
end
|
41
44
|
|
45
|
+
def connection
|
46
|
+
if replicas.any?
|
47
|
+
@connection ||= Mongo::MongoReplicaSetClient.new(replicas, connection_options)
|
48
|
+
else
|
49
|
+
@connection ||= Mongo::MongoClient.new(host, port, connection_options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
42
53
|
def connection_options
|
43
|
-
options.except('host', 'port', 'database', 'username', 'password').symbolize_keys
|
54
|
+
options.except('host', 'port', 'database', 'username', 'password', 'replicas').symbolize_keys
|
44
55
|
end
|
45
56
|
|
46
57
|
def options
|
@@ -80,9 +80,13 @@ module MongoModel
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def update_all(updates)
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
if updates.any?
|
84
|
+
selector = MongoOptions.new(klass, :conditions => finder_conditions).selector
|
85
|
+
collection.update(selector, { "$set" => updates }, { :multi => true })
|
86
|
+
reset
|
87
|
+
else
|
88
|
+
self
|
89
|
+
end
|
86
90
|
end
|
87
91
|
|
88
92
|
def update(ids, updates)
|
data/lib/mongomodel/version.rb
CHANGED
@@ -22,7 +22,7 @@ module MongoModel
|
|
22
22
|
end
|
23
23
|
define_class(:IllustratedChapter, :Chapter)
|
24
24
|
define_class(:Book, Document) do
|
25
|
-
has_many :chapters, :by => :foreign_key, :limit => 5
|
25
|
+
has_many :chapters, :by => :foreign_key, :limit => 5, :order => :id.asc
|
26
26
|
end
|
27
27
|
define_class(:NonChapter, Document)
|
28
28
|
|
@@ -96,6 +96,21 @@ module MongoModel
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
describe "embedded collection with :clear => true" do
|
100
|
+
define_user(EmbeddedDocument)
|
101
|
+
define_class(:Account, described_class) do
|
102
|
+
property :owners, Collection[User]
|
103
|
+
accepts_nested_attributes_for :owners, :clear => true
|
104
|
+
end
|
105
|
+
|
106
|
+
subject { Account.new(:owners => [User.new, User.new]) }
|
107
|
+
|
108
|
+
it "clears the collection first when assigning attributes" do
|
109
|
+
subject.owners_attributes = [{ :age => 18 }]
|
110
|
+
subject.owners.size.should == 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
99
114
|
describe "belongs_to association" do
|
100
115
|
define_user(Document)
|
101
116
|
define_class(:Account, described_class) do
|
@@ -175,6 +190,20 @@ module MongoModel
|
|
175
190
|
}.should raise_error(MongoModel::TooManyDocuments, "Maximum 2 documents are allowed. Got 3 documents instead.")
|
176
191
|
end
|
177
192
|
end
|
193
|
+
|
194
|
+
describe "has_many association with :clear => true" do
|
195
|
+
define_class(:Account, described_class) do
|
196
|
+
has_many :owners, :class => User
|
197
|
+
accepts_nested_attributes_for :owners, :clear => true
|
198
|
+
end
|
199
|
+
|
200
|
+
subject { Account.new(:owners => [User.new, User.new]) }
|
201
|
+
|
202
|
+
it "clears the collection first when assigning attributes" do
|
203
|
+
subject.owners_attributes = [{ :age => 18 }]
|
204
|
+
subject.owners.size.should == 1
|
205
|
+
end
|
206
|
+
end
|
178
207
|
end
|
179
208
|
end
|
180
209
|
end
|
@@ -87,46 +87,58 @@ module MongoModel
|
|
87
87
|
end
|
88
88
|
|
89
89
|
describe Index do
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
describe "#to_args" do
|
91
|
+
it "converts index with single key" do
|
92
|
+
Index.new(:title).to_args.should == [:title]
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
95
|
+
it "converts nested index with single key" do
|
96
|
+
Index.new('page.title').to_args.should == [:'page.title']
|
97
|
+
end
|
97
98
|
|
98
|
-
|
99
|
-
|
100
|
-
|
99
|
+
it "converts index with unique option" do
|
100
|
+
Index.new(:title, :unique => true).to_args.should == [:title, { :unique => true }]
|
101
|
+
end
|
102
|
+
|
103
|
+
it "converts index with name option" do
|
104
|
+
Index.new(:title, :name => "my_name").to_args.should == [:title, { :name => "my_name" }]
|
105
|
+
end
|
106
|
+
|
107
|
+
it "converts index with multiple options" do
|
108
|
+
Index.new(:title, :name => "my_name", :unique => true).to_args.should == [:title, { :name => "my_name", :unique => true }]
|
109
|
+
end
|
101
110
|
|
102
|
-
|
103
|
-
|
104
|
-
|
111
|
+
it "converts index with descending key" do
|
112
|
+
Index.new(:title => :descending).to_args.should == [[[:title, Mongo::DESCENDING]]]
|
113
|
+
end
|
105
114
|
|
106
|
-
|
107
|
-
|
108
|
-
|
115
|
+
it "converts index with multiple keys" do
|
116
|
+
Index.new(:title, :age).to_args.should == [[[:age, Mongo::ASCENDING], [:title, Mongo::ASCENDING]]]
|
117
|
+
end
|
109
118
|
|
110
|
-
|
111
|
-
|
112
|
-
|
119
|
+
it "converts index with multiple keys (ascending and descending)" do
|
120
|
+
Index.new(:title => :ascending, :age => :descending).to_args.should == [[[:age, Mongo::DESCENDING], [:title, Mongo::ASCENDING]]]
|
121
|
+
end
|
113
122
|
|
114
|
-
|
115
|
-
|
116
|
-
|
123
|
+
it "converts geospatial index with no options" do
|
124
|
+
Index.new(:position => :geo2d).to_args.should == [[[:position, Mongo::GEO2D]]]
|
125
|
+
end
|
117
126
|
|
118
|
-
|
119
|
-
|
127
|
+
it "converts geospatial index with min/max options" do
|
128
|
+
Index.new(:position => :geo2d, :min => -50, :max => 50).to_args.should == [[[:position, Mongo::GEO2D]], { :min => -50, :max => 50 }]
|
129
|
+
end
|
120
130
|
end
|
121
131
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
132
|
+
describe "equality" do
|
133
|
+
it "is equal to an equivalent index" do
|
134
|
+
Index.new(:title).should == Index.new(:title)
|
135
|
+
Index.new(:title, :age).should == Index.new(:title => :ascending, :age => :ascending)
|
136
|
+
end
|
126
137
|
|
127
|
-
|
128
|
-
|
129
|
-
|
138
|
+
it "is not equal to an non-equivalent index" do
|
139
|
+
Index.new(:title).should_not == Index.new(:age)
|
140
|
+
Index.new(:title, :age).should_not == Index.new(:title => :ascending, :age => :descending)
|
141
|
+
end
|
130
142
|
end
|
131
143
|
end
|
132
144
|
end
|
@@ -94,6 +94,15 @@ module MongoModel
|
|
94
94
|
|
95
95
|
::CustomCollectionNameSubclass.collection_name.should == 'foobar'
|
96
96
|
end
|
97
|
+
|
98
|
+
it "allows subclasses to set a custom collection name" do
|
99
|
+
class ::CustomCollectionName < Document; end
|
100
|
+
class ::CustomCollectionNameSubclass < ::CustomCollectionName
|
101
|
+
self.collection_name = 'custom'
|
102
|
+
end
|
103
|
+
|
104
|
+
::CustomCollectionNameSubclass.collection_name.should == 'custom'
|
105
|
+
end
|
97
106
|
end
|
98
107
|
|
99
108
|
describe "#collection" do
|
@@ -104,6 +113,12 @@ module MongoModel
|
|
104
113
|
it "uses the correct collection name" do
|
105
114
|
User.collection.name.should == 'users'
|
106
115
|
end
|
116
|
+
|
117
|
+
it "is updated when the collection name changes" do
|
118
|
+
collection = User.collection
|
119
|
+
User.collection_name = "custom"
|
120
|
+
User.collection.name.should == "custom"
|
121
|
+
end
|
107
122
|
end
|
108
123
|
|
109
124
|
describe "#database" do
|
@@ -31,6 +31,32 @@ module MongoModel
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
describe "indexes" do
|
35
|
+
define_class(:Article, Document) do
|
36
|
+
property :title, String
|
37
|
+
end
|
38
|
+
|
39
|
+
it "creates an index on the attribute" do
|
40
|
+
Article.should_receive(:index).with(:title, :unique => true)
|
41
|
+
Article.validates_uniqueness_of :title
|
42
|
+
end
|
43
|
+
|
44
|
+
it "creates an index on the lowercase attribute if :case_sensitive => false" do
|
45
|
+
Article.should_receive(:index).with("_lowercase_title", :unique => true)
|
46
|
+
Article.validates_uniqueness_of :title, :case_sensitive => false
|
47
|
+
end
|
48
|
+
|
49
|
+
it "creates a compound index when a scope is passed" do
|
50
|
+
Article.should_receive(:index).with(:title, :author_id, :unique => true)
|
51
|
+
Article.validates_uniqueness_of :title, :scope => :author_id
|
52
|
+
end
|
53
|
+
|
54
|
+
it "does not create an index if :index => false" do
|
55
|
+
Article.should_not_receive(:index)
|
56
|
+
Article.validates_uniqueness_of :title, :index => false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
34
60
|
describe "basic case" do
|
35
61
|
define_class(:Article, Document) do
|
36
62
|
property :title, String
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MongoModel
|
4
|
+
describe Configuration do
|
5
|
+
it "uses a standard connection (Mongo::MongoClient) if no replicas are specified" do
|
6
|
+
Configuration.new({}).connection.should be_an_instance_of(Mongo::MongoClient)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "uses a replica set connection (Mongo::MongoReplicaSetClient) if replicas are specified" do
|
10
|
+
Configuration.new({ :replicas => ['127.0.0.1:27017'], :connect => false }).connection.should be_an_instance_of(Mongo::MongoReplicaSetClient)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongomodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Pohlenz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -259,6 +259,7 @@ files:
|
|
259
259
|
- spec/mongomodel/embedded_document_spec.rb
|
260
260
|
- spec/mongomodel/mongomodel_spec.rb
|
261
261
|
- spec/mongomodel/support/collection_spec.rb
|
262
|
+
- spec/mongomodel/support/configuration_spec.rb
|
262
263
|
- spec/mongomodel/support/map_spec.rb
|
263
264
|
- spec/mongomodel/support/mongo_operator_spec.rb
|
264
265
|
- spec/mongomodel/support/mongo_options_spec.rb
|