Class: Angelo::Base

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
ParamsParser, Celluloid::Logger
Defined in:
lib/angelo/base.rb

Defined Under Namespace

Classes: ChunkedResponse, EventSource

Constant Summary collapse

@@addr =
DEFAULT_ADDR
@@port =
DEFAULT_PORT
@@ping_time =
DEFAULT_PING_TIME
@@log_level =
DEFAULT_LOG_LEVEL
@@report_errors =
false

Constants included from ParamsParser

ParamsParser::AMPERSAND, ParamsParser::EMPTY_JSON, ParamsParser::EQUALS, ParamsParser::SEMICOLON

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ParamsParser

#content_type?, #form_encoded?, #json?, #parse_formencoded, #parse_post_body, #parse_query_string, #recurse_symhash

Constructor Details

#initialize(responder) ⇒ Base

Returns a new instance of Base.



177
178
179
180
# File 'lib/angelo/base.rb', line 177

def initialize responder
  @responder = responder
  @klass = self.class
end

Class Attribute Details

.app_fileObject

Returns the value of attribute app_file.



31
32
33
# File 'lib/angelo/base.rb', line 31

def app_file
  @app_file
end

.serverObject

Returns the value of attribute server.



31
32
33
# File 'lib/angelo/base.rb', line 31

def server
  @server
end

Instance Attribute Details

#responderObject

Returns the value of attribute responder.



27
28
29
# File 'lib/angelo/base.rb', line 27

def responder
  @responder
end

Class Method Details

.after(opts = {}, &block) ⇒ Object



105
106
107
# File 'lib/angelo/base.rb', line 105

def after opts = {}, &block
  filter :after, opts, &block
end

.before(opts = {}, &block) ⇒ Object



101
102
103
# File 'lib/angelo/base.rb', line 101

def before opts = {}, &block
  filter :before, opts, &block
end

.compile!(name, &block) ⇒ Object



63
64
65
66
67
68
# File 'lib/angelo/base.rb', line 63

def compile! name, &block
  define_method name, &block
  method = instance_method name
  remove_method name
  method
end

.content_type(type) ⇒ Object



143
144
145
# File 'lib/angelo/base.rb', line 143

def content_type type
  Responder.content_type type
end

.eventsource(path, headers = nil, &block) ⇒ Object



119
120
121
# File 'lib/angelo/base.rb', line 119

def eventsource path, headers = nil, &block
  routes[:get][path] = Responder::Eventsource.new headers, &block
end

.filter(which, opts = {}, &block) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/angelo/base.rb', line 82

def filter which, opts = {}, &block
  f = compile! :filter, &block
  case opts
  when String
    filter_by which, opts, f
  when Hash
    if opts[:path]
      filter_by which, opts[:path], f
    else
      filters[which][:default] << f
    end
  end
end

.filter_by(which, path, meth) ⇒ Object



96
97
98
99
# File 'lib/angelo/base.rb', line 96

def filter_by which, path, meth
  filters[which][path] ||= []
  filters[which][path] << meth
end

.filtersObject



78
79
80
# File 'lib/angelo/base.rb', line 78

def filters
  @filters ||= {before: {default: []}, after: {default: []}}
end

.inherited(subclass) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/angelo/base.rb', line 33

def inherited subclass

  # set app_file from caller stack
  #
  subclass.app_file = caller(1).map {|l| l.split(/:(?=|in )/, 3)[0,1]}.flatten[0]

  # bring RequestError into this namespace
  #
  subclass.class_eval 'class RequestError < Angelo::RequestError; end'

  class << subclass

    def root
      @root ||= File.expand_path '..', app_file
    end

    def view_dir
      v = self.class_variable_get(:@@views) rescue DEFAULT_VIEW_DIR
      File.join root, v
    end

    def public_dir
      p = self.class_variable_get(:@@public_dir) rescue DEFAULT_PUBLIC_DIR
      File.join root, p
    end

  end

end

.local_path(path) ⇒ Object



158
159
160
161
162
163
# File 'lib/angelo/base.rb', line 158

def local_path path
  if public_dir
    lp = File.join(public_dir, path)
    File.file?(lp) ? lp : nil
  end
end

.on_pong(&block) ⇒ Object



