chrono_model 0.10.1 → 0.11.0

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
  SHA1:
3
- metadata.gz: 576c9df58b2b91c8521458a12eaa036d687287fb
4
- data.tar.gz: 4529a430a634516a8c3c1007589c05ba346e0aa3
3
+ metadata.gz: 6372b41af69ba073bf8d6ea27f34c238ce3c1426
4
+ data.tar.gz: 9bda73df3fa2d169da83fedd4d499521745a61b8
5
5
  SHA512:
6
- metadata.gz: 369e1b1fc4874efb19b3b981a96727da8ea6c6cd34521ed746c2357481256c8344f64341b8e09fdebc97bac71dd235ad9fde66340e4c806b66e8aff4c0442f3f
7
- data.tar.gz: 9788dea81eb29affaed8e83200b043bae597e14bbf0292fa908f3f5fc6d2d82da7fcad37d28ef2dda3fe46e53e6ab6443c95588b0f7a4b2d27b7ecc63d9c23e5
6
+ metadata.gz: a04bfe04096c15f977c0972458e53508c81bccbf84b81df79e990f3174152af5f788f1564acf27ace77e3721ba9717a30ec8e5d68f813aaa2761528bed57da52
7
+ data.tar.gz: 66289e54ef6511b1ff005705386585f82ce5571a99763b37d522d1208dcd87c738c7a2b2d004e205ed5438ff53d28cf8cc076bf29eb5657be318f326953efd1b
@@ -56,6 +56,8 @@ module ChronoModel
56
56
  end
57
57
  end
58
58
 
59
+ # Build a preloader at the +as_of_time+ of this relation
60
+ #
59
61
  def build_preloader
60
62
  ActiveRecord::Associations::Preloader.new(as_of_time: as_of_time)
61
63
  end
@@ -68,15 +70,60 @@ module ChronoModel
68
70
  module Preloader
69
71
  attr_reader :options
70
72
 
71
- NULL_RELATION = ActiveRecord::Associations::Preloader::NULL_RELATION
72
- AS_OF_PRELOAD_SCOPE = Struct.new(:as_of_time, *NULL_RELATION.members)
73
-
73
+ # We overwrite the initializer in order to pass the +as_of_time+
74
+ # parameter above in the build_preloader
75
+ #
74
76
  def initialize(options = {})
75
77
  @options = options.freeze
76
78
  end
77
79
 
80
+ # See +ActiveRecord::Associations::Preloader::NULL_RELATION+
81
+ NULL_RELATION = ActiveRecord::Associations::Preloader::NULL_RELATION
82
+
83
+ # Extension of +NULL_RELATION+ adding an +as_of_time+ property. See
84
+ # +preload+.
85
+ AS_OF_PRELOAD_SCOPE = Struct.new(:as_of_time, *NULL_RELATION.members)
86
+
87
+ # Patches the AR Preloader (lib/active_record/associations/preloader.rb)
88
+ # in order to carry around the +as_of_time+ of the original invocation.
89
+ #
90
+ # * The +records+ are the parent records where the association is defined
91
+ # * The +associations+ are the association names involved in preloading
92
+ # * The +given_preload_scope+ is the preloading scope, that is used only
93
+ # in the :through association and it holds the intermediate records
94
+ # _through_ which the final associated records are eventually fetched.
95
+ #
96
+ # As the +preload_scope+ is passed around to all the different
97
+ # incarnations of the preloader strategies, we are using it to pass
98
+ # around the +as_of_time+ of the original query invocation, so that
99
+ # preloaded records are preloaded honoring the +as_of_time+.
100
+ #
101
+ # The +preload_scope+ is not nil only for through associations, but the
102
+ # preloader interfaces expect it to be always defined, for consistency.
103
+ # So, AR defines a +NULL_RELATION+ constant to pass around for the
104
+ # association types that do not have a preload_scope. It quacks like a
105
+ # Relation, and it contains only the methods that are used by the other
106
+ # preloader methods. We extend AR's +NULL_RELATION+ with an `as_of_time`
107
+ # property.
108
+ #
109
+ # For `:through` associations, the +given_preload_scope+ is already a
110
+ # +Relation+, that already has the +as_of_time+ getters and setters,
111
+ # so we use them here.
112
+ #
78
113
  def preload(records, associations, given_preload_scope = nil)
