mongodb_model 2.0.2 → 2.1.0

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/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake_ext'
2
2
 
3
3
  project \
4
4
  name: "mongodb_model",
5
- # version: '2.0.0',
5
+ version: '2.1.0',
6
6
  gem: true,
7
7
  summary: "Object Model for MongoDB",
8
8
 
@@ -23,7 +23,8 @@ module Mongo::Model::Assignment
23
23
  v = type.cast(v) if type
24
24
  send "#{n}=", v
25
25
  else
26
- raise "mass assignment for :#{n} attribute not allowed!"
26
+ # Do nothing, just silently skip it.
27
+ # raise "mass assignment for :#{n} attribute not allowed!"
27
28
  end
28
29
  end
29
30
  else
@@ -1,10 +1,12 @@
1
+ require 'mongo/model/support/rson'
2
+
1
3
  module Mongo::Model::Conversion
2
4
  def model_to_json *args, &block
3
- to_hash(*args, &block).to_json
5
+ to_rson(*args, &block).to_json
4
6
  end
5
7
 
6
8
  def model_to_xml *args, &block
7
- to_hash(*args, &block).to_xml
9
+ to_rson(*args, &block).to_xml
8
10
  end
9
11
 
10
12
  alias_method :to_json, :model_to_json
@@ -19,13 +21,15 @@ module Mongo::Model::Conversion
19
21
  alias_method :as_xml, :model_to_xml
20
22
  end
21
23
 
22
- def to_hash options = {}
24
+ def to_rson options = {}
25
+ options = {profile: options} if options.is_a? Symbol
26
+
23
27
  if profile = options[:profile]
24
28
  raise "no other optins are allowed when using :profile option!" if options.size > 1
25
29
  profile_options = self.class.profiles[profile] || raise("profile :#{profile} not defined for #{self.class}!")
26
- to_hash profile_options.merge(_profile: profile)
30
+ to_rson profile_options.merge(_profile: profile)
27
31
  else
28
- options.validate_options! :only, :except, :methods, :errors, :_profile
32
+ options.validate_options! :only, :except, :methods, :errors, :id, :_profile
29
33
  child_options = options[:_profile] ? {profile: options[:_profile]} : {}
30
34
 
31
35
  instance_variables = self.persistent_instance_variable_names
@@ -39,15 +43,15 @@ module Mongo::Model::Conversion
39
43
  result = {}
40
44
  instance_variables.each do |iv_name|
41
45
  value = instance_variable_get iv_name
42
- value = convert_model value, :to_hash, child_options
43
- result[iv_name[1.. -1].to_sym] = value
46
+ # value = convert_object value, :to_rson, child_options
47
+ result[iv_name[1.. -1].to_sym] = value.to_rson child_options
44
48
  end
45
49
 
46
50
  methods = options[:methods] ? Array(options[:methods]) : []
47
51
  methods.each do |method|
48
52
  value = send method
49
- value = convert_model value, :to_hash, child_options
50
- result[method.to_sym] = value
53
+ # value = convert_object value, :to_rson, child_options
54
+ result[method.to_sym] = value.to_rson child_options
51
55
  end
52
56
 
53
57
  with_errors = options.include?(:errors) ? options[:errors] : true
@@ -57,24 +61,12 @@ module Mongo::Model::Conversion
57
61
  result[:errors] = errors
58
62
  end
59
63
 
64
+ result[:id] = _id if _id and (options[:id] != false)
65
+
60
66
  result
61
67
  end
62
68
  end
63
69
 
64
- protected
65
- def convert_model obj, method, options
66
- if obj.respond_to? :collect_with_value
67
- # Array or Hash.
68
- obj.collect_with_value{|v| convert_model v, method, options}
69
- elsif obj.respond_to? method
70
- # Model.
71
- obj.send method, options
72
- else
73
- # Simple object.
74
- obj
75
- end
76
- end
77
-
78
70
  module ClassMethods
79
71
  inheritable_accessor :profiles, {}
80
72
  def profile name, options = {}
@@ -22,7 +22,19 @@ module Mongo::Model::Db
22
22
  def default_collection_name
23
23
  first_ancestor_class = ancestors.find{|a| a.is_a? Class} ||
24
24
  raise("can't evaluate default collection name for #{self}!")
