Skip to content

Instantly share code, notes, and snippets.

@annikoff
Last active February 22, 2024 14:09
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save annikoff/331f785aa7a207a7945b1eca6eff526b to your computer and use it in GitHub Desktop.
Save annikoff/331f785aa7a207a7945b1eca6eff526b to your computer and use it in GitHub Desktop.
Custom generators

The main generator

# lib/generators/rails/policy/policy_generator.rb

module Rails
  module Generators
    class PolicyGenerator < NamedBase
      source_root File.expand_path('templates', __dir__)

      def copy_policy_file
        template 'policy.erb', File.join("app/policies", class_path, "#{file_name}_policy.rb")
      end

      hook_for :test_framework
    end
  end
end

The generator's template

# lib/generators/rails/policy/templates/policy.erb

class <%= class_name %>Policy
  # Add default methods
end

A hook to invoke the custom generator with scaffolding or with controller's generators

# lib/generators/rails/policy/hooks.rb

require 'rails/generators'
require 'rails/generators/rails/scaffold/scaffold_generator'
require 'rails/generators/rails/controller/controller_generator'

Rails::Generators::ScaffoldGenerator.hook_for :policy, default: true, type: :boolean # invoke with scaffolding generators
Rails::Generators::ControllerGenerator.hook_for :policy, default: true, type: :boolean # invoke with the controllers' generator.

The generator of a spec file

# lib/generators/rspec/policy/policy_generator.rb

module Rspec
  module Generators
    class PolicyGenerator < Rails::Generators::NamedBase
      source_root File.expand_path('templates', __dir__)

      def copy_policy_spec_file
        template 'policy_spec.erb',  File.join("spec/policies", class_path, "#{file_name}_policy_spec.rb")
      end
    end
  end
end

The spec's template

# lib/generators/rspec/policy/templates/policy_spec.erb

require 'spec_helper'

describe <%= class_name %>Policy do
  pending "add some examples to (or delete) #{__FILE__}"
end

Application config to hook up custom generators

# config/application.rb

module YourAppName
  class Application < Rails::Application
    # ...

    config.generators do |g|
      g.test_framework  :rspec
      require './lib/generators/rails/policy/hooks'
    end
  end
end

Generate scaffolding

$ rails g scaffold user
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    rspec
      create      spec/requests/users_spec.rb
      create      spec/views/users/edit.html.erb_spec.rb
      create      spec/views/users/index.html.erb_spec.rb
      create      spec/views/users/new.html.erb_spec.rb
      create      spec/views/users/show.html.erb_spec.rb
      create      spec/routing/users_routing_spec.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      rspec
      create        spec/helpers/users_helper_spec.rb
      invoke    jbuilder
      create      app/views/users/index.json.jbuilder
      create      app/views/users/show.json.jbuilder
      create      app/views/users/_user.json.jbuilder
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/users.scss
      invoke  css
   identical    app/assets/stylesheets/scaffold.css
      invoke  policy
      create    app/policies/user_policy.rb
      invoke    rspec
      create      spec/policies/user_policy_spec.rb

$ cat app/policies/user_policy.rb
class UserPolicy
  # Add default methods
end

$ cat spec/policies/user_policy_spec.rb
require 'spec_helper'

describe UserPolicy do
  pending "add some examples to (or delete) #{__FILE__}"
end

Generate a controller inside a module

$ rails g controller api/project
      create  app/controllers/api/project_controller.rb
      invoke  erb
      create    app/views/api/project
      invoke  helper
      create    app/helpers/api/project_helper.rb
      invoke    rspec
      create      spec/helpers/api/project_helper_spec.rb
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/api/project.scss
      invoke  policy
      create    app/policies/api/project_policy.rb
      invoke    rspec
      create      spec/policies/api/project_policy_spec.rb

$ cat app/policies/api/project_policy.rb
class Api::ProjectPolicy
  # Add default methods
end

$ cat spec/policies/api/project_policy_spec.rb
require 'spec_helper'

describe Api::ProjectPolicy do
  pending "add some examples to (or delete) #{__FILE__}"
end
@james-em
Copy link

Np !

Unsure if this

module ActiveModel
  class Railtie < Rails::Railtie
    generators do |app|
      Rails::Generators.configure! app.config.generators
      Rails::Generators::ControllerGenerator.include CustomPolicyGenerator::ControllerGenerator
      Rails::Generators::ScaffoldControllerGenerator.include CustomPolicyGenerator::ScaffoldControllerGenerator
    end
  end
end

is the best way but outside of that, Rails::Generators::ScaffoldControllerGenerator is not defined and I am forced to use a require. If I use the requires:

require "rails/generators"
require "rails/generators/rails/controller/controller_generator"
require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"

my custom templates for controller are ignored.

@dyeje
Copy link

dyeje commented Jun 18, 2022

Thanks yall. I wrote a blogpost to cover what ended up working for me. I did end up needing the requires, but I wasn't using custom templates so not sure if it breaks that like you mentioned.

Adding a Custom Generator to Rails Scaffold

@annikoff
Copy link
Author

@dyeje That's cool. A lot of attention to this gist :)

@dyeje
Copy link

dyeje commented Jun 20, 2022

I assumed it would be as simple as plugging in a config, so I was surprised when this turned into a multi-hour adventure.

@tbrammar
Copy link

@james-em I'm not too sure that your controller scaffold templates are supposed to end with .tt 🤔

Have you tried simply ending them with .rb and seeing whether rails picks them up?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment