activerecord_views 0.0.1
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.
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.markdown +82 -0
- data/Rakefile +1 -0
- data/activerecord_views.gemspec +25 -0
- data/lib/active_record_views/checksum_cache.rb +34 -0
- data/lib/active_record_views/extension.rb +24 -0
- data/lib/active_record_views/railtie.rb +18 -0
- data/lib/active_record_views/registered_view.rb +38 -0
- data/lib/active_record_views/version.rb +3 -0
- data/lib/active_record_views.rb +66 -0
- data/lib/activerecord_views.rb +1 -0
- data/spec/active_record_views_extension_spec.rb +82 -0
- data/spec/active_record_views_spec.rb +70 -0
- data/spec/internal/app/models/external_file_test_model.rb +3 -0
- data/spec/internal/app/models/external_file_test_model.sql +1 -0
- data/spec/internal/app/models/heredoc_test_model.rb +5 -0
- data/spec/internal/app/models/missing_file_test_model.rb +3 -0
- data/spec/internal/config/database.yml +3 -0
- data/spec/internal/db/schema.rb +2 -0
- data/spec/spec_helper.rb +39 -0
- metadata +153 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Jason Weathered
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# ActiveRecordViews
|
2
|
+
|
3
|
+
ActiveRecordViews makes it easy to create and update PostgreSQL database views for ActiveRecord models.
|
4
|
+
|
5
|
+
Advantages over creating views manually in migrations include:
|
6
|
+
|
7
|
+
* Automatic reloading in development mode.
|
8
|
+
This avoids the need to to run `rake db:migrate:redo` after every change.
|
9
|
+
|
10
|
+
* Keeps view changes in a single SQL file instead of spread across multiple migration files.
|
11
|
+
This allows changes to views to be easily reviewed with `git diff`.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's `Gemfile`:
|
16
|
+
|
17
|
+
```
|
18
|
+
gem 'activerecord_views'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
app/models/account.rb:
|
24
|
+
|
25
|
+
``` ruby
|
26
|
+
class Account < ActiveRecord::Base
|
27
|
+
has_many :transactions
|
28
|
+
|
29
|
+
has_one :account_balance
|
30
|
+
delegate :balance, :to => :account_balance
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
app/models/transaction.rb:
|
35
|
+
|
36
|
+
``` ruby
|
37
|
+
class Transaction < ActiveRecord::Base
|
38
|
+
belongs_to :account
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
app/models/account_balance.rb:
|
43
|
+
|
44
|
+
``` ruby
|
45
|
+
class AccountBalance < ActiveRecord::Base
|
46
|
+
is_view
|
47
|
+
|
48
|
+
belongs_to :account
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
app/models/account_balance.sql:
|
53
|
+
|
54
|
+
``` sql
|
55
|
+
SELECT accounts.id AS account_id, coalesce(sum(transactions.amount), 0) AS balance
|
56
|
+
FROM accounts
|
57
|
+
LEFT JOIN transactions ON accounts.id = transactions.account_id
|
58
|
+
GROUP BY accounts.id
|
59
|
+
```
|
60
|
+
|
61
|
+
Example usage:
|
62
|
+
|
63
|
+
``` ruby
|
64
|
+
p Account.first.balance
|
65
|
+
|
66
|
+
Account.includes(:account_balance).find_each do |account|
|
67
|
+
p account.balance
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
### Usage outside of Rails
|
72
|
+
|
73
|
+
``` ruby
|
74
|
+
require 'active_record'
|
75
|
+
require 'active_record_views'
|
76
|
+
ActiveRecordViews.load_path = ['.']
|
77
|
+
ActiveRecordViews.init!
|
78
|
+
```
|
79
|
+
|
80
|
+
## License
|
81
|
+
|
82
|
+
MIT
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'active_record_views/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'activerecord_views'
|
8
|
+
gem.version = ActiveRecordViews::VERSION
|
9
|
+
gem.authors = ['Jason Weathered']
|
10
|
+
gem.email = ['jason@jasoncodes.com']
|
11
|
+
gem.summary = %q{Automatic database view creation for ActiveRecord}
|
12
|
+
gem.homepage = 'http://github.com/jasoncodes/activerecord_views'
|
13
|
+
gem.license = 'MIT'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'activerecord', ['>= 3.1', '< 4.1']
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rspec-rails', '>= 2.14'
|
23
|
+
gem.add_development_dependency 'combustion', '>= 0.5.1'
|
24
|
+
gem.add_development_dependency 'pg'
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module ActiveRecordViews
|
4
|
+
class ChecksumCache
|
5
|
+
class Model < ActiveRecord::Base
|
6
|
+
self.table_name = 'active_record_views'
|
7
|
+
self.primary_key = 'name'
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(connection)
|
11
|
+
@connection = connection
|
12
|
+
init_state_table!
|
13
|
+
end
|
14
|
+
|
15
|
+
def init_state_table!
|
16
|
+
unless @connection.table_exists?('active_record_views')
|
17
|
+
@connection.execute 'CREATE TABLE active_record_views(name text PRIMARY KEY, checksum text NOT NULL);'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(name)
|
22
|
+
Model.where(:name => name).first_or_initialize.checksum
|
23
|
+
end
|
24
|
+
|
25
|
+
def set(name, checksum)
|
26
|
+
row = Model.where(:name => name).first_or_initialize
|
27
|
+
if checksum
|
28
|
+
row.update_attributes! :checksum => checksum
|
29
|
+
else
|
30
|
+
row.destroy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecordViews
|
2
|
+
module Extension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def self.currently_migrating?
|
6
|
+
if defined? Rake
|
7
|
+
Rake.application.top_level_tasks.include?('db:migrate')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def is_view(sql = nil)
|
13
|
+
sql ||= begin
|
14
|
+
sql_path = ActiveRecordViews.find_sql_file(self.name.underscore)
|
15
|
+
ActiveRecordViews.register_for_reload self, sql_path
|
16
|
+
File.read sql_path
|
17
|
+
end
|
18
|
+
unless ActiveRecordViews::Extension.currently_migrating?
|
19
|
+
ActiveRecordViews.create_view self.connection, self.table_name, sql
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveRecordViews
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
initializer 'active_record_views' do |app|
|
4
|
+
ActiveSupport.on_load :active_record do
|
5
|
+
ActiveRecordViews.sql_load_path << Rails.root + 'app/models'
|
6
|
+
ActiveRecordViews.init!
|
7
|
+
end
|
8
|
+
|
9
|
+
ActiveSupport.on_load :action_controller do
|
10
|
+
unless app.config.cache_classes
|
11
|
+
ActionDispatch::Callbacks.before do
|
12
|
+
ActiveRecordViews.reload_stale_views!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActiveRecordViews
|
2
|
+
class RegisteredView
|
3
|
+
attr_reader :model_class, :sql_path
|
4
|
+
|
5
|
+
def initialize(model_class, sql_path)
|
6
|
+
@model_class_name = model_class.name
|
7
|
+
@sql_path = sql_path
|
8
|
+
update_timestamp!
|
9
|
+
end
|
10
|
+
|
11
|
+
def model_class
|
12
|
+
@model_class_name.constantize
|
13
|
+
end
|
14
|
+
|
15
|
+
def stale?
|
16
|
+
sql_timestamp != @cached_sql_timestamp
|
17
|
+
end
|
18
|
+
|
19
|
+
def reload!
|
20
|
+
if File.exists? sql_path
|
21
|
+
ActiveRecordViews.create_view model_class.connection, model_class.table_name, File.read(sql_path)
|
22
|
+
else
|
23
|
+
ActiveRecordViews.drop_view model_class.connection, model_class.table_name
|
24
|
+
end
|
25
|
+
update_timestamp!
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def sql_timestamp
|
31
|
+
File.exists?(sql_path) ? File.mtime(sql_path) : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_timestamp!
|
35
|
+
@cached_sql_timestamp = sql_timestamp
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'active_record_views/version'
|
2
|
+
require 'active_record_views/railtie' if defined? Rails
|
3
|
+
require 'active_record_views/registered_view'
|
4
|
+
require 'active_record_views/checksum_cache'
|
5
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
6
|
+
require 'digest/sha1'
|
7
|
+
|
8
|
+
module ActiveRecordViews
|
9
|
+
mattr_accessor :sql_load_path
|
10
|
+
self.sql_load_path = []
|
11
|
+
|
12
|
+
mattr_accessor :registered_views
|
13
|
+
self.registered_views = []
|
14
|
+
|
15
|
+
def self.init!
|
16
|
+
require 'active_record_views/extension'
|
17
|
+
::ActiveRecord::Base.send :include, ActiveRecordViews::Extension
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.find_sql_file(name)
|
21
|
+
self.sql_load_path.each do |dir|
|
22
|
+
path = "#{dir}/#{name}.sql"
|
23
|
+
return path if File.exists?(path)
|
24
|
+
end
|
25
|
+
raise "could not find #{name}.sql"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.create_view(connection, name, sql)
|
29
|
+
cache = ActiveRecordViews::ChecksumCache.new(connection)
|
30
|
+
checksum = Digest::SHA1.hexdigest(sql)
|
31
|
+
return if cache.get(name) == checksum
|
32
|
+
|
33
|
+
begin
|
34
|
+
connection.execute "CREATE OR REPLACE VIEW #{connection.quote_table_name name} AS #{sql}"
|
35
|
+
rescue ActiveRecord::StatementInvalid => original_exception
|
36
|
+
begin
|
37
|
+
connection.transaction :requires_new => true do
|
38
|
+
connection.execute "DROP VIEW #{connection.quote_table_name name}"
|
39
|
+
connection.execute "CREATE VIEW #{connection.quote_table_name name} AS #{sql}"
|
40
|
+
end
|
41
|
+
rescue
|
42
|
+
raise original_exception
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
cache.set name, checksum
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.drop_view(connection, name)
|
50
|
+
cache = ActiveRecordViews::ChecksumCache.new(connection)
|
51
|
+
connection.execute "DROP VIEW IF EXISTS #{connection.quote_table_name name}"
|
52
|
+
cache.set name, nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.register_for_reload(sql_path, model_path)
|
56
|
+
self.registered_views << RegisteredView.new(sql_path, model_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.reload_stale_views!
|
60
|
+
self.registered_views.each do |registered_view|
|
61
|
+
if registered_view.stale?
|
62
|
+
registered_view.reload!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_record_views'
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecordViews::Extension do
|
4
|
+
describe '.as_view' do
|
5
|
+
it 'creates database views from heredocs' do
|
6
|
+
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
7
|
+
expect(HeredocTestModel.first.name).to eq 'Here document'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'creates database views from external SQL files' do
|
11
|
+
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
12
|
+
expect(ExternalFileTestModel.first.name).to eq 'External SQL file'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'errors if external SQL file is missing' do
|
16
|
+
expect {
|
17
|
+
MissingFileTestModel
|
18
|
+
}.to raise_error RuntimeError, /could not find missing_file_test_model.sql/
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'reloads the database view when external SQL file is modified' do
|
22
|
+
%w[foo bar baz].each do |sql|
|
23
|
+
expect(ActiveRecordViews).to receive(:create_view).with(
|
24
|
+
anything,
|
25
|
+
'modified_file_test_models',
|
26
|
+
sql
|
27
|
+
).once.ordered
|
28
|
+
end
|
29
|
+
|
30
|
+
with_temp_sql_dir do |temp_dir|
|
31
|
+
sql_file = File.join(temp_dir, 'modified_file_test_model.sql')
|
32
|
+
update_file sql_file, 'foo'
|
33
|
+
|
34
|
+
class ModifiedFileTestModel < ActiveRecord::Base
|
35
|
+
is_view
|
36
|
+
end
|
37
|
+
|
38
|
+
update_file sql_file, 'bar'
|
39
|
+
|
40
|
+
test_request
|
41
|
+
test_request # second request does not `create_view` again
|
42
|
+
|
43
|
+
update_file sql_file, 'baz'
|
44
|
+
|
45
|
+
test_request
|
46
|
+
end
|
47
|
+
test_request # trigger cleanup
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'drops the view if the external SQL file is deleted' do
|
51
|
+
with_temp_sql_dir do |temp_dir|
|
52
|
+
sql_file = File.join(temp_dir, 'deleted_file_test_model.sql')
|
53
|
+
File.write sql_file, "SELECT 1 AS id, 'delete test'::text AS name"
|
54
|
+
|
55
|
+
class DeletedFileTestModel < ActiveRecord::Base
|
56
|
+
is_view
|
57
|
+
end
|
58
|
+
|
59
|
+
expect(DeletedFileTestModel.first.name).to eq 'delete test'
|
60
|
+
|
61
|
+
File.unlink sql_file
|
62
|
+
|
63
|
+
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
|
+
test_request
|
66
|
+
test_request # second request does not `drop_view` again
|
67
|
+
|
68
|
+
expect {
|
69
|
+
DeletedFileTestModel.first.name
|
70
|
+
}.to raise_error ActiveRecord::StatementInvalid, /relation "deleted_file_test_models" does not exist/
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'does not create if database view is initially up to date' do
|
75
|
+
ActiveRecordViews.create_view ActiveRecord::Base.connection, 'initial_create_test_models', 'SELECT 42 as id'
|
76
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ACREATE (?:OR REPLACE )?VIEW/).never
|
77
|
+
class InitialCreateTestModel < ActiveRecord::Base
|
78
|
+
is_view 'SELECT 42 as id'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecordViews do
|
4
|
+
describe '.create_view' do
|
5
|
+
let(:connection) { ActiveRecord::Base.connection }
|
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
|
+
def create_test_view(sql)
|
14
|
+
ActiveRecordViews.create_view connection, 'test', sql
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_view_sql
|
18
|
+
connection.select_value <<-SQL
|
19
|
+
SELECT view_definition
|
20
|
+
FROM information_schema.views
|
21
|
+
WHERE table_name = 'test'
|
22
|
+
SQL
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'creates database view' do
|
26
|
+
expect(test_view_sql).to be_nil
|
27
|
+
create_test_view 'select 1 as id'
|
28
|
+
expect(test_view_sql).to eq 'SELECT 1 AS id;'
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with existing view' do
|
32
|
+
before do
|
33
|
+
create_test_view 'select 1 as id'
|
34
|
+
expect(test_view_sql).to eq 'SELECT 1 AS id;'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'updates view with compatible change' do
|
38
|
+
create_test_view 'select 2 as id'
|
39
|
+
expect(test_view_sql).to eq 'SELECT 2 AS id;'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'recreates view with incompatible change' do
|
43
|
+
create_test_view "select 'foo'::text as name"
|
44
|
+
expect(test_view_sql).to eq "SELECT 'foo'::text AS name;"
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'having a dependant view' do
|
48
|
+
before do
|
49
|
+
connection.execute 'CREATE VIEW dependency AS SELECT * FROM test'
|
50
|
+
end
|
51
|
+
|
52
|
+
after do
|
53
|
+
connection.execute 'DROP VIEW dependency'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'updates view with compatible change' do
|
57
|
+
create_test_view 'select 2 as id'
|
58
|
+
expect(test_view_sql).to eq 'SELECT 2 AS id;'
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'fails to update view with incompatible signature change' do
|
62
|
+
expect {
|
63
|
+
create_test_view "select 'foo'::text as name"
|
64
|
+
}.to raise_error ActiveRecord::StatementInvalid, /cannot change name of view column/
|
65
|
+
expect(test_view_sql).to eq 'SELECT 1 AS id;'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
SELECT 1 AS id, 'External SQL file'::text AS name;
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'combustion'
|
5
|
+
require 'active_record_views'
|
6
|
+
Combustion.initialize! :active_record, :action_controller do
|
7
|
+
config.cache_classes = false
|
8
|
+
end
|
9
|
+
require 'rspec/rails'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.use_transactional_fixtures = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_request
|
16
|
+
begin
|
17
|
+
Rails.application.call({'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/'})
|
18
|
+
rescue ActionController::RoutingError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def with_temp_sql_dir
|
23
|
+
Dir.mktmpdir do |temp_dir|
|
24
|
+
begin
|
25
|
+
old_sql_load_path = ActiveRecordViews.sql_load_path
|
26
|
+
ActiveRecordViews.sql_load_path = [temp_dir] + old_sql_load_path
|
27
|
+
yield temp_dir
|
28
|
+
ensure
|
29
|
+
ActiveRecordViews.sql_load_path = old_sql_load_path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_file(file, new_content)
|
35
|
+
time = File.exists?(file) ? File.mtime(file) : Time.parse('2012-01-01')
|
36
|
+
time = time + 1
|
37
|
+
File.write file, new_content
|
38
|
+
File.utime time, time, file
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord_views
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jason Weathered
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.1'
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '4.1'
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.1'
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '4.1'
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec-rails
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.14'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '2.14'
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: combustion
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 0.5.1
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.5.1
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: pg
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
description:
|
85
|
+
email:
|
86
|
+
- jason@jasoncodes.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE.txt
|
94
|
+
- README.markdown
|
95
|
+
- Rakefile
|
96
|
+
- activerecord_views.gemspec
|
97
|
+
- lib/active_record_views.rb
|
98
|
+
- lib/active_record_views/checksum_cache.rb
|
99
|
+
- lib/active_record_views/extension.rb
|
100
|
+
- lib/active_record_views/railtie.rb
|
101
|
+
- lib/active_record_views/registered_view.rb
|
102
|
+
- lib/active_record_views/version.rb
|
103
|
+
- lib/activerecord_views.rb
|
104
|
+
- spec/active_record_views_extension_spec.rb
|
105
|
+
- spec/active_record_views_spec.rb
|
106
|
+
- spec/internal/app/models/external_file_test_model.rb
|
107
|
+
- spec/internal/app/models/external_file_test_model.sql
|
108
|
+
- spec/internal/app/models/heredoc_test_model.rb
|
109
|
+
- spec/internal/app/models/missing_file_test_model.rb
|
110
|
+
- spec/internal/config/database.yml
|
111
|
+
- spec/internal/db/schema.rb
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
homepage: http://github.com/jasoncodes/activerecord_views
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
hash: -3691843505786316541
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ! '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
segments:
|
136
|
+
- 0
|
137
|
+
hash: -3691843505786316541
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 1.8.24
|
141
|
+
signing_key:
|
142
|
+
specification_version: 3
|
143
|
+
summary: Automatic database view creation for ActiveRecord
|
144
|
+
test_files:
|
145
|
+
- spec/active_record_views_extension_spec.rb
|
146
|
+
- spec/active_record_views_spec.rb
|
147
|
+
- spec/internal/app/models/external_file_test_model.rb
|
148
|
+
- spec/internal/app/models/external_file_test_model.sql
|
149
|
+
- spec/internal/app/models/heredoc_test_model.rb
|
150
|
+
- spec/internal/app/models/missing_file_test_model.rb
|
151
|
+
- spec/internal/config/database.yml
|
152
|
+
- spec/internal/db/schema.rb
|
153
|
+
- spec/spec_helper.rb
|