hyper-model 1.0.alpha1.5 → 1.0.alpha1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2458d824d9c7121f8e628dca72a9df1f6a87ecfc947361046887ea1eca56657
4
- data.tar.gz: 4e6d8aa0a03d07c0080d73aa8bddbc876c1fa56eaebca3f7615425914f8fee07
3
+ metadata.gz: b2f63052beefadb742e008e12976afe889105a0cdbd864ea49902654eb82be51
4
+ data.tar.gz: 2bdf35fb8b4ccd7fd00144be9ba52d8a133797e47825b91cf6acae8dc0673043
5
5
  SHA512:
6
- metadata.gz: 2e0e89354d96b059f62b51023a568e2b334ac2b47bc5940bc8ac6621940259e0b03ec90a61e5e9e135a07954d0cd75a93c6f765486412f80470aedb5a5dc96d0
7
- data.tar.gz: 6b1642506f04f6fc00fd6e6be388632e1157e6405c629928a61d5a8d525fac8666b0f6611d4e261060573b2dc3b184ea3fe0c5640f1a955d2193899b6223202e
6
+ metadata.gz: 2e7481f8e04d3d464507d7c25c20b3dc55167dd6b8cb368f28113e9f52b4ba95815611b8f4fe740d207860f1bc60cdb4ddcdafb996d83ddf2d51afb8cc397fb5
7
+ data.tar.gz: e0e5fb04e304496c3d0ef8353a9ed0bd36a303257e3f062d9ed61e75b4af8d1ded8ed18defd37e7e44dd79b618b221d129287a39d9e331bb20b9cc648c668124
data/.rspec CHANGED
@@ -1,2 +1 @@
1
- --format documentation
2
1
  --color
data/Gemfile CHANGED
@@ -1,10 +1,11 @@
1
1
  source 'https://rubygems.org'
2
- #gem "opal-jquery", git: "https://github.com/opal/opal-jquery.git", branch: "master"
3
- # hyper-model is still using an ancient inlined version of hyper-spec
4
- #gem 'hyper-spec', path: '../hyper-spec'
5
- gem 'hyperstack-config', path: '../hyperstack-config'
6
2
  gem 'hyper-state', path: '../hyper-state'
7
3
  gem 'hyper-component', path: '../hyper-component'
8
4
  gem 'hyper-operation', path: '../hyper-operation'
9
-
5
+ gem 'hyper-spec', path: '../hyper-spec'
6
+ gem 'hyper-trace', path: '../hyper-trace'
7
+ gem 'hyperstack-config', path: '../hyperstack-config'
8
+ unless ENV['OPAL_VERSION']&.match("0.11")
9
+ gem 'opal-browser', git: 'https://github.com/opal/opal-browser'
10
+ end
10
11
  gemspec
data/Rakefile CHANGED
@@ -32,7 +32,7 @@ end
32
32
 
33
33
  namespace :spec do
34
34
  task :prepare do
35
- sh %(cd spec/test_app; bundle exec rails db:setup)
35
+ sh %(cd spec/test_app; rm db/schema.rb; RAILS_ENV=test bundle exec rails db:setup; RAILS_ENV=test bundle exec rails db:migrate)
36
36
  end
37
37
  (1..7).each do |batch|
38
38
  RSpec::Core::RakeTask.new(:"batch#{batch}") do |t|
data/hyper-model.gemspec CHANGED
@@ -27,31 +27,24 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.add_dependency 'activemodel'
29
29
  spec.add_dependency 'activerecord', '>= 4.0.0'
30
- spec.add_dependency 'hyper-component', HyperModel::VERSION
31
30
  spec.add_dependency 'hyper-operation', HyperModel::VERSION
32
- spec.add_development_dependency 'bundler', ['>= 1.17.3', '< 2.1']
33
- spec.add_development_dependency 'capybara'
34
- spec.add_development_dependency 'chromedriver-helper', '1.2.0'
35
- spec.add_development_dependency 'libv8'
36
- spec.add_development_dependency 'mini_racer', '~> 0.2.4'
37
- spec.add_development_dependency 'selenium-webdriver'
31
+
32
+ spec.add_development_dependency 'bundler'
38
33
  spec.add_development_dependency 'database_cleaner'
39
34
  spec.add_development_dependency 'factory_bot_rails'
40
- #spec.add_development_dependency 'hyper-spec', HyperModel::VERSION
41
- spec.add_development_dependency 'mysql2'
42
- spec.add_development_dependency 'opal-activesupport', '~> 0.3.1'
43
- spec.add_development_dependency 'opal-browser', '~> 0.2.0'
44
- spec.add_development_dependency 'opal-rails', '~> 0.9.4'
45
- spec.add_development_dependency 'parser'
46
- spec.add_development_dependency 'pry'
35
+ spec.add_development_dependency 'hyper-spec', HyperModel::VERSION
36
+ spec.add_development_dependency 'hyper-trace', HyperModel::VERSION
37
+ spec.add_development_dependency 'mini_racer'
38
+ spec.add_development_dependency 'pg'
39
+ spec.add_development_dependency 'opal-rails', '>= 0.9.4', '< 2.0'
47
40
  spec.add_development_dependency 'pry-rescue'
41
+ spec.add_development_dependency 'pry-stack_explorer'
48
42
  spec.add_development_dependency 'puma'
49
43
  spec.add_development_dependency 'pusher'
50
44
  spec.add_development_dependency 'pusher-fake'
51
- spec.add_development_dependency 'rails', '>= 4.0.0'
45
+ spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '>= 5.0.0', '< 7.0'
52
46
  spec.add_development_dependency 'rake'
53
47
  spec.add_development_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'
54
- spec.add_development_dependency 'reactrb-rails-generator'
55
48
  spec.add_development_dependency 'rspec-collection_matchers'
56
49
  spec.add_development_dependency 'rspec-expectations'
57
50
  spec.add_development_dependency 'rspec-its'
@@ -62,8 +55,7 @@ Gem::Specification.new do |spec|
62
55
  spec.add_development_dependency 'rubocop', '~> 0.51.0'
63
56
  spec.add_development_dependency 'shoulda'
64
57
  spec.add_development_dependency 'shoulda-matchers'
65
- spec.add_development_dependency 'spring-commands-rspec'
66
- spec.add_development_dependency 'sqlite3', '~> 1.3.6' # see https://github.com/rails/rails/issues/35153, '~> 1.3.6'
58
+ spec.add_development_dependency 'spring-commands-rspec', '~> 1.0.4'
59
+ spec.add_development_dependency 'sqlite3', '~> 1.4.2' # see https://github.com/rails/rails/issues/35153, '~> 1.3.6'
67
60
  spec.add_development_dependency 'timecop', '~> 0.8.1'
68
- spec.add_development_dependency 'unparser'
69
61
  end
@@ -5,17 +5,46 @@ module ActiveRecord
5
5
  # processes these arguments, and the will always leave the true server side scoping
6
6
  # proc in the `:server` opts. This method is common to client and server.
7
7
  class Base
8
- def self._synchromesh_scope_args_check(args)
9
- opts = if args.count == 2 && args[1].is_a?(Hash)
10
- args[1].merge(server: args[0])
11
- elsif args[0].is_a? Hash
12
- args[0]
13
- else
14
- { server: args[0] }
15
- end
16
- return opts if opts && opts[:server].respond_to?(:call)
17
- raise 'must provide either a proc as the first arg or by the '\
18
- '`:server` option to scope and default_scope methods'
8
+ class << self
9
+ def _synchromesh_scope_args_check(args)
10
+ opts = if args.count == 2 && args[1].is_a?(Hash)
11
+ args[1].merge(server: args[0])
12
+ elsif args[0].is_a? Hash
13
+ args[0]
14
+ else
15
+ { server: args[0] }
16
+ end
17
+ return opts if opts && opts[:server].respond_to?(:call)
18
+ raise 'must provide either a proc as the first arg or by the '\
19
+ '`:server` option to scope and default_scope methods'
20
+ end
21
+
22
+ alias pre_hyperstack_has_and_belongs_to_many has_and_belongs_to_many unless RUBY_ENGINE == 'opal'
23
+
24
+ def has_and_belongs_to_many(other, opts = {}, &block)
25
+ join_table_name = [other.to_s, table_name].sort.join('_')
26
+ join_model_name = "HyperstackInternalHabtm#{join_table_name.singularize.camelize}"
27
+ join_model =
28
+ if Object.const_defined? join_model_name
29
+ Object.const_get(join_model_name)
30
+ else
31
+ Object.const_set(join_model_name, Class.new(ActiveRecord::Base))
32
+ end
33
+
34
+ join_model.class_eval { belongs_to other.to_s.singularize.to_sym }
35
+
36
+ has_many join_model_name.underscore.pluralize.to_sym
37
+
38
+ if RUBY_ENGINE == 'opal'
39
+ Object.const_set("HABTM_#{other.to_s.camelize}", join_model)
40
+ join_model.inheritance_column = nil
41
+ has_many other, through: join_model_name.underscore.pluralize.to_sym
42
+ else
43
+ join_model.table_name = join_table_name
44
+ join_model.belongs_to other
45
+ pre_hyperstack_has_and_belongs_to_many(other, opts, &block)
46
+ end
47
+ end
19
48
  end
20
49
  end
21
50
  if RUBY_ENGINE != 'opal'
@@ -270,6 +299,12 @@ module ActiveRecord
270
299
  Hyperstack::InternalPolicy.raise_operation_access_violation(:scoped_denied, "#{self.class} regulation denies scope access. Called from #{caller_locations(1)}")
271
300
  end
272
301
 
302
+ unless method_defined? :saved_changes # for backwards compatibility to Rails < 5.1.7
303
+ def saved_changes
304
+ previous_changes
305
+ end
306
+ end
307
+
273
308
  # call do_not_synchronize to block synchronization of a model
274
309
 
275
310
  def self.do_not_synchronize
@@ -286,17 +321,28 @@ module ActiveRecord
286
321
  self.class.do_not_synchronize?
287
322
  end
288
323
 
324
+ before_create :synchromesh_mark_update_time
325
+ before_update :synchromesh_mark_update_time
326
+ before_destroy :synchromesh_mark_update_time
327
+
328
+ attr_reader :__synchromesh_update_time
329
+
330
+ def synchromesh_mark_update_time
331
+ @__synchromesh_update_time = Time.now.to_f
332
+ end
333
+
289
334
  after_commit :synchromesh_after_create, on: [:create]
290
335
  after_commit :synchromesh_after_change, on: [:update]
291
336
  after_commit :synchromesh_after_destroy, on: [:destroy]
292
337
 
293
338
  def synchromesh_after_create
339
+ puts "#{self}.synchromesh_after_create: #{do_not_synchronize?} channels: #{Hyperstack::Connection.active}" if Hyperstack::Connection.show_diagnostics
294
340
  return if do_not_synchronize?
295
341
  ReactiveRecord::Broadcast.after_commit :create, self
296
342
  end
297
343
 
298
344
  def synchromesh_after_change
299
- return if do_not_synchronize? || previous_changes.empty?
345
+ return if do_not_synchronize? || saved_changes.empty?
300
346
  ReactiveRecord::Broadcast.after_commit :change, self
301
347
  end
302
348
 
data/lib/hyper-model.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'set'
2
2
  require 'hyperstack-config'
3
- require 'hyper-component'
3
+
4
+ Hyperstack.import 'hyper-model'
5
+
4
6
  if RUBY_ENGINE == 'opal'
5
7
  require 'hyper-operation'
6
8
  require 'active_support'
@@ -1,3 +1,3 @@
1
1
  module HyperModel
2
- VERSION = '1.0.alpha1.5'
2
+ VERSION = '1.0.alpha1.6'
3
3
  end
@@ -3,7 +3,7 @@ module ActiveRecord
3
3
  class Base
4
4
 
5
5
  def self.reflect_on_all_associations
6
- base_class.instance_eval { @associations ||= superclass.instance_eval { (@associations && @associations.dup) || [] } }
6
+ @associations ||= superclass.instance_eval { @associations&.dup || [] }
7
7
  end
8
8
 
9
9
  def self.reflect_on_association(attr)
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  end
12
12
 
13
13
  def self.reflect_on_association_by_foreign_key(key)
14
- reflection_finder { |assoc| assoc.association_foreign_key == key }
14
+ reflection_finder { |assoc| assoc.association_foreign_key == key && assoc.macro != :has_many }
15
15
  end
16
16
 
17
17
  def self.reflection_finder(&block)
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  module Associations
33
33
 
34
34
  class AssociationReflection
35
-
35
+ attr_reader :klass_name
36
36
  attr_reader :association_foreign_key
37
37
  attr_reader :attribute
38
38
  attr_reader :macro
@@ -79,6 +79,10 @@ module ActiveRecord
79
79
  @macro != :has_many
80
80
  end
81
81
 
82
+ def habtm?
83
+ through_association&.klass_name =~ /^HyperstackInternalHabtm/
84
+ end
85
+
82
86
  def through_association
83
87
  return unless @options[:through]
84
88
  @through_association ||= @owner_class.reflect_on_all_associations.detect do |association|
@@ -161,6 +165,7 @@ module ActiveRecord
161
165
  def find_inverse(model) # private
162
166
  the_klass = klass(model)
163
167
  the_klass.reflect_on_all_associations.each do |association|
168
+ next if association == self
164
169
  next if association.association_foreign_key != @association_foreign_key
165
170
  next if association.attribute == attribute
166
171
  return association if association.polymorphic? || association.klass == owner_class
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  )
17
17
 
18
18
  def self.__hyperstack_internal_scoped_find_by(attrs)
19
- collection = all.apply_scope(:___hyperstack_internal_scoped_find_by, attrs)
19
+ collection = all.apply_scope(:___hyperstack_internal_scoped_find_by, attrs).observed
20
20
  if !collection.collection
21
21
  collection._find_by_initializer(self, attrs)
22
22
  else
@@ -1,11 +1,20 @@
1
1
  module ActiveRecord
2
-
3
2
  module ClassMethods
4
-
5
- alias _new_without_sti_type_cast new
6
-
7
- def new(*args, &block)
8
- _new_without_sti_type_cast(*args, &block).cast_to_current_sti_type
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
@@ -57,10 +66,13 @@ module ActiveRecord
57
66
  end
58
67
  dealiased_attrs = {}
59
68
  attrs.each { |attr, value| dealiased_attrs[_dealias_attribute(attr)] = value }
69
+ dealiased_attrs
60
70
  end
61
71
 
62
- def find(id)
63
- find_by(primary_key => id)
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])
64
76
  end
65
77
 
66
78
  def find_by(attrs = {})
@@ -295,24 +307,34 @@ module ActiveRecord
295
307
  assoc = Associations::AssociationReflection.new(self, macro, name, opts)
296
308
  if macro == :has_many
297
309
  define_method(name) { @backing_record.get_has_many(assoc, nil) }
298
- define_method("#{name}=") { |val| @backing_record.set_has_many(assoc, val) }
310
+ define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_has_many(assoc, val) }
299
311
  else
300
312
  define_method(name) { @backing_record.get_belongs_to(assoc, nil) }
301
- define_method("#{name}=") { |val| @backing_record.set_belongs_to(assoc, val) }
313
+ define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_belongs_to(assoc, val) }
302
314
  end
315
+ alias_method "#{name}=", "_hyperstack_internal_setter_#{name}"
303
316
  assoc
304
317
  end
305
318
  end
306
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
+
307
328
  def composed_of(name, opts = {})
308
329
  reflection = Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
309
330
  if reflection.klass < ActiveRecord::Base
310
331
  define_method(name) { @backing_record.get_ar_aggregate(reflection, nil) }
311
- define_method("#{name}=") { |val| @backing_record.set_ar_aggregate(reflection, val) }
332
+ define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_ar_aggregate(reflection, val) }
312
333
  else
313
334
  define_method(name) { @backing_record.get_non_ar_aggregate(name, nil) }
314
- define_method("#{name}=") { |val| @backing_record.set_non_ar_aggregate(reflection, val) }
335
+ define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_non_ar_aggregate(reflection, val) }
315
336
  end
337
+ alias_method "#{name}=", "_hyperstack_internal_setter_#{name}"
316
338
  end
317
339
 
318
340
  def column_names
@@ -337,34 +359,29 @@ module ActiveRecord
337
359
  vector = args.count.zero? ? name : [[name] + args]
338
360
  @backing_record.get_server_method(vector, true)
339
361
  end
362
+ define_method("_hyperstack_internal_setter_#{name}") do |val|
363
+ backing_record.set_attr_value(name, val)
364
+ end
340
365
  end
341
366
 
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
-
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.
356
372
 
357
373
  def define_attribute_methods
358
374
  columns_hash.each do |name, column_hash|
359
- next if name == primary_key
375
+ next if name == :id
360
376
  # only add serialized key if its serialized. This just makes testing a bit
361
377
  # easier by keeping the columns_hash the same if there are no seralized strings
362
378
  # see rspec ./spec/batch1/column_types/column_type_spec.rb:100
363
379
  column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
364
-
380
+
365
381
  define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
366
382
  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}=")
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}=")
368
385
  define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
369
386
  define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
370
387
  end
@@ -397,18 +414,23 @@ module ActiveRecord
397
414
  associations = reflect_on_all_associations
398
415
 
399
416
  already_processed_keys = Set.new
400
- old_param = param.dup
401
417
 
402
418
  param = param.collect do |key, value|
403
419
  next if already_processed_keys.include? key
404
420
 
405
421
  model_name = model_id = nil
406
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
+
407
429
  assoc = associations.detect do |poly_assoc|
408
430
  if key == poly_assoc.polymorphic_type_attribute
409
431
  model_name = value
410
432
  already_processed_keys << poly_assoc.association_foreign_key
411
- elsif key == poly_assoc.association_foreign_key
433
+ elsif key == poly_assoc.association_foreign_key && (poly_assoc.polymorphic_type_attribute || poly_assoc.macro == :belongs_to)
412
434
  model_id = value
413
435
  already_processed_keys << poly_assoc.polymorphic_type_attribute
414
436
  end
@@ -440,7 +462,7 @@ module ActiveRecord
440
462
  [assoc.attribute, { id: [value]}]
441
463
  end
442
464
  else
443
- [key, [value]]
465
+ [*key, [value]]
444
466
  end
445
467
  end.compact
446
468
  ReactiveRecord::Base.load_data do
@@ -1,7 +1,34 @@
1
1
  module ActiveRecord
2
2
  module InstanceMethods
3
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
+
4
30
  def method_missing(missing, *args, &block)
31
+ missing ||= `Opal.__name_of_super`
5
32
  column = self.class.columns_hash.detect { |name, *| missing =~ /^#{name}/ }
6
33
  if column
7
34
  name = column[0]
@@ -17,6 +44,16 @@ module ActiveRecord
17
44
  end
18
45
  end
19
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
+
20
57
  def inspect
21
58
  "<#{model_name}:#{ReactiveRecord::Operations::Base::FORMAT % to_key} "\
22
59
  "(#{ReactiveRecord::Operations::Base::FORMAT % object_id}) "\
@@ -55,7 +92,7 @@ module ActiveRecord
55
92
  end
56
93
  self.class.load_data do
57
94
  h.each do |attribute, value|
58
- next if attribute == primary_key
95
+ next if attribute == :id
59
96
  @ar_instance[attribute] = value
60
97
  changed_attributes << attribute
61
98
  end
@@ -89,8 +126,8 @@ module ActiveRecord
89
126
  @backing_record.revert
90
127
  end
91
128
 
92
- def changed?
93
- @backing_record.changed?
129
+ def changed?(attr = nil)
130
+ @backing_record.changed?(*attr)
94
131
  end
95
132
 
96
133
  def dup
@@ -125,6 +162,14 @@ module ActiveRecord
125
162
  # end
126
163
  end
127
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
+
128
173
  def load(*attributes, &block)
129
174
  first_time = true
130
175
  ReactiveRecord.load do