Add `template_source_dir` to templating support

I have a template file that lets me bootstrap Rails skeletons with my preferred gems and run boilerplate generators and migrations. This works flawlessly apart from the addition of new files.

Currently Thor’s actions allow usage of file and create_file. But the problem I have with them is the source of the file itself has to be provided either inside the template itself or be read from an absolute location in the file system.

The way I saw it – it’d make more sense (to me) to add the static files (e.g. configs) next to the template file itself. The problem though is that during the generation I don’t have access to the location of the template itself. And getting it is rather cumbersome (I’d have to parse through ARGV and massage the path of the found -m value).

I added a PoC to Rails::Generators::Actions module that provides template_source_dir helper method to retrieve a folder of the currently used template as well as overrides the apply method to store the latest template directory for said helper to use:

def apply(path, config = {})
  (@_template_path_stack ||= []) << (path =~ %r{^https?://} 
    ? path
    : File.expand_path(path))
  super
ensure
  @_template_path_stack.pop
end

def template_source_dir
  return unless (path = @_template_path_stack&.last)
  path =~ %r{^https?://} ?
    URI(path).then { |u| "#{u.scheme}://#{u.host}#{File.dirname(u.path)}" } :
    File.dirname(path)
end

I have tested this with the following template structure:

template/
  template.rb # the rails app template file
  tasks.json  # my frequently used tasks in vscode-like editors

The template.rb had the following snippet inside:

tasks_json_path = File.join(template_source_dir, 'tasks.json')
tasks_json = File.read(tasks_json_path)

create_file '.vscode/tasks.json' do
  tasks_json
end

I ran the rails app generator using my local rails checkout

ruby ./rails/railties/exe/rails new tpl-test -m ./template/template.rb

And it properly took the contents of tasks.json into the rails app.

Before doing tests with URLs and covering this with tests I’d like to know if anyone would be interested in this feature and/or consider it useful in general?

The two “meat” methods were generated by LLM.

What you are looking for is source_paths. Here is the guide about it; Thor methods like create_file and copy_file resolve paths from it. You can override the method or prepend/append to its returned value, and typically you’ll want to add __dir__ to the source_paths, so you can reference your files relative to the template’s directory.

I would advise against using file, which is a Rails alias to Thor’s create_file, but is a private API.

Edit: the section of the guide I linked to does not officially exist any more, so I made a PR to restore it.

1 Like

Thanks for the reply!

That is very neat! I did not know that. It seems that for now then my needs are met.