mongo_mapper-unstable 2010.2.27 → 2010.2.28

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/VERSION CHANGED
@@ -1 +1 @@
1
- 2010.02.27
1
+ 2010.02.28
@@ -23,7 +23,7 @@ module MongoMapper
23
23
  plugin Plugins::Serialization
24
24
  plugin Plugins::Validations
25
25
  plugin Plugins::Callbacks # for now callbacks needs to be after validations
26
-
26
+
27
27
  extend Plugins::Validations::DocumentMacros
28
28
  end
29
29
 
@@ -200,7 +200,7 @@ module MongoMapper
200
200
  def set_database_name(name)
201
201
  @database_name = name
202
202
  end
203
-
203
+
204
204
  def database_name
205
205
  @database_name
206
206
  end
@@ -374,9 +374,14 @@ module MongoMapper
374
374
  end
375
375
 
376
376
  def delete
377
+ @_destroyed = true
377
378
  self.class.delete(id) unless new?
378
379
  end
379
380
 
381
+ def destroyed?
382
+ @_destroyed == true
383
+ end
384
+
380
385
  def reload
381
386
  if attrs = collection.find_one({:_id => _id})
382
387
  self.class.associations.each { |name, assoc| send(name).reset if respond_to?(name) }
@@ -387,6 +392,12 @@ module MongoMapper
387
392
  end
388
393
  end
389
394
 
395
+ # Used by embedded docs to find root easily without if/respond_to? stuff.
396
+ # Documents are always root documents.
397
+ def _root_document
398
+ self
399
+ end
400
+
390
401
  private
391
402
  def create_or_update(options={})
392
403
  result = new? ? create(options) : update(options)
@@ -20,7 +20,7 @@ module MongoMapper
20
20
  plugin Plugins::Serialization
21
21
  plugin Plugins::Validations
22
22
 
23
- attr_accessor :_root_document, :_parent_document
23
+ attr_reader :_root_document, :_parent_document
24
24
  end
25
25
 
26
26
  super
@@ -50,6 +50,11 @@ module MongoMapper
50
50
  end
51
51
  result
52
52
  end
53
+
54
+ def _parent_document=(value)
55
+ @_root_document = value._root_document
56
+ @_parent_document = value
57
+ end
53
58
  end # InstanceMethods
54
59
  end # EmbeddedDocument
55
60
  end # MongoMapper
@@ -30,19 +30,8 @@ module MongoMapper
30
30
  alias_method :concat, :<<
31
31
 
32
32
  private
33
- def _root_document
34
- if owner.respond_to?(:_root_document)
35
- owner._root_document
36
- else
37
- owner
38
- end
39
- end
40
-
41
33
  def assign_references(*docs)
42
- docs.each do |doc|
43
- doc._root_document = _root_document
44
- doc._parent_document = owner
45
- end
34
+ docs.each { |doc| doc._parent_document = owner }
46
35
  end
47
36
  end
48
37
  end
@@ -112,7 +112,7 @@ module MongoMapper
112
112
  end
113
113
 
114
114
  def foreign_key
115
- options[:foreign_key] || owner.class.name.underscore.gsub("/", "_") + "_id"
115
+ options[:foreign_key] || owner.class.name.to_s.underscore.gsub("/", "_") + "_id"
116
116
  end
117
117
  end
118
118
  end
@@ -1,22 +1,50 @@
1
+ # Almost all of this callback stuff is pulled directly from ActiveSupport
2
+ # in the interest of support rails 2 and 3 at the same time and is the
3
+ # same copyright as rails.
1
4
  module MongoMapper
2
5
  module Plugins
3
6
  module Callbacks
4
7
  def self.configure(model)
5
8
  model.class_eval do
6
- include ActiveSupport::Callbacks
7
-
8
9
  define_callbacks(
9
- :before_save, :after_save,
10
- :before_create, :after_create,
11
- :before_update, :after_update,
12
- :before_validation, :after_validation,
13
- :before_validation_on_create, :after_validation_on_create,
14
- :before_validation_on_update, :after_validation_on_update,
15
- :before_destroy, :after_destroy
10
+ :before_save, :after_save,
11
+ :before_create, :after_create,
12
+ :before_update, :after_update,
13
+ :before_validation, :after_validation,
14
+ :before_validation_on_create, :after_validation_on_create,
15
+ :before_validation_on_update, :after_validation_on_update,
16
+ :before_destroy, :after_destroy
16
17
  )
17
18
  end
18
19
  end