79
114
  if (as_of_time = options[:as_of_time])
115
+ preload_scope = if given_preload_scope.respond_to?(:as_of_time!)
116
+ given_preload_scope.as_of_time!(as_of_time)
117
+ else
118
+ null_relation_preload_scope(as_of_time, given_preload_scope)
119
+ end
120
+ end
121
+
122
+ super records, associations, preload_scope
123
+ end
124
+
125
+ private
126
+ def null_relation_preload_scope(as_of_time, given_preload_scope)
80
127
  preload_scope = AS_OF_PRELOAD_SCOPE.new
81
128
 
82
129
  preload_scope.as_of_time = as_of_time
@@ -85,12 +132,15 @@ module ChronoModel
85
132
  NULL_RELATION.members.each do |member|
86
133
  preload_scope[member] = given_preload_scope[member]
87
134
  end
88
- end
89
135
 
90
- super records, associations, preload_scope
91
- end
136
+ return preload_scope
137
+ end
92
138
 
93
139
  module Association
140
+ # Builds the preloader scope taking into account a potential
141
+ # +as_of_time+ set above in +Preloader#preload+ and coming from the
142
+ # user's query invocation.
143
+ #
94
144
  def build_scope
95
145
  scope = super
96
146
 
@@ -17,6 +17,8 @@ end
17
17
  # PG utilities
18
18
  #
19
19
  module PG
20
+ SQL_COMMENT_BEGIN = "--".freeze
21
+
20
22
  extend self
21
23
 
22
24
  def config!
@@ -30,6 +32,7 @@ module PG
30
32
 
31
33
  def make_dump(target, username, database, *options)
32
34
  exec 'pg_dump', '-f', target, '-U', username, '-d', database, *options
35
+ remove_sql_header_comments(target)
33
36
  end
34
37
 
35
38
  def load_dump(source, username, database, *options)
@@ -76,4 +79,20 @@ module PG
76
79
  ActiveRecord::Base.logger
77
80
  end
78
81
 
82
+ private
83
+
84
+ def remove_sql_header_comments(filename)
85
+ tempfile = Tempfile.open("uncommented_structure.sql")
86
+ begin
87
+ File.foreach(filename) do |line|
88
+ unless line.start_with?(SQL_COMMENT_BEGIN)
89
+ tempfile << line
90
+ end
91
+ end
92
+ ensure
93
+ tempfile.close
94
+ end
95
+ FileUtils.mv(tempfile.path, filename)
96
+ end
97
+
79
98
  end
@@ -1,3 +1,3 @@
1
1
  module ChronoModel
2
- VERSION = "0.10.1"
2
+ VERSION = "0.11.0"
3
3
  end
@@ -74,6 +74,11 @@ module ChronoTest::Helpers
74
74
  t.references :foo
75
75
  end
76
76
 
77
+ adapter.create_table 'sub_bars' do |t|
78
+ t.string :name
79
+ t.references :bar
80
+ end
81
+
77
82
  adapter.create_table 'bazs' do |t|
78
83
  t.string :name
79
84
  t.references :bar
@@ -104,17 +109,27 @@ module ChronoTest::Helpers
104
109
  include ChronoModel::TimeMachine
105
110
 
106
111
  has_many :bars
112
+ has_many :sub_bars, :through => :bars
107
113
  end
108
114
 
109
115
  class ::Bar < ActiveRecord::Base
110
116
  include ChronoModel::TimeMachine
111
117
 
112
118
  belongs_to :foo
119
+ has_many :sub_bars
113
120
  has_one :baz
114
121
 
115
122
  has_timeline :with => :foo
116
123
  end
117
124
 
125
+ class ::SubBar < ActiveRecord::Base
126
+ include ChronoModel::TimeGate
127
+
128
+ belongs_to :bar
129
+
130
+ has_timeline :with => :bar
131
+ end
132
+
118
133
  class ::Baz < ActiveRecord::Base
119
134
  include ChronoModel::TimeGate
120
135
 
@@ -16,11 +16,18 @@ describe ChronoModel::TimeMachine do
16
16
  bar = ts_eval { Bar.create! :name => 'bar', :foo => foo }
17
17
  ts_eval(bar) { update_attributes! :name => 'foo bar' }
18
18
 
