chrono_model 0.10.1 → 0.11.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
  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