mongo_doc 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.textile CHANGED
@@ -1,7 +1,22 @@
1
1
  h1. MongoDoc
2
2
 
3
- Version: 0.3.2 2010/03/11
3
+ Version: Turbulence (0.4.0) 2010/03/12
4
4
 
5
+ h2. What's New in Turbulence (0.4.0)
6
+
7
+ API changes (@key@ and @has_*@ still supported, will be deprectated soon)
8
+
9
+ * @MongoDoc::Document.attr_accessor@ works like the old @key@ macro, and allows two parameters:
10
+ ** @:default => 'some default value'@
11
+ ** @:type => Date@ used when dealing with values from the web which will be coming in as @String@ values
12
+ * Association macros have been renamed
13
+ ** @embed@ was @has_one@
14
+ ** @embed_many@ was @has_many@
15
+ ** @embed_hash@ was @has_hash@
16
+
17
+ h2. Notes
18
+
19
+ * 2010-03-12 Thanks to weather in ATL, cleaned up attr_accessor and switched to embed association macros
5
20
  * 2010-03-10 Slides are out of date, use key instead of attr_accessor with MongoDoc::Document (implementation was way too hackish)
6
21
  * 2010-02-23 API is *changing* significantly
7
22
  * 2010-01-23 Tracking MongoDoc with @git@? *READ THIS NOTE*[1]
@@ -55,9 +70,9 @@ collection.save(contact)
55
70
 
56
71
  p. We can query using the powerful mongoDB query syntax, and have it return Ruby objects:
57
72
 
58
- bc.. results = collection.find('addresses.state' => 'FL')
59
- hashrocket = results.to_a.find {|contact| contact.name == 'Hashrocket'}
60
- puts hashrocket.addresses.first.phone_number
73
+ bc.. in_fl = collection.where('addresses.state' => 'FL')
74
+ in_fl_hashrocket = in_fl.where('name' => /rocket/)
75
+ puts in_fl_hashrocket.first.addresses.first.phone_number
61
76
 
62
77
  p. Take a look in the examples directory for more code.
63
78
 
@@ -72,24 +87,24 @@ Lets define a @Contact@ document with an @Address@ embedded document:
72
87
  bc.. class Address
73
88
  include MongoDoc::Document
74
89
 
75
- key :street
76
- key :city
77
- key :state
78
- key :zip_code
79
- key :phone_number
90
+ attr_accessor :street
91
+ attr_accessor :city
92
+ attr_accessor :state
93
+ attr_accessor :zip_code
94
+ attr_accessor :phone_number
80
95
  end
81
96
 
82
97
  class Contact
83
98
  include MongoDoc::Document
84
99
 
85
- key :name
86
- key :interests
87
- has_many :addresses
100
+ attr_accessor :name
101
+ attr_accessor :interests
102
+ embed_many :addresses
88
103
 
89
- named_scope :in_state, lambda {|state| {:where => {'addresses.state' => state}}}
104
+ scope :in_state, lambda {|state| where('addresses.state' => state)}
90
105
  end
91
106
 
92
- p. Since a mongoDB document has no fixed schema, we define the composition of a document directly in our classes. Please note we do not specify types! We can also specify @has_one@ or @has_many@ associations.
107
+ p. Since a mongoDB document has no fixed schema, we define the composition of a document directly in our classes. We can also specify associations using @embed@, @embed_many@, and @embed_hash@ (similar to ActiveRecord's @has_one@ and @has_many@.
93
108
 
94
109
  Building and saving a document is easy:
95
110
 
@@ -103,11 +118,12 @@ contact.save
103
118
 
104
119
  p. Now that we have some data, we can query using our named scope:
105
120
 
106
- bc. hashrocket = Contact.in_state('FL').find {|contact| contact.name == 'Hashrocket'}
121
+ bc.. hashrocket_in_fl = Contact.in_state('FL').where(:name => /rocket/)
122
+ hashrocket_address = hashrocket_in_fl.first.addresses.first
107
123
 
108
124
  p. And we can even perform partial updates:
109
125
 
110
- bc. hashrocket.addresses.first.update_attributes(:street => '320 First Street North, #712')
126
+ bc. hashrocket_address.update_attributes(:street => '320 First Street North, #712')
111
127
 
112
128
  h2. Installation
113
129
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.2
1
+ 0.4.0
@@ -3,19 +3,19 @@ require 'mongo_doc'
3
3
  class Address
4
4
  include MongoDoc::Document
5
5
 
6
- key :street
7
- key :city
8
- key :state
9
- key :zip_code
10
- key :phone_number
6
+ attr_accessor :street
7
+ attr_accessor :city
8
+ attr_accessor :state
9
+ attr_accessor :zip_code
10
+ attr_accessor :phone_number
11
11
  end
12
12
 
13
13
  class Contact
14
14
  include MongoDoc::Document
15
15
 
16
- key :name
17
- key :interests
18
- has_many :addresses
16
+ attr_accessor :name
17
+ attr_accessor :interests
18
+ embed_many :addresses
19
19
 
20
20
  scope :in_state, lambda {|state| where('addresses.state' => state)}
21
21
  end
@@ -32,9 +32,9 @@ puts contact.to_param
32
32
  puts Contact.find_one(contact.to_param).addresses.first.street
33
33
  Contact.find(contact.to_param).each {|c| puts c.name}
34
34
 
35
- hashrocket = Contact.in_state('FL').find {|contact| contact.name == 'Hashrocket'}
35
+ hashrocket_in_fl = Contact.in_state('FL').where(:name => /rocket/)
36
36
 
37
- hashrocket_address = hashrocket.addresses.first
37
+ hashrocket_address = hashrocket_in_fl.first.addresses.first
38
38
  hashrocket_address.update_attributes(:street => '320 First Street North, #712')
39
39
 
40
40
  puts Contact.where(:name => 'Hashrocket').first.addresses.first.street
@@ -1,28 +1,28 @@
1
1
  class Address
2
2
  include MongoDoc::Document
3
3
 
4
- key :street
5
- key :city
6
- key :state
7
- key :zip_code
4
+ attr_accessor :street
5
+ attr_accessor :city
6
+ attr_accessor :state
7
+ attr_accessor :zip_code
8
8
  end
9
9
 
10
10
  class Place
11
11
  include MongoDoc::Document
12
12
 
13
- key :name
14
- key :type
15
- has_one :address
13
+ attr_accessor :name
14
+ attr_accessor :type
15
+ embed :address
16
16
  end
17
17
 
18
18
  class Contact
19
19
  include MongoDoc::Document
20
20
 
21
- key :name
22
- key :type
23
- key :note
24
- key :interests
25
- has_many :addresses
21
+ attr_accessor :name
22
+ attr_accessor :type
23
+ attr_accessor :note
24
+ attr_accessor :interests
25
+ embed_many :addresses
26
26
 
27
27
  scope :rubyists, any_in(:interests => ['ruby'])
28
28
  scope :contract_work, any_in(:interests => ['contract work'])
@@ -32,7 +32,7 @@ end
32
32
  class Event
33
33
  include MongoDoc::Document
34
34
 
35
- key :name
36
- key :venue
37
- key :date, :type => Date
35
+ attr_accessor :name
36
+ attr_accessor :venue
37
+ attr_accessor :date, :type => Date
38
38
  end
@@ -0,0 +1,100 @@
1
+ require 'mongo_doc/associations/proxy_base'
2
+ require 'mongo_doc/associations/collection_proxy'
3
+ require 'mongo_doc/associations/document_proxy'
4
+ require 'mongo_doc/associations/hash_proxy'
5
+
6
+ module MongoDoc
7
+ module Associations
8
+
9
+ def embed(*args)
10
+ options = args.extract_options!
11
+ assoc_class = if class_name = options.delete(:class_name)
12
+ self.class_from_name(class_name)
13
+ end
14
+
15
+ args.each do |name|
16
+ _associations << name unless _associations.include?(name)
17
+
18
+ attr_reader name
19
+
20
+ define_method("#{name}=") do |value|
21
+ association = instance_variable_get("@#{name}")
22
+ unless association
23
+ association = Associations::DocumentProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
24
+ instance_variable_set("@#{name}", association)
25
+ end
26
+ association.document = value
27
+ end
28
+
29
+ validates_embedded name, :if => Proc.new { !send(name).nil? }
30
+ end
31
+ end
32
+ alias :has_one :embed
33
+
34
+ def embed_many(*args)
35
+ options = args.extract_options!
36
+ assoc_class = if class_name = options.delete(:class_name)
37
+ self.class_from_name(class_name)
38
+ end
39
+
40
+ args.each do |name|
41
+ _associations << name unless _associations.include?(name)
42
+
43
+ define_method("#{name}") do
44
+ association = instance_variable_get("@#{name}")
45
+ unless association
46
+ association = Associations::CollectionProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
47
+ instance_variable_set("@#{name}", association)
48
+ end
49
+ association
50
+ end
51
+
52
+ validates_embedded name
53
+
54
+ define_method("#{name}=") do |arrayish|
55
+ proxy = send("#{name}")
56
+ proxy.clear
57
+ Array.wrap(arrayish).each do|item|
58
+ proxy << item
59
+ end
60
+ end
61
+ end
62
+ end
63
+ alias :has_many :embed_many
64
+
65
+ def embed_hash(*args)
66
+ options = args.extract_options!
67
+ assoc_class = if class_name = options.delete(:class_name)
68
+ self.class_from_name(class_name)
69
+ end
70
+
71
+ args.each do |name|
72
+ _associations << name unless _associations.include?(name)
73
+
74
+ define_method("#{name}") do
75
+ association = instance_variable_get("@#{name}")
76
+ unless association
77
+ association = Associations::HashProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
78
+ instance_variable_set("@#{name}", association)
79
+ end
80
+ association
81
+ end
82
+
83
+ validates_embedded name
84
+
85
+ define_method("#{name}=") do |hash|
86
+ send("#{name}").replace(hash)
87
+ end
88
+ end
89
+ end
90
+ alias :has_hash :embed_hash
91
+
92
+ def class_from_name(name)
93
+ type_name_with_module(name.to_s.classify).constantize rescue nil
94
+ end
95
+
96
+ def type_name_with_module(type_name)
97
+ (/^::/ =~ type_name) ? type_name : "#{parent}::#{type_name}"
98
+ end
99
+ end
100
+ end
@@ -1,8 +1,3 @@
1
- require 'mongo_doc/associations/proxy_base'
2
- require 'mongo_doc/associations/collection_proxy'
3
- require 'mongo_doc/associations/document_proxy'
4
- require 'mongo_doc/associations/hash_proxy'
5
-
6
1
  module MongoDoc
7
2
  module Attributes
8
3
  def self.included(klass)
@@ -51,33 +46,40 @@ module MongoDoc
51
46
  end
52
47
 
53
48
  module ClassMethods
49
+
50
+ def self.extended(klass)
51
+ klass.class_eval do
52
+ metaclass.alias_method_chain :attr_accessor, :mongo
53
+ end
54
+ end
55
+
54
56
  def _attributes
55
57
  _keys + _associations
56
58
  end
57
59
 
58
- def key(*args)
60
+ def attr_accessor_with_mongo(*args)
59
61
  opts = args.extract_options!
60
62
  default = opts.delete(:default)
61
63
  type = opts.delete(:type)
62
64
  args.each do |name|
63
65
  _keys << name unless _keys.include?(name)
64
- if default
65
- attr_writer name
66
+ attr_writer name
66
67
 
67
- define_method("_default_#{name}", default.kind_of?(Proc) ? default : Proc.new { default })
68
+ if default
69
+ define_method("_default_#{name}", default.kind_of?(Proc) ? default : proc { default })
68
70
  private "_default_#{name}"
69
71
 
70
72
  module_eval(<<-RUBY, __FILE__, __LINE__)
71
- def #{name}
72
- unless defined? @#{name}
73
- @#{name} = _default_#{name}
74
- end
75
- class << self; attr_reader :#{name} end
76
- @#{name}
77
- end
73
+ def #{name} # def birth_date
74
+ unless defined? @#{name} # unless defined? @birth_date
75
+ @#{name} = _default_#{name} # @birth_date = _default_birth_date
76
+ end # end
77
+ class << self; attr_reader :#{name} end # class << self; attr_reader :birth_date end
78
+ @#{name} # @birth_date
79
+ end # end
78
80
  RUBY
79
81
  else
80
- attr_accessor name
82
+ attr_reader name
81
83
  end
82
84
 
83
85
  if type and type.respond_to?(:cast_from_string)
@@ -93,94 +95,8 @@ module MongoDoc
93
95
  end
94
96
  end
95
97
  end
98
+ alias :key :attr_accessor_with_mongo
96
99
 
97
- def has_one(*args)
98
- options = args.extract_options!
99
- assoc_class = if class_name = options.delete(:class_name)
100
- self.class_from_name(class_name)
101
- end
102
-
103
- args.each do |name|
104
- _associations << name unless _associations.include?(name)
105
-
106
- attr_reader name
107
-
108
- define_method("#{name}=") do |value|
109
- association = instance_variable_get("@#{name}")
110
- unless association
111
- association = Associations::DocumentProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
112
- instance_variable_set("@#{name}", association)
113
- end
114
- association.document = value
115
- end
116
-
117
- validates_embedded name, :if => Proc.new { !send(name).nil? }
118
- end
119
- end
120
-
121
- def has_many(*args)
122
- options = args.extract_options!
123
- assoc_class = if class_name = options.delete(:class_name)
124
- self.class_from_name(class_name)
125
- end
126
-
127
- args.each do |name|
128
- _associations << name unless _associations.include?(name)
129
-
130
- define_method("#{name}") do
131
- association = instance_variable_get("@#{name}")
132
- unless association
133
- association = Associations::CollectionProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
134
- instance_variable_set("@#{name}", association)
135
- end
136
- association
137
- end
138
-
139
- validates_embedded name
140
-
141
- define_method("#{name}=") do |arrayish|
142
- proxy = send("#{name}")
143
- proxy.clear
144
- Array.wrap(arrayish).each do|item|
145
- proxy << item
146
- end
147
- end
148
- end
149
- end
150
-
151
- def has_hash(*args)
152
- options = args.extract_options!
153
- assoc_class = if class_name = options.delete(:class_name)
154
- self.class_from_name(class_name)
155
- end
156
-
157
- args.each do |name|
158
- _associations << name unless _associations.include?(name)
159
-
160
- define_method("#{name}") do
161
- association = instance_variable_get("@#{name}")
162
- unless association
163
- association = Associations::HashProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
164
- instance_variable_set("@#{name}", association)
165
- end
166
- association
167
- end
168
-
169
- validates_embedded name
170
-
171
- define_method("#{name}=") do |hash|
172
- send("#{name}").replace(hash)
173
- end
174
- end
175
- end
176
-
177
- def class_from_name(name)
178
- type_name_with_module(name.to_s.classify).constantize rescue nil
179
- end
180
-
181
- def type_name_with_module(type_name)
182
- (/^::/ =~ type_name) ? type_name : "#{parent}::#{type_name}"
183
- end
184
100
  end
185
101
  end
186
102
  end
@@ -40,7 +40,7 @@ module MongoDoc
40
40
  end
41
41
 
42
42
  # Determine if the context is empty or blank given the criteria. Will
43
- # perform a quick has_one asking only for the id.
43
+ # perform a quick find_one asking only for the id.
44
44
  #
45
45
  # Example:
46
46
  #
@@ -10,7 +10,7 @@ module Mongoid
10
10
  class UnknownContext < RuntimeError; end
11
11
 
12
12
  # Determines the context to be used for this criteria. If the class is an
13
- # embedded document, then the context will be the array in the has_many
13
+ # embedded document, then the context will be the array in the embed_many
14
14
  # association it is in. If the class is a root, then the database itself
15
15
  # will be the context.
16
16
  #
@@ -1,6 +1,7 @@
1
1
  require 'mongo_doc/bson'
2
2
  require 'mongo_doc/query'
3
3
  require 'mongo_doc/attributes'
4
+ require 'mongo_doc/associations'
4
5
  require 'mongo_doc/criteria'
5
6
  require 'mongo_doc/finders'
6
7
  require 'mongo_doc/scope'
@@ -16,6 +17,7 @@ module MongoDoc
16
17
  def self.included(klass)
17
18
  klass.class_eval do
18
19
  include Attributes
20
+ extend Associations
19
21
  extend ClassMethods
20
22
  extend Criteria
21
23
  extend Finders
data/lib/mongo_doc.rb CHANGED
@@ -4,7 +4,7 @@ require 'validatable'
4
4
  require 'will_paginate/collection'
5
5
 
6
6
  module MongoDoc
7
- VERSION = '0.3.1'
7
+ VERSION = '0.4.0'
8
8
  end
9
9
 
10
10
  require 'mongo_doc/connection'
data/mongo_doc.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongo_doc}
8
- s.version = "0.3.2"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Les Hill"]
12
- s.date = %q{2010-03-11}
12
+ s.date = %q{2010-03-13}
13
13
  s.description = %q{ODM for MongoDB}
