mongomodel 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 730ec72969141979066d7a38f450b9622485bb55
4
- data.tar.gz: b72b388e3b6b6d041dd4f6431233d25c0aeddd8c
3
+ metadata.gz: 3d006082bc3f5bf2357182fecea49893a06e5994
4
+ data.tar.gz: d53384261c1968e770c65c4eb4e8faa32ade2c22
5
5
  SHA512:
6
- metadata.gz: b954cd2ce4ed17b3dc9406572d8523092db0d249dfe9c1c61418b96b15702a91df7e740e4ed9af3581d41e5f37c8497447fceb794f7c01f661aa935007999f39
7
- data.tar.gz: 0a92c9c4215cea8d312d9ecd13684c8bf9d54cfada4410e6587902506edc6093314672a39e448ce07659bf1991b82eb24e77879227f15eebce938e8f6f0dbc3c
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
- args << { :min => @min, :max => @max }
96
- elsif unique?
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
- if superclass.abstract_class?
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
- # Enable safety checks on save
44
- klass.save_safely = true
45
-
46
- # Create unique indexes to deal with race condition
47
- attributes.each do |attr_name|
48
- if options[:case_sensitive]
49
- klass.index *[attr_name] + Array.wrap(options[:scope]) << { :unique => true }
50
- else
51
- lowercase_key = "_lowercase_#{attr_name}"
52
- klass.before_save { attributes[lowercase_key] = send(attr_name).downcase }
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
- @connection ||= Mongo::Connection.new(host, port, connection_options)
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
- selector = MongoOptions.new(klass, :conditions => finder_conditions).selector
84
- collection.update(selector, { "$set" => updates }, { :multi => true })
85
- reset
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)
@@ -1,3 +1,3 @@
1
1
  module MongoModel
2
- VERSION = "0.5.3"
2
+ VERSION = "0.5.4"
3
3
  end
@@ -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
- it "converts index with single key to arguments for Mongo::Collection#create_index" do
91
- Index.new(:title).to_args.should == [:title]
92
- end
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
- it "converts nested index with single key to arguments for Mongo::Collection#create_index" do
95
- Index.new('page.title').to_args.should == [:'page.title']
96
- end
95
+ it "converts nested index with single key" do
96
+ Index.new('page.title').to_args.should == [:'page.title']
97
+ end
97
98
 
98
- it "converts index with unique option to arguments for Mongo::Collection#create_index" do
99
- Index.new(:title, :unique => true).to_args.should == [:title, { :unique => true }]
100
- end
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
- it "converts index with descending key to arguments for Mongo::Collection#create_index" do
103
- Index.new(:title => :descending).to_args.should == [[[:title, Mongo::DESCENDING]]]
104
- end
111
+ it "converts index with descending key" do
112
+ Index.new(:title => :descending).to_args.should == [[[:title, Mongo::DESCENDING]]]
113
+ end
105
114
 
106
- it "converts index with multiple keys to arguments for Mongo::Collection#create_index" do
107
- Index.new(:title, :age).to_args.should == [[[:age, Mongo::ASCENDING], [:title, Mongo::ASCENDING]]]
108
- end
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
- it "converts index with multiple keys (ascending and descending) to arguments for Mongo::Collection#create_index" do
111
- Index.new(:title => :ascending, :age => :descending).to_args.should == [[[:age, Mongo::DESCENDING], [:title, Mongo::ASCENDING]]]
112
- end
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
- it "converts geospatial index with no options" do
115
- Index.new(:position => :geo2d).to_args.should == [[[:position, Mongo::GEO2D]]]
116
- end
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
- it "converts geospatial index with min/max options" do
119
- Index.new(:position => :geo2d, :min => -50, :max => 50).to_args.should == [[[:position, Mongo::GEO2D]], { :min => -50, :max => 50 }]
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
- it "is equal to an equivalent index" do
123
- Index.new(:title).should == Index.new(:title)
124
- Index.new(:title, :age).should == Index.new(:title => :ascending, :age => :ascending)
125
- end
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
- it "is not equal to an non-equivalent index" do
128
- Index.new(:title).should_not == Index.new(:age)
129
- Index.new(:title, :age).should_not == Index.new(:title => :ascending, :age => :descending)
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.3
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-04-13 00:00:00.000000000 Z
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