require_relative "../lib/myrails/version" require 'thor' require 'active_support/all' module Myrails class Myrails < Thor include Thor::Actions source_root "#{__dir__}/myrails/templates" TEMPLATES = source_root ENVIRONMENTS = %w(development test production) desc 'model', 'Generates and empty rails model with the given name and its related spec file Use --namespace=namespace to create a namespaced model' option :name, required: true option :namespace def model if options[:namespace] template 'rails/namespace_model.rb', "app/models/#{options[:namespace]}/#{options[:name]}.rb" template 'rspec/namespace_model.rb', "spec/models/#{options[:namespace]}/#{options[:name]}_spec.rb" else template 'rails/model.rb', "app/models/#{options[:name]}.rb" template 'rspec/model.rb', "spec/models/#{options[:name]}_spec.rb" end end desc 'controller', 'Generate a rails controller with the given name along with boiler plate code and related spec file. Add --namespace=NAME to create a namespaced controller' option :name, required: true option :namespace def controller if options[:namespace] template 'rails/namespace_controller.rb', "app/controllers/#{options[:namespace]}/#{options[:name].pluralize}_controller.rb" template 'rails/parent_namespace_controller.rb', "app/controllers/#{options[:namespace]}/#{options[:namespace]}_controller.rb" template 'rspec/namespace_controller.rb', "spec/controllers/#{options[:namespace]}/#{options[:name].pluralize}_controller_spec.rb" run "mkdir -p app/views/#{options[:namespace]}/#{options[:name].pluralize}" else template 'rails/controller.rb', "app/controllers/#{options[:name].pluralize}_controller.rb" template 'rspec/controller.rb', "spec/controllers/#{options[:name].pluralize}_controller_spec.rb" run "mkdir -p app/views/#{options[:name].pluralize}" end end desc 'policy', 'Generate a pundit policy with the given name and a related spec file' option :name, required: true def policy template 'rails/pundit.rb', "app/policies/#{options[:name]}_policy.rb" template 'rspec/pundit.rb', "spec/policies/#{options[:name]}_policy_spec.rb" end desc 'presenter', 'Generate a presenter class with the given name and a related spec file' option :name, required: true def presenters copy_file 'presenters/base.rb', 'app/presenters/base_presenter.rb' template 'presenters/presenter.rb', "app/presenters/#{options[:name]}_presenter.rb" copy_file 'presenters/presenter_config.rb', 'spec/support/configs/presenter.rb' template 'presenters/presenter_spec.rb', "spec/presenters/#{options[:name]}_presenter_spec.rb" end desc 'factory', 'Generate a factory_girl file for use with rspec' option :name, required: true def factory template 'rspec/factory.rb', "spec/factories/#{options[:name]}.rb" end desc 'install_application_helper', 'Replace current application helper with one that has commonly used code' def install_application_helper copy_file 'rails/application_helper.rb', 'app/helpers/application_helper.rb' end desc 'install_ui', 'Generate UI files and code for prototyping views in app/views/ui' def install_ui copy_file 'ui/ui_controller.rb', 'app/controllers/ui_controller.rb' copy_file 'ui/index.html.haml', 'app/views/ui/index.html.haml' inject_into_file 'config/routes.rb', after: "Rails.application.routes.draw do\n" do <<-CODE # Requires an application restart everytime a new page is added. Dir.glob('app/views/ui/*.html.haml').sort.each do |file| action = File.basename(file,'.html.haml') get \"ui/\#{action}\", controller: 'ui', action: action end CODE end end desc 'install_rspec', 'Generate rspec structure & rspec configuration that I commonly use' def install_rspec insert_into_file 'Gemfile', after: "gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n" do <<-CODE gem 'rspec-rails', group: :test CODE end run 'bundle install' run 'rails g rspec:install' copy_file 'rspec/database_cleaner.rb', "spec/support/configs/database_cleaner.rb" copy_file 'rspec/factory_girl.rb', 'spec/support/configs/factory_girl.rb' copy_file 'rspec/shoulda_matchers.rb', 'spec/support/configs/shoulda_matchers.rb' copy_file 'rspec/silence_backtrace.rb', 'spec/support/configs/silence_rspec_backtrace.rb' copy_file 'rspec/javascript.rb', 'spec/support/configs/javascript.rb' copy_file 'rspec/mailer.rb', 'spec/support/configs/mailer.rb' copy_file 'rspec/router.rb', 'spec/support/configs/router.rb' copy_file 'rspec/files.rb', 'spec/support/configs/files.rb' end desc 'install_mailer', 'Generate sendgrid initializer and mail interceptor' option :email, required: true def install_mailer copy_file 'mailer/sendgrid.rb', 'config/initializers/sendgrid.rb' template 'mailer/dev_mail_interceptor.rb', 'app/mailers/dev_mail_interceptor.rb' ENVIRONMENTS.each do |environment| unless environment == 'production' inject_into_file "config/environments/#{environment}.rb", after: "# config.action_view.raise_on_missing_translations = true\n" do <<-CODE ActionMailer::Base.register_interceptor(DevMailInterceptor) CODE end end end end desc 'config_env', 'Add code to environment files. Host refers to url options. Name option referes to controller and mailer default_url_options' option :name, required: true def config_env ENVIRONMENTS.each do |environment| if options[:name].nil? say "Missing parameter: --name" else case environment when 'development' inject_into_file 'config/environments/development.rb', after: "config.action_mailer.raise_delivery_errors = false\n" do <<-CODE config.action_mailer.delivery_method = :letter_opener config.action_mailer.perform_deliveries = false config.action_mailer.default_url_options = { host: 'http://#{options[:name]}.dev' } config.action_controller.default_url_options = { host: 'http://#{options[:name]}.dev' } CODE end when 'test' inject_into_file 'config/environments/test.rb', after: "config.action_mailer.delivery_method = :test\n" do <<-CODE config.action_mailer.default_url_options = { host: 'http://localhost:3000' } config.action_controller.default_url_options = { host: 'http://localhost:3000' } CODE end when 'production' inject_into_file 'config/environments/production.rb', after: "config.active_record.dump_schema_after_migration = false\n" do <<-CODE config.action_mailer.default_url_options = { host: '' } config.action_controller.default_url_options = { host: '' } config.assets.compile = true CODE end end end end end desc 'install_rails_helper', 'Add code to rspec/rails_helper so rspec runs the way I like' def install_rails_helper inject_into_file "spec/rails_helper.rb", after: "require 'rspec/rails'\n" do <<-CODE require 'simplecov' SimpleCov.start #use Chromedriver unless ENV['NOCHROME'] Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app, :browser => :chrome) end end CODE end gsub_file 'spec/rails_helper.rb', "# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }", "Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }" gsub_file "spec/rails_helper.rb", "config.use_transactional_fixtures = true", "config.use_transactional_fixtures = false" inject_into_file 'spec/rails_helper.rb', after: "RSpec.configure do |config|\n" do <<-CODE config.include(JavascriptHelper, type: :feature) CODE end end desc 'install_devise', 'Generate devise files' def install_devise insert_into_file 'Gemfile', after: "gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n" do <<-CODE gem 'devise', '~> 4.2.0' CODE end run 'bundle update' copy_file 'rspec/devise.rb', 'spec/support/configs/devise.rb' devise_model = ask("What would you like to call the devise model? Default: user") devise_model = devise_model.empty? ? 'user' : devise_model run 'rails generate devise:install' run 'rake db:migrate' run "rails generate devise #{devise_model}" run 'rails generate devise:views' inject_into_file 'app/controllers/application_controller.rb', before: 'protect_from_forgery with: :exception' do <<-CODE # Devise authentication method before_action :authenticate_#{devise_model}! CODE end if File.exist?('app/controllers/ui_controller.rb') inject_into_file 'app/controllers/ui_controller.rb', after: "class UiController < ApplicationController\n" do <<-CODE skip_before_action :authenticate_#{devise_model}! CODE end end if yes?('Will you be needing registration params override? Answer "yes" if you will be adding attributes to the user model') inject_into_file 'app/controllers/application_controller.rb', after: "skip_before_action :authenticate_#{devise_model}!\n" do <<-CODE # Before action include additional registration params # (see #configure_permitted_parameters) before_action :configure_permitted_parameters, if: :devise_controller? CODE end inject_into_file 'app/controllers/application_controller.rb', after: "private\n" do <<-CODE # Register additional registration params def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute, :attribute]) end CODE end end end desc 'install_pundit', 'Install pundit gem and generate pundit files and application controller code' def install_pundit insert_into_file 'Gemfile', after: "gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n" do <<-CODE gem 'pundit' CODE end insert_into_file 'Gemfile', before: "group :development, :test do" do <<-CODE gem 'pundit-matchers', '~> 1.1.0' CODE end run 'bundle update' run 'rails g pundit:install' inject_into_file 'app/controllers/application_controller.rb', before: '# Prevent CSRF attacks by raising an exception.' do <<-CODE # Add pundit authorization include Pundit CODE end inject_into_file 'app/controllers/application_controller.rb', after: "protect_from_forgery with: :exception\n" do <<-CODE # Rescue from pundit error # (see #user_not_authorized) rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized CODE end inject_into_file 'app/controllers/application_controller.rb', after: "private\n" do <<-CODE # Method to gracefully let a user know they are are not authorized # # @return flash [Hash] the action notice def user_not_authorized flash[:alert] = "You are not authorized to perform this action." redirect_to home_path end CODE end end desc 'install_footnotes', 'Install rails-footnotes and run its generator' def install_footnotes insert_into_file 'Gemfile', after: "gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n" do <<-CODE gem 'rails-footnotes' CODE end run 'bundle install' run 'rails generate rails_footnotes:install' end desc 'install_gems', 'Add & Install gems that I commonly use' def install_gems insert_into_file 'Gemfile', before: "group :development, :test do" do <<-CODE group :test do gem 'simplecov' gem 'shoulda-matchers' gem 'factory_girl_rails' gem 'database_cleaner' gem 'capybara' gem 'selenium-webdriver' gem 'chromedriver-helper' gem 'launchy' gem 'rails-controller-testing' end CODE end insert_into_file 'Gemfile', after: "group :development, :test do\n" do <<-CODE gem 'faker' gem 'yard' gem 'letter_opener' gem "rails-erd" CODE end insert_into_file 'Gemfile', after: "gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n" do <<-CODE gem 'node' gem 'bootstrap-sass', '~> 3.3.1' gem 'autoprefixer-rails' gem 'haml-rails' gem "ransack" gem 'will_paginate' gem "font-awesome-rails" gem 'trix' gem 'record_tag_helper' CODE end run 'bundle install' insert_into_file 'app/controllers/application_controller.rb', before: 'end' do <<-CODE private CODE end end desc 'install_assets', 'Generate common asset pipeline files' def install_assets run "rm app/assets/stylesheets/application.css" copy_file 'assets/application.css.sass', 'app/assets/stylesheets/application.css.sass' copy_file 'assets/application.js', 'app/assets/javascripts/application.js' copy_file 'assets/animate.scss', 'app/assets/stylesheets/animate.scss' copy_file 'assets/will_paginate.scss', 'app/assets/stylesheets/will_paginate.scss' end desc 'install_css', 'Generate & include application css theme' def install_css themes = Dir[File.join(TEMPLATES, 'assets', 'bootstrap_themes', '*')] themes.each_with_index do |theme, index| say "[#{index}] #{File.basename(theme,'.*')}" end idx = ask("Choose a color theme (by number) for the application. Default: 'spacelab'") idx = idx.empty? ? themes.index{|theme| theme if theme.include?('spacelab')} : idx.to_i copy_file(themes[idx], "app/assets/stylesheets/#{File.basename(themes[idx])}") inject_into_file 'app/assets/stylesheets/application.css.sass', before: "@import will_paginate" do <<-CODE @import #{File.basename(themes[idx], '.*')} CODE end end desc 'install_footer', 'Generate & include application footer' def install_footer footers = Dir[File.join(TEMPLATES, 'layout', 'footers', '*.haml')] footers_css = Dir[File.join(TEMPLATES, 'layout', 'footers', 'css', '*')] footers.each_with_index do |footer, index| say "[#{index}] #{File.basename(footer,'.html.*')}" end idx = ask("Chose a footer theme (by number) for the application. Deault: 'footer-distributed (Basic)'") idx = idx.empty? ? footers.index{|footer| footer if footer.include?('footer-distributed.html.haml')} : idx.to_i copy_file footers[idx], "app/views/layouts/_footer.html.haml" copy_file footers_css[idx], "app/assets/stylesheets/#{File.basename(footers_css[idx])}" inject_into_file 'app/assets/stylesheets/application.css.sass', after: "@import animate\n" do <<-CODE @import #{File.basename(footers_css[idx], '.*')} CODE end end desc 'install_layout', 'Generate common layout files' def install_layout run 'rm app/views/layouts/application.html.erb' template 'layout/application.html.haml', 'app/views/layouts/application.html.haml' template 'layout/_nav.html.haml', 'app/views/layouts/_nav.html.haml' copy_file 'layout/_info_messages.html.haml', 'app/views/layouts/_info_messages.html.haml' copy_file 'layout/_success_message.html.haml', 'app/views/layouts/_success_message.html.haml' copy_file 'layout/_error_messages.html.haml', 'app/views/layouts/_error_messages.html.haml' end desc 'install_heroku', 'setup application for use with heroku using sqlite3 for development' def install_heroku insert_into_file 'Gemfile', before: "group :development, :test do\n" do <<-CODE group :production do gem 'pg' gem 'rails_12factor' end CODE end copy_file 'db/sqlite3_database.yml', 'config/database.yml' copy_file 'heroku/Procfile', 'Procfile' copy_file 'heroku/puma.rb', 'config/puma.rb' end desc 'git_init', "Initialize git with some files autormoatically ignored" def git_init run 'git init' run 'echo /coverage >> .gitignore' run 'echo /config/application.yml >> .gitignore' run 'git add --all' run "git commit -m 'initial commit'" end desc 'mysql_switch', 'Switch to mysql database' def mysql_switch gsub_file 'Gemfile', "gem 'sqlite3'", "gem 'mysql2', '>= 0.3.13', '< 0.5'" run 'bundle install' copy_file 'db/mysql_database.yml', 'config/database.yml' end desc 'new_ui NAME', 'Create a new ui view' def new_ui(name) run "touch app/views/ui/#{name}.html.haml" say "DON'T FORGET: Restart Powify App" end end end Myrails::Myrails.start(ARGV)