activerecord_views 0.0.3 → 0.0.4
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 +4 -4
- data/lib/active_record_views/checksum_cache.rb +15 -7
- data/lib/active_record_views/extension.rb +10 -2
- data/lib/active_record_views/registered_view.rb +1 -1
- data/lib/active_record_views/version.rb +1 -1
- data/lib/active_record_views.rb +42 -18
- data/spec/active_record_views_checksum_cache_spec.rb +45 -0
- data/spec/active_record_views_extension_spec.rb +14 -1
- data/spec/active_record_views_spec.rb +52 -6
- data/spec/internal/app/models/erb_test_model.rb +3 -0
- data/spec/internal/app/models/erb_test_model.sql.erb +1 -0
- data/spec/internal/app/models/modified_a.rb +3 -0
- data/spec/internal/app/models/modified_b.rb +3 -0
- data/spec/spec_helper.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be3aa1741d6dced3c4afcf32a941d3821ec0eafc
|
4
|
+
data.tar.gz: f29929115236d12395bad521420b03a4f799f382
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c623e3dec891c353d1bb3d2b4a6466a2ea4ea520d5c4082007a3303c358982b6e832d251e9127b37ce0ba1b366cb866547eaa0ae57173fb868d0c4683582e4f
|
7
|
+
data.tar.gz: 7cde80415bc90e545f7cdcc6f987525c7c53050e59a2e26b8a83838f40c0f9e9cbbeae29ad15568e4a4e886a028b3bfce359167453d56f7af0d80a89111e3d3d
|
@@ -6,19 +6,27 @@ module ActiveRecordViews
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def init_state_table!
|
9
|
-
|
10
|
-
|
9
|
+
table_exists = @connection.table_exists?('active_record_views')
|
10
|
+
|
11
|
+
if table_exists && !@connection.column_exists?('active_record_views', 'class_name')
|
12
|
+
@connection.execute 'DROP TABLE active_record_views;'
|
13
|
+
table_exists = false
|
14
|
+
end
|
15
|
+
|
16
|
+
unless table_exists
|
17
|
+
@connection.execute 'CREATE TABLE active_record_views(name text PRIMARY KEY, class_name text NOT NULL UNIQUE, checksum text NOT NULL);'
|
11
18
|
end
|
12
19
|
end
|
13
20
|
|
14
21
|
def get(name)
|
15
|
-
@connection.
|
22
|
+
@connection.select_one("SELECT class_name, checksum FROM active_record_views WHERE name = #{@connection.quote name}").try(:symbolize_keys)
|
16
23
|
end
|
17
24
|
|
18
|
-
def set(name,
|
19
|
-
if
|
20
|
-
|
21
|
-
|
25
|
+
def set(name, data)
|
26
|
+
if data
|
27
|
+
data.assert_valid_keys :class_name, :checksum
|
28
|
+
if @connection.update("UPDATE active_record_views SET class_name = #{@connection.quote data[:class_name]}, checksum = #{@connection.quote data[:checksum]} WHERE name = #{@connection.quote name}") == 0
|
29
|
+
@connection.insert "INSERT INTO active_record_views (name, class_name, checksum) VALUES (#{@connection.quote name}, #{@connection.quote data[:class_name]}, #{@connection.quote data[:checksum]})"
|
22
30
|
end
|
23
31
|
else
|
24
32
|
@connection.delete "DELETE FROM active_record_views WHERE name = #{@connection.quote name}"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
1
3
|
module ActiveRecordViews
|
2
4
|
module Extension
|
3
5
|
extend ActiveSupport::Concern
|
@@ -13,10 +15,16 @@ module ActiveRecordViews
|
|
13
15
|
sql ||= begin
|
14
16
|
sql_path = ActiveRecordViews.find_sql_file(self.name.underscore)
|
15
17
|
ActiveRecordViews.register_for_reload self, sql_path
|
16
|
-
|
18
|
+
|
19
|
+
if sql_path.end_with?('.erb')
|
20
|
+
ERB.new(File.read(sql_path)).result
|
21
|
+
else
|
22
|
+
File.read(sql_path)
|
23
|
+
end
|
17
24
|
end
|
25
|
+
|
18
26
|
unless ActiveRecordViews::Extension.currently_migrating?
|
19
|
-
ActiveRecordViews.create_view self.connection, self.table_name, sql
|
27
|
+
ActiveRecordViews.create_view self.connection, self.table_name, self.name, sql
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
@@ -18,7 +18,7 @@ module ActiveRecordViews
|
|
18
18
|
|
19
19
|
def reload!
|
20
20
|
if File.exists? sql_path
|
21
|
-
ActiveRecordViews.create_view model_class.connection, model_class.table_name, File.read(sql_path)
|
21
|
+
ActiveRecordViews.create_view model_class.connection, model_class.table_name, model_class.name, File.read(sql_path)
|
22
22
|
else
|
23
23
|
ActiveRecordViews.drop_view model_class.connection, model_class.table_name
|
24
24
|
end
|
data/lib/active_record_views.rb
CHANGED
@@ -21,6 +21,8 @@ module ActiveRecordViews
|
|
21
21
|
self.sql_load_path.each do |dir|
|
22
22
|
path = "#{dir}/#{name}.sql"
|
23
23
|
return path if File.exists?(path)
|
24
|
+
path = path + '.erb'
|
25
|
+
return path if File.exists?(path)
|
24
26
|
end
|
25
27
|
raise "could not find #{name}.sql"
|
26
28
|
end
|
@@ -32,27 +34,35 @@ module ActiveRecordViews
|
|
32
34
|
!connection.outside_transaction?
|
33
35
|
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
begin
|
38
|
+
recursing = Thread.current[:active_record_views_without_transaction]
|
39
|
+
Thread.current[:active_record_views_without_transaction] = true
|
40
|
+
|
41
|
+
if in_transaction && !recursing
|
42
|
+
begin
|
43
|
+
temp_connection = connection.pool.checkout
|
44
|
+
yield temp_connection
|
45
|
+
ensure
|
46
|
+
connection.pool.checkin temp_connection
|
47
|
+
end
|
48
|
+
else
|
49
|
+
yield connection
|
41
50
|
end
|
42
|
-
|
43
|
-
|
51
|
+
ensure
|
52
|
+
Thread.current[:active_record_views_without_transaction] = nil
|
44
53
|
end
|
45
54
|
end
|
46
55
|
|
47
|
-
def self.create_view(connection, name, sql)
|
56
|
+
def self.create_view(connection, name, class_name, sql)
|
48
57
|
without_transaction connection do |connection|
|
49
58
|
cache = ActiveRecordViews::ChecksumCache.new(connection)
|
50
|
-
|
51
|
-
return if cache.get(name) ==
|
59
|
+
data = {class_name: class_name, checksum: Digest::SHA1.hexdigest(sql)}
|
60
|
+
return if cache.get(name) == data
|
52
61
|
|
53
62
|
begin
|
54
63
|
connection.execute "CREATE OR REPLACE VIEW #{connection.quote_table_name name} AS #{sql}"
|
55
64
|
rescue ActiveRecord::StatementInvalid => original_exception
|
65
|
+
raise unless view_exists?(connection, name)
|
56
66
|
connection.transaction :requires_new => true do
|
57
67
|
without_dependencies connection, name do
|
58
68
|
connection.execute "DROP VIEW #{connection.quote_table_name name}"
|
@@ -61,7 +71,7 @@ module ActiveRecordViews
|
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
64
|
-
cache.set name,
|
74
|
+
cache.set name, data
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
@@ -73,6 +83,14 @@ module ActiveRecordViews
|
|
73
83
|
end
|
74
84
|
end
|
75
85
|
|
86
|
+
def self.view_exists?(connection, name)
|
87
|
+
connection.select_value(<<-SQL).present?
|
88
|
+
SELECT 1
|
89
|
+
FROM information_schema.views
|
90
|
+
WHERE table_schema = 'public' AND table_name = #{connection.quote name};
|
91
|
+
SQL
|
92
|
+
end
|
93
|
+
|
76
94
|
def self.get_view_dependencies(connection, name)
|
77
95
|
connection.select_rows <<-SQL
|
78
96
|
WITH RECURSIVE dependants AS (
|
@@ -93,11 +111,12 @@ module ActiveRecordViews
|
|
93
111
|
|
94
112
|
SELECT
|
95
113
|
oid::regclass::text AS name,
|
114
|
+
class_name,
|
96
115
|
pg_catalog.pg_get_viewdef(oid) AS definition
|
97
116
|
FROM dependants
|
98
117
|
INNER JOIN active_record_views ON active_record_views.name = oid::regclass::text
|
99
118
|
WHERE level > 0
|
100
|
-
GROUP BY oid
|
119
|
+
GROUP BY oid, class_name
|
101
120
|
ORDER BY MAX(level)
|
102
121
|
;
|
103
122
|
SQL
|
@@ -106,19 +125,24 @@ module ActiveRecordViews
|
|
106
125
|
def self.without_dependencies(connection, name)
|
107
126
|
dependencies = get_view_dependencies(connection, name)
|
108
127
|
|
109
|
-
dependencies.reverse.each do |name, _|
|
128
|
+
dependencies.reverse.each do |name, _, _|
|
110
129
|
connection.execute "DROP VIEW #{name};"
|
111
130
|
end
|
112
131
|
|
113
132
|
yield
|
114
133
|
|
115
|
-
dependencies.each do |name, definition|
|
116
|
-
|
134
|
+
dependencies.each do |name, class_name, definition|
|
135
|
+
begin
|
136
|
+
class_name.constantize
|
137
|
+
rescue NameError => e
|
138
|
+
raise unless e.missing_name?(class_name)
|
139
|
+
connection.execute "CREATE VIEW #{name} AS #{definition};"
|
140
|
+
end
|
117
141
|
end
|
118
142
|
end
|
119
143
|
|
120
|
-
def self.register_for_reload(
|
121
|
-
self.registered_views << RegisteredView.new(
|
144
|
+
def self.register_for_reload(model_class, sql_path)
|
145
|
+
self.registered_views << RegisteredView.new(model_class, sql_path)
|
122
146
|
end
|
123
147
|
|
124
148
|
def self.reload_stale_views!
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecordViews::ChecksumCache do
|
4
|
+
let(:connection) { ActiveRecord::Base.connection }
|
5
|
+
|
6
|
+
describe 'initialisation' do
|
7
|
+
context 'with no existing table' do
|
8
|
+
it 'creates the table' do
|
9
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ACREATE TABLE active_record_views/).once.and_call_original
|
10
|
+
|
11
|
+
expect(connection.table_exists?('active_record_views')).to eq false
|
12
|
+
ActiveRecordViews::ChecksumCache.new(connection)
|
13
|
+
expect(connection.table_exists?('active_record_views')).to eq true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with existing table' do
|
18
|
+
before do
|
19
|
+
ActiveRecordViews::ChecksumCache.new(connection)
|
20
|
+
expect(connection.table_exists?('active_record_views')).to eq true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'does not recreate the table' do
|
24
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).never
|
25
|
+
|
26
|
+
ActiveRecordViews::ChecksumCache.new(connection)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with old table' do
|
31
|
+
before do
|
32
|
+
connection.execute 'CREATE TABLE active_record_views(name text PRIMARY KEY, checksum text NOT NULL);'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'recreates the table' do
|
36
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ADROP TABLE active_record_views/).once.and_call_original
|
37
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ACREATE TABLE active_record_views/).once.and_call_original
|
38
|
+
|
39
|
+
expect(connection.column_exists?('active_record_views', 'class_name')).to eq false
|
40
|
+
ActiveRecordViews::ChecksumCache.new(connection)
|
41
|
+
expect(connection.column_exists?('active_record_views', 'class_name')).to eq true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -17,6 +17,11 @@ describe ActiveRecordViews::Extension do
|
|
17
17
|
expect(Namespace::TestModel.first.name).to eq 'Namespaced SQL file'
|
18
18
|
end
|
19
19
|
|
20
|
+
it 'creates database views from external ERB files' do
|
21
|
+
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
22
|
+
expect(ErbTestModel.first.name).to eq 'ERB file'
|
23
|
+
end
|
24
|
+
|
20
25
|
it 'errors if external SQL file is missing' do
|
21
26
|
expect {
|
22
27
|
MissingFileTestModel
|
@@ -28,6 +33,7 @@ describe ActiveRecordViews::Extension do
|
|
28
33
|
expect(ActiveRecordViews).to receive(:create_view).with(
|
29
34
|
anything,
|
30
35
|
'modified_file_test_models',
|
36
|
+
'ModifiedFileTestModel',
|
31
37
|
sql
|
32
38
|
).once.ordered
|
33
39
|
end
|
@@ -76,11 +82,18 @@ describe ActiveRecordViews::Extension do
|
|
76
82
|
end
|
77
83
|
|
78
84
|
it 'does not create if database view is initially up to date' do
|
79
|
-
ActiveRecordViews.create_view ActiveRecord::Base.connection, 'initial_create_test_models', 'SELECT 42 as id'
|
85
|
+
ActiveRecordViews.create_view ActiveRecord::Base.connection, 'initial_create_test_models', 'InitialCreateTestModel', 'SELECT 42 as id'
|
80
86
|
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ACREATE (?:OR REPLACE )?VIEW/).never
|
81
87
|
class InitialCreateTestModel < ActiveRecord::Base
|
82
88
|
is_view 'SELECT 42 as id'
|
83
89
|
end
|
84
90
|
end
|
91
|
+
|
92
|
+
it 'successfully recreates modified paired views with incompatible changes' do
|
93
|
+
ActiveRecordViews.create_view ActiveRecord::Base.connection, 'modified_as', 'ModifiedA', 'SELECT 11 AS old_name;'
|
94
|
+
ActiveRecordViews.create_view ActiveRecord::Base.connection, 'modified_bs', 'ModifiedB', 'SELECT old_name FROM modified_as;'
|
95
|
+
|
96
|
+
expect(ModifiedB.first.attributes.except(nil)).to eq('new_name' => 22)
|
97
|
+
end
|
85
98
|
end
|
86
99
|
end
|
@@ -5,7 +5,7 @@ describe ActiveRecordViews do
|
|
5
5
|
let(:connection) { ActiveRecord::Base.connection }
|
6
6
|
|
7
7
|
def create_test_view(sql)
|
8
|
-
ActiveRecordViews.create_view connection, 'test', sql
|
8
|
+
ActiveRecordViews.create_view connection, 'test', 'Test', sql
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_view_sql
|
@@ -30,6 +30,17 @@ describe ActiveRecordViews do
|
|
30
30
|
expect(test_view_sql).to eq 'SELECT 1 AS id;'
|
31
31
|
end
|
32
32
|
|
33
|
+
it 'records checksum and class name' do
|
34
|
+
create_test_view 'select 1 as id'
|
35
|
+
expect(connection.select_all('select * from active_record_views').to_a).to eq [
|
36
|
+
{
|
37
|
+
'name' => 'test',
|
38
|
+
'class_name' => 'Test',
|
39
|
+
'checksum' => Digest::SHA1.hexdigest('select 1 as id')
|
40
|
+
}
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
33
44
|
it 'persists views if transaction rolls back' do
|
34
45
|
expect(test_view_sql).to be_nil
|
35
46
|
connection.transaction :requires_new => true do
|
@@ -39,6 +50,12 @@ describe ActiveRecordViews do
|
|
39
50
|
expect(test_view_sql).to eq 'SELECT 1 AS id;'
|
40
51
|
end
|
41
52
|
|
53
|
+
it 'raises descriptive error if view SQL is invalid' do
|
54
|
+
expect {
|
55
|
+
create_test_view 'select blah'
|
56
|
+
}.to raise_error ActiveRecord::StatementInvalid, /column "blah" does not exist/
|
57
|
+
end
|
58
|
+
|
42
59
|
context 'with existing view' do
|
43
60
|
before do
|
44
61
|
create_test_view 'select 1 as id'
|
@@ -57,11 +74,11 @@ describe ActiveRecordViews do
|
|
57
74
|
|
58
75
|
context 'having dependant views' do
|
59
76
|
before do
|
60
|
-
ActiveRecordViews.create_view connection, 'dependency1', 'SELECT id FROM test;'
|
61
|
-
ActiveRecordViews.create_view connection, 'dependency2a', 'SELECT id, id * 2 AS id2 FROM dependency1;'
|
62
|
-
ActiveRecordViews.create_view connection, 'dependency2b', 'SELECT id, id * 4 AS id4 FROM dependency1;'
|
63
|
-
ActiveRecordViews.create_view connection, 'dependency3', 'SELECT * FROM dependency2b;'
|
64
|
-
ActiveRecordViews.create_view connection, 'dependency4', 'SELECT id FROM dependency1 UNION ALL SELECT id FROM dependency3;'
|
77
|
+
ActiveRecordViews.create_view connection, 'dependency1', 'Dependency1', 'SELECT id FROM test;'
|
78
|
+
ActiveRecordViews.create_view connection, 'dependency2a', 'Dependency2a', 'SELECT id, id * 2 AS id2 FROM dependency1;'
|
79
|
+
ActiveRecordViews.create_view connection, 'dependency2b', 'Dependency2b', 'SELECT id, id * 4 AS id4 FROM dependency1;'
|
80
|
+
ActiveRecordViews.create_view connection, 'dependency3', 'Dependency3', 'SELECT * FROM dependency2b;'
|
81
|
+
ActiveRecordViews.create_view connection, 'dependency4', 'Dependency4', 'SELECT id FROM dependency1 UNION ALL SELECT id FROM dependency3;'
|
65
82
|
end
|
66
83
|
|
67
84
|
after do
|
@@ -118,4 +135,33 @@ describe ActiveRecordViews do
|
|
118
135
|
end
|
119
136
|
end
|
120
137
|
end
|
138
|
+
|
139
|
+
describe '.without_transaction' do
|
140
|
+
let(:original_connection) { ActiveRecord::Base.connection }
|
141
|
+
|
142
|
+
it 'yields original connection if no active transaction' do
|
143
|
+
ActiveRecordViews.without_transaction original_connection do |new_connection|
|
144
|
+
expect(new_connection).to eq original_connection
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'yields a new connection if inside a transaction' do
|
149
|
+
original_connection.transaction do
|
150
|
+
ActiveRecordViews.without_transaction original_connection do |new_connection|
|
151
|
+
expect(new_connection).to_not eq original_connection
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'yields original connection if called recursively' do
|
157
|
+
ActiveRecordViews.without_transaction original_connection do |new_connection_1|
|
158
|
+
expect(new_connection_1).to eq original_connection
|
159
|
+
new_connection_1.transaction do
|
160
|
+
ActiveRecordViews.without_transaction new_connection_1 do |new_connection_2|
|
161
|
+
expect(new_connection_2).to eq new_connection_1
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
121
167
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
SELECT 1 AS id, '<%= "ERB" %> file'::text AS name;
|
data/spec/spec_helper.rb
CHANGED
@@ -23,7 +23,7 @@ RSpec.configure do |config|
|
|
23
23
|
WHERE table_schema = 'public';
|
24
24
|
SQL
|
25
25
|
view_names.each do |view_name|
|
26
|
-
connection.execute "DROP VIEW IF EXISTS #{connection.quote_table_name view_name}"
|
26
|
+
connection.execute "DROP VIEW IF EXISTS #{connection.quote_table_name view_name} CASCADE"
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Weathered
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -111,12 +111,17 @@ files:
|
|
111
111
|
- lib/active_record_views/registered_view.rb
|
112
112
|
- lib/active_record_views/version.rb
|
113
113
|
- lib/activerecord_views.rb
|
114
|
+
- spec/active_record_views_checksum_cache_spec.rb
|
114
115
|
- spec/active_record_views_extension_spec.rb
|
115
116
|
- spec/active_record_views_spec.rb
|
117
|
+
- spec/internal/app/models/erb_test_model.rb
|
118
|
+
- spec/internal/app/models/erb_test_model.sql.erb
|
116
119
|
- spec/internal/app/models/external_file_test_model.rb
|
117
120
|
- spec/internal/app/models/external_file_test_model.sql
|
118
121
|
- spec/internal/app/models/heredoc_test_model.rb
|
119
122
|
- spec/internal/app/models/missing_file_test_model.rb
|
123
|
+
- spec/internal/app/models/modified_a.rb
|
124
|
+
- spec/internal/app/models/modified_b.rb
|
120
125
|
- spec/internal/app/models/namespace/test_model.rb
|
121
126
|
- spec/internal/app/models/namespace/test_model.sql
|
122
127
|
- spec/internal/config/database.yml
|
@@ -147,12 +152,17 @@ signing_key:
|
|
147
152
|
specification_version: 4
|
148
153
|
summary: Automatic database view creation for ActiveRecord
|
149
154
|
test_files:
|
155
|
+
- spec/active_record_views_checksum_cache_spec.rb
|
150
156
|
- spec/active_record_views_extension_spec.rb
|
151
157
|
- spec/active_record_views_spec.rb
|
158
|
+
- spec/internal/app/models/erb_test_model.rb
|
159
|
+
- spec/internal/app/models/erb_test_model.sql.erb
|
152
160
|
- spec/internal/app/models/external_file_test_model.rb
|
153
161
|
- spec/internal/app/models/external_file_test_model.sql
|
154
162
|
- spec/internal/app/models/heredoc_test_model.rb
|
155
163
|
- spec/internal/app/models/missing_file_test_model.rb
|
164
|
+
- spec/internal/app/models/modified_a.rb
|
165
|
+
- spec/internal/app/models/modified_b.rb
|
156
166
|
- spec/internal/app/models/namespace/test_model.rb
|
157
167
|
- spec/internal/app/models/namespace/test_model.sql
|
158
168
|
- spec/internal/config/database.yml
|