mongo_mapper-unstable 2010.2.27 → 2010.2.28

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