scenic 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.hound.yml +2 -4
- data/.rubocop.yml +129 -0
- data/.travis.yml +11 -17
- data/Appraisals +14 -21
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +3 -7
- data/Gemfile +1 -1
- data/NEWS.md +32 -13
- data/README.md +15 -16
- data/Rakefile +2 -2
- data/gemfiles/rails42.gemfile +1 -1
- data/gemfiles/rails50.gemfile +1 -1
- data/gemfiles/rails51.gemfile +1 -1
- data/gemfiles/rails52.gemfile +8 -0
- data/gemfiles/rails_edge.gemfile +3 -3
- data/lib/generators/scenic/materializable.rb +9 -0
- data/lib/generators/scenic/model/model_generator.rb +2 -2
- data/lib/generators/scenic/view/USAGE +1 -0
- data/lib/generators/scenic/view/templates/db/migrate/create_view.erb +1 -1
- data/lib/generators/scenic/view/templates/db/migrate/update_view.erb +1 -1
- data/lib/generators/scenic/view/view_generator.rb +13 -5
- data/lib/scenic/adapters/postgres.rb +14 -4
- data/lib/scenic/adapters/postgres/refresh_dependencies.rb +12 -2
- data/lib/scenic/adapters/postgres/views.rb +10 -1
- data/lib/scenic/schema_dumper.rb +2 -2
- data/lib/scenic/statements.rb +24 -6
- data/lib/scenic/version.rb +1 -1
- data/lib/scenic/view.rb +1 -2
- data/scenic.gemspec +22 -23
- data/spec/acceptance/user_manages_views_spec.rb +2 -1
- data/spec/dummy/app/models/application_record.rb +5 -0
- data/spec/generators/scenic/model/model_generator_spec.rb +1 -1
- data/spec/generators/scenic/view/view_generator_spec.rb +1 -1
- data/spec/scenic/adapters/postgres/refresh_dependencies_spec.rb +60 -26
- data/spec/scenic/adapters/postgres_spec.rb +2 -2
- data/spec/scenic/definition_spec.rb +1 -1
- data/spec/scenic/schema_dumper_spec.rb +17 -2
- data/spec/scenic/statements_spec.rb +48 -13
- data/spec/spec_helper.rb +1 -1
- data/spec/support/generator_spec_setup.rb +1 -1
- metadata +15 -13
- data/gemfiles/rails40.gemfile +0 -8
- data/gemfiles/rails41.gemfile +0 -8
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# Scenic
|
2
2
|
|
3
|
-
![Scenic Landscape](https://images.
|
3
|
+
![Scenic Landscape](https://user-images.githubusercontent.com/152152/49344534-a8817480-f646-11e8-8431-3d95d349c070.png)
|
4
4
|
|
5
|
-
[![Build Status](https://travis-ci.org/
|
6
|
-
[![
|
7
|
-
[![
|
5
|
+
[![Build Status](https://travis-ci.org/scenic-views/scenic.svg?branch=master)](https://travis-ci.org/scenic-views/scenic)
|
6
|
+
[![Documentation Quality](http://inch-ci.org/github/scenic-views/scenic.svg?branch=master)](http://inch-ci.org/github/scenic-views/scenic)
|
7
|
+
[![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
|
8
8
|
|
9
9
|
Scenic adds methods to `ActiveRecord::Migration` to create and manage database
|
10
10
|
views in Rails.
|
@@ -21,6 +21,12 @@ Scenic ships with support for PostgreSQL. The adapter is configurable (see
|
|
21
21
|
`Scenic::Configuration`) and has a minimal interface (see
|
22
22
|
`Scenic::Adapters::Postgres`) that other gems can provide.
|
23
23
|
|
24
|
+
## So how do I install this?
|
25
|
+
|
26
|
+
If you're using Postgres, Add `gem "scenic"` to your Gemfile and run `bundle
|
27
|
+
install`. If you're using something other than Postgres, check out the available
|
28
|
+
[third party adapters](https://github.com/scenic-views/scenic#faqs).
|
29
|
+
|
24
30
|
## Great, how do I create a view?
|
25
31
|
|
26
32
|
You've got this great idea for a view you'd like to call `search_results`. You
|
@@ -247,21 +253,14 @@ We are aware of the following existing adapter libraries for Scenic which may
|
|
247
253
|
meet your needs:
|
248
254
|
|
249
255
|
* [scenic_sqlite_adapter](https://github.com/pdebelak/scenic_sqlite_adapter)
|
250
|
-
* [scenic-mysql_adapter](https://github.com/EmpaticoOrg/scenic-mysql_adapter
|
256
|
+
* [scenic-mysql_adapter](https://github.com/EmpaticoOrg/scenic-mysql_adapter)
|
257
|
+
* [scenic-sqlserver-adapter](https://github.com/ClickMechanic/scenic_sqlserver_adapter)
|
258
|
+
* [scenic-oracle_enhanced_adapter](https://github.com/PMACS/scenic_oracle_enhanced_adapter)
|
251
259
|
|
252
260
|
## About
|
253
261
|
|
254
|
-
Scenic is maintained by [Derek Prior]
|
255
|
-
|
256
|
-
thoughtbot, inc.
|
262
|
+
Scenic is maintained by [Derek Prior], [Caleb Thompson], and you, our
|
263
|
+
contributors.
|
257
264
|
|
258
265
|
[Derek Prior]: http://prioritized.net
|
259
266
|
[Caleb Thompson]: http://calebthompson.io
|
260
|
-
|
261
|
-
![thoughtbot](http://presskit.thoughtbot.com/images/thoughtbot-logo-for-readmes.svg)
|
262
|
-
|
263
|
-
We love open source software! See [our other projects][community] or [hire
|
264
|
-
us][hire] to help build your product.
|
265
|
-
|
266
|
-
[community]: https://thoughtbot.com/community?utm_source=github
|
267
|
-
[hire]: https://thoughtbot.com/hire-us?utm_source=github
|
data/Rakefile
CHANGED
data/gemfiles/rails42.gemfile
CHANGED
data/gemfiles/rails50.gemfile
CHANGED
data/gemfiles/rails51.gemfile
CHANGED
data/gemfiles/rails_edge.gemfile
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", :
|
6
|
-
gem "arel", :
|
5
|
+
gem "rails", git: "https://github.com/rails/rails"
|
6
|
+
gem "arel", git: "https://github.com/rails/arel"
|
7
7
|
|
8
|
-
gemspec :
|
8
|
+
gemspec path: "../"
|
@@ -10,6 +10,11 @@ module Scenic
|
|
10
10
|
required: false,
|
11
11
|
desc: "Makes the view materialized",
|
12
12
|
default: false
|
13
|
+
class_option :no_data,
|
14
|
+
type: :boolean,
|
15
|
+
required: false,
|
16
|
+
desc: "Adds WITH NO DATA when materialized view creates/updates",
|
17
|
+
default: false
|
13
18
|
end
|
14
19
|
|
15
20
|
private
|
@@ -17,6 +22,10 @@ module Scenic
|
|
17
22
|
def materialized?
|
18
23
|
options[:materialized]
|
19
24
|
end
|
25
|
+
|
26
|
+
def no_data?
|
27
|
+
options[:no_data]
|
28
|
+
end
|
20
29
|
end
|
21
30
|
end
|
22
31
|
end
|
@@ -8,7 +8,7 @@ module Scenic
|
|
8
8
|
# @api private
|
9
9
|
class ModelGenerator < Rails::Generators::NamedBase
|
10
10
|
include Scenic::Generators::Materializable
|
11
|
-
source_root File.expand_path("
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
12
12
|
|
13
13
|
def invoke_rails_model_generator
|
14
14
|
invoke "model",
|
@@ -35,7 +35,7 @@ module Scenic
|
|
35
35
|
|
36
36
|
def evaluate_template(source)
|
37
37
|
source = File.expand_path(find_in_source_paths(source.to_s))
|
38
|
-
context = instance_eval("binding")
|
38
|
+
context = instance_eval("binding", __FILE__, __LINE__)
|
39
39
|
ERB.new(
|
40
40
|
::File.binread(source),
|
41
41
|
nil,
|
@@ -4,7 +4,7 @@ class <%= migration_class_name %> < <%= activerecord_migration_class %>
|
|
4
4
|
update_view <%= formatted_plural_name %>,
|
5
5
|
version: <%= version %>,
|
6
6
|
revert_to_version: <%= previous_version %>,
|
7
|
-
materialized: true
|
7
|
+
materialized: <%= no_data? ? "{ no_data: true }" : true %>
|
8
8
|
<%- else -%>
|
9
9
|
update_view <%= formatted_plural_name %>, version: <%= version %>, revert_to_version: <%= previous_version %>
|
10
10
|
<%- end -%>
|
@@ -8,7 +8,7 @@ module Scenic
|
|
8
8
|
class ViewGenerator < Rails::Generators::NamedBase
|
9
9
|
include Rails::Generators::Migration
|
10
10
|
include Scenic::Generators::Materializable
|
11
|
-
source_root File.expand_path("
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
12
12
|
|
13
13
|
def create_views_directory
|
14
14
|
unless views_directory_path.exist?
|
@@ -56,7 +56,7 @@ module Scenic
|
|
56
56
|
|
57
57
|
def migration_class_name
|
58
58
|
if creating_new_view?
|
59
|
-
"Create#{class_name.
|
59
|
+
"Create#{class_name.tr('.', '').pluralize}"
|
60
60
|
else
|
61
61
|
"Update#{class_name.pluralize}ToVersion#{version}"
|
62
62
|
end
|
@@ -74,7 +74,7 @@ module Scenic
|
|
74
74
|
private
|
75
75
|
|
76
76
|
def views_directory_path
|
77
|
-
@views_directory_path ||= Rails.root.join(
|
77
|
+
@views_directory_path ||= Rails.root.join("db", "views")
|
78
78
|
end
|
79
79
|
|
80
80
|
def version_regex
|
@@ -82,7 +82,7 @@ module Scenic
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def creating_new_view?
|
85
|
-
previous_version
|
85
|
+
previous_version.zero?
|
86
86
|
end
|
87
87
|
|
88
88
|
def definition
|
@@ -94,7 +94,7 @@ module Scenic
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def plural_file_name
|
97
|
-
@plural_file_name ||= file_name.pluralize.
|
97
|
+
@plural_file_name ||= file_name.pluralize.tr(".", "_")
|
98
98
|
end
|
99
99
|
|
100
100
|
def destroying?
|
@@ -109,6 +109,14 @@ module Scenic
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
+
def create_view_options
|
113
|
+
if materialized?
|
114
|
+
", materialized: #{no_data? ? '{ no_data: true }' : true}"
|
115
|
+
else
|
116
|
+
""
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
112
120
|
def destroying_initial_view?
|
113
121
|
destroying? && version == 1
|
114
122
|
end
|
@@ -122,6 +122,9 @@ module Scenic
|
|
122
122
|
#
|
123
123
|
# @param name The name of the materialized view to create
|
124
124
|
# @param sql_definition The SQL schema that defines the materialized view.
|
125
|
+
# @param no_data [Boolean] Default: false. Set to true to create
|
126
|
+
# materialized view without running the associated query. You will need
|
127
|
+
# to perform a non-concurrent refresh to populate with data.
|
125
128
|
#
|
126
129
|
# This is typically called in a migration via {Statements#create_view}.
|
127
130
|
#
|
@@ -129,9 +132,13 @@ module Scenic
|
|
129
132
|
# in use does not support materialized views.
|
130
133
|
#
|
131
134
|
# @return [void]
|
132
|
-
def create_materialized_view(name, sql_definition)
|
135
|
+
def create_materialized_view(name, sql_definition, no_data: false)
|
133
136
|
raise_unless_materialized_views_supported
|
134
|
-
execute
|
137
|
+
execute <<-SQL
|
138
|
+
CREATE MATERIALIZED VIEW #{quote_table_name(name)} AS
|
139
|
+
#{sql_definition}
|
140
|
+
#{'WITH NO DATA' if no_data};
|
141
|
+
SQL
|
135
142
|
end
|
136
143
|
|
137
144
|
# Updates a materialized view in the database.
|
@@ -144,17 +151,20 @@ module Scenic
|
|
144
151
|
#
|
145
152
|
# @param name The name of the view to update
|
146
153
|
# @param sql_definition The SQL schema for the updated view.
|
154
|
+
# @param no_data [Boolean] Default: false. Set to true to create
|
155
|
+
# materialized view without running the associated query. You will need
|
156
|
+
# to perform a non-concurrent refresh to populate with data.
|
147
157
|
#
|
148
158
|
# @raise [MaterializedViewsNotSupportedError] if the version of Postgres
|
149
159
|
# in use does not support materialized views.
|
150
160
|
#
|
151
161
|
# @return [void]
|
152
|
-
def update_materialized_view(name, sql_definition)
|
162
|
+
def update_materialized_view(name, sql_definition, no_data: false)
|
153
163
|
raise_unless_materialized_views_supported
|
154
164
|
|
155
165
|
IndexReapplication.new(connection: connection).on(name) do
|
156
166
|
drop_materialized_view(name)
|
157
|
-
create_materialized_view(name, sql_definition)
|
167
|
+
create_materialized_view(name, sql_definition, no_data: no_data)
|
158
168
|
end
|
159
169
|
end
|
160
170
|
|
@@ -46,10 +46,20 @@ module Scenic
|
|
46
46
|
def to_sorted_array
|
47
47
|
dependency_hash = parse_to_hash(raw_dependencies)
|
48
48
|
sorted_arr = tsort(dependency_hash)
|
49
|
+
|
49
50
|
idx = sorted_arr.find_index do |dep|
|
50
|
-
|
51
|
+
if view_to_refresh.to_s.include?(".")
|
52
|
+
dep == view_to_refresh.to_s
|
53
|
+
else
|
54
|
+
dep.ends_with?(".#{view_to_refresh}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if idx.present?
|
59
|
+
sorted_arr[0...idx]
|
60
|
+
else
|
61
|
+
[]
|
51
62
|
end
|
52
|
-
sorted_arr[0...idx]
|
53
63
|
end
|
54
64
|
|
55
65
|
private
|
@@ -57,7 +57,16 @@ module Scenic
|
|
57
57
|
|
58
58
|
def pg_identifier(name)
|
59
59
|
return name if name =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/
|
60
|
-
|
60
|
+
|
61
|
+
pgconn.quote_ident(name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def pgconn
|
65
|
+
if defined?(PG::Connection)
|
66
|
+
PG::Connection
|
67
|
+
else
|
68
|
+
PGconn
|
69
|
+
end
|
61
70
|
end
|
62
71
|
end
|
63
72
|
end
|
data/lib/scenic/schema_dumper.rb
CHANGED
@@ -32,8 +32,8 @@ module Scenic
|
|
32
32
|
def ignored?(table_name)
|
33
33
|
["schema_migrations", ignore_tables].flatten.any? do |ignored|
|
34
34
|
case ignored
|
35
|
-
when String
|
36
|
-
when Regexp
|
35
|
+
when String then remove_prefix_and_suffix(table_name) == ignored
|
36
|
+
when Regexp then remove_prefix_and_suffix(table_name) =~ ignored
|
37
37
|
else
|
38
38
|
raise StandardError, "ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values."
|
39
39
|
end
|
data/lib/scenic/statements.rb
CHANGED
@@ -9,8 +9,9 @@ module Scenic
|
|
9
9
|
# @param sql_definition [String] The SQL query for the view schema. An error
|
10
10
|
# will be raised if `sql_definition` and `version` are both set,
|
11
11
|
# as they are mutually exclusive.
|
12
|
-
# @param materialized [Boolean] Set to true to create a materialized
|
13
|
-
#
|
12
|
+
# @param materialized [Boolean, Hash] Set to true to create a materialized
|
13
|
+
# view. Set to { no_data: true } to create materialized view without
|
14
|
+
# loading data. Defaults to false.
|
14
15
|
# @return The database response from executing the create statement.
|
15
16
|
#
|
16
17
|
# @example Create from `db/views/searches_v02.sql`
|
@@ -36,7 +37,11 @@ module Scenic
|
|
36
37
|
sql_definition ||= definition(name, version)
|
37
38
|
|
38
39
|
if materialized
|
39
|
-
Scenic.database.create_materialized_view(
|
40
|
+
Scenic.database.create_materialized_view(
|
41
|
+
name,
|
42
|
+
sql_definition,
|
43
|
+
no_data: no_data(materialized),
|
44
|
+
)
|
40
45
|
else
|
41
46
|
Scenic.database.create_view(name, sql_definition)
|
42
47
|
end
|
@@ -75,8 +80,9 @@ module Scenic
|
|
75
80
|
# as they are mutually exclusive.
|
76
81
|
# @param revert_to_version [Fixnum] The version number to rollback to on
|
77
82
|
# `rake db rollback`
|
78
|
-
# @param materialized [Boolean] True if updating a materialized view.
|
79
|
-
#
|
83
|
+
# @param materialized [Boolean, Hash] True if updating a materialized view.
|
84
|
+
# Set to { no_data: true } to update materialized view without loading
|
85
|
+
# data. Defaults to false.
|
80
86
|
# @return The database response from executing the create statement.
|
81
87
|
#
|
82
88
|
# @example
|
@@ -100,7 +106,11 @@ module Scenic
|
|
100
106
|
sql_definition ||= definition(name, version)
|
101
107
|
|
102
108
|
if materialized
|
103
|
-
Scenic.database.update_materialized_view(
|
109
|
+
Scenic.database.update_materialized_view(
|
110
|
+
name,
|
111
|
+
sql_definition,
|
112
|
+
no_data: no_data(materialized),
|
113
|
+
)
|
104
114
|
else
|
105
115
|
Scenic.database.update_view(name, sql_definition)
|
106
116
|
end
|
@@ -141,5 +151,13 @@ module Scenic
|
|
141
151
|
def definition(name, version)
|
142
152
|
Scenic::Definition.new(name, version).to_sql
|
143
153
|
end
|
154
|
+
|
155
|
+
def no_data(materialized)
|
156
|
+
if materialized.is_a?(Hash)
|
157
|
+
materialized.fetch(:no_data, false)
|
158
|
+
else
|
159
|
+
false
|
160
|
+
end
|
161
|
+
end
|
144
162
|
end
|
145
163
|
end
|
data/lib/scenic/version.rb
CHANGED
data/lib/scenic/view.rb
CHANGED
@@ -45,10 +45,9 @@ module Scenic
|
|
45
45
|
materialized_option = materialized ? "materialized: true, " : ""
|
46
46
|
|
47
47
|
<<-DEFINITION
|
48
|
-
create_view #{name.inspect}, #{materialized_option}
|
48
|
+
create_view #{name.inspect}, #{materialized_option}sql_definition: <<-\SQL
|
49
49
|
#{definition.indent(2)}
|
50
50
|
SQL
|
51
|
-
|
52
51
|
DEFINITION
|
53
52
|
end
|
54
53
|
end
|
data/scenic.gemspec
CHANGED
@@ -1,38 +1,37 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require "scenic/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = "scenic"
|
8
7
|
spec.version = Scenic::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
8
|
+
spec.authors = ["Derek Prior", "Caleb Thompson"]
|
9
|
+
spec.email = ["derekprior@gmail.com", "caleb@calebthompson.io"]
|
10
|
+
spec.summary = "Support for database views in Rails migrations"
|
12
11
|
spec.description = <<-DESCRIPTION
|
13
12
|
Adds methods to ActiveRecord::Migration to create and manage database views
|
14
13
|
in Rails
|
15
14
|
DESCRIPTION
|
16
|
-
spec.homepage =
|
17
|
-
spec.license =
|
15
|
+
spec.homepage = "https://github.com/scenic-views/scenic"
|
16
|
+
spec.license = "MIT"
|
18
17
|
|
19
18
|
spec.files = `git ls-files -z`.split("\x0")
|
20
19
|
spec.test_files = spec.files.grep(%r{^spec/})
|
21
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ["lib"]
|
22
21
|
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
22
|
+
spec.add_development_dependency "appraisal"
|
23
|
+
spec.add_development_dependency "bundler", ">= 1.5"
|
24
|
+
spec.add_development_dependency "database_cleaner"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec", ">= 3.3"
|
27
|
+
spec.add_development_dependency "pg", "~> 0.19"
|
28
|
+
spec.add_development_dependency "pry"
|
29
|
+
spec.add_development_dependency "ammeter", ">= 1.1.3"
|
30
|
+
spec.add_development_dependency "yard"
|
31
|
+
spec.add_development_dependency "redcarpet"
|
33
32
|
|
34
|
-
spec.add_dependency
|
35
|
-
spec.add_dependency
|
33
|
+
spec.add_dependency "activerecord", ">= 4.0.0"
|
34
|
+
spec.add_dependency "railties", ">= 4.0.0"
|
36
35
|
|
37
|
-
spec.required_ruby_version =
|
36
|
+
spec.required_ruby_version = ">= 2.3.0"
|
38
37
|
end
|