massive_record 0.2.1 → 0.2.2.rc1

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 (135) hide show
  1. data/CHANGELOG.md +58 -2
  2. data/Gemfile.lock +17 -17
  3. data/README.md +98 -41
  4. data/lib/massive_record.rb +2 -1
  5. data/lib/massive_record/adapters/thrift/hbase/hbase.rb +2425 -2154
  6. data/lib/massive_record/adapters/thrift/hbase/hbase_constants.rb +3 -3
  7. data/lib/massive_record/adapters/thrift/hbase/hbase_types.rb +195 -195
  8. data/lib/massive_record/adapters/thrift/row.rb +35 -4
  9. data/lib/massive_record/adapters/thrift/table.rb +49 -12
  10. data/lib/massive_record/orm/attribute_methods.rb +77 -5
  11. data/lib/massive_record/orm/attribute_methods/cast_numbers_on_write.rb +24 -0
  12. data/lib/massive_record/orm/attribute_methods/dirty.rb +18 -0
  13. data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +24 -3
  14. data/lib/massive_record/orm/attribute_methods/write.rb +8 -1
  15. data/lib/massive_record/orm/base.rb +62 -8
  16. data/lib/massive_record/orm/column.rb +7 -11
  17. data/lib/massive_record/orm/default_id.rb +1 -1
  18. data/lib/massive_record/orm/embedded.rb +66 -0
  19. data/lib/massive_record/orm/errors.rb +17 -0
  20. data/lib/massive_record/orm/finders.rb +124 -71
  21. data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +1 -1
  22. data/lib/massive_record/orm/finders/scope.rb +58 -34
  23. data/lib/massive_record/orm/id_factory.rb +22 -105
  24. data/lib/massive_record/orm/id_factory/atomic_incrementation.rb +117 -0
  25. data/lib/massive_record/orm/id_factory/timestamp.rb +60 -0
  26. data/lib/massive_record/orm/identity_map.rb +256 -0
  27. data/lib/massive_record/orm/log_subscriber.rb +18 -0
  28. data/lib/massive_record/orm/observer.rb +69 -0
  29. data/lib/massive_record/orm/persistence.rb +47 -119
  30. data/lib/massive_record/orm/persistence/operations.rb +100 -0
  31. data/lib/massive_record/orm/persistence/operations/atomic_operation.rb +71 -0
  32. data/lib/massive_record/orm/persistence/operations/destroy.rb +17 -0
  33. data/lib/massive_record/orm/persistence/operations/embedded/destroy.rb +26 -0
  34. data/lib/massive_record/orm/persistence/operations/embedded/insert.rb +27 -0
  35. data/lib/massive_record/orm/persistence/operations/embedded/operation_helpers.rb +66 -0
  36. data/lib/massive_record/orm/persistence/operations/embedded/reload.rb +39 -0
  37. data/lib/massive_record/orm/persistence/operations/embedded/update.rb +29 -0
  38. data/lib/massive_record/orm/persistence/operations/insert.rb +19 -0
  39. data/lib/massive_record/orm/persistence/operations/reload.rb +26 -0
  40. data/lib/massive_record/orm/persistence/operations/suppress.rb +15 -0
  41. data/lib/massive_record/orm/persistence/operations/table_operation_helpers.rb +106 -0
  42. data/lib/massive_record/orm/persistence/operations/update.rb +25 -0
  43. data/lib/massive_record/orm/query_instrumentation.rb +26 -49
  44. data/lib/massive_record/orm/raw_data.rb +47 -0
  45. data/lib/massive_record/orm/relations.rb +4 -0
  46. data/lib/massive_record/orm/relations/interface.rb +134 -0
  47. data/lib/massive_record/orm/relations/metadata.rb +58 -12
  48. data/lib/massive_record/orm/relations/proxy.rb +17 -12
  49. data/lib/massive_record/orm/relations/proxy/embedded_in.rb +54 -0
  50. data/lib/massive_record/orm/relations/proxy/embedded_in_polymorphic.rb +15 -0
  51. data/lib/massive_record/orm/relations/proxy/embeds_many.rb +215 -0
  52. data/lib/massive_record/orm/relations/proxy/references_many.rb +112 -88
  53. data/lib/massive_record/orm/relations/proxy/references_one.rb +1 -1
  54. data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
  55. data/lib/massive_record/orm/relations/proxy_collection.rb +84 -0
  56. data/lib/massive_record/orm/schema/column_family.rb +3 -2
  57. data/lib/massive_record/orm/schema/{column_interface.rb → embedded_interface.rb} +38 -4
  58. data/lib/massive_record/orm/schema/field.rb +2 -0
  59. data/lib/massive_record/orm/schema/table_interface.rb +19 -2
  60. data/lib/massive_record/orm/single_table_inheritance.rb +37 -2
  61. data/lib/massive_record/orm/timestamps.rb +17 -7
  62. data/lib/massive_record/orm/validations.rb +4 -0
  63. data/lib/massive_record/orm/validations/associated.rb +50 -0
  64. data/lib/massive_record/rails/railtie.rb +31 -0
  65. data/lib/massive_record/version.rb +1 -1
  66. data/lib/massive_record/wrapper/cell.rb +8 -1
  67. data/massive_record.gemspec +4 -4
  68. data/spec/adapter/thrift/atomic_increment_spec.rb +16 -0
  69. data/spec/adapter/thrift/table_find_spec.rb +14 -2
  70. data/spec/adapter/thrift/table_spec.rb +6 -6
  71. data/spec/adapter/thrift/utf8_encoding_of_id_spec.rb +71 -0
  72. data/spec/orm/cases/attribute_methods_spec.rb +215 -22
  73. data/spec/orm/cases/auto_generate_id_spec.rb +1 -1
  74. data/spec/orm/cases/change_id_spec.rb +62 -0
  75. data/spec/orm/cases/default_id_spec.rb +25 -6
  76. data/spec/orm/cases/default_values_spec.rb +6 -3
  77. data/spec/orm/cases/dirty_spec.rb +150 -102
  78. data/spec/orm/cases/embedded_spec.rb +250 -0
  79. data/spec/orm/cases/{finder_default_scope.rb → finder_default_scope_spec.rb} +4 -0
  80. data/spec/orm/cases/finder_scope_spec.rb +96 -29
  81. data/spec/orm/cases/finders_spec.rb +57 -10
  82. data/spec/orm/cases/id_factory/atomic_incrementation_spec.rb +72 -0
  83. data/spec/orm/cases/id_factory/timestamp_spec.rb +61 -0
  84. data/spec/orm/cases/identity_map/identity_map_spec.rb +357 -0
  85. data/spec/orm/cases/identity_map/middleware_spec.rb +74 -0
  86. data/spec/orm/cases/log_subscriber_spec.rb +15 -2
  87. data/spec/orm/cases/observing_spec.rb +61 -0
  88. data/spec/orm/cases/persistence_spec.rb +151 -60
  89. data/spec/orm/cases/raw_data_spec.rb +58 -0
  90. data/spec/orm/cases/single_table_inheritance_spec.rb +58 -2
  91. data/spec/orm/cases/table_spec.rb +3 -3
  92. data/spec/orm/cases/time_zone_awareness_spec.rb +27 -0
  93. data/spec/orm/cases/timestamps_spec.rb +23 -109
  94. data/spec/orm/cases/validation_spec.rb +9 -0
  95. data/spec/orm/models/address.rb +5 -1
  96. data/spec/orm/models/address_with_timestamp.rb +12 -0
  97. data/spec/orm/models/car.rb +5 -0
  98. data/spec/orm/models/person.rb +13 -1
  99. data/spec/orm/models/person_with_timestamp.rb +4 -2
  100. data/spec/orm/models/test_class.rb +1 -0
  101. data/spec/orm/persistence/operations/atomic_operation_spec.rb +58 -0
  102. data/spec/orm/persistence/operations/destroy_spec.rb +22 -0
  103. data/spec/orm/persistence/operations/embedded/destroy_spec.rb +71 -0
  104. data/spec/orm/persistence/operations/embedded/insert_spec.rb +59 -0
  105. data/spec/orm/persistence/operations/embedded/operation_helpers_spec.rb +92 -0
  106. data/spec/orm/persistence/operations/embedded/reload_spec.rb +67 -0
  107. data/spec/orm/persistence/operations/embedded/update_spec.rb +60 -0
  108. data/spec/orm/persistence/operations/insert_spec.rb +31 -0
  109. data/spec/orm/persistence/operations/reload_spec.rb +48 -0
  110. data/spec/orm/persistence/operations/suppress_spec.rb +17 -0
  111. data/spec/orm/persistence/operations/table_operation_helpers_spec.rb +98 -0
  112. data/spec/orm/persistence/operations/update_spec.rb +25 -0
  113. data/spec/orm/persistence/operations_spec.rb +58 -0
  114. data/spec/orm/relations/interface_spec.rb +188 -0
  115. data/spec/orm/relations/metadata_spec.rb +92 -15
  116. data/spec/orm/relations/proxy/embedded_in_polymorphic_spec.rb +37 -0
  117. data/spec/orm/relations/proxy/embedded_in_spec.rb +66 -0
  118. data/spec/orm/relations/proxy/embeds_many_spec.rb +651 -0
  119. data/spec/orm/relations/proxy/references_many_spec.rb +466 -2
  120. data/spec/orm/schema/column_family_spec.rb +21 -0
  121. data/spec/orm/schema/embedded_interface_spec.rb +181 -0
  122. data/spec/orm/schema/field_spec.rb +7 -0
  123. data/spec/orm/schema/table_interface_spec.rb +31 -1
  124. data/spec/shared/orm/id_factories.rb +44 -0
  125. data/spec/shared/orm/model_with_timestamps.rb +132 -0
  126. data/spec/shared/orm/persistence/a_persistence_embedded_operation_class.rb +3 -0
  127. data/spec/shared/orm/persistence/a_persistence_operation_class.rb +11 -0
  128. data/spec/shared/orm/persistence/a_persistence_table_operation_class.rb +11 -0
  129. data/spec/shared/orm/relations/proxy.rb +9 -2
  130. data/spec/spec_helper.rb +9 -0
  131. data/spec/support/mock_massive_record_connection.rb +2 -1
  132. metadata +106 -21
  133. data/spec/orm/cases/column_spec.rb +0 -49
  134. data/spec/orm/cases/id_factory_spec.rb +0 -92
  135. data/spec/orm/schema/column_interface_spec.rb +0 -136
