How to change source for a custom rails generator? (Thor)
Asked Answered
D

4

2

I'm making a custom generator that generates a new rails app, and I do it like this

require 'thor'
require 'rails/generators/rails/app/app_generator'

class AppBuilder < Rails::AppBuilder
  include Thor::Actions
  include Thor::Shell
  ...
end

The problem is, how do I add a new source directory (which is then used by Thor::Actions#copy_file, Thor::Actions#template, and the others)? I saw in the Thor's documentation that Thor::Actions#source_paths holds the sources (it's an array of paths), so I tried overriding it inside my class (since I've included Thor::Actions):

def source_paths
  [File.join(File.expand_path(File.dirname(__FILE__)), "templates")] + super
end

With this I wanted to add the ./templates directory in the sources, while still keeping the Rails' one (that's why the + super at the end). But it doesn't work, it still lists the Rails' source path as the only one.

I tried browsing through the Rails' source code, but I couldn't find how Rails put his directory in the source paths. And I really want to know that :)

Disaster answered 26/3, 2012 at 21:7 Comment(0)
D
5

This worked:

require 'thor'
require 'rails/generators/rails/app/app_generator'

module Thor::Actions
  def source_paths
    [MY_TEMPLATES]
  end
end

class AppBuilder < Rails::AppBuilder
  ...
end

I don't understand why, but I've spent too much time on this already, so I don't care.

Disaster answered 30/3, 2012 at 13:57 Comment(0)
T
5

Thor will access your source_paths method and add these to the defaults:

  # Returns the source paths in the following order:
  #
  #   1) This class source paths
  #   2) Source root
  #   3) Parents source paths
  #
  def source_paths_for_search
    paths = []
    paths += self.source_paths
    paths << self.source_root if self.source_root
    paths += from_superclass(:source_paths, [])
    paths
  end

So all you need to do in your class is:

class NewgemGenerator < Thor::Group

  include Thor::Actions

  def source_paths
    ['/whatever', './templates']
  end

end

Hope this helps :)

Talkingto answered 30/3, 2012 at 9:30 Comment(6)
I've tried that, I still get this error: Could not find "<any_file>" in any of your source paths. Your current source paths are: path/to/gems/railties-3.2.2/lib/rails/generators/rails/app/templates, so it didn't pick it up at all.Disaster
I added the class definition and include I was using to my answer.Talkingto
But I have to subclass Rails::AppBuilder, because I'm overriding what that class generates.Disaster
I'll award you the bounty, so it doesn't go to waste.Disaster
Thanks! Good luck with your project.Talkingto
I think that source_path method should be wrapped inside no_tasks block to prevent generating exceptions.Gipps
D
5

This worked:

require 'thor'
require 'rails/generators/rails/app/app_generator'

module Thor::Actions
  def source_paths
    [MY_TEMPLATES]
  end
end

class AppBuilder < Rails::AppBuilder
  ...
end

I don't understand why, but I've spent too much time on this already, so I don't care.

Disaster answered 30/3, 2012 at 13:57 Comment(0)
C
2

The source_paths method doesn't work when using AppBuilder. (which is another option to using rails templates). I have a files directory next to the app_builder.rb file that this class is in. I have this working, though it seems there should still be a more elegant way.

tree .
|-- app_builder.rb
|-- files
     `-- Gemfile
class AppBuilder < Rails::AppBuilder

  def initialize generator
    super generator
    path = File.expand_path( File.join( '..', File.dirname( __FILE__ )) )
    source_paths << path
  end

  def gemfile
    copy_file 'files/Gemfile', 'Gemfile'
  end

and then on the console:

rails new my_app -b path_to_app_builder.rb

The dots are required since the ruby file 'app_builder.rb' is slurped up and eval'd after the rails new command changes into the new app directory (I think).

Chorister answered 11/2, 2013 at 3:32 Comment(0)
I
1

It's an old post, however, the problem remains the same, I spent time to figure that out. I let a comment here if it can help.

Rails add by default the path lib/templates so you can custom any templates by copying them in this directory.

Check out the correct directories structures https://github.com/rails/rails/tree/v6.0.1/railties/lib/rails/generators/rails

There is subtle though and is not so obvious to catch.

Take for instance the helper generator, as mentioned in the official Rails documentation, the structure is

https://github.com/rails/rails/tree/v6.0.1/railties/lib/rails/generators/rails/helper

- helper
  - templates
    - helper.rb.tt
  - helper_generator.rb 

Here, what Railties expects to find in your project:

- lib
  - templates 
    - helper
      - helper.rb

your helper.rb is a copy of helper.rb.tt

the last thing, if you plan to use it in Rails::Engine you have to tell it to load that path

# lib/blorgh/engine.rb 
module Blorgh
  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
    config.generators.templates << File.expand_path('../templates', __dir__)
  end
end

Really hope that can help and save time for others.

  1. https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/application/configuration.rb#L188
  2. https://guides.rubyonrails.org/generators.html#customizing-your-workflow-by-changing-generators-templates
  3. https://github.com/rails/rails/blob/v6.0.1/railties/CHANGELOG.md
  4. https://guides.rubyonrails.org/engines.html
Isostasy answered 3/12, 2019 at 16:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.