mongomodel 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile CHANGED
@@ -1,12 +1,11 @@
1
1
  source "http://rubygems.org"
2
- git "git://github.com/rails/rails.git"
3
2
 
4
- gem "activemodel", ">= 3.0.0.beta4"
5
- gem "activesupport", ">= 3.0.0.beta4"
3
+ gem "activemodel", "~> 3.0.0"
4
+ gem "activesupport", "~> 3.0.0"
6
5
  gem "tzinfo"
7
6
 
8
- gem "mongo", '>= 1.0'
9
- gem "bson", '>= 1.0'
10
- gem "bson_ext", '>= 1.0'
7
+ gem "mongo", '= 1.0.7'
8
+ gem "bson", '= 1.0.7'
9
+ gem "bson_ext", '= 1.0.7'
11
10
 
12
11
  gem "rspec"
data/Rakefile CHANGED
@@ -46,10 +46,9 @@ begin
46
46
  gem.authors = ["Sam Pohlenz"]
47
47
  gem.version = MongoModel::VERSION
48
48
 
49
- gem.add_dependency('activesupport', '>= 3.0.0.beta4')
50
- gem.add_dependency('activemodel', '>= 3.0.0.beta4')
51
- gem.add_dependency('mongo', '>= 1.0')
52
- gem.add_dependency('bson', '>= 1.0')
49
+ gem.add_dependency('activesupport', '~> 3.0.0')
50
+ gem.add_dependency('activemodel', '~> 3.0.0')
51
+ gem.add_dependency('mongo', '~> 1.0.7')
53
52
  gem.add_development_dependency('rspec', '>= 1.3.0')
54
53
  end
55
54
 
@@ -14,7 +14,9 @@ module MongoModel
14
14
 
15
15
  module ClassMethods
16
16
  def property(name, type, options={})
17
- properties[name.to_sym] = Property.new(name, type, options)
17
+ properties[name.to_sym] = Property.new(name, type, options).tap do |property|
18
+ include type.mongomodel_accessors(property) if type.respond_to?(:mongomodel_accessors)
19
+ end
18
20
  end
19
21
 
20
22
  def model_properties
@@ -40,15 +42,19 @@ module MongoModel
40
42
  end
41
43
 
42
44
  def default(instance)
43
- default = options[:default]
44
-
45
- if default.respond_to?(:call)
46
- case default.arity
47
- when 0 then default.call
48
- else default.call(instance)
45
+ if options.key?(:default)
46
+ default = options[:default]
47
+
48
+ if default.respond_to?(:call)
49
+ case default.arity
50
+ when 0 then default.call
51
+ else default.call(instance)
52
+ end
53
+ else
54
+ default.duplicable? ? default.dup : default
49
55
  end
50
- else
51
- default.duplicable? ? default.dup : default
56
+ elsif type.respond_to?(:mongomodel_default)
57
+ type.mongomodel_default(instance)
52
58
  end
53
59
  end
54
60
 
@@ -31,7 +31,7 @@ module MongoModel
31
31
  if locking_enabled? && _lock_version > 1
32
32
  begin
33
33
  collection.update({ '_id' => id, '_lock_version' => _lock_version-1 }, to_mongo)
34
- success = database.last_status['updatedExisting']
34
+ success = database.get_last_error['updatedExisting']
35
35
 
36
36
  self._lock_version -= 1 unless success
37
37
 
@@ -68,10 +68,10 @@ module MongoModel
68
68
  self.class.database
69
69
  end
70
70
 
71
- # Generate a new BSON::ObjectID for the record.
71
+ # Generate a new BSON::ObjectId for the record.
72
72
  # Override in subclasses for custom ID generation.
73
73
  def generate_id
74
- ::BSON::ObjectID.new.to_s
74
+ ::BSON::ObjectId.new.to_s
75
75
  end
76
76
 
77
77
  module ClassMethods
@@ -1,6 +1,70 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
1
3
  module MongoModel
2
4
  module DocumentExtensions
3
5
  module Validations
6
+ class UniquenessValidator < ActiveModel::EachValidator
7
+ def initialize(options)
8
+ super(options.reverse_merge(:case_sensitive => true))
9
+ end
10
+
11
+ def setup(klass)
12
+ @klass = klass
13
+
14
+ # Enable safety checks on save
15
+ klass.save_safely = true
16
+
17
+ # Create unique indexes to deal with race condition
18
+ attributes.each do |attr_name|
19
+ if options[:case_sensitive]
20
+ klass.index *[attr_name] + Array.wrap(options[:scope]) << { :unique => true }
21
+ else
22
+ lowercase_key = "_lowercase_#{attr_name}"
23
+ klass.before_save { attributes[lowercase_key] = send(attr_name).downcase }
24
+ klass.index *[lowercase_key] + Array.wrap(options[:scope]) << { :unique => true }
25
+ end
26
+ end
27
+ end
28
+
29
+ def validate_each(record, attribute, value)
30
+ finder_class = find_finder_class_for(record)
31
+ unique_scope = finder_class.scoped
32
+
33
+ if options[:case_sensitive] || !value.is_a?(String)
34
+ unique_scope = unique_scope.where(attribute => value)
35
+ else
36
+ unique_scope = unique_scope.where("_lowercase_#{attribute}" => value.downcase)
37
+ end
38
+
39
+ Array.wrap(options[:scope]).each do |scope|
40
+ unique_scope = unique_scope.where(scope => record.send(scope))
41
+ end
42
+
43
+ unique_scope = unique_scope.where(:id.ne => record.id) unless record.new_record?
44
+
45
+ if unique_scope.any?
46
+ record.errors.add(attribute, :taken, :message => options[:message], :value => value)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # The check for an existing value should be run from a class that
53
+ # isn't abstract. This means working down from the current class
54
+ # (self), to the first non-abstract class. Since classes don't know
55
+ # their subclasses, we have to build the hierarchy between self and
56
+ # the record's class.
57
+ def find_finder_class_for(record) #:nodoc:
58
+ class_hierarchy = [record.class]
59
+
60
+ while class_hierarchy.first != @klass
61
+ class_hierarchy.insert(0, class_hierarchy.first.superclass)
62
+ end
63
+
64
+ class_hierarchy.detect { |klass| !klass.abstract_class? }
65
+ end
66
+ end
67
+
4
68
  module ClassMethods
5
69
  # Validates whether the value of the specified attributes are unique across the system. Useful for making sure that only one user
6
70
  # can be named "davidhh".
@@ -37,42 +101,7 @@ module MongoModel
37
101
  # Note that this validation method does not have the same race condition suffered by ActiveRecord and other ORMs.
38
102
  # A unique index is added to the collection to ensure that the collection never ends up in an invalid state.
39
103
  def validates_uniqueness_of(*attr_names)
40
- configuration = { :case_sensitive => true }
41
- configuration.update(attr_names.extract_options!)
42
-
43
- # Enable safety checks on save
44
- self.save_safely = true
45
-
46
- # Create unique indexes to deal with race condition
47
- attr_names.each do |attr_name|
48
- if configuration[:case_sensitive]
49
- index *[attr_name] + Array(configuration[:scope]) << { :unique => true }
50
- else
51
- lowercase_key = "_lowercase_#{attr_name}"
52
- before_save { attributes[lowercase_key] = send(attr_name).downcase }
53
- index *[lowercase_key] + Array(configuration[:scope]) << { :unique => true }
54
- end
55
- end
56
-
57
- validates_each(attr_names, configuration) do |record, attr_name, value|
58
- unique_scope = scoped
59
-
60
- if configuration[:case_sensitive] || !value.is_a?(String)
61
- unique_scope = unique_scope.where(attr_name => value)
62
- else
63
- unique_scope = unique_scope.where("_lowercase_#{attr_name}" => value.downcase)
64
- end
65
-
66
- Array(configuration[:scope]).each do |scope|
67
- unique_scope = unique_scope.where(scope => record.send(scope))
68
- end
69
-
70
- unique_scope = unique_scope.where(:id.ne => record.id) unless record.new_record?
71
-
72
- if unique_scope.any?
73
- record.errors.add(attr_name, :taken, :default => configuration[:message], :value => value)
74
- end
75
- end
104
+ validates_with UniquenessValidator, _merge_attributes(attr_names)
76
105
  end
77
106
  end
78
107
  end
@@ -115,6 +115,10 @@ module MongoModel
115
115
  super(convert_value(value))
116
116
  end
117
117
 
118
+ def key(value)
119
+ super(convert_value(value))
120
+ end
121
+
118
122
  def update(hash)
119
123
  hash.each_pair { |k, v| self[k] = v }
120
124
  self
@@ -71,7 +71,7 @@ module MongoModel
71
71
 
72
72
  def add_type_to_selector
73
73
  unless selector['_type'] || @model.superclass.abstract_class?
74
- selector['_type'] = { '$in' => [@model.to_s] + @model.subclasses }
74
+ selector['_type'] = { '$in' => [@model.to_s] + @model.subclasses.map { |m| m.to_s } }
75
75
  end
76
76
  end
77
77
  end
@@ -31,6 +31,12 @@ module MongoModel
31
31
  CEVAL
32
32
  end
33
33
 
34
+ def from(value, &block)
35
+ new_scope = clone
36
+ new_scope.from_value = value.is_a?(String) ? klass.database.collection(value) : value
37
+ new_scope
38
+ end
39
+
34
40
  def reverse_order
35
41
  if order_values.empty?
36
42
  order(:id.desc)
@@ -5,9 +5,8 @@ module MongoModel
5
5
  module Types
6
6
  class Time < Object
7
7
  def cast(value)
8
- time = value.to_time.utc
9
- # BSON only stores time accurate to the millisecond
10
- ::Time.at((time.to_f * 1000).floor / 1000.0)
8
+ time = value.to_time
9
+ time.change(:usec => (time.usec / 1000.0).floor * 1000)
11
10
  rescue
12
11
  nil
13
12
  end
@@ -1,3 +1,3 @@
1
1
  module MongoModel
2
- VERSION = "0.2.6"
2
+ VERSION = "0.2.7"
3
3
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongomodel}
8
- s.version = "0.2.6"
8
+ s.version = "0.2.7"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Sam Pohlenz"]
12
- s.date = %q{2010-06-09}
12
+ s.date = %q{2010-09-05}
13
13
  s.default_executable = %q{console}
14
14
  s.description = %q{MongoModel is a MongoDB ORM for Ruby/Rails similar to ActiveRecord and DataMapper.}
15
15
  s.email = %q{sam@sampohlenz.com}
@@ -217,23 +217,20 @@ Gem::Specification.new do |s|
217
217
  s.specification_version = 3
218
218
 
219
219
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
220
- s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0.beta4"])
221
- s.add_runtime_dependency(%q<activemodel>, [">= 3.0.0.beta4"])
222
- s.add_runtime_dependency(%q<mongo>, [">= 1.0"])
223
- s.add_runtime_dependency(%q<bson>, [">= 1.0"])
220
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.0"])
221
+ s.add_runtime_dependency(%q<activemodel>, ["~> 3.0.0"])
222
+ s.add_runtime_dependency(%q<mongo>, ["~> 1.0.7"])
224
223
  s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
225
224
  else
226
- s.add_dependency(%q<activesupport>, [">= 3.0.0.beta4"])
227
- s.add_dependency(%q<activemodel>, [">= 3.0.0.beta4"])
228
- s.add_dependency(%q<mongo>, [">= 1.0"])
229
- s.add_dependency(%q<bson>, [">= 1.0"])
225
+ s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
226
+ s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
227
+ s.add_dependency(%q<mongo>, ["~> 1.0.7"])
230
228
  s.add_dependency(%q<rspec>, [">= 1.3.0"])
231
229
  end
232
230
  else
233
- s.add_dependency(%q<activesupport>, [">= 3.0.0.beta4"])
234
- s.add_dependency(%q<activemodel>, [">= 3.0.0.beta4"])
235
- s.add_dependency(%q<mongo>, [">= 1.0"])
236
- s.add_dependency(%q<bson>, [">= 1.0"])
231
+ s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
232
+ s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
233
+ s.add_dependency(%q<mongo>, ["~> 1.0.7"])
237
234
  s.add_dependency(%q<rspec>, [">= 1.3.0"])
238
235
  end
239
236
  end
@@ -15,6 +15,7 @@ module MongoModel
15
15
  properties[:date] = MongoModel::Properties::Property.new(:date, Date)
16
16
  properties[:time] = MongoModel::Properties::Property.new(:time, Time)
17
17
  properties[:custom] = MongoModel::Properties::Property.new(:custom, CustomClass)
18
+ properties[:custom_default] = MongoModel::Properties::Property.new(:custom_default, CustomClassWithDefault)
18
19
  properties[:default] = MongoModel::Properties::Property.new(:default, String, :default => 'Default')
19
20
  properties[:as] = MongoModel::Properties::Property.new(:as, String, :as => '_custom_as')
20
21
  properties
@@ -28,6 +29,10 @@ module MongoModel
28
29
  subject[:default].should == 'Default'
29
30
  end
30
31
 
32
+ it "should set default property value using mongomodel_default if defined by class" do
33
+ subject[:custom_default].should == CustomClassWithDefault.new("Custom class default")
34
+ end
35
+
31
36
  describe "setting to nil" do
32
37
  specify "all property types should be nullable" do
33
38
  properties.keys.each do |property|
@@ -52,5 +52,18 @@ module MongoModel
52
52
  factory.manager.should be_nil
53
53
  end
54
54
  end
55
+
56
+ describe "when using a property type that defines #mongomodel_accessors" do
57
+ define_class(:ParentClass, described_class) do
58
+ property :custom, CustomClassWithAccessors
59
+ end
60
+
61
+ subject { ParentClass.new }
62
+
63
+ it "should include methods from the module" do
64
+ subject.should respond_to(:custom_accessor)
65
+ subject.custom_accessor.should == "Custom accessor method"
66
+ end
67
+ end
55
68
  end
56
69
  end
@@ -101,8 +101,14 @@ module MongoModel
101
101
  subject.value?(456).should be_true
102
102
  end
103
103
 
104
- it "should cast values on #index" do
105
- subject.index(456).should == "123"
104
+ if Hash.method_defined?(:key)
105
+ it "should cast values on #key" do
106
+ subject.key(456).should == "123"
107
+ end
108
+ else
109
+ it "should cast values on #index" do
110
+ subject.index(456).should == "123"
111
+ end
106
112
  end
107
113
 
108
114
  it "should cast key/values on #replace" do
@@ -177,8 +183,14 @@ module MongoModel
177
183
  subject.value?("Another").should be_true
178
184
  end
179
185
 
180
- it "should cast values on #index" do
181
- subject.index("First").should == :abc
186
+ if Hash.method_defined?(:key)
187
+ it "should cast values on #key" do
188
+ subject.key("First").should == :abc
189
+ end
190
+ else
191
+ it "should cast values on #index" do
192
+ subject.index("First").should == :abc
193
+ end
182
194
  end
183
195
 
184
196
  it "should cast key/values on #replace" do
@@ -309,6 +309,10 @@ module MongoModel
309
309
  it "should override collection" do
310
310
  subject.from(NotAPost.collection).collection.should == NotAPost.collection
311
311
  end
312
+
313
+ it "should allow collection to be set using string" do
314
+ subject.from(NotAPost.collection.name).collection.name.should == NotAPost.collection.name
315
+ end
312
316
  end
313
317
 
314
318
  describe "#first" do
@@ -21,3 +21,19 @@ class CustomClass
21
21
  new(value.to_s)
22
22
  end
23
23
  end
24
+
25
+ class CustomClassWithDefault < CustomClass
26
+ def self.mongomodel_default(doc)
27
+ new("Custom class default")
28
+ end
29
+ end
30
+
31
+ class CustomClassWithAccessors < CustomClass
32
+ def self.mongomodel_accessors(property)
33
+ Module.new do
34
+ define_method(:custom_accessor) do
35
+ "Custom accessor method"
36
+ end
37
+ end
38
+ end
39
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongomodel
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 6
10
- version: 0.2.6
9
+ - 7
10
+ version: 0.2.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sam Pohlenz
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-09 00:00:00 +09:30
18
+ date: 2010-09-05 00:00:00 +09:30
19
19
  default_executable: console
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -24,15 +24,14 @@ dependencies:
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ">="
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
- hash: -1848230024
29
+ hash: 7
30
30
  segments:
31
31
  - 3
32
32
  - 0
33
33
  - 0
34
- - beta4
35
- version: 3.0.0.beta4
34
+ version: 3.0.0
36
35
  type: :runtime
37
36
  version_requirements: *id001
38
37
  - !ruby/object:Gem::Dependency
@@ -41,15 +40,14 @@ dependencies:
41
40
  requirement: &id002 !ruby/object:Gem::Requirement
42
41
  none: false
43
42
  requirements:
44
- - - ">="
43
+ - - ~>
45
44
  - !ruby/object:Gem::Version
46
- hash: -1848230024
45
+ hash: 7
47
46
  segments:
48
47
  - 3
49
48
  - 0
50
49
  - 0
51
- - beta4
52
- version: 3.0.0.beta4
50
+ version: 3.0.0
53
51
  type: :runtime
54
52
  version_requirements: *id002
55
53
  - !ruby/object:Gem::Dependency
@@ -58,34 +56,20 @@ dependencies:
58
56
  requirement: &id003 !ruby/object:Gem::Requirement
59
57
  none: false
60
58
  requirements:
61
- - - ">="
59
+ - - ~>
62
60
  - !ruby/object:Gem::Version
63
- hash: 15
61
+ hash: 25
64
62
  segments:
65
63
  - 1
66
64
  - 0
67
- version: "1.0"
65
+ - 7
66
+ version: 1.0.7
68
67
  type: :runtime
69
68
  version_requirements: *id003
70
- - !ruby/object:Gem::Dependency
71
- name: bson
72
- prerelease: false
73
- requirement: &id004 !ruby/object:Gem::Requirement
74
- none: false
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- hash: 15
79
- segments:
80
- - 1
81
- - 0
82
- version: "1.0"
83
- type: :runtime
84
- version_requirements: *id004
85
69
  - !ruby/object:Gem::Dependency
86
70
  name: rspec
87
71
  prerelease: false
88
- requirement: &id005 !ruby/object:Gem::Requirement
72
+ requirement: &id004 !ruby/object:Gem::Requirement
89
73
  none: false
90
74
  requirements:
91
75
  - - ">="
@@ -97,7 +81,7 @@ dependencies:
97
81
  - 0
98
82
  version: 1.3.0
99
83
  type: :development
100
- version_requirements: *id005
84
+ version_requirements: *id004
101
85
  description: MongoModel is a MongoDB ORM for Ruby/Rails similar to ActiveRecord and DataMapper.
102
86
  email: sam@sampohlenz.com
103
87
  executables: