massive_record 0.2.1 → 0.2.2.rc1

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