Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 4f03b4dd1a
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 279 lines (228 sloc) 9.674 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
#!/usr/bin/env ruby
# Load bundler
begin
  require 'rubygems'
  require 'bundler'
rescue LoadError
  puts "\n=== ACTION REQUIRED ===\n\n"
  puts "Could not load the bundler gem. This is a required dependency of Refinery CMS."
  puts "Please install it with `gem install bundler`.\n\n"
  exit
end

# Load other required libraries
require 'pathname'
require 'fileutils'
require 'optparse'

# Load refinerycms
require Pathname.new(File.expand_path(File.dirname(__FILE__) << "/..")).join('vendor', 'refinerycms', 'refinery.rb')

module Refinery
  class AppGenerator

    def initialize(input)
      # Default options
      @input = input
      @options = {
        :database => 'sqlite3',
        :force => false,
        :heroku => false,
        :gems => []
      }

      @optparse = OptionParser.new do |opts|
        opts.banner = "Purpose: Installs Refinery CMS to the specified directory"
        opts.banner << "\nUsage: #{opts.program_name} /path/to/project [options]"

        opts.separator ""
        opts.separator "Specific Options:"

        # Rails supports more options, but Refinery is only tested on these three
        databases = %w(mysql postgresql sqlite3)
        opts.on("-d DATABASE", "--database DATABASE", databases, "Select the database (default sqlite3)", " #{databases.join('/')}") do |db|
          @options[:database] = db
        end

        opts.on("-g", "--gems gem1,gem2,gem3", Array, "Additional gems to install") do |gems|
          @options[:gems] = gems.reject {|g| g.to_s =~ /^(refinerycms|rmagick)$/}.map {|g| "gem '#{g.to_s}'"}
        end

        opts.on("-f", "--force", "Force overwriting of directory") do
          @options[:force] = true
        end

        opts.on("--heroku [APP_NAME]", "Set up and deploy to Heroku") do |app_name|
          @options[:heroku] = app_name || ''
        end

        opts.separator ""
        opts.separator "Common options:"

        opts.on_tail("-h", "--help", "Display this screen") do
          puts opts
          exit
        end

        opts.on_tail("-v", "--version", "Display the version") do
          puts Refinery.version
          exit
        end
      end
    end

    def run!
      # Grab input and ensure that the path doesn't exist already and other checks.
      validate!

      # Generate a Refinery CMS application
      generate!

      # Add in the Refinery specifics to the application
      refine!

      # Output helpful messages to user
      output!
    end


    def validate!
      # Check for valid input
      begin
        @optparse.parse!(@input)
      rescue OptionParser::ParseError => pe
        puts pe
        puts "\n"
        puts @optparse
        exit
      end

      # Ensure only one path is specified
      unless @input.size == 1
        puts "Please specify a single path to install Refinery CMS"
        puts "\n"
        puts @optparse
        exit
      end

      # Get the name and path of the new application
      @app_path = Pathname.new(File.expand_path(@input.first))
      @app_name = @app_path.to_s.split(File::SEPARATOR).last

      if @app_path.directory? and @options[:force] == false
        puts "The directory '#{@app_path}' that you specified already exists."
        puts "Use --force to overwrite an existing directory."
        exit
      end
    end

    def generate!
      # Generate a rails application
      rails_command = "rails new \"#{@app_path}\""
      rails_command << " --database #{@options[:database]}"
      rails_command << " --force" if @options[:force]
      rails_command << " --skip-test-unit --skip-prototype"
      run_command(rails_command, {:cd => false})
      abort "Generating Rails application failed. Exiting..." unless $?.success?
    end

    def refine!
      # Overwrite rails defaults with refinery defaults
      %w(app db).each do |folder|
        FileUtils::cp_r Refinery.root.join(folder), @app_path, :verbose => false
      end

      # Copy in important files to the Rails app.
      %w(.gitignore readme.md license.md config/routes.rb config/settings.rb config/environments/production.rb
config/initializers/acts_as_indexed_config.rb config/initializers/field_with_error_fix.rb
config/initializers/fix_rack.rb).each do |file|
        FileUtils::cp_r Refinery.root.join(file), @app_path.join(file), :verbose => false if Refinery.root.join(file).exist?
      end

      # Copy stylesheets so they're ready to use.
      %w(application.css formatting.css home.css).map{|stylesheet|
        Refinery.root.join('vendor', 'refinerycms', 'core', 'public', 'stylesheets', stylesheet)
      }.each do |stylesheet|
        FileUtils::cp stylesheet.to_s,
                      @app_path.join('public', 'stylesheets').to_s,
                      :verbose => false if stylesheet.file?
      end

      # Remove default application layout, README, and public/index.html
      %w(README app/views/layouts/application.html.erb public/index.html).each do |remove|