@@ -0,0 +1,47 @@
1
+ module MassiveRecord
2
+ module ORM
3
+
4
+ #
5
+ # Class to hold one raw data, and some meta data for that value.
6
+ # As of writing this the meta data is when cell which the data
7
+ # originates from was last written to (created_at).
8
+ #
9
+ class RawData
10
+ attr_reader :value, :created_at
11
+
12
+
13
+ class << self
14
+ def new_with_data_from(object)
15
+ send("new_with_data_from_#{object.class.to_s.demodulize.underscore}", object)
16
+ end
17
+
18
+
19
+ private
20
+
21
+ def new_with_data_from_cell(cell)
22
+ new(value: cell.value, created_at: cell.created_at)
23
+ end
24
+ end
25
+
26
+
27
+
28
+ def initialize(attributes)
29
+ @value = attributes[:value]
30
+ @created_at = attributes[:created_at]
31
+ end
32
+
33
+ delegate :inspect, :to_s, :to => :value
34
+
35
+
36
+ def ==(other)
37
+ other.equal?(self) ||
38
+ other.instance_of?(self.class) && value == other.value && created_at == other.created_at
39
+ end
40
+ alias_method :eql?, :==
41
+
42
+ def hash
43
+ [id, created_at].hash
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,6 +1,10 @@
1
1
  require 'massive_record/orm/relations/proxy'