25
- first_ancestor_class.alias.pluralize.underscore.to_sym
25
+ als = first_ancestor_class.alias
26
+
27
+ unless als.respond_to? :pluralize
28
+ warn <<-TEXT
29
+ WARN: It seems that there's no `String.pluralize` method, Mongo::Model needs it to automatically infer
30
+ collection name from the model class name.
31
+ Please specify collection name explicitly (like `collection :users`) or provide the `pluralize`
32
+ method.
33
+ TEXT
34
+ raise "collection name for #{first_ancestor_class} not defined (add it, like `collection :users`)!"
35
+ end
36
+
37
+ als.pluralize.underscore.to_sym
26
38
  end
27
39
  end
28
40
  end
@@ -0,0 +1,38 @@
1
+ module Mongo::Model::IdentityMap
2
+ inherited do
3
+ Mongo::Model::IdentityMap.models.add self
4
+ end
5
+
6
+ def original
7
+ unless _cache[:original_cached]
8
+ _cache[:original_cached] = true
9
+ _cache[:original] = _id && self.class.get_from_identity_map(_id)
10
+ end
11
+ _cache[:original]
12
+ end
13
+
14
+ module ClassMethods
15
+ def identity_map
16
+ @identity_map ||= {}
17
+ end
18
+
19
+ def get_from_identity_map id
20
+ doc = identity_map[id]
21
+ from_mongo doc if doc
22
+ end
23
+
24
+ def from_mongo doc
25
+ model = super doc
26
+ model.class.identity_map[model._id] = doc if model._id
27
+ model
28
+ end
29
+ end
30
+
31
+ class << self
32
+ def models
33
+ @models ||= Set.new
34
+ end
35
+
36
+ def clear; models.collect(&:identity_map).every.clear end
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ require 'mongo/migration/tasks'
2
+
3
+ namespace :db do
4
+ desc "Internal task to prepare migration environment"
5
+ task migration_evnironment: :environment do
6
+ require 'mongo/migration'
7
+
8
+ Dir["#{rad.runtime_path}/db/**/*.rb"].each{|f| require f.sub(/\.rb$/, '')}
9
+ end
10
+ end
@@ -0,0 +1,41 @@
1
+ # Registering it as component.
2
+
3
+ class Mongo::Model::Component
4
+ attr_accessor :db
5
+ attr_required :db
6
+
7
+ attr_accessor :fs
8
+ attr_required :fs
9
+ end
10
+
11
+ rad.register :models do
12
+ Mongo::Model::Component.new
13
+ end
14
+
15
+ # Using DB connection setting defined in component's config file.
16
+ Mongo.metaclass_eval do
17
+ def db name
18
+ config = rad.models.db[name.to_s] || raise("no database config for #{name} alias!")
19
+ host, port, options = config['host'], config['port'], (config['options'] || {})
20
+ connection = self.connection host, port, options
21
+ db_name = config['name'] || raise("no database name for #{name} alias!")
22
+ connection.db db_name
23
+ end
24
+ cache_method_with_params :db
25
+
26
+ def connection host, port, options
27
+ options[:logger] = rad.logger unless options.include? :logger
28
+ Mongo::Connection.new host, port, options
29
+ end
30
+ cache_method_with_params :connection
31
+ end
32
+
33
+ # Localization
34
+
35
+ Mongo::Model::Misc.class_eval do
36
+ def t *args; rad.locale.t *args end
37
+ end
38
+
39
+ Mongo::Model::Misc::ClassMethods.class_eval do
40
+ def t *args; rad.locale.t *args end
41
+ end
@@ -20,6 +20,8 @@ module Mongo::Model::Rails
20
20
  persisted? ? [_id] : nil
21
21
  end
22
22
 
23
+ def new_record?; new? end
24
+
23
25
  module ClassMethods
24
26
  def model_name
25
27
  @_model_name ||= begin
@@ -1,17 +1,14 @@
1
- require 'mongo/object'
2
1
  require 'ruby_ext'
3
2
 
4
3
  module Mongo::Model; end
5
4
 
