djsun-mongo_mapper 0.5.6.6 → 0.5.8.1

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.
Files changed (53) hide show
  1. data/.gitignore +3 -1
  2. data/Rakefile +13 -8
  3. data/VERSION +1 -1
  4. data/djsun-mongo_mapper.gemspec +17 -21
  5. data/lib/mongo_mapper/associations/base.rb +32 -36
  6. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -2
  7. data/lib/mongo_mapper/associations/many_documents_proxy.rb +19 -10
  8. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +2 -2
  9. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +21 -36
  10. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +1 -1
  11. data/lib/mongo_mapper/associations/proxy.rb +3 -2
  12. data/lib/mongo_mapper/associations.rb +114 -8
  13. data/lib/mongo_mapper/callbacks.rb +18 -0
  14. data/lib/mongo_mapper/document.rb +173 -37
  15. data/lib/mongo_mapper/dynamic_finder.rb +1 -1
  16. data/lib/mongo_mapper/embedded_document.rb +9 -13
  17. data/lib/mongo_mapper/finder_options.rb +67 -44
  18. data/lib/mongo_mapper/pagination.rb +2 -0
  19. data/lib/mongo_mapper/serialization.rb +1 -1
  20. data/lib/mongo_mapper/serializers/json_serializer.rb +1 -1
  21. data/lib/mongo_mapper/support.rb +9 -0
  22. data/lib/mongo_mapper/validations.rb +12 -42
  23. data/lib/mongo_mapper.rb +11 -5
  24. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +5 -5
  25. data/test/functional/associations/test_belongs_to_proxy.rb +29 -31
  26. data/test/functional/associations/test_many_documents_as_proxy.rb +5 -5
  27. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +27 -3
  28. data/test/functional/associations/test_many_embedded_proxy.rb +58 -38
  29. data/test/functional/associations/test_many_polymorphic_proxy.rb +45 -3
  30. data/test/functional/associations/test_many_proxy.rb +61 -11
  31. data/test/functional/test_associations.rb +3 -3
  32. data/test/functional/test_binary.rb +1 -1
  33. data/test/functional/test_callbacks.rb +1 -1
  34. data/test/functional/test_dirty.rb +3 -3
  35. data/test/functional/test_document.rb +62 -58
  36. data/test/functional/test_embedded_document.rb +1 -1
  37. data/test/functional/test_pagination.rb +1 -1
  38. data/test/functional/test_rails_compatibility.rb +1 -1
  39. data/test/functional/test_validations.rb +46 -14
  40. data/test/models.rb +87 -35
  41. data/test/support/{test_timing.rb → timing.rb} +1 -1
  42. data/test/test_helper.rb +8 -13
  43. data/test/unit/serializers/test_json_serializer.rb +0 -4
  44. data/test/unit/test_association_base.rb +24 -8
  45. data/test/unit/test_document.rb +40 -71
  46. data/test/unit/test_embedded_document.rb +27 -67
  47. data/test/unit/test_finder_options.rb +16 -0
  48. data/test/unit/test_key.rb +5 -17
  49. data/test/unit/test_mongomapper.rb +2 -2
  50. data/test/unit/test_pagination.rb +4 -0
  51. metadata +10 -12
  52. data/mongo_mapper.gemspec +0 -170
  53. data/test/functional/associations/test_namespace.rb +0 -27
data/.gitignore CHANGED
@@ -5,4 +5,6 @@ rdoc
5
5
  pkg
6
6
  *~
7
7
  *.gem
8
- tmp
8
+ tmp
9
+ .yardoc
10
+ doc/*
data/Rakefile CHANGED
@@ -1,23 +1,24 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
-
4
3
  require 'jeweler'
4
+ require 'yard'
5
+ require 'yard/rake/yardoc_task'
6
+
5
7
  Jeweler::Tasks.new do |gem|
6
8
  gem.name = "djsun-mongo_mapper"
7
- gem.summary = %Q{Awesome gem for modeling your domain and storing it in mongo}
8
- gem.description = %Q{Awesome gem for modeling your domain and storing it in mongo}
9
+ gem.summary = %(Model your domain, store it in MongoDB)
9
10
  gem.email = "nunemaker@gmail.com"
10
11
  gem.homepage = "http://github.com/jnunemaker/mongomapper"
11
- gem.authors = ["John Nunemaker"]
12
+ gem.authors = ["John Nunemaker", "David James"]
12
13
 
13
14
  gem.add_dependency('activesupport', '>= 2.3')
14
- gem.add_dependency('mongo', '>= 0.15.1')
15
- gem.add_dependency('jnunemaker-validatable', '>= 1.8.0')
15
+ gem.add_dependency('mongo', '>= 0.16')
16
+ gem.add_dependency('jnunemaker-validatable', '>= 1.8.1')
16
17
 
17
18
  gem.add_development_dependency('jnunemaker-matchy', '>= 0.4.0')
18
19
  gem.add_development_dependency('shoulda', '>= 2.10.2')
19
- gem.add_development_dependency('timecop', '>= 0.3.1')
20
- gem.add_development_dependency('mocha', '>= 0.9.4')
20
+ gem.add_development_dependency('timecop', '>= 0.3.2')
21
+ gem.add_development_dependency('mocha', '>= 0.9.8')
21
22
  end
22
23
 
23
24
  Jeweler::GemcutterTasks.new
@@ -48,3 +49,7 @@ end
48
49
 
49
50
  task :default => :test
50
51
  task :test => :check_dependencies
52
+
53
+ YARD::Rake::YardocTask.new(:doc) do |t|
54
+ t.options = ["--legacy"]
55
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.6.6
1
+ 0.5.8.1
@@ -5,13 +5,12 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{djsun-mongo_mapper}
8
- s.version = "0.5.6.6"
8
+ s.version = "0.5.8.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["John Nunemaker"]
11
+ s.authors = ["John Nunemaker", "David James"]
12
12
  s.date = %q{2009-11-04}
13
13
  s.default_executable = %q{mmconsole}
14
- s.description = %q{Awesome gem for modeling your domain and storing it in mongo}
15
14
  s.email = %q{nunemaker@gmail.com}
16
15
  s.executables = ["mmconsole"]
17
16
  s.extra_rdoc_files = [
@@ -53,7 +52,6 @@ Gem::Specification.new do |s|
53
52
  "lib/mongo_mapper/serializers/json_serializer.rb",
54
53
  "lib/mongo_mapper/support.rb",
55
54
  "lib/mongo_mapper/validations.rb",
56
- "mongo_mapper.gemspec",
57
55
  "specs.watchr",
58
56
  "test/NOTE_ON_TESTING",
59
57
  "test/functional/associations/test_belongs_to_polymorphic_proxy.rb",
@@ -63,7 +61,6 @@ Gem::Specification.new do |s|
63
61
  "test/functional/associations/test_many_embedded_proxy.rb",
64
62
  "test/functional/associations/test_many_polymorphic_proxy.rb",
65
63
  "test/functional/associations/test_many_proxy.rb",
66
- "test/functional/associations/test_namespace.rb",
67
64
  "test/functional/test_associations.rb",
68
65
  "test/functional/test_binary.rb",
69
66
  "test/functional/test_callbacks.rb",
@@ -76,7 +73,7 @@ Gem::Specification.new do |s|
76
73
  "test/functional/test_validations.rb",
77
74
  "test/models.rb",
78
75
  "test/support/custom_matchers.rb",
79
- "test/support/test_timing.rb",
76
+ "test/support/timing.rb",
80
77
  "test/test_helper.rb",
81
78
  "test/unit/serializers/test_json_serializer.rb",
82
79
  "test/unit/test_association_base.rb",
@@ -98,7 +95,7 @@ Gem::Specification.new do |s|
98
95
  s.rdoc_options = ["--charset=UTF-8"]
99
96
  s.require_paths = ["lib"]
100
97
  s.rubygems_version = %q{1.3.5}
101
- s.summary = %q{Awesome gem for modeling your domain and storing it in mongo}
98
+ s.summary = %q{Model your domain, store it in MongoDB}
102
99
  s.test_files = [
103
100
  "test/functional/associations/test_belongs_to_polymorphic_proxy.rb",
104
101
  "test/functional/associations/test_belongs_to_proxy.rb",
@@ -107,7 +104,6 @@ Gem::Specification.new do |s|
107
104
  "test/functional/associations/test_many_embedded_proxy.rb",
108
105
  "test/functional/associations/test_many_polymorphic_proxy.rb",
109
106
  "test/functional/associations/test_many_proxy.rb",
110
- "test/functional/associations/test_namespace.rb",
111
107
  "test/functional/test_associations.rb",
112
108
  "test/functional/test_binary.rb",
113
109
  "test/functional/test_callbacks.rb",
@@ -120,7 +116,7 @@ Gem::Specification.new do |s|
120
116
  "test/functional/test_validations.rb",
121
117
  "test/models.rb",
122
118
  "test/support/custom_matchers.rb",
123
- "test/support/test_timing.rb",
119
+ "test/support/timing.rb",
124
120
  "test/test_helper.rb",
125
121
  "test/unit/serializers/test_json_serializer.rb",
126
122
  "test/unit/test_association_base.rb",
@@ -145,29 +141,29 @@ Gem::Specification.new do |s|
145
141
 
146
142
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
147
143
  s.add_runtime_dependency(%q<activesupport>, [">= 2.3"])
148
- s.add_runtime_dependency(%q<mongo>, [">= 0.15.1"])
149
- s.add_runtime_dependency(%q<jnunemaker-validatable>, [">= 1.8.0"])
144
+ s.add_runtime_dependency(%q<mongo>, [">= 0.16"])
145
+ s.add_runtime_dependency(%q<jnunemaker-validatable>, [">= 1.8.1"])
150
146
  s.add_development_dependency(%q<jnunemaker-matchy>, [">= 0.4.0"])
151
147
  s.add_development_dependency(%q<shoulda>, [">= 2.10.2"])
152
- s.add_development_dependency(%q<timecop>, [">= 0.3.1"])
153
- s.add_development_dependency(%q<mocha>, [">= 0.9.4"])
148
+ s.add_development_dependency(%q<timecop>, [">= 0.3.2"])
149
+ s.add_development_dependency(%q<mocha>, [">= 0.9.8"])
154
150
  else
155
151
  s.add_dependency(%q<activesupport>, [">= 2.3"])
156
- s.add_dependency(%q<mongo>, [">= 0.15.1"])
157
- s.add_dependency(%q<jnunemaker-validatable>, [">= 1.8.0"])
152
+ s.add_dependency(%q<mongo>, [">= 0.16"])
153
+ s.add_dependency(%q<jnunemaker-validatable>, [">= 1.8.1"])
158
154
  s.add_dependency(%q<jnunemaker-matchy>, [">= 0.4.0"])
159
155
  s.add_dependency(%q<shoulda>, [">= 2.10.2"])
160
- s.add_dependency(%q<timecop>, [">= 0.3.1"])
161
- s.add_dependency(%q<mocha>, [">= 0.9.4"])
156
+ s.add_dependency(%q<timecop>, [">= 0.3.2"])
157
+ s.add_dependency(%q<mocha>, [">= 0.9.8"])
162
158
  end
163
159
  else
164
160
  s.add_dependency(%q<activesupport>, [">= 2.3"])
165
- s.add_dependency(%q<mongo>, [">= 0.15.1"])
166
- s.add_dependency(%q<jnunemaker-validatable>, [">= 1.8.0"])
161
+ s.add_dependency(%q<mongo>, [">= 0.16"])
162
+ s.add_dependency(%q<jnunemaker-validatable>, [">= 1.8.1"])
167
163
  s.add_dependency(%q<jnunemaker-matchy>, [">= 0.4.0"])
168
164
  s.add_dependency(%q<shoulda>, [">= 2.10.2"])
169
- s.add_dependency(%q<timecop>, [">= 0.3.1"])
170
- s.add_dependency(%q<mocha>, [">= 0.9.4"])
165
+ s.add_dependency(%q<timecop>, [">= 0.3.2"])
166
+ s.add_dependency(%q<mocha>, [">= 0.9.8"])
171
167
  end
172
168
  end
173
169
 
@@ -1,41 +1,27 @@
1
- module ConstantHelper
2
- def self.lookup(class_name, scope)
3
- if class_name.is_a?(Class)
4
- class_name
5
- elsif namespaced?(class_name)
6
- class_name.constantize
7
- else
8
- lookup_in_parent(class_name, scope)
9
- end
10
- end
11
-
12
- def self.namespaced?(class_name)
13
- class_name.include?("::")
14
- end
15
-
16
- def self.lookup_in_parent(class_name, scope)
17
- parent = parent_scope(scope)
18
- return nil unless parent.const_defined?(class_name)
19
- parent.const_get(class_name)
20
- end
21
-
22
- def self.parent_scope(scope)
23
- chain = scope.to_s.split("::")[0 ... -1]
24
- chain.reduce(Object) { |m, o| m.const_get(o) }
25
- end
26
-
27
- def self.leaf(constant_name)
28
- constant_name.split("::").last
29
- end
30
- end
31
-
32
1
  module MongoMapper
33
2
  module Associations
3
+ # Base class for keeping track of associations.
4
+ #
5
+ # @private
34
6
  class Base
35
- attr_reader :type, :name, :options
7
+ attr_reader :type, :name, :options, :finder_options
8
+
9
+ # Options that should not be considered MongoDB query options/criteria
10
+ AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :polymorphic]
11
+
12
+ def initialize(type, name, options={}, &extension)
13
+ @type, @name, @options, @finder_options = type, name, {}, {}
14
+ options.symbolize_keys!
36
15
 
37
- def initialize(type, name, options = {})
38
- @type, @name, @options = type, name, options
16
+ options[:extend] = modulized_extensions(extension, options[:extend])
17
+
18
+ options.each_pair do |key, value|
19
+ if AssociationOptions.include?(key)
20
+ @options[key] = value
21
+ else
22
+ @finder_options[key] = value
23
+ end
24
+ end
39
25
  end
40
26
 
41
27
  def class_name
@@ -49,9 +35,9 @@ module MongoMapper
49
35
  end
50
36
  end
51
37
  end
52
-
38
+
53
39
  def klass
54
- @klass ||= ConstantHelper.lookup(class_name, options[:scope])
40
+ @klass ||= options[:class] || class_name.constantize
55
41
  end
56
42
 
57
43
  def many?
@@ -109,6 +95,16 @@ module MongoMapper
109
95
  end
110
96
  end # end begin
111
97
  end # end proxy_class
98
+
99
+ private
100
+
101
+ # @param [Array<Module, Proc>] extensions a collection of Modules or
102
+ # Procs that extend the behaviour of this association.
103
+ def modulized_extensions(*extensions)
104
+ extensions.flatten.compact.map do |extension|
105
+ Proc === extension ? Module.new(&extension) : extension
106
+ end
107
+ end
112
108
  end
113
109
  end
114
110
  end
@@ -8,10 +8,8 @@ module MongoMapper
8
8
 
9
9
  def apply_scope(doc)
10
10
  ensure_owner_saved
11
-
12
11
  doc.send("#{as_type_name}=", @owner.class.name)
13
12
  doc.send("#{as_id_name}=", @owner.id)
14
-
15
13
  doc
16
14
  end
17
15
 
@@ -10,25 +10,30 @@ module MongoMapper
10
10
  options = args.extract_options!
11
11
  klass.find(*args << scoped_options(options))
12
12
  end
13
+
14
+ def find!(*args)
15
+ options = args.extract_options!
16
+ klass.find!(*args << scoped_options(options))
17
+ end
13
18
 
14
19
  def paginate(options)
15
20
  klass.paginate(scoped_options(options))
16
21
  end
17
22
 
18
23
  def all(options={})
19
- find(:all, scoped_options(options))
24
+ klass.all(scoped_options(options))
20
25
  end
21
26
 
22
27
  def first(options={})
23
- find(:first, scoped_options(options))
28
+ klass.first(scoped_options(options))
24
29
  end
25
30
 
26
31
  def last(options={})
27
- find(:last, scoped_options(options))
32
+ klass.last(scoped_options(options))
28
33
  end
29
34
 
30
35
  def count(options={})
31
- klass.count(options.merge(scoped_conditions))
36
+ klass.count(scoped_options(options))
32
37
  end
33
38
 
34
39
  def replace(docs)
@@ -56,6 +61,12 @@ module MongoMapper
56
61
  apply_scope(doc).save
57
62
  doc
58
63
  end
64
+
65
+ def create!(attrs={})
66
+ doc = klass.new(attrs)
67
+ apply_scope(doc).save!
68
+ doc
69
+ end
59
70
 
60
71
  def destroy_all(options={})
61
72
  all(options).map(&:destroy)
@@ -89,13 +100,13 @@ module MongoMapper
89
100
  def scoped_conditions
90
101
  {self.foreign_key => @owner.id}
91
102
  end
92
-
103
+
93
104
  def scoped_options(options)
94
- options.merge(scoped_conditions)
105
+ @association.finder_options.merge(options).merge(scoped_conditions)
95
106
  end
96
107
 
97
108
  def find_target
98
- find(:all)
109
+ all
99
110
  end
100
111
 
101
112
  def ensure_owner_saved
@@ -109,9 +120,7 @@ module MongoMapper
109
120
  end
110
121
 
111
122
  def foreign_key
112
- @association.options[:foreign_key] ||
113
- (ConstantHelper.leaf(@owner.class.name).
114
- underscore.gsub("/", "_") + "_id")
123
+ @association.options[:foreign_key] || @owner.class.name.underscore.gsub("/", "_") + "_id"
115
124
  end
116
125
  end
117
126
  end
@@ -1,6 +1,6 @@
1
1
  module MongoMapper
2
2
  module Associations
3
- class ManyEmbeddedPolymorphicProxy < Proxy
3
+ class ManyEmbeddedPolymorphicProxy < Proxy
4
4
  def replace(v)
5
5
  @_values = v.map do |doc_or_hash|
6
6
  if doc_or_hash.kind_of?(EmbeddedDocument)
@@ -30,4 +30,4 @@ module MongoMapper
30
30
  end
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -5,34 +5,23 @@ module MongoMapper
5
5
  @_values = v.map { |e| e.kind_of?(EmbeddedDocument) ? e.attributes : e }
6
6
  reset
7
7
  end
8
-
9
- def build(opts={})
10
- owner = @owner
11
- child = @association.klass.new(opts)
12
- assign_parent_reference(child)
13
- child._root_document = owner
14
- self << child
15
- child
8
+
9
+ def build(attributes={})
10
+ doc = @association.klass.new(attributes)
11
+ assign_root_document(doc)
12
+ self << doc
13
+ doc
16
14
  end
17
-
18
- def find(opts)
19
- case opts
20
- when :all
21
- self
22
- when String
23
- if load_target
24
- child = @target.detect {|item| item.id == opts}
25
- assign_parent_reference(child)
26
- child
27
- end
28
- end
15
+
16
+ def find(id)
17
+ load_target
18
+ @target.detect { |item| item.id == id }
29
19
  end
30
20
 
31
21
  def <<(*docs)
32
22
  if load_target
33
- root = @owner._root_document || @owner
34
23
  docs.each do |doc|
35
- doc._root_document = root
24
+ assign_root_document(doc)
36
25
  @target << doc
37
26
  end
38
27
  end
@@ -40,28 +29,24 @@ module MongoMapper
40
29
  alias_method :push, :<<
41
30
  alias_method :concat, :<<
42
31
 
43
- protected
32
+ private
44
33
  def find_target
45
34
  (@_values || []).map do |e|
46
35
  child = @association.klass.new(e)
47
- assign_parent_reference(child)
36
+ assign_root_document(child)
48
37
  child
49
38
  end
50
39
  end
51
-
52
- private
53
-
54
- def assign_parent_reference(child)
55
- return unless child && @owner
56
- return if @owner.class.name.blank?
57
- owner = @owner
58
- child.class_eval do
59
- define_method(owner.class.name.underscore) do
60
- owner
61
- end
40
+
41
+ def root_document
42
+ @owner._root_document || @owner
43
+ end
44
+
45
+ def assign_root_document(*docs)
46
+ docs.each do |doc|
47
+ doc._root_document = root_document
62
48
  end
63
49
  end
64
-
65
50
  end
66
51
  end
67
52
  end
@@ -1,6 +1,6 @@
1
1
  module MongoMapper
2
2
  module Associations
3
- class ManyPolymorphicProxy < ManyDocumentsProxy
3
+ class ManyPolymorphicProxy < ManyDocumentsProxy
4
4
  private
5
5
  def apply_scope(doc)
6
6
  doc.send("#{@association.type_key_name}=", doc.class.name)
@@ -6,6 +6,7 @@ module MongoMapper
6
6
  def initialize(owner, association)
7
7
  @owner = owner
8
8
  @association = association
9
+ @association.options[:extend].each { |ext| proxy_extend(ext) }
9
10
  reset
10
11
  end
11
12
 
@@ -32,7 +33,7 @@ module MongoMapper
32
33
  def replace(v)
33
34
  raise NotImplementedError
34
35
  end
35
-
36
+
36
37
  def inspect
37
38
  load_target
38
39
  @target.inspect
@@ -42,7 +43,7 @@ module MongoMapper
42
43
  load_target
43
44
  @target.nil?
44
45
  end
45
-
46
+
46
47
  protected
47
48
  def method_missing(method, *args)
48
49
  if load_target
@@ -1,13 +1,121 @@
1
1
  module MongoMapper
2
2
  module Associations
3
3
  module ClassMethods
4
- def belongs_to(association_id, options = {})
5
- create_association(:belongs_to, association_id, options, self)
4
+ ##
5
+ # This macro allows you define a "belongs-to" relationship between one
6
+ # document and some other document.
7
+ #
8
+ # == Requirements
9
+ #
10
+ # Usage of this macro requires that your document define a key that can
11
+ # be used to store the ID of the target document that is the parent of
12
+ # this document.
13
+ #
14
+ # == Conventions
15
+ #
16
+ # The following is a list of the conventions used by MongoMapper in
17
+ # defining a belongs-to relationship. Each can likely be overridden via
18
+ # the +options+ parameter.
19
+ #
20
+ # * The name of your belongs-to association is the lowercase, singular
21
+ # name of the target class
22
+ # * A key with the name of your association exists with an "_id" suffix
23
+ # to store the ID of the target of this relationship
24
+ #
25
+ # @param [Symbol] association_id The name of this association
26
+ # @param [Hash] options Optional parameters that define the
27
+ # characteristics of this relationship. These are often used to
28
+ # override MongoMapper conventions.
29
+ # @option options [Boolean] :polymorphic (false) Set this option to
30
+ # <code>true</code> to define a relationship that can be between this
31
+ # document and any other type of document. Note that you *must* also
32
+ # have a key on your document to store the type of document in this
33
+ # relationship.
34
+ # @option options [String] :class_name If your relationship doesn't use
35
+ # the name of some class, you *must* use this option to indicate the
36
+ # target class for this relationship.
37
+ # @option options [Symbol] :foreign_key Use this option to specify a
38
+ # non-conventional key that stores the ID of the parent in this
39
+ # relationship
40
+ #
41
+ # @example Conventional, and simple, usage of <code>belongs_to</code>
42
+ # class Novel
43
+ # include MongoMapper::Document
44
+ #
45
+ # key :author_id, String # our "foreign key"
46
+ #
47
+ # belongs_to :author
48
+ # end
49
+ #
50
+ # @example Using :foreign_key and :class_name
51
+ # class Pet
52
+ # include MongoMapper::Document
53
+ #
54
+ # key :person_id, String
55
+ #
56
+ # belongs_to :owner,
57
+ # :foreign_key => :person_id,
58
+ # :class_name => "Person"
59
+ # end
60
+ #
61
+ # @example Defining a polymorphic belongs-to relationship
62
+ # class Vehicle
63
+ # include MongoMapper::Document
64
+ #
65
+ # key :owner_id, String
66
+ # key :owner_type, String
67
+ #
68
+ # belongs_to :owner,
69
+ # :polymorphic => true
70
+ # end
71
+ #
72
+ # @example Non-standard polymorphic belongs-to relationship
73
+ # class Vehicle
74
+ # include MongoMapper::Document
75
+ #
76
+ # key :person_id, String
77
+ # key :person_type, String
78
+ #
79
+ # belongs_to :owner,
80
+ # :polymorphic => true,
81
+ # :foreign_key => "person_id",
82
+ # :type_key_name => "person_type"
83
+ # end
84
+ def belongs_to(association_id, options={}, &extension)
85
+ create_association(:belongs_to, association_id, options, &extension)
6
86
  self
7
87
  end
8
88
 
9
- def many(association_id, options = {})
10
- create_association(:many, association_id, options, self)
89
+ ##
90
+ # This macro allows you to define a "has-many" relationship between a
91
+ # document, and numerous child documents.
92
+ #
93
+ # == Conventions
94
+ #
95
+ # The following is a list of the conventions used by MongoMapper in
96
+ # defining this relationship. Each can likely be overridden via the
97
+ # +options+ parameter.
98
+ #
99
+ # * The name of your association is the lowercase, *plural* name of the
100
+ # target class
101
+ # * Your target class must have a "foreign key" bearing the name of this
102
+ # class suffixed by "_id"
103
+ #
104
+ # @param [Symbol] association_id The name of this association
105
+ # @param [Hash] options Optional parameters that define the
106
+ # characteristics of this relationship. These are often used to
107
+ # override MongoMapper conventions.
108
+ # @option options [String] :class_name If your relationship doesn't use
109
+ # the name of some class, you *must* use this option to indicate the
110
+ # target class for this relationship.
111
+ # @option options [Symbol] :foreign_key Use this option to specify a
112
+ # non-conventional key that stores the ID of the parent in this
113
+ # relationship
114
+ # @option options [#to_s] :as Used when the target relationship is
115
+ # polymorphic (i.e. the +belongs_to+ has set <tt>:polymorphic</tt> to
116
+ # +true+). See examples for usage.
117
+ def many(association_id, options={}, &extension)
118
+ create_association(:many, association_id, options, &extension)
11
119
  self
12
120
  end
13
121
 
@@ -18,9 +126,8 @@ module MongoMapper
18
126
  end
19
127
 
20
128
  private
21
- def create_association(type, name, options, scope)
22
- options[:scope] = scope
23
- association = Associations::Base.new(type, name, options)
129
+ def create_association(type, name, options, &extension)
130
+ association = Associations::Base.new(type, name, options, &extension)
24
131
  associations[association.name] = association
25
132
  define_association_methods(association)
26
133
  define_dependent_callback(association)
@@ -60,7 +167,6 @@ module MongoMapper
60
167
  end
61
168
  end
62
169
  end
63
-
64
170
  end
65
171
 
66
172
  module InstanceMethods
@@ -1,4 +1,17 @@
1
1
  module MongoMapper
2
+ # This module is mixed into the Document module to provide call-backs before
3
+ # and after the following events:
4
+ #
5
+ # * save
6
+ # * create
7
+ # * update
8
+ # * validation
9
+ # ** every validation
10
+ # ** validation when created
11
+ # ** validation when updated
12
+ # * destruction
13
+ #
14
+ # @see ActiveSupport::Callbacks
2
15
  module Callbacks
3
16
  def self.included(model) #:nodoc:
4
17
  model.class_eval do
@@ -42,6 +55,11 @@ module MongoMapper
42
55
  return result
43
56
  end
44
57
 
58
+ # Here we override the +destroy+ method to allow for the +before_destroy+
59
+ # and +after_destroy+ call-backs. Note that the +destroy+ call is aborted
60
+ # if the +before_destroy+ call-back returns +false+.
61
+ #
62
+ # @return the result of calling +destroy+ on the document
45
63
  def destroy #:nodoc:
46
64
  return false if callback(:before_destroy) == false
47
65
  result = super