activerecord_views 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.markdown +2 -0
- data/lib/active_record_views/checksum_cache.rb +5 -1
- data/lib/active_record_views/extension.rb +16 -1
- data/lib/active_record_views/version.rb +1 -1
- data/spec/active_record_views_checksum_cache_spec.rb +6 -3
- data/spec/active_record_views_extension_spec.rb +30 -4
- data/spec/active_record_views_spec.rb +1 -0
- data/spec/tasks_spec.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3c9db1b593bf137570cde8f2e4fcaf6837aa414
|
4
|
+
data.tar.gz: 81143c5d6e77cfe47971b9bca74dde2f8a1c3847
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcf258e4d370ee70ad3a96488271d206ec7e8fe27dce4c123b9a176a10d12cd06cf2c1f3d7f2c21446a98045015967cbc4130d8414e48db0b9d1081e8afb6691
|
7
|
+
data.tar.gz: 684a7373b1167ab08a20f565e7f33c93cc5420e85ec34186488af74f36772672927dcaa993198273d83f568fd05ffae05eaf2c8a40d55096a80af1ea9785ac8d
|
data/README.markdown
CHANGED
@@ -121,6 +121,8 @@ AccountBalance.refresh_view!
|
|
121
121
|
AccountBalance.view_populated? # => true
|
122
122
|
```
|
123
123
|
|
124
|
+
ActiveRecordViews records when a view was last refreshed. This is often useful for giving users an idea of how stale data. To retrieve this timestamp, call `.refreshed_at` on the model:
|
125
|
+
|
124
126
|
PostgreSQL 9.4 supports refreshing materialized views concurrently. This allows other processes to continue reading old cached data while the view is being updated. To use this feature you must have define a unique index on the materialized view:
|
125
127
|
|
126
128
|
```sql
|
@@ -22,8 +22,12 @@ module ActiveRecordViews
|
|
22
22
|
@connection.execute "ALTER TABLE active_record_views ADD COLUMN options json NOT NULL DEFAULT '{}';"
|
23
23
|
end
|
24
24
|
|
25
|
+
if table_exists && !@connection.column_exists?('active_record_views', 'refreshed_at')
|
26
|
+
@connection.execute "ALTER TABLE active_record_views ADD COLUMN refreshed_at timestamp;"
|
27
|
+
end
|
28
|
+
|
25
29
|
unless table_exists
|
26
|
-
@connection.execute "CREATE TABLE active_record_views(name text PRIMARY KEY, class_name text NOT NULL UNIQUE, checksum text NOT NULL, options json NOT NULL DEFAULT '{}');"
|
30
|
+
@connection.execute "CREATE TABLE active_record_views(name text PRIMARY KEY, class_name text NOT NULL UNIQUE, checksum text NOT NULL, options json NOT NULL DEFAULT '{}', refreshed_at timestamp);"
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
@@ -48,7 +48,10 @@ module ActiveRecordViews
|
|
48
48
|
raise ArgumentError, 'invalid concurrent option'
|
49
49
|
end
|
50
50
|
|
51
|
-
connection.
|
51
|
+
connection.transaction do
|
52
|
+
connection.execute "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if concurrent} #{connection.quote_table_name self.table_name};"
|
53
|
+
connection.execute "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = #{connection.quote self.table_name};"
|
54
|
+
end
|
52
55
|
end
|
53
56
|
|
54
57
|
def view_populated?
|
@@ -64,6 +67,18 @@ module ActiveRecordViews
|
|
64
67
|
|
65
68
|
ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(value)
|
66
69
|
end
|
70
|
+
|
71
|
+
def refreshed_at
|
72
|
+
value = connection.select_value(<<-SQL.squish)
|
73
|
+
SELECT refreshed_at
|
74
|
+
FROM active_record_views
|
75
|
+
WHERE name = #{connection.quote self.table_name};
|
76
|
+
SQL
|
77
|
+
|
78
|
+
if value
|
79
|
+
ActiveSupport::TimeZone['UTC'].parse(value)
|
80
|
+
end
|
81
|
+
end
|
67
82
|
end
|
68
83
|
end
|
69
84
|
end
|
@@ -62,7 +62,7 @@ describe ActiveRecordViews::ChecksumCache do
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
context 'existing table without options' do
|
65
|
+
context 'existing table without options or refreshed at timestamp' do
|
66
66
|
before do
|
67
67
|
connection.execute 'CREATE TABLE active_record_views(name text PRIMARY KEY, class_name text NOT NULL UNIQUE, checksum text NOT NULL);'
|
68
68
|
|
@@ -71,14 +71,17 @@ describe ActiveRecordViews::ChecksumCache do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'upgrades the table without losing data' do
|
74
|
-
expect(ActiveRecord::Base.connection).to receive(:execute).with("ALTER TABLE active_record_views ADD COLUMN options json NOT NULL DEFAULT '{}';").once.and_call_original
|
75
|
-
|
76
74
|
expect(connection.column_exists?('active_record_views', 'options')).to eq false
|
75
|
+
expect(connection.column_exists?('active_record_views', 'refreshed_at')).to eq false
|
77
76
|
expect(ActiveRecordViews.view_exists?(connection, 'test_view')).to eq true
|
78
77
|
|
78
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with("ALTER TABLE active_record_views ADD COLUMN options json NOT NULL DEFAULT '{}';").once.and_call_original
|
79
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with("ALTER TABLE active_record_views ADD COLUMN refreshed_at timestamp;").once.and_call_original
|
80
|
+
|
79
81
|
ActiveRecordViews::ChecksumCache.new(connection)
|
80
82
|
|
81
83
|
expect(connection.column_exists?('active_record_views', 'options')).to eq true
|
84
|
+
expect(connection.column_exists?('active_record_views', 'refreshed_at')).to eq true
|
82
85
|
expect(ActiveRecordViews.view_exists?(connection, 'test_view')).to eq true
|
83
86
|
end
|
84
87
|
end
|
@@ -149,8 +149,14 @@ describe ActiveRecordViews::Extension do
|
|
149
149
|
}.to raise_error ActiveRecord::StatementInvalid, /materialized view "materialized_view_test_models" has not been populated/
|
150
150
|
|
151
151
|
expect(MaterializedViewTestModel.view_populated?).to eq false
|
152
|
+
expect(MaterializedViewTestModel.refreshed_at).to eq nil
|
153
|
+
|
152
154
|
MaterializedViewTestModel.refresh_view!
|
155
|
+
|
153
156
|
expect(MaterializedViewTestModel.view_populated?).to eq true
|
157
|
+
expect(MaterializedViewTestModel.refreshed_at).to be_a ActiveSupport::TimeWithZone
|
158
|
+
expect(MaterializedViewTestModel.refreshed_at.zone).to eq 'UTC'
|
159
|
+
expect(MaterializedViewTestModel.refreshed_at).to be_within(1.second).of Time.now
|
154
160
|
|
155
161
|
expect(MaterializedViewTestModel.first!.id).to eq 123
|
156
162
|
|
@@ -182,8 +188,18 @@ describe ActiveRecordViews::Extension do
|
|
182
188
|
end
|
183
189
|
MaterializedViewConcurrentRefreshTestModel.refresh_view!
|
184
190
|
|
185
|
-
|
186
|
-
|
191
|
+
[
|
192
|
+
'BEGIN',
|
193
|
+
'REFRESH MATERIALIZED VIEW "materialized_view_refresh_test_models";',
|
194
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_refresh_test_models';",
|
195
|
+
'COMMIT',
|
196
|
+
'BEGIN',
|
197
|
+
'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_concurrent_refresh_test_models";',
|
198
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_concurrent_refresh_test_models';",
|
199
|
+
'COMMIT',
|
200
|
+
].each do |sql|
|
201
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with(sql).once.and_call_original
|
202
|
+
end
|
187
203
|
|
188
204
|
MaterializedViewRefreshTestModel.refresh_view!
|
189
205
|
MaterializedViewConcurrentRefreshTestModel.refresh_view! concurrent: true
|
@@ -194,8 +210,18 @@ describe ActiveRecordViews::Extension do
|
|
194
210
|
is_view 'SELECT 1 AS id;', materialized: true, unique_columns: [:id]
|
195
211
|
end
|
196
212
|
|
197
|
-
|
198
|
-
|
213
|
+
[
|
214
|
+
'BEGIN',
|
215
|
+
'REFRESH MATERIALIZED VIEW "materialized_view_auto_refresh_test_models";',
|
216
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
|
217
|
+
'COMMIT',
|
218
|
+
'BEGIN',
|
219
|
+
'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_auto_refresh_test_models";',
|
220
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
|
221
|
+
'COMMIT',
|
222
|
+
].each do |sql|
|
223
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with(sql).once.and_call_original
|
224
|
+
end
|
199
225
|
|
200
226
|
MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
|
201
227
|
MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
|
data/spec/tasks_spec.rb
CHANGED
@@ -11,6 +11,6 @@ describe 'rake db:structure:dump' do
|
|
11
11
|
sql = File.read('spec/internal/db/structure.sql')
|
12
12
|
FileUtils.rm_f 'spec/internal/db/structure.sql'
|
13
13
|
|
14
|
-
expect(sql).to match
|
14
|
+
expect(sql).to match(/COPY active_record_views.+test_view\tTestView/m)
|
15
15
|
end
|
16
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord_views
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Weathered
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -155,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
155
|
version: '0'
|
156
156
|
requirements: []
|
157
157
|
rubyforge_project:
|
158
|
-
rubygems_version: 2.
|
158
|
+
rubygems_version: 2.4.7
|
159
159
|
signing_key:
|
160
160
|
specification_version: 4
|
161
161
|
summary: Automatic database view creation for ActiveRecord
|