2
+ require 'massive_record/orm/relations/proxy_collection'
2
3
  require 'massive_record/orm/relations/proxy/references_one'
3
4
  require 'massive_record/orm/relations/proxy/references_one_polymorphic'
4
5
  require 'massive_record/orm/relations/proxy/references_many'
6
+ require 'massive_record/orm/relations/proxy/embeds_many'
7
+ require 'massive_record/orm/relations/proxy/embedded_in'
8
+ require 'massive_record/orm/relations/proxy/embedded_in_polymorphic'
5
9
  require 'massive_record/orm/relations/metadata'
6
10
  require 'massive_record/orm/relations/interface'
@@ -92,6 +92,74 @@ module MassiveRecord
92
92
  create_references_many_accessors(metadata)
93
93
  end
94
94
 
95
+
96
+
97
+ #
98
+ # Used to defined a relationship to other models where the other models are embedded inside of owner record.
99
+ #
100
+ # class Person < MassiveRecord::ORM::Table
101
+ # embeds_many :addresses
102
+ # end
103
+ #
104
+ #
105
+ # The embeds many association gets one column family per association. embeds_many :addresses
106
+ # will by default be stored in the addresses column family. You can however do this:
107
+ # embeds_many :addresses, :store_in => :base to manipulate the column family it is stored within.
108
+ #
109
+ # Embedded records gets a composite key consisting of base_class and the record's id. This is how
110
+ # it is possible to mix embedded collection in to one column family / existing family with "normal" attributes.
111
+ # Please mind however, doing such a mixing might get you into trouble if you have attribute names which looks
112
+ # like an embedded address key. Companybook wanted this option, as they said haveing multiple column family might
113
+ # slow down Hbase.
114
+ #
115
+ # Attributes will be serialized by the Base.coder, by default will be JSON, but it really can be anything.
116
+ # The way records are stored inside of a column family will be:
117
+ #
118
+ # | key | attributes |
119
+ # ---------------------------------------------------------------------
120
+ # | "address|id1" | { :street => "Askerveien", :number => "12", etc... }
121
+ #
122
+ #
123
+ #
124
+ # Options, all optional:
125
+ #
126
+ # <tt>class_name</tt>:: Class name is calculated from name, but can be overridden here.
127
+ # <tt>store_in</tt>:: Send in the column family to store foreign key in. If none given,
128
+ #
129
+ def embeds_many(name, *args)
130
+ metadata = set_up_relation('embeds_many', name, *args)
131
+ metadata.owner_class = self
132
+ add_column_family(metadata.store_in)
133
+ create_embeds_many_accessors(metadata)
134
+ end
135
+
136
+
137
+
138
+ #
139
+ # Embedded in is being used together with embeds_many on the other side
140
+ # of such a relation.
141
+ #
142
+ # class Address < MassiveRecord::ORM::Embedded
143
+ # embedded_in :person
144
+ # end
145
+ #
146
+ # You can also pass in :polymorphic => true as an option. If you do so here is an example:
147
+ #
148
+ # class Person < MassiveRecord::ORM::Table
149
+ # embeds_many :addresses, :inverse_of => :addressable
150
+ # end
151
+ #
152
+ # class Address < MassiveRecord::ORM::Embedded
153
+ # embedded_in :addressable, :inverse_of => :addresses, :polymorphic => true
154
+ # end
155
+ #
156
+ def embedded_in(name, *args)
157
+ metadata = set_up_relation('embedded_in', name, *args)
158
+ metadata.owner_class = self
159
+ create_embedded_in_accessors(metadata)
160
+ end
161
+
162
+
95
163
  private
