class Suspenders::AppBuilder

Public Instance Methods

accept_defaults() click to toggle source
# File lib/voyage/app_builder.rb, line 10
def accept_defaults
  if agree?('Would you like to accept all defaults? [slim, devise w/ first & last name, refills nav & footer] (Y/n)')
    @@accept_defaults = true
  else
    @@accept_defaults = false
  end
end
add_admin_views_for_devise_resource(adding_first_and_last_name) click to toggle source
# File lib/voyage/app_builder.rb, line 316
def add_admin_views_for_devise_resource(adding_first_and_last_name)
  config = { adding_first_and_last_name: adding_first_and_last_name }
  template '../templates/users_index.html.erb', 'app/views/admin/users/index.html.erb', config

  if @@use_slim
    inside('lib') do # arbitrary, run in context of newly generated app
      run "erb2slim '../app/views/users' '../app/views/users'"
      run "erb2slim -d '../app/views/users'"

      run "erb2slim '../app/views/admin/users' '../app/views/admin/users'"
      run "erb2slim -d '../app/views/admin/users'"
    end
  end

  template '../templates/admin_users_controller.rb', 'app/controllers/admin/users_controller.rb'
  template '../templates/admin_controller.rb', 'app/controllers/admin/admin_controller.rb'
end
add_analytics_initializer() click to toggle source
# File lib/voyage/app_builder.rb, line 294
def add_analytics_initializer
  template '../templates/analytics_ruby_initializer.rb', 'config/initializers/analytics_ruby.rb'
  template '../templates/analytics_alias.html.erb.erb', 'app/views/users/analytics_alias.html.erb'
end
add_api_foundation() click to toggle source
# File lib/voyage/app_builder.rb, line 563
    def add_api_foundation
      # Create /app/api/base_api_controller.rb
      template '../templates/api_base_controller.rb', 'app/controllers/api/base_api_controller.rb', force: true

      # Create /app/api/v1/users_controller.rb
      template '../templates/api_users_controller.rb', 'app/controllers/api/v1/users_controller.rb', force: true

      # Update routes to include namespaced API
      inject_into_file 'config/routes.rb', before: /^end/ do <<-RUBY.gsub(/^ {6}/, '')

        # API-specific routes
        namespace 'api' do
          namespace 'v1' do
            resources :users, except: [:new, :edit]
          end
        end
        RUBY
      end
    end
add_app_css_file() click to toggle source
# File lib/voyage/app_builder.rb, line 666
    def add_app_css_file
      create_file "app/assets/stylesheets/#{app_name}.sass" do <<-RUBY.gsub(/^ {8}/, '')
        .outer-wrapper
          position: relative
          min-height: 100%

        .navigation-wrapper
          @include outer-container

        .main
          padding: 18px
          @include outer-container
          padding-bottom: 450px

        .footer
          min-height: 420px
          position: absolute
          bottom: 0
          left: 0
        RUBY
      end
    end
add_auto_annotate_models_rake_task() click to toggle source
# File lib/voyage/app_builder.rb, line 769
def add_auto_annotate_models_rake_task
  template '../templates/auto_annotate_models.rake', 'lib/tasks/auto_annotate_models.rake', force: true
end
add_bullet_gem_configuration() click to toggle source
# File lib/suspenders/app_builder.rb, line 63
    def add_bullet_gem_configuration
      config = <<-RUBY
  config.after_initialize do
    Bullet.enable = true
    Bullet.bullet_logger = true
    Bullet.rails_logger = true
  end

      RUBY

      inject_into_file(
        "config/environments/development.rb",
        config,
        after: "config.action_mailer.raise_delivery_errors = true\n",
      )
    end
add_canard_roles_to_devise_resource() click to toggle source
# File lib/voyage/app_builder.rb, line 365
    def add_canard_roles_to_devise_resource
      inject_into_file 'app/models/user.rb', before: /^end/ do <<-RUBY.gsub(/^ {6}/, '')

        before_create :generate_uuid

        # Permissions cascade/inherit through the roles listed below. The order of
        # this list is important, it should progress from least to most privelage
        ROLES = [:admin].freeze
        acts_as_user roles: ROLES
        roles ROLES

        validates :email,
                  presence: true,
                  format: /\\A[-a-z0-9_+\\.]+\\@([-a-z0-9]+\\.)+[a-z0-9]{2,8}\\z/i,
                  uniqueness: true

        # NOTE: these password validations won't run if the user has an invite token
        validates :password,
                  presence: true,
                  length: { within: 8..72 },
                  confirmation: true,
                  on: :create
        validates :password_confirmation,
                  presence: true,
                  on: :create

        def tester?
          (email =~ /(example.com|headway.io)$/).present?
        end

        private

        def generate_uuid
          loop do
            uuid = SecureRandom.uuid
            self.uuid = uuid
            break unless User.exists?(uuid: uuid)
          end
        end
        RUBY
      end
    end
add_custom_routes_for_devise() click to toggle source
# File lib/voyage/app_builder.rb, line 417
    def add_custom_routes_for_devise
      find = <<-RUBY.gsub(/^ {6}/, '')
        devise_for :users
        resources :users
      RUBY

      replace = <<-RUBY.gsub(/^ {6}/, '')
        devise_for :users, controllers: {
          registrations: 'devise_customizations/registrations',
        }

        resources :users do
          member do
            get 'analytics_alias'
          end
        end

        namespace :admin do
          resources :users do
            member do
              get 'impersonate'
            end

            collection do
              get 'stop_impersonating'
            end
          end
        end

        authenticated :user do
          # root to: 'dashboard#show', as: :authenticated_root
          root to: 'high_voltage/pages#show', id: 'welcome', as: :authenticated_root
        end

        devise_scope :user do
          get 'sign-in',  to: 'devise/sessions#new'
          get 'sign-out', to: 'devise/sessions#destroy'
        end
      RUBY

      replace_in_file 'config/routes.rb', find, replace
    end
add_devise_registrations_controller() click to toggle source
# File lib/voyage/app_builder.rb, line 288
def add_devise_registrations_controller
  template '../templates/devise_registrations_controller.rb',
           'app/controllers/devise_customizations/registrations_controller.rb'
