scenic 1.4.1 → 1.5.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 +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
|
-

|
|
4
4
|
|
|
5
|
-
[](https://travis-ci.org/scenic-views/scenic)
|
|
6
|
+
[](http://inch-ci.org/github/scenic-views/scenic)
|
|
7
|
+
[](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
|
-

|
|
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
|