96
164
 
97
165
  def set_up_relation(type, name, *args)
@@ -142,6 +210,29 @@ module MassiveRecord
142
210
  add_field_to_column_family(metadata.store_in, metadata.foreign_key, :type => :array, :allow_nil => false)
143
211
  end
144
212
  end
213
+
214
+
215
+ def create_embeds_many_accessors(metadata)
216
+ validates_associated metadata.name
217
+
218
+ redefine_method(metadata.name) do
219
+ relation_proxy(metadata.name)
220
+ end
221
+
222
+ redefine_method(metadata.name+'=') do |records|
223
+ relation_proxy(metadata.name).replace(records)
224
+ end
225
+ end
226
+
227
+ def create_embedded_in_accessors(metadata)
228
+ redefine_method(metadata.name) do
229
+ relation_proxy(metadata.name)
230
+ end
231
+
232
+ redefine_method(metadata.name+'=') do |record|
233
+ relation_proxy(metadata.name).replace(record)
234
+ end
235
+ end
145
236
  end
146
237
 
147
238
 
@@ -151,9 +242,52 @@ module MassiveRecord
151
242
  super
152
243
  end
153
244
 
245
+ def attributes=(attributes)
246
+ attributes_for_relations = {}
247
+
248
+ if relations && attributes.is_a?(Hash)
249
+ attributes.stringify_keys!
250
+ relation_names = relations.collect(&:name)
251
+
252
+ attributes.delete_if do |attr_name, value|
253
+ attributes_for_relations[attr_name] = value if relation_names.include? attr_name
254
+ end
255
+ end
256
+
257
+ super(attributes)
258
+ super(attributes_for_relations)
259
+ end
260
+
261
+
262
+ def relation_proxies
263
+ (relations || []).map { |metadata| relation_proxy(metadata.name) }
264
+ end
265
+
266
+ def relation_proxies_for_embedded
267
+ (relations || []).select(&:embedded?).map { |metadata| relation_proxy(metadata.name) }
268
+ end
269
+
154
270
 
155
271
  private
156
272
 
273
+ def create
274
+ embedded_relations = relation_proxies_for_embedded.select(&:changed?)
275
+ embedded_relations.each(&:parent_will_be_saved!)
276
+
277
+ super
278
+
279
+ embedded_relations.each(&:parent_has_been_saved!)
280
+ end
281
+
282
+ def update(attribute_names_to_update = attributes_with_embedded)
283
+ embedded_relations = relation_proxies_for_embedded.select(&:changed?)
284
+ embedded_relations.each(&:parent_will_be_saved!)
285
+
286
+ super
287
+
288
+ embedded_relations.each(&:parent_has_been_saved!)
289
+ end
290
+
157
291
  def relation_proxy(name)
158
292
  name = name.to_s
159
293
 
@@ -3,14 +3,17 @@ require 'active_support/core_ext/array/extract_options'
3
3
  module MassiveRecord
4
4
  module ORM
5
5
  module Relations
6
+ # Raised when an invalid start option is given to a find_in_batches
7
+ class InvalidStartsWithOption < MassiveRecordError
8
+ end
6
9
 
7
10
  #
8
11
  # The master of metadata related to a relation. For instance;
9
12
  # references_one :employee, :foreign_key => "person_id", :class_name => "Person"
10
13
  #
11
14
  class Metadata
12
- attr_writer :foreign_key, :store_in, :class_name, :name, :relation_type, :polymorphic
13
- attr_accessor :find_with
15
+ attr_writer :foreign_key, :store_in, :class_name, :name, :relation_type, :polymorphic, :inverse_of
16
+ attr_accessor :find_with, :owner_class
14
17
  attr_reader :records_starts_from
15
18
 
16
19
  def initialize(name, options = {})
@@ -26,6 +29,7 @@ module MassiveRecord
26
29
  self.find_with = options[:find_with]
27
30
  self.records_starts_from = options[:records_starts_from] if options[:records_starts_from]
28
31
  self.polymorphic = options[:polymorphic]
32
+ self.inverse_of = options[:inverse_of]
29
33
  end
30
34
 
31
35
 
@@ -62,12 +66,17 @@ module MassiveRecord
62
66
  (@class_name || calculate_class_name).to_s
63
67
  end
64
68
 
69
+ def inverse_of
70
+ (@inverse_of || calculate_inverse_of).to_s
71
+ end
72
+
65
73
  def proxy_target_class
66
74
  class_name.constantize
67
75
  end
68
76
 
69
77
  def store_in
70
- @store_in.to_s if @store_in
78
+ return @store_in.to_s if @store_in
79
+ @store_in = name if embedded?
71
80
  end
72
81
 
