chrono_model 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 143b16e6e193bd56c88d1541f965150b07af191992aec7978dc94c84d7e53f87
4
- data.tar.gz: 4aa06ed4110a6dbd9047f580b2972589d813f5108b422fe6ba0a80c021d56d03
3
+ metadata.gz: 15a81b8b75b7b77c03e9e750db3122cc0107e238696882bd67f2fe8fc7647274
4
+ data.tar.gz: 599dd55436b014a5db0ce7c1b003aa34ce15e3e12f08dac3e5cc0c5549823a8e
5
5
  SHA512:
6
- metadata.gz: 57a62853b840b3edd78d1e0402c8d66458b01980956c87c880b84261490ed6f251a00b497c70f56646b4dd051869554118fc426d31b9bfda0af367e8977f3612
7
- data.tar.gz: 854b001d29648c4b191eb7e985d720d72bd82cd43dc40df481cc63a83d684fe15922b8127ea40418ad89e5cc7cee30bb3e2551e2eeb6964bbb6477a97aa2f109
6
+ metadata.gz: 2ba2dc61cc6a62e573dea411005abf071e8010bcd249b05f274afefc756b1ac85b47e6469a6c9988f3b97c3f340891606bbe32d97cd75dfc4fcf0c1f99148805
7
+ data.tar.gz: 2cce42ff59e611beb10929f8484b8a0ec9eb106f2284770566d51d979eb6b8bb0c0627f88a4e8d1ef0c83cd7dfbd0733b44e09b339994a0424e7114181a74bf6
@@ -22,7 +22,7 @@ module ChronoModel
22
22
  @as_of_time = as_of_time
23
23
 
24
24
  virtual_table = history_model.
25
- virtual_table_at(@as_of_time, @table_alias || @table_name)
25
+ virtual_table_at(@as_of_time, table_name: @table_alias || @table_name)
26
26
 
27
27
  super(virtual_table)
28
28
  end
@@ -29,64 +29,37 @@ module ChronoModel
29
29
  descendants_with_history.reject(&:history?)
30
30
  end
31
31
 
32
- # STI support. TODO: more thorough testing
32
+ # STI support.
33
33
  #
34
34
  def inherited(subclass)
35
35
  super
36
36
 
37
- # Do not smash stack: as the below method is defining a
38
- # new anonymous class, without this check this leads to
39
- # infinite recursion.
37
+ # Do not smash stack. The below +define_history_model_for+ method
38
+ # defines a new inherited class via Class.new(), thus +inherited+
39
+ # is going to be called again. By that time the subclass is still
40
+ # an anonymous one, so its +name+ method returns nil. We use this
41
+ # condition to avoid infinite recursion.
42
+ #
43
+ # Sadly, we can't avoid it by calling +.history?+, because in the
44
+ # subclass the HistoryModel hasn't been included yet.
45
+ #
40
46
  unless subclass.name.nil?
41
- ChronoModel::TimeMachine.define_inherited_history_model_for(subclass)
47
+ ChronoModel::TimeMachine.define_history_model_for(subclass)
42
48
  end
43
49
  end
44
50
  end
45
51
  end
46
52
 
47
53
  def self.define_history_model_for(model)
48
- history = Class.new(model) { include ChronoModel::TimeMachine::HistoryModel }
54
+ history = Class.new(model) do
55
+ include ChronoModel::TimeMachine::HistoryModel
56
+ end
49
57
 
50
58
  model.singleton_class.instance_eval do
51
59
  define_method(:history) { history }
52
60
  end
53
61
 
54
- history.singleton_class.instance_eval do
55
- define_method(:sti_name) { model.sti_name }
56
- end
57
-
58
62
  model.const_set :History, history
