How to copy directory of *.tt files with thor?

266 Views Asked by At

Is it possible to copy a directory of *.tt files without executing template engine. I'd like to improve my rails templates and add custom scaffold templates (views, controllers and specs), but every time I use directory method

directory "lib/templates", force: true

I see an exception when thor tries to start template engine for those files. How to just copy my directory?

1

There are 1 best solutions below

0
Alex On BEST ANSWER

I think the simplest and safest option is to glob and copy_file as described ^ by @engineersmnky.

But, there is a way to use directory and not execute templates. The only thing that stands in the way is this constant:

TEMPLATE_EXTNAME = ".tt"

Normally, you shouldn't change constants, but it was asking for it by not being frozen. What's the worst that can happentm:

# test.thor

class Test < Thor
  include Thor::Actions
  def self.source_root = __dir__

  desc "example", "task"
  def example
    ignore_tt do
      directory "templates", "copy"           # <= ok
    end
    # directory_with_tt "templates", "copy"   # <= ok
    # directory         "templates", "copy"   # <= fail
  end

  private

  def ignore_tt
    # NOTE: change template extension so it would skip
    #       `when /#{TEMPLATE_EXTNAME}$/` condition and
    #        fallback to default `copy_file`
    Thor::TEMPLATE_EXTNAME.concat "_no_match" # => .tt_no_match
    yield
  ensure
    # NOTE: make sure to undo our dirty work after the block
    Thor::TEMPLATE_EXTNAME.chomp! "_no_match" # => .tt
  end

  def directory_with_tt(...)
    ignore_tt do
      directory(...)
    end
  end
end

and a little test:

$ cat templates/*.tt
class <%= name.capitalize %>
end

$ thor test:example
      create  copy
      create  copy/file.tt

$ cat copy/*.tt
class <%= name.capitalize %>
end

If the above method is too hacky, here is another one, wasn't as simple as I thought, just had to get source_root and destination_root right.

class Test < Thor
  include Thor::Actions
  def self.source_root = "#{__dir__}/templates"

  argument :name

  desc "example", "task"
  def example
    # copy directory, skip .tt
    directory ".", "copy", exclude_pattern: /\.tt$/

    # copy .tt
    Pathname.new(self.class.source_root).glob("**/*.tt").each do |tt|
      copy_file tt, File.join("copy", tt.relative_path_from(self.class.source_root))
    end

    # # this works too
    # self.destination_root = "copy"
    # Pathname.new(self.class.source_root).glob("**/*.tt").each do |tt|
    #   copy_file tt.relative_path_from(self.class.source_root)
    # end
  end
end
# %name% and .empty_directory are handled by `directory`
# file.tt is copied with `copy_file`

templates
├── %name%.rb
├── empty
│  └── .empty_directory
└── tt
   └── file.tt
$ thor test:example app_name
      create  copy
      create  copy/app_name.rb
      create  copy/empty
      create  copy/tt/file.tt