14
14
  s.email = %q{leshill@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -54,6 +54,7 @@ Gem::Specification.new do |s|
54
54
  "features/support/support.rb",
55
55
  "features/using_criteria.feature",
56
56
  "lib/mongo_doc.rb",
57
+ "lib/mongo_doc/associations.rb",
57
58
  "lib/mongo_doc/associations/collection_proxy.rb",
58
59
  "lib/mongo_doc/associations/document_proxy.rb",
59
60
  "lib/mongo_doc/associations/hash_proxy.rb",
@@ -119,6 +120,7 @@ Gem::Specification.new do |s|
119
120
  "spec/associations/collection_proxy_spec.rb",
120
121
  "spec/associations/document_proxy_spec.rb",
121
122
  "spec/associations/hash_proxy_spec.rb",
123
+ "spec/associations_spec.rb",
122
124
  "spec/attributes_accessor_spec.rb",
123
125
  "spec/attributes_spec.rb",
124
126
  "spec/bson_matchers.rb",
@@ -155,6 +157,7 @@ Gem::Specification.new do |s|
155
157
  "spec/associations/collection_proxy_spec.rb",
156
158
  "spec/associations/document_proxy_spec.rb",
157
159
  "spec/associations/hash_proxy_spec.rb",
160
+ "spec/associations_spec.rb",
158
161
  "spec/attributes_accessor_spec.rb",
159
162
  "spec/attributes_spec.rb",
160
163
  "spec/bson_matchers.rb",
@@ -4,11 +4,11 @@ describe MongoDoc::Associations::CollectionProxy do
4
4
  class CollectionProxyTest
5
5
  include MongoDoc::Document
6
6
 
7
- key :name
7
+ attr_accessor :name
8
8
  end
9
9
 
10
10
  let(:root) { stub('root', :register_save_observer => nil) }
11
- let(:proxy) { MongoDoc::Associations::CollectionProxy.new(:assoc_name => 'has_many_name', :assoc_class => CollectionProxyTest, :root => root, :parent => root) }
11
+ let(:proxy) { MongoDoc::Associations::CollectionProxy.new(:assoc_name => 'embed_many_name', :assoc_class => CollectionProxyTest, :root => root, :parent => root) }
12
12
  let(:item) { CollectionProxyTest.new }
13
13
 
14
14
  context "#<<" do
@@ -4,11 +4,11 @@ describe MongoDoc::Associations::HashProxy do
4
4
  class HashProxyTest
5
5
  include MongoDoc::Document
6
6
 
7
- key :name
7
+ attr_accessor :name
8
8
  end
9
9
 
10
10
  let(:root) { stub('root', :register_save_observer => nil) }
11
- let(:proxy) { MongoDoc::Associations::HashProxy.new(:assoc_name => 'has_many_name', :assoc_class => HashProxyTest, :root => root, :parent => root) }
11
+ let(:proxy) { MongoDoc::Associations::HashProxy.new(:assoc_name => 'embed_hash_name', :assoc_class => HashProxyTest, :root => root, :parent => root) }
12
12
  let(:item) { HashProxyTest.new }
13
13
  let(:other_item) {[1,2]}
14
14