activerecord_views 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|