mongomodel 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
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: