lib/angelo/base.rb in angelo-0.4.1 vs lib/angelo/base.rb in angelo-0.5.0

- old
+ new

@@ -1,112 +1,75 @@ module Angelo class Base extend Forwardable include ParamsParser - include Celluloid::Logger + include Celluloid::Internals::Logger + include Templates include Tilt::ERB include Mustermann - def_delegators :@responder, :content_type, :headers, :mustermann, :redirect, :request, :transfer_encoding + def_delegators :@responder, :content_type, :headers, :mustermann, :redirect, :redirect!, :request, :transfer_encoding def_delegators :@klass, :public_dir, :report_errors?, :sse_event, :sse_message, :sses, :websockets attr_accessor :responder + attr_writer :request_body - class << self - - attr_accessor :app_file, :server - - def inherited subclass - - # Set app_file by groveling up the caller stack until we find - # the first caller from a directory different from __FILE__. - # This allows base.rb to be required from an arbitrarily deep - # nesting of require "angelo/<whatever>" and still set - # app_file correctly. - # - subclass.app_file = caller_locations.map(&:absolute_path).find do |f| - !f.start_with?(File.dirname(__FILE__) + File::SEPARATOR) - end - - # bring RequestError into this namespace - # - subclass.class_eval 'class RequestError < Angelo::RequestError; end' - - subclass.addr DEFAULT_ADDR - subclass.port DEFAULT_PORT - - subclass.ping_time DEFAULT_PING_TIME - subclass.log_level DEFAULT_LOG_LEVEL - - # Parse command line options if angelo/main has been required. - # They could also be parsed in run, but this makes them - # available to and overridable by the DSL. - # - subclass.parse_options(ARGV.dup) if @angelo_main - - class << subclass - - def root - @root ||= File.expand_path '..', app_file - end - - end - - end - end - # Methods defined in module DSL will be available in the DSL both - # in Angelo::Base subclasses and in the top level DSL. + # in Angelo::Base subclasses as class methods and, if angelo/main + # is required, in the top level DSL by forwarding to an anonymous + # Angelo::Base subclass. module DSL def addr a = nil @addr = a if a @addr end + def port p = nil + @port = p if p + @port + end + def log_level ll = nil @log_level = ll if ll @log_level end def ping_time pt = nil @ping_time = pt if pt @ping_time end - def port p = nil - @port = p if p - @port - end - def views_dir d = nil @views_dir = d if d - @views_dir ||= DEFAULT_VIEWS_DIR File.join root, @views_dir end + def reload_templates!(on = true) + @reload_templates = on + end + def public_dir d = nil @public_dir = d if d - @public_dir ||= DEFAULT_PUBLIC_DIR File.join root, @public_dir end def report_errors! @report_errors = true end HTTPABLE.each do |m| define_method m do |path, opts = {}, &block| path = ::Mustermann.new path, opts - routes[m][path] = Responder.new &block + routes[m][path] = Responder.new m, &block end end def websocket path, &block path = ::Mustermann.new path - routes[:websocket][path] = Responder::Websocket.new &block + routes[:websocket][path] = Responder::Websocket.new nil, &block end def eventsource path, headers = nil, &block path = ::Mustermann.new path routes[:get][path] = Responder::Eventsource.new headers, &block @@ -123,40 +86,83 @@ def after opts = {}, &block filter :after, opts, &block end def on_pong &block - Responder::Websocket.on_pong = block + @on_pong = block if block + @on_pong end def content_type type Responder.content_type type end + def default_headers hs + Responder.default_headers = Responder.default_headers.merge hs + end end - # Make the DSL methods available to subclass-level code. - # main.rb makes them available to the top level. + class << self + include DSL - extend DSL + attr_accessor :app_file, :server - class << self + def inherited subclass + + # Set app_file by groveling up the caller stack until we find + # the first caller from a directory different from __FILE__. + # This allows base.rb to be required from an arbitrarily deep + # nesting of require "angelo/<whatever>" and still set + # app_file correctly. + # + subclass.app_file = caller_locations.map(&:absolute_path).find do |f| + !f.start_with?(File.dirname(__FILE__) + File::SEPARATOR) + end + + # bring RequestError into this namespace + # + subclass.class_eval 'class RequestError < Angelo::RequestError; end' + + subclass.addr DEFAULT_ADDR + subclass.port DEFAULT_PORT + + subclass.ping_time DEFAULT_PING_TIME + subclass.log_level DEFAULT_LOG_LEVEL + + subclass.views_dir DEFAULT_VIEWS_DIR + subclass.public_dir DEFAULT_PUBLIC_DIR + + # Parse command line options if angelo/main has been required. + # They could also be parsed in run, but this makes them + # available to and overridable by the DSL. + # + subclass.parse_options(ARGV.dup) if @angelo_main + + end + + def root + @root ||= File.expand_path '..', app_file + end + def report_errors? !!@report_errors end def routes @routes ||= Hash.new{|h,k| h[k] = RouteMap.new} end def filters - @filters ||= {before: {default: []}, after: {default: []}} + @filters ||= { + before: Hash.new{|h,k| h[k] = []}, + after: Hash.new{|h,k| h[k] = []}, + } end def filter which, opts = {}, &block case opts - when String + when String, Regexp filter_by which, opts, block when Hash if opts[:path] filter_by which, opts[:path], block else @@ -165,11 +171,10 @@ end end def filter_by which, path, block pattern = ::Mustermann.new path - filters[which][pattern] ||= [] filters[which][pattern] << block end def websockets reject = true @websockets ||= Stash::Websocket.new server @@ -245,16 +250,20 @@ def request_headers @request_headers ||= Hash.new do |hash, key| if Symbol === key k = key.to_s.upcase k.gsub! UNDERSCORE, DASH - rhv = request.headers.select {|header_key,v| header_key.upcase == k} - hash[key] = rhv.values.first + _, value = request.headers.find {|header_key,v| header_key.upcase == k} + hash[key] = value end end end + def request_body + @request_body ||= request.body.to_s + end + task :handle_websocket do |ws| begin while !ws.closed? do ws.read end @@ -264,13 +273,11 @@ end end task :ping_websockets do every(@base.ping_time) do - websockets.all_each do |ws| - ws.socket << ::WebSocket::Message.ping.to_data - end + websockets.all_each {|ws| ws.ping &@base.on_pong } end end task :handle_event_source do |socket, block| begin @@ -351,15 +358,15 @@ self.class.filters[which].each do |pattern, filters| case pattern when :default filters.each {|filter| instance_eval &filter} when ::Mustermann - if pattern.match request.path - @pre_filter_params = params - @params = @pre_filter_params.merge pattern.params(request.path) + if mustermann_params = pattern.params(request.path) + pre_filter_params = params + @params = pre_filter_params.merge mustermann_params filters.each {|filter| instance_eval &filter} - @params = @pre_filter_params + @params = pre_filter_params end end end end @@ -400,14 +407,11 @@ def []= route, responder @hash[route] = responder end def [] route - responder = nil - if mustermann = @hash.keys.select {|k| k.match(route)}.first - responder = @hash.fetch mustermann - responder.mustermann = mustermann - end + mustermann, responder = @hash.find {|k,v| k.match(route)} + responder.mustermann = mustermann if mustermann responder end end