module Liars class C attr_accessor :body, :headers, :status, :params, :cookies class << self def inherited(child); (@@subclasses ||= []) << child; end def subclasses; @@subclasses; end def run(r=$stdin, e=ENV) route = e['PATH_INFO'] @@subclasses.each do |klass| action, args = klass.get_action_and_args(route) if action controller = klass.new(r, e, e['REQUEST_METHOD']||"GET") return controller.send(action, *args) && controller end end end def route_of(action, *routes) @@routes ||= {} @@routes[action] = routes end # 实际上是返回一个action的名称和这个action所有可能用到的参数列表 # 如edit上映射的route是'/edit/(\d+)',而实际访问的路径是'/edit/1'时 # 1将会被作为一个参数传给edit方法 def get_action_and_args(route) @@routes.each do |action, action_routes| action_routes.find do |r| match = Regexp.new("^#{r}$").match(route) return action, match[1..-1] if match end end end end def initialize(r, e, m) @status, @method, @env, @headers, @root = 200, m.downcase, e, {'Content-Type'=>'text/html'}, e["SCRIPT_NAME"].sub(/\/$/,'') @params = qsp(e["QUERY_STRING"]) @cookies = kp(e["HTTP_COOKIE"]) @params.merge!(qsp(r.read)) if @method == "post" end def redirect_to(url) r(302,'','Location'=>url) end # A quick means of setting this controller's status, body and headers. # Used internally by Camping, but... by all means... # # r(302, '', 'Location' => self / "/view/12") # # Is equivalent to: # # redirect "/view/12" # def r(s, b, h = {}); @status = s; @headers.merge!(h); @body = b; end # URL escapes a string. # # Camping.escape("I'd go to the museum straightway!") # #=> "I%27d+go+to+the+museum+straightway%21" # def escape(s); s.to_s.gsub(/[^ \w.-]+/n){'%'+($&.unpack('H2'*$&.size)*'%').upcase}.tr(' ', '+') end # Unescapes a URL-encoded string. # # Camping.un("I%27d+go+to+the+museum+straightway%21") # #=> "I'd go to the museum straightway!" # def un(s); s.tr('+', ' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')} end def qsp(qs, d='&;') (qs || '').split(/[#{d}] */n).inject({}) do |h, p| k, v = un(p).split('=', 2) if (k = k.split(/[\]\[]+/)).length == 1 h[k[0].to_sym] = v else (h[k[0].to_sym] ||= {})[k[1].to_sym] = v end h end end def kp(s); qsp(s, ';,'); end def render(action=(caller[0]=~/`(\w+)'/;$1)) # 查找当前controller对应的view,然后根据action来找到渲染方法 name = (self.class.to_s =~ /(\w+)(C|Controller)$/; $1) view_class = eval("#{name}View") rescue eval("#{name}V") view_instance = view_class.new instance_variables.each do |iv| view_instance.instance_variable_set(iv, instance_variable_get(iv)) end render_result = view_instance.respond_to?(:layout) ? view_instance.send(:layout, &view_instance.method(action)) : view_instance.send(action) @body = view_instance.stream || render_result end end end