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.
- 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
|