scenic 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.hound.yml +2 -4
  3. data/.rubocop.yml +129 -0
  4. data/.travis.yml +11 -17
  5. data/Appraisals +14 -21
  6. data/CODE_OF_CONDUCT.md +76 -0
  7. data/CONTRIBUTING.md +3 -7
  8. data/Gemfile +1 -1
  9. data/NEWS.md +32 -13
  10. data/README.md +15 -16
  11. data/Rakefile +2 -2
  12. data/gemfiles/rails42.gemfile +1 -1
  13. data/gemfiles/rails50.gemfile +1 -1
  14. data/gemfiles/rails51.gemfile +1 -1
  15. data/gemfiles/rails52.gemfile +8 -0
  16. data/gemfiles/rails_edge.gemfile +3 -3
  17. data/lib/generators/scenic/materializable.rb +9 -0
  18. data/lib/generators/scenic/model/model_generator.rb +2 -2
  19. data/lib/generators/scenic/view/USAGE +1 -0
  20. data/lib/generators/scenic/view/templates/db/migrate/create_view.erb +1 -1
  21. data/lib/generators/scenic/view/templates/db/migrate/update_view.erb +1 -1
  22. data/lib/generators/scenic/view/view_generator.rb +13 -5
  23. data/lib/scenic/adapters/postgres.rb +14 -4
  24. data/lib/scenic/adapters/postgres/refresh_dependencies.rb +12 -2
  25. data/lib/scenic/adapters/postgres/views.rb +10 -1
  26. data/lib/scenic/schema_dumper.rb +2 -2
  27. data/lib/scenic/statements.rb +24 -6
  28. data/lib/scenic/version.rb +1 -1
  29. data/lib/scenic/view.rb +1 -2
  30. data/scenic.gemspec +22 -23
  31. data/spec/acceptance/user_manages_views_spec.rb +2 -1
  32. data/spec/dummy/app/models/application_record.rb +5 -0
  33. data/spec/generators/scenic/model/model_generator_spec.rb +1 -1
  34. data/spec/generators/scenic/view/view_generator_spec.rb +1 -1
  35. data/spec/scenic/adapters/postgres/refresh_dependencies_spec.rb +60 -26
  36. data/spec/scenic/adapters/postgres_spec.rb +2 -2
  37. data/spec/scenic/definition_spec.rb +1 -1
  38. data/spec/scenic/schema_dumper_spec.rb +17 -2
  39. data/spec/scenic/statements_spec.rb +48 -13
  40. data/spec/spec_helper.rb +1 -1
  41. data/spec/support/generator_spec_setup.rb +1 -1
  42. metadata +15 -13
  43. data/gemfiles/rails40.gemfile +0 -8
  44. data/gemfiles/rails41.gemfile +0 -8
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Scenic
2
2
 
3
- ![Scenic Landscape](https://images.thoughtbot.com/announcing-scenic--versioned-database-views-for-rails/MRUcPsxrTGCeWKyE59Zg_landscape.png)
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/thoughtbot/scenic.svg)](https://travis-ci.org/thoughtbot/scenic)
6
- [![Code Climate](https://codeclimate.com/repos/53c9736269568066a3000c35/badges/85aa9b19f3037252c55d/gpa.svg)](https://codeclimate.com/repos/53c9736269568066a3000c35/feed)
7
- [![Documentation Quality](http://inch-ci.org/github/thoughtbot/scenic.svg?branch=master)](http://inch-ci.org/github/thoughtbot/scenic)
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] and [Caleb Thompson], funded by
255
- thoughtbot, inc. The names and logos for thoughtbot are trademarks of
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
@@ -1,5 +1,5 @@
1
- require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
@@ -5,4 +5,4 @@ source "https://rubygems.org"
5
5
  gem "activerecord", "~> 4.2.0"
6
6
  gem "railties", "~> 4.2.0"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
@@ -5,4 +5,4 @@ source "https://rubygems.org"
5
5
  gem "activerecord", "~> 5.0.0"
6
6
  gem "railties", "~> 5.0.0"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
@@ -5,4 +5,4 @@ source "https://rubygems.org"
5
5
  gem "activerecord", "~> 5.1.0"
6
6
  gem "railties", "~> 5.1.0"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.2.0"
6
+ gem "railties", "~> 5.2.0"
7
+
8
+ gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", :git => "https://github.com/rails/rails"
6
- gem "arel", :git => "https://github.com/rails/arel"
5
+ gem "rails", git: "https://github.com/rails/rails"
6
+ gem "arel", git: "https://github.com/rails/arel"
7
7
 
8
- gemspec :path => "../"
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("../templates", __FILE__)
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,
@@ -6,6 +6,7 @@ Description:
6
6
  and a migration to replace the old version with the new.
7
7
 
8
8
  To create a materialized view, pass the '--materialized' option.
9
+ To create a materialized view with NO DATA, pass '--no-data' option.
9
10
 
10
11
  Examples:
11
12
  rails generate scenic:view searches
@@ -1,5 +1,5 @@
1
1
  class <%= migration_class_name %> < <%= activerecord_migration_class %>
2
2
  def change
3
- create_view <%= formatted_plural_name %><%= ", materialized: true" if materialized? %>
3
+ create_view <%= formatted_plural_name %><%= create_view_options %>
4
4
  end
5
5
  end
@@ -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("../templates", __FILE__)
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.gsub('.', '').pluralize}"
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(*%w(db views))
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 == 0
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.gsub(".", "_")
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 "CREATE MATERIALIZED VIEW #{quote_table_name(name)} AS #{sql_definition};"
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
- dep.include?(view_to_refresh.to_s)
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
- PGconn.quote_ident(name)
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
@@ -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; remove_prefix_and_suffix(table_name) == ignored
36
- when Regexp; remove_prefix_and_suffix(table_name) =~ ignored
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
@@ -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 view.
13
- # Defaults to false.
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(name, sql_definition)
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
- # Defaults to false.
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(name, sql_definition)
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
@@ -1,3 +1,3 @@
1
1
  module Scenic
2
- VERSION = "1.4.1".freeze
2
+ VERSION = "1.5.0".freeze
3
3
  end
@@ -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} sql_definition: <<-\SQL
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
@@ -1,38 +1,37 @@
1
- # coding: utf-8
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 'scenic/version'
3
+ require "scenic/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = 'scenic'
6
+ spec.name = "scenic"
8
7
  spec.version = Scenic::VERSION
9
- spec.authors = ['Derek Prior', 'Caleb Thompson']
10
- spec.email = ['derekprior@gmail.com', 'caleb@calebthompson.io']
11
- spec.summary = %q{Support for database views in Rails migrations}
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 = 'https://github.com/thoughtbot/scenic'
17
- spec.license = 'MIT'
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 = ['lib']
20
+ spec.require_paths = ["lib"]
22
21
 
23
- spec.add_development_dependency 'appraisal'
24
- spec.add_development_dependency 'bundler', '>= 1.5'
25
- spec.add_development_dependency 'database_cleaner'
26
- spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'rspec', '>= 3.3'
28
- spec.add_development_dependency 'pg'
29
- spec.add_development_dependency 'pry'
30
- spec.add_development_dependency 'ammeter', '>= 1.1.3'
31
- spec.add_development_dependency 'yard'
32
- spec.add_development_dependency 'redcarpet'
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 'activerecord', '>= 4.0.0'
35
- spec.add_dependency 'railties', '>= 4.0.0'
33
+ spec.add_dependency "activerecord", ">= 4.0.0"
34
+ spec.add_dependency "railties", ">= 4.0.0"
36
35
 
37
- spec.required_ruby_version = '~> 2.1'
36
+ spec.required_ruby_version = ">= 2.3.0"
38
37
  end