123
124
125
# File 'lib/angelo/base.rb', line 123

def on_pong &block
  Responder::Websocket.on_pong = block
end

.routesObject



70
71
72
73
74
75
76
# File 'lib/angelo/base.rb', line 70

def routes
  @routes ||= {}
  ROUTABLE.each do |m|
    @routes[m] ||= {}
  end
  @routes
end

.run(addr = @@addr, port = @@port) ⇒ Object



147
148
149
150
151
152
153
154
155
156
# File 'lib/angelo/base.rb', line 147

def run addr = @@addr, port = @@port
  Celluloid.logger.level = @@log_level
  @server = Angelo::Server.new self, addr, port
  @server.async.ping_websockets
  trap "INT" do
    @server.terminate if @server and @server.alive?
    exit
  end
  sleep
end

.sse_event(event_name, data) ⇒ Object



165
166
167
168
# File 'lib/angelo/base.rb', line 165

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) ⇒ Object



170
171
172
173
# File 'lib/angelo/base.rb', line 170

def sse_message data
  data = data.to_json if Hash === data
  SSE_DATA_TEMPLATE % data
end

.ssesObject



137
138
139
140
141
# File 'lib/angelo/base.rb', line 137

def sses
  @sses ||= Stash::SSE.new server
  @sses.reject! &:closed?
  @sses
end

.task(name, &block) ⇒ Object



127
128
129
# File 'lib/angelo/base.rb', line 127

def task name, &block
  Angelo::Server.define_task name, &block
end

.websocket(path, &block) ⇒ Object



115
116
117
# File 'lib/angelo/base.rb', line 115

def websocket path, &block
  routes[:websocket][path] = Responder::Websocket.new &block
end

.websocketsObject



131
132
133
134
135
# File 'lib/angelo/base.rb', line 131

def websockets
  @websockets ||= Stash::Websocket.new server
  @websockets.reject! &:closed?
  @websockets
end

Instance Method Details

#async(meth, *args) ⇒ Object



182
183
184
# File 'lib/angelo/base.rb', line 182

def async meth, *args
  self.class.server.async.__send__ meth, *args
end

#chunked_response(&block) ⇒ Object



308
309
310
311
# File 'lib/angelo/base.rb', line 308

def chunked_response &block
  transfer_encoding :chunked
  ChunkedResponse.new &block
end

#eventsource(&block) ⇒ Object



288
289
290
291
292
# File 'lib/angelo/base.rb', line 288

def eventsource &block
  headers SSE_HEADER
  async :handle_event_source, EventSource.new(responder.connection.detach.socket), block
  halt 200, :sse
end

#filter(which) ⇒ Object



302
303
304
305
306
# File 'lib/angelo/base.rb', line 302

def filter which
  fs = self.class.filters[which][:default]
  fs += self.class.filters[which][request.path] if self.class.filters[which][request.path]
  fs.each {|f| f.bind(self).call}
end

#future(meth, *args) ⇒ Object



186
187
188
# File 'lib/angelo/base.rb', line 186

def future meth, *args
  self.class.server.future.__send__ meth, *args
end

#halt(status = 400, body = '') ⇒ Object



242
243
244
# File 'lib/angelo/base.rb', line 242

def halt status = 400, body = ''
  throw :halt, HALT_STRUCT.new(status, body)
end

#paramsObject



190
191
192
193
194
195
196
197
# File 'lib/angelo/base.rb', line 190

def params
  @params ||= case request.method
              when GET, DELETE, OPTIONS
                parse_query_string
              when POST, PUT
                parse_post_body
              end
end

#report_errors?Boolean

Returns:

  • (Boolean)


294
295
296
# File 'lib/angelo/base.rb', line 294

def report_errors?
  @@report_errors
end

#request_headersObject



199
200
201
202
203
204
205
206
207
208
# File 'lib/angelo/base.rb', line 199

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
    end
  end
end

#send_data(data, opts = {}) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/angelo/base.rb', line 268

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 = {}) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/angelo/base.rb', line 246

def send_file local_file, opts = {}
  lp = self.class.local_path local_file

  # 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) ⇒ Object



298
299
300
# File 'lib/angelo/base.rb', line 298

def sleep time
  Celluloid.sleep time
end