19
+ #
20
+ subbar = ts_eval { SubBar.create! :name => 'sub-bar', :bar => bar }
21
+ ts_eval(subbar) { update_attributes! :name => 'bar sub-bar' }
22
+
19
23
  ts_eval(foo) { update_attributes! :name => 'new foo' }
20
24
 
21
25
  ts_eval(bar) { update_attributes! :name => 'bar bar' }
22
26
  ts_eval(bar) { update_attributes! :name => 'new bar' }
23
27
 
28
+ ts_eval(subbar) { update_attributes! :name => 'sub-bar sub-bar' }
29
+ ts_eval(subbar) { update_attributes! :name => 'new sub-bar' }
30
+
24
31
  #
25
32
  baz = Baz.create :name => 'baz', :bar => bar
26
33
 
@@ -145,6 +152,16 @@ describe ChronoModel::TimeMachine do
145
152
  it { expect(Foo.as_of(bar.ts[3]).includes(:bars).first.bars.first.name).to eq 'new bar' }
146
153
 
147
154
 
155
+ it { expect(Foo.as_of(foo.ts[0]).includes(bars: :sub_bars).first.bars).to eq [] }
156
+ it { expect(Foo.as_of(foo.ts[1]).includes(bars: :sub_bars).first.bars).to eq [] }
157
+ it { expect(Foo.as_of(foo.ts[2]).includes(bars: :sub_bars).first.bars).to eq [bar] }
158
+
159
+ it { expect(Foo.as_of(bar.ts[0]).includes(bars: :sub_bars).first.bars.first.name).to eq 'bar' }
160
+ it { expect(Foo.as_of(bar.ts[1]).includes(bars: :sub_bars).first.bars.first.name).to eq 'foo bar' }
161
+ it { expect(Foo.as_of(bar.ts[2]).includes(bars: :sub_bars).first.bars.first.name).to eq 'bar bar' }
162
+ it { expect(Foo.as_of(bar.ts[3]).includes(bars: :sub_bars).first.bars.first.name).to eq 'new bar' }
163
+
164
+
148
165
  it { expect(Bar.as_of(bar.ts[0]).includes(:foo).first.foo).to eq foo }
149
166
  it { expect(Bar.as_of(bar.ts[1]).includes(:foo).first.foo).to eq foo }
150
167
  it { expect(Bar.as_of(bar.ts[2]).includes(:foo).first.foo).to eq foo }
@@ -154,6 +171,18 @@ describe ChronoModel::TimeMachine do
154
171
  it { expect(Bar.as_of(bar.ts[1]).includes(:foo).first.foo.name).to eq 'foo bar' }
155
172
  it { expect(Bar.as_of(bar.ts[2]).includes(:foo).first.foo.name).to eq 'new foo' }
156
173
  it { expect(Bar.as_of(bar.ts[3]).includes(:foo).first.foo.name).to eq 'new foo' }
174
+
175
+
176
+ it { expect(Bar.as_of(bar.ts[0]).includes(foo: :sub_bars).first.foo).to eq foo }
177
+ it { expect(Bar.as_of(bar.ts[1]).includes(foo: :sub_bars).first.foo).to eq foo }
178
+ it { expect(Bar.as_of(bar.ts[2]).includes(foo: :sub_bars).first.foo).to eq foo }
179
+ it { expect(Bar.as_of(bar.ts[3]).includes(foo: :sub_bars).first.foo).to eq foo }
180
+
181
+ it { expect(Bar.as_of(bar.ts[0]).includes(foo: :sub_bars).first.foo.name).to eq 'foo bar' }
182
+ it { expect(Bar.as_of(bar.ts[1]).includes(foo: :sub_bars).first.foo.name).to eq 'foo bar' }
183
+ it { expect(Bar.as_of(bar.ts[2]).includes(foo: :sub_bars).first.foo.name).to eq 'new foo' }
184
+ it { expect(Bar.as_of(bar.ts[3]).includes(foo: :sub_bars).first.foo.name).to eq 'new foo' }
185
+
157
186
  end
158
187
 
159
188
  it 'doesn\'t raise RecordNotFound when no history records are found' do
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: 0.10.1
4
+ version: 0.11.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: 2017-07-25 00:00:00.000000000 Z
12
+ date: 2017-09-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord