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.
- data/CHANGELOG.md +58 -2
- data/Gemfile.lock +17 -17
- data/README.md +98 -41
- data/lib/massive_record.rb +2 -1
- data/lib/massive_record/adapters/thrift/hbase/hbase.rb +2425 -2154
- data/lib/massive_record/adapters/thrift/hbase/hbase_constants.rb +3 -3
- data/lib/massive_record/adapters/thrift/hbase/hbase_types.rb +195 -195
- data/lib/massive_record/adapters/thrift/row.rb +35 -4
- data/lib/massive_record/adapters/thrift/table.rb +49 -12
- data/lib/massive_record/orm/attribute_methods.rb +77 -5
- data/lib/massive_record/orm/attribute_methods/cast_numbers_on_write.rb +24 -0
- data/lib/massive_record/orm/attribute_methods/dirty.rb +18 -0
- data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +24 -3
- data/lib/massive_record/orm/attribute_methods/write.rb +8 -1
- data/lib/massive_record/orm/base.rb +62 -8
- data/lib/massive_record/orm/column.rb +7 -11
- data/lib/massive_record/orm/default_id.rb +1 -1
- data/lib/massive_record/orm/embedded.rb +66 -0
- data/lib/massive_record/orm/errors.rb +17 -0
- data/lib/massive_record/orm/finders.rb +124 -71
- data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +1 -1
- data/lib/massive_record/orm/finders/scope.rb +58 -34
- data/lib/massive_record/orm/id_factory.rb +22 -105
- data/lib/massive_record/orm/id_factory/atomic_incrementation.rb +117 -0
- data/lib/massive_record/orm/id_factory/timestamp.rb +60 -0
- data/lib/massive_record/orm/identity_map.rb +256 -0
- data/lib/massive_record/orm/log_subscriber.rb +18 -0
- data/lib/massive_record/orm/observer.rb +69 -0
- data/lib/massive_record/orm/persistence.rb +47 -119
- data/lib/massive_record/orm/persistence/operations.rb +100 -0
- data/lib/massive_record/orm/persistence/operations/atomic_operation.rb +71 -0
- data/lib/massive_record/orm/persistence/operations/destroy.rb +17 -0
- data/lib/massive_record/orm/persistence/operations/embedded/destroy.rb +26 -0
- data/lib/massive_record/orm/persistence/operations/embedded/insert.rb +27 -0
- data/lib/massive_record/orm/persistence/operations/embedded/operation_helpers.rb +66 -0
- data/lib/massive_record/orm/persistence/operations/embedded/reload.rb +39 -0
- data/lib/massive_record/orm/persistence/operations/embedded/update.rb +29 -0
- data/lib/massive_record/orm/persistence/operations/insert.rb +19 -0
- data/lib/massive_record/orm/persistence/operations/reload.rb +26 -0
- data/lib/massive_record/orm/persistence/operations/suppress.rb +15 -0
- data/lib/massive_record/orm/persistence/operations/table_operation_helpers.rb +106 -0
- data/lib/massive_record/orm/persistence/operations/update.rb +25 -0
- data/lib/massive_record/orm/query_instrumentation.rb +26 -49
- data/lib/massive_record/orm/raw_data.rb +47 -0
- data/lib/massive_record/orm/relations.rb +4 -0
- data/lib/massive_record/orm/relations/interface.rb +134 -0
- data/lib/massive_record/orm/relations/metadata.rb +58 -12
- data/lib/massive_record/orm/relations/proxy.rb +17 -12
- data/lib/massive_record/orm/relations/proxy/embedded_in.rb +54 -0
- data/lib/massive_record/orm/relations/proxy/embedded_in_polymorphic.rb +15 -0
- data/lib/massive_record/orm/relations/proxy/embeds_many.rb +215 -0
- data/lib/massive_record/orm/relations/proxy/references_many.rb +112 -88
- data/lib/massive_record/orm/relations/proxy/references_one.rb +1 -1
- data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
- data/lib/massive_record/orm/relations/proxy_collection.rb +84 -0
- data/lib/massive_record/orm/schema/column_family.rb +3 -2
- data/lib/massive_record/orm/schema/{column_interface.rb → embedded_interface.rb} +38 -4
- data/lib/massive_record/orm/schema/field.rb +2 -0
- data/lib/massive_record/orm/schema/table_interface.rb +19 -2
- data/lib/massive_record/orm/single_table_inheritance.rb +37 -2
- data/lib/massive_record/orm/timestamps.rb +17 -7
- data/lib/massive_record/orm/validations.rb +4 -0
- data/lib/massive_record/orm/validations/associated.rb +50 -0
- data/lib/massive_record/rails/railtie.rb +31 -0
- data/lib/massive_record/version.rb +1 -1
- data/lib/massive_record/wrapper/cell.rb +8 -1
- data/massive_record.gemspec +4 -4
- data/spec/adapter/thrift/atomic_increment_spec.rb +16 -0
- data/spec/adapter/thrift/table_find_spec.rb +14 -2
- data/spec/adapter/thrift/table_spec.rb +6 -6
- data/spec/adapter/thrift/utf8_encoding_of_id_spec.rb +71 -0
- data/spec/orm/cases/attribute_methods_spec.rb +215 -22
- data/spec/orm/cases/auto_generate_id_spec.rb +1 -1
- data/spec/orm/cases/change_id_spec.rb +62 -0
- data/spec/orm/cases/default_id_spec.rb +25 -6
- data/spec/orm/cases/default_values_spec.rb +6 -3
- data/spec/orm/cases/dirty_spec.rb +150 -102
- data/spec/orm/cases/embedded_spec.rb +250 -0
- data/spec/orm/cases/{finder_default_scope.rb → finder_default_scope_spec.rb} +4 -0
- data/spec/orm/cases/finder_scope_spec.rb +96 -29
- data/spec/orm/cases/finders_spec.rb +57 -10
- data/spec/orm/cases/id_factory/atomic_incrementation_spec.rb +72 -0
- data/spec/orm/cases/id_factory/timestamp_spec.rb +61 -0
- data/spec/orm/cases/identity_map/identity_map_spec.rb +357 -0
- data/spec/orm/cases/identity_map/middleware_spec.rb +74 -0
- data/spec/orm/cases/log_subscriber_spec.rb +15 -2
- data/spec/orm/cases/observing_spec.rb +61 -0
- data/spec/orm/cases/persistence_spec.rb +151 -60
- data/spec/orm/cases/raw_data_spec.rb +58 -0
- data/spec/orm/cases/single_table_inheritance_spec.rb +58 -2
- data/spec/orm/cases/table_spec.rb +3 -3
- data/spec/orm/cases/time_zone_awareness_spec.rb +27 -0
- data/spec/orm/cases/timestamps_spec.rb +23 -109
- data/spec/orm/cases/validation_spec.rb +9 -0
- data/spec/orm/models/address.rb +5 -1
- data/spec/orm/models/address_with_timestamp.rb +12 -0
- data/spec/orm/models/car.rb +5 -0
- data/spec/orm/models/person.rb +13 -1
- data/spec/orm/models/person_with_timestamp.rb +4 -2
- data/spec/orm/models/test_class.rb +1 -0
- data/spec/orm/persistence/operations/atomic_operation_spec.rb +58 -0
- data/spec/orm/persistence/operations/destroy_spec.rb +22 -0
- data/spec/orm/persistence/operations/embedded/destroy_spec.rb +71 -0
- data/spec/orm/persistence/operations/embedded/insert_spec.rb +59 -0
- data/spec/orm/persistence/operations/embedded/operation_helpers_spec.rb +92 -0
- data/spec/orm/persistence/operations/embedded/reload_spec.rb +67 -0
- data/spec/orm/persistence/operations/embedded/update_spec.rb +60 -0
- data/spec/orm/persistence/operations/insert_spec.rb +31 -0
- data/spec/orm/persistence/operations/reload_spec.rb +48 -0
- data/spec/orm/persistence/operations/suppress_spec.rb +17 -0
- data/spec/orm/persistence/operations/table_operation_helpers_spec.rb +98 -0
- data/spec/orm/persistence/operations/update_spec.rb +25 -0
- data/spec/orm/persistence/operations_spec.rb +58 -0
- data/spec/orm/relations/interface_spec.rb +188 -0
- data/spec/orm/relations/metadata_spec.rb +92 -15
- data/spec/orm/relations/proxy/embedded_in_polymorphic_spec.rb +37 -0
- data/spec/orm/relations/proxy/embedded_in_spec.rb +66 -0
- data/spec/orm/relations/proxy/embeds_many_spec.rb +651 -0
- data/spec/orm/relations/proxy/references_many_spec.rb +466 -2
- data/spec/orm/schema/column_family_spec.rb +21 -0
- data/spec/orm/schema/embedded_interface_spec.rb +181 -0
- data/spec/orm/schema/field_spec.rb +7 -0
- data/spec/orm/schema/table_interface_spec.rb +31 -1
- data/spec/shared/orm/id_factories.rb +44 -0
- data/spec/shared/orm/model_with_timestamps.rb +132 -0
- data/spec/shared/orm/persistence/a_persistence_embedded_operation_class.rb +3 -0
- data/spec/shared/orm/persistence/a_persistence_operation_class.rb +11 -0
- data/spec/shared/orm/persistence/a_persistence_table_operation_class.rb +11 -0
- data/spec/shared/orm/relations/proxy.rb +9 -2
- data/spec/spec_helper.rb +9 -0
- data/spec/support/mock_massive_record_connection.rb +2 -1
- metadata +106 -21
- data/spec/orm/cases/column_spec.rb +0 -49
- data/spec/orm/cases/id_factory_spec.rb +0 -92
- 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
|
93
|
-
|
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
|
-
|
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
|
-
|
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
|