59
-
60
- return history
61
- end
62
-
63
- def self.define_inherited_history_model_for(subclass)
64
- # Define history model for the subclass
65
- history = Class.new(subclass.superclass.history)
66
- history.table_name = subclass.superclass.history.table_name
67
-
68
- # Override the STI name on the history subclass
69
- history.singleton_class.instance_eval do
70
- define_method(:sti_name) { subclass.sti_name }
71
- end
72
-
73
- # Return the subclass history via the .history method
74
- subclass.singleton_class.instance_eval do
75
- define_method(:history) { history }
76
- end
77
-
78
- # Define the History constant inside the subclass
79
- subclass.const_set :History, history
80
-
81
- history.instance_eval do
82
- # Monkey patch of ActiveRecord::Inheritance.
83
- # STI fails when a Foo::History record has Foo as type in the
84
- # inheritance column; AR expects the type to be an instance of the
85
- # current class or a descendant (or self).
86
- def find_sti_class(type_name)
87
- super(type_name + "::History")
88
- end
89
- end
90
63
  end
91
64
 
92
65
  module ClassMethods
@@ -50,21 +50,6 @@ module ChronoModel
50
50
  true
51
51
  end
52
52
 
53
- # Getting the correct quoted_table_name can be tricky when
54
- # STI is involved. If Orange < Fruit, then Orange::History < Fruit::History
55
- # (see define_inherited_history_model_for).
56
- # This means that the superclass method returns Fruit::History, which
57
- # will give us the wrong table name. What we actually want is the
58
- # superclass of Fruit::History, which is Fruit. So, we use
59
- # non_history_superclass instead. -npj
60
- def non_history_superclass(klass = self)
61
- if klass.superclass.history?
62
- non_history_superclass(klass.superclass)
63
- else
64
- klass.superclass
65
- end
66
- end
67
-
68
53
  def relation
69
54
  super.as_of_time!(Time.now)
70
55
  end
@@ -72,14 +57,15 @@ module ChronoModel
72
57
  # Fetches as of +time+ records.
73
58
  #
74
59
  def as_of(time)
75
- non_history_superclass.from(virtual_table_at(time)).as_of_time!(time)
60
+ superclass.from(virtual_table_at(time)).as_of_time!(time)
76
61
  end
77
62
 
78
- def virtual_table_at(time, name = nil)
79
- name = name ? connection.quote_table_name(name) :
80
- non_history_superclass.quoted_table_name
63
+ def virtual_table_at(time, table_name: nil)
64
+ virtual_name = table_name ?
65
+ connection.quote_table_name(table_name) :
66
+ superclass.quoted_table_name
81
67
 
82
- "(#{at(time).to_sql}) #{name}"
68
+ "(#{at(time).to_sql}) #{virtual_name}"
83
69
  end
84
70
 
85
71
  # Fetches history record at the given time
@@ -102,6 +88,37 @@ module ChronoModel
102
88
  def of(object)
103
89
  where(id: object)
104
90
  end
91
+
92
+ # The `sti_name` method returns the contents of the inheritance
93
+ # column, and it is usually the class name. The inherited class
94
+ # name has the "::History" suffix but that is never going to be
95
+ # present in the data.
96
+ #
97
+ # As such it is overriden here to return the same contents that
98
+ # the parent would have returned.
99
+ def sti_name
100
+ superclass.sti_name
101
+ end
102
+
103
+ # For STI to work, the history model needs to have the exact same
104
+ # semantics as the model it inherits from. However given it is
105
+ # actually inherited, the original AR implementation would return
106
+ # false here. But for STI sake, the history model is located in the
107
+ # same exact hierarchy location as its parent, thus this is defined in
108
+ # this override.
109
+ #
110
+ def descends_from_active_record?
111
+ superclass.descends_from_active_record?
112
+ end
113
+
114
+ private
115
+ # STI fails when a Foo::History record has Foo as type in the
116
+ # inheritance column; AR expects the type to be an instance of the
117
+ # current class or a descendant (or self).
118
+ #
119
+ def find_sti_class(type_name)
120
+ super(type_name + "::History")
121
+ end
105
122
  end
106
123
 
107
124
  # The history id is `hid`, but this cannot set as primary key
@@ -170,7 +187,7 @@ module ChronoModel
170
187
  # Returns this history entry's current record
171
188
  #
172
189
  def current_version
173
- self.class.non_history_superclass.find(rid)
190
+ self.class.superclass.find(rid)
174
191
  end
