hyper-model 1.0.alpha1.2 → 1.0.alpha1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.rspec +0 -1
- data/Gemfile +6 -5
- data/Rakefile +27 -3
- data/hyper-model.gemspec +11 -19
- data/lib/active_record_base.rb +105 -33
- data/lib/enumerable/pluck.rb +3 -2
- data/lib/hyper-model.rb +4 -1
- 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 +130 -34
- data/lib/reactive_record/active_record/base.rb +32 -0
- data/lib/reactive_record/active_record/class_methods.rb +124 -52
- 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 +73 -5
- data/lib/reactive_record/active_record/public_columns_hash.rb +25 -26
- data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +22 -5
- data/lib/reactive_record/active_record/reactive_record/base.rb +50 -24
- data/lib/reactive_record/active_record/reactive_record/collection.rb +226 -68
- data/lib/reactive_record/active_record/reactive_record/dummy_polymorph.rb +22 -0
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +27 -15
- data/lib/reactive_record/active_record/reactive_record/getters.rb +33 -10
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +81 -51
- data/lib/reactive_record/active_record/reactive_record/lookup_tables.rb +5 -5
- data/lib/reactive_record/active_record/reactive_record/operations.rb +10 -3
- data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +3 -0
- data/lib/reactive_record/active_record/reactive_record/setters.rb +105 -68
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +249 -32
- data/lib/reactive_record/broadcast.rb +62 -25
- data/lib/reactive_record/interval.rb +3 -3
- data/lib/reactive_record/permissions.rb +14 -2
- data/lib/reactive_record/scope_description.rb +3 -2
- data/lib/reactive_record/server_data_cache.rb +99 -49
- data/polymorph-notes.md +143 -0
- data/spec_fails.txt +3 -0
- metadata +54 -153
- data/Gemfile.lock +0 -421
@@ -6,5 +6,37 @@ module ActiveRecord
|
|
6
6
|
|
7
7
|
scope :limit, ->() {}
|
8
8
|
scope :offset, ->() {}
|
9
|
+
|
10
|
+
finder_method :__hyperstack_internal_scoped_last
|
11
|
+
scope :__hyperstack_internal_scoped_last_n, ->(n) { last(n) }
|
12
|
+
|
13
|
+
def self.where(*args)
|
14
|
+
if args[0].is_a? Hash
|
15
|
+
# we can compute membership in the scope when the arg is a hash
|
16
|
+
__hyperstack_internal_where_hash_scope(args[0])
|
17
|
+
else
|
18
|
+
# otherwise the scope has to always be computed on the server
|
19
|
+
__hyperstack_internal_where_sql_scope(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
scope :__hyperstack_internal_where_hash_scope,
|
24
|
+
client: ->(attrs) { !attrs.detect { |k, v| self[k] != v } }
|
25
|
+
|
26
|
+
scope :__hyperstack_internal_where_sql_scope
|
27
|
+
|
28
|
+
ReactiveRecord::ScopeDescription.new(
|
29
|
+
self, :___hyperstack_internal_scoped_find_by,
|
30
|
+
client: ->(attrs) { !attrs.detect { |attr, value| attributes[attr] != value } }
|
31
|
+
)
|
32
|
+
|
33
|
+
def self.__hyperstack_internal_scoped_find_by(attrs)
|
34
|
+
collection = all.apply_scope(:___hyperstack_internal_scoped_find_by, attrs).observed
|
35
|
+
if !collection.collection
|
36
|
+
collection._find_by_initializer(self, attrs)
|
37
|
+
else
|
38
|
+
collection.first
|
39
|
+
end
|
40
|
+
end
|
9
41
|
end
|
10
42
|
end
|
@@ -1,11 +1,20 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
|
3
2
|
module ClassMethods
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
begin
|
4
|
+
# Opal 0.11 super did not work with new, but new was defined
|
5
|
+
alias _new_without_sti_type_cast new
|
6
|
+
def new(*args, &block)
|
7
|
+
_new_without_sti_type_cast(*args, &block).cast_to_current_sti_type
|
8
|
+
end
|
9
|
+
rescue NameError
|
10
|
+
def self.extended(base)
|
11
|
+
base.singleton_class.class_eval do
|
12
|
+
alias_method :_new_without_sti_type_cast, :new
|
13
|
+
define_method :new do |*args, &block|
|
14
|
+
_new_without_sti_type_cast(*args, &block).cast_to_current_sti_type
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
9
18
|
end
|
10
19
|
|
11
20
|
def base_class
|
@@ -51,14 +60,27 @@ module ActiveRecord
|
|
51
60
|
@model_name ||= ActiveModel::Name.new(self)
|
52
61
|
end
|
53
62
|
|
54
|
-
def
|
55
|
-
|
63
|
+
def __hyperstack_preprocess_attrs(attrs)
|
64
|
+
if inheritance_column && self < base_class && !attrs.key?(inheritance_column)
|
65
|
+
attrs = attrs.merge(inheritance_column => model_name.to_s)
|
66
|
+
end
|
67
|
+
dealiased_attrs = {}
|
68
|
+
attrs.each { |attr, value| dealiased_attrs[_dealias_attribute(attr)] = value }
|
69
|
+
dealiased_attrs
|
70
|
+
end
|
71
|
+
|
72
|
+
def find(*args)
|
73
|
+
args = args[0] if args[0].is_a? Array
|
74
|
+
return args.collect { |id| find(id) } if args.count > 1
|
75
|
+
find_by(primary_key => args[0])
|
56
76
|
end
|
57
77
|
|
58
|
-
def find_by(
|
59
|
-
|
60
|
-
|
61
|
-
|
78
|
+
def find_by(attrs = {})
|
79
|
+
attrs = __hyperstack_preprocess_attrs(attrs)
|
80
|
+
# r = ReactiveRecord::Base.find_locally(self, attrs, new_only: true)
|
81
|
+
# return r.ar_instance if r
|
82
|
+
(r = __hyperstack_internal_scoped_find_by(attrs)) || return
|
83
|
+
r.backing_record.sync_attributes(attrs).set_ar_instance!
|
62
84
|
end
|
63
85
|
|
64
86
|
def enum(*args)
|
@@ -162,7 +184,7 @@ module ActiveRecord
|
|
162
184
|
:full_table_name_prefix, :full_table_name_suffix, :reset_sequence_name, :sequence_name=, :next_sequence_value, :column_defaults, :content_columns,
|
163
185
|
:readonly_attributes, :attr_readonly, :create, :create!, :instantiate, :find, :type_caster, :arel_table, :find_by, :find_by!, :initialize_find_by_cache,
|
164
186
|
:generated_association_methods, :arel_engine, :arel_attribute, :predicate_builder, :collection_cache_key, :relation_delegate_class,
|
165
|
-
:initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors,
|
187
|
+
:initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors,
|
166
188
|
:references, :uniq, :maximum, :none, :exists?, :second, :limit, :order, :eager_load, :update, :delete_all, :destroy, :ids, :many?, :pluck, :third,
|
167
189
|
:delete, :fourth, :fifth, :forty_two, :second_to_last, :third_to_last, :preload, :sum, :take!, :first!, :last!, :second!, :offset, :select, :fourth!,
|
168
190
|
:third!, :third_to_last!, :fifth!, :where, :first_or_create, :second_to_last!, :forty_two!, :first, :having, :any?, :one?, :none?, :find_or_create_by,
|
@@ -175,8 +197,11 @@ module ActiveRecord
|
|
175
197
|
]
|
176
198
|
|
177
199
|
def method_missing(name, *args, &block)
|
178
|
-
if
|
179
|
-
|
200
|
+
if name == 'human_attribute_name'
|
201
|
+
opts = args[1] || {}
|
202
|
+
opts[:default] || args[0]
|
203
|
+
elsif args.count == 1 && name.start_with?("find_by_") && !block
|
204
|
+
find_by(name.sub(/^find_by_/, '') => args[0])
|
180
205
|
elsif [].respond_to?(name)
|
181
206
|
all.send(name, *args, &block)
|
182
207
|
elsif name.end_with?('!')
|
@@ -193,16 +218,6 @@ module ActiveRecord
|
|
193
218
|
# Any method ending with ! just means apply the method after forcing a reload
|
194
219
|
# from the DB.
|
195
220
|
|
196
|
-
# alias pre_synchromesh_method_missing method_missing
|
197
|
-
#
|
198
|
-
# def method_missing(name, *args, &block)
|
199
|
-
# return all.send(name, *args, &block) if [].respond_to?(name)
|
200
|
-
# if name.end_with?('!')
|
201
|
-
# return send(name.chop, *args, &block).send(:reload_from_db) rescue nil
|
202
|
-
# end
|
203
|
-
# pre_synchromesh_method_missing(name, *args, &block)
|
204
|
-
# end
|
205
|
-
|
206
221
|
def create(*args, &block)
|
207
222
|
new(*args).save(&block)
|
208
223
|
end
|
@@ -213,9 +228,6 @@ module ActiveRecord
|
|
213
228
|
singleton_class.send(:define_method, name) do |*vargs|
|
214
229
|
all.build_child_scope(scope_description, *name, *vargs)
|
215
230
|
end
|
216
|
-
# singleton_class.send(:define_method, "#{name}=") do |_collection|
|
217
|
-
# raise 'NO LONGER IMPLEMENTED - DOESNT PLAY WELL WITH SYNCHROMESH'
|
218
|
-
# end
|
219
231
|
end
|
220
232
|
|
221
233
|
def default_scope(*args, &block)
|
@@ -249,10 +261,6 @@ module ActiveRecord
|
|
249
261
|
end
|
250
262
|
end
|
251
263
|
|
252
|
-
# def all=(_collection)
|
253
|
-
# raise "NO LONGER IMPLEMENTED DOESNT PLAY WELL WITH SYNCHROMESH"
|
254
|
-
# end
|
255
|
-
|
256
264
|
def unscoped
|
257
265
|
ReactiveRecord::Base.unscoped[self] ||=
|
258
266
|
ReactiveRecord::Collection
|
@@ -264,7 +272,8 @@ module ActiveRecord
|
|
264
272
|
ReactiveRecord::ScopeDescription.new(self, "_#{name}", {})
|
265
273
|
[name, "#{name}!"].each do |method|
|
266
274
|
singleton_class.send(:define_method, method) do |*vargs|
|
267
|
-
all.apply_scope("_#{method}", *vargs)
|
275
|
+
collection = all.apply_scope("_#{method}", *vargs)
|
276
|
+
collection.first
|
268
277
|
end
|
269
278
|
end
|
270
279
|
end
|
@@ -298,24 +307,34 @@ module ActiveRecord
|
|
298
307
|
assoc = Associations::AssociationReflection.new(self, macro, name, opts)
|
299
308
|
if macro == :has_many
|
300
309
|
define_method(name) { @backing_record.get_has_many(assoc, nil) }
|
301
|
-
define_method("#{name}
|
310
|
+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_has_many(assoc, val) }
|
302
311
|
else
|
303
312
|
define_method(name) { @backing_record.get_belongs_to(assoc, nil) }
|
304
|
-
define_method("#{name}
|
313
|
+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_belongs_to(assoc, val) }
|
305
314
|
end
|
315
|
+
alias_method "#{name}=", "_hyperstack_internal_setter_#{name}"
|
306
316
|
assoc
|
307
317
|
end
|
308
318
|
end
|
309
319
|
|
320
|
+
def table_name
|
321
|
+
@table_name || name.downcase.pluralize
|
322
|
+
end
|
323
|
+
|
324
|
+
def table_name=(name)
|
325
|
+
@table_name = name
|
326
|
+
end
|
327
|
+
|
310
328
|
def composed_of(name, opts = {})
|
311
329
|
reflection = Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
|
312
330
|
if reflection.klass < ActiveRecord::Base
|
313
331
|
define_method(name) { @backing_record.get_ar_aggregate(reflection, nil) }
|
314
|
-
define_method("#{name}
|
332
|
+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_ar_aggregate(reflection, val) }
|
315
333
|
else
|
316
334
|
define_method(name) { @backing_record.get_non_ar_aggregate(name, nil) }
|
317
|
-
define_method("#{name}
|
335
|
+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_non_ar_aggregate(reflection, val) }
|
318
336
|
end
|
337
|
+
alias_method "#{name}=", "_hyperstack_internal_setter_#{name}"
|
319
338
|
end
|
320
339
|
|
321
340
|
def column_names
|
@@ -340,16 +359,31 @@ module ActiveRecord
|
|
340
359
|
vector = args.count.zero? ? name : [[name] + args]
|
341
360
|
@backing_record.get_server_method(vector, true)
|
342
361
|
end
|
362
|
+
define_method("_hyperstack_internal_setter_#{name}") do |val|
|
363
|
+
backing_record.set_attr_value(name, val)
|
364
|
+
end
|
343
365
|
end
|
344
366
|
|
367
|
+
# define all the methods for each column. To allow overriding the methods they will NOT
|
368
|
+
# be defined if already defined (i.e. by the model) See the instance_methods module for how
|
369
|
+
# super calls are handled in this case. The _hyperstack_internal_setter_... methods
|
370
|
+
# are used by the load_from_json method when bringing in data from the server, and so therefore
|
371
|
+
# does not want to be overriden.
|
372
|
+
|
345
373
|
def define_attribute_methods
|
346
374
|
columns_hash.each do |name, column_hash|
|
347
|
-
next if name ==
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
375
|
+
next if name == :id
|
376
|
+
# only add serialized key if its serialized. This just makes testing a bit
|
377
|
+
# easier by keeping the columns_hash the same if there are no seralized strings
|
378
|
+
# see rspec ./spec/batch1/column_types/column_type_spec.rb:100
|
379
|
+
column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
|
380
|
+
|
381
|
+
define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
|
382
|
+
define_method("#{name}!") { @backing_record.get_attr_value(name, true) } unless method_defined?("#{name}!")
|
383
|
+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_attr_value(name, val) }
|
384
|
+
alias_method "#{name}=", "_hyperstack_internal_setter_#{name}" unless method_defined?("#{name}=")
|
385
|
+
define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
|
386
|
+
define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
|
353
387
|
end
|
354
388
|
self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
|
355
389
|
end
|
@@ -370,29 +404,67 @@ module ActiveRecord
|
|
370
404
|
# TODO: changed values as changes while just updating the synced values.
|
371
405
|
target =
|
372
406
|
if param[primary_key]
|
373
|
-
find(param[primary_key])
|
407
|
+
ReactiveRecord::Base.find(self, primary_key => param[primary_key]).tap do |r|
|
408
|
+
r.backing_record.loaded_id = param[primary_key]
|
409
|
+
end
|
374
410
|
else
|
375
411
|
new
|
376
412
|
end
|
377
413
|
|
378
414
|
associations = reflect_on_all_associations
|
379
415
|
|
416
|
+
already_processed_keys = Set.new
|
417
|
+
|
380
418
|
param = param.collect do |key, value|
|
381
|
-
|
382
|
-
|
419
|
+
next if already_processed_keys.include? key
|
420
|
+
|
421
|
+
model_name = model_id = nil
|
422
|
+
|
423
|
+
# polymorphic association is where the belongs_to side holds the
|
424
|
+
# id, and the type of the model the id points to
|
425
|
+
|
426
|
+
# belongs_to :duplicate_of, class_name: 'Report', required: false
|
427
|
+
# has_many :duplicates, class_name: 'Report', foreign_key: 'duplicate_of_id'
|
428
|
+
|
429
|
+
assoc = associations.detect do |poly_assoc|
|
430
|
+
if key == poly_assoc.polymorphic_type_attribute
|
431
|
+
model_name = value
|
432
|
+
already_processed_keys << poly_assoc.association_foreign_key
|
433
|
+
elsif key == poly_assoc.association_foreign_key && (poly_assoc.polymorphic_type_attribute || poly_assoc.macro == :belongs_to)
|
434
|
+
model_id = value
|
435
|
+
already_processed_keys << poly_assoc.polymorphic_type_attribute
|
436
|
+
end
|
383
437
|
end
|
384
438
|
|
385
439
|
if assoc
|
386
|
-
if value
|
387
|
-
[assoc.attribute, { id: [value] }]
|
388
|
-
else
|
440
|
+
if !value
|
389
441
|
[assoc.attribute, [nil]]
|
442
|
+
elsif assoc.polymorphic?
|
443
|
+
model_id ||= param.detect { |k, *| k == assoc.association_foreign_key }&.last
|
444
|
+
model_id ||= target.send(assoc.attribute)&.id
|
445
|
+
if model_id.nil?
|
446
|
+
raise "Error in #{self.name}._react_param_conversion. \n"\
|
447
|
+
"Could not determine the id of #{assoc.attribute} of #{target.inspect}.\n"\
|
448
|
+
"It was not provided in the conversion data, "\
|
449
|
+
"and it is unknown on the client"
|
450
|
+
end
|
451
|
+
model_name ||= param.detect { |k, *| k == assoc.polymorphic_type_attribute }&.last
|
452
|
+
model_name ||= target.send(assoc.polymorphic_type_attribute)
|
453
|
+
unless Object.const_defined?(model_name)
|
454
|
+
raise "Error in #{self.name}._react_param_conversion. \n"\
|
455
|
+
"Could not determine the type of #{assoc.attribute} of #{target.inspect}.\n"\
|
456
|
+
"It was not provided in the conversion data, "\
|
457
|
+
"and it is unknown on the client"
|
458
|
+
end
|
459
|
+
|
460
|
+
[assoc.attribute, { id: [model_id], model_name: [model_name] }]
|
461
|
+
else
|
462
|
+
[assoc.attribute, { id: [value]}]
|
390
463
|
end
|
391
464
|
else
|
392
|
-
[key, [value]]
|
465
|
+
[*key, [value]]
|
393
466
|
end
|
394
|
-
end
|
395
|
-
# TODO: verify wrapping with load_data was added so broadcasting works in 1.0.0.lap28
|
467
|
+
end.compact
|
396
468
|
ReactiveRecord::Base.load_data do
|
397
469
|
ReactiveRecord::ServerDataCache.load_from_json(Hash[param], target)
|
398
470
|
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,59 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module InstanceMethods
|
3
|
+
|
4
|
+
# if methods are missing, then they must be a column, which we look up
|
5
|
+
# in the columns_hash.
|
6
|
+
|
7
|
+
# For effeciency all attributes will by default have all the methods defined,
|
8
|
+
# when the class is loaded. See define_attribute_methods class method.
|
9
|
+
# However a model may override the attribute methods definition, but then call
|
10
|
+
# super. Which will result in the method missing call.
|
11
|
+
|
12
|
+
# When loading data from the server we do NOT want to call overridden methods
|
13
|
+
# so we also define a _hyperstack_internal_setter_... method for each attribute
|
14
|
+
# as well as for belongs_to relationships, server_methods, and the special
|
15
|
+
# type and model_name methods. See the ClassMethods module for details.
|
16
|
+
|
17
|
+
# meanwhile in Opal 1.0 there is currently an issue where the name of the method
|
18
|
+
# does not get passed to method_missing from super.
|
19
|
+
# https://github.com/opal/opal/issues/2165
|
20
|
+
# So the following hack works around that issue until its fixed.
|
21
|
+
|
22
|
+
%x{
|
23
|
+
Opal.orig_find_super_dispatcher = Opal.find_super_dispatcher
|
24
|
+
Opal.find_super_dispatcher = function(obj, mid, current_func, defcheck, allow_stubs) {
|
25
|
+
Opal.__name_of_super = mid;
|
26
|
+
return Opal.orig_find_super_dispatcher(obj, mid, current_func, defcheck, allow_stubs)
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
def method_missing(missing, *args, &block)
|
31
|
+
missing ||= `Opal.__name_of_super`
|
32
|
+
column = self.class.columns_hash.detect { |name, *| missing =~ /^#{name}/ }
|
33
|
+
if column
|
34
|
+
name = column[0]
|
35
|
+
case missing
|
36
|
+
when /\!\z/ then @backing_record.get_attr_value(name, true)
|
37
|
+
when /\=\z/ then @backing_record.set_attr_value(name, *args)
|
38
|
+
when /\_changed\?\z/ then @backing_record.changed?(name)
|
39
|
+
when /\?/ then @backing_record.get_attr_value(name, nil).present?
|
40
|
+
else @backing_record.get_attr_value(name, nil)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# the system assumes that there is "virtual" model_name and type attribute so
|
48
|
+
# we define the internal setter here. If the user defines some other attributes
|
49
|
+
# or uses these names no harm is done since the exact same method would have been
|
50
|
+
# defined by the define_attribute_methods class method anyway.
|
51
|
+
%i[model_name type].each do |attr|
|
52
|
+
define_method("_hyperstack_internal_setter_#{attr}") do |val|
|
53
|
+
@backing_record.set_attr_value(:model_name, val)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
3
57
|
def inspect
|
4
58
|
"<#{model_name}:#{ReactiveRecord::Operations::Base::FORMAT % to_key} "\
|
5
59
|
"(#{ReactiveRecord::Operations::Base::FORMAT % object_id}) "\
|
@@ -38,7 +92,7 @@ module ActiveRecord
|
|
38
92
|
end
|
39
93
|
self.class.load_data do
|
40
94
|
h.each do |attribute, value|
|
41
|
-
next if attribute ==
|
95
|
+
next if attribute == :id
|
42
96
|
@ar_instance[attribute] = value
|
43
97
|
changed_attributes << attribute
|
44
98
|
end
|
@@ -72,8 +126,8 @@ module ActiveRecord
|
|
72
126
|
@backing_record.revert
|
73
127
|
end
|
74
128
|
|
75
|
-
def changed?
|
76
|
-
@backing_record.changed?
|
129
|
+
def changed?(attr = nil)
|
130
|
+
@backing_record.changed?(*attr)
|
77
131
|
end
|
78
132
|
|
79
133
|
def dup
|
@@ -81,7 +135,11 @@ module ActiveRecord
|
|
81
135
|
end
|
82
136
|
|
83
137
|
def ==(ar_instance)
|
84
|
-
@backing_record == ar_instance.instance_eval { @backing_record }
|
138
|
+
return true if @backing_record == ar_instance.instance_eval { @backing_record }
|
139
|
+
return false unless ar_instance.is_a?(ActiveRecord::Base)
|
140
|
+
return false if ar_instance.new_record?
|
141
|
+
return false unless self.class.base_class == ar_instance.class.base_class
|
142
|
+
id == ar_instance.id
|
85
143
|
end
|
86
144
|
|
87
145
|
def [](attr)
|
@@ -104,6 +162,14 @@ module ActiveRecord
|
|
104
162
|
# end
|
105
163
|
end
|
106
164
|
|
165
|
+
def increment!(attr)
|
166
|
+
load(attr).then { |current_value| update(attr => current_value + 1) }
|
167
|
+
end
|
168
|
+
|
169
|
+
def decrement!(attr)
|
170
|
+
load(attr).then { |current_value| update(attr => current_value - 1) }
|
171
|
+
end
|
172
|
+
|
107
173
|
def load(*attributes, &block)
|
108
174
|
first_time = true
|
109
175
|
ReactiveRecord.load do
|
@@ -144,10 +210,12 @@ module ActiveRecord
|
|
144
210
|
@backing_record.destroyed
|
145
211
|
end
|
146
212
|
|
147
|
-
def
|
213
|
+
def new_record?
|
148
214
|
@backing_record.new?
|
149
215
|
end
|
150
216
|
|
217
|
+
alias new? new_record?
|
218
|
+
|
151
219
|
def errors
|
152
220
|
Hyperstack::Internal::State::Variable.get(@backing_record, @backing_record)
|
153
221
|
@backing_record.errors
|