scenic 1.2.0 → 1.3.0
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/README.md +43 -0
- data/Rakefile +15 -1
- data/lib/generators/scenic/model/model_generator.rb +2 -2
- data/lib/scenic/adapters/postgres.rb +25 -0
- data/lib/scenic/adapters/postgres/views.rb +11 -2
- data/lib/scenic/command_recorder.rb +8 -0
- data/lib/scenic/statements.rb +30 -0
- data/lib/scenic/version.rb +1 -1
- data/lib/scenic/view.rb +3 -1
- data/spec/acceptance/user_manages_views_spec.rb +86 -0
- data/spec/acceptance_helper.rb +32 -0
- data/spec/scenic/adapters/postgres_spec.rb +37 -0
- data/spec/scenic/command_recorder_spec.rb +26 -0
- data/spec/scenic/schema_dumper_spec.rb +16 -0
- data/spec/scenic/statements_spec.rb +30 -0
- metadata +7 -5
- data/spec/smoke +0 -135
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d319d2791defb8c1025c78605b3003c0275664ba
|
4
|
+
data.tar.gz: 0c79eb3cb32bf171ec085987b83ac89df8bd4ffe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f858662b6d60d851a0aa402401c750da7a9c482d018e49e6fcdde39cd5672bd4d8cbd5593f422afa7541c0867805f28e0a73cb1add550c0742552851f67359a3
|
7
|
+
data.tar.gz: 81c7ec7accf60272ff77476c73b31ed7b332334874d150b655b5933a4546be4a009a75c9dcd4a2d93a623de702c34c6e6f80bb76d5f3f8665ff65a56dc2ca896
|
data/README.md
CHANGED
@@ -78,6 +78,49 @@ Scenic detected that we already had an existing `search_results` view at version
|
|
78
78
|
update to the version 2 schema. All that's left for you to do is tweak the
|
79
79
|
schema in the new definition and run the `update_view` migration.
|
80
80
|
|
81
|
+
## What if I want to change a view without dropping it?
|
82
|
+
|
83
|
+
The `update_view` statement used by default will drop your view then create
|
84
|
+
a new version of it.
|
85
|
+
|
86
|
+
This is not desirable when you have complicated hierarchies of views, especially
|
87
|
+
when some of those views may be materialized and take a long time to recreate.
|
88
|
+
|
89
|
+
You can use `replace_view` to generate a CREATE OR REPLACE VIEW SQL statement.
|
90
|
+
|
91
|
+
See postgresql documentation on how this works:
|
92
|
+
http://www.postgresql.org/docs/current/static/sql-createview.html
|
93
|
+
|
94
|
+
To start replacing a view run the generator like for a regular change:
|
95
|
+
|
96
|
+
```sh
|
97
|
+
$ rails generate scenic:view search_results
|
98
|
+
create db/views/search_results_v02.sql
|
99
|
+
create db/migrate/[TIMESTAMP]_update_search_results_to_version_2.rb
|
100
|
+
```
|
101
|
+
|
102
|
+
Now, edit the migration. It should look something like:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class UpdateSearchResultsToVersion2 < ActiveRecord::Migration
|
106
|
+
def change
|
107
|
+
update_view :search_results, version: 2, revert_to_version: 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
Update it to use replace view:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
class UpdateSearchResultsToVersion2 < ActiveRecord::Migration
|
116
|
+
def change
|
117
|
+
replace_view :search_results, version: 2, revert_to_version: 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
Now you can run the migration like normal.
|
123
|
+
|
81
124
|
## Can I use this view to back a model?
|
82
125
|
|
83
126
|
You bet! Using view-backed models can help promote concepts hidden in your
|
data/Rakefile
CHANGED
@@ -12,4 +12,18 @@ namespace :dummy do
|
|
12
12
|
Dummy::Application.load_tasks
|
13
13
|
end
|
14
14
|
|
15
|
-
task
|
15
|
+
task(:spec).clear
|
16
|
+
desc "Run specs other than spec/acceptance"
|
17
|
+
RSpec::Core::RakeTask.new("spec") do |task|
|
18
|
+
task.exclude_pattern = "spec/acceptance/**/*_spec.rb"
|
19
|
+
task.verbose = false
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run acceptance specs in spec/acceptance"
|
23
|
+
RSpec::Core::RakeTask.new("spec:acceptance") do |task|
|
24
|
+
task.pattern = "spec/acceptance/**/*_spec.rb"
|
25
|
+
task.verbose = false
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Run the specs and acceptance tests"
|
29
|
+
task default: %w(spec spec:acceptance)
|
@@ -11,12 +11,12 @@ module Scenic
|
|
11
11
|
source_root File.expand_path("../templates", __FILE__)
|
12
12
|
|
13
13
|
def invoke_rails_model_generator
|
14
|
-
invoke "model", [
|
14
|
+
invoke "model", [file_path.singularize], options.merge(migration: false)
|
15
15
|
end
|
16
16
|
|
17
17
|
def inject_model_methods
|
18
18
|
if materialized? && generating?
|
19
|
-
inject_into_class "app/models/#{file_path}.rb", class_name do
|
19
|
+
inject_into_class "app/models/#{file_path.singularize}.rb", class_name do
|
20
20
|
evaluate_template("model.erb")
|
21
21
|
end
|
22
22
|
end
|
@@ -81,6 +81,31 @@ module Scenic
|
|
81
81
|
create_view(name, sql_definition)
|
82
82
|
end
|
83
83
|
|
84
|
+
# Replaces a view in the database using `CREATE OR REPLACE VIEW`.
|
85
|
+
#
|
86
|
+
# This results in a `CREATE OR REPLACE VIEW`. Most of the time the
|
87
|
+
# explicitness of the two step process used in {#update_view} is preferred
|
88
|
+
# to `CREATE OR REPLACE VIEW` because the former ensures that the view you
|
89
|
+
# are trying to update did, in fact, already exist. Additionally,
|
90
|
+
# `CREATE OR REPLACE VIEW` is allowed only to add new columns to the end
|
91
|
+
# of an existing view schema. Existing columns cannot be re-ordered,
|
92
|
+
# removed, or have their types changed. Drop and create overcomes this
|
93
|
+
# limitation as well.
|
94
|
+
#
|
95
|
+
# However, when there is a tangled dependency tree
|
96
|
+
# `CREATE OR REPLACE VIEW` can be preferable.
|
97
|
+
#
|
98
|
+
# This is typically called in a migration via
|
99
|
+
# {Statements#replace_view}.
|
100
|
+
#
|
101
|
+
# @param name The name of the view to update
|
102
|
+
# @param sql_definition The SQL schema for the updated view.
|
103
|
+
#
|
104
|
+
# @return [void]
|
105
|
+
def replace_view(name, sql_definition)
|
106
|
+
execute "CREATE OR REPLACE VIEW #{quote_table_name(name)} AS #{sql_definition};"
|
107
|
+
end
|
108
|
+
|
84
109
|
# Drops the named view from the database
|
85
110
|
#
|
86
111
|
# This is typically called in a migration via {Statements#drop_view}.
|
@@ -27,7 +27,8 @@ module Scenic
|
|
27
27
|
SELECT
|
28
28
|
c.relname as viewname,
|
29
29
|
pg_get_viewdef(c.oid) AS definition,
|
30
|
-
c.relkind AS kind
|
30
|
+
c.relkind AS kind,
|
31
|
+
n.nspname AS namespace
|
31
32
|
FROM pg_class c
|
32
33
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
33
34
|
WHERE
|
@@ -39,8 +40,16 @@ module Scenic
|
|
39
40
|
end
|
40
41
|
|
41
42
|
def to_scenic_view(result)
|
43
|
+
namespace, viewname = result.values_at "namespace", "viewname"
|
44
|
+
|
45
|
+
if namespace != "public"
|
46
|
+
namespaced_viewname = "#{namespace}.#{viewname}"
|
47
|
+
else
|
48
|
+
namespaced_viewname = viewname
|
49
|
+
end
|
50
|
+
|
42
51
|
Scenic::View.new(
|
43
|
-
name:
|
52
|
+
name: namespaced_viewname,
|
44
53
|
definition: result["definition"].strip,
|
45
54
|
materialized: result["kind"] == "m",
|
46
55
|
)
|
@@ -15,6 +15,10 @@ module Scenic
|
|
15
15
|
record(:update_view, args)
|
16
16
|
end
|
17
17
|
|
18
|
+
def replace_view(*args)
|
19
|
+
record(:replace_view, args)
|
20
|
+
end
|
21
|
+
|
18
22
|
def invert_create_view(args)
|
19
23
|
[:drop_view, args]
|
20
24
|
end
|
@@ -27,6 +31,10 @@ module Scenic
|
|
27
31
|
perform_scenic_inversion(:update_view, args)
|
28
32
|
end
|
29
33
|
|
34
|
+
def invert_replace_view(args)
|
35
|
+
perform_scenic_inversion(:replace_view, args)
|
36
|
+
end
|
37
|
+
|
30
38
|
private
|
31
39
|
|
32
40
|
def perform_scenic_inversion(method, args)
|
data/lib/scenic/statements.rb
CHANGED
@@ -89,6 +89,36 @@ module Scenic
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
# Update a database view to a new version using `CREATE OR REPLACE VIEW`.
|
93
|
+
#
|
94
|
+
# The existing view is replaced using the supplied `version`
|
95
|
+
# parameter.
|
96
|
+
#
|
97
|
+
# Does not work with materialized views due to lack of database support.
|
98
|
+
#
|
99
|
+
# @param name [String, Symbol] The name of the database view.
|
100
|
+
# @param version [Fixnum] The version number of the view.
|
101
|
+
# @param revert_to_version [Fixnum] The version number to rollback to on
|
102
|
+
# `rake db rollback`
|
103
|
+
# @return The database response from executing the create statement.
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# replace_view :engagement_reports, version: 3, revert_to_version: 2
|
107
|
+
#
|
108
|
+
def replace_view(name, version: nil, revert_to_version: nil, materialized: false)
|
109
|
+
if version.blank?
|
110
|
+
raise ArgumentError, "version is required"
|
111
|
+
end
|
112
|
+
|
113
|
+
if materialized
|
114
|
+
raise ArgumentError, "Cannot replace materialized views"
|
115
|
+
end
|
116
|
+
|
117
|
+
sql_definition = definition(name, version)
|
118
|
+
|
119
|
+
Scenic.database.replace_view(name, sql_definition)
|
120
|
+
end
|
121
|
+
|
92
122
|
private
|
93
123
|
|
94
124
|
def definition(name, version)
|
data/lib/scenic/version.rb
CHANGED
data/lib/scenic/view.rb
CHANGED
@@ -43,8 +43,10 @@ module Scenic
|
|
43
43
|
# @api private
|
44
44
|
def to_schema
|
45
45
|
materialized_option = materialized ? "materialized: true, " : ""
|
46
|
+
safe_to_symbolize_name = name.include?(".") ? "'#{name}'" : name
|
47
|
+
|
46
48
|
<<-DEFINITION
|
47
|
-
create_view :#{
|
49
|
+
create_view :#{safe_to_symbolize_name}, #{materialized_option} sql_definition: <<-\SQL
|
48
50
|
#{definition.indent(2)}
|
49
51
|
SQL
|
50
52
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "acceptance_helper"
|
2
|
+
|
3
|
+
describe "User manages views" do
|
4
|
+
it "handles simple views" do
|
5
|
+
successfully "rails generate scenic:model search_result"
|
6
|
+
write_definition "search_results_v01", "SELECT 'needle'::text AS term"
|
7
|
+
|
8
|
+
successfully "rake db:migrate"
|
9
|
+
verify_result "SearchResult.take.term", "needle"
|
10
|
+
|
11
|
+
successfully "rails generate scenic:view search_results"
|
12
|
+
verify_identical_view_definitions "search_results_v01", "search_results_v02"
|
13
|
+
|
14
|
+
write_definition "search_results_v02", "SELECT 'haystack'::text AS term"
|
15
|
+
successfully "rake db:migrate"
|
16
|
+
|
17
|
+
successfully "rake db:reset"
|
18
|
+
verify_result "SearchResult.take.term", "haystack"
|
19
|
+
|
20
|
+
successfully "rake db:rollback"
|
21
|
+
successfully "rake db:rollback"
|
22
|
+
successfully "rails destroy scenic:model search_result"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "handles materialized views" do
|
26
|
+
successfully "rails generate scenic:model child --materialized"
|
27
|
+
write_definition "children_v01", "SELECT 'Owen'::text AS name, 5 AS age"
|
28
|
+
|
29
|
+
successfully "rake db:migrate"
|
30
|
+
verify_result "Child.take.name", "Owen"
|
31
|
+
|
32
|
+
add_index "children", "name"
|
33
|
+
add_index "children", "age"
|
34
|
+
|
35
|
+
successfully "rails runner 'Child.refresh'"
|
36
|
+
|
37
|
+
successfully "rails generate scenic:view child --materialized"
|
38
|
+
verify_identical_view_definitions "children_v01", "children_v02"
|
39
|
+
|
40
|
+
write_definition "children_v02", "SELECT 'Elliot'::text AS name"
|
41
|
+
successfully "rake db:migrate"
|
42
|
+
|
43
|
+
successfully "rake db:reset"
|
44
|
+
verify_result "Child.take.name", "Elliot"
|
45
|
+
verify_schema_contains 'add_index "children"'
|
46
|
+
|
47
|
+
successfully "rake db:rollback"
|
48
|
+
successfully "rake db:rollback"
|
49
|
+
successfully "rails destroy scenic:model child"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "handles plural view names gracefully during generation" do
|
53
|
+
successfully "rails generate scenic:model search_results --materialized"
|
54
|
+
end
|
55
|
+
|
56
|
+
def successfully(command)
|
57
|
+
`RAILS_ENV=test #{command}`
|
58
|
+
expect($?.exitstatus).to eq(0), "'#{command}' was unsuccessful"
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_definition(file, contents)
|
62
|
+
File.open("db/views/#{file}.sql", File::WRONLY) do |definition|
|
63
|
+
definition.truncate(0)
|
64
|
+
definition.write(contents)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def verify_result(command, expected_output)
|
69
|
+
successfully %{rails runner "#{command} == '#{expected_output}' || exit(1)"}
|
70
|
+
end
|
71
|
+
|
72
|
+
def verify_identical_view_definitions(def_a, def_b)
|
73
|
+
successfully "cmp db/views/#{def_a}.sql db/views/#{def_b}.sql"
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_index(table, column)
|
77
|
+
successfully(<<-CMD.strip)
|
78
|
+
rails runner 'ActiveRecord::Migration.add_index "#{table}", "#{column}"'
|
79
|
+
CMD
|
80
|
+
end
|
81
|
+
|
82
|
+
def verify_schema_contains(statement)
|
83
|
+
expect(File.readlines("db/schema.rb").grep(/#{statement}/))
|
84
|
+
.not_to be_empty, "Schema does not contain '#{statement}'"
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "bundler"
|
2
|
+
|
3
|
+
ENV["RAILS_ENV"] = "test"
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.around(:each) do |example|
|
7
|
+
Dir.chdir("spec/dummy") do
|
8
|
+
example.run
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
config.before(:suite) do
|
13
|
+
Dir.chdir("spec/dummy") do
|
14
|
+
system <<-CMD
|
15
|
+
git init 1>/dev/null &&
|
16
|
+
git add -A &&
|
17
|
+
git commit --no-gpg-sign --message 'initial' 1>/dev/null
|
18
|
+
CMD
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
config.after(:suite) do
|
23
|
+
Dir.chdir("spec/dummy") do
|
24
|
+
system <<-CMD
|
25
|
+
rake db:drop db:create &&
|
26
|
+
git add -A &&
|
27
|
+
git reset --hard HEAD 1>/dev/null &&
|
28
|
+
rm -rf .git/ 1>/dev/null
|
29
|
+
CMD
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -38,6 +38,22 @@ module Scenic
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
describe "#replace_view" do
|
42
|
+
it "successfully replaces a view" do
|
43
|
+
adapter = Postgres.new
|
44
|
+
|
45
|
+
adapter.create_view("greetings", "SELECT text 'hi' AS greeting")
|
46
|
+
|
47
|
+
view = adapter.views.first.definition
|
48
|
+
expect(view).to eql "SELECT 'hi'::text AS greeting;"
|
49
|
+
|
50
|
+
adapter.replace_view("greetings", "SELECT text 'hello' AS greeting")
|
51
|
+
|
52
|
+
view = adapter.views.first.definition
|
53
|
+
expect(view).to eql "SELECT 'hello'::text AS greeting;"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
41
57
|
describe "#drop_view" do
|
42
58
|
it "successfully drops a view" do
|
43
59
|
adapter = Postgres.new
|
@@ -137,6 +153,27 @@ module Scenic
|
|
137
153
|
"people_with_names",
|
138
154
|
]
|
139
155
|
end
|
156
|
+
|
157
|
+
context "with views in non public schemas" do
|
158
|
+
it "returns also the non public views" do
|
159
|
+
adapter = Postgres.new
|
160
|
+
|
161
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
162
|
+
CREATE VIEW parents AS SELECT text 'Joe' AS name
|
163
|
+
SQL
|
164
|
+
|
165
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
166
|
+
CREATE SCHEMA scenic;
|
167
|
+
CREATE VIEW scenic.parents AS SELECT text 'Maarten' AS name;
|
168
|
+
SET search_path TO scenic, public;
|
169
|
+
SQL
|
170
|
+
|
171
|
+
expect(adapter.views.map(&:name)).to eq [
|
172
|
+
"parents",
|
173
|
+
"scenic.parents",
|
174
|
+
]
|
175
|
+
end
|
176
|
+
end
|
140
177
|
end
|
141
178
|
end
|
142
179
|
end
|
@@ -65,6 +65,32 @@ describe Scenic::CommandRecorder do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
describe "#replace_view" do
|
69
|
+
it "records the replaced view" do
|
70
|
+
args = [:users, { version: 2 }]
|
71
|
+
|
72
|
+
recorder.replace_view(*args)
|
73
|
+
|
74
|
+
expect(recorder.commands).to eq [[:replace_view, args, nil]]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "reverts to replace_view with the specified revert_to_version" do
|
78
|
+
args = [:users, { version: 2, revert_to_version: 1 }]
|
79
|
+
revert_args = [:users, { version: 1 }]
|
80
|
+
|
81
|
+
recorder.revert { recorder.replace_view(*args) }
|
82
|
+
|
83
|
+
expect(recorder.commands).to eq [[:replace_view, revert_args]]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "raises when reverting without revert_to_version set" do
|
87
|
+
args = [:users, { version: 42, another_argument: 1 }]
|
88
|
+
|
89
|
+
expect { recorder.revert { recorder.replace_view(*args) } }
|
90
|
+
.to raise_error(ActiveRecord::IrreversibleMigration)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
68
94
|
def recorder
|
69
95
|
@recorder ||= ActiveRecord::Migration::CommandRecorder.new
|
70
96
|
end
|
@@ -20,4 +20,20 @@ describe Scenic::SchemaDumper, :db do
|
|
20
20
|
|
21
21
|
expect(Search.first.haystack).to eq "needle"
|
22
22
|
end
|
23
|
+
|
24
|
+
context "with views in non public schemas" do
|
25
|
+
it "dumps a create_view including namespace for a view in the database" do
|
26
|
+
view_definition = "SELECT 'needle'::text AS haystack"
|
27
|
+
Search.connection.execute "CREATE SCHEMA scenic; SET search_path TO scenic, public"
|
28
|
+
Search.connection.create_view :"scenic.searches", sql_definition: view_definition
|
29
|
+
stream = StringIO.new
|
30
|
+
|
31
|
+
ActiveRecord::SchemaDumper.dump(Search.connection, stream)
|
32
|
+
|
33
|
+
output = stream.string
|
34
|
+
expect(output).to include "create_view :'scenic.searches',"
|
35
|
+
|
36
|
+
Search.connection.drop_view :'scenic.searches'
|
37
|
+
end
|
38
|
+
end
|
23
39
|
end
|
@@ -95,6 +95,36 @@ module Scenic
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
+
describe "replace_view" do
|
99
|
+
it "replaces the view in the database" do
|
100
|
+
definition = instance_double("Definition", to_sql: "definition")
|
101
|
+
allow(Definition).to receive(:new)
|
102
|
+
.with(:name, 3)
|
103
|
+
.and_return(definition)
|
104
|
+
|
105
|
+
connection.replace_view(:name, version: 3)
|
106
|
+
|
107
|
+
expect(Scenic.database).to have_received(:replace_view)
|
108
|
+
.with(:name, definition.to_sql)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "fails to replace the materialized view in the database" do
|
112
|
+
definition = instance_double("Definition", to_sql: "definition")
|
113
|
+
allow(Definition).to receive(:new)
|
114
|
+
.with(:name, 3)
|
115
|
+
.and_return(definition)
|
116
|
+
|
117
|
+
expect do
|
118
|
+
connection.replace_view(:name, version: 3, materialized: true)
|
119
|
+
end.to raise_error(ArgumentError, /Cannot replace materialized views/)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "raises an error if not supplied a version" do
|
123
|
+
expect { connection.replace_view :views }
|
124
|
+
.to raise_error(ArgumentError, /version is required/)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
98
128
|
def connection
|
99
129
|
Class.new { extend Statements }
|
100
130
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scenic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Prior
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-05-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: appraisal
|
@@ -234,6 +234,8 @@ files:
|
|
234
234
|
- lib/scenic/version.rb
|
235
235
|
- lib/scenic/view.rb
|
236
236
|
- scenic.gemspec
|
237
|
+
- spec/acceptance/user_manages_views_spec.rb
|
238
|
+
- spec/acceptance_helper.rb
|
237
239
|
- spec/dummy/.gitignore
|
238
240
|
- spec/dummy/Rakefile
|
239
241
|
- spec/dummy/bin/bundle
|
@@ -258,7 +260,6 @@ files:
|
|
258
260
|
- spec/scenic/definition_spec.rb
|
259
261
|
- spec/scenic/schema_dumper_spec.rb
|
260
262
|
- spec/scenic/statements_spec.rb
|
261
|
-
- spec/smoke
|
262
263
|
- spec/spec_helper.rb
|
263
264
|
- spec/support/generator_spec_setup.rb
|
264
265
|
- spec/support/view_definition_helpers.rb
|
@@ -282,11 +283,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
282
283
|
version: '0'
|
283
284
|
requirements: []
|
284
285
|
rubyforge_project:
|
285
|
-
rubygems_version: 2.
|
286
|
+
rubygems_version: 2.5.1
|
286
287
|
signing_key:
|
287
288
|
specification_version: 4
|
288
289
|
summary: Support for database views in Rails migrations
|
289
290
|
test_files:
|
291
|
+
- spec/acceptance/user_manages_views_spec.rb
|
292
|
+
- spec/acceptance_helper.rb
|
290
293
|
- spec/dummy/.gitignore
|
291
294
|
- spec/dummy/Rakefile
|
292
295
|
- spec/dummy/bin/bundle
|
@@ -311,7 +314,6 @@ test_files:
|
|
311
314
|
- spec/scenic/definition_spec.rb
|
312
315
|
- spec/scenic/schema_dumper_spec.rb
|
313
316
|
- spec/scenic/statements_spec.rb
|
314
|
-
- spec/smoke
|
315
317
|
- spec/spec_helper.rb
|
316
318
|
- spec/support/generator_spec_setup.rb
|
317
319
|
- spec/support/view_definition_helpers.rb
|
data/spec/smoke
DELETED
@@ -1,135 +0,0 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
|
3
|
-
set -euo pipefail
|
4
|
-
|
5
|
-
setup() {
|
6
|
-
cd spec/dummy
|
7
|
-
git init
|
8
|
-
git add -A
|
9
|
-
git commit --no-gpg-sign --message "initial"
|
10
|
-
}
|
11
|
-
|
12
|
-
teardown() {
|
13
|
-
git add -A
|
14
|
-
git reset --hard HEAD
|
15
|
-
rm -rf .git/
|
16
|
-
rake db:drop db:create
|
17
|
-
}
|
18
|
-
|
19
|
-
trap teardown EXIT
|
20
|
-
|
21
|
-
verifySearchResults() {
|
22
|
-
echo "verify search results"
|
23
|
-
local expectedResult=$1
|
24
|
-
local actualResult=$(rails runner "puts Search.take.results")
|
25
|
-
[[ "$actualResult" == "$expectedResult" ]] || exit 1
|
26
|
-
echo "[success]"
|
27
|
-
}
|
28
|
-
|
29
|
-
verifySchemaContains() {
|
30
|
-
local expectedString=$1
|
31
|
-
echo "verify schema contains '$expectedString'"
|
32
|
-
grep -q "$expectedString" db/schema.rb || exit 1
|
33
|
-
echo "[success]"
|
34
|
-
}
|
35
|
-
|
36
|
-
writeToFileAndMigrateAndVerifySearchResults() {
|
37
|
-
echo "write search definition and migrate"
|
38
|
-
local version=$1
|
39
|
-
local expectedResult=$2
|
40
|
-
local filePath=db/views/searches_v$version\.sql
|
41
|
-
cp /dev/null $filePath
|
42
|
-
echo "SELECT '$expectedResult'::text AS results, 1 AS user_id" >> $filePath
|
43
|
-
rake db:migrate
|
44
|
-
echo "[success]"
|
45
|
-
verifySearchResults $expectedResult
|
46
|
-
}
|
47
|
-
|
48
|
-
main() {
|
49
|
-
setup
|
50
|
-
echo "rails generate scenic:model search"
|
51
|
-
rails generate scenic:model search
|
52
|
-
[[ -f db/views/searches_v01.sql ]] || exit 1
|
53
|
-
[[ -f app/models/search.rb ]] || exit 1
|
54
|
-
[[ -n "$(find db/migrate -maxdepth 1 -name "*create_searches.rb" -print -quit)" ]] || exit 1
|
55
|
-
echo "[success]"
|
56
|
-
|
57
|
-
writeToFileAndMigrateAndVerifySearchResults "01" "search-results"
|
58
|
-
|
59
|
-
echo "rails generate scenic:view search (to get updates search view)"
|
60
|
-
rails generate scenic:view search
|
61
|
-
[[ -f db/views/searches_v02.sql ]] || exit 1
|
62
|
-
cmp db/views/searches_v01.sql db/views/searches_v02.sql || exit 1
|
63
|
-
[[ -n "$(find db/migrate -maxdepth 1 -name "*update_searches_to_version_2.rb" -print -quit)" ]] || exit 1
|
64
|
-
echo "[success]"
|
65
|
-
|
66
|
-
writeToFileAndMigrateAndVerifySearchResults "02" "different-results"
|
67
|
-
|
68
|
-
echo "rake db:rollback"
|
69
|
-
rake db:rollback
|
70
|
-
echo "[success]"
|
71
|
-
|
72
|
-
verifySearchResults "search-results"
|
73
|
-
|
74
|
-
echo "rails destroy scenic:view search"
|
75
|
-
rails destroy scenic:view search
|
76
|
-
[[ ! -f db/views/searches_v02.sql ]] || exit 1
|
77
|
-
[[ -z "$(find db/migrate -maxdepth 1 -name "*update_searches_to_version_2.rb" -print -quit)" ]] || exit 1
|
78
|
-
[[ -f db/views/searches_v01.sql ]] || exit 1
|
79
|
-
[[ -f app/models/search.rb ]] || exit 1
|
80
|
-
[[ -n "$(find db/migrate -maxdepth 1 -name "*create_searches.rb" -print -quit)" ]] || exit 1
|
81
|
-
echo "[success]"
|
82
|
-
|
83
|
-
echo "rake db:rollback"
|
84
|
-
rake db:rollback
|
85
|
-
echo "[success]"
|
86
|
-
|
87
|
-
echo "rails destroy scenic:view search"
|
88
|
-
rails destroy scenic:model search
|
89
|
-
[[ ! -f db/views/searches_v01.sql ]] || exit 1
|
90
|
-
[[ ! -f app/models/search.rb ]] || exit 1
|
91
|
-
[[ -z "$(find db/migrate -maxdepth 1 -name "*create_searches.rb" -print -quit)" ]] || exit 1
|
92
|
-
echo "[success]"
|
93
|
-
|
94
|
-
echo "rails generate scenic:model search (materialized)"
|
95
|
-
rails generate scenic:model search --materialized
|
96
|
-
[[ -f db/views/searches_v01.sql ]] || exit 1
|
97
|
-
[[ -f app/models/search.rb ]] || exit 1
|
98
|
-
[[ -n "$(find db/migrate -maxdepth 1 -name "*create_searches.rb" -print -quit)" ]] || exit 1
|
99
|
-
echo "[success]"
|
100
|
-
|
101
|
-
writeToFileAndMigrateAndVerifySearchResults "01" "search-results"
|
102
|
-
|
103
|
-
echo "refresh materialized view"
|
104
|
-
rails runner "Search.refresh" || exit 1
|
105
|
-
echo "[success]"
|
106
|
-
|
107
|
-
echo "add indexes to materialized view"
|
108
|
-
rails runner 'ActiveRecord::Migration.add_index :searches, :results' || exit 1
|
109
|
-
rails runner 'ActiveRecord::Migration.add_index :searches, :user_id' || exit 1
|
110
|
-
echo "[success]"
|
111
|
-
|
112
|
-
echo "update materialized view"
|
113
|
-
rails generate scenic:view search --materialized
|
114
|
-
echo "SELECT 'test'::text AS results" > db/views/searches_v02.sql
|
115
|
-
rake db:migrate
|
116
|
-
verifySearchResults 'test'
|
117
|
-
verifySchemaContains 'add_index "searches"'
|
118
|
-
|
119
|
-
echo "rake db:rollback"
|
120
|
-
rake db:rollback
|
121
|
-
rake db:rollback
|
122
|
-
echo "[success]"
|
123
|
-
|
124
|
-
echo "rails destroy scenic:model search --materialized"
|
125
|
-
rails destroy scenic:view search --materialized
|
126
|
-
rails destroy scenic:model search --materialized
|
127
|
-
[[ ! -f app/models/search.rb ]] || exit 1
|
128
|
-
[[ ! -f db/views/searches_v01.sql ]] || exit 1
|
129
|
-
[[ -z "$(find db/migrate -maxdepth 1 -name "*create_searches.rb" -print -quit)" ]] || exit 1
|
130
|
-
echo "[success]"
|
131
|
-
|
132
|
-
echo "[done]"
|
133
|
-
}
|
134
|
-
|
135
|
-
main $*
|