end
add_favicon() click to toggle source
# File lib/voyage/app_builder.rb, line 773
def add_favicon
  template '../templates/favicon.ico', 'app/assets/images/favicon.ico', force: true
end
add_high_voltage_static_pages() click to toggle source
# File lib/voyage/app_builder.rb, line 615
    def add_high_voltage_static_pages
      template '../templates/about.html.erb', "app/views/pages/about.html.#{@@use_slim ? 'slim' : 'erb'}"
      template '../templates/welcome.html.erb', "app/views/pages/welcome.html.erb"

      inject_into_file 'config/routes.rb', before: /^end/ do <<-RUBY.gsub(/^ {6}/, '')
        root 'high_voltage/pages#show', id: 'welcome'
        RUBY
      end

      create_file 'config/initializers/high_voltage.rb' do <<-RUBY.gsub(/^ {8}/, '')
        HighVoltage.configure do |config|
          config.route_drawer = HighVoltage::RouteDrawers::Root
        end
        RUBY
      end
    end
add_refills_to_stylesheets() click to toggle source
# File lib/voyage/app_builder.rb, line 724
    def add_refills_to_stylesheets
      inject_into_file 'app/assets/stylesheets/application.scss', after: '@import "refills/flashes";'  do <<-RUBY.gsub(/^ {8}/, '')
        \n@import "refills/navigation";
        @import "refills/footer";

        @import "#{app_name}";
        RUBY
      end
    end
add_rubocop_config() click to toggle source
# File lib/voyage/app_builder.rb, line 765
def add_rubocop_config
  template '../templates/rubocop.yml', '.rubocop.yml', force: true
end
add_specs() click to toggle source
# File lib/voyage/app_builder.rb, line 781
    def add_specs
      inject_into_file 'app/jobs/application_job.rb', before: "class ApplicationJob < ActiveJob::Base" do <<-RUBY.gsub(/^ {8}/, '')
        # :nocov:
        RUBY
      end

      template '../templates/specs/controllers/admin/users_controller_spec.rb', 'spec/controllers/admin/users_controller_spec.rb', force: true
      template '../templates/specs/controllers/application_controller_spec.rb', 'spec/controllers/application_controller_spec.rb', force: true
    end
add_to_gitignore() click to toggle source
# File lib/voyage/app_builder.rb, line 822
    def add_to_gitignore
      inject_into_file '.gitignore', after: '/tmp/*' do <<-RUBY.gsub(/^ {8}/, '')

        .env
        .zenflow-log
        errors.err
        .ctags
        .cadre/coverage.vim
        RUBY
      end
    end
add_token_auth() click to toggle source
# File lib/voyage/app_builder.rb, line 541
    def add_token_auth
      inject_into_file 'app/models/user.rb', after: "class User < ApplicationRecord" do <<-'RUBY'.gsub(/^ {6}/, '')

        acts_as_token_authenticatable
        RUBY
      end

      generate 'migration add_authentication_token_to_users "authentication_token:string{30}:uniq"'

      # specs
      template '../templates/specs/features/user_impersonation_spec.rb', 'spec/features/user_impersonation_spec.rb', force: true
      template '../templates/specs/features/user_list_spec.rb', 'spec/features/user_list_spec.rb', force: true
      template '../templates/specs/features/user_signup_spec.rb', 'spec/features/user_signup_spec.rb', force: true
      template '../templates/specs/requests/user_api_spec.rb', 'spec/requests/user_api_spec.rb', force: true
      template '../templates/specs/support/api/schemas/user.json', 'spec/support/api/schemas/user.json', force: true
      template '../templates/config_initializers_ams.rb', 'config/initializers/ams.rb', force: true
      template '../templates/specs/support/matchers/api_schema_matcher.rb', 'spec/support/matchers/api_schema_matcher.rb', force: true
      template '../templates/specs/mailers/application_mailer_spec.rb.erb', 'spec/mailers/application_mailer_spec.rb', force: true
      template '../templates/specs/support/features/session_helpers.rb', 'spec/support/features/session_helpers.rb', force: true
      template '../templates/specs/support/request_spec_helper.rb', 'spec/support/request_spec_helper.rb', force: true
    end
agree?(prompt) click to toggle source
# File lib/voyage/app_builder.rb, line 3
def agree?(prompt)
  puts prompt
  response = STDIN.gets.chomp

  response.empty? || %w(y yes).include?(response.downcase.strip)
end
authorize_devise_resource_for_index_action() click to toggle source
# File lib/voyage/app_builder.rb, line 334
    def authorize_devise_resource_for_index_action
      generate 'canard:ability user can:manage:user cannot:destroy:user'
      generate 'canard:ability admin can:destroy:user'

      %w(admins users).each do |resource_name|
        replace_in_file "spec/abilities/#{resource_name}_spec.rb", "require 'cancan/matchers'", "require_relative '../support/matchers/custom_cancan'"
      end

      find = <<-RUBY.gsub(/^ {4}/, '')
        it { is_expected.to be_able_to(:manage, user) }
      RUBY
      replace = <<-RUBY.gsub(/^ {4}/, '')
        it { is_expected.to be_able_to(:manage, acting_user) }
        it { is_expected.to_not be_able_to(:manage, user) }
      RUBY
      replace_in_file 'spec/abilities/users_spec.rb', find, replace

      find = <<-RUBY.gsub(/^ {6}/, '')
        can [:manage], User
      RUBY
      replace = <<-RUBY.gsub(/^ {6}/, '')
        can [:manage], User do |u|
          u == user
        end
      RUBY
      replace_in_file 'app/abilities/users.rb', find, replace

      generate 'migration add_roles_mask_to_users roles_mask:integer'
      template '../templates/custom_cancan_matchers.rb', 'spec/support/matchers/custom_cancan.rb'
    end
bundle_without_production() click to toggle source
# File lib/voyage/app_builder.rb, line 25
def bundle_without_production
  template '../templates/bundle_config', '.bundle/config'
end
configure_action_mailer() click to toggle source
# File lib/suspenders/app_builder.rb, line 312
def configure_action_mailer
  action_mailer_host "development", %{"localhost:3000"}
  action_mailer_host "test", %{"www.example.com"}
  action_mailer_host "production", %{ENV.fetch("APPLICATION_HOST")}
