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

Add rails db:system:change command #34832

Merged
merged 4 commits into from Jan 16, 2019

Conversation

gmcgibbon
Copy link
Member

@gmcgibbon gmcgibbon commented Dec 31, 2018

Part two of #34710.

After studying up commands and generators, I decided to make a command that delegates to a generator (similar to rails new / app generator). This seems to give us the most flexibility with how the command interacts with files, and if the user wants to accept the config overwrites or not.

TODO:

  • Fix three level nested generator lookup
  • Add tests for generator with file assertions
  • Add chagelog entry

@rails-bot rails-bot bot added the railties label Dec 31, 2018
@gmcgibbon gmcgibbon force-pushed the db_system_change_command branch 8 times, most recently from 9342aa9 to 6e19f90 Compare January 2, 2019 06:50
Copy link
Member

@eileencodes eileencodes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. I left 2 minor comments.

when "jdbcsqlite3" then ["activerecord-jdbcsqlite3-adapter", nil]
when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil]
when "jdbc" then ["activerecord-jdbc-adapter", nil]
else [database, nil]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this raise an error if we get to the else? Otherwise I think this will silently fail.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, sqlite3 and ibm_db have a direct adapter name to gem name mapping with no version locking. The value itself should be validated by the #initialize check in ChangeGenerator.


if context
lookups << "#{name}:#{context}"
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do these new lines do? I've always found the base and context naming here confusing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm essentially only adding lookups << "rails:#{base}:#{name}" if a base exists. This is necessary due to the second part of the lookup code:

unless base || context
  unless name.to_s.include?(?:)
    lookups << "#{name}:#{name}"
    lookups << "rails:#{name}"
  end
  lookups << "#{name}"
end

Specifically, lookups << "rails:#{name}" which limits us to looking up single segment generator names in the toplevel rails namespace.

When looking up rails g db:system:change, name is the last segment ("change"), base is the namespace path ("db:system"), and context is nil. I think context is used if name isn't the command name, but I'm not sure why. Maybe we should change base to path?

Copy link
Contributor

@kaspth kaspth Jan 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it's possible for users to call rails g db:system:change? I thought this generator was internal and sufficiently called via it's class start method from the command?

Copy link
Member Author

@gmcgibbon gmcgibbon Jan 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is currently possible, yes. If we don't want it to be possible, and only called via the command, I can remove this patch to #find_by_namespace. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll go ahead and revert the changes to the generator lookup and hide this generator so it can only be called through the command. I'm assuming we would prefer only one way of changing databases.

Copy link
Contributor

@kaspth kaspth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're getting pretty adept at finding your way around the codebase! Lovely to see 💪

module Db
module System
class ChangeCommand < Base # :nodoc:
class_option :to, desc: "The DBMS to switch to."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we generally say DBMS elsewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only once in a comment of activerecord/lib/active_record/connection_adapters/abstract_adapter.rb. I see RDBMS in guides/source/active_record_basics.md and activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb, but I don't think that's much better. I'll change it to database for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try "database type"? It's not brilliant, but maybe an improvement? Or "database platform"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using "database system" would match the command name and reinforce the concept. But it's perhaps not expounding on what a "database system" means, that meaning only comes from seeing MySQL, Postgres and the gang.

Copy link
Member Author

@gmcgibbon gmcgibbon Jan 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree that database system makes the most sense given the command name. Will use that

railties/lib/rails/generators/app_name.rb Outdated Show resolved Hide resolved
railties/lib/rails/generators/app_name.rb Outdated Show resolved Hide resolved

def valid_const?
if /^\d/.match?(app_const)
raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curiosity for another time: since 2.6 allows constants to start with non ascii characters, does that mean we could name a Rails app, say, 666?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kaspth not 666, because it needs to begin with something the unicode database as included in the particular ruby version thinks is an uppercase letter. Just not limited to ascii.

railties/lib/rails/generators/database.rb Outdated Show resolved Hide resolved
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
GEMFILE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will we have to maintain these Gemfiles going forward?

Copy link
Member Author

@gmcgibbon gmcgibbon Jan 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I initially tried to generate all test apps without --skip-gemfile, but then rails will run bundle install and crash because rails 6 isn't released yet. I just noticed we have skip_bundle as an app generator option, so I'll try using that, and using the gem template in generator tests.

assert_match "adapter: postgresql", content
assert_match "database: test_app", content
end
assert_file("Gemfile") do |content|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a newline after output and between the assert_file seems like it would increase readability in these tests.

module Command
module Db
module System
class ChangeCommand < Base # :nodoc:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For another time: seeing this deep module nesting makes me think we should consider adding something akin to Action Mailboxes' routing for commands.

@kaspth
Copy link
Contributor

kaspth commented Jan 2, 2019

Forgot to ask, how are we handling cases where people who may have changed their config/database.yml when calling this command?

@gmcgibbon gmcgibbon changed the title [WIP] Add rails db:system:change command Add rails db:system:change command Jan 2, 2019
@gmcgibbon
Copy link
Member Author

gmcgibbon commented Jan 3, 2019

Forgot to ask, how are we handling cases where people who may have changed their config/database.yml when calling this command?

The generator will detect a configuration file already exists and gives options to overwrite, show a diff, reject the change, or merge the two files together.

Eg.

rails g db:system:change --to mysql
    conflict  config/database.yml
Overwrite /Users/gannon/dummy/config/database.yml? (enter "h" for help) [Ynaqdhm] h
        Y - yes, overwrite
        n - no, do not overwrite
        a - all, overwrite this and all others
        q - quit, abort
        d - diff, show the differences between the old and the new
        h - help, show this help
        m - merge, run merge tool
