require 'rubygems' require 'optparse' require 'ostruct' require 'fileutils' require 'yaml' require 'erubis' module Merb class Config def self.setup defaults = { :host => "0.0.0.0", :port => "4000", :allow_reloading => true, :merb_root => Dir.pwd, :cache_templates => false, :use_mutex => true } begin options = defaults.merge(YAML.load(Erubis::Eruby.new(IO.read("#{defaults[:merb_root]}/dist/conf/merb.yml")).result)) rescue options = defaults end options end end class Server class << self def merb_config options = Merb::Config.setup opts = OptionParser.new do |opts| opts.banner = "Usage: merb [fdcepghmisluMG] [argument]" opts.define_head "Merb Mongrel+ Erb. Lightweight replacement for ActionPack" opts.separator '*'*80 opts.separator 'If no flags are given, Merb starts in the foreground on port 4000' opts.separator '*'*80 opts.on("-u", "--user USER", "This flag is for having merb run as a user other than the one currently logged in. Note: if you set this you must also provide a --group option for it to take effect.") do |config| options[:user] = config end opts.on("-G", "--group GROUP", "This flag is for having merb run as a group other than the one currently logged in. Note: if you set this you must also provide a --user option for it to take effect.") do |config| options[:group] = config end opts.on("-f", "--config-file FILENAME", "This flag is for adding extra config files for things like the upload progress module") do |config| options[:config] = config end opts.on("-d", "--daemonize", "This will run a single merb in the background") do |config| options[:daemonize] = true end opts.on("-c", "--cluster-nodes NUM_MERBS", "Number of merb daemons to run") do |nodes| options[:cluster] = nodes end opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000") do |port| options[:port] = port end opts.on("-h", "--host HOSTNAME", "Host to bind to(default is all IP's)") do |host| options[:host] = host end opts.on("-m", "--merb-root MERB_ROOT", "the path to the MERB_ROOT for the app you want to run") do |merb_root| options[:merb_root] = File.expand_path(merb_root) end opts.on("-i", "--irb-console", "This flag will start merb in irb console mode. All your models and other classes will be available for you in an irb session.") do |console| options[:console] = true end opts.on("-s", "--start-drb PORTNUM", "This is the port number to run the drb daemon on for sessions and uplod progress monitoring.") do |drb_port| options[:start_drb] = true options[:only_drb] = true options[:drb_server_port] = drb_port end opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of these options: DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN") do |loglevel| options[:log_level] = loglevel end opts.on("-e", "--environment STRING", "Run merb in the correct mode(development, production, testing)") do |env| options[:environment] = env end opts.on("-r", "--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]", "Command-line option to run scripts and/or code in the merb app") do |stuff_to_run| options[:runner] = stuff_to_run end opts.on("-g", "--generate-app PATH", "Generate a fresh merb app at PATH") do |path| options[:generate] = path || Dir.pwd end opts.on("-k", "--kill PORT or all", "Kill one merb proceses by port number. use merb -k all to kill all merbs") do |ports| options[:kill] = ports end opts.on("-M", "--merb-config FILENAME", "This flag is for explicitly declaring the merb app's config file") do |config| options[:merb_config] = config end opts.on("-X", "--mutex on/off", "This flag is for turning the mutex lock on and off.") do |mutex| if mutex == 'off' options[:use_mutex] = false else options[:use_mutex] = true end end opts.on("-?", "--help", "Show this help message") do puts opts exit end end opts.parse!(@@merb_raw_opts) # Added by: Rogelio J. Samour 2007-01-23 # We need to reload the options that exist in the App's merb.yml # This is needed when one calls merb NOT from the merb_app ROOT # like so: merb --merb-config /path/to/dist/conf/merb.yml -m /path/to/merb/app # or if we add :merb_root: /path/to/merb/app in the merb.yml we can now only call it # like so: merb --merb-config /path/to/dist/conf/merb.yml if options[:merb_config] options = options.merge(YAML.load(Erubis::Eruby.new(IO.read("#{options[:merb_config]}")).result)) end @@merb_opts = options end def initialize_merb require 'merb' require @@merb_opts[:merb_root] / 'dist/conf/router.rb' require @@merb_opts[:merb_root] / 'dist/conf/merb_init.rb' end def run @@merb_raw_opts = ARGV merb_config if k = @@merb_opts[:kill] begin Dir[@@merb_opts[:merb_root] + "/log/merb.#{k == 'all' ? '*' : k }.pid"].each do |f| puts f pid = IO.read(f).chomp.to_i Process.kill(9, pid) FileUtils.rm f puts "killed PID: #{pid}" end rescue puts "Failed to kill! #{k}" ensure exit end end case @@merb_opts[:environment].to_s when 'development' @@merb_opts[:allow_reloading] ||= true @@merb_opts[:show_error] ||= true when 'production' @@merb_opts[:allow_reloading] = false @@merb_opts[:show_error] ||= false @@merb_opts[:cache_templates] = true when 'test' @@merb_opts[:allow_reloading] ||= true @@merb_opts[:show_error] ||= true else @@merb_opts[:allow_reloading] ||= true @@merb_opts[:show_error] ||= true end @@merb_opts[:dist_root] = @@merb_opts[:merb_root]+'/dist' $LOAD_PATH.unshift( File.join(@@merb_opts[:dist_root] , '/app/controllers') ) $LOAD_PATH.unshift( File.join(@@merb_opts[:dist_root] , '/app/models') ) $LOAD_PATH.unshift( File.join(@@merb_opts[:dist_root] , '/lib') ) if File.exist? File.join(@@merb_opts[:dist_root] , '/framework') $LOAD_PATH.unshift( File.join(@@merb_opts[:dist_root] , '/framework') ) end if @@merb_opts[:generate] require 'merb/generators/merb_app/merb_app' ::Merb::AppGenerator.run @@merb_opts[:generate] exit! end if @@merb_opts[:console] initialize_merb Object.class_eval do def show_routes Merb::Router.generator.paths.each {|p| puts p.inspect} nil end def url(path, o={}) Merb::Router.generator.generate(path,o) end end ARGV.clear # Avoid passing args to IRB require 'irb' require 'irb/completion' def exit exit! end if File.exists? ".irbrc" ENV['IRBRC'] = ".irbrc" end IRB.start exit! end if @@merb_opts[:runner] initialize_merb code_or_file = @@merb_opts[:runner] if File.exists?(code_or_file) eval(File.read(code_or_file)) else eval(code_or_file) end exit! end if @@merb_opts[:start_drb] puts "Starting merb drb server on port: #{@@merb_opts[:drb_server_port]}" start(@@merb_opts[:drb_server_port], :drbserver_start) exit if @@merb_opts[:only_drb] end if @@merb_opts[:cluster] delete_pidfiles @@merb_opts[:port].to_i.upto(@@merb_opts[:port].to_i+@@merb_opts[:cluster].to_i-1) do |port| puts "Starting merb server on port: #{port}" start(port) end elsif @@merb_opts[:daemonize] delete_pidfiles(@@merb_opts[:port]) start(@@merb_opts[:port]) else trap('TERM') { exit } mongrel_start(@@merb_opts[:port]) end end def store_pid(pid,port) File.open("#{@@merb_opts[:merb_root]}/log/merb.#{port}.pid", 'w'){|f| f.write("#{Process.pid}\n")} end def start(port,what=:mongrel_start) fork do Process.setsid exit if fork if what == :mongrel_start store_pid(Process.pid, port) else store_pid(Process.pid, "drb.#{port}") end Dir.chdir @@merb_opts[:merb_root] File.umask 0000 STDIN.reopen "/dev/null" STDOUT.reopen "/dev/null", "a" STDERR.reopen STDOUT trap("TERM") { exit } send(what, port) end end def delete_pidfiles(portor_star='*') Dir["#{@@merb_opts[:merb_root]}/log/merb.#{portor_star}.pid"].each do |pid| FileUtils.rm(pid) rescue nil end end def drbserver_start(port) puts "Starting merb drb server on port: #{port}" require 'merb/merb_drb_server' drb_init = File.join(@@merb_opts[:merb_root], "/dist/conf/merb_drb_init") require drb_init if File.exist?(drb_init) DRb.start_service("druby://#{@@merb_opts[:host]}:#{port}", Merb::DrbServiceProvider) DRb.thread.join end def mongrel_start(port) @@merb_opts[:port] = port unless @@merb_opts[:generate] || @@merb_opts[:console] || @@merb_opts[:only_drb] || @@merb_opts[:kill] puts %{Merb started with these options:} puts @@merb_opts.to_yaml; puts end initialize_merb mconf_hash = {:host => (@@merb_opts[:host]||"0.0.0.0"), :port => (port ||4000)} if @@merb_opts[:user] and @@merb_opts[:group] mconf_hash[:user] = @@merb_opts[:user] mconf_hash[:group] = @@merb_opts[:group] end mconfig = Mongrel::Configurator.new(mconf_hash) do yconfig = YAML.load(Erubis::Eruby.new(IO.read(File.expand_path(@@merb_opts[:config]))).result) if @@merb_opts[:config] listener do uri( "/", :handler => MerbUploadHandler.new(yconfig), :in_front => true) if @@merb_opts[:config] uri "/", :handler => MerbHandler.new(@@merb_opts[:dist_root]+'/public') uri "/favicon.ico", :handler => Mongrel::Error404Handler.new("") end trap("INT") { stop } run end mconfig.join end def config @@merb_opts end end end # Server end # Merb