Repository / root / nitro / bin / nitro

#!/usr/bin/env ruby

# TODO:
#   - Currently we just pass command line option thru.
#     It would be better to properly screen the options.

$LOAD_PATH.unshift "lib"

require "facets/command"
require "facets/settings"

# The default Nitro management script. Provides a number of
# options for managing the lifecycle of Nitro applications.

module Nitro

  #

  class NitroCommand < Console::MasterCommand
    include UniversalOptions

    # Typical application main file names.

    APPLICATION_FILES = %w{ app.rb run.rb start.rb application.rb }

    # Live mode?

    attr_accessor :live

    # Daemon

    attr_accessor :daemon ; alias_method :d=, :daemon=

    # Setup a cluster. The input is the size of the cluster.
    #
    # Example:
    #
    #   $ nitro --cluster=5
    #
    # starts 5 instances that listen to consecutive port numbers.

    attr_reader :cluster

    def cluster=(count)
      @daemon  = true   # cluster implies daemon mode
      @cluster = count.to_i
    end

    # Starting port offset.

    def port_offset ; @port_offset.to_i ; end

    def port_offset=(number)
      @port_offset = number.to_i
    end

    # The default action, starts the application. You can
    # alternatively use the start/run aliases.
    #
    # Examples:
    #
    #   $ nitro
    #   $ nitro start
    #   $ nitro run

    def start(arguments, options)
      if file = application_file
        if ic = cluster
          ic.times do |i|
            exec_application(file, port_offset + i)
          end
        else
          exec_application(file, port_offset)
        end
      else
        puts "No application found!"
        # FIXME: better error mesage and/or show default app!
      end
    end
    alias_method :run,     :start
    alias_method :default, :start

    # State server.

    def stateserver
      return unless yes
      if File.exist?("state.rb") and Dir[".d*.pid"].empty?
        puts "Starting state server"
        system "ruby #{ENV['RUBYOPT']} -Ilib state.rb --daemon"
      end
    end

    # Dump the version of the framework.

    def version
      require "nitro"
      puts "Nitro #{Nitro::Version}"
      exit
    end
    alias_method :v, :version

    # Stop all running application instances.

    def stop
      kill_processes("a*.pid")
    end

    # Restart all application servers.

    def restart
      stop()
      start()
    end

    # Stop all running application instances and related
    # servers (for example drb servers)

    def kill
      kill_processes("a*.pid")
      kill_processes("d*.pid")
    end

    # Starts an IRB console attached to the web application.
    #
    # Example:
    #
    #   $ nitro console

    def console
      if RUBY_PLATFORM =~ /mswin32/
        irb_name = "irb.bat"
      else
        irb_name = 'RUBYOPT="-rubygems -Ilib"; irb'
      end

      if f = application_file
        ENV["NITRO_ADAPTER"] = "script"
        exec "#{irb_name} -r #{f} -r irb/completion --noinspect"
      end
    end

    # Create the skeleton for a new Nitro application.
    #
    # Example:
    #
    #   $ nitro create myapp

    def create(arguments, options)
      app_name = arguments.first

      require "nitro" # here ???

      path = File.expand_path(app_name)

      if File.exists? path
        STDERR.puts "ERROR: Path #{path} already exists! Aborting!"
        exit 1
      end

      puts "Building a skeleton Nitro application in '#{path}'"

      FileUtils.cp_r(Nitro.proto_path, path)

      puts "Done"
    end

  private

    # Find out the application main file.

    def application_file
      for f in APPLICATION_FILES
        if File.exist? f
          return f
        end
      end

      return false
    end

    # Kill processes marked by .pid files.

    def kill_processes(pattern = "*.pid")
      Dir[File.join(".temp", pattern)].each do |f|
        pid = File.basename(f, ".pid")[1..-1]
        begin
          puts "Killing process '#{pid}'"
          system "kill -9 #{pid}"
    #      Process.kill(-9, pid.to_i)
        rescue => ex
          # drink it!
        ensure
          File.unlink(f)
        end
      end
    end

    # Start the application server. If a cluster is defined,
    # multiple instances of the application server are spawned.

    def exec_application(file, port_offset=nil)
      options = master_options

      # don;t pass these options thru.
      options.delete('cluster')
      options.delete('port_offset')

      # re-collect daemon attribute b/c cluster may have change it.
      options['daemon']      = daemon
      options['port-offset'] = port_offset if port_offset && port_offset > 0

      cmd = "PORT_OFFSET=#{port_offset}; ruby #{ENV['RUBYOPT']} -Ilib #{file} #{options.to_console}"

      # Mark this invocation with the application name.
      # Useful to identify process when  runing ps on Unix. Use
      # for example:
      #
      # ps aux | grep ruby

      cmd << " --app=#{File.basename(Dir.pwd)}"

      puts cmd if ENV["NITRO_DEBUG"]

      system(cmd)
    end

    # Catch undefined options, will be handled by the application
    # command object.

    def option_missing(opt, arg = nil)
      # drink it! were passing it on.
    end

  end

end

Nitro::NitroCommand.start(ARGV.dup)

Latest changes

20071027172905 George Moschovitis Branch version of Nitro (experimental features)
20080114155827 George Moschovitis Fixed command line parsing [trans].
20071126002728 Arne Brasseur Fix create skeleton
20071116121914 transfire@gmail.com fixed to nitro command
20071110210540 transfire@gmail.com fix nitro command
20071110145939 transfire@gmail.com Rewrote nitro command to work with Facets 2.1
20071101083458 George Moschovitis Minor.
20071031013046 transfire@gmail.com Vexing autoload and a mis-spelled "intialize"
20071030200617 George Moschovitis Various changes.
20071030195040 transfire@gmail.com A little more 50.
20071029022426 transfire@gmail.com fix nitro command
20071028212636 George Moschovitis Small fix to make bin/nitro run again. Everything seems to run now.
20071027172905 George Moschovitis Branch version of Nitro (experimental features)

All changes