mongo_doc 0.3.2 → 0.4.0

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