require 'thor' require 'find' class ProjecterCLI < Thor # The create command is responsible for driving project creation. # XXX: This should be simpler. Eventually refactor once we have # composable projects, different project types besides lib/thorapp. include Thor::Actions add_runtime_options! source_root(File.expand_path('../../..', __FILE__)) desc 'create PROJECT', 'Create a new Thor CLI skeleton in ./PROJECT' method_options uses_templates: false, uses_files: false, library: false # rubocop:disable Metrics/AbcSize def create(project) @project = project run("git init #{@project}", capture: true) unless File.exist?(File.join(@project, '.git')) create_project_dirs projecter_file( 'Gemfile' => 'Gemfile', 'Guardfile' => 'Guardfile', 'Rakefile' => 'Rakefile', 'dot-gitignore' => '.gitignore', 'dot-metrics' => '.metrics', 'dot-rspec' => '.rspec', 'dot-rubocop.yml' => '.rubocop.yml' ) lib_templates = { 'gemspec.tt' => "#{project}.gemspec", 'README.md.tt' => 'README.md', 'reek.tt' => "#{project}.reek", 'mainlib.rb.tt' => ['lib', "#{project}.rb"], 'version.rb.tt' => ['lib', project, 'version.rb'], 'spec_helper.rb.tt' => %w(spec spec_helper.rb), 'spec_fixtures.rb.tt' => %w(spec fixtures.rb), 'spec_resources.rb.tt' => %w(spec resources.rb) } app_templates = { 'mainapp.tt' => ['bin', project], 'version-cmd.rb.tt' => %w(lib commands version.rb) } templates = lib_templates templates.merge(app_templates) unless options.library? projecter_template( templates, project: project, classname: project .split(/[_-]/) .map(&:capitalize) .join, library_only: options.library? ) inside(@project) do # XXX: KLUDGE: Add an empty .gitignore file in each empty directory # XXX: KLUDGE: so that git can track the dir. Find.find('.') do |path| next if path.start_with?('./.git/') next unless FileTest.directory?(path) next unless Dir.entries(path) == %w(. ..) create_file File.join(path, '.gitignore'), '' end run('git add .gitignore', capture: true) run('git add .', capture: true) run("git commit --allow-empty -m 'Created #{@project} using Projecter #{Projecter::VERSION}.'", capture: true) end end # rubocop:enable Metrics/AbcSize private def projecter_file(mapping) mapping.each do |src, dst| copy_file(File.join('files', src), File.join(@project, dst)) end end def projecter_template(mapping, bindings) mapping.each do |src, dst| dst = [dst] unless dst.is_a?(Array) srcname = File.join('templates', src) dstname = File.join(*[@project, dst].flatten) template(srcname, dstname, bindings) if dst.include?('bin') chmod(dstname, 0755) unless (File.stat(dstname).mode & 0755) == 0755 end end end # rubocop:disable Metrics/AbcSize def create_project_dirs source_dirs = ['lib', "lib/#{@project}"] %w(bin lib/commands).map { |d| source_dirs << d } unless options.library? test_resource_dirs = %w(fixtures resources) test_kinds = %w(unit integration acceptance) test_dirs = (test_resource_dirs + test_kinds.product(source_dirs)).map { |path| ['spec', path].join('/') } project_dirs = source_dirs + test_dirs project_dirs << 'templates' if options.uses_templates? project_dirs << 'files' if options.uses_files? project_dirs.each do |dir| empty_directory File.join(@project, dir) end end # rubocop:enable Metrics/AbcSize def library? @library ||= options.library? end end