73
82
  def store_foreign_key_in
@@ -81,17 +90,24 @@ module MassiveRecord
81
90
  end
82
91
 
83
92
  def persisting_foreign_key?
84
- !!store_in && !records_starts_from
93
+ !embedded? && !!store_in && !records_starts_from
85
94
  end
86
95
 
87
96
 
88
97
  def polymorphic
89
98
  !!@polymorphic
90
99
  end
100
+ alias polymorphic? polymorphic
91
101
 
92
- def polymorphic?
93
- polymorphic
102
+ def embedded
103
+ relation_type == "embeds_many"
94
104
  end
105
+ alias embedded? embedded
106
+
107
+ def embedded_in
108
+ relation_type == "embedded_in" || relation_type == "embedded_in_polymorphic"
109
+ end
110
+ alias embedded_in? embedded_in
95
111
 
96
112
 
97
113
  def new_relation_proxy(proxy_owner)
@@ -111,16 +127,37 @@ module MassiveRecord
111
127
 
112
128
 
113
129
  def represents_a_collection?
114
- relation_type == 'references_many'
115
- end
116
-
117
-
130
+ %w(references_many embeds_many).include? relation_type
131
+ end
132
+
133
+ #
134
+ # Sets a method which we should ask for how to find where
135
+ # related records starts with. Method injects a find_with
136
+ # Proc which finds are made through.
137
+ #
138
+ # That proc takes different options as it sends on to the
139
+ # receiving finder method on target class. It also takes a
140
+ # block which is sent on to the finder method.
141
+ #
118
142
  def records_starts_from=(method)
119
143
  @records_starts_from = method
120
144
 
121
145
  if @records_starts_from
122
- self.find_with = Proc.new do |proxy_owner, options = {}|
123
- start = proxy_owner.send(records_starts_from) and proxy_target_class.all(options.merge({:start => start}))
146
+ self.find_with = Proc.new do |proxy_owner, options = {}, &block|
147
+ options = MassiveRecord::Adapters::Thrift::Table.warn_and_change_deprecated_finder_options(options)
148
+
149
+ finder_method = options.delete(:finder_method) || :all
150
+
151
+ if ids_starts_with = proxy_owner.send(records_starts_from)
152
+ if options[:starts_with]
153
+ if options[:starts_with].starts_with?(ids_starts_with)
154
+ ids_starts_with = options[:starts_with]
155
+ else
156
+ raise InvalidStartsWithOption.new("The starts with option: #{options[:starts_with]} must begin with: #{ids_starts_with}.")
157
+ end
158
+ end
159
+ proxy_target_class.send(finder_method, options.merge({:starts_with => ids_starts_with}), &block)
160
+ end
124
161
  end
125
162
  else
126
163
  self.find_with = nil
@@ -135,6 +172,15 @@ module MassiveRecord
135
172
  name.to_s.classify
136
173
  end
137
174
 
175
+ def calculate_inverse_of
176
+ raise "Can't return inverse of without it being explicitly set or without an owner_class" unless owner_class
177
+ if represents_a_collection?
178
+ owner_class.to_s.demodulize.underscore.singularize
179
+ else
180
+ owner_class.to_s.demodulize.underscore.pluralize
181
+ end
182
+ end
183
+
138
184
  def calculate_foreign_key
139
185
  if represents_a_collection?
140
186
  name.downcase.singularize + "_ids"
@@ -46,8 +46,8 @@ module MassiveRecord
46
46
  # Returns the proxy_target. Loads it, if it's not there.
47
47
  # Returns nil if for some reason proxy_target could not be found.
48
48
  #
49
- def load_proxy_target
50
- self.proxy_target = find_proxy_target_or_find_with_proc if find_proxy_target?
49
+ def load_proxy_target(options = {})
50
+ self.proxy_target = find_proxy_target_or_find_with_proc(options) if find_proxy_target?
51
51
  proxy_target
52
52
  rescue RecordNotFound
53
53
  reset
@@ -58,13 +58,13 @@ module MassiveRecord
58
58
  load_proxy_target
59
59
  end
60
60
 
