couchobject 0.5.0 → 0.6.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.
Files changed (63) hide show
  1. data/History.txt +10 -0
  2. data/Manifest.txt +30 -6
  3. data/README.txt +580 -42
  4. data/TODO +2 -2
  5. data/config/hoe.rb +1 -1
  6. data/lib/couch_object.rb +7 -2
  7. data/lib/couch_object/database.rb +19 -34
  8. data/lib/couch_object/document.rb +13 -6
  9. data/lib/couch_object/error_classes.rb +110 -0
  10. data/lib/couch_object/persistable.rb +954 -36
  11. data/lib/couch_object/persistable/has_many_relations_array.rb +91 -0
  12. data/lib/couch_object/persistable/meta_classes.rb +568 -0
  13. data/lib/couch_object/persistable/overloaded_methods.rb +209 -0
  14. data/lib/couch_object/server.rb +1 -1
  15. data/lib/couch_object/utils.rb +44 -0
  16. data/lib/couch_object/version.rb +1 -1
  17. data/lib/couch_object/view.rb +129 -6
  18. data/script/console +0 -0
  19. data/script/destroy +0 -0
  20. data/script/generate +0 -0
  21. data/script/txt2html +0 -0
  22. data/spec/database_spec.rb +23 -31
  23. data/spec/database_spec.rb.orig +173 -0
  24. data/spec/document_spec.rb +21 -3
  25. data/spec/integration/database_integration_spec.rb +46 -15
  26. data/spec/integration/integration_helper.rb +3 -3
  27. data/spec/persistable/callback.rb +44 -0
  28. data/spec/persistable/callback_spec.rb +44 -0
  29. data/spec/persistable/cloning.rb +77 -0
  30. data/spec/persistable/cloning_spec.rb +77 -0
  31. data/spec/persistable/comparing_objects.rb +350 -0
  32. data/spec/persistable/comparing_objects_spec.rb +350 -0
  33. data/spec/persistable/deleting.rb +113 -0
  34. data/spec/persistable/deleting_spec.rb +113 -0
  35. data/spec/persistable/error_messages.rb +32 -0
  36. data/spec/persistable/error_messages_spec.rb +32 -0
  37. data/spec/persistable/loading.rb +339 -0
  38. data/spec/persistable/loading_spec.rb +339 -0
  39. data/spec/persistable/new_methods.rb +70 -0
  40. data/spec/persistable/new_methods_spec.rb +70 -0
  41. data/spec/persistable/persistable_helper.rb +194 -0
  42. data/spec/persistable/relations.rb +470 -0
  43. data/spec/persistable/relations_spec.rb +470 -0
  44. data/spec/persistable/saving.rb +137 -0
  45. data/spec/persistable/saving_spec.rb +137 -0
  46. data/spec/persistable/setting_storage_location.rb +65 -0
  47. data/spec/persistable/setting_storage_location_spec.rb +65 -0
  48. data/spec/persistable/timestamps.rb +76 -0
  49. data/spec/persistable/timestamps_spec.rb +76 -0
  50. data/spec/persistable/unsaved_changes.rb +211 -0
  51. data/spec/persistable/unsaved_changes_spec.rb +211 -0
  52. data/spec/server_spec.rb +5 -5
  53. data/spec/utils_spec.rb +60 -0
  54. data/spec/view_spec.rb +40 -7
  55. data/website/index.html +22 -7
  56. data/website/index.txt +13 -5
  57. metadata +93 -61
  58. data/bin/couch_ruby_view_requestor +0 -81
  59. data/lib/couch_object/model.rb +0 -5
  60. data/lib/couch_object/proc_condition.rb +0 -14
  61. data/spec/model_spec.rb +0 -5
  62. data/spec/persistable_spec.rb +0 -91
  63. data/spec/proc_condition_spec.rb +0 -26
