activerecord_views 0.0.1 → 0.0.2
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 +7 -0
- data/.gitignore +1 -0
- data/Appraisals +7 -0
- data/README.markdown +7 -7
- data/Rakefile +13 -0
- data/activerecord_views.gemspec +1 -0
- data/gemfiles/rails3_2.gemfile +7 -0
- data/gemfiles/rails4_0.gemfile +7 -0
- data/lib/active_record_views/checksum_cache.rb +5 -11
- data/lib/active_record_views/extension.rb +1 -1
- data/lib/active_record_views/version.rb +1 -1
- data/lib/active_record_views.rb +39 -16
- data/spec/active_record_views_extension_spec.rb +5 -1
- data/spec/active_record_views_spec.rb +10 -7
- data/spec/internal/app/models/namespace/test_model.rb +3 -0
- data/spec/internal/app/models/namespace/test_model.sql +1 -0
- data/spec/internal/config/database.yml +1 -0
- data/spec/spec_helper.rb +18 -1
- metadata +39 -34
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a3d25414ba7a2d31b49f8604f17f5c2d73824135
|
4
|
+
data.tar.gz: a68b23bae39199bfa2ae160dfc0bb0e091a2dc88
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e41efe797b61dc942ac41b595b39b0d3c651b2e2966132b313b58f0d342f06fbb170239fc7fe079b879a7a60ea60f02688ee861b6d96f620b745fd4e7bdf2efe
|
7
|
+
data.tar.gz: 090e0cef956babdaacb0fb71199aff94a5e38f04c8b68f1c8b885c99e18f09d80a31e9c145aa0cf2d0ac66e77fe95fc8d530136d7840f34a104b260721b91784
|
data/.gitignore
CHANGED
data/Appraisals
ADDED
data/README.markdown
CHANGED
@@ -14,7 +14,7 @@ Advantages over creating views manually in migrations include:
|
|
14
14
|
|
15
15
|
Add this line to your application's `Gemfile`:
|
16
16
|
|
17
|
-
```
|
17
|
+
```ruby
|
18
18
|
gem 'activerecord_views'
|
19
19
|
```
|
20
20
|
|
@@ -22,7 +22,7 @@ gem 'activerecord_views'
|
|
22
22
|
|
23
23
|
app/models/account.rb:
|
24
24
|
|
25
|
-
```
|
25
|
+
```ruby
|
26
26
|
class Account < ActiveRecord::Base
|
27
27
|
has_many :transactions
|
28
28
|
|
@@ -33,7 +33,7 @@ end
|
|
33
33
|
|
34
34
|
app/models/transaction.rb:
|
35
35
|
|
36
|
-
```
|
36
|
+
```ruby
|
37
37
|
class Transaction < ActiveRecord::Base
|
38
38
|
belongs_to :account
|
39
39
|
end
|
@@ -41,7 +41,7 @@ end
|
|
41
41
|
|
42
42
|
app/models/account_balance.rb:
|
43
43
|
|
44
|
-
```
|
44
|
+
```ruby
|
45
45
|
class AccountBalance < ActiveRecord::Base
|
46
46
|
is_view
|
47
47
|
|
@@ -51,7 +51,7 @@ end
|
|
51
51
|
|
52
52
|
app/models/account_balance.sql:
|
53
53
|
|
54
|
-
```
|
54
|
+
```sql
|
55
55
|
SELECT accounts.id AS account_id, coalesce(sum(transactions.amount), 0) AS balance
|
56
56
|
FROM accounts
|
57
57
|
LEFT JOIN transactions ON accounts.id = transactions.account_id
|
@@ -60,7 +60,7 @@ GROUP BY accounts.id
|
|
60
60
|
|
61
61
|
Example usage:
|
62
62
|
|
63
|
-
```
|
63
|
+
```ruby
|
64
64
|
p Account.first.balance
|
65
65
|
|
66
66
|
Account.includes(:account_balance).find_each do |account|
|
@@ -70,7 +70,7 @@ end
|
|
70
70
|
|
71
71
|
### Usage outside of Rails
|
72
72
|
|
73
|
-
```
|
73
|
+
```ruby
|
74
74
|
require 'active_record'
|
75
75
|
require 'active_record_views'
|
76
76
|
ActiveRecordViews.load_path = ['.']
|
data/Rakefile
CHANGED
@@ -1 +1,14 @@
|
|
1
|
+
require 'appraisal'
|
2
|
+
require 'bundler/setup'
|
1
3
|
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
task :default do
|
9
|
+
if ENV['BUNDLE_GEMFILE'] == File.expand_path('Gemfile')
|
10
|
+
exec 'rake appraisal:all'
|
11
|
+
else
|
12
|
+
Rake::Task['spec'].invoke
|
13
|
+
end
|
14
|
+
end
|
data/activerecord_views.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency 'activerecord', ['>= 3.1', '< 4.1']
|
21
21
|
|
22
|
+
gem.add_development_dependency 'appraisal'
|
22
23
|
gem.add_development_dependency 'rspec-rails', '>= 2.14'
|
23
24
|
gem.add_development_dependency 'combustion', '>= 0.5.1'
|
24
25
|
gem.add_development_dependency 'pg'
|
@@ -1,12 +1,5 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
|
3
1
|
module ActiveRecordViews
|
4
2
|
class ChecksumCache
|
5
|
-
class Model < ActiveRecord::Base
|
6
|
-
self.table_name = 'active_record_views'
|
7
|
-
self.primary_key = 'name'
|
8
|
-
end
|
9
|
-
|
10
3
|
def initialize(connection)
|
11
4
|
@connection = connection
|
12
5
|
init_state_table!
|
@@ -19,15 +12,16 @@ module ActiveRecordViews
|
|
19
12
|
end
|
20
13
|
|
21
14
|
def get(name)
|
22
|
-
|
15
|
+
@connection.select_value("SELECT checksum FROM active_record_views WHERE name = #{@connection.quote name}")
|
23
16
|
end
|
24
17
|
|
25
18
|
def set(name, checksum)
|
26
|
-
row = Model.where(:name => name).first_or_initialize
|
27
19
|
if checksum
|
28
|
-
|
20
|
+
if @connection.update("UPDATE active_record_views SET checksum = #{@connection.quote checksum} WHERE name = #{@connection.quote name}") == 0
|
21
|
+
@connection.insert "INSERT INTO active_record_views (name, checksum) VALUES (#{@connection.quote name}, #{@connection.quote checksum})"
|
22
|
+
end
|
29
23
|
else
|
30
|
-
|
24
|
+
@connection.delete "DELETE FROM active_record_views WHERE name = #{@connection.quote name}"
|
31
25
|
end
|
32
26
|
end
|
33
27
|
end
|
data/lib/active_record_views.rb
CHANGED
@@ -25,31 +25,54 @@ module ActiveRecordViews
|
|
25
25
|
raise "could not find #{name}.sql"
|
26
26
|
end
|
27
27
|
|
28
|
+
def self.without_transaction(connection)
|
29
|
+
in_transaction = if connection.respond_to? :transaction_open?
|
30
|
+
connection.transaction_open?
|
31
|
+
else
|
32
|
+
!connection.outside_transaction?
|
33
|
+
end
|
34
|
+
|
35
|
+
if in_transaction
|
36
|
+
begin
|
37
|
+
temp_connection = connection.pool.checkout
|
38
|
+
yield temp_connection
|
39
|
+
ensure
|
40
|
+
connection.pool.checkin temp_connection
|
41
|
+
end
|
42
|
+
else
|
43
|
+
yield connection
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
28
47
|
def self.create_view(connection, name, sql)
|
29
|
-
|
30
|
-
|
31
|
-
|
48
|
+
without_transaction connection do |connection|
|
49
|
+
cache = ActiveRecordViews::ChecksumCache.new(connection)
|
50
|
+
checksum = Digest::SHA1.hexdigest(sql)
|
51
|
+
return if cache.get(name) == checksum
|
32
52
|
|
33
|
-
begin
|
34
|
-
connection.execute "CREATE OR REPLACE VIEW #{connection.quote_table_name name} AS #{sql}"
|
35
|
-
rescue ActiveRecord::StatementInvalid => original_exception
|
36
53
|
begin
|
37
|
-
connection.
|
38
|
-
|
39
|
-
|
54
|
+
connection.execute "CREATE OR REPLACE VIEW #{connection.quote_table_name name} AS #{sql}"
|
55
|
+
rescue ActiveRecord::StatementInvalid => original_exception
|
56
|
+
begin
|
57
|
+
connection.transaction :requires_new => true do
|
58
|
+
connection.execute "DROP VIEW #{connection.quote_table_name name}"
|
59
|
+
connection.execute "CREATE VIEW #{connection.quote_table_name name} AS #{sql}"
|
60
|
+
end
|
61
|
+
rescue
|
62
|
+
raise original_exception
|
40
63
|
end
|
41
|
-
rescue
|
42
|
-
raise original_exception
|
43
64
|
end
|
44
|
-
end
|
45
65
|
|
46
|
-
|
66
|
+
cache.set name, checksum
|
67
|
+
end
|
47
68
|
end
|
48
69
|
|
49
70
|
def self.drop_view(connection, name)
|
50
|
-
|
51
|
-
|
52
|
-
|
71
|
+
without_transaction connection do |connection|
|
72
|
+
cache = ActiveRecordViews::ChecksumCache.new(connection)
|
73
|
+
connection.execute "DROP VIEW IF EXISTS #{connection.quote_table_name name}"
|
74
|
+
cache.set name, nil
|
75
|
+
end
|
53
76
|
end
|
54
77
|
|
55
78
|
def self.register_for_reload(sql_path, model_path)
|
@@ -12,6 +12,11 @@ describe ActiveRecordViews::Extension do
|
|
12
12
|
expect(ExternalFileTestModel.first.name).to eq 'External SQL file'
|
13
13
|
end
|
14
14
|
|
15
|
+
it 'creates database views from namespaced external SQL files' do
|
16
|
+
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
17
|
+
expect(Namespace::TestModel.first.name).to eq 'Namespaced SQL file'
|
18
|
+
end
|
19
|
+
|
15
20
|
it 'errors if external SQL file is missing' do
|
16
21
|
expect {
|
17
22
|
MissingFileTestModel
|
@@ -61,7 +66,6 @@ describe ActiveRecordViews::Extension do
|
|
61
66
|
File.unlink sql_file
|
62
67
|
|
63
68
|
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ADROP/).once.and_call_original
|
64
|
-
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\A(?:RELEASE )?SAVEPOINT/).at_least(1).times.and_call_original
|
65
69
|
test_request
|
66
70
|
test_request # second request does not `drop_view` again
|
67
71
|
|
@@ -4,19 +4,13 @@ describe ActiveRecordViews do
|
|
4
4
|
describe '.create_view' do
|
5
5
|
let(:connection) { ActiveRecord::Base.connection }
|
6
6
|
|
7
|
-
self.use_transactional_fixtures = false
|
8
|
-
after do
|
9
|
-
connection.execute 'DROP VIEW IF EXISTS test'
|
10
|
-
connection.execute 'DROP TABLE IF EXISTS active_record_views'
|
11
|
-
end
|
12
|
-
|
13
7
|
def create_test_view(sql)
|
14
8
|
ActiveRecordViews.create_view connection, 'test', sql
|
15
9
|
end
|
16
10
|
|
17
11
|
def test_view_sql
|
18
12
|
connection.select_value <<-SQL
|
19
|
-
SELECT view_definition
|
13
|
+
SELECT trim(view_definition)
|
20
14
|
FROM information_schema.views
|
21
15
|
WHERE table_name = 'test'
|
22
16
|
SQL
|
@@ -28,6 +22,15 @@ describe ActiveRecordViews do
|
|
28
22
|
expect(test_view_sql).to eq 'SELECT 1 AS id;'
|
29
23
|
end
|
30
24
|
|
25
|
+
it 'persists views if transaction rolls back' do
|
26
|
+
expect(test_view_sql).to be_nil
|
27
|
+
connection.transaction :requires_new => true do
|
28
|
+
create_test_view 'select 1 as id'
|
29
|
+
raise ActiveRecord::Rollback
|
30
|
+
end
|
31
|
+
expect(test_view_sql).to eq 'SELECT 1 AS id;'
|
32
|
+
end
|
33
|
+
|
31
34
|
context 'with existing view' do
|
32
35
|
before do
|
33
36
|
create_test_view 'select 1 as id'
|
@@ -0,0 +1 @@
|
|
1
|
+
SELECT 1 AS id, 'Namespaced SQL file'::text AS name;
|
data/spec/spec_helper.rb
CHANGED
@@ -5,11 +5,28 @@ require 'combustion'
|
|
5
5
|
require 'active_record_views'
|
6
6
|
Combustion.initialize! :active_record, :action_controller do
|
7
7
|
config.cache_classes = false
|
8
|
+
config.active_record.whitelist_attributes = true if Rails::VERSION::MAJOR < 4
|
8
9
|
end
|
9
10
|
require 'rspec/rails'
|
10
11
|
|
11
12
|
RSpec.configure do |config|
|
12
|
-
config.use_transactional_fixtures =
|
13
|
+
config.use_transactional_fixtures = false
|
14
|
+
|
15
|
+
config.before do
|
16
|
+
connection = ActiveRecord::Base.connection
|
17
|
+
|
18
|
+
connection.execute 'DROP TABLE IF EXISTS active_record_views'
|
19
|
+
|
20
|
+
view_names = connection.select_values <<-SQL
|
21
|
+
SELECT table_name
|
22
|
+
FROM information_schema.views
|
23
|
+
WHERE table_schema = 'public';
|
24
|
+
SQL
|
25
|
+
view_names.each do |view_name|
|
26
|
+
connection.execute "DROP VIEW IF EXISTS #{connection.quote_table_name view_name}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
13
30
|
end
|
14
31
|
|
15
32
|
def test_request
|
metadata
CHANGED
@@ -1,84 +1,89 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord_views
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jason Weathered
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-06-22 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: activerecord
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '3.1'
|
22
|
-
- - <
|
20
|
+
- - "<"
|
23
21
|
- !ruby/object:Gem::Version
|
24
22
|
version: '4.1'
|
25
23
|
type: :runtime
|
26
24
|
prerelease: false
|
27
25
|
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
26
|
requirements:
|
30
|
-
- -
|
27
|
+
- - ">="
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '3.1'
|
33
|
-
- - <
|
30
|
+
- - "<"
|
34
31
|
- !ruby/object:Gem::Version
|
35
32
|
version: '4.1'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: appraisal
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: rspec-rails
|
38
49
|
requirement: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
50
|
requirements:
|
41
|
-
- -
|
51
|
+
- - ">="
|
42
52
|
- !ruby/object:Gem::Version
|
43
53
|
version: '2.14'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
56
|
version_requirements: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
57
|
requirements:
|
49
|
-
- -
|
58
|
+
- - ">="
|
50
59
|
- !ruby/object:Gem::Version
|
51
60
|
version: '2.14'
|
52
61
|
- !ruby/object:Gem::Dependency
|
53
62
|
name: combustion
|
54
63
|
requirement: !ruby/object:Gem::Requirement
|
55
|
-
none: false
|
56
64
|
requirements:
|
57
|
-
- -
|
65
|
+
- - ">="
|
58
66
|
- !ruby/object:Gem::Version
|
59
67
|
version: 0.5.1
|
60
68
|
type: :development
|
61
69
|
prerelease: false
|
62
70
|
version_requirements: !ruby/object:Gem::Requirement
|
63
|
-
none: false
|
64
71
|
requirements:
|
65
|
-
- -
|
72
|
+
- - ">="
|
66
73
|
- !ruby/object:Gem::Version
|
67
74
|
version: 0.5.1
|
68
75
|
- !ruby/object:Gem::Dependency
|
69
76
|
name: pg
|
70
77
|
requirement: !ruby/object:Gem::Requirement
|
71
|
-
none: false
|
72
78
|
requirements:
|
73
|
-
- -
|
79
|
+
- - ">="
|
74
80
|
- !ruby/object:Gem::Version
|
75
81
|
version: '0'
|
76
82
|
type: :development
|
77
83
|
prerelease: false
|
78
84
|
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
none: false
|
80
85
|
requirements:
|
81
|
-
- -
|
86
|
+
- - ">="
|
82
87
|
- !ruby/object:Gem::Version
|
83
88
|
version: '0'
|
84
89
|
description:
|
@@ -88,12 +93,15 @@ executables: []
|
|
88
93
|
extensions: []
|
89
94
|
extra_rdoc_files: []
|
90
95
|
files:
|
91
|
-
- .gitignore
|
96
|
+
- ".gitignore"
|
97
|
+
- Appraisals
|
92
98
|
- Gemfile
|
93
99
|
- LICENSE.txt
|
94
100
|
- README.markdown
|
95
101
|
- Rakefile
|
96
102
|
- activerecord_views.gemspec
|
103
|
+
- gemfiles/rails3_2.gemfile
|
104
|
+
- gemfiles/rails4_0.gemfile
|
97
105
|
- lib/active_record_views.rb
|
98
106
|
- lib/active_record_views/checksum_cache.rb
|
99
107
|
- lib/active_record_views/extension.rb
|
@@ -107,39 +115,34 @@ files:
|
|
107
115
|
- spec/internal/app/models/external_file_test_model.sql
|
108
116
|
- spec/internal/app/models/heredoc_test_model.rb
|
109
117
|
- spec/internal/app/models/missing_file_test_model.rb
|
118
|
+
- spec/internal/app/models/namespace/test_model.rb
|
119
|
+
- spec/internal/app/models/namespace/test_model.sql
|
110
120
|
- spec/internal/config/database.yml
|
111
121
|
- spec/internal/db/schema.rb
|
112
122
|
- spec/spec_helper.rb
|
113
123
|
homepage: http://github.com/jasoncodes/activerecord_views
|
114
124
|
licenses:
|
115
125
|
- MIT
|
126
|
+
metadata: {}
|
116
127
|
post_install_message:
|
117
128
|
rdoc_options: []
|
118
129
|
require_paths:
|
119
130
|
- lib
|
120
131
|
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
132
|
requirements:
|
123
|
-
- -
|
133
|
+
- - ">="
|
124
134
|
- !ruby/object:Gem::Version
|
125
135
|
version: '0'
|
126
|
-
segments:
|
127
|
-
- 0
|
128
|
-
hash: -3691843505786316541
|
129
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
-
none: false
|
131
137
|
requirements:
|
132
|
-
- -
|
138
|
+
- - ">="
|
133
139
|
- !ruby/object:Gem::Version
|
134
140
|
version: '0'
|
135
|
-
segments:
|
136
|
-
- 0
|
137
|
-
hash: -3691843505786316541
|
138
141
|
requirements: []
|
139
142
|
rubyforge_project:
|
140
|
-
rubygems_version:
|
143
|
+
rubygems_version: 2.2.2
|
141
144
|
signing_key:
|
142
|
-
specification_version:
|
145
|
+
specification_version: 4
|
143
146
|
summary: Automatic database view creation for ActiveRecord
|
144
147
|
test_files:
|
145
148
|
- spec/active_record_views_extension_spec.rb
|
@@ -148,6 +151,8 @@ test_files:
|
|
148
151
|
- spec/internal/app/models/external_file_test_model.sql
|
149
152
|
- spec/internal/app/models/heredoc_test_model.rb
|
150
153
|
- spec/internal/app/models/missing_file_test_model.rb
|
154
|
+
- spec/internal/app/models/namespace/test_model.rb
|
155
|
+
- spec/internal/app/models/namespace/test_model.sql
|
151
156
|
- spec/internal/config/database.yml
|
152
157
|
- spec/internal/db/schema.rb
|
153
158
|
- spec/spec_helper.rb
|