mongodoc 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +80 -0
- data/VERSION +1 -0
- data/data/.gitignore +2 -0
- data/features/mongodoc_base.feature +117 -0
- data/features/saving_an_object.feature +17 -0
- data/features/step_definitions/collection_steps.rb +14 -0
- data/features/step_definitions/connect_steps.rb +4 -0
- data/features/step_definitions/document_steps.rb +88 -0
- data/features/step_definitions/json_steps.rb +9 -0
- data/features/step_definitions/object_steps.rb +43 -0
- data/features/step_definitions/util_steps.rb +7 -0
- data/features/support/support.rb +9 -0
- data/lib/mongodoc.rb +17 -0
- data/lib/mongodoc/attributes.rb +97 -0
- data/lib/mongodoc/base.rb +163 -0
- data/lib/mongodoc/bson.rb +45 -0
- data/lib/mongodoc/connection.rb +20 -0
- data/lib/mongodoc/ext/array.rb +5 -0
- data/lib/mongodoc/ext/binary.rb +7 -0
- data/lib/mongodoc/ext/boolean_class.rb +11 -0
- data/lib/mongodoc/ext/date.rb +16 -0
- data/lib/mongodoc/ext/date_time.rb +13 -0
- data/lib/mongodoc/ext/dbref.rb +7 -0
- data/lib/mongodoc/ext/hash.rb +7 -0
- data/lib/mongodoc/ext/nil_class.rb +5 -0
- data/lib/mongodoc/ext/numeric.rb +17 -0
- data/lib/mongodoc/ext/object.rb +17 -0
- data/lib/mongodoc/ext/object_id.rb +7 -0
- data/lib/mongodoc/ext/regexp.rb +5 -0
- data/lib/mongodoc/ext/string.rb +5 -0
- data/lib/mongodoc/ext/symbol.rb +5 -0
- data/lib/mongodoc/ext/time.rb +5 -0
- data/lib/mongodoc/parent_proxy.rb +37 -0
- data/lib/mongodoc/proxy.rb +76 -0
- data/lib/mongodoc/query.rb +7 -0
- data/lib/mongodoc/value_equals.rb +8 -0
- data/mongod.example.yml +2 -0
- data/mongodoc.gemspec +117 -0
- data/script/console +8 -0
- data/spec/attributes_spec.rb +159 -0
- data/spec/base_ext.rb +9 -0
- data/spec/base_spec.rb +273 -0
- data/spec/bson_matchers.rb +54 -0
- data/spec/bson_spec.rb +316 -0
- data/spec/connection_spec.rb +81 -0
- data/spec/parent_proxy_spec.rb +42 -0
- data/spec/query_spec.rb +12 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/test_classes.rb +19 -0
- data/spec/test_documents.rb +35 -0
- metadata +159 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
class Object
|
2
|
+
def to_bson(*args)
|
3
|
+
{MongoDoc::BSON::CLASS_KEY => self.class.name}.tap do |bson_hash|
|
4
|
+
instance_variables.each do |name|
|
5
|
+
bson_hash[name[1..-1]] = instance_variable_get(name).to_bson(args)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.bson_create(bson_hash, options = {})
|
11
|
+
new.tap do |obj|
|
12
|
+
bson_hash.each do |name, value|
|
13
|
+
obj.instance_variable_set("@#{name}", MongoDoc::BSON.decode(value, options))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MongoDoc
|
2
|
+
module Document
|
3
|
+
class ParentProxy
|
4
|
+
attr_reader :assoc_name, :_parent
|
5
|
+
|
6
|
+
def initialize(parent, assoc_name)
|
7
|
+
raise ArgumentError.new('ParentProxy requires a parent') if parent.nil?
|
8
|
+
raise ArgumentError.new('ParentProxy require an association name') if assoc_name.blank?
|
9
|
+
@_parent = parent
|
10
|
+
@assoc_name = assoc_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def path_to_root(attrs)
|
14
|
+
assoc_attrs = attrs.inject({}) do |assoc_attrs, (key, value)|
|
15
|
+
assoc_attrs["#{assoc_name}.#{key}"] = value
|
16
|
+
assoc_attrs
|
17
|
+
end
|
18
|
+
_parent.path_to_root(assoc_attrs)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def method_missing(method, *args)
|
24
|
+
unless @_parent.respond_to?(method)
|
25
|
+
message = "undefined method `#{method.to_s}' for proxied \"#{@_parent}\":#{@_parent.class.to_s}"
|
26
|
+
raise NoMethodError, message
|
27
|
+
end
|
28
|
+
|
29
|
+
if block_given?
|
30
|
+
@_parent.send(method, *args) { |*block_args| yield(*block_args) }
|
31
|
+
else
|
32
|
+
@_parent.send(method, *args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Thanks Sandro!
|
2
|
+
# http://github.com/sandro
|
3
|
+
module MongoDoc
|
4
|
+
module Document
|
5
|
+
class Proxy
|
6
|
+
# List of array methods (that are not in +Object+) that need to be
|
7
|
+
# delegated to +collection+.
|
8
|
+
ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
|
9
|
+
|
10
|
+
# List of additional methods that must be delegated to +collection+.
|
11
|
+
MUST_DEFINE = %w[to_a to_ary inspect to_bson ==]
|
12
|
+
|
13
|
+
(ARRAY_METHODS + MUST_DEFINE).uniq.each do |method|
|
14
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
15
|
+
def #{method}(*args, &block) # def each(*args, &block)
|
16
|
+
collection.send(:#{method}, *args, &block) # collection.send(:each, *args, &block)
|
17
|
+
end # end
|
18
|
+
RUBY
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :assoc_name, :collection, :collection_class, :_parent, :_root
|
22
|
+
|
23
|
+
def _parent=(parent)
|
24
|
+
@_parent = parent
|
25
|
+
end
|
26
|
+
|
27
|
+
def _root=(root)
|
28
|
+
@_root = root
|
29
|
+
collection.each do |item|
|
30
|
+
item._root = root
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(options)
|
35
|
+
@assoc_name = options[:assoc_name]
|
36
|
+
@collection = []
|
37
|
+
@collection_class = options[:collection_class]
|
38
|
+
@_root = options[:root]
|
39
|
+
@_parent = options[:parent]
|
40
|
+
end
|
41
|
+
|
42
|
+
alias_method :append, :<<
|
43
|
+
def <<(items)
|
44
|
+
items = [items] unless items.kind_of?(Array)
|
45
|
+
items.each do |item|
|
46
|
+
item = collection_class.new(item) if Hash === item
|
47
|
+
raise NotADocumentError unless collection_class === item
|
48
|
+
append item
|
49
|
+
item._parent = self
|
50
|
+
item._root = _root
|
51
|
+
end
|
52
|
+
self
|
53
|
+
end
|
54
|
+
alias_method :push, :<<
|
55
|
+
alias_method :concat, :<<
|
56
|
+
|
57
|
+
# Lie about our class. Borrowed from Rake::FileList
|
58
|
+
# Note: Does not work for case equality (<tt>===</tt>)
|
59
|
+
def is_a?(klass)
|
60
|
+
klass == Array || super(klass)
|
61
|
+
end
|
62
|
+
alias kind_of? is_a?
|
63
|
+
|
64
|
+
def path_to_root(attrs)
|
65
|
+
_parent.path_to_root(attrs)
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
def _propose_update_attributes(src, attrs, safe)
|
71
|
+
src.errors.add(:base, 'update_attributes called through a has_many')
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/mongod.example.yml
ADDED
data/mongodoc.gemspec
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{mongodoc}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Les Hill"]
|
12
|
+
s.date = %q{2009-11-22}
|
13
|
+
s.description = %q{ODM for MongoDB}
|
14
|
+
s.email = %q{leshill@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"data/.gitignore",
|
27
|
+
"features/mongodoc_base.feature",
|
28
|
+
"features/saving_an_object.feature",
|
29
|
+
"features/step_definitions/collection_steps.rb",
|
30
|
+
"features/step_definitions/connect_steps.rb",
|
31
|
+
"features/step_definitions/document_steps.rb",
|
32
|
+
"features/step_definitions/json_steps.rb",
|
33
|
+
"features/step_definitions/object_steps.rb",
|
34
|
+
"features/step_definitions/util_steps.rb",
|
35
|
+
"features/support/support.rb",
|
36
|
+
"lib/mongodoc.rb",
|
37
|
+
"lib/mongodoc/attributes.rb",
|
38
|
+
"lib/mongodoc/base.rb",
|
39
|
+
"lib/mongodoc/bson.rb",
|
40
|
+
"lib/mongodoc/connection.rb",
|
41
|
+
"lib/mongodoc/ext/array.rb",
|
42
|
+
"lib/mongodoc/ext/binary.rb",
|
43
|
+
"lib/mongodoc/ext/boolean_class.rb",
|
44
|
+
"lib/mongodoc/ext/date.rb",
|
45
|
+
"lib/mongodoc/ext/date_time.rb",
|
46
|
+
"lib/mongodoc/ext/dbref.rb",
|
47
|
+
"lib/mongodoc/ext/hash.rb",
|
48
|
+
"lib/mongodoc/ext/nil_class.rb",
|
49
|
+
"lib/mongodoc/ext/numeric.rb",
|
50
|
+
"lib/mongodoc/ext/object.rb",
|
51
|
+
"lib/mongodoc/ext/object_id.rb",
|
52
|
+
"lib/mongodoc/ext/regexp.rb",
|
53
|
+
"lib/mongodoc/ext/string.rb",
|
54
|
+
"lib/mongodoc/ext/symbol.rb",
|
55
|
+
"lib/mongodoc/ext/time.rb",
|
56
|
+
"lib/mongodoc/parent_proxy.rb",
|
57
|
+
"lib/mongodoc/proxy.rb",
|
58
|
+
"lib/mongodoc/query.rb",
|
59
|
+
"lib/mongodoc/value_equals.rb",
|
60
|
+
"mongod.example.yml",
|
61
|
+
"mongodoc.gemspec",
|
62
|
+
"script/console",
|
63
|
+
"spec/attributes_spec.rb",
|
64
|
+
"spec/base_ext.rb",
|
65
|
+
"spec/base_spec.rb",
|
66
|
+
"spec/bson_matchers.rb",
|
67
|
+
"spec/bson_spec.rb",
|
68
|
+
"spec/connection_spec.rb",
|
69
|
+
"spec/parent_proxy_spec.rb",
|
70
|
+
"spec/query_spec.rb",
|
71
|
+
"spec/spec.opts",
|
72
|
+
"spec/spec_helper.rb",
|
73
|
+
"spec/test_classes.rb",
|
74
|
+
"spec/test_documents.rb"
|
75
|
+
]
|
76
|
+
s.homepage = %q{http://github.com/leshill/mongodoc}
|
77
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
78
|
+
s.require_paths = ["lib"]
|
79
|
+
s.rubygems_version = %q{1.3.5}
|
80
|
+
s.summary = %q{ODM for MongoDB}
|
81
|
+
s.test_files = [
|
82
|
+
"spec/attributes_spec.rb",
|
83
|
+
"spec/base_ext.rb",
|
84
|
+
"spec/base_spec.rb",
|
85
|
+
"spec/bson_matchers.rb",
|
86
|
+
"spec/bson_spec.rb",
|
87
|
+
"spec/connection_spec.rb",
|
88
|
+
"spec/parent_proxy_spec.rb",
|
89
|
+
"spec/query_spec.rb",
|
90
|
+
"spec/spec_helper.rb",
|
91
|
+
"spec/test_classes.rb",
|
92
|
+
"spec/test_documents.rb"
|
93
|
+
]
|
94
|
+
|
95
|
+
if s.respond_to? :specification_version then
|
96
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
97
|
+
s.specification_version = 3
|
98
|
+
|
99
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
100
|
+
s.add_runtime_dependency(%q<mongo>, ["= 0.16"])
|
101
|
+
s.add_runtime_dependency(%q<durran-validatable>, ["= 1.8.2"])
|
102
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
103
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
104
|
+
else
|
105
|
+
s.add_dependency(%q<mongo>, ["= 0.16"])
|
106
|
+
s.add_dependency(%q<durran-validatable>, ["= 1.8.2"])
|
107
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
108
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
109
|
+
end
|
110
|
+
else
|
111
|
+
s.add_dependency(%q<mongo>, ["= 0.16"])
|
112
|
+
s.add_dependency(%q<durran-validatable>, ["= 1.8.2"])
|
113
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
114
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
data/script/console
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "MongoDoc::Document::Attributes" do
|
4
|
+
context ".key" do
|
5
|
+
class TestKeys < MongoDoc::Base
|
6
|
+
end
|
7
|
+
|
8
|
+
it "adds its arguments to _keys" do
|
9
|
+
TestKeys.key :attr1, :attr2
|
10
|
+
TestKeys._keys.should == [:attr1, :attr2]
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "accessors" do
|
14
|
+
before do
|
15
|
+
TestKeys.key :attr1
|
16
|
+
end
|
17
|
+
|
18
|
+
subject do
|
19
|
+
TestKeys.new
|
20
|
+
end
|
21
|
+
it "has an attr1 reader" do
|
22
|
+
should respond_to(:attr1)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "has an attr1 writer" do
|
26
|
+
should respond_to(:attr1=)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "used with inheritance" do
|
31
|
+
class TestParent < MongoDoc::Base
|
32
|
+
key :parent_attr
|
33
|
+
end
|
34
|
+
|
35
|
+
class TestChild < TestParent
|
36
|
+
key :child_attr
|
37
|
+
end
|
38
|
+
|
39
|
+
it "has its own keys" do
|
40
|
+
TestChild._keys.should include(:child_attr)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "has the keys from the parent class" do
|
44
|
+
TestChild._keys.should include(*TestParent._keys)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context ".has_one" do
|
50
|
+
class TestDoc < MongoDoc::Base
|
51
|
+
has_one :subdoc
|
52
|
+
end
|
53
|
+
|
54
|
+
class SubDoc < MongoDoc::Base
|
55
|
+
key :data
|
56
|
+
end
|
57
|
+
|
58
|
+
it "sets the subdocuments parent to the parent proxy" do
|
59
|
+
subdoc = SubDoc.new
|
60
|
+
doc = TestDoc.new(:subdoc => subdoc)
|
61
|
+
MongoDoc::Document::ParentProxy.should === subdoc._parent
|
62
|
+
subdoc._parent._parent.should == doc
|
63
|
+
end
|
64
|
+
|
65
|
+
it "set the subdocuments root" do
|
66
|
+
subdoc = SubDoc.new
|
67
|
+
middoc = TestDoc.new
|
68
|
+
doc = TestDoc.new(:subdoc => middoc)
|
69
|
+
middoc.subdoc = subdoc
|
70
|
+
subdoc._root.should == doc
|
71
|
+
end
|
72
|
+
|
73
|
+
it "sets the subdocuments root no matter how when it is inserted" do
|
74
|
+
subdoc = SubDoc.new
|
75
|
+
middoc = TestDoc.new(:subdoc => subdoc)
|
76
|
+
doc = TestDoc.new(:subdoc => middoc)
|
77
|
+
subdoc._root.should == doc
|
78
|
+
end
|
79
|
+
|
80
|
+
class HasOneValidationTest < MongoDoc::Base
|
81
|
+
key :data
|
82
|
+
validates_presence_of :data
|
83
|
+
end
|
84
|
+
|
85
|
+
it "cascades validations down" do
|
86
|
+
invalid = HasOneValidationTest.new
|
87
|
+
doc = TestDoc.new(:subdoc => invalid)
|
88
|
+
doc.should have(1).error_on(:subdoc)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "._attributes" do
|
93
|
+
class TestHasOneDoc < MongoDoc::Base
|
94
|
+
key :key
|
95
|
+
has_one :has_one
|
96
|
+
end
|
97
|
+
|
98
|
+
it "is _keys + _associations" do
|
99
|
+
TestHasOneDoc._attributes.should == TestHasOneDoc._keys + TestHasOneDoc._associations
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context ".has_many" do
|
104
|
+
|
105
|
+
class SubHasManyDoc < MongoDoc::Base
|
106
|
+
key :data
|
107
|
+
end
|
108
|
+
|
109
|
+
class TestHasManyDoc < MongoDoc::Base
|
110
|
+
has_many :sub_docs, :class_name => 'SubHasManyDoc'
|
111
|
+
end
|
112
|
+
|
113
|
+
class TestImplicitHasManyDoc < MongoDoc::Base
|
114
|
+
has_many :sub_has_many_docs
|
115
|
+
end
|
116
|
+
|
117
|
+
it "uses a proxy" do
|
118
|
+
MongoDoc::Document::Proxy.should === TestHasManyDoc.new.sub_docs
|
119
|
+
end
|
120
|
+
|
121
|
+
it "sets the subdocuments parent to the proxy" do
|
122
|
+
subdoc = SubHasManyDoc.new
|
123
|
+
doc = TestHasManyDoc.new(:sub_docs => [subdoc])
|
124
|
+
subdoc._parent.should == doc.sub_docs
|
125
|
+
end
|
126
|
+
|
127
|
+
it "set the subdocuments root to the root" do
|
128
|
+
subdoc = SubHasManyDoc.new
|
129
|
+
doc = TestHasManyDoc.new(:sub_docs => [subdoc])
|
130
|
+
subdoc._root.should == doc
|
131
|
+
end
|
132
|
+
|
133
|
+
it "uses the association name to find the children's class name" do
|
134
|
+
subdoc = SubHasManyDoc.new
|
135
|
+
doc = TestImplicitHasManyDoc.new(:sub_has_many_docs => [subdoc])
|
136
|
+
end
|
137
|
+
|
138
|
+
class HasManyValidationChild < MongoDoc::Base
|
139
|
+
key :data
|
140
|
+
validates_presence_of :data
|
141
|
+
end
|
142
|
+
|
143
|
+
class HasManyValidationTest < MongoDoc::Base
|
144
|
+
has_many :subdocs, :class_name => 'HasManyValidationChild'
|
145
|
+
end
|
146
|
+
|
147
|
+
it "cascades validations and marks it in the parent" do
|
148
|
+
invalid = HasManyValidationChild.new
|
149
|
+
doc = HasManyValidationTest.new(:subdocs => [invalid])
|
150
|
+
doc.should have(1).error_on(:subdocs)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "cascades validations and marks it in the child" do
|
154
|
+
invalid = HasManyValidationChild.new
|
155
|
+
doc = HasManyValidationTest.new(:subdocs => [invalid])
|
156
|
+
invalid.should have(1).error_on(:data)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|