angelo 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ require "angelo"
2
+
3
+ module Angelo
4
+ class Base
5
+ @angelo_main = true
6
+
7
+ # Takes a block and/or modules that define methods that can be
8
+ # used from within request handlers. Methods can also be defined
9
+ # at the top level but they get defined as Object instance methods
10
+ # which isn't good.
11
+
12
+ module DSL
13
+ def helpers(*args, &block)
14
+ args.each do |mod|
15
+ include(mod)
16
+ end
17
+ block && class_exec(&block)
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+
24
+ # The intent here is to add the DSL to the top-level object "main" by
25
+ # creating an anonymous Angelo::Base subclass and forwarding the DSL
26
+ # methods to it from the top-level object. Then run the app at exit.
27
+ #
28
+ # If angelo/main is required from somewhere other than the top level,
29
+ # all bets are off.
30
+
31
+ if self.to_s == "main"
32
+ # We are probably at the top level.
33
+
34
+ require "forwardable"
35
+ self.extend Forwardable
36
+ @angelo_app = Class.new(Angelo::Base)
37
+ self.def_delegators :@angelo_app, *Angelo::Base::DSL.instance_methods
38
+
39
+ at_exit do
40
+ # Don't run @angelo_app on uncaught exceptions including exit
41
+ # being called which raises SystemExit. The rationale being that
42
+ # exit means exit, not "run the server".
43
+ if $!.nil?
44
+ @angelo_app.run!
45
+ end
46
+ end
47
+ end
@@ -9,25 +9,25 @@ module Angelo
9
9
 
10
10
  attr_reader :last_response
11
11
 
12
- def define_app &block
13
-
12
+ def define_app app = nil, &block
14
13
  before do
15
- app = Class.new Angelo::Base
16
-
17
- app.class_eval { content_type :html } # reset
14
+ if app.nil? && block
15
+ app = Class.new Angelo::Base do
16
+ content_type :html # reset
17
+ class_eval &block
18
+ end
19
+ end
18
20
  Celluloid.logger.level = ::Logger::ERROR # see spec_helper.rb:9
19
21
 
20
- app.class_eval &block
21
22
  @server = Angelo::Server.new app
22
23
  app.server = @server
23
- $reactor = Reactor.new unless $reactor.alive?
24
+ $reactor = Reactor.new if $reactor == nil || !$reactor.alive?
24
25
  end
25
26
 
26
27
  after do
27
28
  sleep 0.1
28
29
  @server.terminate if @server and @server.alive?
29
30
  end
30
-
31
31
  end
32
32
 
33
33
  def hc
@@ -40,12 +40,8 @@ module Angelo
40
40
  url
41
41
  end
42
42
 
43
- def hc_req method, path, params = {}, headers = {}
44
- @last_response = if block_given?
45
- hc.__send__ method, url(path), params, headers, &Proc.new
46
- else
47
- hc.__send__ method, url(path), params, headers
48
- end
43
+ def hc_req method, path, params = {}, headers = {}, &block
44
+ @last_response = hc.__send__ method, url(path), params, headers, &block
49
45
  end
50
46
  private :hc_req
51
47
 
@@ -70,15 +66,8 @@ module Angelo
70
66
  private :http_req
71
67
 
72
68
  [:get, :post, :put, :delete, :options, :head].each do |m|
73
- define_method m do |path, params = {}, headers = {}|
74
-
75
- # http_req m, path, params, headers
76
-
77
- if block_given?
78
- hc_req m, path, params, headers, &Proc.new
79
- else
80
- hc_req m, path, params, headers
81
- end
69
+ define_method m do |path, params = {}, headers = {}, &block|
70
+ hc_req m, path, params, headers, &block
82
71
  end
83
72
  end
84
73
 
@@ -114,6 +103,44 @@ module Angelo
114
103
  last_response.headers['Content-Type'].split(';').must_include JSON_TYPE
115
104
  end
116
105
 
106
+ module Cellper
107
+
108
+ @@stop = false
109
+ @@testers = {}
110
+
111
+ def define_action sym, &block
112
+ define_method sym, &block
113
+ end
114
+
115
+ def remove_action sym
116
+ remove_method sym
117
+ end
118
+
119
+ def unstop!
120
+ @@stop = false
121
+ end
122
+
123
+ def stop!
124
+ @@stop = true
125
+ end
126
+
127
+ def stop?
128
+ @@stop
129
+ end
130
+
131
+ def testers; @@testers; end
132
+ end
133
+
134
+ class Reactor
135
+ include Celluloid::IO
136
+ extend Cellper
137
+ end
138
+
139
+ class ActorPool
140
+ include Celluloid
141
+ extend Cellper
142
+ end
143
+
117
144
  end
118
145
 
119
146
  class WebsocketHelper
@@ -173,4 +200,5 @@ module Angelo
173
200
  end
174
201
 
175
202
  end
203
+
176
204
  end
@@ -9,7 +9,7 @@ module Angelo
9
9
  EMPTY_JSON = '{}'
10
10
 
11
11
  def parse_formencoded str
12
- str.split(AMPERSAND).reduce(Responder.symhash) do |p, kv|
12
+ str.split(AMPERSAND).reduce(SymHash.new) do |p, kv|
13
13
  key, value = kv.split(EQUALS).map {|s| CGI.unescape s}
14
14
  p[key] = value
15
15
  p
@@ -22,28 +22,18 @@ module Angelo
22
22
 
23
23
  def parse_post_body
24
24
  body = request.body.to_s
25
- qs = parse_query_string
26
25
  case
27
26
  when form_encoded?
28
- body = parse_formencoded body
29
- qs.merge! body
30
- when json?
31
- body = EMPTY_JSON if body.empty?
32
- body = JSON.parse body
33
- recurse_symhash qs.merge! body
27
+ parse_formencoded body
28
+ when json? && !body.empty?
29
+ SymHash.new JSON.parse body
34
30
  else
35
- qs
31
+ {}
36
32
  end
37
33
  end
38
34
 
39
- def recurse_symhash h
40
- h.each do |k,v|
41
- if Hash === v
42
- h[k] = Responder.symhash.merge! v
43
- recurse_symhash h[k]
44
- end
45
- end
46
- h
35
+ def parse_query_string_and_post_body
36
+ parse_query_string.merge! parse_post_body
47
37
  end
48
38
 
49
39
  def form_encoded?
@@ -64,4 +54,30 @@ module Angelo
64
54
 
65
55
  end
66
56
 
57
+ class SymHash < Hash
58
+
59
+ # Returns a Hash that allows values to be fetched with String or
60
+ # Symbol keys.
61
+ def initialize h = nil
62
+ super(){|hash,key| hash[key.to_s] if Symbol === key}
63
+ unless h.nil?
64
+ merge! h
65
+
66
+ # Replace values that are Hashes with SymHashes, recursively.
67
+ each do |k,v|
68
+ self[k] = case v
69
+ when Hash
70
+ SymHash.new(v)
71
+ when Array
72
+ v.map {|e| Hash === e ? SymHash.new(e) : e}
73
+ else
74
+ v
75
+ end
76
+ end
77
+
78
+ end
79
+ end
80
+
81
+ end
82
+
67
83
  end
@@ -25,17 +25,13 @@ module Angelo
25
25
  @default_headers
26
26
  end
27
27
 
28
- def symhash
29
- Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
30
- end
31
-
32
28
  end
33
29
 
34
- attr_accessor :connection, :request
30
+ attr_accessor :connection, :mustermann, :request
35
31
  attr_writer :base
36
32
 
37
33
  def initialize &block
38
- @response_handler = Base.compile! :request_handler, &block
34
+ @response_handler = block
39
35
  end
40
36
 
41
37
  def reset!
@@ -48,7 +44,7 @@ module Angelo
48
44
  def handle_request
49
45
  if @response_handler
50
46
  @base.filter :before
51
- @body = catch(:halt) { @response_handler.bind(@base).call || EMPTY_STRING }
47
+ @body = catch(:halt) { @base.instance_exec(&@response_handler) || EMPTY_STRING }
52
48
 
53
49
  # TODO any real reason not to run afters with SSE?
54
50
  case @body
@@ -164,8 +160,13 @@ module Angelo
164
160
  @body = EMPTY_STRING
165
161
 
166
162
  else
167
- unless @chunked and @body.respond_to? :each
168
- raise RequestError.new "what is this? #{@body}"
163
+ if respond_with? :json and @body.respond_to? :to_json
164
+ @body = @body.to_json
165
+ raise "uhhh? #{@body}" unless String === @body
166
+ else
167
+ unless @chunked and @body.respond_to? :each
168
+ raise RequestError.new "what is this? #{@body}"
169
+ end
169
170
  end
170
171
  end
171
172
 
@@ -13,29 +13,30 @@ module Angelo
13
13
  end
14
14
 
15
15
  def handle_request
16
- begin
17
- if @response_handler
18
- @base.filter :before
19
- @body = catch(:halt) { @base.eventsource &@response_handler.bind(@base) }
20
- if HALT_STRUCT === @body
21
- raise RequestError.new 'unknown sse error' unless @body.body == :sse
22
- end
23
-
24
- # TODO any real reason not to run afters with SSE?
25
- # @base.filter :after
26
-
27
- respond
28
- else
16
+ if !@response_handler
29
17
  raise NotImplementedError
18
+ end
19
+ @base.filter :before
20
+ @body = catch(:halt) do
21
+ @base.eventsource do |socket|
22
+ @base.instance_exec(socket, &@response_handler)
30
23
  end
31
- rescue IOError => ioe
32
- warn "#{ioe.class} - #{ioe.message}"
33
- rescue RequestError => re
34
- headers SSE_HEADER
35
- handle_error re, re.type
36
- rescue => e
37
- handle_error e
38
24
  end
25
+ if HALT_STRUCT === @body
26
+ raise RequestError.new 'unknown sse error' unless @body.body == :sse
27
+ end
28
+
29
+ # TODO any real reason not to run afters with SSE?
30
+ # @base.filter :after
31
+
32
+ respond
33
+ rescue IOError => ioe
34
+ warn "#{ioe.class} - #{ioe.message}"
35
+ rescue RequestError => re
36
+ headers SSE_HEADER
37
+ handle_error re, re.type
38
+ rescue => e
39
+ handle_error e
39
40
  end
