scenic-cascade 0.1.2 → 0.1.3
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/CHANGELOG.md +12 -1
- data/Gemfile.lock +1 -1
- data/README.md +6 -0
- data/codecov.yml +3 -0
- data/lib/generators/scenic/view/cascade/cascade_generator.rb +105 -11
- data/lib/generators/scenic/view/cascade/templates/db/migrate/update_view.rb.erb +43 -5
- data/lib/scenic/cascade/dependency_finder.rb +14 -7
- data/lib/scenic/cascade/dependent_finder.rb +14 -7
- data/lib/scenic/cascade/version.rb +1 -1
- data/lib/scenic/cascade/view_with_version.rb +4 -2
- data/sig/generators/scenic/view/cascade/cascade_generator.rbs +13 -0
- data/sig/generators/scenic/view/view_generator.rbs +4 -0
- data/sig/scenic/cascade/view_with_version.rbs +2 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29ecc270cd637c5654557c3d349c43a59483476d7a0758a3988136d4c3fa4a28
|
|
4
|
+
data.tar.gz: b53b4f0a78d929753627aabcab2dbf75c6e6aad79fe5ee6a77b5a1b45b9b9af0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 642f9d3a40bf02b8da393a10e645c7609e5562652654c016ad27a6806b86d8bbf4f98ffd4e1554ef4fca5a60dab878badf298bbc0200b38ae1aa16c0baccddf3
|
|
7
|
+
data.tar.gz: b95a92d620f673192b07e82de00151e4e251e1ecaeb13c24df4c6fa85670a71093b04a2d0d44dde9b86bf7e066dc112689292b808eabe27e3549dddea849c134
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.3] - 2026-11-01
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Recreate dependent views when generating a migration with a Rails table name prefix.
|
|
15
|
+
- Discover and recreate dependent views across PostgreSQL schemas without mixing identically named views or their SQL definitions.
|
|
16
|
+
- Remove generated view definitions and partial migrations when migration generation fails, so retries use the correct version.
|
|
17
|
+
- Apply `--replace` and supported `--side-by-side` options when generating view update migrations.
|
|
18
|
+
- Avoid leaving an orphan SQL definition when `--skip` skips a conflicting migration.
|
|
19
|
+
|
|
10
20
|
## [0.1.2] - 2026-11-01
|
|
11
21
|
|
|
12
22
|
### Fixed
|
|
@@ -27,7 +37,8 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
27
37
|
|
|
28
38
|
- Initial release of the cascading Scenic view migration generator.
|
|
29
39
|
|
|
30
|
-
[Unreleased]: https://github.com/akiomik/scenic-cascade/compare/v0.1.
|
|
40
|
+
[Unreleased]: https://github.com/akiomik/scenic-cascade/compare/v0.1.3...HEAD
|
|
41
|
+
[0.1.3]: https://github.com/akiomik/scenic-cascade/compare/v0.1.2...v0.1.3
|
|
31
42
|
[0.1.2]: https://github.com/akiomik/scenic-cascade/compare/v0.1.1...v0.1.2
|
|
32
43
|
[0.1.1]: https://github.com/akiomik/scenic-cascade/compare/v0.1.0...v0.1.1
|
|
33
44
|
[0.1.0]: https://github.com/akiomik/scenic-cascade/releases/tag/v0.1.0
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -37,6 +37,12 @@ $ bin/rails generate scenic:view:cascade search_results
|
|
|
37
37
|
create db/migrate/20220714233704_create_search_results.rb
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
For a schema-qualified view, pass its database relation name, such as `reporting.orders`.
|
|
41
|
+
If Rails uses a table name prefix, include it after the schema name (for example, `reporting.app_orders`).
|
|
42
|
+
For unqualified names, pass the name without the prefix; Rails applies it in the migration.
|
|
43
|
+
If another schema precedes `public` in PostgreSQL's `search_path` and contains a view with the same name,
|
|
44
|
+
use a schema-qualified Scenic definition for the public dependent view (for example, `public_reports_v01.sql`).
|
|
45
|
+
|
|
40
46
|
## How it works
|
|
41
47
|
|
|
42
48
|
Consider a situation where the following three views exist:
|
data/codecov.yml
ADDED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'erb'
|
|
3
4
|
require 'generators/scenic/view/view_generator'
|
|
4
5
|
require 'scenic/cascade'
|
|
5
6
|
require 'scenic/cascade/view_with_version'
|
|
@@ -12,27 +13,83 @@ module Scenic
|
|
|
12
13
|
source_root File.expand_path('templates', __dir__ || '.')
|
|
13
14
|
|
|
14
15
|
def create_view_definition
|
|
16
|
+
@definition_path = @original_definition = nil
|
|
17
|
+
validate_update_options! unless destroying? || creating_new_view?
|
|
15
18
|
dependents unless destroying? || creating_new_view?
|
|
19
|
+
path = Scenic::Definition.new(plural_file_name, version).full_path.to_s
|
|
20
|
+
@definition_path = path
|
|
21
|
+
@original_definition = File.binread(path) if File.file?(path)
|
|
16
22
|
super
|
|
23
|
+
rescue StandardError
|
|
24
|
+
restore_definition
|
|
25
|
+
raise
|
|
17
26
|
end
|
|
18
27
|
|
|
19
28
|
def create_migration_file
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
template, destination = migration_template_details
|
|
30
|
+
pattern = File.join(destination_root, 'db/migrate', "[0-9]*_#{File.basename(destination)}")
|
|
31
|
+
original_migrations = Dir.glob(pattern).to_h { |path| [path, File.binread(path)] }
|
|
32
|
+
migration_template(template, destination)
|
|
33
|
+
# Thor returns normally for --skip even though it did not create a migration.
|
|
34
|
+
restore_definition if options[:skip] && skipped_migration?(template, pattern, original_migrations)
|
|
35
|
+
rescue StandardError
|
|
36
|
+
restore_migrations(pattern, original_migrations) if pattern && original_migrations
|
|
37
|
+
restore_definition
|
|
38
|
+
raise
|
|
27
39
|
end
|
|
28
40
|
|
|
29
41
|
private
|
|
30
42
|
|
|
43
|
+
def validate_update_options!
|
|
44
|
+
raise ArgumentError, '--replace cannot update a materialized view' if options[:replace] && materialized?
|
|
45
|
+
|
|
46
|
+
validate_side_by_side_option! if options[:side_by_side]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def validate_side_by_side_option!
|
|
50
|
+
raise ArgumentError, '--side-by-side requires --materialized' unless materialized?
|
|
51
|
+
raise ArgumentError, '--side-by-side cannot update schema-qualified views' if plural_name.include?('.')
|
|
52
|
+
|
|
53
|
+
raise ArgumentError, '--side-by-side cannot be combined with --no-data' if options[:no_data]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def migration_template_details
|
|
57
|
+
return ['db/migrate/create_view.rb.erb', "db/migrate/create_#{plural_file_name}.rb"] if
|
|
58
|
+
creating_new_view? || destroying_initial_view?
|
|
59
|
+
|
|
60
|
+
['db/migrate/update_view.rb.erb', "db/migrate/update_#{plural_file_name}_to_version_#{version}.rb"]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def restore_definition
|
|
64
|
+
return unless (path = @definition_path)
|
|
65
|
+
|
|
66
|
+
if (original = @original_definition)
|
|
67
|
+
restore_file(path, original)
|
|
68
|
+
elsif File.file?(path)
|
|
69
|
+
File.delete(path)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def restore_migrations(pattern, originals)
|
|
74
|
+
(Dir.glob(pattern) - originals.keys).each { |path| File.delete(path) }
|
|
75
|
+
originals.each { |path, content| restore_file(path, content) }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def restore_file(path, content)
|
|
79
|
+
File.binwrite(path, content) unless File.file?(path) && File.binread(path) == content
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def skipped_migration?(template, pattern, originals)
|
|
83
|
+
return false unless Dir.glob(pattern).to_h { |path| [path, File.binread(path)] } == originals
|
|
84
|
+
|
|
85
|
+
originals.values.first != ERB.new(File.read(find_in_source_paths(template)), trim_mode: '-').result(binding).b
|
|
86
|
+
end
|
|
87
|
+
|
|
31
88
|
def dependents
|
|
32
89
|
cached_dependents = @dependents
|
|
33
90
|
return cached_dependents unless cached_dependents.nil?
|
|
34
91
|
|
|
35
|
-
deps = Scenic::Cascade.view_dependents_of(
|
|
92
|
+
deps = Scenic::Cascade.view_dependents_of(database_view_name, recursive: true)
|
|
36
93
|
views = deps.map { |dep| view_with_version_for(dep) }
|
|
37
94
|
# The last visit follows every prerequisite, even when names sort in the opposite order.
|
|
38
95
|
@dependents = views.reverse.uniq { |view| [view.name, view.version, view.materialized?] }.reverse
|
|
@@ -40,11 +97,48 @@ module Scenic
|
|
|
40
97
|
|
|
41
98
|
def view_with_version_for(dep)
|
|
42
99
|
name = dep.from.name
|
|
43
|
-
|
|
100
|
+
definition_name = definition_name_for(name)
|
|
101
|
+
definition = Scenic::Cascade.find_definitions_of(definition_name).last
|
|
44
102
|
raise ArgumentError, "Dependent view #{name} has no Scenic definition file" if definition.nil?
|
|
45
103
|
|
|
46
|
-
|
|
47
|
-
|
|
104
|
+
Scenic::Cascade::ViewWithVersion.new(
|
|
105
|
+
name: definition_name, version: definition.version.to_i,
|
|
106
|
+
materialized: dep.from.materialized?, database_name: qualified_name(name)
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def definition_name_for(name)
|
|
111
|
+
return name if name.include?('.')
|
|
112
|
+
|
|
113
|
+
qualified = qualified_name(name)
|
|
114
|
+
qualified_definitions = Scenic::Cascade.find_definitions_of(qualified)
|
|
115
|
+
bare_definitions = Scenic::Cascade.find_definitions_of(name)
|
|
116
|
+
|
|
117
|
+
if qualified_definitions.any? && bare_definitions.any?
|
|
118
|
+
raise ArgumentError, "Dependent view #{name} has ambiguous Scenic definitions: #{name} and #{qualified}"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
return qualified if qualified_definitions.any?
|
|
122
|
+
return name if bare_definitions.empty? || bare_name_resolves_to_public?(name)
|
|
123
|
+
|
|
124
|
+
raise ArgumentError, "Dependent view #{name} has no unambiguous Scenic definition file for #{qualified}"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def bare_name_resolves_to_public?(name)
|
|
128
|
+
connection = ActiveRecord::Base.connection
|
|
129
|
+
bare = connection.quote(connection.quote_table_name(name))
|
|
130
|
+
qualified = connection.quote(connection.quote_table_name(qualified_name(name)))
|
|
131
|
+
connection.select_value("SELECT to_regclass(#{bare}) = to_regclass(#{qualified})")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def qualified_name(name)
|
|
135
|
+
name.include?('.') ? name : "public.#{name}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def database_view_name
|
|
139
|
+
return plural_name if plural_name.include?('.')
|
|
140
|
+
|
|
141
|
+
"#{ActiveRecord::Base.table_name_prefix}#{plural_name}#{ActiveRecord::Base.table_name_suffix}"
|
|
48
142
|
end
|
|
49
143
|
end
|
|
50
144
|
end
|
|
@@ -1,26 +1,64 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class <%= migration_class_name %> < <%= activerecord_migration_class %>
|
|
4
|
+
<%- if dependents.any? || plural_name.include?('.') -%>
|
|
5
|
+
class ExactViewName < String
|
|
6
|
+
alias table_name to_s
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
<%- end -%>
|
|
10
|
+
<%- target_name = plural_name.include?('.') ? "ExactViewName.new(#{plural_name.inspect})" : formatted_plural_name -%>
|
|
11
|
+
<%- update_method = options[:replace] ? 'replace_view' : 'update_view' -%>
|
|
12
|
+
<%- materialized_flags = [] -%>
|
|
13
|
+
<%- materialized_flags << 'no_data: true' if options[:no_data] -%>
|
|
14
|
+
<%- materialized_flags << 'side_by_side: true' if options[:side_by_side] -%>
|
|
15
|
+
<%- materialized_options = materialized_flags.empty? ? 'true' : "{ #{materialized_flags.join(', ')} }" -%>
|
|
4
16
|
def change
|
|
5
17
|
<%- dependents.reverse_each do |dep| -%>
|
|
6
|
-
|
|
18
|
+
<%- if dep.database_name == dep.name -%>
|
|
19
|
+
drop_view ExactViewName.new(<%= dep.database_name.inspect %>),
|
|
7
20
|
revert_to_version: <%= dep.version %>,
|
|
8
21
|
materialized: <%= dep.materialized? %>
|
|
22
|
+
<%- else -%>
|
|
23
|
+
reversible do |direction|
|
|
24
|
+
direction.up do
|
|
25
|
+
drop_view ExactViewName.new(<%= dep.database_name.inspect %>), materialized: <%= dep.materialized? %>
|
|
26
|
+
end
|
|
27
|
+
direction.down do
|
|
28
|
+
create_view ExactViewName.new(<%= dep.database_name.inspect %>),
|
|
29
|
+
sql_definition: Scenic::Definition.new(<%= dep.name.inspect %>, <%= dep.version %>).to_sql,
|
|
30
|
+
materialized: <%= dep.materialized? %>
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
<%- end -%>
|
|
9
34
|
<%- end -%>
|
|
10
35
|
|
|
11
36
|
<%- if materialized? -%>
|
|
12
|
-
|
|
37
|
+
<%= update_method %> <%= target_name %>,
|
|
13
38
|
version: <%= version %>,
|
|
14
39
|
revert_to_version: <%= previous_version %>,
|
|
15
|
-
materialized: <%=
|
|
40
|
+
materialized: <%= materialized_options %>
|
|
16
41
|
<%- else -%>
|
|
17
|
-
|
|
42
|
+
<%= update_method %> <%= target_name %>, version: <%= version %>, revert_to_version: <%= previous_version %>
|
|
18
43
|
<%- end -%>
|
|
19
44
|
|
|
20
45
|
<%- dependents.each do |dep| -%>
|
|
21
|
-
|
|
46
|
+
<%- if dep.database_name == dep.name -%>
|
|
47
|
+
create_view ExactViewName.new(<%= dep.database_name.inspect %>),
|
|
22
48
|
version: <%= dep.version %>,
|
|
23
49
|
materialized: <%= dep.materialized? %>
|
|
50
|
+
<%- else -%>
|
|
51
|
+
reversible do |direction|
|
|
52
|
+
direction.up do
|
|
53
|
+
create_view ExactViewName.new(<%= dep.database_name.inspect %>),
|
|
54
|
+
sql_definition: Scenic::Definition.new(<%= dep.name.inspect %>, <%= dep.version %>).to_sql,
|
|
55
|
+
materialized: <%= dep.materialized? %>
|
|
56
|
+
end
|
|
57
|
+
direction.down do
|
|
58
|
+
drop_view ExactViewName.new(<%= dep.database_name.inspect %>), materialized: <%= dep.materialized? %>
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
<%- end -%>
|
|
24
62
|
<%- end -%>
|
|
25
63
|
end
|
|
26
64
|
end
|
|
@@ -14,21 +14,28 @@ module Scenic
|
|
|
14
14
|
module ClassMethods
|
|
15
15
|
DEPENDENCY_SQL = <<-SQL
|
|
16
16
|
SELECT DISTINCT
|
|
17
|
-
dependee.relname
|
|
17
|
+
CASE WHEN dependee_ns.nspname = 'public' THEN dependee.relname
|
|
18
|
+
ELSE dependee_ns.nspname || '.' || dependee.relname END AS to
|
|
18
19
|
, dependee.relkind = 'm' AS to_materialized
|
|
19
|
-
, depender.relname
|
|
20
|
+
, CASE WHEN depender_ns.nspname = 'public' THEN depender.relname
|
|
21
|
+
ELSE depender_ns.nspname || '.' || depender.relname END AS from
|
|
20
22
|
, depender.relkind = 'm' AS from_materialized
|
|
23
|
+
, format('%I.%I', dependee_ns.nspname, dependee.relname) AS to_lookup
|
|
21
24
|
FROM pg_depend d
|
|
22
25
|
JOIN pg_rewrite r
|
|
23
26
|
ON d.objid = r.oid
|
|
24
27
|
JOIN pg_class AS dependee
|
|
25
28
|
ON d.refobjid = dependee.oid
|
|
29
|
+
JOIN pg_namespace AS dependee_ns
|
|
30
|
+
ON dependee.relnamespace = dependee_ns.oid
|
|
26
31
|
JOIN pg_class AS depender
|
|
27
32
|
ON r.ev_class = depender.oid
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
JOIN pg_namespace AS depender_ns
|
|
34
|
+
ON depender.relnamespace = depender_ns.oid
|
|
35
|
+
WHERE depender.oid = to_regclass(?)
|
|
36
|
+
AND dependee.oid != depender.oid
|
|
30
37
|
AND dependee.relkind in ('m', 'v')
|
|
31
|
-
ORDER BY
|
|
38
|
+
ORDER BY to_lookup;
|
|
32
39
|
SQL
|
|
33
40
|
|
|
34
41
|
private_constant :DEPENDENCY_SQL
|
|
@@ -41,8 +48,8 @@ module Scenic
|
|
|
41
48
|
return [] if dependencies.empty?
|
|
42
49
|
return dependencies unless recursive
|
|
43
50
|
|
|
44
|
-
dependencies.flat_map do |dependency|
|
|
45
|
-
[dependency, *view_dependencies_of(
|
|
51
|
+
raw_dependencies.zip(dependencies).flat_map do |row, dependency|
|
|
52
|
+
[dependency, *view_dependencies_of(row.fetch('to_lookup'), recursive: true)]
|
|
46
53
|
end
|
|
47
54
|
end
|
|
48
55
|
end
|
|
@@ -14,21 +14,28 @@ module Scenic
|
|
|
14
14
|
module ClassMethods
|
|
15
15
|
DEPENDANT_SQL = <<-SQL
|
|
16
16
|
SELECT DISTINCT
|
|
17
|
-
dependee.relname
|
|
17
|
+
CASE WHEN dependee_ns.nspname = 'public' THEN dependee.relname
|
|
18
|
+
ELSE dependee_ns.nspname || '.' || dependee.relname END AS to
|
|
18
19
|
, dependee.relkind = 'm' AS to_materialized
|
|
19
|
-
, depender.relname
|
|
20
|
+
, CASE WHEN depender_ns.nspname = 'public' THEN depender.relname
|
|
21
|
+
ELSE depender_ns.nspname || '.' || depender.relname END AS from
|
|
20
22
|
, depender.relkind = 'm' AS from_materialized
|
|
23
|
+
, format('%I.%I', depender_ns.nspname, depender.relname) AS from_lookup
|
|
21
24
|
FROM pg_depend d
|
|
22
25
|
JOIN pg_rewrite r
|
|
23
26
|
ON d.objid = r.oid
|
|
24
27
|
JOIN pg_class AS dependee
|
|
25
28
|
ON d.refobjid = dependee.oid
|
|
29
|
+
JOIN pg_namespace AS dependee_ns
|
|
30
|
+
ON dependee.relnamespace = dependee_ns.oid
|
|
26
31
|
JOIN pg_class AS depender
|
|
27
32
|
ON r.ev_class = depender.oid
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
JOIN pg_namespace AS depender_ns
|
|
34
|
+
ON depender.relnamespace = depender_ns.oid
|
|
35
|
+
WHERE dependee.oid = to_regclass(?)
|
|
36
|
+
AND depender.oid != dependee.oid
|
|
30
37
|
AND depender.relkind in ('m', 'v')
|
|
31
|
-
ORDER BY
|
|
38
|
+
ORDER BY from_lookup;
|
|
32
39
|
SQL
|
|
33
40
|
|
|
34
41
|
private_constant :DEPENDANT_SQL
|
|
@@ -41,8 +48,8 @@ module Scenic
|
|
|
41
48
|
return [] if dependencies.empty?
|
|
42
49
|
return dependencies unless recursive
|
|
43
50
|
|
|
44
|
-
dependencies.flat_map do |dependency|
|
|
45
|
-
[dependency, *view_dependents_of(
|
|
51
|
+
raw_dependencies.zip(dependencies).flat_map do |row, dependency|
|
|
52
|
+
[dependency, *view_dependents_of(row.fetch('from_lookup'), recursive: true)]
|
|
46
53
|
end
|
|
47
54
|
end
|
|
48
55
|
|
|
@@ -6,14 +6,16 @@ module Scenic
|
|
|
6
6
|
module Cascade
|
|
7
7
|
# Represents a view with version
|
|
8
8
|
class ViewWithVersion < Scenic::Cascade::View
|
|
9
|
-
attr_reader :version
|
|
9
|
+
attr_reader :version, :database_name
|
|
10
10
|
|
|
11
|
-
def initialize(name:, version:, materialized:)
|
|
11
|
+
def initialize(name:, version:, materialized:, database_name: name)
|
|
12
12
|
super(name: name, materialized: materialized)
|
|
13
13
|
|
|
14
14
|
raise TypeError unless version.is_a?(Integer)
|
|
15
|
+
raise TypeError unless database_name.is_a?(String)
|
|
15
16
|
|
|
16
17
|
@version = version
|
|
18
|
+
@database_name = database_name
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def ==(other)
|
|
@@ -3,14 +3,27 @@ module Scenic
|
|
|
3
3
|
module View
|
|
4
4
|
class CascadeGenerator < Scenic::Generators::ViewGenerator
|
|
5
5
|
@dependents: Array[Scenic::Cascade::ViewWithVersion]?
|
|
6
|
+
@definition_path: String?
|
|
7
|
+
@original_definition: String?
|
|
6
8
|
|
|
7
9
|
def create_view_definition: -> void
|
|
8
10
|
def create_migration_file: -> void
|
|
9
11
|
|
|
10
12
|
private
|
|
11
13
|
|
|
14
|
+
def validate_update_options!: -> void
|
|
15
|
+
def validate_side_by_side_option!: -> void
|
|
12
16
|
def dependents: -> Array[Scenic::Cascade::ViewWithVersion]
|
|
17
|
+
def migration_template_details: -> [String, String]
|
|
18
|
+
def restore_definition: -> void
|
|
19
|
+
def restore_migrations: (String, Hash[String, String]) -> void
|
|
20
|
+
def restore_file: (String, String) -> void
|
|
21
|
+
def skipped_migration?: (String, String, Hash[String, String]) -> bool
|
|
13
22
|
def view_with_version_for: (Scenic::Cascade::Dependency) -> Scenic::Cascade::ViewWithVersion
|
|
23
|
+
def database_view_name: -> String
|
|
24
|
+
def definition_name_for: (String) -> String
|
|
25
|
+
def bare_name_resolves_to_public?: (String) -> bool
|
|
26
|
+
def qualified_name: (String) -> String
|
|
14
27
|
end
|
|
15
28
|
end
|
|
16
29
|
end
|
|
@@ -3,12 +3,16 @@ module Scenic
|
|
|
3
3
|
class ViewGenerator
|
|
4
4
|
def self.source_root: (String?) -> void
|
|
5
5
|
def migration_template: (String, String) -> void
|
|
6
|
+
def destination_root: -> String
|
|
7
|
+
def find_in_source_paths: (String) -> String
|
|
8
|
+
def options: -> Hash[Symbol, untyped]
|
|
6
9
|
|
|
7
10
|
def create_view_definition: -> void
|
|
8
11
|
def version: -> Integer
|
|
9
12
|
|
|
10
13
|
private
|
|
11
14
|
|
|
15
|
+
def materialized?: -> bool
|
|
12
16
|
def plural_name: -> String
|
|
13
17
|
def plural_file_name: -> String
|
|
14
18
|
def creating_new_view?: -> bool
|
|
@@ -8,8 +8,9 @@ module Scenic
|
|
|
8
8
|
|
|
9
9
|
class ViewWithVersion < Scenic::Cascade::View
|
|
10
10
|
attr_reader version: Integer
|
|
11
|
+
attr_reader database_name: String
|
|
11
12
|
|
|
12
|
-
def initialize: (name: String, version: Integer, materialized: bool) -> void
|
|
13
|
+
def initialize: (name: String, version: Integer, materialized: bool, ?database_name: String) -> void
|
|
13
14
|
|
|
14
15
|
def ==: (_ViewWithVersionLike) -> bool
|
|
15
16
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: scenic-cascade
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Akiomi Kamakura
|
|
@@ -44,6 +44,7 @@ files:
|
|
|
44
44
|
- README.md
|
|
45
45
|
- Rakefile
|
|
46
46
|
- Steepfile
|
|
47
|
+
- codecov.yml
|
|
47
48
|
- docker-compose.yml
|
|
48
49
|
- lib/generators/scenic/view/cascade/USAGE
|
|
49
50
|
- lib/generators/scenic/view/cascade/cascade_generator.rb
|