Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename view #320

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ changelog, see the [commits] for each version via the version links.

[commits]: https://github.com/scenic-views/scenic/commits/master

## next

### Added

- `rename_view` allow to rename a view. Generators have been updated to
accept a `--rename` option.

## [1.5.4] - September 16, 2020

[1.5.4]: https://github.com/scenic-views/scenic/compare/v1.5.3...v1.5.4
Expand Down
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,68 @@ you would need to refresh view B first, then right after refresh view A. If you
would like this cascading refresh of materialized views, set `cascade: true`
when you refresh your materialized view.

## I made a mistake and I need to rename a view?

Run the view generator as following:

```sh
$ rails generate scenic:view results --rename search_results
create db/views/results_v01.sql
create db/migrate/[TIMESTAMP]_update_results_to_version_2.rb
```

Now, `db/views/results_v01.sql` sould be identical to
`db/views/search_results_v01.sql`, you will need to edit this file if you rename
another views in this migration that is in this definition, so just rename it
too accordingly ;-)
The migration should look something like this:

```ruby
class UpdateResultsToVersion2 < ActiveRecord::Migration
def change
rename_view :search_results, :results,
version: 2,
revert_to_version: 1
end
end
```

## Need to change a materialized view without downtime?

Before using `replace_view` on materialized views, you need to create and
populate the next version of the view in a previous release. So you will have
to follow these steps:

1. Create a new materialized view
`rails generate scenic:view table_name_next --materialized --no-data`
that will take improvement of your future view `table_name`, you can
copy-paste the content of the last version of `table_name` as a starter.
2. Deploy and apply this migration
3. Refresh the view within a task or in the Rails console
`Scenic.database.refresh_materialized_view(:table_name_nexts, concurrently: false)`
4. Use that view by removing the previous and renaming the next one in a single
migration
`rails generate scenic:view table_name --materialized --rename table_name_next`
and edit the migration to change `rename_view` by `replace_view`:
```ruby
def change
replace_view :table_name_nexts, :table_names,
version: 1,
revert_to_version: 1,
materialized: true
end
```

`replace_view` will internaly do:
1. store indexes of `table_names`
2. remove `table_names`
3. rename `table_names_next` to `table_names`
4. ensure that the definition on the database is the same as the one in
`db/views` folder
5. rename indexes by replacing `table_names_next` by `table_names` in
their names
6. applied valid stored indexes on the new version of `table_names`

## I don't need this view anymore. Make it go away.

Scenic gives you `drop_view` too:
Expand Down
1 change: 1 addition & 0 deletions lib/generators/scenic/view/USAGE
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Description:

To create a materialized view, pass the '--materialized' option.
To create a materialized view with NO DATA, pass '--no-data' option.
To rename a view, pass '--rename actual_view_name' option.

Examples:
rails generate scenic:view searches
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class <%= migration_class_name %> < <%= activerecord_migration_class %>
def change
create_view <%= formatted_plural_name %><%= create_view_options %>
create_view <%= format_view_name(plural_name) %><%= create_view_options %>
end
end
12 changes: 7 additions & 5 deletions lib/generators/scenic/view/templates/db/migrate/update_view.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
class <%= migration_class_name %> < <%= activerecord_migration_class %>
def change
<%- if renaming? -%>
rename_view <%= format_view_name(previous_plural_name) %>, <%= format_view_name(plural_name) %>,
<%- else -%>
update_view <%= format_view_name(plural_name) %>,
<%- end -%>
version: <%= definition.version %>,
revert_to_version: <%= previous_definition.version %><%- if materialized? -%>,<%- end -%>
<%- if materialized? -%>
update_view <%= formatted_plural_name %>,
version: <%= version %>,
revert_to_version: <%= previous_version %>,
materialized: <%= no_data? ? "{ no_data: true }" : true %>
<%- else -%>
update_view <%= formatted_plural_name %>, version: <%= version %>, revert_to_version: <%= previous_version %>
<%- end -%>
end
end
77 changes: 48 additions & 29 deletions lib/generators/scenic/view/view_generator.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require "rails/generators"
require "rails/generators/active_record"
require "generators/scenic/materializable"
require "scenic/definition"
require "scenic/definitions"

module Scenic
module Generators
Expand All @@ -10,17 +12,23 @@ class ViewGenerator < Rails::Generators::NamedBase
include Scenic::Generators::Materializable
source_root File.expand_path("templates", __dir__)

class_option :rename,
type: :string,
required: false,
banner: "PREVIOUS_VIEW_NAME",
desc: "rename from previous view name"

def create_views_directory
unless views_directory_path.exist?
empty_directory(views_directory_path)
unless Scenic.configuration.definitions_path.exist?
empty_directory(Scenic.configuration.definitions_path)
end
end

def create_view_definition
if creating_new_view?
create_file definition.path
else
copy_file previous_definition.full_path, definition.full_path
copy_file previous_definition.path, definition.path
end
end

Expand All @@ -31,6 +39,7 @@ def create_migration_file
"db/migrate/create_#{plural_file_name}.rb",
)
else
version = definition.version
migration_template(
"db/migrate/update_view.erb",
"db/migrate/update_#{plural_file_name}_to_version_#{version}.rb",
Expand All @@ -43,22 +52,11 @@ def self.next_migration_number(dir)
end

no_tasks do
def previous_version
@previous_version ||=
Dir.entries(views_directory_path)
.map { |name| version_regex.match(name).try(:[], "version").to_i }
.max
end

def version
@version ||= destroying? ? previous_version : previous_version.next
end

def migration_class_name
if creating_new_view?
"Create#{class_name.tr('.', '').pluralize}"
else
"Update#{class_name.pluralize}ToVersion#{version}"
"Update#{class_name.pluralize}ToVersion#{definition.version}"
end
end

Expand All @@ -79,36 +77,57 @@ def file_name
super.tr(".", "_")
end

def views_directory_path
@views_directory_path ||= Rails.root.join("db", "views")
def previous_file_name
(options[:rename] || singular_name).tr(".", "_")
end

def previous_plural_name
(options[:rename] || singular_name).pluralize
end

def version_regex
/\A#{plural_file_name}_v(?<version>\d+)\.sql\z/
def previous_plural_file_name
previous_file_name.pluralize
end

def definitions
@definitions ||= Scenic::Definitions.new(
plural_file_name,
)
end

def previous_definitions
@previous_definitions ||= Scenic::Definitions.new(
previous_plural_file_name,
)
end

def creating_new_view?
previous_version.zero?
previous_definitions.none?
end

def definition
Scenic::Definition.new(plural_file_name, version)
@definition ||= Scenic::Definition.new(
plural_file_name,
(definitions.max.try(:version) || 0)
.public_send(destroying? ? :itself : :next),
)
end

def previous_definition
Scenic::Definition.new(plural_file_name, previous_version)
@previous_definition ||= previous_definitions.max ||
Scenic::Definition.new(previous_plural_file_name, 0)
end

def destroying?
behavior == :revoke
end

def formatted_plural_name
if plural_name.include?(".")
"\"#{plural_name}\""
else
":#{plural_name}"
end
def renaming?
options[:rename]
end

def format_view_name(name)
name.include?(".") ? "\"#{name}\"" : ":#{name}"
end

def create_view_options
Expand All @@ -120,7 +139,7 @@ def create_view_options
end

def destroying_initial_view?
destroying? && version == 1
destroying? && definition.version == 1
end
end
end
Expand Down
1 change: 0 additions & 1 deletion lib/scenic.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require "scenic/configuration"
require "scenic/adapters/postgres"
require "scenic/command_recorder"
require "scenic/definition"
require "scenic/railtie"
require "scenic/schema_dumper"
require "scenic/statements"
Expand Down
Loading