6
5
  %w(
7
- support
8
-
9
6
  db
10
7
  conversion
11
8
  assignment
12
9
  callbacks
13
- validation
14
10
  crud
11
+ validation
15
12
  query
16
13
  query_mixin
17
14
  scope
@@ -20,17 +17,18 @@ module Mongo::Model; end
20
17
  model
21
18
  ).each{|f| require "mongo/model/#{f}"}
22
19
 
20
+ # Assembling model.
23
21
  module Mongo
24
22
  module Model
25
- autoload :FileModel, 'mongo/model/integration/file_model'
23
+ autoload :IdentityMap, 'mongo/model/identity_map'
26
24
 
27
25
  inherit \
28
26
  Db,
29
27
  Conversion,
30
28
  Assignment,
31
29
  Callbacks,
32
- Validation,
33
30
  Crud,
31
+ Validation,
34
32
  QueryMixin,
35
33
  Scope,
36
34
  AttributeConvertors,
@@ -45,15 +43,19 @@ Mongo.defaults.merge! \
45
43
  safe: true,
46
44
  generate_id: true
47
45
 
48
- # Integration with Rails.
46
+ # Integrations.
47
+
49
48
  unless $dont_use_rails
50
49
  require 'mongo/model/integration/rails' if defined? Rails
51
50
  end
52
51
 
53
- # Integration with Validatable2
54
52
  unless $dont_use_validatable
55
53
  require 'validatable'
56
54
  require 'mongo/model/integration/validatable'
57
55
  require 'mongo/model/integration/validatable/uniqueness_validator'
58
56
  Mongo::Model.inherit Validatable::Model
57
+ end
58
+
59
+ unless $dont_use_file_model
60
+ Mongo::Model.autoload :FileModel, 'mongo/model/integration/file_model'
59
61
  end
@@ -8,7 +8,6 @@ module Mongo::Model::Misc
8
8
  self.updated_at = now
9
9
  end
10
10
 
11
-
12
11
  def _cache
13
12
  @_cache ||= {}
14
13
  end
@@ -32,7 +31,11 @@ module Mongo::Model::Misc
32
31
  end
33
32
 
34
33
  def original
35
- @_original ||= _id? ? self.class.by_id(self._id) : nil
34
+ unless _cache[:original_cached]
35
+ _cache[:original_cached] = true
36
+ _cache[:original] = _id && self.class.by_id(_id)
37
+ end
38
+ _cache[:original]
36
39
  end
37
40
 
38
41
  module ClassMethods
@@ -41,5 +44,9 @@ module Mongo::Model::Misc
41
44
  before_create :update_created_at
42
45
  before_save :update_updated_at
43
46
  end
47
+
48
+ def create_index *args
49
+ collection.create_index *args
50
+ end
44
51
  end
45
52
  end
@@ -70,5 +70,17 @@ module Mongo::Model
70
70
  list.collect!{|n| :"@#{n}"}
71
71
  if list.empty? then _embedded else _embedded.push(*list) end
72
72
  end
73
+
74
+ def from_mongo doc
75
+ model = ::Mongo::Object.from_mongo doc
76
+ model.run_after_callbacks :build, :build
77
+ model
78
+ end
79
+ end
80
+
81
+ class << self
82
+ def originals
83
+ @originals ||= {}
84
+ end
73
85
  end
74
86
  end
@@ -87,9 +87,21 @@ module Mongo::Model::Scope
87
87
  list = list.collect{|item| item.is_a?(Array) ? item : [item, 1]}
88
88
  query({}, sort: list)
89
89
  end
90
+ alias_method :sort_by, :sort
90
91
  def snapshot; query({}, snapshot: true) end
91
92
 
92
- def paginate page, per_page
93
+ PER_PAGE, MAX_PER_PAGE = 25, 100
94
+ def paginate *args
95
+ args.size.must.be_in 1..2
96
+ if args.size == 2
97
+ page, per_page = *args
98
+ else
99
+ options = args.first
100
+ page, per_page = options[:page], options[:per_page]
101
+ end
102
+ page ||= 1
103
+ per_page ||= PER_PAGE
104
+ per_page = MAX_PER_PAGE if per_page > MAX_PER_PAGE
93
105
  skip((page - 1) * per_page).limit(per_page)
94
106
  end
95
107
 
@@ -5,6 +5,12 @@ rspec do
5
5
  class << self
6
6
  def with_mongo_model