61
- def reset
61
+ def reset(force = true)
62
62
  @loaded = @proxy_target = nil
63
63
  end
64
64
 
65
65
  def replace(proxy_target)
66
66
  if proxy_target.nil?
67
- reset
67
+ reset(true)
68
68
  else
69
69
  raise_if_type_mismatch(proxy_target)
70
70
  self.proxy_target = proxy_target
@@ -88,9 +88,6 @@ module MassiveRecord
88
88
  @loaded = true
89
89
  end
90
90
 
91
-
92
-
93
-
94
91
  def respond_to?(*args)
95
92
  super || (load_proxy_target && proxy_target.respond_to?(*args))
96
93
  end
@@ -103,6 +100,14 @@ module MassiveRecord
103
100
  end
104
101
 
105
102
 
103
+ def blank?
104
+ load_proxy_target.blank?
105
+ end
106
+
107
+ def is_a?(type)
108
+ load_proxy_target.is_a?(type)
109
+ end
110
+
106
111
  # Strange.. Without Rails, to_param goes through method_missing,
107
112
  # With Rails it seems like the proxy answered to to_param, which
108
113
  # kinda was not what I wanted.
@@ -113,8 +118,8 @@ module MassiveRecord
113
118
 
114
119
  protected
115
120
 
116
- def find_proxy_target_or_find_with_proc
117
- find_with_proc? ? find_proxy_target_with_proc : find_proxy_target
121
+ def find_proxy_target_or_find_with_proc(options = {})
122
+ find_with_proc? ? find_proxy_target_with_proc(options) : find_proxy_target(options)
118
123
  end
119
124
 
120
125
  #
@@ -123,7 +128,7 @@ module MassiveRecord
123
128
  # data contains a find_with proc; in that case find_proxy_target_with_proc
124
129
  # is used instead
125
130
  #
126
- def find_proxy_target
131
+ def find_proxy_target(options = {})
127
132
  end
128
133
 
129
134
  #
@@ -132,8 +137,8 @@ module MassiveRecord
132
137
  # references_many proxy ensures that the result of proc
133
138
  # is put inside of an array.
134
139
  #
135
- def find_proxy_target_with_proc(options = {})
136
- metadata.find_with.call(proxy_owner, options)
140
+ def find_proxy_target_with_proc(options = {}, &block)
141
+ metadata.find_with.call(proxy_owner, options, &block)
137
142
  end
138
143
 
139
144
  #
@@ -0,0 +1,54 @@
1
+ module MassiveRecord
2
+ module ORM
3
+ module Relations
4
+ class Proxy
5
+ class EmbeddedIn < Proxy
6
+
7
+ def replace(proxy_target, update_inverse_relation = true)
8
+ proxy_target_was = self.proxy_target
9
+
10
+ super(proxy_target).tap do |proxy_target_is|
11
+ if update_inverse_relation
12
+ if proxy_target_is.present?
13
+ unless proxy_target_is.respond_to? metadata.inverse_of
14
+ raise RelationMissing.new(
15
+ <<-TXT
16
+ Expected '#{metadata.proxy_target_class}' to have an embedded
17
+ relation defined with name '#{metadata.inverse_of}'.
18
+ TXT
19
+ )
20
+ end
21
+
22
+ if proxy_target_was.nil?
23
+ proxy_target_is.send(metadata.inverse_of).push proxy_owner
24
+ elsif proxy_target_was != proxy_target_is
25
+ proxy_target_was.send(metadata.inverse_of).delete(proxy_owner)
26
+ proxy_target_was.save if proxy_target_was.persisted?
27
+ proxy_target_is.send(metadata.inverse_of).push proxy_owner
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def reset(force = false)
35
+ # Does nothing.. if we are resetting this inside of an embedded setting
36
+ # we will lose the knowledge about parent.
37
+ super if force
38
+ end
39
+
40
+
41
+ private
42
+
43
+
44
+ # We can never load proxy target,
45
+ # it will always be set for us, as we live
46
+ # inside of our own target
47
+ def can_find_proxy_target?
48
+ false
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end