require "thor" require "date" module UcbRailsCli class Cli < Thor MASTER_KEY_PATH = File.expand_path("~/.ucb_rails_master_key") ## error messages INSTALL_RAILS = "Unable to find rails - please make sure you've installed Rails 5.1 or greater" VERIFY_KEY = "Unable to find the master key for encrypted credentials\n" + "in #{MASTER_KEY_PATH} or the RAILS_MASTER_KEY environment variable.\n" + "Please set the master key in one of these two places before proceeding." GEMFILE_ERROR = "Unable to add new gems to Gemfile - check the console output for more info" LAYOUT_FILES = "Unable to copy view templates - check the console output for more info" HELPER_FILES = "Unable to copy helpers - check the console output for more info" ASSETS = "Unable to copy app assets - check the console output for more info" ROUTES = "Unable to copy routes file - check the console output for more info" MIGRATIONS = "Unable to copy migrations from ucb_rails_user gem - check the console output for more info" CONTROLLERS = "Unable to copy controllers - check the console output for more info" CONFIG_FILES = "Unable to copy config files - check the console output for more info" RSPEC = "Unable to install RSpec - check the console output for more info" desc 'version', 'Display version' map %w[-v --version] => :version def version say "ucb_rails_cli #{UcbRailsCli::VERSION}" end desc "new APP_NAME", "generate a new UCB Rails app called NAME" option :rails_options def new(app_name) puts "Generating #{app_name}..." verify_rails or exit_with_error(INSTALL_RAILS) verify_master_key or exit_with_error(VERIFY_KEY) create_rails_app(app_name) or exit_with_error add_to_gemfile(app_name) or exit_with_error(GEMFILE_ERROR) add_layout_files(app_name) or exit_with_error(LAYOUT_FILES) add_helpers(app_name) or exit_with_error(HELPER_FILES) add_assets(app_name) or exit_with_error(ASSETS) add_routes(app_name) or exit_with_error(ROUTES) add_controllers(app_name) or exit_with_error(CONTROLLERS) add_config_files(app_name) or exit_with_error(CONFIG_FILES) install_migrations(app_name) or exit_with_error(MIGRATIONS) setup_rspec(app_name) or exit_with_error(RSPEC) puts <<-END ******************************************************************** Finished! Your new app is in ./#{app_name} From here, you should set up the database: cd #{app_name} bin/rails db:create bin/rails db:migrate then start the server as usual: bin/rails server then access the homepage at http://localhost:3000. You should be able to login with your usual UCB credentials. Enjoy! END end private def exit_with_error(msg="") puts msg exit -1 end def verify_rails begin `rails --version` =~ /Rails (\d)\./ major_version = $1&.to_i return major_version && major_version >= 5 rescue Exception => e return false end end def verify_master_key ENV["RAILS_MASTER_KEY"].to_s.length > 0 || FileTest.file?(MASTER_KEY_PATH) end def create_rails_app(app_name) result = system("rails new #{app_name} #{options[:rails_options]}") return true if result puts "Unable to run \"rails new\" to create a new app - check the console output \n" + "for more details about what went wrong" false end def add_to_gemfile(app_name) if system("cat #{template_dir}/Gemfile_additions >> #{app_name}/Gemfile") puts "Installing new gems..." system "cd #{app_name} && bundle install" end end def add_layout_files(app_name) puts "Installing template files..." if system("cp #{template_dir}/views/* #{app_name}/app/views/layouts") system "rm -f #{app_name}/app/views/layouts/application.html.erb" else false end end def add_helpers(app_name) puts "Installing helpers..." file_contents = `cat #{template_dir}/helpers/application_helper.rb` .gsub("%APP_NAME", humanize(app_name)) .gsub("%APP_START_YEAR", Date.today.year.to_s) system "echo '#{file_contents}' > #{app_name}/app/helpers/application_helper.rb" system("cp #{template_dir}/helpers/flash_helper.rb #{app_name}/app/helpers/flash_helper.rb") true end def add_assets(app_name) puts "Installing assets..." result = system("cp #{template_dir}/stylesheets/main.sass #{app_name}/app/assets/stylesheets") && system("cp -R #{template_dir}/stylesheets/themes #{app_name}/app/assets/stylesheets") && system("cp #{template_dir}/images/* #{app_name}/app/assets/images") if result # don't clobber the default file until we're sure everything else went through system "cp #{template_dir}/stylesheets/application.css #{app_name}/app/assets/stylesheets" system "cp #{template_dir}/javascripts/application.js #{app_name}/app/assets/javascripts" else false end end def add_routes(app_name) puts "Installing routes..." system "cp #{template_dir}/config/routes.rb #{app_name}/config/routes.rb" end def add_controllers(app_name) puts "Installing controllers..." system "cp #{template_dir}/controllers/* #{app_name}/app/controllers/" end def add_config_files(app_name) puts "Installing config files..." system("cp #{template_dir}/config/initializers/ucb_rails_user.rb #{app_name}/config/initializers") system("cp #{template_dir}/config/credentials.yml.enc #{app_name}/config/") # add exeception_notification config to production.rb config prod_config_addition = <<-END_CONFIG config.middleware.use ExceptionNotification::Rack, email: { email_prefix: "[#{app_name}] ", sender_address: %{"notifier" }, exception_recipients: %w{ucb-ist-developers@berkeley.edu} } end END_CONFIG prod_config_contents = `cat #{app_name}/config/environments/production.rb` .gsub(/end\s\Z/, prod_config_addition) system "echo '#{prod_config_contents}' > #{app_name}/config/environments/production.rb" # move master key, unless in ENV unless ENV["MASTER_KEY_PATH"].to_s.length > 0 system "cp ~/.ucb_rails_master_key #{app_name}/config/master.key" end # set default timezone in application.rb application_config_contents = `cat #{app_name}/config/application.rb` .gsub(/end\s*end\s\Z/, %Q( config.time_zone = "Pacific Time (US & Canada)"\n end\nend)) File.open("#{app_name}/config/application.rb", "w") do |file| file.puts(application_config_contents) end true end def install_migrations(app_name) puts "Installing migrations from ucb_rails_user..." system "cd #{app_name} && bundle install" system "cd #{app_name} && bundle exec rails railties:install:migrations" end def setup_rspec(app_name) puts "Setting up rspec..." success = system("cd #{app_name} && bin/spring stop && bin/rails generate rspec:install") && system("rm -rf #{app_name}/test") && system("cp -R #{template_dir}/rspc_files/* #{app_name}/spec") success or return false rails_helper_contents = `cat #{app_name}/spec/rails_helper.rb` .gsub(/end\s\Z/, "#{custom_spec_helper_config()}\nend") .gsub("require 'rspec/rails'", "require 'rspec/rails'\nrequire 'ucb_rails_user/spec_helpers'") File.open("#{app_name}/spec/rails_helper.rb", "w") do |file| file.puts(rails_helper_contents) end true end def template_dir "#{File.dirname(__dir__)}/ucb_rails_cli/templates" end def humanize(str) str .gsub("_", " ") .split .map(&:capitalize) .join(" ") end def custom_spec_helper_config config = <<-END_CONFIG # ucb_rails config Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } config.include UcbRailsUser::SpecHelpers UcbRailsUser.config do |config| config.user_session_manager = "UcbRailsUser::UserSessionManager::TestSessionManager" end END_CONFIG end end end