hyper-model 1.0.alpha1.4 → 1.0.alpha1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|