end
configure_action_mailer_in_specs() click to toggle source
# File lib/suspenders/app_builder.rb, line 287
def configure_action_mailer_in_specs
  copy_file 'action_mailer.rb', 'spec/support/action_mailer.rb'
end
configure_active_job() click to toggle source
# File lib/suspenders/app_builder.rb, line 318
def configure_active_job
  configure_application_file(
    "config.active_job.queue_adapter = :delayed_job"
  )
  configure_environment "test", "config.active_job.queue_adapter = :inline"
end
configure_automatic_deployment() click to toggle source
# File lib/suspenders/app_builder.rb, line 382
    def configure_automatic_deployment
      deploy_command = <<-YML.strip_heredoc
      deployment:
        staging:
          branch: master
          commands:
            - bin/deploy staging
      YML

      append_file "circle.yml", deploy_command
    end
configure_background_jobs_for_rspec() click to toggle source
# File lib/suspenders/app_builder.rb, line 283
def configure_background_jobs_for_rspec
  run 'rails g delayed_job:active_record'
end
configure_capybara_webkit() click to toggle source
# File lib/suspenders/app_builder.rb, line 291
def configure_capybara_webkit
  copy_file "capybara_webkit.rb", "spec/support/capybara_webkit.rb"
end
configure_ci() click to toggle source
# File lib/suspenders/app_builder.rb, line 270
def configure_ci
  template "circle.yml.erb", "circle.yml"
end
configure_generators() click to toggle source
# File lib/suspenders/app_builder.rb, line 105
    def configure_generators
      config = <<-RUBY

    config.generators do |generate|
      generate.helper false
      generate.javascripts false
      generate.request_specs false
      generate.routing_specs false
      generate.stylesheets false
      generate.test_framework :rspec
      generate.view_specs false
    end

      RUBY

      inject_into_class 'config/application.rb', 'Application', config
    end
configure_i18n_for_missing_translations() click to toggle source
# File lib/suspenders/app_builder.rb, line 278
def configure_i18n_for_missing_translations
  raise_on_missing_translations_in("development")
  raise_on_missing_translations_in("test")
end
configure_i18n_for_test_environment() click to toggle source
# File lib/suspenders/app_builder.rb, line 274
def configure_i18n_for_test_environment
  copy_file "i18n.rb", "spec/support/i18n.rb"
end
configure_quiet_assets() click to toggle source
# File lib/suspenders/app_builder.rb, line 88
    def configure_quiet_assets
      config = <<-RUBY
    config.assets.quiet = true
      RUBY

      inject_into_class "config/application.rb", "Application", config
    end
configure_rack_timeout() click to toggle source
# File lib/suspenders/app_builder.rb, line 300
    def configure_rack_timeout
      rack_timeout_config = <<-RUBY
Rack::Timeout.timeout = (ENV["RACK_TIMEOUT"] || 10).to_i
      RUBY

      append_file "config/environments/production.rb", rack_timeout_config
    end
configure_rspec() click to toggle source
# File lib/suspenders/app_builder.rb, line 263
def configure_rspec
  remove_file "spec/rails_helper.rb"
  remove_file "spec/spec_helper.rb"
  copy_file "rails_helper.rb", "spec/rails_helper.rb"
  copy_file "spec_helper.rb", "spec/spec_helper.rb"
end
configure_rvm_prepend_bin_to_path() click to toggle source
# File lib/voyage/app_builder.rb, line 797
    def configure_rvm_prepend_bin_to_path
      run "rm -f $rvm_path/hooks/after_cd_bundler"

      run "touch $rvm_path/hooks/after_cd_bundler"

      git_safe_dir = <<-RUBY.gsub(/^ {8}/, '')
        #!/usr/bin/env bash
        export PATH=".git/safe/../../bin:$PATH"
        RUBY

      run "echo '#{git_safe_dir}' >> $rvm_path/hooks/after_cd_bundler"

      run 'chmod +x $rvm_path/hooks/after_cd_bundler'

      run 'mkdir -p .git/safe'
    end
configure_simple_form() click to toggle source
# File lib/suspenders/app_builder.rb, line 308
def configure_simple_form
  bundle_command "exec rails generate simple_form:install"
end
configure_smtp() click to toggle source
# File lib/suspenders/app_builder.rb, line 135
    def configure_smtp
      copy_file 'smtp.rb', 'config/smtp.rb'

      prepend_file 'config/environments/production.rb',
        %{require Rails.root.join("config/smtp")\n}

      config = <<-RUBY

  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = SMTP_SETTINGS
      RUBY

      inject_into_file 'config/environments/production.rb', config,
        after: "config.action_mailer.raise_delivery_errors = false"
    end
configure_spec_support_features() click to toggle source
# File lib/suspenders/app_builder.rb, line 258
def configure_spec_support_features
  empty_directory_with_keep_file 'spec/features'
  empty_directory_with_keep_file 'spec/support/features'
end
configure_time_formats() click to toggle source
# File lib/suspenders/app_builder.rb, line 295
def configure_time_formats
  remove_file "config/locales/en.yml"
  template "config_locales_en.yml.erb", "config/locales/en.yml"
end
copy_dotfiles() click to toggle source
# File lib/suspenders/app_builder.rb, line 351
def copy_dotfiles
  directory("dotfiles", ".")
end
copy_env_to_example() click to toggle source
# File lib/voyage/app_builder.rb, line 818
def copy_env_to_example
  run 'cp .env .env.example'
end
copy_miscellaneous_files() click to toggle source
# File lib/suspenders/app_builder.rb, line 412
def copy_miscellaneous_files
  copy_file "browserslist", "browserslist"
  copy_file "errors.rb", "config/initializers/errors.rb"
  copy_file "json_encoding.rb", "config/initializers/json_encoding.rb"
end
create_application_layout() click to toggle source
# File lib/suspenders/app_builder.rb, line 218
def create_application_layout
  template 'suspenders_layout.html.erb.erb',
    'app/views/layouts/application.html.erb',
    force: true
end
create_database() click to toggle source
# File lib/suspenders/app_builder.rb, line 229
def create_database
  bundle_command 'exec rake db:create db:migrate'
