ilyakatz / data-migrate

Migrate and update data alongside your database structure.
MIT License
1.4k stars 192 forks source link

Rake tasks are missing when installed in Rails via another gem #149

Open wscourge opened 3 years ago

wscourge commented 3 years ago

Problem

I have a gem with the following code:

# whatever/whatever.gemspec
Gem::Specification.new do |spec|
  # ...
  # ...
  spec.files = Dir['{app,lib}/**/*', 'CHANGELOG.md', 'LICENSE.md', 'README.md']
  spec.add_dependency('rails')
  spec.add_dependency('data_migrate')
  # ...
end

After bundling it into my brand-new Rails app:

# brand_new_rails_app/Gemfile
# ...
# ...
gem 'whatever', path: 'path/to/whatever'
# ...
# ...

and running rails -T in the command line, I do not see the rake tasks available via the data_migrate gem. Running rails data:migrate produces the following:

Don't know how to build task 'data:migrate' (See the list of available tasks with rails --tasks)

Additionally, what seems weird and makes me think that this is rake-tasks specific, the data_migrate generator is available - running rails g --help lists it as follows:

# ...
DataMigration:
  data_migration
# ...

Question

Any of you guys have an idea what am I doing wrong? The problem is obviously on my side, cause when 'data_migrate' is added directly to the brand new rails app, the tasks are available.

Related SO questions

I went through all of the stuff below with no avail. As you can see:

  1. I use the add_dependency, not the add_development_dependency
  2. I install my 'whatever' gem outside any of the groups, so it is available for every Rails.env
  3. I include files using /**/* wildcard (without extension Dir['{app,lib}/**/*']) - I don't think it is actually relevant, as tasks come from the data_migrate gem dependency, not my gem.

    Including rake tasks in gems Why are db tasks missing from rake? Rake tasks inside gem not being found Why is this gem not adding rake tasks to a rails app?

I'll appreciate any help, thanks in advance.

wscourge commented 3 years ago

I have also tried using add_runtime_dependency with no avail

kaka-ruto commented 1 year ago

This works for me when I add the gem "data_migrate" into the gem's Gemfile and not the .gemspec

However, the task names are preceded by an app namespace

rake -T data
rake app:data:abort_if_pending_migrations          # Raises an error if there are pending data migrations
rake app:data:dump                                 # Create a db/data_schema.rb file that stores the current data version
rake app:data:forward                              # Pushes the schema to the next version (specify steps w/ STEP=n)
rake app:data:migrate                              # Migrate data migrations (options: VERSION=x, VERBOSE=false)
rake app:data:migrate:down                         # Runs the "down" for a given migration VERSION
rake app:data:migrate:redo                         # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x)
rake app:data:migrate:status                       # Display status of data migrations
rake app:data:migrate:up                           # Runs the "up" for a given migration VERSION
rake app:data:rollback                             # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake app:data:schema:load                          # Load data_schema.rb file into the database
rake app:data:version                              # Retrieves the current schema version number for data migrations
rake app:db:abort_if_pending_migrations:with_data  # Raises an error if there are pending migrations or data migrations
rake app:db:forward:with_data                      # Pushes the schema to the next version (specify steps w/ STEP=n)
rake app:db:migrate:down:with_data                 # Runs the "down" for a given migration VERSION
rake app:db:migrate:redo:with_data                 # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x)
rake app:db:migrate:status:with_data               # Display status of data and schema migrations
rake app:db:migrate:up:with_data                   # Runs the "up" for a given migration VERSION
rake app:db:migrate:with_data                      # Migrate the database data and schema (options: VERSION=x, VERBOSE=false)
rake app:db:rollback:with_data                     # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake app:db:schema:load:with_data                  # Load both schema.rb and data_schema.rb file into the database
rake app:db:structure:load:with_data               # Load both structure.sql and data_schema.rb file into the database
rake app:db:version:with_data                      # Retrieves the current schema version numbers for data and schema migrations
jeffktan commented 4 months ago

In the Rakefile, I added the following to the bottom to support having rake db:migrate run rake db:migrate:with_data from the root gem directory. (Don't forget to setup your Rakefile similar to this https://github.com/twinge/questionnaire_engine/blob/engine2/Rakefile)

# Adds data_migrate rake tasks to root
spec = Gem::Specification.find_by_name 'data_migrate'
load "#{spec.gem_dir}/tasks/databases.rake"
namespace :db do
  # Adds extra alias to support data_migrate tasks ran from root
  task :_dump => ["app:db:_dump"]
  # Create alias to run data migrations along with the normal db migrations
  task :migrate => ["db:migrate:with_data"]
end

In the engine.rb file, don't forget to configure the db and data configurations:

    # include migrations in enclosing app
    # https://tanzu.vmware.com/content/blog/leave-your-migrations-in-your-rails-engines
    initializer :append_migrations do |app|
      unless app.root.to_s.match root.to_s
        config.paths['db/migrate'].expanded.each do |expanded_path|
          app.config.paths['db/migrate'] << expanded_path
        end
      end
    end

    # include data migrations in parent app
    initializer :data_migrations do |app|
      ::DataMigrate.configure do |data_migrate|
        default_path = ::DataMigrate::Config.new.data_migrations_path
        data_migrate.data_migrations_path = [root.join('db', 'data')]
      end
    end

In the parent app, I had to run the rake db:load_config task before running rake db:migrate:with_data as it was missing it so it didn't pick up the gem's db/migrate files. (Maybe someone can add a PR to make sure rake db:migrate:with_data runs the load_config task like how Rails does it with rake db:migrate) I added the following to the Rakefile in the parent app so that rake db:migrate would run all the db and data migrations in the parent app as well as the gem.

namespace :db do
  task :migrate => ["db:load_config", "db:migrate:with_data"]
end