jnunemaker-mongomapper 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/History +17 -0
- data/README.rdoc +6 -3
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/bin/mmconsole +56 -0
- data/lib/mongomapper.rb +48 -17
- data/lib/mongomapper/associations.rb +31 -39
- data/lib/mongomapper/associations/base.rb +40 -22
- data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +33 -0
- data/lib/mongomapper/associations/belongs_to_proxy.rb +10 -14
- data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +34 -0
- data/lib/mongomapper/associations/{has_many_embedded_proxy.rb → many_embedded_proxy.rb} +5 -5
- data/lib/mongomapper/associations/many_proxy.rb +55 -0
- data/lib/mongomapper/associations/proxy.rb +21 -14
- data/lib/mongomapper/callbacks.rb +1 -1
- data/lib/mongomapper/document.rb +82 -59
- data/lib/mongomapper/embedded_document.rb +121 -130
- data/lib/mongomapper/finder_options.rb +21 -6
- data/lib/mongomapper/key.rb +5 -7
- data/lib/mongomapper/observing.rb +1 -41
- data/lib/mongomapper/pagination.rb +52 -0
- data/lib/mongomapper/rails_compatibility/document.rb +15 -0
- data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
- data/lib/mongomapper/serialization.rb +1 -1
- data/mongomapper.gemspec +62 -36
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/test_associations.rb +485 -0
- data/test/{test_callbacks.rb → functional/test_callbacks.rb} +2 -1
- data/test/functional/test_document.rb +636 -0
- data/test/functional/test_pagination.rb +82 -0
- data/test/functional/test_rails_compatibility.rb +31 -0
- data/test/functional/test_validations.rb +172 -0
- data/test/models.rb +92 -0
- data/test/test_helper.rb +5 -0
- data/test/{serializers → unit/serializers}/test_json_serializer.rb +0 -0
- data/test/unit/test_association_base.rb +131 -0
- data/test/unit/test_document.rb +115 -0
- data/test/{test_embedded_document.rb → unit/test_embedded_document.rb} +158 -66
- data/test/{test_finder_options.rb → unit/test_finder_options.rb} +66 -0
- data/test/{test_key.rb → unit/test_key.rb} +13 -1
- data/test/unit/test_mongo_id.rb +35 -0
- data/test/{test_mongomapper.rb → unit/test_mongomapper.rb} +0 -0
- data/test/{test_observing.rb → unit/test_observing.rb} +0 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +34 -0
- data/test/{test_serializations.rb → unit/test_serializations.rb} +0 -2
- data/test/{test_validations.rb → unit/test_validations.rb} +0 -134
- metadata +68 -36
- data/lib/mongomapper/associations/has_many_proxy.rb +0 -28
- data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +0 -31
- data/lib/mongomapper/rails_compatibility.rb +0 -23
- data/test/test_associations.rb +0 -149
- data/test/test_document.rb +0 -944
- data/test/test_rails_compatibility.rb +0 -29
data/.gitignore
CHANGED
data/History
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
0.3.0 7/28/2009
|
2
|
+
* 5 major additions, 3 minor additions, 3 bug fix, and other miscellany
|
3
|
+
* BACKWORDS COMPATIBILITY BREAK: _id is now stored in binary form (recommended by mongodb team) instead of string, api is the same everywhere as before but data stored with string id's previous to change will need to be updated
|
4
|
+
* Added Document#paginate which works just like find but adds pagination (dcu did basics and I pimped)
|
5
|
+
* Added a basic console for playing around with MongoMapper (dcu)
|
6
|
+
* Embedded associations can now be deeply nested (Keith Hanson)
|
7
|
+
* Added support for many polymorphic documents (Felipe Coury and Me)
|
8
|
+
* Fixed bug where conditions that disallowed using $in, $all and $any with an array
|
9
|
+
* Bumped version of validatable so :if validation option supports symbol/string to proc.
|
10
|
+
* Document#create with no attributes now creates a document as long as it is valid
|
11
|
+
* Now defining accessor methods when key is declared rather than using method missing and all that jazz
|
12
|
+
* Attributes now have boolean methods that return true or false based on whether they have value present
|
13
|
+
* Added scoped finds and pagination on many document association.
|
14
|
+
* find first and last now use natural order which is more reliable.
|
15
|
+
* Updated to latest ruby driver (0.10.1)
|
16
|
+
|
17
|
+
|
1
18
|
0.2.0 7/7/2009
|
2
19
|
* 2 major additions (observers, associations), several minor additions, and a few bug fixes
|
3
20
|
* Added observers
|
data/README.rdoc
CHANGED
@@ -2,15 +2,18 @@
|
|
2
2
|
|
3
3
|
Awesome gem for modeling your domain and storing it in mongo.
|
4
4
|
|
5
|
-
|
5
|
+
Releases are tagged on github and also released as gems on github and rubyforge. Master is pushed to whenever I add a patch or a new feature. To build from master, you can clone the code, generate the updated gemspec, build the gem and install.
|
6
6
|
|
7
|
-
|
7
|
+
* rake gemspec
|
8
|
+
* gem build mongomapper.gemspec
|
9
|
+
* gem install the gem that was built
|
8
10
|
|
9
11
|
== Note on Patches/Pull Requests
|
10
12
|
|
11
13
|
* Fork the project.
|
12
14
|
* Make your feature addition or bug fix.
|
13
|
-
*
|
15
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
16
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
|
14
17
|
* Send me a pull request. Bonus points for topic branches.
|
15
18
|
|
16
19
|
== Dependencies
|
data/Rakefile
CHANGED
@@ -12,8 +12,9 @@ begin
|
|
12
12
|
gem.rubyforge_project = "mongomapper"
|
13
13
|
|
14
14
|
gem.add_dependency('activesupport')
|
15
|
-
gem.add_dependency('mongodb-mongo', '0.
|
16
|
-
gem.add_dependency('jnunemaker-validatable', '1.7.
|
15
|
+
gem.add_dependency('mongodb-mongo', '0.10.1')
|
16
|
+
gem.add_dependency('jnunemaker-validatable', '1.7.2')
|
17
|
+
gem.add_dependency('deep_merge', '0.1.0')
|
17
18
|
|
18
19
|
gem.add_development_dependency('mocha', '0.9.4')
|
19
20
|
gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/bin/mmconsole
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.dirname(__FILE__)+"/../lib"
|
3
|
+
|
4
|
+
require 'mongomapper'
|
5
|
+
require 'irb'
|
6
|
+
|
7
|
+
IRB.setup(nil)
|
8
|
+
irb = IRB::Irb.new
|
9
|
+
|
10
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
11
|
+
|
12
|
+
irb.context.evaluate("require 'irb/completion'", 0)
|
13
|
+
irb.context.evaluate(%@
|
14
|
+
include XGen::Mongo::Driver
|
15
|
+
include MongoMapper
|
16
|
+
|
17
|
+
MongoMapper.database = "mmtest"
|
18
|
+
$db = MongoMapper.database
|
19
|
+
|
20
|
+
@, 0)
|
21
|
+
|
22
|
+
puts %@
|
23
|
+
Welcome to the MongoMapper Console!
|
24
|
+
|
25
|
+
Example 1:
|
26
|
+
things = $db.collection("things")
|
27
|
+
things.insert("name" => "Raw Thing")
|
28
|
+
things.insert("name" => "Another Thing", "date" => Time.now)
|
29
|
+
|
30
|
+
cursor = things.find("name" => "Raw Thing")
|
31
|
+
puts cursor.next_object.inspect
|
32
|
+
|
33
|
+
Example 2:
|
34
|
+
class Thing
|
35
|
+
include MongoMapper::Document
|
36
|
+
key :name, String, :required => true
|
37
|
+
key :date, Time
|
38
|
+
end
|
39
|
+
|
40
|
+
thing = Thing.new
|
41
|
+
thing.name = "My thing"
|
42
|
+
thing.date = Time.now
|
43
|
+
thing.save
|
44
|
+
|
45
|
+
all_things = Thing.all
|
46
|
+
puts all_things.map { |object| object.name }.inspect
|
47
|
+
|
48
|
+
@
|
49
|
+
|
50
|
+
trap("SIGINT") do
|
51
|
+
irb.signal_handle
|
52
|
+
end
|
53
|
+
catch(:IRB_EXIT) do
|
54
|
+
irb.eval_input
|
55
|
+
end
|
56
|
+
|
data/lib/mongomapper.rb
CHANGED
@@ -2,38 +2,69 @@ require 'pathname'
|
|
2
2
|
require 'rubygems'
|
3
3
|
|
4
4
|
gem 'activesupport'
|
5
|
-
gem 'mongodb-mongo', '0.
|
6
|
-
gem 'jnunemaker-validatable', '1.7.
|
5
|
+
gem 'mongodb-mongo', '0.10.1'
|
6
|
+
gem 'jnunemaker-validatable', '1.7.2'
|
7
|
+
gem 'deep_merge', '0.1.0'
|
7
8
|
|
8
9
|
require 'activesupport'
|
9
10
|
require 'mongo'
|
10
11
|
require 'validatable'
|
12
|
+
require 'deep_merge'
|
11
13
|
|
12
|
-
|
14
|
+
class BasicObject #:nodoc:
|
15
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
|
16
|
+
end unless defined?(BasicObject)
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
class Boolean
|
19
|
+
def self.mm_typecast(value)
|
20
|
+
['true', 't', '1'].include?(value.to_s.downcase)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class MongoID < XGen::Mongo::Driver::ObjectID
|
25
|
+
def self.mm_typecast(value)
|
26
|
+
begin
|
27
|
+
if value.is_a?(XGen::Mongo::Driver::ObjectID)
|
28
|
+
value
|
29
|
+
else
|
30
|
+
XGen::Mongo::Driver::ObjectID::from_string(value.to_s)
|
31
|
+
end
|
32
|
+
rescue => exception
|
33
|
+
if exception.message == 'illegal ObjectID format'
|
34
|
+
raise MongoMapper::DocumentNotFound
|
35
|
+
else
|
36
|
+
raise exception
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
dir = Pathname(__FILE__).dirname.expand_path + 'mongomapper'
|
22
43
|
|
44
|
+
require dir + 'associations'
|
23
45
|
require dir + 'associations/proxy'
|
24
46
|
require dir + 'associations/array_proxy'
|
25
47
|
require dir + 'associations/base'
|
26
|
-
|
27
|
-
require dir + 'associations/
|
28
|
-
require dir + 'associations/
|
48
|
+
require dir + 'associations/many_proxy'
|
49
|
+
require dir + 'associations/many_embedded_proxy'
|
50
|
+
require dir + 'associations/many_embedded_polymorphic_proxy'
|
29
51
|
require dir + 'associations/belongs_to_proxy'
|
30
|
-
require dir + 'associations/
|
31
|
-
require dir + '
|
52
|
+
require dir + 'associations/belongs_to_polymorphic_proxy'
|
53
|
+
require dir + 'callbacks'
|
54
|
+
require dir + 'finder_options'
|
55
|
+
require dir + 'key'
|
56
|
+
require dir + 'observing'
|
57
|
+
require dir + 'pagination'
|
58
|
+
require dir + 'rails_compatibility/document'
|
59
|
+
require dir + 'rails_compatibility/embedded_document'
|
60
|
+
require dir + 'save_with_validation'
|
61
|
+
require dir + 'serialization'
|
62
|
+
require dir + 'validations'
|
32
63
|
|
33
64
|
require dir + 'embedded_document'
|
34
65
|
require dir + 'document'
|
35
66
|
|
36
|
-
module MongoMapper
|
67
|
+
module MongoMapper
|
37
68
|
class DocumentNotFound < StandardError; end
|
38
69
|
class DocumentNotValid < StandardError
|
39
70
|
def initialize(document)
|
@@ -2,33 +2,12 @@ module MongoMapper
|
|
2
2
|
module Associations
|
3
3
|
module ClassMethods
|
4
4
|
def belongs_to(association_id, options = {})
|
5
|
-
|
6
|
-
|
7
|
-
ref_id = "#{association_id}_id"
|
8
|
-
key ref_id, String
|
9
|
-
|
10
|
-
define_method("#{ref_id}=") do |value|
|
11
|
-
write_attribute(ref_id, value)
|
12
|
-
end
|
13
|
-
|
14
|
-
if options[:polymorphic]
|
15
|
-
ref_type = "#{association_id}_type"
|
16
|
-
key ref_type, String
|
17
|
-
|
18
|
-
define_method("#{ref_type}=") do |value|
|
19
|
-
write_attribute(ref_type, value)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
define_association_methods(association)
|
24
|
-
|
5
|
+
create_association(:belongs_to, association_id, options)
|
25
6
|
self
|
26
7
|
end
|
27
8
|
|
28
9
|
def many(association_id, options = {})
|
29
|
-
|
30
|
-
define_association_methods(association)
|
31
|
-
|
10
|
+
create_association(:many, association_id, options)
|
32
11
|
self
|
33
12
|
end
|
34
13
|
|
@@ -37,31 +16,44 @@ module MongoMapper
|
|
37
16
|
end
|
38
17
|
|
39
18
|
private
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def define_association_methods(association)
|
47
|
-
define_method(association.name) do
|
48
|
-
get_proxy(association)
|
19
|
+
def create_association(type, name, options)
|
20
|
+
association = Associations::Base.new(type, name, options)
|
21
|
+
associations[association.name] = association
|
22
|
+
define_association_methods(association)
|
23
|
+
define_association_keys(association)
|
24
|
+
association
|
49
25
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
26
|
+
|
27
|
+
def define_association_methods(association)
|
28
|
+
define_method(association.name) do
|
29
|
+
get_proxy(association)
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method("#{association.name}=") do |value|
|
33
|
+
get_proxy(association).replace(value)
|
34
|
+
value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def define_association_keys(association)
|
39
|
+
if association.belongs_to?
|
40
|
+
key(association.belongs_to_key_name, String)
|
41
|
+
key(association.type_key_name, String) if association.polymorphic?
|
42
|
+
end
|
43
|
+
|
44
|
+
if association.many? && association.polymorphic?
|
45
|
+
association.klass.send(:key, association.type_key_name, String)
|
46
|
+
end
|
54
47
|
end
|
55
|
-
end
|
56
48
|
end
|
57
49
|
|
58
50
|
module InstanceMethods
|
59
51
|
def get_proxy(association)
|
60
|
-
proxy = self.instance_variable_get(association.ivar)
|
61
|
-
if proxy.nil?
|
52
|
+
unless proxy = self.instance_variable_get(association.ivar)
|
62
53
|
proxy = association.proxy_class.new(self, association)
|
63
54
|
self.instance_variable_set(association.ivar, proxy)
|
64
55
|
end
|
56
|
+
|
65
57
|
proxy
|
66
58
|
end
|
67
59
|
end
|
@@ -8,43 +8,61 @@ module MongoMapper
|
|
8
8
|
@type = type
|
9
9
|
@name = name
|
10
10
|
end
|
11
|
-
|
12
|
-
def klass
|
13
|
-
class_name.constantize
|
14
|
-
end
|
15
|
-
|
11
|
+
|
16
12
|
def class_name
|
17
13
|
@class_name ||= begin
|
18
14
|
if cn = options[:class_name]
|
19
15
|
cn
|
20
|
-
elsif
|
16
|
+
elsif many?
|
21
17
|
name.to_s.singularize.camelize
|
22
18
|
else
|
23
19
|
name.to_s.camelize
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|
27
|
-
|
23
|
+
|
24
|
+
def klass
|
25
|
+
@klass ||= class_name.constantize
|
26
|
+
end
|
27
|
+
|
28
|
+
def many?
|
29
|
+
@many_type ||= @type == :many
|
30
|
+
end
|
31
|
+
|
32
|
+
def belongs_to?
|
33
|
+
@belongs_to_type ||= @type == :belongs_to
|
34
|
+
end
|
35
|
+
|
36
|
+
def polymorphic?
|
37
|
+
!!@options[:polymorphic]
|
38
|
+
end
|
39
|
+
|
40
|
+
def type_key_name
|
41
|
+
@type_key_name ||= many? ? '_type' : "#{name}_type"
|
42
|
+
end
|
43
|
+
|
44
|
+
def belongs_to_key_name
|
45
|
+
"#{name}_id"
|
46
|
+
end
|
47
|
+
|
28
48
|
def ivar
|
29
49
|
@ivar ||= "@_#{name}"
|
30
50
|
end
|
51
|
+
|
52
|
+
def embeddable?
|
53
|
+
many? && klass.embeddable?
|
54
|
+
end
|
31
55
|
|
32
56
|
def proxy_class
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
HasManyEmbeddedProxy
|
43
|
-
else
|
44
|
-
HasManyProxy
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
57
|
+
@proxy_class ||= begin
|
58
|
+
if many?
|
59
|
+
return ManyProxy unless self.klass.embeddable?
|
60
|
+
polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
|
61
|
+
else
|
62
|
+
polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
|
63
|
+
end
|
64
|
+
end # end begin
|
65
|
+
end # end proxy_class
|
48
66
|
end
|
49
67
|
end
|
50
68
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
class BelongsToPolymorphicProxy < Proxy
|
4
|
+
def replace(doc)
|
5
|
+
if doc
|
6
|
+
doc.save if doc.new?
|
7
|
+
id, type = doc.id, doc.class.name
|
8
|
+
end
|
9
|
+
|
10
|
+
@owner.send("#{@association.belongs_to_key_name}=", id)
|
11
|
+
@owner.send("#{@association.type_key_name}=", type)
|
12
|
+
|
13
|
+
reload_target
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def find_target
|
18
|
+
proxy_class.find(proxy_id) if proxy_id && proxy_class
|
19
|
+
end
|
20
|
+
|
21
|
+
def proxy_id
|
22
|
+
@proxy_id ||= @owner.send(@association.belongs_to_key_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def proxy_class
|
26
|
+
@proxy_class ||= begin
|
27
|
+
klass = @owner.send(@association.type_key_name)
|
28
|
+
klass && klass.constantize
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,26 +1,22 @@
|
|
1
1
|
module MongoMapper
|
2
2
|
module Associations
|
3
3
|
class BelongsToProxy < Proxy
|
4
|
-
def replace(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
v.save if v.new?
|
9
|
-
@owner.__send__(:write_attribute, ref_id, v.id)
|
10
|
-
else
|
11
|
-
@owner.__send__(:write_attribute, ref_id, nil)
|
4
|
+
def replace(doc)
|
5
|
+
if doc
|
6
|
+
doc.save if doc.new?
|
7
|
+
id = doc.id
|
12
8
|
end
|
13
|
-
|
9
|
+
|
10
|
+
@owner.send("#{@association.belongs_to_key_name}=", id)
|
14
11
|
reload_target
|
15
12
|
end
|
16
13
|
|
17
14
|
protected
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
def find_target
|
16
|
+
if ref = @owner.send(@association.belongs_to_key_name)
|
17
|
+
@association.klass.find(ref)
|
18
|
+
end
|
22
19
|
end
|
23
|
-
end
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|