Add custom generator in addition to default rails generators - ruby-on-rails

What I need is to add some translations to i18n on scaffold generating. I'd like to know: is there possibility to force rails g scaffold to invoke my own generator in addition to defaults?
If not, how can I invoke default Rails generators (e.g. active_record) in my generator?
Thank you!

According to the doc here, you can use generate from your generator to invoke another generator.
generate a_generator_name, args_as_string
class FooGenerator < Rails::Generators::Base
argument :attributes, :type => :array
def call
do_your_stuff
run_scaffold
end
private
def do_your_stuff
end
def run_scaffold
generate 'scaffold', attributes.join(' ')
end
end

Related

using ActiveRecord argument in custom generator

i wanna this custom generator named form , generate a model and create its migration file in db/migrate
this is output :
rails g form test name:string
create app/models/name:string.rb
create app/controllers/name:strings_controller.rb
create app/javascript/api/name:string.js
create app/javascript/component/name:strings.vue
create app/javascript/pages/name:string/index.vue
create app/javascript/pages/name:string/layout.vue
create app/javascript/store/actions/name:string.js
create app/javascript/store/getters/name:string.js
create app/javascript/store/modules/name:string.js
create app/javascript/store/mutations/name:string.js
create db/migrate/20200816162324_create_name:strings.rb
and this is my custom generator class :
require 'rails/generators/active_record'
class FormGenerator < ActiveRecord::Generators::Base
source_root File.expand_path('templates', __FILE__)
source_root File.expand_path('templates', __dir__)
argument :model, type: :string
def create_template
template "models/form.template", "app/models/#{model}.rb"
template "controllers/forms_controller.template", "app/controllers/#{model}s_controller.rb"
template "javascript/api/form.template", "app/javascript/api/#{model}.js"
template "javascript/pages/component/forms.template", "app/javascript/component/#{model}s.vue"
template "javascript/pages/form/index.template", "app/javascript/pages/#{model}/index.vue"
template "javascript/pages/form/layout.template", "app/javascript/pages/#{model}/layout.vue"
template "javascript/store/actions/form.template", "app/javascript/store/actions/#{model}.js"
template "javascript/store/getters/form.template", "app/javascript/store/getters/#{model}.js"
template "javascript/store/modules/form.template", "app/javascript/store/modules/#{model}.js"
template "javascript/store/mutations/form.template", "app/javascript/store/mutations/#{model}.js"
migration_template "create_forms.template", "db/migrate/create_#{model}s.rb"
end
as you can see generator uses the column that i gave it to model instead of model itself
template "models/form.template", "app/models/#{model}.rb" //code
rails g form test name:string
create app/models/name:string.rb //output
what should i do to fix that?
From Ruby on Rails Guide, if you want to add custom command line arguments to a custom generator you have to declare it as a class_option. In this case your custom arguments are an array of columns. For example:
class FormGenerator < ActiveRecord::Generators::Base
...
class_option :columns, type: :array, default: [] # Options :type and :default are not required
def create_template
#columns = options[:columns] # You can use this variable inside template
...
end
end
For more information about class_option method click here.

How Do I Create A Custom Rails Generate Model function?

I have a Rails 5 application that has separate migrations for three databases. I'm adding a new database. I normally do a rails g model field1:type1 field2:type2 field3:type.... to create my database tables with the desired fields. I want to create generate model statements where it will generate the model for the desired database and put it in the corresponding db/migrate folder.
I have custom database migration generators for the additional databases in lib/generators. Here is an example of the custom migration generator.
lib/generators/stats_migration_generator.rb
require 'rails/generators/active_record/migration/migration_generator'
class StatsMigrationGenerator < ActiveRecord::Generators::MigrationGenerator
source_root File.join(File.dirname(ActiveRecord::Generators::MigrationGenerator.instance_method(:create_migration_file).source_location.first), "templates")
def create_migration_file
set_local_assigns!
validate_file_name!
migration_template #migration_template, "db_stats/migrate/#{file_name}.rb"
end
end
I assume I need to create lib/generators/mydb_model_generator.rb or whatever the correct filename structure is for each additional database. I'm thinking I could do something like this after looking at the model_generator.rb file in GitHub.
lib/generators/stats_model_generator.rb
require 'rails/generators/active_record/model/model_generator'
class StatsModelGenerator < ActiveRecord::Generators::ModelGenerator
source_root File.join(File.dirname(ActiveRecord::Generators::ModelGenerator.instance_method(:create_migration_file).source_location.first), "templates")
def create_migration_file
set_local_assigns!
validate_file_name!
migration_template #migration_template, "db_stats/migrate/#{file_name}.rb"
end
end
Here are my questions after looking at the git repository for Rails, the Rails Guide regarding creating and customizing Generators and the Ruby on Rails API documentation:
How do I find what I need to require?
How do I find what method I need to override the db/migrate folder name?
How do I find out how to name the rb file for the model generator?
Are my assumptions correct in my attempt to create the model generator?
Well I did not think I would find the solution so quickly. I took another look at model_generator.rb in GitHub and had an aha moment. I realized that I just needed to replace the method in the default generator with my version which points to the db migrate folder I want. Here is the solution I came up with.
require 'rails/generators/active_record/model/model_generator'
class MydbModelGenerator < ActiveRecord::Generators::ModelGenerator
source_root File.join(File.dirname(ActiveRecord::Generators::ModelGenerator.instance_method(:create_migration_file).source_location.first), "templates")
def create_migration_file
return unless options[:migration] && options[:parent].nil?
attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false
migration_template "../../migration/templates/create_table_migration.rb", File.join("db_mydb/migrate", "create_#{table_name}.rb")
end
end
Now I can do rails g mydb_model field1:type1 field2:type2 field3:type and it creates the model migration file in the correct migration folder.

Command to create a rake task with some folder

rails g task folder1/namespace1 task1
Above command will create the task1.rake inside lib/tasks/task1.rake
But, I need to keep my task1.rake inside lib/tasks/folder1/task1.rake
The rails g task generator does not work this way.
Here are the code for the TaskGenerator class
module Rails
module Generators
class TaskGenerator < NamedBase # :nodoc:
argument :actions, type: :array, default: [], banner: "action action"
def create_task_files
template 'task.rb', File.join('lib/tasks', "#{file_name}.rake")
end
end
end
end
As you see the lib/tasks path is hardcoded and you can not pass in options to alter the path.
I think this might be a great addition to the TaskGenerator class.
The answer to your question is that you have to make the folders manually.

Add new migrations from Rails engine gem to app via generator

I'm building a Rails engine in a ruby gem. It includes some migrations right now that are called when you run:
rails g myengine:install
The code in the generator is as follows:
module MyEngine
module Generators
class InstallGenerator < ::Rails::Generators::Base
include Rails::Generators::Migration
source_root File.expand_path('../templates', __FILE__)
# ...
def copy_migrations
migration_template "migrations/migration1.rb", "db/migrate/migration1.rb"
migration_template "migrations/migration2.rb", "db/migrate/migration2.rb"
end
# ...
end
end
end
However, if I run rails g myengine:install again, it fails with this error:
Another migration is already named migration1: /Users/jh/Code/Web/demoapp/db/migrate/20130327222221_migration1.rb
I want it to just silently ignore the fact that there's already a migration and continue on to the next migration. What would be the best way to do this?
EDIT:
Per Dmitry's answer, this was my solution:
def copy_migrations
copy_migration "migration1"
copy_migration "migration2"
end
protected
def copy_migration(filename)
if self.class.migration_exists?("db/migrate", "#{filename}")
say_status("skipped", "Migration #{filename}.rb already exists")
else
migration_template "migrations/#{filename}.rb", "db/migrate/#{filename}.rb"
end
end
Using migration_template in Rails as example, you could perhaps check for destination = self.class.migration_exists?(migration_dir, #migration_file_name) and if migration already exists, skip over making migration_template call.

Rails: how to extend a generator?

I'm trying to extend the model generator in Rails ( rails g model ). Basically my generator should do the same thing as the model one, but copy 2 additional files. Simple as that.
I reviewed Railscast #218 ( http://railscasts.com/episodes/218-making-generators-in-rails-3 ) which was very informative but i couldn't find any info about extending generators.
Checking the source code of rails, it looks like the model generator is in lib/rails/generators/rails/model/model_generator.rb defined as Rails::Generators::ModelGenerator.
I tried to make my generator extend this class but it results in:
Error: uninitialized constant Rails::Generators::ModelGenerator.
And my attempts to require this file were not successful.
So I decided to stop and ask here. What is the proper way of extending a generator?
Take a look at hooks and invoke.
class MyGenerator < Rails::Generators::Base
def create_my_file
# Do your generators stuff
create_file "config/initializers/my.rb", "# Add content here"
# Create model
invoke("model", ["model_name", "arg1", "arg2"])
end
end
Hope this help.
Generate your custom generator:
rails generate generator my_model
Open lib/generators/my_model/my_model_generator.rb and change it to:
require 'rails/generators/active_record/model/model_generator'
class MyModelGenerator < ActiveRecord::Generators::ModelGenerator
source_root File.expand_path('../templates', __FILE__)
end
This works for rails engines. Don't forget to add required templates.

Resources