hyper-model 1.0.alpha1.4 → 1.0.alpha1.5
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.
- checksums.yaml +4 -4
- data/lib/active_record_base.rb +1 -1
- data/lib/hyper-model.rb +1 -0
- data/lib/hyper_model/version.rb +1 -1
- data/lib/hyper_react/input_tags.rb +2 -1
- data/lib/reactive_record/active_record/associations.rb +117 -32
- data/lib/reactive_record/active_record/class_methods.rb +68 -14
- data/lib/reactive_record/active_record/error.rb +2 -0
- data/lib/reactive_record/active_record/errors.rb +8 -4
- data/lib/reactive_record/active_record/instance_methods.rb +22 -1
- data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +4 -4
- data/lib/reactive_record/active_record/reactive_record/base.rb +16 -5
- data/lib/reactive_record/active_record/reactive_record/collection.rb +53 -29
- data/lib/reactive_record/active_record/reactive_record/dummy_polymorph.rb +22 -0
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +1 -0
- data/lib/reactive_record/active_record/reactive_record/getters.rb +19 -7
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +28 -11
- data/lib/reactive_record/active_record/reactive_record/lookup_tables.rb +5 -5
- data/lib/reactive_record/active_record/reactive_record/setters.rb +104 -66
- data/lib/reactive_record/broadcast.rb +19 -9
- data/lib/reactive_record/interval.rb +3 -3
- data/lib/reactive_record/scope_description.rb +3 -2
- data/lib/reactive_record/server_data_cache.rb +12 -3
- data/polymorph-notes.md +143 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2458d824d9c7121f8e628dca72a9df1f6a87ecfc947361046887ea1eca56657
|
4
|
+
data.tar.gz: 4e6d8aa0a03d07c0080d73aa8bddbc876c1fa56eaebca3f7615425914f8fee07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e0e89354d96b059f62b51023a568e2b334ac2b47bc5940bc8ac6621940259e0b03ec90a61e5e9e135a07954d0cd75a93c6f765486412f80470aedb5a5dc96d0
|
7
|
+
data.tar.gz: 6b1642506f04f6fc00fd6e6be388632e1157e6405c629928a61d5a8d525fac8666b0f6611d4e261060573b2dc3b184ea3fe0c5640f1a955d2193899b6223202e
|
data/lib/active_record_base.rb
CHANGED
@@ -255,7 +255,7 @@ module ActiveRecord
|
|
255
255
|
pre_syncromesh_has_many name, *args, opts.except(:regulate), &block
|
256
256
|
end
|
257
257
|
|
258
|
-
%i[belongs_to has_one].each do |macro|
|
258
|
+
%i[belongs_to has_one composed_of].each do |macro|
|
259
259
|
alias_method :"pre_syncromesh_#{macro}", macro
|
260
260
|
define_method(macro) do |name, *aargs, &block|
|
261
261
|
define_method(:"__secure_remote_access_to_#{name}") do |this, _acting_user, *args|
|
data/lib/hyper-model.rb
CHANGED
@@ -20,6 +20,7 @@ if RUBY_ENGINE == 'opal'
|
|
20
20
|
require "reactive_record/active_record/reactive_record/isomorphic_base"
|
21
21
|
require 'reactive_record/active_record/reactive_record/dummy_value'
|
22
22
|
require 'reactive_record/active_record/reactive_record/column_types'
|
23
|
+
require 'reactive_record/active_record/reactive_record/dummy_polymorph'
|
23
24
|
require "reactive_record/active_record/aggregations"
|
24
25
|
require "reactive_record/active_record/associations"
|
25
26
|
require "reactive_record/active_record/reactive_record/backing_record_inspector"
|
data/lib/hyper_model/version.rb
CHANGED
@@ -38,16 +38,45 @@ module ActiveRecord
|
|
38
38
|
attr_reader :macro
|
39
39
|
attr_reader :owner_class
|
40
40
|
attr_reader :source
|
41
|
+
attr_reader :source_type
|
42
|
+
attr_reader :options
|
43
|
+
attr_reader :polymorphic_type_attribute
|
41
44
|
|
42
45
|
def initialize(owner_class, macro, name, options = {})
|
43
46
|
owner_class.reflect_on_all_associations << self
|
44
47
|
@owner_class = owner_class
|
45
48
|
@macro = macro
|
46
49
|
@options = options
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
unless options[:polymorphic]
|
51
|
+
@klass_name = options[:class_name] || (collection? && name.camelize.singularize) || name.camelize
|
52
|
+
end
|
53
|
+
|
54
|
+
if @klass_name < ActiveRecord::Base
|
55
|
+
@klass = @klass_name
|
56
|
+
@klass_name = @klass_name.name
|
57
|
+
end rescue nil
|
58
|
+
|
59
|
+
@association_foreign_key =
|
60
|
+
options[:foreign_key] ||
|
61
|
+
(macro == :belongs_to && "#{name}_id") ||
|
62
|
+
(options[:as] && "#{options[:as]}_id") ||
|
63
|
+
(options[:polymorphic] && "#{name}_id") ||
|
64
|
+
"#{@owner_class.name.underscore}_id"
|
65
|
+
if options[:through]
|
66
|
+
@source = options[:source] || @klass_name.underscore
|
67
|
+
@source_type = options[:source_type] || @klass_name
|
68
|
+
end
|
69
|
+
@polymorphic_type_attribute = "#{name}_type" if options[:polymorphic]
|
50
70
|
@attribute = name
|
71
|
+
@through_associations = Hash.new { |_h, k| [] unless k }
|
72
|
+
end
|
73
|
+
|
74
|
+
def collection?
|
75
|
+
@macro == :has_many
|
76
|
+
end
|
77
|
+
|
78
|
+
def singular?
|
79
|
+
@macro != :has_many
|
51
80
|
end
|
52
81
|
|
53
82
|
def through_association
|
@@ -62,63 +91,119 @@ module ActiveRecord
|
|
62
91
|
|
63
92
|
alias through_association? through_association
|
64
93
|
|
65
|
-
|
94
|
+
# class Membership < ActiveRecord::Base
|
95
|
+
# belongs_to :uzer
|
96
|
+
# belongs_to :memerable, polymorphic: true
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# class Project < ActiveRecord::Base
|
100
|
+
# has_many :memberships, as: :memerable, dependent: :destroy
|
101
|
+
# has_many :uzers, through: :memberships
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# class Group < ActiveRecord::Base
|
105
|
+
# has_many :memberships, as: :memerable, dependent: :destroy
|
106
|
+
# has_many :uzers, through: :memberships
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# class Uzer < ActiveRecord::Base
|
110
|
+
# has_many :memberships
|
111
|
+
# has_many :groups, through: :memberships, source: :memerable, source_type: 'Group'
|
112
|
+
# has_many :projects, through: :memberships, source: :memerable, source_type: 'Project'
|
113
|
+
# end
|
114
|
+
|
115
|
+
# so find the belongs_to relationship whose attribute == ta.source
|
116
|
+
# now find the inverse of that relationship using source_value as the model
|
117
|
+
# now find any has many through relationships that use that relationship as there source.
|
118
|
+
# each of those attributes in the source_value have to be updated.
|
119
|
+
|
120
|
+
# self is the through association
|
121
|
+
|
122
|
+
|
123
|
+
def through_associations(model)
|
124
|
+
# given self is a belongs_to association currently pointing to model
|
66
125
|
# find all associations that use the inverse association as the through association
|
67
126
|
# that is find all associations that are using this association in a through relationship
|
68
|
-
|
69
|
-
|
127
|
+
the_klass = klass(model)
|
128
|
+
@through_associations[the_klass] ||= the_klass.reflect_on_all_associations.select do |assoc|
|
129
|
+
assoc.through_association&.inverse == self
|
70
130
|
end
|
71
131
|
end
|
72
132
|
|
73
|
-
def
|
74
|
-
#
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
assoc.source == attribute
|
133
|
+
def source_belongs_to_association # private
|
134
|
+
# given self is a has_many_through association return the corresponding belongs_to association
|
135
|
+
# for the source
|
136
|
+
@source_belongs_to_association ||=
|
137
|
+
through_association.inverse.owner_class.reflect_on_all_associations.detect do |sibling|
|
138
|
+
sibling.attribute == source
|
80
139
|
end
|
81
|
-
end.flatten
|
82
140
|
end
|
83
141
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
142
|
+
def source_associations(model)
|
143
|
+
# given self is a has_many_through association find the source_association for the given model
|
144
|
+
source_belongs_to_association.through_associations(model)
|
87
145
|
end
|
88
146
|
|
89
|
-
|
90
|
-
|
147
|
+
alias :polymorphic? polymorphic_type_attribute
|
148
|
+
|
149
|
+
def inverse(model = nil)
|
150
|
+
return @inverse if @inverse
|
151
|
+
ta = through_association
|
152
|
+
found = ta ? ta.inverse : find_inverse(model)
|
153
|
+
@inverse = found unless polymorphic?
|
154
|
+
found
|
91
155
|
end
|
92
156
|
|
93
|
-
def
|
94
|
-
|
157
|
+
def inverse_of(model = nil)
|
158
|
+
inverse(model).attribute
|
159
|
+
end
|
160
|
+
|
161
|
+
def find_inverse(model) # private
|
162
|
+
the_klass = klass(model)
|
163
|
+
the_klass.reflect_on_all_associations.each do |association|
|
95
164
|
next if association.association_foreign_key != @association_foreign_key
|
96
|
-
next if association.klass != @owner_class
|
97
165
|
next if association.attribute == attribute
|
98
|
-
return association if klass ==
|
166
|
+
return association if association.polymorphic? || association.klass == owner_class
|
99
167
|
end
|
168
|
+
raise "could not find inverse of polymorphic belongs_to: #{model.inspect} #{self.inspect}" if options[:polymorphic]
|
100
169
|
# instead of raising an error go ahead and create the inverse relationship if it does not exist.
|
101
170
|
# https://github.com/hyperstack-org/hyperstack/issues/89
|
102
171
|
if macro == :belongs_to
|
103
|
-
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{
|
104
|
-
|
172
|
+
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.has_many :#{@owner_class.name.underscore.pluralize}, foreign_key: #{@association_foreign_key}", :warning
|
173
|
+
the_klass.has_many @owner_class.name.underscore.pluralize, foreign_key: @association_foreign_key
|
174
|
+
elsif options[:as]
|
175
|
+
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.belongs_to :#{options[:as]}, polymorphic: true", :warning
|
176
|
+
the_klass.belongs_to options[:as], polymorphic: true
|
105
177
|
else
|
106
|
-
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{
|
107
|
-
|
178
|
+
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.belongs_to :#{@owner_class.name.underscore}, foreign_key: #{@association_foreign_key}", :warning
|
179
|
+
the_klass.belongs_to @owner_class.name.underscore, foreign_key: @association_foreign_key
|
108
180
|
end
|
109
181
|
end
|
110
182
|
|
111
|
-
def klass
|
112
|
-
@klass ||= Object.const_get(@klass_name)
|
183
|
+
def klass(model = nil)
|
184
|
+
@klass ||= Object.const_get(@klass_name) if @klass_name
|
185
|
+
if @klass && model && !(model.class <= @klass || @klass <= model.class)
|
186
|
+
# TODO: added || @klass <= model.class can both cases really happen I guess so
|
187
|
+
raise "internal error: provided model #{model} is not subclass of #{@klass}"
|
188
|
+
end
|
189
|
+
raise 'no model supplied for polymorphic relationship' unless @klass || model
|
190
|
+
@klass || model.class
|
113
191
|
end
|
114
192
|
|
115
193
|
def collection?
|
116
194
|
[:has_many].include? @macro
|
117
195
|
end
|
118
196
|
|
119
|
-
|
197
|
+
def remove_member(member, owner)
|
198
|
+
collection = owner.attributes[attribute]
|
199
|
+
return if collection.nil?
|
200
|
+
collection.delete(member)
|
201
|
+
end
|
120
202
|
|
203
|
+
def add_member(member, owner)
|
204
|
+
owner.attributes[attribute] ||= ReactiveRecord::Collection.new(owner_class, owner, self)
|
205
|
+
owner.attributes[attribute]._internal_push member
|
206
|
+
end
|
207
|
+
end
|
121
208
|
end
|
122
|
-
|
123
|
-
|
124
209
|
end
|
@@ -172,7 +172,7 @@ module ActiveRecord
|
|
172
172
|
:full_table_name_prefix, :full_table_name_suffix, :reset_sequence_name, :sequence_name=, :next_sequence_value, :column_defaults, :content_columns,
|
173
173
|
:readonly_attributes, :attr_readonly, :create, :create!, :instantiate, :find, :type_caster, :arel_table, :find_by, :find_by!, :initialize_find_by_cache,
|
174
174
|
:generated_association_methods, :arel_engine, :arel_attribute, :predicate_builder, :collection_cache_key, :relation_delegate_class,
|
175
|
-
:initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors,
|
175
|
+
:initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors,
|
176
176
|
:references, :uniq, :maximum, :none, :exists?, :second, :limit, :order, :eager_load, :update, :delete_all, :destroy, :ids, :many?, :pluck, :third,
|
177
177
|
:delete, :fourth, :fifth, :forty_two, :second_to_last, :third_to_last, :preload, :sum, :take!, :first!, :last!, :second!, :offset, :select, :fourth!,
|
178
178
|
:third!, :third_to_last!, :fifth!, :where, :first_or_create, :second_to_last!, :forty_two!, :first, :having, :any?, :one?, :none?, :find_or_create_by,
|
@@ -185,7 +185,10 @@ module ActiveRecord
|
|
185
185
|
]
|
186
186
|
|
187
187
|
def method_missing(name, *args, &block)
|
188
|
-
if
|
188
|
+
if name == 'human_attribute_name'
|
189
|
+
opts = args[1] || {}
|
190
|
+
opts[:default] || args[0]
|
191
|
+
elsif args.count == 1 && name.start_with?("find_by_") && !block
|
189
192
|
find_by(name.sub(/^find_by_/, '') => args[0])
|
190
193
|
elsif [].respond_to?(name)
|
191
194
|
all.send(name, *args, &block)
|
@@ -336,14 +339,34 @@ module ActiveRecord
|
|
336
339
|
end
|
337
340
|
end
|
338
341
|
|
342
|
+
# def define_attribute_methods
|
343
|
+
# columns_hash.each do |name, column_hash|
|
344
|
+
# next if name == primary_key
|
345
|
+
# column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
|
346
|
+
#
|
347
|
+
# define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
|
348
|
+
# define_method("#{name}!") { @backing_record.get_attr_value(name, true) } unless method_defined?("#{name}!")
|
349
|
+
# define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) } unless method_defined?("#{name}=")
|
350
|
+
# define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
|
351
|
+
# define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
|
352
|
+
# end
|
353
|
+
# self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
|
354
|
+
# end
|
355
|
+
|
356
|
+
|
339
357
|
def define_attribute_methods
|
340
358
|
columns_hash.each do |name, column_hash|
|
341
359
|
next if name == primary_key
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
360
|
+
# only add serialized key if its serialized. This just makes testing a bit
|
361
|
+
# easier by keeping the columns_hash the same if there are no seralized strings
|
362
|
+
# see rspec ./spec/batch1/column_types/column_type_spec.rb:100
|
363
|
+
column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
|
364
|
+
|
365
|
+
define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
|
366
|
+
define_method("#{name}!") { @backing_record.get_attr_value(name, true) } unless method_defined?("#{name}!")
|
367
|
+
define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) } unless method_defined?("#{name}=")
|
368
|
+
define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
|
369
|
+
define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
|
347
370
|
end
|
348
371
|
self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
|
349
372
|
end
|
@@ -373,22 +396,53 @@ module ActiveRecord
|
|
373
396
|
|
374
397
|
associations = reflect_on_all_associations
|
375
398
|
|
399
|
+
already_processed_keys = Set.new
|
400
|
+
old_param = param.dup
|
401
|
+
|
376
402
|
param = param.collect do |key, value|
|
377
|
-
|
378
|
-
|
403
|
+
next if already_processed_keys.include? key
|
404
|
+
|
405
|
+
model_name = model_id = nil
|
406
|
+
|
407
|
+
assoc = associations.detect do |poly_assoc|
|
408
|
+
if key == poly_assoc.polymorphic_type_attribute
|
409
|
+
model_name = value
|
410
|
+
already_processed_keys << poly_assoc.association_foreign_key
|
411
|
+
elsif key == poly_assoc.association_foreign_key
|
412
|
+
model_id = value
|
413
|
+
already_processed_keys << poly_assoc.polymorphic_type_attribute
|
414
|
+
end
|
379
415
|
end
|
380
416
|
|
381
417
|
if assoc
|
382
|
-
if value
|
383
|
-
[assoc.attribute, { id: [value] }]
|
384
|
-
else
|
418
|
+
if !value
|
385
419
|
[assoc.attribute, [nil]]
|
420
|
+
elsif assoc.polymorphic?
|
421
|
+
model_id ||= param.detect { |k, *| k == assoc.association_foreign_key }&.last
|
422
|
+
model_id ||= target.send(assoc.attribute)&.id
|
423
|
+
if model_id.nil?
|
424
|
+
raise "Error in #{self.name}._react_param_conversion. \n"\
|
425
|
+
"Could not determine the id of #{assoc.attribute} of #{target.inspect}.\n"\
|
426
|
+
"It was not provided in the conversion data, "\
|
427
|
+
"and it is unknown on the client"
|
428
|
+
end
|
429
|
+
model_name ||= param.detect { |k, *| k == assoc.polymorphic_type_attribute }&.last
|
430
|
+
model_name ||= target.send(assoc.polymorphic_type_attribute)
|
431
|
+
unless Object.const_defined?(model_name)
|
432
|
+
raise "Error in #{self.name}._react_param_conversion. \n"\
|
433
|
+
"Could not determine the type of #{assoc.attribute} of #{target.inspect}.\n"\
|
434
|
+
"It was not provided in the conversion data, "\
|
435
|
+
"and it is unknown on the client"
|
436
|
+
end
|
437
|
+
|
438
|
+
[assoc.attribute, { id: [model_id], model_name: [model_name] }]
|
439
|
+
else
|
440
|
+
[assoc.attribute, { id: [value]}]
|
386
441
|
end
|
387
442
|
else
|
388
443
|
[key, [value]]
|
389
444
|
end
|
390
|
-
end
|
391
|
-
# TODO: verify wrapping with load_data was added so broadcasting works in 1.0.0.lap28
|
445
|
+
end.compact
|
392
446
|
ReactiveRecord::Base.load_data do
|
393
447
|
ReactiveRecord::ServerDataCache.load_from_json(Hash[param], target)
|
394
448
|
end
|
@@ -89,7 +89,7 @@ module ActiveModel
|
|
89
89
|
# person.errors.values # => [["cannot be nil", "must be specified"]]
|
90
90
|
def values
|
91
91
|
messages.select do |key, value|
|
92
|
-
!value
|
92
|
+
!value&.empty?
|
93
93
|
end.values
|
94
94
|
end
|
95
95
|
|
@@ -105,9 +105,9 @@ module ActiveModel
|
|
105
105
|
attr_name =
|
106
106
|
attribute.to_s.tr('.', '_').tr('_', ' ').gsub(/_id$/, '').capitalize
|
107
107
|
# attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
|
108
|
-
|
108
|
+
if @base.class.respond_to?(:human_attribute_name)
|
109
109
|
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
|
110
|
-
|
110
|
+
end
|
111
111
|
# I18n.t(:"errors.format",
|
112
112
|
# default: "%{attribute} %{message}",
|
113
113
|
# attribute: attr_name,
|
@@ -219,8 +219,12 @@ module ActiveModel
|
|
219
219
|
# person.errors.clear
|
220
220
|
# person.errors.full_messages # => []
|
221
221
|
def clear
|
222
|
+
non_reactive_clear.tap { reactive_empty! true }
|
223
|
+
end
|
224
|
+
|
225
|
+
def non_reactive_clear
|
222
226
|
messages.clear
|
223
|
-
details.clear
|
227
|
+
details.clear
|
224
228
|
end
|
225
229
|
|
226
230
|
# Merges the errors from <tt>other</tt>.
|
@@ -1,5 +1,22 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module InstanceMethods
|
3
|
+
|
4
|
+
def method_missing(missing, *args, &block)
|
5
|
+
column = self.class.columns_hash.detect { |name, *| missing =~ /^#{name}/ }
|
6
|
+
if column
|
7
|
+
name = column[0]
|
8
|
+
case missing
|
9
|
+
when /\!\z/ then @backing_record.get_attr_value(name, true)
|
10
|
+
when /\=\z/ then @backing_record.set_attr_value(name, *args)
|
11
|
+
when /\_changed\?\z/ then @backing_record.changed?(name)
|
12
|
+
when /\?/ then @backing_record.get_attr_value(name, nil).present?
|
13
|
+
else @backing_record.get_attr_value(name, nil)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
3
20
|
def inspect
|
4
21
|
"<#{model_name}:#{ReactiveRecord::Operations::Base::FORMAT % to_key} "\
|
5
22
|
"(#{ReactiveRecord::Operations::Base::FORMAT % object_id}) "\
|
@@ -81,7 +98,11 @@ module ActiveRecord
|
|
81
98
|
end
|
82
99
|
|
83
100
|
def ==(ar_instance)
|
84
|
-
@backing_record == ar_instance.instance_eval { @backing_record }
|
101
|
+
return true if @backing_record == ar_instance.instance_eval { @backing_record }
|
102
|
+
return false unless ar_instance.is_a?(ActiveRecord::Base)
|
103
|
+
return false if ar_instance.new_record?
|
104
|
+
return false unless self.class.base_class == ar_instance.class.base_class
|
105
|
+
id == ar_instance.id
|
85
106
|
end
|
86
107
|
|
87
108
|
def [](attr)
|