class Angelo::Base

Attributes

app_file[RW]
server[RW]
request_body[W]
responder[RW]

Public Class Methods

filter(which, opts = {}) click to toggle source
# File lib/angelo/base.rb, line 161
def filter which, opts = {}, &block
  case opts
  when String, Regexp
    filter_by which, opts, block
  when Hash
    if opts[:path]
      filter_by which, opts[:path], block
    else
      filters[which][:default] << block
    end
  end
end
filter_by(which, path, block) click to toggle source
# File lib/angelo/base.rb, line 174
def filter_by which, path, block
  pattern = ::Mustermann.new path
  filters[which][pattern] << block
end
filters() click to toggle source
# File lib/angelo/base.rb, line 154
def filters
  @filters ||= {
    before: Hash.new{|h,k| h[k] = []},
    after: Hash.new{|h,k| h[k] = []},
  }
end
inherited(subclass) click to toggle source
# File lib/angelo/base.rb, line 109
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
local_path(path) click to toggle source
# File lib/angelo/base.rb, line 209
def local_path path
  if public_dir
    lp = File.join(public_dir, path)
    File.file?(lp) ? lp : nil
  end
end
new(responder) click to toggle source
# File lib/angelo/base.rb, line 228
def initialize responder
  @responder = responder
  @klass = self.class
end
parse_options(argv) click to toggle source

It seems more sensible to put this in main.rb since it's used only if angelo/main is required, but it's here so it can be tested, since requiring angelo/main doesn't play well with the test code.

# File lib/angelo/base.rb, line 378
def self.parse_options(argv)
  require "optparse"

  optparse = OptionParser.new do |op|
    op.banner = "Usage: #{$0} [options]"

    op.on('-p port', OptionParser::DecimalInteger, "set the port (default is #{port})") {|val| port val}
    op.on('-o addr', "set the host (default is #{addr})") {|val| addr val}
    op.on('-h', '--help', "Show this help") do
      puts op
      exit
    end
  end

  begin
    optparse.parse(argv)
  rescue OptionParser::ParseError => ex
    $stderr.puts ex
    $stderr.puts optparse
    exit 1
  end
end
report_errors?() click to toggle source
# File lib/angelo/base.rb, line 146
def report_errors?
  !!@report_errors
end
root() click to toggle source
# File lib/angelo/base.rb, line 142
def root
  @root ||= File.expand_path '..', app_file
end
routes() click to toggle source
# File lib/angelo/base.rb, line 150
def routes
  @routes ||= Hash.new{|h,k| h[k] = RouteMap.new}
end
run(_addr = addr, _port = port, options = {}) click to toggle source
# File lib/angelo/base.rb, line 195
def run _addr = addr, _port = port, options = {}, blocking = false
  Celluloid.logger.level = log_level
  @server = Angelo::Server.new self, _addr, _port, options
  @server.async.ping_websockets
  if blocking
    trap "INT" do
      @server.terminate if @server and @server.alive?
      exit
    end
    sleep
  end
  @server
end
run!(_addr = addr, _port = port, options = {}) click to toggle source
# File lib/angelo/base.rb, line 191
def run! _addr = addr, _port = port, options = {}
  run _addr, _port, options, true
end
sse_event(event_name, data) click to toggle source
# File lib/angelo/base.rb, line 216
def sse_event event_name, data
  data = data.to_json if Hash === data
  SSE_EVENT_TEMPLATE % [event_name.to_s, data]
end
sse_message(data) click to toggle source
# File lib/angelo/base.rb, line 221
def sse_message data
  data = data.to_json if Hash === data
  SSE_DATA_TEMPLATE % data
end
sses(reject = true) click to toggle source
# File lib/angelo/base.rb, line 185
def sses reject = true
  @sses ||= Stash::SSE.new server
  @sses.reject! &:closed? if reject
  @sses
end
websockets(reject = true) click to toggle source
# File lib/angelo/base.rb, line 179
def websockets reject = true
  @websockets ||= Stash::Websocket.new server
  @websockets.reject! &:closed? if reject
  @websockets
end

Public Instance Methods

async(meth, *args) click to toggle source
# File lib/angelo/base.rb, line 233
def async meth, *args
  self.class.server.async.__send__ meth, *args
end
chunked_response(&block) click to toggle source
# File lib/angelo/base.rb, line 352
def chunked_response &block
  transfer_encoding :chunked
  ChunkedResponse.new &block
end
eventsource(&block) click to toggle source
# File lib/angelo/base.rb, line 342
def eventsource &block
  headers SSE_HEADER
  async :handle_event_source, EventSource.new(responder), block
  halt 200, :sse
end
filter(which) click to toggle source
# File lib/angelo/base.rb, line 357
def filter which
  self.class.filters[which].each do |pattern, filters|
    case pattern
    when :default
      filters.each {|filter| instance_eval &filter}
    when ::Mustermann
      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
      end
    end
  end
end
future(meth, *args) click to toggle source
# File lib/angelo/base.rb, line 237
def future meth, *args
  self.class.server.future.__send__ meth, *args
end
halt(status = 400, body = '') click to toggle source
# File lib/angelo/base.rb, line 295
def halt status = 400, body = ''
  throw :halt, HALT_STRUCT.new(status, body)
end
params() click to toggle source
# File lib/angelo/base.rb, line 241
def params
  @params ||= case request.method
              when GET, DELETE, OPTIONS
                parse_query_string
              when PATCH, POST, PUT
                parse_query_string_and_post_body
              end.merge mustermann.params(request.path)
end
request_body() click to toggle source
# File lib/angelo/base.rb, line 261
def request_body
  @request_body ||= request.body.to_s
end
request_headers() click to toggle source
# File lib/angelo/base.rb, line 250
def request_headers
  @request_headers ||= Hash.new do |hash, key|
    if Symbol === key
      k = key.to_s.upcase
      k.gsub! UNDERSCORE, DASH
      _, value = request.headers.find {|header_key,v| header_key.upcase == k}
      hash[key] = value
    end
  end
end
send_data(data, opts = {}) click to toggle source
# File lib/angelo/base.rb, line 322
def send_data data, opts = {}
  # Content-Type
  #
  headers CONTENT_TYPE_HEADER_KEY =>
    (MIME::Types.type_for(File.extname(opts[:filename]))[0].content_type rescue HTML_TYPE)

  # Content-Disposition
  #
  if opts[:disposition] == :attachment
    headers CONTENT_DISPOSITION_HEADER_KEY =>
      ATTACHMENT_CONTENT_DISPOSITION % opts[:filename]
  end

  # Content-Length
  #
  headers CONTENT_LENGTH_HEADER_KEY => data.length

  halt 200, data
end
send_file(local_file, opts = {}) click to toggle source
# File lib/angelo/base.rb, line 299
def send_file local_file, opts = {}
  lp = local_file[0] == File::SEPARATOR ? local_file : File.expand_path(File.join(self.class.root, local_file))
  halt 404 unless File.exist? lp

  # Content-Type
  #
  headers CONTENT_TYPE_HEADER_KEY =>
    (MIME::Types.type_for(File.extname(lp))[0].content_type rescue HTML_TYPE)

  # Content-Disposition
  #
  if opts[:disposition] == :attachment or opts[:filename]
    headers CONTENT_DISPOSITION_HEADER_KEY =>
      ATTACHMENT_CONTENT_DISPOSITION % (opts[:filename] or File.basename(lp))
  end

  # Content-Length
  #
  headers CONTENT_LENGTH_HEADER_KEY => File.size(lp)

  halt 200, File.read(lp)
end
sleep(time) click to toggle source
# File lib/angelo/base.rb, line 348
def sleep time
  Celluloid.sleep time
end