40
41
 
41
42
  def respond
@@ -22,10 +22,9 @@ module Angelo
22
22
  begin
23
23
  if @response_handler
24
24
  Angelo.log @connection, @request, @websocket, :switching_protocols
25
- @bound_response_handler ||= @response_handler.bind @base
26
25
  @websocket.on_pong &Responder::Websocket.on_pong
27
26
  @base.filter :before
28
- @bound_response_handler[@websocket]
27
+ @base.instance_exec(@websocket, &@response_handler)
29
28
  @base.filter :after
30
29
  else
31
30
  raise NotImplementedError
@@ -127,9 +127,11 @@ module Angelo
127
127
  extend Stash::ClassMethods
128
128
  include Stash
129
129
 
130
- def event data
131
- raise ArgumentError.new 'use #message method for "messages"' if @context == :default
132
- each {|s| s.write Angelo::Base.sse_event(@context, data)}
130
+ def event *args
131
+ name, data = args
132
+ raise ArgumentError if @context == :default and data.nil?
133
+ data, name = name, @context if data.nil?
134
+ each {|s| s.write Angelo::Base.sse_event(name, data)}
133
135
  nil
134
136
  end
135
137
 
@@ -13,16 +13,13 @@ module Angelo
13
13
  # hrm, sneaky
14
14
  #
15
15
  def self.included base
16
-
17
- # TODO: remove at 0.4
18
- warn "[DEPRECATED] Angelo::Tilt::ERB will be included by default in angelo >= 0.4"
19
- raise "Angelo requires Tilt >= 2.0, you have #{::Tilt::VERSION}" unless ::Tilt::VERSION.to_i >= 2
20
-
21
16
  base.extend ClassMethods
22
17
  end
23
18
 
24
19
  module ClassMethods
25
20
 
21
+ @reload_templates = false
22
+
26
23
  def view_glob *glob
27
24
  File.join views_dir, *glob
28
25
  end
@@ -33,6 +30,7 @@ module Angelo
33
30
  return h if (block_given? && yield(v))
34
31
  sym.gsub! File::SEPARATOR, UNDERSCORE
35
32
  sym.gsub! /\.\w+?\.erb$/, EMPTY_STRING
33
+ sym.gsub! /^#{LAYOUTS_DIR}#{UNDERSCORE}/, EMPTY_STRING
36
34
  h[sym.to_sym] = ::Tilt::ERBTemplate.new v
37
35
  h
38
36
  end
@@ -40,22 +38,51 @@ module Angelo
40
38
 
41
39
  def templates type = DEFAULT_TYPE
42
40
  @templates ||= {}
43
- @templates[type] ||= templatify('**', "*.#{type}.erb") do |v|
41
+ if reload_templates?
42
+ @templates[type] = load_templates(type)
43
+ else
44
+ @templates[type] ||= load_templates(type)
45
+ end
46
+ end
47
+
48
+ def load_templates type = DEFAULT_TYPE
49
+ templatify('**', "*.#{type}.erb") do |v|
44
50
  v =~ /^#{LAYOUTS_DIR}#{File::SEPARATOR}/
45
51
  end
46
52
  end
47
53
 
48
54
  def layout_templates type = DEFAULT_TYPE
49
- @layout_templates ||= templatify LAYOUTS_DIR, "*.#{type}.erb"
55
+ if reload_templates?
56
+ @layout_templates = load_layout_templates(type)
57
+ else
58
+ @layout_templates ||= load_layout_templates(type)
59
+ end
60
+ end
61
+
62
+ def load_layout_templates type = DEFAULT_TYPE
63
+ templatify LAYOUTS_DIR, "*.#{type}.erb"
50
64
  end
51
65
 
52
66
  def default_layout type = DEFAULT_TYPE
53
67
  @default_layout ||= {}
54
- if @default_layout[type].nil?
55
- l = view_glob(DEFAULT_LAYOUT % type)
56
- @default_layout[type] = ::Tilt::ERBTemplate.new l if File.exist? l
68
+ if reload_templates?
69
+ @default_layout[type] = load_default_layout(type)
70
+ else
71
+ @default_layout[type] ||= load_default_layout(type)
57
72
  end
58
- @default_layout[type]
73
+ end
74
+
75
+ def load_default_layout type = DEFAULT_TYPE
76
+ l = view_glob(DEFAULT_LAYOUT % type)
77
+ ::Tilt::ERBTemplate.new l if File.exist? l
78
+ end
79
+
80
+ def reload_templates! on = true
81
+ @reload_templates = on
82
+ end
83
+
84
+ def reload_templates?
85
+ @reload_templates
59
86
  end
60
87
 
61
88
  end
@@ -1,3 +1,3 @@
1
1
  module Angelo
2
- VERSION = '0.3.3'
2
+ VERSION = '0.4.0'
3
3
  end