Report abuse


			
#!/usr/bin/env ruby -wKU

Halcyon [gems/halcyon-0.5.0/lib/halcyon.rb]


			
%w(rubygems rack merb/core_ext merb/router json uri logger).each{|dep|require dep}

module Halcyon

  def self.version
    [0,5,0].join('.')
  end

  class Application
    def initialize(options={})
      @options = options
      @logger = @options[:logger]
      @logger.progname = self.class.to_s
      @logger.formatter = proc{|s,t,p,m|"#{s} [#{t.strftime("%Y-%m-%d %H:%M:%S")}] (#{$$}) #{p} :: #{m}\n"}
      @logger.info "Starting up..."
      startup if respond_to? :startup
      @logger.info "Started. Listening on #{@options[:port]}."
      finalize =  Proc.new do
        @logger.info "Shutting down #{$$}."
        exit
      end
      %w(INT KILL TERM QUIT HUP).each{|sig|trap(sig, finalize)} # http://en.wikipedia.org/wiki/Signal_%28computing%29
    end
    def call(env)
      @time_started = Time.now
      @env = env
      @request = Rack::Request.new(@env)
      @response = Rack::Response.new
      @response['Content-Type'] = "text/plain" # "application/json"
      @response['User-Agent'] = "JSON/#{JSON::VERSION} Compatible (en-US) Halcyon::Application/#{Halcyon.version}"
      @env['halcyon.route'] = Router.route(@env)
      response = _dispatch(@env['halcyon.route'])
      @response.status = response[:status]
      @response.write response.to_json
      @time_finished = Time.now - @time_started
      req_time, req_per_sec = ((@time_finished*1e4).round.to_f/1e4), (((1.0/@time_finished)*1e2).round.to_f/1e2)
      @logger.info "[#{@response.status}] #{@env['REQUEST_URI']} (#{req_time}s;#{req_per_sec}req/s)"
      @response.finish
    end
    def _dispatch(route)
      @params = route.reject{|key, val| [:action, :module].include? key}
      @params.merge!(query_params)
      @logger << "\nParams: #{@params.inspect}\n"
      # make sure that the right module/action is called based on the route
      case route[:module]
      when NilClass
        # not a part of a module
        send(route[:action].to_sym)
      when Symbol
        # module name specified
        self.class.class_eval { include const_get(route[:module]) }
        self.class.const_get(route[:module]).instance_method(route[:action].to_sym).bind(self).call
        # TODO: figure out how to do this... the bound object has to be kind_of? the module where it was plucked from
      when String
        # pulled from URL, so camelize (from merb/core_ext) and symbolize first
        _dispatch(route.merge(:module => route[:module].camelize.to_sym))
      end
    end
    def self.route
      if block_given?
        Router.prepare do |router|
          Router.default_to yield(router) || {:action => 'not_found'}
        end
      end
    end
    class Router < Merb::Router
      def self.default_to route
        @@default_route = route.is_a?(Hash) ? route : {:action => 'not_found'}
      end
      def self.route(env)
        uri = URI.parse(env['REQUEST_URI'] || env['PATH_INFO']).path
        path = (uri ? uri.split('?').first : '').sub(/\/+/, '/')
        path = path[0..-2] if (path[-1] == ?/) && path.size > 1
        req = Struct.new(:path, :method).new(path, env['REQUEST_METHOD'].downcase.to_sym)
        route = self.match(req, {})
        if route[0].nil?
          env['halcyon.logger'].debug "No route found. Using default." if env['halcyon.logger'].is_a? Logger
          @@default_route
        else
          route[1]
        end
      end
    end
    def params
      @params
    end
    def query_params
      @env['QUERY_STRING'].split(/&/).inject({}){|h,kp| k,v = kp.split(/=/); h[k] = v; h}.symbolize_keys!
    end
    def uri
      # special parsing is done to remove the protocol, host, and port that
      # some Handlers leave in there. (Fixes inconsistencies.)
      URI.parse(@env['REQUEST_URI'] || @env['PATH_INFO']).path
    end
    def method
      @env['REQUEST_METHOD'].downcase.to_sym
    end
    def post
      @req.POST.symbolize_keys!
    end
    def get
      @req.GET.symbolize_keys!
    end
    def ok(msg='OK')
      {:status => 200, :body => msg}
    end
    def not_found(msg='Not Found')
      {:status => 400, :body => msg}
    end
  end

  class Logger < ::Logger; end #:nodoc:

  class Client; end #:nodoc:

end

Halcyon App [app/happ.rb]


			
class HApp < Halcyon::Application
  route do |r|
    r.match('/reports/create').to(:module => 'reports', :action => 'create')
    r.match('/reports/list').to(:module => 'reports', :action => 'list')
    r.match('/list').to(:action => 'list')
  end
  module Reports
    def create
      # access to params[:key] and post[:key] just like normal
      @logger.info 'creating new report'
      ok('created')
    end
    def list
      @logger.info 'listing reports'
      ok
    end
  end
  def list
    @logger.info 'not listing reports'
    ok 'This is not what you were looking for!'
  end
end

App RackUp Config [app/config/start.ru]


			
# require 'halcyon'
# require 'happ'
# app = Rack::Builder.new do
#   use Rack::ShowExceptions
#   run HApp.new(:options => 'go here', :logger => Halcyon::Logger.new(STDOUT))
# end
options = {
  :go => 'here',
  :logger => Halcyon::Logger.new(STDOUT),
  :port => 4000
}
Rack::Handler::Mongrel.run HApp.new(options), :Port => options[:port]

Run with (from app/)


			
# thin start -r config/start.ru -p 3822