Overwrite /Users/gannon/dummy/config/database.yml? (enter "h" for help) [Ynaqdhm]

@gmcgibbon gmcgibbon force-pushed the db_system_change_command branch 2 times, most recently from 2131e71 to 88e1d38 Compare January 9, 2019 19:19
Add `rails db:system:change` command for changing databases.

```
bin/rails db:system:change --to=postgresql
   force  config/database.yml
    gsub  Gemfile
```

The change command copies a template `config/database.yml` with
the target database adapter into your app, and replaces your database
gem with the target database gem.
@gmcgibbon
Copy link
Member Author

I've hidden the change generator, reverted the patch to call it with rails g, and removed the static gemfile strings in favour of template evaluation. Let me know if there's anything else this PR needs to get shipped!

@kaspth kaspth merged commit 90536eb into rails:master Jan 16, 2019
@bogdanvlviv
Copy link
Contributor

bogdanvlviv commented Jan 21, 2019

Hey, Thanks for adding this!
Since this PR is merged we should close #34710, and #34732?
Also, I think we should exclude db:change from commands (railties/lib/rails/command.rb file) in order to not show it on rails --help, (we should show db:system:change instead).
Sorry for the too late review.

Edited: Seems like Rails::Command::Db::System::ChangeCommand.printing_commands returns ["db:change"] instead of [db:system:change].

@rafaelfranca
Copy link
Member

cc @gmcgibbon

@gmcgibbon
Copy link
Member Author

Thanks for pointing that out @bogdanvlviv, I'll see what I can do!

@gmcgibbon gmcgibbon deleted the db_system_change_command branch January 23, 2019 20:20
kamipo added a commit to kamipo/rails that referenced this pull request Jun 13, 2019
We sometimes say "✂️ newline after `private`" in a code review (e.g.
rails#18546 (comment),
rails#34832 (comment)).

Now `Layout/EmptyLinesAroundAccessModifier` cop have new enforced style
`EnforcedStyle: only_before` (rubocop/rubocop#7059).

That cop and enforced style will reduce the our code review cost.
suketa added a commit to suketa/rails_sandbox that referenced this pull request Jul 20, 2019
Add rails db:system:change command
rails/rails#34832
@simi
Copy link
Contributor

simi commented Dec 28, 2019

Implementing this as a command makes it inconsistent at few places:

  1. bin/rails db:system:change --help shows wrong usage (there is just change instead of db:system:change)
[retro@retro  test-app (master #%)]❤ bin/rails db:system:change --help
Usage:
  rails change [options]

Options:
  [--to=TO]  # The database system to switch to.

  1. this command is not present for bin/rake -T, but all other db:* commands are
  2. this command is not present for bin/rails --tasks, but all other db:* commands are

But it is present for rails command.

[retro@retro  test-app (master #%)]❤ bin/rails
The most common rails commands are:
 generate     Generate new code (short-cut alias: "g")
 console      Start the Rails console (short-cut alias: "c")
 server       Start the Rails server (short-cut alias: "s")
 test         Run tests except system tests (short-cut alias: "t")
 test:system  Run system tests
 dbconsole    Start a console for the database specified in config/database.yml
              (short-cut alias: "db")

 new          Create a new Rails application. "rails new my_app" creates a
              new application called MyApp in "./my_app"


All commands can be run with -h (or --help) for more information.
In addition to those commands, there are:

  about
  action_mailbox:ingress:exim
  action_mailbox:ingress:postfix
  action_mailbox:ingress:qmail
  action_mailbox:install
  action_text:install
  action_text:install:migrations
  active_storage:install
  app:template
  app:update
  assets:clean[keep]
  assets:clobber
  assets:environment
  assets:precompile
  cache_digests:dependencies
  cache_digests:nested_dependencies
  credentials:diff
  credentials:edit
  credentials:show
  db:create
  db:drop
  db:environment:set
  db:fixtures:load
  db:migrate
  db:migrate:down
  db:migrate:redo
  db:migrate:status
  db:migrate:up
  db:prepare
  db:reset
  db:rollback
  db:schema:cache:clear
  db:schema:cache:dump
  db:schema:dump
  db:schema:load
  db:seed
  db:seed:replant
  db:setup
  db:structure:dump
  db:structure:load
  db:system:change
  db:version
  destroy
  dev:cache
  encrypted:edit
  encrypted:show
  initializers
  log:clear
  middleware
  notes
  restart
  routes
  runner
  secret
  secrets:edit
  secrets:setup
  secrets:show
  stats
  test:db
  time:zones[country_or_offset]
  tmp:clear
  tmp:create
  version
  webpacker
  webpacker:binstubs
  webpacker:check_binstubs
  webpacker:check_node
  webpacker:check_yarn
  webpacker:clean[keep]
  webpacker:clobber
  webpacker:compile
  webpacker:info
  webpacker:install
  webpacker:install:angular
  webpacker:install:coffee
  webpacker:install:elm
  webpacker:install:erb
  webpacker:install:react
  webpacker:install:stimulus
  webpacker:install:svelte
  webpacker:install:typescript
  webpacker:install:vue
  webpacker:verify_install
  webpacker:yarn_install
  yarn:install
  zeitwerk:check

koic pushed a commit to koic/oracle-enhanced that referenced this pull request Apr 15, 2020
We sometimes say "✂️ newline after `private`" in a code review (e.g.
rails/rails#18546 (comment),
rails/rails#34832 (comment)).

Now `Layout/EmptyLinesAroundAccessModifier` cop have new enforced style
`EnforcedStyle: only_before` (rubocop/rubocop#7059).

That cop and enforced style will reduce the our code review cost.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants