angelo 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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