end
create_deploy_script() click to toggle source
# File lib/suspenders/app_builder.rb, line 364
    def create_deploy_script
      copy_file "bin_deploy", "bin/deploy"

      instructions = <<-MARKDOWN

## Deploying

If you have previously run the `./bin/setup` script,
you can deploy to staging and production with:

    % ./bin/deploy staging
    % ./bin/deploy production
      MARKDOWN

      append_file "README.md", instructions
      run "chmod a+x bin/deploy"
    end
create_github_repo(repo_name) click to toggle source
# File lib/suspenders/app_builder.rb, line 394
def create_github_repo(repo_name)
  run "hub create #{repo_name}"
end
create_heroku_apps(flags) click to toggle source
# File lib/suspenders/app_builder.rb, line 359
def create_heroku_apps(flags)
  create_staging_heroku_app(flags)
  create_production_heroku_app(flags)
end
create_partials_directory() click to toggle source
# File lib/suspenders/app_builder.rb, line 198
def create_partials_directory
  empty_directory 'app/views/application'
end
create_shared_css_overrides() click to toggle source
# File lib/suspenders/app_builder.rb, line 211
def create_shared_css_overrides
  copy_file(
    "_css_overrides.html.erb",
    "app/views/application/_css_overrides.html.erb",
  )
end
create_shared_flashes() click to toggle source
# File lib/suspenders/app_builder.rb, line 202
def create_shared_flashes
  copy_file "_flashes.html.erb", "app/views/application/_flashes.html.erb"
  copy_file "flashes_helper.rb", "app/helpers/flashes_helper.rb"
end
create_shared_javascripts() click to toggle source
# File lib/suspenders/app_builder.rb, line 207
def create_shared_javascripts
  copy_file '_javascript.html.erb', 'app/views/application/_javascript.html.erb'
end
customize_application_controller_for_devise(adding_first_and_last_name, devise_token_auth) click to toggle source
# File lib/voyage/app_builder.rb, line 163
    def customize_application_controller_for_devise(adding_first_and_last_name, devise_token_auth)
      inject_into_file 'app/controllers/application_controller.rb', before: "class ApplicationController < ActionController::Base" do <<-RUBY.gsub(/^ {8}/, '')
        # rubocop:disable Metrics/ClassLength, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/LineLength
        RUBY
      end

      inject_into_file 'app/controllers/application_controller.rb', after: "  protect_from_forgery with: :exception" do <<-RUBY.gsub(/^ {6}/, '')

        check_authorization unless: :devise_or_pages_controller?
        impersonates :user
        #{'acts_as_token_authentication_handler_for User' if devise_token_auth}

        before_action :configure_permitted_parameters, if: :devise_controller?
        before_action :authenticate_user!, unless: -> { is_a?(HighVoltage::PagesController) }
        before_action :add_layout_name_to_gon
        before_action :detect_device_type

        rescue_from CanCan::AccessDenied do |exception|
          redirect_to root_path, alert: exception.message
        end

        # Example Traditional Event: analytics_track(user, 'Created Widget', { widget_name: 'foo' })
        # Example Page View:         analytics_track(user, 'Page Viewed', { page_name: 'Terms and Conditions', url: '/terms' })
        #
        # NOTE: setup some defaults that we want to track on every event mixpanel_track
        # NOTE: the identify step happens on every page load to keep intercom.io and mixpanel people up to date
        def analytics_track(user, event_name, options = {})
          return if user.tester?

          sanitized_options = sanitize_hash_javascript(options)

          segment_attributes = {
            user_id: user.uuid,
            event: event_name,
            properties: {
              browser:         "\#{browser.name rescue 'unknown'}",
              browser_id:      "\#{browser.id rescue 'unknown'}",
              browser_version: "\#{browser.version rescue 'unknown'}",
              platform:        "\#{browser.platform rescue 'unknown'}",
              roles:           "\#{user.roles.map(&:to_s).join(',') rescue ''}",
              rails_env:       Rails.env.to_s,
            }.merge(sanitized_options),
          }

          Analytics.track(segment_attributes)
        end

        protected

        def devise_or_pages_controller?
          devise_controller? == true || is_a?(HighVoltage::PagesController)
        end

        def sanitize_hash_javascript(hash)
          hash.deep_stringify_keys
              .deep_transform_keys { |k| sanitize_javascript(k) }
              .transform_values    { |v| sanitize_javascript(v) }
        end

        def sanitize_javascript(value)
          value.is_a?(String) ? ActionView::Base.new.escape_javascript(value) : value
        end

        def configure_permitted_parameters
          devise_parameter_sanitizer.permit(
            :sign_up,
            keys: [
              #{':first_name,' if adding_first_and_last_name}
              #{':last_name,' if adding_first_and_last_name}
              :email,
              :password,
              :password_confirmation,
              :remember_me,
            ],
          )

          devise_parameter_sanitizer.permit(
            :sign_in,
            keys: [
              :login, :email, :password, :remember_me
            ],
          )

          devise_parameter_sanitizer.permit(
            :account_update,
            keys: [
              #{':first_name,' if adding_first_and_last_name}
              #{':last_name,' if adding_first_and_last_name}
              :email,
              :password,
              :password_confirmation,
              :current_password,
            ],
          )
        end

        def add_layout_name_to_gon
          gon.layout =
            case devise_controller?
            when true
              'devise'
            else
              'application'
            end
        end

        def detect_device_type
          request.variant =
            case request.user_agent
            when /iPad/i
              :tablet
            when /iPhone/i
              :phone
            when /Android/i && /mobile/i
              :phone
            when /Android/i
              :tablet
            when /Windows Phone/i
              :phone
            end
        end
        RUBY
      end
    end
customize_application_js() click to toggle source
# File lib/voyage/app_builder.rb, line 583
    def customize_application_js
      template '../templates/application.js', 'app/assets/javascripts/application.js', force: true

      inject_into_file 'app/views/application/_javascript.html.erb', after: '<%= render "analytics" %>' do <<-RUBY.gsub(/^ {8}/, '')

        <%= render "analytics_identify" %>
      RUBY
      end
    end