7
7
  with_mongo
8
+ before{Mongo::Model::IdentityMap.clear}
9
+ end
10
+
11
+ def with_models *args
12
+ ::Models
13
+ with_mongo_model *args
8
14
  end
9
15
  end
10
16
  end
@@ -0,0 +1,68 @@
1
+ class Object
2
+ def rson?
3
+ false
4
+ end
5
+ end
6
+
7
+ [
8
+ Time,
9
+ FalseClass,
10
+ TrueClass,
11
+ Numeric,
12
+ Symbol,
13
+ String,
14
+ NilClass,
15
+ ].each do |klass|
16
+ klass.class_eval do
17
+ def to_rson options = {}
18
+ self
19
+ end
20
+
21
+ def rson?
22
+ true
23
+ end
24
+ end
25
+ end
26
+
27
+ # [
28
+ # String
29
+ # ].each do |klass|
30
+ # klass.class_eval do
31
+ # def to_rson options = {}
32
+ # self.to_sym
33
+ # end
34
+ #
35
+ # def rson?
36
+ # false
37
+ # end
38
+ # end
39
+ # end
40
+
41
+ Array.class_eval do
42
+ def to_rson options = {}
43
+ collect{|v| v.to_rson(options)}
44
+ end
45
+
46
+ def rson?
47
+ all?{|v| v.rson?}
48
+ end
49
+ end
50
+
51
+ [Hash, OpenObject].each do |klass|
52
+ klass.class_eval do
53
+ def to_rson options = {}
54
+ r = self.class.new
55
+ each do |k, v|
56
+ r[k.to_sym] = v.to_rson(options)
57
+ end
58
+ r
59
+ end
60
+
61
+ def rson?
62
+ each do |k, v|
63
+ return false unless k.rson? and v.rson?
64
+ end
65
+ true
66
+ end
67
+ end
68
+ end
@@ -5,7 +5,38 @@ module Mongo::Model::Validation
5
5
  end
6
6
  def invalid?(options = {}); !valid?(options) end
7
7
 
8
+ # Catching erros during CRUD and adding it to errors, like unique index.
9
+
10
+ def create_object *args
11
+ with_exceptions_as_errors do
12
+ super
13
+ end
14
+ end
15
+
16
+ def update_object *args
17
+ with_exceptions_as_errors do
18
+ super
19
+ end
20
+ end
21
+
22
+ def delete_object *args
23
+ with_exceptions_as_errors do
24
+ super
25
+ end
26
+ end
27
+
8
28
  protected
29
+ def with_exceptions_as_errors &block
30
+ block.call
31
+ rescue Mongo::OperationFailure => e
32
+ if [11000, 11001].include? e.error_code
33
+ errors.add :base, "not unique value!"
34
+ false
35
+ else
36
+ raise e
37
+ end
38
+ end
39
+
9
40
  def run_validations options = {}
10
41
  with_model_callbacks [:validate], options, [self] do
11
42
  # Validating main model.
data/lib/mongo/model.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'mongodb_model/gems'
2
2
 
3
+ require 'mongo/object'
4
+
3
5
  module Mongo
4
6
  autoload :Model, 'mongo/model/load'
5
7
  end
@@ -1,8 +1,5 @@
1
- # gem 'i18n', '~> 0.5'
2
-
3
1
  if respond_to? :fake_gem
4
2
  fake_gem 'mongodb'
5
- # fake_gem 'file_model'
6
3
  fake_gem 'validatable2'
7
4
  fake_gem 'ruby_ext'
8
5
  end
@@ -38,9 +38,12 @@ describe 'Attribute assignment' do
38
38
 
39
39
  u = User.new
40
40
  u.set name: 'Alex', has_mail: '1', age: '31', position: [11, 34]
41
- -> {u.set name: 'Alex', banned: false}.should raise_error(/not allowed/)
42
41
  [u.name, u.has_mail, u.age, u.position, u.banned].should == ['Alex', true, 31, [11, 34], nil]
43
42
 
43
+ # Should skip not allowed attributes.
44
+ u.set banned: false
45
+ u.banned.should be_nil
46
+
44
47
  # should allow to forcefully cast and update any attribute
45
48
  u.set! banned: '0'
46
49
  u.banned.should == false
@@ -67,6 +67,7 @@ describe "Attribute convertors" do
67
67
 
68
68
  # Protection.
69
69
  o.protected_tags = []
70
- -> {o.set protected_tags_as_string: 'Java, Ruby'}.should raise_error(/not allowed/)
70
+ o.set protected_tags_as_string: 'Java, Ruby'
71
+ o.protected_tags = []
71
72
  end
72
73
  end
@@ -155,7 +155,7 @@ describe 'Callbacks' do
155
155
  instance.should_receive(:after_build)
156
156
  end
157
157
  EmbeddedObject.after_instantiate do |instance|
158
- instance.should_receive(:after_build)
158
+ instance.should_not_receive(:after_build)
159
159
  end
160
160
  db.objects.first
161
161
  end
@@ -40,7 +40,7 @@ describe 'Conversion' do
40
40
 
41
41
  it "should work without arguments" do
42
42
  post = build_post_with_comment
43
- post.to_hash.should == {
43
+ post.to_rson.should == {
44
44
  text: 'StarCraft releasing soon!',
45
45
  token: 'secret',
46
46
  comments: [
@@ -51,14 +51,14 @@ describe 'Conversion' do
51
51
 
52
52
  it "should accept :only, :except and :methods options" do
53
53
  post = build_post_with_comment
54
- post.to_hash(only: :text).should == {text: 'StarCraft releasing soon!'}
55
- post.to_hash(except: :token).should == {
54
+ post.to_rson(only: :text).should == {text: 'StarCraft releasing soon!'}
55
+ post.to_rson(except: :token).should == {
56
56
  text: 'StarCraft releasing soon!',
57
57
  comments: [
58
58
  {text: 'Cool!'}
59
59
  ]
60
60
  }
61
- post.to_hash(only: [], methods: :teaser).should == {teaser: 'StarCraft r'}
61
+ post.to_rson(only: [], methods: :teaser).should == {teaser: 'StarCraft r'}
62
62
  end
63
63
 
64
64
  it "should use conversion profiles" do
@@ -68,13 +68,21 @@ describe 'Conversion' do
68
68
 
69
69
  post = build_post_with_comment
70
70
 
71
- -> {post.to_hash(profile: :public)}.should raise_error(/profile :public not defined for Comment/)
71
+ -> {post.to_rson(profile: :public)}.should raise_error(/profile :public not defined for Comment/)
72
72
 
73
73
  Comment.class_eval do
74
74
  profile :public
75
75
  end
76
76
 
77
- post.to_hash(profile: :public).should == {
77
+ post.to_rson(profile: :public).should == {
78
+ text: 'StarCraft releasing soon!',
79
+ teaser: 'StarCraft r',
80
+ comments: [
81
+ {text: 'Cool!'}
82
+ ]
83
+ }
84
+
85
+ post.to_rson(:public).should == {
78
86
  text: 'StarCraft releasing soon!',
79
87
  teaser: 'StarCraft r',
80
88
  comments: [
@@ -91,29 +99,29 @@ describe 'Conversion' do
91
99
  post = Post.new text: 'StarCraft releasing soon!'
92
100
  post.valid?.should be_false
93
101
 
94
- post.to_hash.should == {
102
+ post.to_rson.should == {
95
103
  text: 'StarCraft releasing soon!',
96
104
  errors: {token: ["can't be empty"]}
97
105
  }
98
106
 
99
- post.to_hash(errors: false).should == {
107
+ post.to_rson(errors: false).should == {
100
108
  text: 'StarCraft releasing soon!'
101
109
  }
102
110
  end
103
111
 
104
112
  it "should convert to to_json" do
105
113
  post = build_post_with_comment
106
- rson = mock
107
- rson.should_receive(:to_json).and_return(:ok)
108
- post.should_receive(:to_hash).with(only: :text).and_return(rson)
114
+ hash = mock
115
+ hash.should_receive(:to_json).and_return(:ok)
116
+ post.should_receive(:to_rson).with(only: :text).and_return(hash)
109
117
  post.to_json(only: :text).should == :ok
110
118
  end
111
119
 
112
120
  it "should convert to to_xml" do
113
121
  post = build_post_with_comment
114
- rson = mock
115
- rson.should_receive(:to_xml).and_return(:ok)
116
- post.should_receive(:to_hash).with(only: :text).and_return(rson)
122
+ hash = mock
123
+ hash.should_receive(:to_xml).and_return(:ok)
124
+ post.should_receive(:to_rson).with(only: :text).and_return(hash)
117
125
  post.to_xml(only: :text).should == :ok
118
126
  end
119
127
  end
data/spec/crud_spec.rb CHANGED
@@ -20,6 +20,25 @@ describe "Model CRUD" do
20
20
  end
21
21
  after{remove_constants :Unit}
22
22
 
23
+ it 'save should return true or false' do
24
+ # Successfull create and update.
25
+ unit = Unit.new
26
+ unit.save.class.should == TrueClass
27
+ unit.name = 'Another'
28
+ unit.save.class.should == TrueClass
29
+
30
+ # Invalid create.
31
+ unit = Unit.new
32
+ unit.stub!(:valid?).and_return false
33
+ unit.save.class.should == FalseClass
34
+
35
+ # Invalid update.
36
+ unit = Unit.new
37
+ unit.save.should be_true
38
+ unit.stub!(:valid?).and_return false
39
+ unit.save.class.should == FalseClass
40
+ end
41
+
23
42
  it 'should perform CRUD' do
24
43
  # Read.
25
44
  Unit.count.should == 0
@@ -77,10 +96,7 @@ describe "Model CRUD" do
77
96
 
78
97
  it 'should create model' do
79
98
  u = Unit.create(name: 'Zeratul')
80
- u.new_record?.should be_false
81
-
82
- u = Unit.create!(name: 'Zeratul')
83
- u.new_record?.should be_false
99
+ u.new?.should be_false
84
100
  end
85
101
 
86
102
  it 'should delete all models' do
data/spec/misc_spec.rb CHANGED
@@ -11,10 +11,10 @@ describe 'Miscellaneous' do
11
11
  attr_accessor :name
12
12
  end
13
13
  end
14
- after{remove_constants :Unit3, :User}
14
+ after{remove_constants :Unit, :User}
15
15
 
16
16
  it "should create timestamps" do
17
- class Unit3
17
+ class Unit
18
18
  inherit Mongo::Model
19
19
  collection :units
20
20
 
@@ -23,10 +23,10 @@ describe 'Miscellaneous' do
23
23
  timestamps!
24
24
  end
25
25
 
26
- unit = Unit3.build name: 'Zeratul'
26
+ unit = Unit.build name: 'Zeratul'
27
27
  unit.save!
28
28
 
29
- unit = Unit3.first
29
+ unit = Unit.first
30
30
  unit.created_at.should_not be_nil
31
31
  unit.updated_at.should_not be_nil
32
32
  created_at,updated_at = unit.created_at, unit.updated_at
@@ -37,10 +37,10 @@ describe 'Miscellaneous' do
37
37
  end
38
38
 
39
39
  it 'should have cache' do
40
- class Unit3
40
+ class Unit
41
41
  inherit Mongo::Model
42
42
  end
43
- u = Unit3.new
43
+ u = Unit.new
44
44
  u._cache.should == {}
45
45
  end
46
46
 
@@ -64,4 +64,45 @@ describe 'Miscellaneous' do
64
64
  u.reload
65
65
  u.name.should == 'Zeratul'
66
66
  end
67
+
68
+ describe 'original' do
69
+ before do
70
+ class Unit
71
+ inherit Mongo::Model
72
+ collection :units
73
+
74
+ attr_accessor :name
75
+ end
76
+
77
+ @unit = Unit.new.tap{|u| u.name = "Zeratul"}
78
+ end
79
+ after{remove_constants :Unit}
80
+
81
+ it "should query original from database" do
82
+ @unit.original.should be_nil
83
+ @unit.save!
84
+
85
+ unit = Unit.first
86
+ unit.name = "Tassadar"
87
+
88
+ Unit.should_receive(:first).with(_id: unit._id).and_return{db.units.first(_id: unit._id)}
89
+ unit.original.name.should == "Zeratul"
90
+ end
91
+
92
+ it "should use identity map if provided" do
93
+ Unit.inherit Mongo::Model::IdentityMap
94
+
95
+ @unit.original.should be_nil
96
+ @unit.save!
97
+
98
+ Unit.identity_map.size.should == 0
99
+ unit = Unit.first
100
+ Unit.identity_map.size.should == 1
101
+
102
+ unit.name = "Tassadar"
103
+
104
+ Unit.should_not_receive :first
105
+ unit.original.name.should == "Zeratul"
106
+ end
107
+ end
67
108
  end
data/spec/query_spec.rb CHANGED
@@ -74,7 +74,9 @@ describe "Query" do
74
74
  end
75
75
  end
76
76
 
77
- -> {SpecialUnit.query(name: 'Zeratul').build age: 500, status: 'active'}.should raise_error(/not allowed/)
77
+ u = SpecialUnit.query(name: 'Zeratul').build status: 'active'
78
+ u.status.should be_nil
79
+
78
80
  u = SpecialUnit.query(name: 'Zeratul', status: 'active').build age: 500
79
81
  u.status.should == 'active'
80
82
  end
@@ -142,4 +142,26 @@ describe "Validation" do
142
142
  unit.should be_valid
143
143
  end
144
144
  end
145
+
146
+ context "database exceptions" do
147
+ before do
148
+ class Unit
149
+ inherit Mongo::Model
150
+ collection :units
151
+
152
+ attr_accessor :name
153
+ end
154
+ end
155
+ after{remove_constants :Unit}
156
+
157
+ it "should convert unique index exception to errors" do
158
+ db.units.create_index [["name", 1]], unique: true
159
+
160
+ Unit.create name: 'Zeratul'
161
+
162
+ unit = Unit.new name: 'Zeratul'
163
+ unit.save.should be_false
164
+ unit.errors[:base].should == ["not unique value!"]
165
+ end
166
+ end
145
167
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongodb_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-19 00:00:00.000000000Z
12
+ date: 2011-12-19 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongodb
16
- requirement: &2848930 !ruby/object:Gem::Requirement
16
+ requirement: &70169713604240 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2848930
24
+ version_requirements: *70169713604240
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: validatable2
27
- requirement: &2848690 !ruby/object:Gem::Requirement
27
+ requirement: &70169713603760 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2848690
35
+ version_requirements: *70169713603760
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: ruby_ext
38
- requirement: &2848450 !ruby/object:Gem::Requirement
38
+ requirement: &70169713603260 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *2848450
46
+ version_requirements: *70169713603260
47
47
  description:
48
48
  email:
49
49
  executables: []
@@ -58,7 +58,10 @@ files:
58
58
  - lib/mongo/model/conversion.rb
59
59
  - lib/mongo/model/crud.rb
60
60
  - lib/mongo/model/db.rb
61
+ - lib/mongo/model/identity_map.rb
61
62
  - lib/mongo/model/integration/file_model.rb
63
+ - lib/mongo/model/integration/rad/tasks.rb
64
+ - lib/mongo/model/integration/rad.rb
62
65
  - lib/mongo/model/integration/rails.rb
63
66
  - lib/mongo/model/integration/validatable/uniqueness_validator.rb
64
67
  - lib/mongo/model/integration/validatable.rb
@@ -70,7 +73,7 @@ files:
70
73
  - lib/mongo/model/scope.rb
71
74
  - lib/mongo/model/spec.rb
72
75
  - lib/mongo/model/support/conversions.rb
73
- - lib/mongo/model/support.rb
76
+ - lib/mongo/model/support/rson.rb
74
77
  - lib/mongo/model/validation.rb
75
78
  - lib/mongo/model.rb
76
79
  - lib/mongodb_model/gems.rb
@@ -109,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
112
  version: '0'
110
113
  requirements: []
111
114
  rubyforge_project:
112
- rubygems_version: 1.8.6
115
+ rubygems_version: 1.8.10
113
116
  signing_key:
114
117
  specification_version: 3
115
118
  summary: Object Model for MongoDB
@@ -1,10 +0,0 @@
1
- Array.class_eval do
2
- alias_method :each_value, :each
3
- alias_method :collect_with_value, :collect
4
- end
5
-
6
- Hash.class_eval do
7
- def collect_with_value &block
8
- {}.tap{|h| self.each{|k, v| h[k] = block.call v}}
9
- end
10
- end