@@ -0,0 +1,91 @@
1
+ module CouchObject
2
+ module Persistable
3
+ #
4
+ # This class is used to easily be able to overwrite
5
+ # functionality in Arrays that enable messaging between classes
6
+ # that are related
7
+ #
8
+ class HasManyRelation < Array
9
+ def initialize(owner)
10
+ @owner = owner
11
+ @call_back_on_add = true
12
+ super()
13
+ end
14
+
15
+ # This method is needed so that HasManyRelations can be
16
+ # compared more resonably :)
17
+ def to_s
18
+ containing_ids = self.map {|v| [v.id, v.revision]}
19
+ "<HasManyRelation:#{containing_ids.sort.join(",")}>"
20
+ end
21
+
22
+ # When adding objects from the load relations method the call back
23
+ # shouldn't be made
24
+ def disable_call_back_on_add; @call_back_on_add = false; end
25
+ def enable_call_back_on_add; @call_back_on_add = true; end
26
+
27
+ def <<(other_object)
28
+ # Makes sure there is not more than one reference for each relation
29
+ return self if self.include?(other_object)
30
+
31
+ # Add the new object
32
+ super(other_object)
33
+
34
+ # We have to find the matching local belongs_to relationsship
35
+ belongs_to_relationship_name = \
36
+ @owner.define_relationship_name(other_object)
37
+
38
+
39
+ if @call_back_on_add
40
+ # Perform a callback to the object so it sets it's
41
+ # belongs_to relationsship
42
+
43
+ method_to_call = (belongs_to_relationship_name + "=").to_sym
44
+
45
+ unless other_object.send(belongs_to_relationship_name) == @owner
46
+ other_object.send(method_to_call, @owner)
47
+ end
48
+ else
49
+ # Performs a semi callback to the object so it sets it's
50
+ # belongs_to relationsship. This semi callback doesn't perform
51
+ # any callbacks itself
52
+
53
+ method_to_call = (belongs_to_relationship_name + \
54
+ "_without_call_back=").to_sym
55
+
56
+ other_object.send(method_to_call, @owner)
57
+
58
+ end
59
+
60
+ self
61
+
62
+ end
63
+
64
+ alias original_delete delete
65
+ def delete(object_to_delete)
66
+ remove(object_to_delete)
67
+
68
+ # Issue a delete statement for the object
69
+ object_to_delete.send(:delete)
70
+ end
71
+
72
+ def remove(object_to_remove)
73
+ remove_callback(object_to_remove)
74
+ perform_remove(object_to_remove)
75
+ end
76
+
77
+ def perform_remove(object_to_remove)
78
+ original_delete(object_to_remove)
79
+ end
80
+
81
+ def remove_callback(other_object)
82
+ method_to_call = (@owner.define_relationship_name(other_object) + \
83
+ "_without_call_back=").to_sym
84
+ other_object.send(method_to_call, nil)
85
+ end
86
+
87
+
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,568 @@
1
+ module CouchObject
2
+ module Persistable
3
+ def self.included(klazz)
4
+ klazz.extend(ClassMethods)
5
+
6
+ #
7
+ # Using meta programming methods for handling, amongst others,
8
+ # * setting of the database uri at design time
9
+ # * including timestamps
10
+ # * managing relations
11
+ # are created
12
+ #
13
+ klazz.class_eval do
14
+
15
+ ##
16
+ # Timestamps
17
+ ##
18
+
19
+ # Timestamps are false by default
20
+ def self.couch_object_timestamp_on_update?; false; end
21
+ def self.couch_object_timestamp_on_create?; false; end
22
+
23
+ #
24
+ # Adds timestamps to the class.
25
+ #
26
+ # Example:
27
+ #
28
+ # class Vacation
29
+ # include CouchObject::Persistable
30
+ # add_timestamp_for :on_create, :on_update
31
+ # end
32
+ #
33
+ # my_vacation = Vacation.new
34
+ # my_vacation.save(db_address)
35
+ # my_vacation.created_at => Somedate
36
+ # my_vacation.updated_at => Somedate
37
+ #
38
+ def self.add_timestamp_for(*timestamp_actions)
39
+ timestamp_actions.each do |action|
40
+ case action
41
+ when :on_create
42
+ self.class_eval do
43
+ def self.couch_object_timestamp_on_create?; true; end
44
+ end
45
+ when :on_update
46
+ self.class_eval do
47
+ def self.couch_object_timestamp_on_update?; true; end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+
55
+ ##
56
+ # Change monitor
57
+ ##
58
+
59
+ # to be implemented later.
60
+ # could be implemented using MonitorFunctions
61
+ # (http://www.erikveen.dds.nl/monitorfunctions/)
62
+
63
+ # Each class monitors it's setters to see if it's content is changed,
64
+ # in which case a flag is set.
65
+ # For this purpose all setters are overridden
66
+
67
+ # self.instance_variable_set("@couch_has_unsaved_changes_flag", false)
68
+ # def unsaved_changes?
69
+ # @couch_has_unsaved_changes_flag
70
+ # end
71
+ # puts "Public setters:"
72
+ # self.public_methods.each do |method|
73
+ # if method.to_s[-1,1] == "="
74
+ # # We have to create an alias for the original method
75
+ # DO MAGIC HERE
76
+ # end
77
+ # end
78
+
79
+
80
+ ##
81
+ # Database storage location
82
+ ##
83
+
84
+ # Location methods are added both as instance methods and
85
+ # as class level methods. The class level methods are needed
86
+ # when loading new objects from the database and the instance
87
+ # methods are used throughout the class
88
+ def self.location; @couch_object_class_storage_location ||= nil; end
89
+ def location; @location; end
90
+ alias storage_location location
91
+
92
+ #
93
+ # Sets the location of the database to use by default
94
+ #
95
+ # Example:
96
+ #
97
+ # class AppleTree
98
+ # include CouchObject::Persistable
99
+ # database 'http://localhost:5984'
100
+ # end
101
+ #
102
+ # apple_tree = AppleTree.new
103
+ # apple_tree.save # saves automatically to the predefined
104
+ # # database location
105
+ #
106
+ def self.database(db_uri)
107
+ @couch_object_class_storage_location = db_uri
108
+ self.instance_eval do
109
+ define_method("location") do
110
+ @location ||= db_uri
111
+ end
112
+ end
113
+ end
114
+
115
+
116
+
117
+ ##
118
+ # Smart savign
119
+ ##
120
+
121
+ def use_smart_save; false; end
122
+ #
123
+ # Smart save (defaults to false), if activated, keeps a snapshot of
124
+ # the objects initial state and evaluates if the class needs to be
125
+ # saved to the database by comparing it to the snapshot when a save
126
+ # is requested.
127
+ #
128
+ # Please notice:
129
+ # Only activate this feature in cases where it is needed.
130
+ # It might slow down the performance of your app if you activate it
131
+ # for classes that you need many instances of and that you won't
132
+ # call the save method on after having loaded them from the database.
133
+ # Please also bare in mind that the class instance will store an
134
+ # extra copy of its contents which will lead to quite a big memory
135
+ # overhead for classes that store a lot of data!
136
+ #
137
+ def self.smart_save
138
+ self.instance_eval do
139
+ define_method("use_smart_save") do
140
+ true
141
+ end
142
+ end
143
+ end
144
+ #
145
+ # Smart save can also be used on a per-case basis if it is sometimes
146
+ # needed and sometimes not.
147
+ #
148
+ # Example:
149
+ #
150
+ # user_without_smart_save_1 = User.get("foo")
151
+ # User.smart_save
152
+ # user_with_smart_save = User.get("bar")
153
+ # User.deactivate_smart_save
154
+ # user_without_smart_save_2 = User.get("bong")
155
+ #
156
+ def self.deactivate_smart_save
157
+ self.instance_eval do
158
+ define_method("use_smart_save") do
159
+ false
160
+ end
161
+ end
162
+ end
163
+
164
+
165
+
166
+ ##
167
+ # Relations
168
+ ##
169
+
170
+ # Default values for has_many, belongs_to and belongs_to_as
171
+ def has_many; []; end
172
+ def has_one; []; end
173
+ def has; []; end
174
+ def belongs_to; []; end
175
+
176
+ #
177
+ # Defines a has_many relation which then again
178
+ # needs a corresponding belongs_to relation in the
179
+ # classes the relation is made with (see the documentation
180
+ # of belongs_to below)
181
+ #
182
+ # Takes:
183
+ # * a symbol indicating the name of the association.
184
+ # The association name can be freely chosen.
185
+ #
186
+ # Example:
187
+ #
188
+ # has_many :fruits
189
+ #
190
+ # Requires a belongs_to relation from the other part. F.ex:
191
+ #
192
+ # belongs_to: :fruit_basket, :as => :fruits
193
+ #
194
+ # Raises:
195
+ # * HasManyAssociationError if the association name
196
+ # is left blank.
197
+ #
198
+ def self.has_many(what_it_has = nil)
199
+ raise CouchObject::Errors::HasManyAssociationError if what_it_has == nil
200
+
201
+ @couch_object_has_many ||= []
202
+ @couch_object_has_many << what_it_has unless \
203
+ @couch_object_has_many.include?(what_it_has)
204
+
205
+ self.instance_eval do
206
+ # The objects are stored in this variable
207
+ has_many_object_variable = \
208
+ "@couch_object_#{what_it_has.to_s}"
209
+
210
+ #
211
+ # Getter which also works as a setter because:
212
+ # * it returns the array that contains the references
213
+ # * when a new relationship is added using << the action
214
+ # it performed by the array, and not self
215
+ #
216
+ define_method(what_it_has.to_s) do
217
+ eval("#{has_many_object_variable}.nil? ? " + \
218
+ "#{has_many_object_variable} = " + \
219
+ "couch_load_has_many_relations(\"#{what_it_has}\") : " + \
220
+ "#{has_many_object_variable}")
221
+ end
222
+
223
+ #
224
+ # Returns:
225
+ # * the name of the relation
226
+ #
227
+ # Example:
228
+ #
229
+ # apple_tree.has_many => :fruits
230
+ # apple_tree.fruits => [apple1, apple2]
231
+ #
232
+ all_the_things_it_has = @couch_object_has_many
233
+ define_method("has_many") do
234
+ # Filtering out the has_one relations so they don't show up
235
+ what_is_has_output = []
236
+ self.send(:has).each do |has|
237
+ what_is_has_output << has \
238
+ unless has.to_s[0..7] == "has_one_"
239
+ end
240
+ what_is_has_output
241
+ end
242
+ define_method("has") do
243
+ all_the_things_it_has
244
+ end
245
+ end
246
+
247
+ end
248
+
249
+ #
250
+ # Defines a belongs_to relation which then again
251
+ # needs a corresponding has_many relation in the
252
+ # class the relation is made with (see the documentation
253
+ # of has_many above)
254
+ #
255
+ # Takes:
256
+ # * a symbol indicating the name of the association.
257
+ # The association name can be freely chosen.
258
+ # * a symbol that indicates the name the corresponding has_many
259
+ # relationship in the owner class
260
+ #
261
+ # Example:
262
+ #
263
+ # belongs_to :fruit_basket, :as => :fruits
264
+ #
265
+ # Requires a has_many relation from the other class that looks
266
+ # something like this:
267
+ #
268
+ # has_many :fruits
269
+ #
270
+ # Raises:
271
+ # * BelongsToAssociationError if the association name,
272
+ # or the :as parameter is left blank.
273
+ #
274
+ def self.belongs_to(what_it_belongs_to = nil, as = nil)
275
+ raise CouchObject::Errors::BelongsToAssociationError \
276
+ if what_it_belongs_to.nil? || as.nil?
277
+
278
+ @couch_object_what_it_belongs_to ||= []
279
+ @couch_object_what_it_belongs_to << what_it_belongs_to unless \
280
+ @couch_object_what_it_belongs_to.include?(what_it_belongs_to)
281
+
282
+ self.instance_eval do
283
+ # The object are stored in this variable
284
+ belongs_to_object_variable = \
285
+ "@couch_object_#{what_it_belongs_to.to_s}"
286
+
287
+ # Getter
288
+ define_method(what_it_belongs_to.to_s) do
289
+ eval("#{belongs_to_object_variable} ||= " + \
290
+ " couch_load_belongs_to_relation(\"#{as[:as]}\")")
291
+ end
292
+
293
+ # Setter
294
+ define_method("#{what_it_belongs_to.to_s}=") do |object_to_add|
295
+ # The first thing we have to do is to check if it is in
296
+ # a has_one or has_many relationship!
297
+ if object_to_add.respond_to?("has_one_#{as[:as]}") || \
298
+ eval("#{belongs_to_object_variable}" + \
299
+ ".respond_to?(:has_one_#{as[:as]})")
300
+ is_a_has_many_relationship = false
301
+ else
302
+ is_a_has_many_relationship = true
303
+ end
304
+
305
+ # Now... there is no good reason loading a belongs_to relation
306
+ # from the database only to remove the relation with the child,
307
+ # because the relation is stored in the child anyway...
308
+ # We therefore temporarily deactivate the loading of belongs_to
309
+ # relations
310
+ original_state_of_load_belongs_to_relations = \
311
+ @do_not_load_belongs_to_relations
312
+ @do_not_load_belongs_to_relations = true
313
+
314
+ if is_a_has_many_relationship
315
+
316
+ # Remove the original relationship in the master object
317
+ eval("#{belongs_to_object_variable}." \
318
+ + "send(:end_relationsship_with, self, \"#{as[:as]}\" ) " \
319
+ + "unless #{belongs_to_object_variable} == nil")
320
+
321
+ # Sets the new relation
322
+ instance_variable_set("#{belongs_to_object_variable}", \
323
+ object_to_add)
324
+
325
+ # Set up the new relationship with the master object
326
+ self.add_relation_to_master(as[:as]) if object_to_add
327
+
328
+ else
329
+
330
+ # Remove old relationship and set the new one
331
+ self.set_has_one_relation_to_master(as[:as], nil)
332
+
333
+ # Sets the new relation
334
+ instance_variable_set("#{belongs_to_object_variable}", \
335
+ object_to_add)
336
+
337
+ # Setup the new relationship
338
+ self.set_has_one_relation_to_master(as[:as], self) \
339
+ if object_to_add
340
+
341
+ end
342
+
343
+ # And now we reset the @do_not_load_belongs_to_relations
344
+ # variable to its original value:
345
+ @do_not_load_belongs_to_relations = \
346
+ original_state_of_load_belongs_to_relations
347
+
348
+ end
349
+
350
+ # Setter without callback for new objects
351
+ # from the load relations method
352
+ define_method("#{what_it_belongs_to.to_s}" + \
353
+ "_without_call_back=") do |object_to_add|
354
+
355
+ # Sets the new relation
356
+ instance_variable_set("#{belongs_to_object_variable}", \
357
+ object_to_add)
358
+ end
359
+
360
+ #
361
+ # Returns:
362
+ # * the getter for a belongs_to relationship as a symbol
363
+ #
364
+ # Example:
365
+ #
366
+ # fruit.belongs_to => :tree
367
+ # fruit.tree => <AppleTree>
368
+ #
369
+ return_value_for_function = @couch_object_what_it_belongs_to
370
+ define_method("belongs_to") do
371
+ return_value_for_function
372
+ end
373
+
374
+ #
375
+ # Returns:
376
+ # * what the corresponding has_many relation is called
377
+ #
378
+ # Example:
379
+ #
380
+ # fruit.belongs_to => :tree
381
+ # fruit.tree = apple_tree
382
+ # fruit.belongs_to_as => :fruits
383
+ # apple_tree.fruits => [fruit]
384
+ #
385
+ define_method("belongs_to_#{what_it_belongs_to}_as") do
386
+ as[:as]
387
+ end
388
+
389
+ end
390
+
391
+ end
392
+
393
+ #
394
+ # has_one relations are added as a layer to the has_many
395
+ # There is created a has_many relation ship but getters and setters
396
+ # for the has_one relationship on top of that that interact with the
397
+ # has_many relationship.
398
+ def self.has_one(what_it_has = nil)
399
+ raise CouchObject::Errors::HasOneAssociationError if what_it_has == nil
400
+
401
+ related_has_many_relationship = "has_one_#{what_it_has.to_s}".to_sym
402
+
403
+ # Create the has_many relationship
404
+ self.send(:has_many, related_has_many_relationship)
405
+
406
+ # Create methods to get and set the relationship
407
+
408
+ # getter
409
+ define_method(what_it_has) do
410
+ self.send(related_has_many_relationship).first
411
+ end
412
+
413
+ define_method("#{what_it_has}=") do |new_relation|
414
+ # Remove the original relation
415
+ self.send(related_has_many_relationship).
416
+ remove(self.send(related_has_many_relationship).first) \
417
+ unless self.send(related_has_many_relationship) == []
418
+
419
+ # Disable callbacks
420
+ self.send(related_has_many_relationship).disable_call_back_on_add
421
+
422
+ if new_relation
423
+
424
+ # Create the new
425
+ self.send(related_has_many_relationship) << new_relation \
426
+ unless new_relation == nil
427
+
428
+ # Set the relationship in the child
429
+ what_it_belongs_to = define_relationship_name(new_relation)
430
+
431
+ new_relation.
432
+ send("#{what_it_belongs_to}_without_call_back=", self)
433
+
434
+ end
435
+
436
+ # Reenable callbacks
437
+ self.send(related_has_many_relationship).enable_call_back_on_add
438
+
439
+ end
440
+
441
+ define_method("has_one") do
442
+ what_is_has_output = []
443
+ self.send(:has).each do |has|
444
+ what_is_has_output << has.to_s[8..-1].to_sym \
445
+ if has.to_s[0..7] == "has_one_"
446
+ end
447
+ what_is_has_output
448
+ end
449
+
450
+ end
451
+
452
+ #
453
+ # Returns the name of the relation in itself matching one of the
454
+ # relations in the other object
455
+ #
456
+ # Example:
457
+ # other_object has defined the relationships:
458
+ # belongs_to :house, :as => :houses
459
+ # belongs_to :humanity
460
+ #
461
+ # self has the relation
462
+ # has_many :houses
463
+ #
464
+ # :houses is returned
465
+ #
466
+ def define_relationship_name(other_object)
467
+
468
+ belongs_to_relationship_name = nil
469
+
470
+ other_object.send(:belongs_to).each do |what_it_belongs_to|
471
+
472
+ name_of_relation_in_master = other_object.
473
+ send("belongs_to_#{what_it_belongs_to}_as".to_sym)
474
+
475
+ return what_it_belongs_to.to_s \
476
+ if self.respond_to?(name_of_relation_in_master)
477
+
478
+ end
479
+
480
+ # There couldn't be found a match... raising an error
481
+ raise "The master class #{self} doesn't have a relation" + \
482
+ " matching the relation defined in the child class " \
483
+ if belongs_to_relationship_name == nil
484
+
485
+ end
486
+
487
+ #
488
+ # This method is called from the method that assigns a
489
+ # belongs_to relation to inform the master object of the relation
490
+ # (has_many relations)
491
+ #
492
+ def add_relation_to_master(relation_name)
493
+
494
+ if master_class = get_master_for_relation(relation_name)
495
+
496
+ masters_objects_relations = \
497
+ master_class.send(relation_name)
498
+
499
+ if masters_objects_relations == []
500
+ masters_objects_relations << self
501
+ else
502
+ unless masters_objects_relations.include?(self)
503
+ masters_objects_relations << self
504
+ end
505
+ end
506
+
507
+ end
508
+
509
+ end
510
+
511
+ #
512
+ # This method is called from the method that assigns a
513
+ # belongs_to relation to inform the master object of the relation
514
+ # (has_one relations)
515
+ #
516
+ def set_has_one_relation_to_master(relation_name, to_what)
517
+
518
+ if master_class = get_master_for_relation(relation_name)
519
+
520
+ # set up the new relationship in the master
521
+ master_class.send("#{relation_name}=", to_what)
522
+
523
+ end
524
+
525
+ end
526
+
527
+ #
528
+ # This method is called from the method that assigns a
529
+ # belongs_to relation to inform the previous master object
530
+ # that the relation ship has ended
531
+ # (has_one relations)
532
+ #
533
+ def end_has_one_relation_to_master(relation_name)
534
+
535
+ set_has_one_relation_to_master(relation_name, nil)
536
+
537
+ end
538
+
539
+
540
+ def get_master_for_relation(relation_name)
541
+
542
+ accessor_for_what_it_belongs_to = nil
543
+ self.send(:belongs_to).each do |what_it_belongs_to|
544
+ # Only load the belongs to relation that is needed
545
+ # We therefore have to find out which of the relations to use
546
+ find_string = "belongs_to_#{what_it_belongs_to}_as"
547
+ accessor_for_what_it_belongs_to = what_it_belongs_to \
548
+ if self.send(find_string) == relation_name
549
+ end
550
+
551
+ return nil if accessor_for_what_it_belongs_to == nil
552
+
553
+ master_class = self.send(accessor_for_what_it_belongs_to)
554
+ return nil if master_class == nil
555
+
556
+ raise "The master class doesn't have a matching relation " + \
557
+ "defined" unless master_class.respond_to?(relation_name)
558
+
559
+ return master_class
560
+
561
+ end
562
+
563
+
564
+ end
565
+ end
566
+
567
+ end
568
+ end