FileUtils::rm @app_path.join(remove) if @app_path.join(remove).exist?
      end

      # Add refinery gems to the Gemfile
      gemfile_contents = File.read(Refinery.root.join('Gemfile'))
      refinery_gems = gemfile_contents.match(/# REFINERY CMS =+.*# END REFINERY CMS =+/m)[0]
      File.open(@app_path.join('Gemfile'), "a") do |f|
        f.write "\n" + refinery_gems + "\n\n# USER DEFINED\n" + @options[:gems].join("\n") + "\n# END USER DEFINED"
      end

      # Change from development to production
      find_and_replace('Gemfile', "gem 'refinerycms', :path => '.'",
                        "gem 'refinerycms', '~> #{Refinery.version}'")

      # Add in AWS-S3 for Heroku
      find_and_replace('Gemfile', "# gem 'aws-s3', :require => 'aws/s3'",
                        "gem 'aws-s3', :require => 'aws/s3'") if @options[:heroku]

      # Replace app constant with Refinery
      # TODO: Find a way around this
      camelized_app_name = @app_name.gsub(/\W/, '_').squeeze('_').gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
      camelized_app_name << "::Application" # We only want to replace occurances like App::Application.
      %w(Rakefile config.ru config/application.rb config/environment.rb
config/environments/development.rb config/environments/production.rb config/environments/test.rb
config/initializers/secret_token.rb config/initializers/session_store.rb).each do |hackable|
        find_and_replace(hackable, camelized_app_name, 'Refinery::Application')
        find_and_replace(hackable, "module #{camelized_app_name.split('::').first}", 'module Refinery')
      end

      # Remove development specific stuff from the .gitinore file
      find_and_replace('.gitignore',
                       /# REFINERY CMS DEVELOPMENT =+.*# END REFINERY CMS DEVELOPMENT =+/m, '')
    end

    def output!
      puts "\n---------"
      puts "Refinery successfully installed in '#{@app_path}'!\n\n"

      # Automate
      # TODO: Check exit codes to see whether or not these worked
      puts "Installing gem requirements using bundler..\n"
      run_command("bundle install")

      puts "\n\nSetting up your development database..\n"
      run_command("rake -f \"#{@app_path.join('Rakefile')}\" db:setup", {:cd => false})

      # Deploy to Heroku
      if @options[:heroku]
        puts "\n\nInitializing and committing to git..\n"
        run_command("git init && git add . && git commit -am 'Initial Commit'")

        puts "\n\nCreating Heroku app..\n"
        run_command("heroku create #{@options[:heroku]}")

        puts "\n\nPushing to Heroku (this takes time, be patient)..\n"
        run_command("git push heroku master")

        puts "\n\nSetting up the Heroku database..\n"
        run_command("heroku rake db:setup")

        puts "\n\nRestarting servers...\n"
        run_command("heroku restart")
      end
      # End automation

      # Output helpful messages
      puts "\n=== ACTION REQUIRED ==="
      puts "Now you can launch your webserver using:"
      puts "\ncd #{@app_path}"
      puts "rails server"
      puts "\nThis will launch the built-in webserver at port 3000."
      puts "You can now see your site running in your browser at http://localhost:3000"

      if @options[:heroku]
        puts "\nIf you want files and images to work on heroku, you will need setup S3:"
        puts "heroku config:add S3_BUCKET=XXXXXXXXX S3_KEY=XXXXXXXXX S3_SECRET=XXXXXXXXXX"
      end

      puts "\nThanks for installing Refinery, enjoy creating your new application!"
      puts "---------\n\n"
    end

    private :validate!, :generate!, :refine!, :output!

    def run_command(command, options = {})
      options = {:cd => true, :puts => true}.merge(options)
      to_run = %w()
      to_run << "cd \"#{@app_path}\" &&" if options[:cd]
      to_run << command

      if Refinery::WINDOWS
        to_run = %w(cmd /c) | to_run.map{|c| c.gsub(/\//m, '\\')}
      end

      to_run = to_run.join(' ')

      if options[:puts]
        puts "Running: #{to_run}"
        IO.popen(to_run) do |t|
          while (line = t.gets)
            puts line
          end
        end
      else
        `#{to_run}`
      end
    end

    def find_and_replace(file, find, replace)
      (contents = @app_path.join(file).read).gsub!(find, replace)
      (@app_path + file).open("w") do |f|
        f.puts contents
      end
    end

    protected :run_command, :find_and_replace
  end
end

unless !defined?(::ChildLabor::Task) or ::ChildLabor::Task.instance_methods.include?(:gets)
  ::ChildLabor::Task.module_eval do
    def gets
      @stdout.gets
    end
  end
end
Refinery::AppGenerator.new(ARGV).run!
Something went wrong with that request. Please try again.