customize_application_mailer() click to toggle source
# File lib/voyage/app_builder.rb, line 777
def customize_application_mailer
  template '../templates/application_mailer.rb.erb', 'app/mailers/application_mailer.rb', force: true
end
customize_devise_views() click to toggle source
# File lib/voyage/app_builder.rb, line 143
    def customize_devise_views
      %w(edit new).each do |file|
        if @@use_slim
          file_path = "app/views/devise/registrations/#{file}.html.slim"
          inject_into_file file_path, before: "    = f.input :email, required: true, autofocus: true" do <<-'RUBY'.gsub(/^ {8}/, '')
            = f.input :first_name, required: true, autofocus: true
            = f.input :last_name, required: true
            RUBY
          end
        else
          file_path = "app/views/devise/registrations/#{file}.html.erb"
          inject_into_file file_path, before: "    <%= f.input :email, required: true, autofocus: true %>" do <<-'RUBY'.gsub(/^ {8}/, '')
            <%= f.input :first_name, required: true, autofocus: true %>
            <%= f.input :last_name, required: true %>
            RUBY
          end
        end
      end
    end
customize_error_pages() click to toggle source
# File lib/suspenders/app_builder.rb, line 418
    def customize_error_pages
      meta_tags =<<-EOS
  <meta charset="utf-8" />
  <meta name="ROBOTS" content="NOODP" />
  <meta name="viewport" content="initial-scale=1" />
      EOS

      %w(500 404 422).each do |page|
        inject_into_file "public/#{page}.html", meta_tags, after: "<head>\n"
        replace_in_file "public/#{page}.html", /<!--.+-->\n/, ''
      end
    end
customize_resource_controller_for_devise(adding_first_and_last_name) click to toggle source
# File lib/voyage/app_builder.rb, line 299
    def customize_resource_controller_for_devise(adding_first_and_last_name)
      bundle_command 'exec rails generate controller users'
      run 'rm spec/controllers/users_controller_spec.rb'

      inject_into_class 'app/controllers/users_controller.rb', 'UsersController' do <<-RUBY.gsub(/^ {6}/, '')
        # https://github.com/CanCanCommunity/cancancan/wiki/authorizing-controller-actions
        # load_and_authorize_resource only: []
        skip_authorization_check only: [:analytics_alias]

        def analytics_alias
          # view file has JS that will identify the anonymous user through segment
          # after registration via "after devise registration path"
        end
        RUBY
      end
    end
customize_user_factory(adding_first_and_last_name) click to toggle source
# File lib/voyage/app_builder.rb, line 460
    def customize_user_factory(adding_first_and_last_name)
      inject_into_file 'spec/factories/users.rb', before: /^  end/ do <<-'RUBY'.gsub(/^ {4}/, '')
        password 'asdfjkl123'
        password_confirmation 'asdfjkl123'
        sequence(:email) { |n| "user_#{n}@example.com" }

        trait :admin do
          roles [:admin]
          email 'admin@example.com'
        end
        RUBY
      end

      if adding_first_and_last_name
        inject_into_file 'spec/factories/users.rb', after: /roles \[:admin\]\n/ do <<-'RUBY'.gsub(/^ {4}/, '')
          first_name 'Admin'
          last_name 'User'
          RUBY
        end
      end
    end
customize_user_spec() click to toggle source
# File lib/voyage/app_builder.rb, line 491
    def customize_user_spec
      find = <<-RUBY.gsub(/^ {6}/, '')
        pending "add some examples to (or delete) \#{__FILE__}"
      RUBY

      replace = <<-RUBY.gsub(/^ {6}/, '')
        describe 'constants' do
          context 'roles' do
            it 'has the admin role' do
              expect(User::ROLES).to eq([:admin])
            end
          end
        end

        describe 'validations' do
          it { is_expected.to validate_presence_of(:email) }
          it { is_expected.to validate_presence_of(:password) }
          it { is_expected.to validate_presence_of(:password_confirmation) }
        end

        context '#tester?' do
          ['example.com', 'headway.io'].each do |domain|
            it "an email including the \#{domain} domain is a tester" do
              user = build(:user, email: "asdf@\#{domain}")
              expect(user.tester?).to eq(true)
            end
          end

          it 'an email including the gmail.com domain is NOT a tester' do
            user = build(:user, email: 'asdf@gmail.com')
            expect(user.tester?).to eq(false)
          end
        end

        context 'new user creation' do
          it 'ensures uniqueness of the uuid' do
            allow(User).to receive(:exists?).and_return(true, false)

            expect do
              create(:user)
            end.to change { User.count }.by(1)

            expect(User).to have_received(:exists?).exactly(2).times
          end
        end
      RUBY

      replace_in_file 'spec/models/user_spec.rb', find, replace
    end
disallow_wrapping_parameters() click to toggle source
# File lib/suspenders/app_builder.rb, line 194
def disallow_wrapping_parameters
  remove_file "config/initializers/wrap_parameters.rb"
end
downgrade_neat_1_8_so_refills_media_mixin_works() click to toggle source

TEMP FIX

https://github.com/thoughtbot/bourbon/issues/993
https://github.com/thoughtbot/refills/issues/400

# File lib/voyage/app_builder.rb, line 638
def downgrade_neat_1_8_so_refills_media_mixin_works
  replace_in_file 'Gemfile', "gem 'neat', '~> 2.0.0.beta.1'", "gem 'neat', '~> 1.8.0'"
  run 'gem uninstall -x neat -v2.0.0'
  run 'bundle'
end
enable_database_cleaner() click to toggle source
# File lib/suspenders/app_builder.rb, line 247
def enable_database_cleaner
  copy_file 'database_cleaner_rspec.rb', 'spec/support/database_cleaner.rb'
end
enable_rack_canonical_host() click to toggle source
# File lib/suspenders/app_builder.rb, line 151
    def enable_rack_canonical_host
      config = <<-RUBY

  if ENV.fetch("HEROKU_APP_NAME", "").include?("staging-pr-")
    ENV["APPLICATION_HOST"] = ENV["HEROKU_APP_NAME"] + ".herokuapp.com"
  end

  config.middleware.use Rack::CanonicalHost, ENV.fetch("APPLICATION_HOST")
      RUBY

      inject_into_file(
        "config/environments/production.rb",
        config,
        after: "Rails.application.configure do",
      )
    end