175
192
 
176
193
  def record #:nodoc:
@@ -1,3 +1,3 @@
1
1
  module ChronoModel
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -4,15 +4,15 @@ require 'support/helpers'
4
4
  describe 'models with counter cache' do
5
5
  include ChronoTest::Helpers::TimeMachine
6
6
 
7
- adapter.create_table 'sections', temporal: true, no_journal: %w( articles_count ) do |t|
8
- t.string :name
9
- t.integer :articles_count, default: 0
10
- end
7
+ adapter.create_table 'sections', temporal: true, no_journal: %w( articles_count ) do |t|
8
+ t.string :name
9
+ t.integer :articles_count, default: 0
10
+ end
11
11
 
12
- adapter.create_table 'articles', temporal: true do |t|
13
- t.string :title
14
- t.references :section
15
- end
12
+ adapter.create_table 'articles', temporal: true do |t|
13
+ t.string :title
14
+ t.references :section
15
+ end
16
16
 
17
17
  class ::Section < ActiveRecord::Base
18
18
  include ChronoModel::TimeMachine
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'support/helpers'
3
+
4
+ describe 'models with STI' do
5
+ include ChronoTest::Helpers::TimeMachine
6
+
7
+ adapter.create_table 'animals', temporal: true do |t|
8
+ t.string :type
9
+ end
10
+
11
+ class ::Animal < ActiveRecord::Base
12
+ include ChronoModel::TimeMachine
13
+ end
14
+
15
+ class ::Dog < Animal
16
+ end
17
+
18
+ class ::Goat < Animal
19
+ end
20
+
21
+ describe 'it generates the right queries' do
22
+ before do
23
+ Dog.create!
24
+ @later = Time.new
25
+ Goat.create!
26
+ end
27
+
28
+ after do
29
+ tables = ['temporal.animals', 'history.animals']
30
+ ActiveRecord::Base.connection.execute "truncate #{tables.join(', ')} cascade"
31
+ end
32
+
33
+ specify "select" do
34
+ expect(Animal.first).to_not be_nil
35
+ expect(Animal.as_of(@later).first).to_not be_nil
36
+ end
37
+
38
+ specify "count" do
39
+ expect(Animal.count).to eq(2)
40
+ expect(Animal.as_of(@later).count).to eq(1)
41
+
42
+ expect(Dog.count).to eq(1)
43
+ expect(Dog.as_of(@later).count).to eq(1)
44
+
45
+ expect(Goat.count).to eq(1)
46
+ expect(Goat.as_of(@later).count).to eq(0)
47
+ end
48
+ end
49
+ end
@@ -93,6 +93,7 @@ describe ChronoModel::TimeMachine do
93
93
  'elements' => Element::History,
94
94
  'sections' => Section::History,
95
95
  'sub_bars' => SubBar::History,
96
+ 'animals' => Animal::History,
96
97
  ) }
97
98
  end
98
99
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chrono_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcello Barnaba
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-04-08 00:00:00.000000000 Z
12
+ date: 2019-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -253,6 +253,7 @@ files:
253
253
  - spec/aruba/migrations_spec.rb
254
254
  - spec/aruba/rake_task_spec.rb
255
255
  - spec/chrono_model/adapter/counter_cache_race_spec.rb
256
+ - spec/chrono_model/adapter/sti_bug_spec.rb
256
257
  - spec/chrono_model/adapter_spec.rb
257
258
  - spec/chrono_model/conversions_spec.rb
258
259
  - spec/chrono_model/json_ops_spec.rb
@@ -308,6 +309,7 @@ test_files:
308
309
  - spec/aruba/migrations_spec.rb
309
310
  - spec/aruba/rake_task_spec.rb
310
311
  - spec/chrono_model/adapter/counter_cache_race_spec.rb
312
+ - spec/chrono_model/adapter/sti_bug_spec.rb
311
313
  - spec/chrono_model/adapter_spec.rb
312
314
  - spec/chrono_model/conversions_spec.rb
313
315
  - spec/chrono_model/json_ops_spec.rb