humanoid 1.2.7
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/.gitignore +6 -0
- data/.watchr +29 -0
- data/HISTORY +342 -0
- data/MIT_LICENSE +20 -0
- data/README.rdoc +56 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/caliper.yml +4 -0
- data/humanoid.gemspec +374 -0
- data/lib/humanoid.rb +111 -0
- data/lib/humanoid/associations.rb +258 -0
- data/lib/humanoid/associations/belongs_to.rb +64 -0
- data/lib/humanoid/associations/belongs_to_related.rb +62 -0
- data/lib/humanoid/associations/has_many.rb +180 -0
- data/lib/humanoid/associations/has_many_related.rb +109 -0
- data/lib/humanoid/associations/has_one.rb +95 -0
- data/lib/humanoid/associations/has_one_related.rb +81 -0
- data/lib/humanoid/associations/options.rb +57 -0
- data/lib/humanoid/associations/proxy.rb +31 -0
- data/lib/humanoid/attributes.rb +184 -0
- data/lib/humanoid/callbacks.rb +23 -0
- data/lib/humanoid/collection.rb +118 -0
- data/lib/humanoid/collections/cyclic_iterator.rb +34 -0
- data/lib/humanoid/collections/master.rb +28 -0
- data/lib/humanoid/collections/mimic.rb +46 -0
- data/lib/humanoid/collections/operations.rb +41 -0
- data/lib/humanoid/collections/slaves.rb +44 -0
- data/lib/humanoid/commands.rb +182 -0
- data/lib/humanoid/commands/create.rb +21 -0
- data/lib/humanoid/commands/delete.rb +16 -0
- data/lib/humanoid/commands/delete_all.rb +23 -0
- data/lib/humanoid/commands/deletion.rb +18 -0
- data/lib/humanoid/commands/destroy.rb +19 -0
- data/lib/humanoid/commands/destroy_all.rb +23 -0
- data/lib/humanoid/commands/save.rb +27 -0
- data/lib/humanoid/components.rb +24 -0
- data/lib/humanoid/config.rb +84 -0
- data/lib/humanoid/contexts.rb +25 -0
- data/lib/humanoid/contexts/enumerable.rb +117 -0
- data/lib/humanoid/contexts/ids.rb +25 -0
- data/lib/humanoid/contexts/mongo.rb +224 -0
- data/lib/humanoid/contexts/paging.rb +42 -0
- data/lib/humanoid/criteria.rb +259 -0
- data/lib/humanoid/criterion/complex.rb +21 -0
- data/lib/humanoid/criterion/exclusion.rb +65 -0
- data/lib/humanoid/criterion/inclusion.rb +91 -0
- data/lib/humanoid/criterion/optional.rb +128 -0
- data/lib/humanoid/cursor.rb +82 -0
- data/lib/humanoid/document.rb +300 -0
- data/lib/humanoid/enslavement.rb +38 -0
- data/lib/humanoid/errors.rb +77 -0
- data/lib/humanoid/extensions.rb +84 -0
- data/lib/humanoid/extensions/array/accessors.rb +17 -0
- data/lib/humanoid/extensions/array/aliasing.rb +4 -0
- data/lib/humanoid/extensions/array/assimilation.rb +26 -0
- data/lib/humanoid/extensions/array/conversions.rb +29 -0
- data/lib/humanoid/extensions/array/parentization.rb +13 -0
- data/lib/humanoid/extensions/boolean/conversions.rb +16 -0
- data/lib/humanoid/extensions/date/conversions.rb +15 -0
- data/lib/humanoid/extensions/datetime/conversions.rb +17 -0
- data/lib/humanoid/extensions/float/conversions.rb +16 -0
- data/lib/humanoid/extensions/hash/accessors.rb +38 -0
- data/lib/humanoid/extensions/hash/assimilation.rb +30 -0
- data/lib/humanoid/extensions/hash/conversions.rb +15 -0
- data/lib/humanoid/extensions/hash/criteria_helpers.rb +20 -0
- data/lib/humanoid/extensions/hash/scoping.rb +12 -0
- data/lib/humanoid/extensions/integer/conversions.rb +16 -0
- data/lib/humanoid/extensions/nil/assimilation.rb +13 -0
- data/lib/humanoid/extensions/object/conversions.rb +33 -0
- data/lib/humanoid/extensions/proc/scoping.rb +12 -0
- data/lib/humanoid/extensions/string/conversions.rb +15 -0
- data/lib/humanoid/extensions/string/inflections.rb +97 -0
- data/lib/humanoid/extensions/symbol/inflections.rb +36 -0
- data/lib/humanoid/extensions/time/conversions.rb +18 -0
- data/lib/humanoid/factory.rb +19 -0
- data/lib/humanoid/field.rb +39 -0
- data/lib/humanoid/fields.rb +62 -0
- data/lib/humanoid/finders.rb +224 -0
- data/lib/humanoid/identity.rb +39 -0
- data/lib/humanoid/indexes.rb +30 -0
- data/lib/humanoid/matchers.rb +36 -0
- data/lib/humanoid/matchers/all.rb +11 -0
- data/lib/humanoid/matchers/default.rb +26 -0
- data/lib/humanoid/matchers/exists.rb +13 -0
- data/lib/humanoid/matchers/gt.rb +11 -0
- data/lib/humanoid/matchers/gte.rb +11 -0
- data/lib/humanoid/matchers/in.rb +11 -0
- data/lib/humanoid/matchers/lt.rb +11 -0
- data/lib/humanoid/matchers/lte.rb +11 -0
- data/lib/humanoid/matchers/ne.rb +11 -0
- data/lib/humanoid/matchers/nin.rb +11 -0
- data/lib/humanoid/matchers/size.rb +11 -0
- data/lib/humanoid/memoization.rb +27 -0
- data/lib/humanoid/named_scope.rb +40 -0
- data/lib/humanoid/scope.rb +75 -0
- data/lib/humanoid/timestamps.rb +30 -0
- data/lib/humanoid/versioning.rb +28 -0
- data/perf/benchmark.rb +77 -0
- data/spec/integration/humanoid/associations_spec.rb +301 -0
- data/spec/integration/humanoid/attributes_spec.rb +22 -0
- data/spec/integration/humanoid/commands_spec.rb +216 -0
- data/spec/integration/humanoid/contexts/enumerable_spec.rb +33 -0
- data/spec/integration/humanoid/criteria_spec.rb +224 -0
- data/spec/integration/humanoid/document_spec.rb +587 -0
- data/spec/integration/humanoid/extensions_spec.rb +26 -0
- data/spec/integration/humanoid/finders_spec.rb +119 -0
- data/spec/integration/humanoid/inheritance_spec.rb +137 -0
- data/spec/integration/humanoid/named_scope_spec.rb +46 -0
- data/spec/models/address.rb +39 -0
- data/spec/models/animal.rb +6 -0
- data/spec/models/comment.rb +8 -0
- data/spec/models/country_code.rb +6 -0
- data/spec/models/employer.rb +5 -0
- data/spec/models/game.rb +6 -0
- data/spec/models/inheritance.rb +56 -0
- data/spec/models/location.rb +5 -0
- data/spec/models/mixed_drink.rb +4 -0
- data/spec/models/name.rb +13 -0
- data/spec/models/namespacing.rb +11 -0
- data/spec/models/patient.rb +4 -0
- data/spec/models/person.rb +98 -0
- data/spec/models/pet.rb +7 -0
- data/spec/models/pet_owner.rb +6 -0
- data/spec/models/phone.rb +7 -0
- data/spec/models/post.rb +15 -0
- data/spec/models/translation.rb +5 -0
- data/spec/models/vet_visit.rb +5 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +141 -0
- data/spec/unit/mongoid/associations/belongs_to_spec.rb +193 -0
- data/spec/unit/mongoid/associations/has_many_related_spec.rb +387 -0
- data/spec/unit/mongoid/associations/has_many_spec.rb +471 -0
- data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
- data/spec/unit/mongoid/associations/has_one_spec.rb +282 -0
- data/spec/unit/mongoid/associations/options_spec.rb +191 -0
- data/spec/unit/mongoid/associations_spec.rb +545 -0
- data/spec/unit/mongoid/attributes_spec.rb +484 -0
- data/spec/unit/mongoid/callbacks_spec.rb +55 -0
- data/spec/unit/mongoid/collection_spec.rb +171 -0
- data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
- data/spec/unit/mongoid/collections/master_spec.rb +41 -0
- data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
- data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
- data/spec/unit/mongoid/commands/create_spec.rb +30 -0
- data/spec/unit/mongoid/commands/delete_all_spec.rb +58 -0
- data/spec/unit/mongoid/commands/delete_spec.rb +35 -0
- data/spec/unit/mongoid/commands/destroy_all_spec.rb +23 -0
- data/spec/unit/mongoid/commands/destroy_spec.rb +44 -0
- data/spec/unit/mongoid/commands/save_spec.rb +105 -0
- data/spec/unit/mongoid/commands_spec.rb +282 -0
- data/spec/unit/mongoid/config_spec.rb +165 -0
- data/spec/unit/mongoid/contexts/enumerable_spec.rb +374 -0
- data/spec/unit/mongoid/contexts/mongo_spec.rb +505 -0
- data/spec/unit/mongoid/contexts_spec.rb +25 -0
- data/spec/unit/mongoid/criteria_spec.rb +769 -0
- data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
- data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
- data/spec/unit/mongoid/criterion/inclusion_spec.rb +211 -0
- data/spec/unit/mongoid/criterion/optional_spec.rb +329 -0
- data/spec/unit/mongoid/cursor_spec.rb +74 -0
- data/spec/unit/mongoid/document_spec.rb +986 -0
- data/spec/unit/mongoid/enslavement_spec.rb +63 -0
- data/spec/unit/mongoid/errors_spec.rb +103 -0
- data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
- data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
- data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
- data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
- data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
- data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
- data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +70 -0
- data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
- data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
- data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
- data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
- data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
- data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
- data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
- data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
- data/spec/unit/mongoid/extensions/object/conversions_spec.rb +43 -0
- data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
- data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
- data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
- data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
- data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
- data/spec/unit/mongoid/factory_spec.rb +31 -0
- data/spec/unit/mongoid/field_spec.rb +81 -0
- data/spec/unit/mongoid/fields_spec.rb +158 -0
- data/spec/unit/mongoid/finders_spec.rb +368 -0
- data/spec/unit/mongoid/identity_spec.rb +88 -0
- data/spec/unit/mongoid/indexes_spec.rb +93 -0
- data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
- data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
- data/spec/unit/mongoid/matchers_spec.rb +329 -0
- data/spec/unit/mongoid/memoization_spec.rb +75 -0
- data/spec/unit/mongoid/named_scope_spec.rb +123 -0
- data/spec/unit/mongoid/scope_spec.rb +240 -0
- data/spec/unit/mongoid/timestamps_spec.rb +25 -0
- data/spec/unit/mongoid/versioning_spec.rb +41 -0
- data/spec/unit/mongoid_spec.rb +37 -0
- metadata +431 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require "humanoid/associations/proxy"
|
|
3
|
+
require "humanoid/associations/belongs_to"
|
|
4
|
+
require "humanoid/associations/belongs_to_related"
|
|
5
|
+
require "humanoid/associations/has_many"
|
|
6
|
+
require "humanoid/associations/has_many_related"
|
|
7
|
+
require "humanoid/associations/has_one"
|
|
8
|
+
require "humanoid/associations/has_one_related"
|
|
9
|
+
|
|
10
|
+
module Humanoid # :nodoc:
|
|
11
|
+
module Associations #:nodoc:
|
|
12
|
+
def self.included(base)
|
|
13
|
+
base.class_eval do
|
|
14
|
+
# Associations need to inherit down the chain.
|
|
15
|
+
class_inheritable_accessor :associations
|
|
16
|
+
self.associations = {}
|
|
17
|
+
|
|
18
|
+
include InstanceMethods
|
|
19
|
+
extend ClassMethods
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module InstanceMethods
|
|
24
|
+
# Returns the associations for the +Document+.
|
|
25
|
+
def associations
|
|
26
|
+
self.class.associations
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Updates all the one-to-many relational associations for the name.
|
|
30
|
+
def update_associations(name)
|
|
31
|
+
send(name).each { |doc| doc.save }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Update the one-to-one relational association for the name.
|
|
35
|
+
def update_association(name)
|
|
36
|
+
association = send(name)
|
|
37
|
+
association.save unless association.nil?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module ClassMethods
|
|
42
|
+
# Adds the association back to the parent document. This macro is
|
|
43
|
+
# necessary to set the references from the child back to the parent
|
|
44
|
+
# document. If a child does not define this association calling
|
|
45
|
+
# persistence methods on the child object will cause a save to fail.
|
|
46
|
+
#
|
|
47
|
+
# Options:
|
|
48
|
+
#
|
|
49
|
+
# name: A +Symbol+ that matches the name of the parent class.
|
|
50
|
+
#
|
|
51
|
+
# Example:
|
|
52
|
+
#
|
|
53
|
+
# class Person
|
|
54
|
+
# include Humanoid::Document
|
|
55
|
+
# has_many :addresses
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# class Address
|
|
59
|
+
# include Humanoid::Document
|
|
60
|
+
# belongs_to :person, :inverse_of => :addresses
|
|
61
|
+
# end
|
|
62
|
+
def belongs_to(name, options = {}, &block)
|
|
63
|
+
unless options.has_key?(:inverse_of)
|
|
64
|
+
raise Errors::InvalidOptions.new("Options for belongs_to association must include :inverse_of")
|
|
65
|
+
end
|
|
66
|
+
self.embedded = true
|
|
67
|
+
add_association(
|
|
68
|
+
Associations::BelongsTo,
|
|
69
|
+
Associations::Options.new(
|
|
70
|
+
options.merge(:name => name, :extend => block)
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Adds a relational association from the child Document to a Document in
|
|
76
|
+
# another database or collection.
|
|
77
|
+
#
|
|
78
|
+
# Options:
|
|
79
|
+
#
|
|
80
|
+
# name: A +Symbol+ that is the related class name.
|
|
81
|
+
#
|
|
82
|
+
# Example:
|
|
83
|
+
#
|
|
84
|
+
# class Game
|
|
85
|
+
# include Humanoid::Document
|
|
86
|
+
# belongs_to_related :person
|
|
87
|
+
# end
|
|
88
|
+
#
|
|
89
|
+
def belongs_to_related(name, options = {}, &block)
|
|
90
|
+
field "#{name.to_s}_id"
|
|
91
|
+
index "#{name.to_s}_id" unless self.embedded
|
|
92
|
+
add_association(
|
|
93
|
+
Associations::BelongsToRelated,
|
|
94
|
+
Associations::Options.new(
|
|
95
|
+
options.merge(:name => name, :extend => block)
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Adds the association from a parent document to its children. The name
|
|
101
|
+
# of the association needs to be a pluralized form of the child class
|
|
102
|
+
# name.
|
|
103
|
+
#
|
|
104
|
+
# Options:
|
|
105
|
+
#
|
|
106
|
+
# name: A +Symbol+ that is the plural child class name.
|
|
107
|
+
#
|
|
108
|
+
# Example:
|
|
109
|
+
#
|
|
110
|
+
# class Person
|
|
111
|
+
# include Humanoid::Document
|
|
112
|
+
# has_many :addresses
|
|
113
|
+
# end
|
|
114
|
+
#
|
|
115
|
+
# class Address
|
|
116
|
+
# include Humanoid::Document
|
|
117
|
+
# belongs_to :person, :inverse_of => :addresses
|
|
118
|
+
# end
|
|
119
|
+
def has_many(name, options = {}, &block)
|
|
120
|
+
add_association(
|
|
121
|
+
Associations::HasMany,
|
|
122
|
+
Associations::Options.new(
|
|
123
|
+
options.merge(:name => name, :extend => block)
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Adds a relational association from the Document to many Documents in
|
|
129
|
+
# another database or collection.
|
|
130
|
+
#
|
|
131
|
+
# Options:
|
|
132
|
+
#
|
|
133
|
+
# name: A +Symbol+ that is the related class name pluralized.
|
|
134
|
+
#
|
|
135
|
+
# Example:
|
|
136
|
+
#
|
|
137
|
+
# class Person
|
|
138
|
+
# include Humanoid::Document
|
|
139
|
+
# has_many_related :posts
|
|
140
|
+
# end
|
|
141
|
+
#
|
|
142
|
+
def has_many_related(name, options = {}, &block)
|
|
143
|
+
add_association(
|
|
144
|
+
Associations::HasManyRelated,
|
|
145
|
+
Associations::Options.new(
|
|
146
|
+
options.merge(:name => name, :parent_key => self.name.foreign_key, :extend => block)
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
before_save do |document|
|
|
150
|
+
document.update_associations(name)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Adds the association from a parent document to its child. The name
|
|
155
|
+
# of the association needs to be a singular form of the child class
|
|
156
|
+
# name.
|
|
157
|
+
#
|
|
158
|
+
# Options:
|
|
159
|
+
#
|
|
160
|
+
# name: A +Symbol+ that is the plural child class name.
|
|
161
|
+
#
|
|
162
|
+
# Example:
|
|
163
|
+
#
|
|
164
|
+
# class Person
|
|
165
|
+
# include Humanoid::Document
|
|
166
|
+
# has_many :addresses
|
|
167
|
+
# end
|
|
168
|
+
#
|
|
169
|
+
# class Address
|
|
170
|
+
# include Humanoid::Document
|
|
171
|
+
# belongs_to :person
|
|
172
|
+
# end
|
|
173
|
+
def has_one(name, options = {}, &block)
|
|
174
|
+
opts = Associations::Options.new(
|
|
175
|
+
options.merge(:name => name, :extend => block)
|
|
176
|
+
)
|
|
177
|
+
type = Associations::HasOne
|
|
178
|
+
add_association(type, opts)
|
|
179
|
+
add_builder(type, opts)
|
|
180
|
+
add_creator(type, opts)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Adds a relational association from the Document to one Document in
|
|
184
|
+
# another database or collection.
|
|
185
|
+
#
|
|
186
|
+
# Options:
|
|
187
|
+
#
|
|
188
|
+
# name: A +Symbol+ that is the related class name pluralized.
|
|
189
|
+
#
|
|
190
|
+
# Example:
|
|
191
|
+
#
|
|
192
|
+
# class Person
|
|
193
|
+
# include Humanoid::Document
|
|
194
|
+
# has_one_related :game
|
|
195
|
+
# end
|
|
196
|
+
def has_one_related(name, options = {}, &block)
|
|
197
|
+
add_association(
|
|
198
|
+
Associations::HasOneRelated,
|
|
199
|
+
Associations::Options.new(
|
|
200
|
+
options.merge(:name => name, :parent_key => self.name.foreign_key, :extend => block)
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
before_save do |document|
|
|
204
|
+
document.update_association(name)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Returns the macro associated with the supplied association name. This
|
|
209
|
+
# will return has_one, has_many, belongs_to or nil.
|
|
210
|
+
#
|
|
211
|
+
# Options:
|
|
212
|
+
#
|
|
213
|
+
# name: The association name.
|
|
214
|
+
#
|
|
215
|
+
# Example:
|
|
216
|
+
#
|
|
217
|
+
# <tt>Person.reflect_on_association(:addresses)</tt>
|
|
218
|
+
def reflect_on_association(name)
|
|
219
|
+
association = associations[name.to_s]
|
|
220
|
+
association ? association.macro : nil
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
protected
|
|
224
|
+
# Adds the association to the associations hash with the type as the key,
|
|
225
|
+
# then adds the accessors for the association. The defined setters and
|
|
226
|
+
# getters for the associations will perform the necessary memoization.
|
|
227
|
+
def add_association(type, options)
|
|
228
|
+
name = options.name.to_s
|
|
229
|
+
associations[name] = type
|
|
230
|
+
define_method(name) do
|
|
231
|
+
memoized(name) { type.instantiate(self, options) }
|
|
232
|
+
end
|
|
233
|
+
define_method("#{name}=") do |object|
|
|
234
|
+
reset(name) { type.update(object, self, options) }
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Adds a builder for a has_one association. This comes in the form of
|
|
239
|
+
# build_name(attributes)
|
|
240
|
+
def add_builder(type, options)
|
|
241
|
+
name = options.name.to_s
|
|
242
|
+
define_method("build_#{name}") do |attrs|
|
|
243
|
+
reset(name) { type.new(self, (attrs || {}).stringify_keys, options) }
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Adds a creator for a has_one association. This comes in the form of
|
|
248
|
+
# create_name(attributes)
|
|
249
|
+
def add_creator(type, options)
|
|
250
|
+
name = options.name.to_s
|
|
251
|
+
define_method("create_#{name}") do |attrs|
|
|
252
|
+
document = send("build_#{name}", attrs)
|
|
253
|
+
document.save; document
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Associations #:nodoc:
|
|
4
|
+
class BelongsTo #:nodoc:
|
|
5
|
+
include Proxy
|
|
6
|
+
|
|
7
|
+
# Creates the new association by setting the internal
|
|
8
|
+
# target as the passed in Document. This should be the
|
|
9
|
+
# parent.
|
|
10
|
+
#
|
|
11
|
+
# All method calls on this object will then be delegated
|
|
12
|
+
# to the internal document itself.
|
|
13
|
+
#
|
|
14
|
+
# Options:
|
|
15
|
+
#
|
|
16
|
+
# target: The parent +Document+
|
|
17
|
+
# options: The association options
|
|
18
|
+
def initialize(target, options)
|
|
19
|
+
@target, @options = target, options
|
|
20
|
+
extends(options)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns the parent document. The id param is present for
|
|
24
|
+
# compatibility with rails, however this could be overwritten
|
|
25
|
+
# in the future.
|
|
26
|
+
def find(id)
|
|
27
|
+
@target
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# Creates the new association by setting the internal
|
|
32
|
+
# document as the passed in Document. This should be the
|
|
33
|
+
# parent.
|
|
34
|
+
#
|
|
35
|
+
# Options:
|
|
36
|
+
#
|
|
37
|
+
# document: The parent +Document+
|
|
38
|
+
# options: The association options
|
|
39
|
+
def instantiate(document, options)
|
|
40
|
+
target = document._parent
|
|
41
|
+
target.nil? ? nil : new(target, options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the macro used to create the association.
|
|
45
|
+
def macro
|
|
46
|
+
:belongs_to
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Perform an update of the relationship of the parent and child. This
|
|
50
|
+
# is initialized by setting a parent object as the association on the
|
|
51
|
+
# +Document+. Will properly set a has_one or a has_many.
|
|
52
|
+
#
|
|
53
|
+
# Returns:
|
|
54
|
+
#
|
|
55
|
+
# A new +BelongsTo+ association proxy.
|
|
56
|
+
def update(target, child, options)
|
|
57
|
+
child.parentize(target, options.inverse_of)
|
|
58
|
+
child.notify
|
|
59
|
+
instantiate(child, options)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Associations #:nodoc:
|
|
4
|
+
class BelongsToRelated #:nodoc:
|
|
5
|
+
include Proxy
|
|
6
|
+
|
|
7
|
+
# Initializing a related association only requires looking up the object
|
|
8
|
+
# by its id.
|
|
9
|
+
#
|
|
10
|
+
# Options:
|
|
11
|
+
#
|
|
12
|
+
# document: The +Document+ that contains the relationship.
|
|
13
|
+
# options: The association +Options+.
|
|
14
|
+
def initialize(document, foreign_key, options, target = nil)
|
|
15
|
+
@options = options
|
|
16
|
+
@target = target || options.klass.find(foreign_key)
|
|
17
|
+
extends(options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
# Instantiate a new +BelongsToRelated+ or return nil if the foreign key is
|
|
22
|
+
# nil. It is preferrable to use this method over the traditional call
|
|
23
|
+
# to new.
|
|
24
|
+
#
|
|
25
|
+
# Options:
|
|
26
|
+
#
|
|
27
|
+
# document: The +Document+ that contains the relationship.
|
|
28
|
+
# options: The association +Options+.
|
|
29
|
+
def instantiate(document, options, target = nil)
|
|
30
|
+
foreign_key = document.send(options.foreign_key)
|
|
31
|
+
foreign_key.blank? ? nil : new(document, foreign_key, options, target)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns the macro used to create the association.
|
|
35
|
+
def macro
|
|
36
|
+
:belongs_to_related
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Perform an update of the relationship of the parent and child. This
|
|
40
|
+
# will assimilate the child +Document+ into the parent's object graph.
|
|
41
|
+
#
|
|
42
|
+
# Options:
|
|
43
|
+
#
|
|
44
|
+
# related: The related object
|
|
45
|
+
# parent: The parent +Document+ to update.
|
|
46
|
+
# options: The association +Options+
|
|
47
|
+
#
|
|
48
|
+
# Example:
|
|
49
|
+
#
|
|
50
|
+
# <tt>BelongsToRelated.update(game, person, options)</tt>
|
|
51
|
+
def update(target, parent, options)
|
|
52
|
+
if target
|
|
53
|
+
parent.send("#{options.foreign_key}=", target.id)
|
|
54
|
+
return instantiate(parent, options, target)
|
|
55
|
+
end
|
|
56
|
+
target
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Associations #:nodoc:
|
|
4
|
+
class HasMany
|
|
5
|
+
include Proxy
|
|
6
|
+
|
|
7
|
+
attr_accessor :association_name, :klass
|
|
8
|
+
|
|
9
|
+
# Appends the object to the +Array+, setting its parent in
|
|
10
|
+
# the process.
|
|
11
|
+
def <<(*objects)
|
|
12
|
+
objects.flatten.each do |object|
|
|
13
|
+
object.parentize(@parent, @association_name)
|
|
14
|
+
@target << object
|
|
15
|
+
object.notify
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
alias :concat :<<
|
|
20
|
+
alias :push :<<
|
|
21
|
+
|
|
22
|
+
# Clears the association, and notifies the parents of the removal.
|
|
23
|
+
def clear
|
|
24
|
+
unless @target.empty?
|
|
25
|
+
object = @target.first
|
|
26
|
+
object.changed(true)
|
|
27
|
+
object.notify_observers(object, true)
|
|
28
|
+
@target.clear
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Builds a new Document and adds it to the association collection. The
|
|
33
|
+
# document created will be of the same class as the others in the
|
|
34
|
+
# association, and the attributes will be passed into the constructor.
|
|
35
|
+
#
|
|
36
|
+
# Returns:
|
|
37
|
+
#
|
|
38
|
+
# The newly created Document.
|
|
39
|
+
def build(attrs = {}, type = nil)
|
|
40
|
+
object = type ? type.instantiate : @klass.instantiate
|
|
41
|
+
object.parentize(@parent, @association_name)
|
|
42
|
+
object.write_attributes(attrs)
|
|
43
|
+
@target << object
|
|
44
|
+
object
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Creates a new Document and adds it to the association collection. The
|
|
48
|
+
# document created will be of the same class as the others in the
|
|
49
|
+
# association, and the attributes will be passed into the constructor and
|
|
50
|
+
# the new object will then be saved.
|
|
51
|
+
#
|
|
52
|
+
# Returns:
|
|
53
|
+
#
|
|
54
|
+
# Rhe newly created Document.
|
|
55
|
+
def create(attrs = {}, type = nil)
|
|
56
|
+
object = build(attrs, type)
|
|
57
|
+
object.save
|
|
58
|
+
object
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Finds a document in this association.
|
|
62
|
+
#
|
|
63
|
+
# If :all is passed, returns all the documents
|
|
64
|
+
#
|
|
65
|
+
# If an id is passed, will return the document for that id.
|
|
66
|
+
#
|
|
67
|
+
# Returns:
|
|
68
|
+
#
|
|
69
|
+
# Array or single Document.
|
|
70
|
+
def find(param)
|
|
71
|
+
return @target if param == :all
|
|
72
|
+
return detect { |document| document.id == param }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Creates the new association by finding the attributes in
|
|
76
|
+
# the parent document with its name, and instantiating a
|
|
77
|
+
# new document for each one found. These will then be put in an
|
|
78
|
+
# internal array.
|
|
79
|
+
#
|
|
80
|
+
# This then delegated all methods to the array class since this is
|
|
81
|
+
# essentially a proxy to an array itself.
|
|
82
|
+
#
|
|
83
|
+
# Options:
|
|
84
|
+
#
|
|
85
|
+
# parent: The parent document to the association.
|
|
86
|
+
# options: The association options.
|
|
87
|
+
def initialize(parent, options)
|
|
88
|
+
@parent, @association_name = parent, options.name
|
|
89
|
+
@klass, @options = options.klass, options
|
|
90
|
+
initialize_each(parent.raw_attributes[@association_name])
|
|
91
|
+
extends(options)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# If the target array does not respond to the supplied method then try to
|
|
95
|
+
# find a named scope or criteria on the class and send the call there.
|
|
96
|
+
#
|
|
97
|
+
# If the method exists on the array, use the default proxy behavior.
|
|
98
|
+
def method_missing(name, *args, &block)
|
|
99
|
+
unless @target.respond_to?(name)
|
|
100
|
+
object = @klass.send(name, *args)
|
|
101
|
+
object.documents = @target
|
|
102
|
+
return object
|
|
103
|
+
end
|
|
104
|
+
super
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Used for setting associations via a nested attributes setter from the
|
|
108
|
+
# parent +Document+.
|
|
109
|
+
#
|
|
110
|
+
# Options:
|
|
111
|
+
#
|
|
112
|
+
# attributes: A +Hash+ of integer keys and +Hash+ values.
|
|
113
|
+
#
|
|
114
|
+
# Returns:
|
|
115
|
+
#
|
|
116
|
+
# The newly build target Document.
|
|
117
|
+
def nested_build(attributes)
|
|
118
|
+
attributes.values.each do |attrs|
|
|
119
|
+
build(attrs)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Paginate the association. Will create a new criteria, set the documents
|
|
124
|
+
# on it and execute in an enumerable context.
|
|
125
|
+
#
|
|
126
|
+
# Options:
|
|
127
|
+
#
|
|
128
|
+
# options: A +Hash+ of pagination options.
|
|
129
|
+
#
|
|
130
|
+
# Returns:
|
|
131
|
+
#
|
|
132
|
+
# A +WillPaginate::Collection+.
|
|
133
|
+
def paginate(options)
|
|
134
|
+
criteria = Humanoid::Criteria.translate(@klass, options)
|
|
135
|
+
criteria.documents = @target
|
|
136
|
+
criteria.paginate
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
protected
|
|
140
|
+
# Initializes each of the attributes in the hash.
|
|
141
|
+
def initialize_each(attributes)
|
|
142
|
+
@target = attributes ? attributes.collect do |attrs|
|
|
143
|
+
klass = attrs.klass
|
|
144
|
+
child = klass ? klass.instantiate(attrs) : @klass.instantiate(attrs)
|
|
145
|
+
child.parentize(@parent, @association_name)
|
|
146
|
+
child
|
|
147
|
+
end : []
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
class << self
|
|
151
|
+
|
|
152
|
+
# Preferred method of creating a new +HasMany+ association. It will
|
|
153
|
+
# delegate to new.
|
|
154
|
+
#
|
|
155
|
+
# Options:
|
|
156
|
+
#
|
|
157
|
+
# document: The parent +Document+
|
|
158
|
+
# options: The association options
|
|
159
|
+
def instantiate(document, options)
|
|
160
|
+
new(document, options)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Returns the macro used to create the association.
|
|
164
|
+
def macro
|
|
165
|
+
:has_many
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Perform an update of the relationship of the parent and child. This
|
|
169
|
+
# is initialized by setting the has_many to the supplied +Enumerable+
|
|
170
|
+
# and setting up the parentization.
|
|
171
|
+
def update(children, parent, options)
|
|
172
|
+
parent.remove_attribute(options.name)
|
|
173
|
+
children.assimilate(parent, options)
|
|
174
|
+
instantiate(parent, options)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|