enable_rack_deflater() click to toggle source
# File lib/suspenders/app_builder.rb, line 168
def enable_rack_deflater
  configure_environment "production", "config.middleware.use Rack::Deflater"
end
gemfile() click to toggle source
# File lib/suspenders/app_builder.rb, line 27
def gemfile
  template "Gemfile.erb", "Gemfile"
end
generate_data_migrations() click to toggle source
# File lib/voyage/app_builder.rb, line 606
def generate_data_migrations
  generate 'data_migrations:install'

  file = Dir['db/migrate/*_create_data_migrations.rb'].first
  replace_in_file file, 'class CreateDataMigrations < ActiveRecord::Migration', "class CreateDataMigrations < ActiveRecord::Migration[4.2]"

  empty_directory_with_keep_file 'db/data_migrate'
end
generate_factories_file() click to toggle source
# File lib/suspenders/app_builder.rb, line 127
def generate_factories_file
  copy_file "factories.rb", "spec/factories.rb"
end
generate_refills() click to toggle source

ADDING REFILLS COMPONENTS


# File lib/voyage/app_builder.rb, line 651
def generate_refills
  if @@accept_defaults || agree?('Would you like to install default Refill components? (Y/n)')
    @@add_refills = true

    bundle_command 'exec rails generate refills:import navigation'
    bundle_command 'exec rails generate refills:import footer'

    add_admin_links_to_navigation

    add_refills_to_stylesheets
  else
    @@add_refills = false
  end
end
generate_rspec() click to toggle source
# File lib/suspenders/app_builder.rb, line 325
def generate_rspec
  generate 'rspec:install'
end
generate_ruby_version_and_gemset() click to toggle source
# File lib/voyage/app_builder.rb, line 602
def generate_ruby_version_and_gemset
  create_file '.ruby-gemset', "#{app_name}\n"
end
generate_seeder_templates(using_devise:) click to toggle source

END DEVISE SETUP


# File lib/voyage/app_builder.rb, line 485
def generate_seeder_templates(using_devise:)
  config = { force: true, using_devise: using_devise }
  template '../templates/seeder.rb.erb', 'lib/seeder.rb', config
  template '../templates/seeds.rb.erb', 'db/seeds.rb', config
end
generate_test_environment() click to toggle source

END ADDING REFILLS COMPONENTS


# File lib/voyage/app_builder.rb, line 737
def generate_test_environment
  template '../templates/controller_helpers.rb', 'spec/support/controller_helpers.rb'
  template '../templates/simplecov.rb', '.simplecov'
end
gitignore() click to toggle source
# File lib/suspenders/app_builder.rb, line 23
def gitignore
  copy_file "suspenders_gitignore", ".gitignore"
end
init_git() click to toggle source
# File lib/suspenders/app_builder.rb, line 355
def init_git
  run 'git init'
end
install_devise() click to toggle source

DEVISE SETUP


# File lib/voyage/app_builder.rb, line 90
def install_devise
  if @@accept_defaults || agree?('Would you like to install Devise? (Y/n)')
    @@use_devise = true

    if @@accept_defaults || agree?('Would you like to install Devise token authentication? (Y/n)')
      devise_token_auth = true
    end

    bundle_command 'exec rails generate devise:install'

    if @@accept_defaults || agree?("Would you like to add first_name and last_name to the devise model? (Y/n)")
      adding_first_and_last_name = true

      bundle_command "exec rails generate resource user first_name:string last_name:string uuid:string"

      replace_in_file 'spec/factories/users.rb',
        'first_name "MyString"', 'first_name { Faker::Name.first_name }'
      replace_in_file 'spec/factories/users.rb',
        'last_name "MyString"', 'last_name { Faker::Name.last_name }'
      replace_in_file 'spec/factories/users.rb',
        'uuid "MyString"', 'uuid { SecureRandom.uuid }'
    end

    bundle_command "exec rails generate devise user"
    bundle_command 'exec rails generate devise:views'

    if @@use_slim
      inside('lib') do # arbitrary, run in context of newly generated app
        run "erb2slim '../app/views/devise' '../app/views/devise'"
        run "erb2slim -d '../app/views/devise'"
      end
    end

    customize_devise_views if adding_first_and_last_name
    customize_application_controller_for_devise(adding_first_and_last_name, devise_token_auth)
    add_devise_registrations_controller
    customize_resource_controller_for_devise(adding_first_and_last_name)
    add_admin_views_for_devise_resource(adding_first_and_last_name)
    add_analytics_initializer
    authorize_devise_resource_for_index_action
    add_canard_roles_to_devise_resource
    update_devise_initializer
    add_custom_routes_for_devise
    customize_user_factory(adding_first_and_last_name)
    generate_seeder_templates(using_devise: true)
    customize_user_spec
    add_token_auth
  else
    @@use_devise = false
    generate_seeder_templates(using_devise: false)
  end
end
overwrite_application_layout() click to toggle source
# File lib/voyage/app_builder.rb, line 865
def overwrite_application_layout
  template '../templates/voyage_layout.html.erb.erb', 'app/views/layouts/application.html.erb', force: true, add_refills: @@add_refills
  update_application_layout_for_slim if @@use_slim

  template '../templates/analytics_identify.html.erb.erb', 'app/views/application/_analytics_identify.html.erb', force: true
end
provide_dev_prime_task() click to toggle source
# File lib/suspenders/app_builder.rb, line 101
def provide_dev_prime_task
  copy_file 'dev.rake', 'lib/tasks/dev.rake'
end
provide_setup_script() click to toggle source
# File lib/suspenders/app_builder.rb, line 96
def provide_setup_script
  template "bin_setup", "bin/setup", force: true
  run "chmod a+x bin/setup"
end
provide_shoulda_matchers_config() click to toggle source
# File lib/suspenders/app_builder.rb, line 251
def provide_shoulda_matchers_config
  copy_file(
    "shoulda_matchers_config_rspec.rb",
    "spec/support/shoulda_matchers.rb"
  )
end
raise_on_delivery_errors() click to toggle source
# File lib/suspenders/app_builder.rb, line 42
def raise_on_delivery_errors
  replace_in_file 'config/environments/development.rb',
    'raise_delivery_errors = false', 'raise_delivery_errors = true'
end
raise_on_missing_assets_in_test() click to toggle source
# File lib/suspenders/app_builder.rb, line 38
def raise_on_missing_assets_in_test
  configure_environment "test", "config.assets.raise_runtime_errors = true"
end
raise_on_unpermitted_parameters() click to toggle source
# File lib/suspenders/app_builder.rb, line 80
    def raise_on_unpermitted_parameters
      config = <<-RUBY
    config.action_controller.action_on_unpermitted_parameters = :raise
      RUBY

      inject_into_class "config/application.rb", "Application", config
    end
rake_db_setup() click to toggle source

Do this last

# File lib/voyage/app_builder.rb, line 792
def rake_db_setup
  rake 'db:migrate'
  rake 'db:seed' if File.exist?('config/initializers/devise.rb')
end
readme() click to toggle source
# File lib/suspenders/app_builder.rb, line 19
def readme
  template 'README.md.erb', 'README.md'
end
remove_config_comment_lines() click to toggle source
# File lib/suspenders/app_builder.rb, line 431
def remove_config_comment_lines
  config_files = [
    "application.rb",
    "environment.rb",
    "environments/development.rb",
    "environments/production.rb",
    "environments/test.rb",
  ]

  config_files.each do |config_file|
    path = File.join(destination_root, "config/#{config_file}")

    accepted_content = File.readlines(path).reject do |line|
      line =~ /^.*#.*$/ || line =~ /^$\n/
    end

    File.open(path, "w") do |file|
      accepted_content.each { |line| file.puts line }
    end
  end
end
remove_routes_comment_lines() click to toggle source
# File lib/suspenders/app_builder.rb, line 453
def remove_routes_comment_lines
  replace_in_file 'config/routes.rb',
    /Rails\.application\.routes\.draw do.*end/m,
    "Rails.application.routes.draw do\nend"
end
replace_default_puma_configuration() click to toggle source
# File lib/suspenders/app_builder.rb, line 329
def replace_default_puma_configuration
  copy_file "puma.rb", "config/puma.rb", force: true
end
replace_gemfile(path) click to toggle source
# File lib/suspenders/app_builder.rb, line 233
def replace_gemfile(path)
  template 'Gemfile.erb', 'Gemfile', force: true do |content|
    if path
      content.gsub(%r{gem .suspenders.}) { |s| %{#{s}, path: "#{path}"} }
    else
      content
    end
  end
end
require_files_in_lib() click to toggle source
# File lib/voyage/app_builder.rb, line 593
    def require_files_in_lib
      create_file 'config/initializers/require_files_in_lib.rb' do <<-RUBY.gsub(/^ {8}/, '')
        # rubocop:disable Rails/FilePath
        Dir[File.join(Rails.root, 'lib', '**', '*.rb')].each { |l| require l }
        # rubocop:enable Rails/FilePath
        RUBY
      end
    end
run_rubocop_auto_correct() click to toggle source
# File lib/voyage/app_builder.rb, line 814
def run_rubocop_auto_correct
  run 'rubocop --auto-correct'
end
set_ruby_to_version_being_used() click to toggle source
# File lib/suspenders/app_builder.rb, line 243
def set_ruby_to_version_being_used
  create_file '.ruby-version', "#{Suspenders::RUBY_VERSION}\n"
end
set_test_delivery_method() click to toggle source
# File lib/suspenders/app_builder.rb, line 55
def set_test_delivery_method
  inject_into_file(
    "config/environments/development.rb",
    "\n  config.action_mailer.delivery_method = :file",
    after: "config.action_mailer.raise_delivery_errors = true",
  )
end
set_up_factory_girl_for_rspec() click to toggle source
# File lib/suspenders/app_builder.rb, line 123
def set_up_factory_girl_for_rspec
  copy_file 'factory_girl_rspec.rb', 'spec/support/factory_girl.rb'
end
set_up_forego() click to toggle source
# File lib/suspenders/app_builder.rb, line 333
def set_up_forego
  copy_file "Procfile", "Procfile"
end
set_up_hound() click to toggle source
# File lib/suspenders/app_builder.rb, line 131
def set_up_hound
  copy_file "hound.yml", ".hound.yml"
end
setup_asset_host() click to toggle source
# File lib/suspenders/app_builder.rb, line 172
    def setup_asset_host
      replace_in_file 'config/environments/production.rb',
        "# config.action_controller.asset_host = 'http://assets.example.com'",
        'config.action_controller.asset_host = ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))'

      replace_in_file 'config/initializers/assets.rb',
        "config.assets.version = '1.0'",
        'config.assets.version = (ENV["ASSETS_VERSION"] || "1.0")'

      config = <<-EOD
config.public_file_server.headers = {
    "Cache-Control" => "public, max-age=31557600",
  }
      EOD

      configure_environment("production", config)
    end
setup_bundler_audit() click to toggle source
# File lib/suspenders/app_builder.rb, line 403
def setup_bundler_audit
  copy_file "bundler_audit.rake", "lib/tasks/bundler_audit.rake"
  append_file "Rakefile", %{\ntask default: "bundle:audit"\n}
end
setup_default_directories() click to toggle source
# File lib/suspenders/app_builder.rb, line 337
def setup_default_directories
  [
    'app/views/pages',
    'spec/lib',
    'spec/controllers',
    'spec/helpers',
    'spec/support/matchers',
    'spec/support/mixins',
    'spec/support/shared_examples'
  ].each do |dir|
    empty_directory_with_keep_file dir
  end
end
setup_default_rake_task() click to toggle source
# File lib/suspenders/app_builder.rb, line 459
    def setup_default_rake_task
      append_file 'Rakefile' do
        <<-EOS
task(:default).clear
task default: [:spec]

if defined? RSpec
  task(:spec).clear
  RSpec::Core::RakeTask.new(:spec) do |t|
    t.verbose = false
  end
end
        EOS
      end
    end
setup_rack_mini_profiler() click to toggle source
# File lib/suspenders/app_builder.rb, line 31
def setup_rack_mini_profiler
  copy_file(
    "rack_mini_profiler.rb",
    "config/initializers/rack_mini_profiler.rb",
  )
end
setup_secret_token() click to toggle source
# File lib/suspenders/app_builder.rb, line 190
def setup_secret_token
  template 'secrets.yml', 'config/secrets.yml', force: true
end
setup_segment() click to toggle source
# File lib/suspenders/app_builder.rb, line 398
def setup_segment
  copy_file '_analytics.html.erb',
    'app/views/application/_analytics.html.erb'
end
setup_spring() click to toggle source
# File lib/suspenders/app_builder.rb, line 408
def setup_spring
  bundle_command "exec spring binstub --all"
end
spin_up_webpacker() click to toggle source
# File lib/voyage/app_builder.rb, line 834
def spin_up_webpacker
  rake 'webpacker:install'
  rake 'webpacker:install:react'
end
update_application_css_file() click to toggle source
# File lib/voyage/app_builder.rb, line 693
    def update_application_css_file
      inject_into_file 'app/assets/stylesheets/application.scss', before: '@import "neat";'  do <<-RUBY.gsub(/^ {8}/, '')
        $visual-grid: true;
        $visual-grid-color: #9cf !default;
        $visual-grid-index: front !default;
        $visual-grid-opacity: 0.1 !default;
        RUBY
      end
    end
update_application_layout_for_slim() click to toggle source
# File lib/voyage/app_builder.rb, line 40
    def update_application_layout_for_slim
      find = <<-RUBY.gsub(/^ {4}/, '')
        <%#
          Configure default and controller-, and view-specific titles in
          config/locales/en.yml. For more see:
          https://github.com/calebthompson/title#usage
        %>
      RUBY

      replace = <<-RUBY.gsub(/^ {8}/, '')
          <% # Configure default and controller-, and view-specific titles in
        # config/locales/en.yml. For more see:
        # https://github.com/calebthompson/title#usage %>
      RUBY

      replace_in_file 'app/views/layouts/application.html.erb', find, replace

      inside('lib') do # arbitrary, run in context of newly generated app
        run "erb2slim '../app/views/layouts' '../app/views/layouts'"
        run "erb2slim -d '../app/views/layouts'"
      end

      # strip trailing space after closing "> in application layout before
      # trying to find and replace it
      replace_in_file 'app/views/layouts/application.html.slim', '| "> ', '| ">'

      find = <<-RUBY.gsub(/^ {6}/, '')
        |  <body class="
        = devise_controller? ? 'devise' : 'application'
        = body_class
        | ">
      RUBY

      replace = <<-RUBY.gsub(/^ {6}/, '')
        body class="\#{devise_controller? ? 'devise' : 'application'} \#{body_class}"
      RUBY

      replace_in_file 'app/views/layouts/application.html.slim', find, replace
    end
update_application_rb_for_slim() click to toggle source
# File lib/voyage/app_builder.rb, line 80
    def update_application_rb_for_slim
      inject_into_file "config/application.rb", after: "     g.fixture_replacement :factory_girl, dir: 'spec/factories'\n" do <<-'RUBY'.gsub(/^ {2}/, '')
        g.template_engine :slim
        RUBY
      end
    end
update_devise_initializer() click to toggle source
# File lib/voyage/app_builder.rb, line 408
def update_devise_initializer
  replace_in_file 'config/initializers/devise.rb',
    'config.sign_out_via = :delete', 'config.sign_out_via = :get'

  replace_in_file 'config/initializers/devise.rb',
    "config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'",
    "config.mailer_sender = 'user@example.com'"
end
update_flashes_css_file() click to toggle source
# File lib/voyage/app_builder.rb, line 689
def update_flashes_css_file
  replace_in_file 'app/assets/stylesheets/refills/_flashes.scss', "margin-bottom: $base-spacing / 2;", "// margin-bottom: $base-spacing / 2;"
end
update_gemset_in_gemfile() click to toggle source
# File lib/voyage/app_builder.rb, line 18
def update_gemset_in_gemfile
  replace_in_file 'Gemfile', '#ruby-gemset', "#ruby-gemset=#{app_name}"

  # Remove commented out lines from template
  gsub_file('Gemfile', /^\s{2}\n/, '')
end
update_test_environment() click to toggle source
# File lib/voyage/app_builder.rb, line 742
    def update_test_environment
      inject_into_file 'spec/support/factory_girl.rb', before: /^end/ do <<-RUBY.gsub(/^ {6}/, '')

        # Spring doesn't reload factory_girl
        config.before(:all) do
          FactoryGirl.reload
        end
        RUBY
      end

      template "../templates/rails_helper.rb.erb", "spec/rails_helper.rb", force: true

      %w(test development).each do |environment|
        inject_into_file "config/environments/#{environment}.rb", after: /^end/ do <<-RUBY.gsub(/^ {10}/, '')

          # NOTE: console can use create(:factory_name), or build(:factory_name) without
          # needing to use FactoryGirl.create(:factory_name).
          include FactoryGirl::Syntax::Methods
          RUBY
        end
      end
    end
use_postgres_config_template() click to toggle source
# File lib/suspenders/app_builder.rb, line 224
def use_postgres_config_template
  template 'postgresql_database.yml.erb', 'config/database.yml',
    force: true
end
use_slim() click to toggle source
# File lib/voyage/app_builder.rb, line 29
def use_slim
  if @@accept_defaults || agree?('Would you like to use slim? (Y/n)')
    @@use_slim = true
    run 'gem install html2slim'
    update_application_rb_for_slim
  else
    @@use_slim = false
    gsub_file('Gemfile', /^gem 'slim-rails'\n/, '')
  end
end

Private Instance Methods

heroku_adapter() click to toggle source
# File lib/suspenders/app_builder.rb, line 483
def heroku_adapter
  @heroku_adapter ||= Adapters::Heroku.new(self)
end
raise_on_missing_translations_in(environment) click to toggle source
# File lib/suspenders/app_builder.rb, line 477
def raise_on_missing_translations_in(environment)
  config = 'config.action_view.raise_on_missing_translations = true'

  uncomment_lines("config/environments/#{environment}.rb", config)
end