require "brut/cli" class Brut::CLI::Apps::Scaffold < Brut::CLI::App description "Create scaffolds of various files to help develop more quckly" opts.on("--overwrite", "If set, any files that exists already will be overwritten by new scaffolds") opts.on("--dry-run", "If set, no files are changed. You will see output of what would happen without this flag") def before_execute ENV["RACK_ENV"] = "development" end class Test < Brut::CLI::Command description "Create a test for a given file in the app" args "source_file_paths..." def execute if args.empty? err.puts "'test' requires one or more files to scaffold a test for" return 1 end files_to_test_files = args.map { |arg| Pathname(arg).expand_path }.map { |pathname| relative = pathname.relative_path_from(Brut.container.app_src_dir) test_file = Brut.container.app_specs_dir / relative.dirname / "#{relative.basename(relative.extname)}.spec.rb" [ pathname, test_file ] }.to_h non_existent_sources = files_to_test_files.keys.select { |pathname| !pathname.exist? } existent_destinations = files_to_test_files.values.select { |pathname| pathname.exist? } if non_existent_sources.any? relative_paths = non_existent_sources.map { |pathname| pathname.relative_path_from(Brut.container.project_root) } err.puts "Not all input files exist:" relative_paths.each do |file| err.puts file end return 1 end if existent_destinations.any? && !global_options.overwrite? relative_paths = existent_destinations.map { |pathname| pathname.relative_path_from(Brut.container.project_root) } err.puts "Some files to be generated already exist. Set --overwrite to overwrite them:" relative_paths.each do |file| err.puts file end return 1 end files_to_test_files.each do |source,destination| result = Prism.parse_file(source.to_s) if !result raise "For some reason Prism did not parse #{source.to_s}" end classes = find_classes(result.value).map { |(module_nodes,class_node)| (module_nodes.map(&:constant_path).map(&:full_name).map(&:to_s) + [class_node.constant_path.full_name.to_s]).compact.join("::") } out.puts "#{destination} will contain tests for:\n#{classes.join("\n")}\n\n" code = ["require \"spec_helper\"\n"] + classes.map { |class_name| %{RSpec.describe #{class_name} do it "should have tests" do expect(false).to eq(true) end end} } if global_options.dry_run? puts code else FileUtils.mkdir_p destination.dirname File.open(destination,"w") do |file| file.puts code end end end 0 end private def find_classes(ast,current_modules = []) classes = [] if ast.nil? return classes end new_module = nil if ast.kind_of?(Prism::ClassNode) classes << [ current_modules, ast ] new_module = ast elsif ast.kind_of?(Prism::ModuleNode) new_module = ast end ast.child_nodes.each do |child| new_current_modules = current_modules + [ new_module ] result = find_classes(child, new_current_modules.compact) classes = classes + result end classes end end class Component < Brut::CLI::Command description "Create a new component, template, and associated test" opts.on("--page","If set, this component is for a specific page and won't go with the other components") args "ComponentName" def execute if args.length != 1 raise "component requires exactly one argument, got #{args.length}" end class_name = RichString.new(args[0]) if class_name.to_s !~ /Component$/ class_name = RichString.new(class_name.to_s + "Component") end relative_path = class_name.underscorized components_src_dir = Brut.container.components_src_dir components_specs_dir = Brut.container.components_specs_dir if options.page? components_src_dir = Brut.container.pages_src_dir components_specs_dir = Brut.container.pages_specs_dir if class_name.to_s !~ /::/ raise "component #{class_name} cannot be a page component - it must be an inner class of an existing page" else existing_page = RichString.new(class_name.to_s.split(/::/)[0..-2].join("::")).underscorized.to_s + ".rb" if !(components_src_dir / existing_page).exist? raise "#{class_name} was set as a page component, however we cannot find the page it belongs in. File #{existing_page} does not exist and should contain that page" end end end source_path = Pathname( (components_src_dir / relative_path).to_s + ".rb" ) html_source_path = Pathname( (components_src_dir / relative_path).to_s + ".html.erb" ) spec_path = Pathname( (components_specs_dir / relative_path).to_s + ".spec.rb" ) exists = [ source_path, html_source_path, spec_path, ].select(&:exist?) if exists.any? && !global_options.overwrite? exists.each do |path| err.puts "'#{path.relative_path_from(Brut.container.project_root)}' exists already" end err.puts "Re-run with --overwrite to overwrite these files" return 1 end if global_options.dry_run? puts "FileUtils.mkdir_p #{source_path.dirname}" puts "FileUtils.mkdir_p #{html_source_path.dirname}" puts "FileUtils.mkdir_p #{spec_path.dirname}" else FileUtils.mkdir_p source_path.dirname FileUtils.mkdir_p html_source_path.dirname FileUtils.mkdir_p spec_path.dirname File.open(source_path,"w") do |file| file.puts %{class #{class_name} < AppComponent def initialize end end} end File.open(html_source_path,"w") do |file| file.puts "