19
-
20
+
21
+ module ClassMethods
22
+ def define_callbacks(*callbacks)
23
+ callbacks.each do |callback|
24
+ class_eval <<-"end_eval"
25
+ def self.#{callback}(*methods, &block)
26
+ callbacks = CallbackChain.build(:#{callback}, *methods, &block)
27
+ @#{callback}_callbacks ||= CallbackChain.new
28
+ @#{callback}_callbacks.concat callbacks
29
+ end
30
+
31
+ def self.#{callback}_callback_chain
32
+ @#{callback}_callbacks ||= CallbackChain.new
33
+
34
+ if superclass.respond_to?(:#{callback}_callback_chain)
35
+ CallbackChain.new(
36
+ superclass.#{callback}_callback_chain +
37
+ @#{callback}_callbacks
38
+ )
39
+ else
40
+ @#{callback}_callbacks
41
+ end
42
+ end
43
+ end_eval
44
+ end
45
+ end
46
+ end
47
+
20
48
  module InstanceMethods
21
49
  def valid?
22
50
  action = new? ? 'create' : 'update'
@@ -37,6 +65,10 @@ module MongoMapper
37
65
  result
38
66
  end
39
67
 
68
+ def run_callbacks(kind, options = {}, &block)
69
+ self.class.send("#{kind}_callback_chain").run(self, options, &block)
70
+ end
71
+
40
72
  private
41
73
  def create_or_update(*args)
42
74
  run_callbacks(:before_save)
@@ -60,6 +92,127 @@ module MongoMapper
60
92
  result
61
93
  end
62
94
  end
95
+
96
+ class CallbackChain < Array
97
+ def self.build(kind, *methods, &block)
98
+ methods, options = extract_options(*methods, &block)
99
+ methods.map! { |method| Callback.new(kind, method, options) }
100
+ new(methods)
101
+ end
102
+
103
+ def run(object, options = {}, &terminator)
104
+ enumerator = options[:enumerator] || :each
105
+
106
+ unless block_given?
107
+ send(enumerator) { |callback| callback.call(object) }
108
+ else
109
+ send(enumerator) do |callback|
110
+ result = callback.call(object)
111
+ break result if terminator.call(result, object)
112
+ end
113
+ end
114
+ end
115
+
116
+ # TODO: Decompose into more Array like behavior
117
+ def replace_or_append!(chain)
118
+ if index = index(chain)
119
+ self[index] = chain
120
+ else
121
+ self << chain
122
+ end
123
+ self
124
+ end
125
+
126
+ def find(callback, &block)
127
+ select { |c| c == callback && (!block_given? || yield(c)) }.first
128
+ end
129
+
130
+ def delete(callback)
131
+ super(callback.is_a?(Callback) ? callback : find(callback))
132
+ end
133
+
134
+ private
135
+ def self.extract_options(*methods, &block)
136
+ methods.flatten!
137
+ options = methods.extract_options!
138
+ methods << block if block_given?
139
+ return methods, options
140
+ end
141
+
142
+ def extract_options(*methods, &block)
143
+ self.class.extract_options(*methods, &block)
144
+ end
145
+ end
146
+
147
+ class Callback
148
+ attr_reader :kind, :method, :identifier, :options
149
+
150
+ def initialize(kind, method, options = {})
151
+ @kind = kind
152
+ @method = method
153
+ @identifier = options[:identifier]
154
+ @options = options
155
+ end
156
+
157
+ def ==(other)
158
+ case other
159
+ when Callback
160
+ (self.identifier && self.identifier == other.identifier) || self.method == other.method
161
+ else
162
+ (self.identifier && self.identifier == other) || self.method == other
163
+ end
164
+ end
165
+
166
+ def eql?(other)
167
+ self == other
168
+ end
169
+
170
+ def dup
171
+ self.class.new(@kind, @method, @options.dup)
172
+ end
173
+
174
+ def hash
175
+ if @identifier
176
+ @identifier.hash
177
+ else
178
+ @method.hash
179
+ end
180
+ end
181
+
182
+ def call(*args, &block)
183
+ evaluate_method(method, *args, &block) if should_run_callback?(*args)
184
+ rescue LocalJumpError
185
+ raise ArgumentError,
186
+ "Cannot yield from a Proc type filter. The Proc must take two " +
187
+ "arguments and execute #call on the second argument."
188
+ end
189
+
190
+ private
191
+ def evaluate_method(method, *args, &block)
192
+ case method
193
+ when Symbol
194
+ object = args.shift
195
+ object.send(method, *args, &block)
196
+ when String
197
+ eval(method, args.first.instance_eval { binding })
198
+ when Proc, Method
199
+ method.call(*args, &block)
200
+ else
201
+ if method.respond_to?(kind)
202
+ method.send(kind, *args, &block)
203
+ else
204
+ raise ArgumentError,
205
+ "Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
206
+ "a block to be invoked, or an object responding to the callback method."
207
+ end
208
+ end
209
+ end
210
+
211
+ def should_run_callback?(*args)
212
+ [options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
213
+ ![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
214
+ end
215
+ end
63
216
  end
64
217
  end
65
218
  end
@@ -175,6 +175,9 @@ module MongoMapper
175
175
  writer_method = "#{name}="
176
176
 
177
177
  if respond_to?(writer_method)
178
+ if writer_method == '_root_document='
179
+ puts "_root_document= #{value.inspect}"
180
+ end
178
181
  self.send(writer_method, value)
179
182
  else
180
183
  self[name.to_s] = value
@@ -309,7 +312,11 @@ module MongoMapper
309
312
 
310
313
  def get(value)
311
314
  if value.nil? && !default_value.nil?
312
- return default_value
315
+ if default_value.respond_to?(:call)
316
+ return default_value.call
317
+ else
318
+ return default_value
319
+ end
313
320
  end
314
321
 
315
322
  type.from_mongo(value)
@@ -2,8 +2,8 @@ module MongoMapper
2
2
  module Plugins
3
3
  module Pagination
4
4
  class Proxy
5
- instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
6
-
5
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|respond_to\?|proxy_|^object_id$)/ }
6
+
7
7
  attr_accessor :subject
8
8
  attr_reader :total_entries, :per_page, :current_page
9
9
  alias limit per_page
@@ -50,7 +50,11 @@ module MongoMapper
50
50
  def method_missing(name, *args, &block)
51
51
  @subject.send(name, *args, &block)
52
52
  end
53
-
53
+
54
+ def respond_to?(name, *args, &block)
55
+ super || @subject.respond_to?(name, *args, &block)
56
+ end
57
+
54
58
  private
55
59
  def per_page=(value)
56
60
  value = 25 if value.blank?
@@ -93,7 +93,7 @@ module MongoMapper
93
93
  def to_order(field, direction=nil)
94
94
  direction ||= 'ASC'
95
95
  direction = direction.upcase == 'ASC' ? 1 : -1
96
- [field.to_s, direction]
96
+ [normalized_key(field).to_s, direction]
97
97
  end
98
98
 
99
99
  def normalized_key(field)
data/lib/mongo_mapper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require 'uri'
2
3
 
3
4
  # if Gem is defined i'll assume you are using rubygems and lock specific versions
4
5
  # call me crazy but a plain old require will just get the latest version you have installed
@@ -10,7 +11,7 @@ if self.class.const_defined?(:Gem)
10
11
  gem 'jnunemaker-validatable', '1.8.1'
11
12
  end
12
13
 
13
- require 'active_support'
14
+ require 'active_support/all'
14
15
  require 'mongo'
15
16
  require 'validatable'
16
17
 
@@ -24,6 +25,9 @@ module MongoMapper
24
25
  # raised when document expected but not found
25
26
  class DocumentNotFound < MongoMapperError; end
26
27
 
28
+ # raised when trying to connect using uri with incorrect scheme
29
+ class InvalidScheme < MongoMapperError; end
30
+
27
31
  # raised when document not valid and using !
28
32
  class DocumentNotValid < MongoMapperError
29
33
  def initialize(document)
@@ -70,13 +74,28 @@ module MongoMapper
70
74
  @@config
71
75
  end
72
76
 
77
+ # @api private
78
+ def self.config_for_environment(environment)
79
+ env = config[environment]
80
+ return env if env['uri'].blank?
81
+
82
+ uri = URI.parse(env['uri'])
83
+ raise InvalidScheme.new('must be mongodb') unless uri.scheme == 'mongodb'
84
+ {
85
+ 'host' => uri.host,
86
+ 'port' => uri.port,
87
+ 'database' => uri.path.gsub(/^\//, ''),
88
+ 'username' => uri.user,
89
+ 'password' => uri.password,
90
+ }
91
+ end
92
+
73
93
  def self.connect(environment, options={})
74
94
  raise 'Set config before connecting. MongoMapper.config = {...}' if config.blank?
75
- MongoMapper.connection = Mongo::Connection.new(config[environment]['host'], config[environment]['port'], options)
76
- MongoMapper.database = config[environment]['database']
77
- if config[environment]['username'].present? && config[environment]['password'].present?
78
- MongoMapper.database.authenticate(config[environment]['username'], config[environment]['password'])
79
- end
95
+ env = config_for_environment(environment)
96
+ MongoMapper.connection = Mongo::Connection.new(env['host'], env['port'], options)
97
+ MongoMapper.database = env['database']
98
+ MongoMapper.database.authenticate(env['username'], env['password']) if env['username'] && env['password']
80
99
  end
81
100
 
82
101
  def self.setup(config, environment, options={})
@@ -108,11 +127,11 @@ module MongoMapper
108
127
  def self.normalize_object_id(value)
109
128
  value.is_a?(String) ? Mongo::ObjectID.from_string(value) : value
110
129
  end
111
-
130
+
112
131
  autoload :Query, 'mongo_mapper/query'
113
132
  autoload :Document, 'mongo_mapper/document'
114
133
  autoload :EmbeddedDocument, 'mongo_mapper/embedded_document'
115
134
  end
116
135
 
117
136
  require 'mongo_mapper/support'
118
- require 'mongo_mapper/plugins'
137
+ require 'mongo_mapper/plugins'
@@ -12,7 +12,7 @@ class DocumentTest < Test::Unit::TestCase
12
12
  key :date, Date
13
13
  end
14
14
  end
15
-
15
+
16
16
  context "array key" do
17
17
  setup do
18
18
  @document.key :tags, Array
@@ -115,6 +115,23 @@ class DocumentTest < Test::Unit::TestCase
115
115
  end
116
116
  end
117
117
 
118
+ context "key with proc default value" do
119
+ setup do
120
+ @document.key :proc_default, String, :default => lambda { return 'string' }
121
+ end
122
+
123
+ should "detect and run proc default" do
124
+ doc = @document.new
125
+ doc.proc_default.should == 'string'
126
+ end
127
+
128
+ should "save and load from mongo" do
129
+ doc = @document.create
130
+ doc = doc.reload
131
+ doc.proc_default.should == 'string'
132
+ end
133
+ end
134
+
118
135
  context "ClassMethods#create (single document)" do
119
136
  setup do
120
137
  @doc_instance = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
@@ -139,7 +156,7 @@ class DocumentTest < Test::Unit::TestCase
139
156
  @doc_instance.last_name.should == 'Nunemaker'
140
157
  @doc_instance.age.should == 27
141
158
  end
142
-
159
+
143
160
  should "not fail if no attributes provided" do
144
161
  document = Doc()
145
162
  lambda { document.create }.should change { document.count }.by(1)
@@ -183,12 +200,12 @@ class DocumentTest < Test::Unit::TestCase
183
200
  should "not create new document" do
184
201
  @document.count.should == 1
185
202
  end
186
-
203
+
187
204
  should "raise error if not provided id" do
188
205
  doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
189
206
  lambda { @document.update }.should raise_error(ArgumentError)
190
207
  end
191
-
208
+
192
209
  should "raise error if not provided attributes" do
193
210
  doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
194
211
  lambda { @document.update(doc._id) }.should raise_error(ArgumentError)
@@ -221,7 +238,7 @@ class DocumentTest < Test::Unit::TestCase
221
238
  @document.find(@doc1._id).age.should == 30
222
239
  @document.find(@doc2._id).age.should == 30
223
240
  end
224
-
241
+
225
242
  should "raise error if not a hash" do
226
243
  lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
227
244
  end
@@ -243,7 +260,7 @@ class DocumentTest < Test::Unit::TestCase
243
260
  @document.find!
244
261
  end
245
262
  end
246
-
263
+
247
264
  should "raise error if trying to find with :all, :first, or :last" do
248
265
  [:all, :first, :last].each do |m|
249
266
  assert_raises(ArgumentError) { @document.find(m) }
@@ -525,7 +542,7 @@ class DocumentTest < Test::Unit::TestCase
525
542
  @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
526
543
  @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
527
544
  @document.destroy(@doc1._id, @doc2._id)
528
-
545
+
529
546
  @document.count.should == 1
530
547
  end
531
548
 
@@ -596,11 +613,11 @@ class DocumentTest < Test::Unit::TestCase
596
613
  @document.count(:age => [26, 27]).should == 2
597
614
  end
598
615
  end
599
-
616
+
600
617
  should "have instance method for collection" do
601
618
  @document.new.collection.name.should == @document.collection.name
602
619
  end
603
-
620
+
604
621
  should "have instance method for database" do
605
622
  @document.new.database.should == @document.database
606
623
  end
@@ -672,7 +689,7 @@ class DocumentTest < Test::Unit::TestCase
672
689
  @document.new.update_attributes({}).should be_false
673
690
  end
674
691
  end
675
-
692
+
676
693
  context "#save (new document)" do
677
694
  setup do
678
695
  @doc = @document.new(:first_name => 'John', :age => '27')
@@ -710,12 +727,12 @@ class DocumentTest < Test::Unit::TestCase
710
727
  should "allow to use custom methods to assign properties" do
711
728
  klass = Doc do
712
729
  key :name, String
713
-
730
+
714
731
  def realname=(value)
715
732
  self.name = value
716
733
  end
717
734
  end
718
-
735
+
719
736
  person = klass.new(:realname => 'David')
720
737
  person.save
721
738
  person.reload.name.should == 'David'
@@ -723,7 +740,7 @@ class DocumentTest < Test::Unit::TestCase
723
740
 
724
741
  context "with key of type date" do
725
742
  should "save the date value as a Time object" do
726
- doc = @document.new(:first_name => 'John', :age => '27', :date => "12/01/2009")
743
+ doc = @document.new(:first_name => 'John', :age => '27', :date => "2009-12-01")
727
744
  doc.save
728
745
  doc.date.should == Date.new(2009, 12, 1)
729
746
  end
@@ -775,7 +792,7 @@ class DocumentTest < Test::Unit::TestCase
775
792
  @document.count.should == 1
776
793
  end
777
794
  end
778
-
795
+
779
796
  context "#save (with options)" do
780
797
  setup do
781
798
  @document = Doc do
@@ -799,7 +816,7 @@ class DocumentTest < Test::Unit::TestCase
799
816
  end
800
817
  end
801
818
  end
802
-
819
+
803
820
  context "#save! (with options)" do
804
821
  setup do
805
822
  @document = Doc do
@@ -840,37 +857,57 @@ class DocumentTest < Test::Unit::TestCase
840
857
  @document.count.should == 0
841
858
  end
842
859
  end
843
-
860
+
844
861
  context "#delete" do
845
862
  setup do
846
- @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
847
- @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
848
-
863
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
864
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
865
+
849
866
  @document.class_eval do
850
867
  before_destroy :before_destroy_callback
851
868
  after_destroy :after_destroy_callback
852
-
869
+
853
870
  def history; @history ||= [] end
854
871
  def before_destroy_callback; history << :after_destroy end
855
872
  def after_destroy_callback; history << :after_destroy end
856
873
  end
857
-
874
+
858
875
  @doc1.delete
859
876
  end
860
877
 
861
878
  should "remove document from collection" do
862
879
  @document.count.should == 1
863
880
  end
864
-
881
+
865
882
  should "not remove other documents" do
866
883
  @document.find(@doc2.id).should_not be(nil)
867
884
  end
868
-
885
+
869
886
  should "not call before/after destroy callbacks" do
870
887
  @doc1.history.should == []
871
888
  end
872
889
  end
873
890
 
891
+ context "#destroyed?" do
892
+ setup do
893
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
894
+ end
895
+
896
+ should "be true if deleted" do
897
+ @doc1.delete
898
+ assert @doc1.destroyed?
899
+ end
900
+
901
+ should "be true if destroyed" do
902
+ @doc1.destroy
903
+ assert @doc1.destroyed?
904
+ end
905
+
906
+ should "be false if not deleted or destroyed" do
907
+ assert ! @doc1.destroyed?
908
+ end
909
+ end
910
+
874
911
  context "Single collection inheritance" do
875
912
  setup do
876
913
  class ::DocParent
@@ -883,7 +920,7 @@ class DocumentTest < Test::Unit::TestCase
883
920
  class ::DocDaughter < ::DocParent; end
884
921
  class ::DocSon < ::DocParent; end
885
922
  class ::DocGrandSon < ::DocSon; end
886
-
923
+
887
924
  DocSon.many :children, :class_name => 'DocGrandSon'
888
925
 
889
926
  @parent = DocParent.new({:name => "Daddy Warbucks"})
@@ -939,7 +976,7 @@ class DocumentTest < Test::Unit::TestCase
939
976
  DocDaughter.all(:order => 'name').should == [carrie, steph]
940
977
  DocParent.all(:order => 'name').should == [carrie, john, steph, steve]
941
978
  end
942
-
979
+
943
980
  should "work with nested hash conditions" do
944
981
  john = DocSon.create(:name => 'John')
945
982
  steve = DocSon.create(:name => 'Steve')
@@ -1023,7 +1060,7 @@ class DocumentTest < Test::Unit::TestCase
1023
1060
  DocParent.delete_all
1024
1061
  }.should change { DocParent.count }.by(-2)
1025
1062
  end
1026
-
1063
+
1027
1064
  should "be able to reload parent inherited class" do
1028
1065
  brian = DocParent.create(:name => 'Brian')
1029
1066
  brian.name = 'B-Dawg'
@@ -1053,7 +1090,7 @@ class DocumentTest < Test::Unit::TestCase
1053
1090
  doc.created_at.should_not be(nil)
1054
1091
  doc.updated_at.should_not be(nil)
1055
1092
  end
1056
-
1093
+
1057
1094
  should "not overwrite created_at if it already exists" do
1058
1095
  original_created_at = 1.month.ago
1059
1096
  doc = @klass.new(:first_name => 'John', :age => 27, :created_at => original_created_at)
@@ -1097,19 +1134,19 @@ class DocumentTest < Test::Unit::TestCase
1097
1134
  setup do
1098
1135
  @document.userstamps!
1099
1136
  end
1100
-
1137
+
1101
1138
  should "add creator_id key" do
1102
1139
  @document.keys.keys.should include('creator_id')
1103
1140
  end
1104
-
1141
+
1105
1142
  should "add updater_id key" do
1106
1143
  @document.keys.keys.should include('updater_id')
1107
1144
  end
1108
-
1145
+
1109
1146
  should "add belongs_to creator" do
1110
1147
  @document.associations.keys.should include('creator')
1111
1148
  end
1112
-
1149
+
1113
1150
  should "add belongs_to updater" do
1114
1151
  @document.associations.keys.should include('updater')
1115
1152
  end
@@ -1143,14 +1180,14 @@ class DocumentTest < Test::Unit::TestCase
1143
1180
  @foo_class = Doc do
1144
1181
  key :name
1145
1182
  end
1146
-
1183
+
1147
1184
  @bar_class = EDoc do
1148
1185
  key :name
1149
1186
  end
1150
-
1187
+
1151
1188
  @document.many :foos, :class => @foo_class
1152
1189
  @document.many :bars, :class => @bar_class
1153
-
1190
+
1154
1191
  @instance = @document.create({
1155
1192
  :age => 39,
1156
1193
  :foos => [@foo_class.new(:name => '1')],
@@ -1164,22 +1201,22 @@ class DocumentTest < Test::Unit::TestCase
1164
1201
  @instance.reload
1165
1202
  @instance.age.should == 39
1166
1203
  end
1167
-
1204
+
1168
1205
  should "reset all associations" do
1169
1206
  @instance.foos.expects(:reset).at_least_once
1170
1207
  @instance.bars.expects(:reset).at_least_once
1171
1208
  @instance.reload
1172
1209
  end
1173
-
1210
+
1174
1211
  should "reinstantiate embedded associations" do
1175
1212
  @instance.reload
1176
1213
  @instance.bars.first.name.should == '1'
1177
1214
  end
1178
-
1215
+
1179
1216
  should "return self" do
1180
1217
  @instance.reload.object_id.should == @instance.object_id
1181
1218
  end
1182
-
1219
+
1183
1220
  should "raise DocumentNotFound if not found" do
1184
1221
  @instance.destroy
1185
1222
  assert_raises(MongoMapper::DocumentNotFound) { @instance.reload }
@@ -1226,7 +1263,7 @@ class DocumentTest < Test::Unit::TestCase
1226
1263
 
1227
1264
  should "allow creating index on multiple keys" do
1228
1265
  @document.ensure_index [[:first_name, 1], [:last_name, -1]]
1229
-
1266
+
1230
1267
  # order is different for different versions of ruby so instead of
1231
1268
  # just checking have_index('first_name_1_last_name_-1') I'm checking
1232
1269
  # the values of the indexes to make sure the index creation was successful
@@ -7,27 +7,27 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
7
7
  key :first_name, String
8
8
  key :last_name, String
9
9
  end
10
-
10
+
11
11
  @pet_klass = EDoc do
12
12
  key :name, String
13
13
  end
14
-
14
+
15
15
  @klass.many :pets, :class => @pet_klass
16
-
16
+
17
17
  @address_class = EDoc do
18
18
  key :city, String
19
19
  key :state, String
20
20
  end
21
21
  end
22
-
23
- context "Saving a document with an embedded document" do
22
+
23
+ context "Saving a document with a key that is an embedded document" do
24
24
  setup do
25
25
  @klass.key :foo, @address_class
26
-
26
+
27
27
  @address = @address_class.new(:city => 'South Bend', :state => 'IN')
28
28
  @doc = @klass.new(:foo => @address)
29
29
  end
30
-
30
+
31
31
  should "embed embedded document" do
32
32
  @doc.save
33
33
  @doc.foo.city.should == 'South Bend'
@@ -38,16 +38,16 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
38
38
  doc.foo.state.should == 'IN'
39
39
  end
40
40
  end
41
-
41
+
42
42
  should "correctly instantiate single collection inherited embedded documents" do
43
43
  document = Doc('Foo') do
44
44
  key :message, Message
45
45
  end
46
-
46
+
47
47
  doc1 = document.create(:message => Enter.new)
48
48
  doc1.reload.message.class.should be(Enter)
49
49
  end
50
-
50
+
51
51
  context "new?" do
52
52
  setup do
53
53
  @klass.key :foo, @address_class
@@ -58,65 +58,65 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
58
58
  doc = @klass.new(:foo => address)
59
59
  address.new?.should == true
60
60
  end
61
-
61
+
62
62
  should "not be new after document is saved" do
63
63
  address = @address_class.new(:city => 'South Bend', :state => 'IN')
64
64
  doc = @klass.new(:foo => address)
65
65
  doc.save
66
66
  doc.foo.new?.should == false
67
67
  end
68
-
68
+
69
69
  should "not be new when document is read back" do
70
70
  address = @address_class.new(:city => 'South Bend', :state => 'IN')
71
71
  doc = @klass.new(:foo => address)
72
72
  doc.save
73
-
73
+
74
74
  doc = doc.reload
75
75
  doc.foo.new?.should == false
76
76
  end
77
77
  end
78
-
78
+
79
79
  should "be able to save" do
80
80
  person = @klass.create
81
-
81
+
82
82
  pet = @pet_klass.new(:name => 'sparky')
83
83
  person.pets << pet
84
84
  pet.should be_new
85
85
  pet.save
86
86
  pet.should_not be_new
87
-
87
+
88
88
  person.reload
89
89
  person.pets.first.should == pet
90
90
  end
91
-
91
+
92
92
  should "be able to dynamically add new keys and save" do
93
93
  person = @klass.create
94
-
94
+
95
95
  pet = @pet_klass.new(:name => 'sparky', :crazy_key => 'crazy')
96
96
  person.pets << pet
97
97
  pet.save
98
-
98
+
99
99
  person.reload
100
100
  person.pets.first.crazy_key.should == 'crazy'
101
101
  end
102
-
102
+
103
103
  should "be able to update_attributes" do
104
104
  pet = @pet_klass.new(:name => 'sparky')
105
105
  person = @klass.create(:pets => [pet])
106
106
  person.reload
107
107
  pet = person.pets.first
108
-
108
+
109
109
  pet.update_attributes(:name => 'koda').should be_true
110
110
  person.reload
111
111
  person.pets.first._id.should == pet._id
112
112
  person.pets.first.name.should == 'koda'
113
113
  end
114
-
114
+
115
115
  should "be able to update_attributes!" do
116
116
  person = @klass.create(:pets => [@pet_klass.new(:name => 'sparky')])
117
117
  person.reload
118
118
  pet = person.pets.first
119
-
119
+
120
120
  attributes = {:name => 'koda'}
121
121
  pet.expects(:attributes=).with(attributes)
122
122
  pet.expects(:save!)
@@ -5,7 +5,7 @@ class Test::Unit::TestCase
5
5
  end_time = Time.now
6
6
 
7
7
  duration = end_time - begin_time
8
- threshold = 1.0
8
+ threshold = 3.0
9
9
 
10
10
  if duration > threshold
11
11
  puts "\nSLOW TEST: #{duration} - #{self.name}"
data/test/test_helper.rb CHANGED
@@ -11,6 +11,10 @@ require 'timecop'
11
11
  require 'mocha'
12
12
  require 'pp'
13
13
 
14
+ if RUBY_VERSION.to_f < 1.9
15
+ require 'leftright' rescue nil
16
+ end
17
+
14
18
  require 'support/custom_matchers'
15
19
  require 'support/timing'
16
20
 
@@ -42,7 +42,7 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
42
42
  Object.send :remove_const, 'Child' if defined?(::Child)
43
43
  Object.send :remove_const, 'OtherChild' if defined?(::OtherChild)
44
44
  end
45
-
45
+
46
46
  context "Including MongoMapper::EmbeddedDocument in a class" do
47
47
  setup do
48
48
  @klass = EDoc()
@@ -249,34 +249,34 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
249
249
 
250
250
  should "convert string object id to mongo object id when assigning id with _id object id type" do
251
251
  id = Mongo::ObjectID.new
252
-
253
252
  doc = @document.new(:id => id.to_s)
254
253
  doc._id.should == id
255
- doc.id.should == id
256
-
254
+ doc.id.should == id
257
255
  doc = @document.new(:_id => id.to_s)
258
256
  doc._id.should == id
259
- doc.id.should == id
257
+ doc.id.should == id
260
258
  end
261
259
 
262
- context "_root_document" do
260
+ context "_parent_document" do
263
261
  should "default to nil" do
262
+ @document.new._parent_document.should be_nil
264
263
  @document.new._root_document.should be_nil
265
264
  end
266
265
 
267
- should "allow setting when initialized" do
266
+ should "set _root_document when setting _parent_document" do
268
267
  root = Doc().new
269
- doc = @document.new :_root_document => root
270
-
268
+ doc = @document.new(:_parent_document => root)
269
+ doc._parent_document.should be(root)
271
270
  doc._root_document.should be(root)
272
271
  end
273
272
 
274
- should "also be set on many embedded documents" do
275
- root = Doc().new
276
- klass = EDoc { many :children }
277
- doc = klass.new(:_root_document => root, :children => [{}])
278
-
279
- doc.children.first._root_document.should == root
273
+ should "set _root_document when setting _parent_document on embedded many" do
274
+ root = Doc().new
275
+ klass = EDoc { many :children }
276
+ parent = klass.new(:_parent_document => root, :children => [{}])
277
+ child = parent.children.first
278
+ child._parent_document.should be(parent)
279
+ child._root_document.should be(root)
280
280
  end
281
281
  end
282
282
 
@@ -42,25 +42,25 @@ class KeyTest < Test::Unit::TestCase
42
42
  should "symbolize option keys" do
43
43
  Key.new(:foo, Integer, 'required' => true).options[:required].should be(true)
44
44
  end
45
-
45
+
46
46
  should "work with just name" do
47
47
  key = Key.new(:foo)
48
48
  key.name.should == 'foo'
49
49
  end
50
-
50
+
51
51
  should "work with name and type" do
52
52
  key = Key.new(:foo, String)
53
53
  key.name.should == 'foo'
54
54
  key.type.should == String
55
55
  end
56
-
56
+
57
57
  should "work with name, type, and options" do
58
58
  key = Key.new(:foo, String, :required => true)
59
59
  key.name.should == 'foo'
60
60
  key.type.should == String
61
61
  key.options[:required].should be_true
62
62
  end
63
-
63
+
64
64
  should "work with name and options" do
65
65
  key = Key.new(:foo, :required => true)
66
66
  key.name.should == 'foo'
@@ -80,7 +80,7 @@ class KeyTest < Test::Unit::TestCase
80
80
  should "not be equal to another key with different type" do
81
81
  Key.new(:name, String).should_not == Key.new(:name, Integer)
82
82
  end
83
-
83
+
84
84
  should "know if it is a embedded_document" do
85
85
  Key.new(:name, EDoc()).embeddable?.should be_true
86
86
  end
@@ -88,12 +88,12 @@ class KeyTest < Test::Unit::TestCase
88
88
  should "know if it is not a embedded_document" do
89
89
  Key.new(:name, String).embeddable?.should be_false
90
90
  end
91
-
91
+
92
92
  should "know if it is a number" do
93
93
  Key.new(:age, Integer).number?.should be_true
94
94
  Key.new(:age, Float).number?.should be_true
95
95
  end
96
-
96
+
97
97
  should "know if it is not a number" do
98
98
  Key.new(:age, String).number?.should be_false
99
99
  end
@@ -110,7 +110,7 @@ class KeyTest < Test::Unit::TestCase
110
110
  key.set(FooType.new('something')).should == 'to_mongo'
111
111
  end
112
112
  end
113
-
113
+
114
114
  context "getting a value with a custom type" do
115
115
  should "use #from_mongo to convert back to custom type" do
116
116
  key = Key.new(:foo, FooType)
@@ -123,7 +123,7 @@ class KeyTest < Test::Unit::TestCase
123
123
  key = Key.new(:foo, String)
124
124
  key.get('bar').should == 'bar'
125
125
  end
126
-
126
+
127
127
  should "work without type" do
128
128
  key = Key.new(:foo)
129
129
  key.get([1, '2']).should == [1, '2']
@@ -144,7 +144,7 @@ class KeyTest < Test::Unit::TestCase
144
144
  end
145
145
  end
146
146
  end
147
-
147
+
148
148
  context "getting a value with a default set" do
149
149
  setup do
150
150
  @key = Key.new(:foo, String, :default => 'baz')
@@ -157,7 +157,7 @@ class KeyTest < Test::Unit::TestCase
157
157
  should "return value if not blank" do
158
158
  @key.get('foobar').should == 'foobar'
159
159
  end
160
-
160
+
161
161
  should "work with Boolean type and false value" do
162
162
  Key.new(:active, Boolean, :default => false).get(nil).should be_false
163
163
  end
@@ -165,5 +165,9 @@ class KeyTest < Test::Unit::TestCase
165
165
  should "work with Boolean type and true value" do
166
166
  Key.new(:active, Boolean, :default => true).get(nil).should be_true
167
167
  end
168
+
169
+ should "work with procs" do
170
+ Key.new(:foo, String, :default => lambda { return 'hello world' }).get(nil).should == "hello world"
171
+ end
168
172
  end
169
173
  end # KeyTest
@@ -31,7 +31,6 @@ class MongoMapperTest < Test::Unit::TestCase
31
31
  'development' => {'host' => '127.0.0.1', 'port' => 27017, 'database' => 'test'},
32
32
  'production' => {'host' => '127.0.0.1', 'port' => 27017, 'database' => 'test-prod'}
33
33
  }
34
-
35
34
  MongoMapper.config = config
36
35
  MongoMapper.config.should == config
37
36
  end
@@ -41,7 +40,16 @@ class MongoMapperTest < Test::Unit::TestCase
41
40
  MongoMapper.config = {
42
41
  'development' => {'host' => '127.0.0.1', 'port' => 27017, 'database' => 'test'}
43
42
  }
43
+ Mongo::Connection.expects(:new).with('127.0.0.1', 27017, {})
44
+ MongoMapper.expects(:database=).with('test')
45
+ Mongo::DB.any_instance.expects(:authenticate).never
46
+ MongoMapper.connect('development')
47
+ end
44
48
 
49
+ should "work without authentication using uri" do
50
+ MongoMapper.config = {
51
+ 'development' => {'uri' => 'mongodb://127.0.0.1:27017/test'}
52
+ }
45
53
  Mongo::Connection.expects(:new).with('127.0.0.1', 27017, {})
46
54
  MongoMapper.expects(:database=).with('test')
47
55
  Mongo::DB.any_instance.expects(:authenticate).never
@@ -52,7 +60,15 @@ class MongoMapperTest < Test::Unit::TestCase
52
60
  MongoMapper.config = {
53
61
  'development' => {'host' => '127.0.0.1', 'port' => 27017, 'database' => 'test'}
54
62
  }
63
+ connection, logger = mock('connection'), mock('logger')
64
+ Mongo::Connection.expects(:new).with('127.0.0.1', 27017, :logger => logger)
65
+ MongoMapper.connect('development', :logger => logger)
66
+ end
55
67
 
68
+ should "work with options using uri" do
69
+ MongoMapper.config = {
70
+ 'development' => {'uri' => 'mongodb://127.0.0.1:27017/test'}
71
+ }
56
72
  connection, logger = mock('connection'), mock('logger')
57
73
  Mongo::Connection.expects(:new).with('127.0.0.1', 27017, :logger => logger)
58
74
  MongoMapper.connect('development', :logger => logger)
@@ -62,10 +78,24 @@ class MongoMapperTest < Test::Unit::TestCase
62
78
  MongoMapper.config = {
63
79
  'development' => {'host' => '127.0.0.1', 'port' => 27017, 'database' => 'test', 'username' => 'john', 'password' => 'secret'}
64
80
  }
81
+ Mongo::DB.any_instance.expects(:authenticate).with('john', 'secret')
82
+ MongoMapper.connect('development')
83
+ end
65
84
 
85
+ should "work with authentication using uri" do
86
+ MongoMapper.config = {
87
+ 'development' => {'uri' => 'mongodb://john:secret@127.0.0.1:27017/test'}
88
+ }
66
89
  Mongo::DB.any_instance.expects(:authenticate).with('john', 'secret')
67
90
  MongoMapper.connect('development')
68
91
  end
92
+
93
+ should "raise error for invalid scheme" do
94
+ MongoMapper.config = {
95
+ 'development' => {'uri' => 'mysql://127.0.0.1:5336/foo'}
96
+ }
97
+ assert_raises(MongoMapper::InvalidScheme) { MongoMapper.connect('development') }
98
+ end
69
99
  end
70
100
 
71
101
  context "setup" do
@@ -12,6 +12,29 @@ class PaginationTest < Test::Unit::TestCase
12
12
  context "Pagination proxy" do
13
13
  include MongoMapper::Plugins::Pagination
14
14
 
15
+ should "respond_to? correctly on proxy readers" do
16
+ proxy = Proxy.new(25, 10, 4)
17
+ proxy.respond_to?(:subject).should be_true
18
+ proxy.respond_to?(:total_entries).should be_true
19
+ proxy.respond_to?(:per_page).should be_true
20
+ proxy.respond_to?(:current_page).should be_true
21
+ proxy.respond_to?(:limit).should be_true
22
+ proxy.respond_to?(:total_pages).should be_true
23
+ proxy.respond_to?(:out_of_bounds?).should be_true
24
+ proxy.respond_to?(:previous_page).should be_true
25
+ proxy.respond_to?(:next_page).should be_true
26
+ proxy.respond_to?(:skip).should be_true
27
+ proxy.respond_to?(:offset).should be_true
28
+
29
+ # make sure it doesnt respond true to everything
30
+ proxy.respond_to?(:blahblahblah).should be_false
31
+ end
32
+
33
+ should "respond_to? correctly on proxy writers" do
34
+ proxy = Proxy.new(25, 10, 4)
35
+ proxy.respond_to?(:subject=).should be_true
36
+ end
37
+
15
38
  should "should have accessors for subject" do
16
39
  subject = [1,2,3,4,5]
17
40
  collection = Proxy.new(25, 2)
@@ -31,6 +54,16 @@ class PaginationTest < Test::Unit::TestCase
31
54
  collection.class.should == Array
32
55
  end
33
56
 
57
+ should "should respond_to? correctly for methods defined on the subject" do
58
+ subject = [1,2,3,4,5]
59
+ def subject.blahblah
60
+ "BLAHBLAH"
61
+ end
62
+ collection = Proxy.new(25, 2, 10)
63
+ collection.subject = subject
64
+ collection.respond_to?(:blahblah).should be_true
65
+ end
66
+
34
67
  should "return correct value for total_entries" do
35
68
  Proxy.new(25, 2, 10).total_entries.should == 25
36
69
  Proxy.new('25', 2, 10).total_entries.should == 25
@@ -38,6 +38,8 @@ class QueryTest < Test::Unit::TestCase
38
38
  end
39
39
 
40
40
  %w{gt lt gte lte ne in nin mod all size where exists}.each do |operator|
41
+ next if operator == 'size' && RUBY_VERSION >= '1.9.1' # 1.9 defines Symbol#size
42
+
41
43
  should "convert #{operator} conditions" do
42
44
  Query.new(Room, :age.send(operator) => 21).criteria.should == {
43
45
  :age => {"$#{operator}" => 21}
@@ -176,6 +178,10 @@ class QueryTest < Test::Unit::TestCase
176
178
  Query.new(Room, :sort => sort, :order => 'foo asc').options[:sort].should == sort
177
179
  end
178
180
 
181
+ should "normalize id to _id" do
182
+ Query.new(Room, :order => :id.asc).options[:sort].should == [['_id', 1]]
183
+ end
184
+
179
185
  should "convert natural in order to proper" do
180
186
  sort = [['$natural', 1]]
181
187
  Query.new(Room, :order => '$natural asc').options[:sort].should == sort
@@ -78,7 +78,7 @@ class SupportTest < Test::Unit::TestCase
78
78
 
79
79
  context "Date#to_mongo" do
80
80
  should "be time if string" do
81
- date = Date.to_mongo('10/1/2009')
81
+ date = Date.to_mongo('2009-10-01')
82
82
  date.should == Time.utc(2009, 10, 1)
83
83
  date.should == date
84
84
  date.month.should == 10
@@ -276,8 +276,12 @@ class SupportTest < Test::Unit::TestCase
276
276
  end
277
277
 
278
278
  context "Time#to_mongo without Time.zone" do
279
+ setup do
280
+ Time.zone = nil
281
+ end
282
+
279
283
  should "be time to milliseconds if string" do
280
- Time.to_mongo('2000-01-01 01:01:01.123456').should == Time.local(2000, 1, 1, 1, 1, 1, 123000).utc
284
+ Time.to_mongo('2000-01-01 01:01:01.123456').to_f.should == Time.local(2000, 1, 1, 1, 1, 1, 123000).utc.to_f
281
285
  end
282
286
 
283
287
  should "be time in utc if time" do
@@ -296,13 +300,13 @@ class SupportTest < Test::Unit::TestCase
296
300
  context "Time#to_mongo with Time.zone" do
297
301
  should "be time to milliseconds if time" do
298
302
  Time.zone = 'Hawaii'
299
- Time.to_mongo(Time.zone.local(2009, 8, 15, 14, 0, 0, 123456)).should == Time.utc(2009, 8, 16, 0, 0, 0, 123000)
303
+ Time.to_mongo(Time.zone.local(2009, 8, 15, 14, 0, 0, 123456)).to_f.should == Time.utc(2009, 8, 16, 0, 0, 0, 123000).to_f
300
304
  Time.zone = nil
301
305
  end
302
306
 
303
307
  should "be time to milliseconds if string" do
304
308
  Time.zone = 'Hawaii'
305
- Time.to_mongo('2009-08-15 14:00:00.123456').should == Time.utc(2009, 8, 16, 0, 0, 0, 123000)
309
+ Time.to_mongo('2009-08-15 14:00:00.123456').to_f.should == Time.utc(2009, 8, 16, 0, 0, 0, 123000).to_f
306
310
  Time.zone = nil
307
311
  end
308
312
 
@@ -359,4 +363,4 @@ class SupportTest < Test::Unit::TestCase
359
363
  id.original_to_json.should == %Q({"$oid": "#{id}"})
360
364
  end
361
365
  end
362
- end
366
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo_mapper-unstable
3
3
  version: !ruby/object:Gem::Version
4
- version: 2010.2.27
4
+ version: 2010.2.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-27 00:00:00 -05:00
12
+ date: 2010-02-28 00:00:00 -05:00
13
13
  default_executable: mmconsole
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency