Skip to content

Instantly share code, notes, and snippets.

@annikoff
Last active December 17, 2024 11:38
Show Gist options
  • 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
@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?

@davedkg
Copy link

davedkg commented Dec 16, 2024

Thread was super helpful. Finally got this working.

rails@8.0, rspec-rails@7.0, pundit@2.4

Override Pundit generators:

lib/templates/pundit/policy/policy.rb.tt
lib/templates/rspec/policy/policy_spec.rb.tt

Add to custom scaffold generator:

hook_for :policy, in: :pundit, default: true, type: :boolean

@annikoff
Copy link
Author

Great, thank you

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