how to generate a migration with custom generator - ruby-on-rails

this is my generator class in lib/genrators/form
class FormGenerator < Rails::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"
end
end
and this is output :
rails g form test name:string
create app/models/test.rb
create app/controllers/tests_controller.rb
create app/javascript/api/test.js
create app/javascript/component/tests.vue
create app/javascript/pages/test/index.vue
create app/javascript/pages/test/layout.vue
create app/javascript/store/actions/test.js
create app/javascript/store/getters/test.js
create app/javascript/store/modules/test.js
create app/javascript/store/mutations/test.js
i wanna this generator , generates a migration file in db/migrate
i have tried these things and it didnt work out :
class FormGenerator < Rails::Generators::Base
include Rails::Generators::Migration
include ActiveRecord::Generators::Base
migration_template

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.

Using partials when building custom generators

I am building custom generators to generate rails models. The generator can use one of many templates to build one model. Some logic is the same in all templates.
Is it possible to extract some parts of the templates into partials?
Here's an example of what I want to do:
lib/generators/custom_model/custom_model_generator.rb
class CustomModelGenerator < Rails::Generators::Base
source_root File.expand_path('../templates', __FILE__)
argument :model_name, type: :string
argument :model_type, type: :string
...
include GeneratorsHelper
def generate_model
template_path =
case model_type
when 'car' then 'car_model.rb.erb'
when 'plane' then 'plane_model.rb.erb'
...
end
template template_path, "app/models/#{model_name}.rb"
end
end
Here is one template:
lib/generators/custom_model/templates/car_model.rb.erb
class <%= model_name.camelcase %> < ApplicationRecord
def start
puts "Vroum!"
end
end
The #start method will also be used in the other model generators. I would like to extract it it something like a partial. Is that possible?

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.

how to create your own rails generator without needing an argument?

It seems that I am stuck figuring out so that my generator doesn't need an argument. So for instance my generator code is this:
class MyGenerator < Rails::Generators::NamedBase
source_root File.expand_path('../templates', __FILE__)
def generate_stylesheet
copy_file "my.css", "public/stylesheets/my.css"
end
end
But when I do rails g my rails always asks for an extra argument. Can you show me how so it doesn't need an extra argument?
Thanks.
You have to use class MyGenerator < Rails::Generators::Base instead of class MyGenerator < Rails::Generators::NamedBase

generators and migrations in plugins (rails 3)

I am simply trying to create a plugin migration generator without any parameters, like : $rails generate yaffle and this should copy the migration file (lib/generators/yaffle/template/create_yaffle.rb) to db/migrate/[timestamp]_create_yaffle.rb.
The problem I am facing here is, its copying, but without timestamp.
Also, when I run $rails generate yaffle it gives me a message that arguments are not provided, it expects to be in this format rails generate yaffle NAME [options]. I dont want to have any options/arguments, it should just be rails generate yaffle.
What should I do?
I followed the generator used in acts_as_commentable , it looks pretty simple, but I don't know where to modify these settings... can anybody help?
Generator Code:
require 'rails/generators'
require 'rails/generators/migration'
class ThumbitGenerator Rails::Generators::NamedBase
source_root File.expand_path('../templates', __FILE__)
def self.next_migration_number(path)
Time.now.utc.strftime("%Y%m%d%H%M%S")
end
def create_model_file
template "like.rb", "app/models/like.rb"
template "liking.rb", "app/models/liking.rb"
template "create_likes.rb", "db/migrate/create_likes.rb"
template "create_likings.rb", "db/migrate/create_likings.rb"
end
end
Ok, I found the answer...
I was using Rails::Generators::NamedBase instead of Rails::Generators::Base in my generator file! When you use NamedBase, it always expects an argument to be passed (which is the name of initializer) Explanation : guides.rubyonrails.org/generators
And I was using template method instead of migration_template because of which migration files din't produce any migration number Explanation: Rails::Generators::Migration.migration_template
So finally, this worked!
require 'rails/generators'
require 'rails/generators/migration'
class ThumbitGenerator < Rails::Generators::Base
include Rails::Generators::Migration
source_root File.expand_path('../templates', __FILE__)
def self.next_migration_number(path)
Time.now.utc.strftime("%Y%m%d%H%M%S")
end
def create_model_file
template "like.rb", "app/models/like.rb"
template "liking.rb", "app/models/liking.rb"
migration_template "create_likes.rb", "db/migrate/create_likes.rb"
migration_template "create_likings.rb", "db/migrate/create_likings.rb"
end
end
A small polish on the solution - to save yourself the hassle of defining the timestamp for the migration and future proof your generator in case Rails core team decides to use another way of stamping (e.g. SHA hashes truncated to 10 characters), you can require 'rails/generators/active_record' and extend ActiveRecord::Generators::Migration like this:
require 'rails/generators'
require 'rails/generators/migration'
require 'rails/generators/active_record'
class ThumbitGenerator < Rails::Generators::Base
include Rails::Generators::Migration
extend ActiveRecord::Generators::Migration
source_root File.expand_path('../templates', __FILE__)
def create_model_file
template "like.rb", "app/models/like.rb"
template "liking.rb", "app/models/liking.rb"
migration_template "create_likes.rb", "db/migrate/create_likes.rb"
migration_template "create_likings.rb", "db/migrate/create_likings.rb"
end
end
UPDATE In Rails 4 ActiveRecord::Generators::Migration is no longer a module, so use instead:
require 'rails/generators'
require 'rails/generators/migration'
require 'rails/generators/active_record'
class ThumbitGenerator < Rails::Generators::Base
include Rails::Generators::Migration
# Implement the required interface for Rails::Generators::Migration
def self.next_migration_number(dirname)
ActiveRecord::Generators::Base.next_migration_number(dirname)
end
source_root File.expand_path('../templates', __FILE__)
def create_model_file
template "like.rb", "app/models/like.rb"
template "liking.rb", "app/models/liking.rb"
migration_template "create_likes.rb", "db/migrate/create_likes.rb"
migration_template "create_likings.rb", "db/migrate/create_likings.rb"
end
end
you can simply inherit from ActiveRecord::Generators::Base and everything will work

Resources