angelo 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb21b9dcec8959a4845e5e6063ae0051b71589c4
4
- data.tar.gz: 88e2ca4bd79df106deaa35c7509d22b237e5918a
3
+ metadata.gz: b034c03ba6c0b50e1d31e5ee1c0bd043b70d8d2e
4
+ data.tar.gz: b4e9ba5af2b009a7fa292f97d6a8b2a3831ec709
5
5
  SHA512:
6
- metadata.gz: a468e2ad3a12a7b3a7fab94816d13141f25675651d456651470c287b1773ef9e766b3a3eba7e45cb6310159caeef32b222026cc2853bded2c7b78ce6d6ccd4ad
7
- data.tar.gz: f844a3b8410c35392490058ee699202b7a29f4b9df923635ec8500485789832db69f470db62f39e4001842cde0581b178207ab9d4bd80bff6ca5175bf85c3014
6
+ metadata.gz: 64b482cc286556b98b03f211134bed314d2c3b3564640aca4b9d2882d4b31d157760d7ef476d7fcb257d3065152f38c4573a24226d3de19f1309136c9a3f8730
7
+ data.tar.gz: 000d06333538e664c71d40e6f0371d6d946c586efe2f1cb3dd42c08d510cb459faa772cacef57c3135315609f85387c70bc147cd0a26f16e8cb0eb8c62e1c632
data/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  changelog
2
2
  =========
3
3
 
4
+ ### 0.1.4 26 mar 2014
5
+
6
+ * add Base.async, Base.on_pong, and Base#async
7
+ * fix websockets context handling, removal
8
+ * add ping task to reactor
9
+
4
10
  ### 0.1.3 24 mar 2014
5
11
 
6
12
  * better testing of websockets
data/README.md CHANGED
@@ -9,7 +9,8 @@ A [Sinatra](https://github.com/sinatra/sinatra)-esque DSL for [Reel](https://git
9
9
 
10
10
  * "easy" websocket support via `socket '/path' do |s|` route handler
11
11
  * "easy" websocket stashing via `websockets` helper
12
- * no rack/rack-style params
12
+ * "easy" event handling via `async` helpers
13
+ * no rack
13
14
  * optional tilt/erb support
14
15
  * optional mustermann support
15
16
 
@@ -24,6 +25,9 @@ class Foo < Angelo::Base
24
25
 
25
26
  TEST = {foo: "bar", baz: 123, bat: false}.to_json
26
27
 
28
+ HEART = '<3'
29
+ @@hearting = false
30
+
27
31
  def pong; 'pong'; end
28
32
  def foo; params[:foo]; end
29
33
 
@@ -46,7 +50,7 @@ class Foo < Angelo::Base
46
50
 
47
51
  socket '/ws' do |ws|
48
52
  websockets[:emit_test] << ws
49
- while msg = ws.read
53
+ ws.on_message do |msg|
50
54
  5.times { ws.write TEST }
51
55
  ws.write foo.to_json
52
56
  end
@@ -60,6 +64,24 @@ class Foo < Angelo::Base
60
64
  websockets[:other] << ws
61
65
  end
62
66
 
67
+ socket '/hearts' do |ws|
68
+
69
+ # this is a call to Base#async, actually calling
70
+ # the reactor to start the task
71
+ #
72
+ async :hearts unless @@hearting
73
+
74
+ websockets[:hearts] << ws
75
+ end
76
+
77
+ # this is a call to Base.async, defining the task
78
+ # to perform on the reactor
79
+ #
80
+ async :hearts do
81
+ @@hearting = true
82
+ every(10){ websockets[:hearts].each {|ws| ws.write HEART } }
83
+ end
84
+
63
85
  end
64
86
 
65
87
  Foo.run
data/lib/angelo.rb CHANGED
@@ -44,6 +44,8 @@ module Angelo
44
44
 
45
45
  LOG_FORMAT = '%s - - "%s %s%s HTTP/%s" %d %s'
46
46
 
47
+ DEFAULT_PING_TIME = 30
48
+
47
49
  def self.log connection, request, socket, status, body_size = '-'
48
50
 
49
51
  remote_ip = ->{
data/lib/angelo/base.rb CHANGED
@@ -10,7 +10,9 @@ module Angelo
10
10
  @@addr = DEFAULT_ADDR
11
11
  @@port = DEFAULT_PORT
12
12
 
13
- if ARGV.any?
13
+ @@ping_time = DEFAULT_PING_TIME
14
+
15
+ if ARGV.any? and not Kernel.const_defined?('RSpec')
14
16
  require 'optparse'
15
17
  OptionParser.new { |op|
16
18
  op.on('-p port', 'set the port (default is 4567)') { |val| @@port = Integer(val) }
@@ -22,7 +24,7 @@ module Angelo
22
24
 
23
25
  class << self
24
26
 
25
- attr_accessor :app_file
27
+ attr_accessor :app_file, :server
26
28
 
27
29
  def inherited subclass
28
30
  subclass.app_file = caller(1).map {|l| l.split(/:(?=|in )/, 3)[0,1]}.flatten[0]
@@ -76,8 +78,16 @@ module Angelo
76
78
  routes[:socket][path] = WebsocketResponder.new &block
77
79
  end
78
80
 
81
+ def on_pong &block
82
+ WebsocketResponder.on_pong = block
83
+ end
84
+
85
+ def async name, &block
86
+ Angelo::Server.define_action name, &block
87
+ end
88
+
79
89
  def websockets
80
- @websockets ||= WebsocketsArray.new
90
+ @websockets ||= WebsocketsArray.new server
81
91
  @websockets.reject! &:closed?
82
92
  @websockets
83
93
  end
@@ -88,6 +98,7 @@ module Angelo
88
98
 
89
99
  def run addr = @@addr, port = @@port
90
100
  @server = Angelo::Server.new self, addr, port
101
+ @server.async.ping_websockets
91
102
  trap "INT" do
92
103
  @server.terminate if @server and @server.alive?
93
104
  exit
@@ -97,6 +108,10 @@ module Angelo
97
108
 
98
109
  end
99
110
 
111
+ def async meth, *args
112
+ self.class.server.async.__send__ meth, *args
113
+ end
114
+
100
115
  def params
101
116
  @params ||= case request.method
102
117
  when GET; parse_query_string
@@ -107,22 +122,67 @@ module Angelo
107
122
 
108
123
  def websockets; self.class.websockets; end
109
124
 
125
+ async :handle_websocket do |ws|
126
+ begin
127
+ while !ws.closed? do
128
+ ws.read
129
+ end
130
+ rescue IOError
131
+ websockets.remove_socket ws
132
+ end
133
+ end
134
+
135
+ async :ping_websockets do
136
+ every(@@ping_time) do
137
+ websockets.each do |ws|
138
+ ws.socket << ::WebSocket::Message.ping.to_data
139
+ end
140
+ end
141
+ end
142
+
110
143
  class WebsocketsArray < Array
144
+ include Celluloid::Logger
145
+
146
+ @@peeraddrs = {}
147
+ @@socket_context = {}
148
+
149
+ def initialize server, context = nil
150
+ @context, @server = context, server
151
+ super()
152
+ end
153
+
154
+ def << ws
155
+ @@socket_context[ws] = @context if @context
156
+ @@peeraddrs[ws] = ws.peeraddr
157
+ @server.async.handle_websocket ws
158
+ super ws
159
+ end
111
160
 
112
161
  def each &block
113
162
  super do |ws|
114
163
  begin
115
164
  yield ws
116
- rescue Reel::SocketError => rse
117
- warn "#{rse.class} - #{rse.message}"
118
- delete ws
165
+ rescue Reel::SocketError
166
+ remove_socket ws
119
167
  end
120
168
  end
121
169
  end
122
170
 
171
+ def remove_socket ws
172
+ if c = @@socket_context[ws]
173
+ warn "removing socket from context ':#{c}' (#{@@peeraddrs[ws][2]})"
174
+ self[c].delete ws
175
+ else
176
+ warn "removing socket (#{@@peeraddrs[ws][2]})"
177
+ delete ws
178
+ end
179
+ @@peeraddrs.delete ws
180
+ end
181
+
123
182
  def [] context
183
+ raise ArgumentError.new "symbol required" unless Symbol === context
124
184
  @@websockets ||= {}
125
- @@websockets[context] ||= self.class.new
185
+ @@websockets[context] ||= self.class.new @server, context
126
186
  end
127
187
 
128
188
  end
@@ -2,6 +2,16 @@ module Angelo
2
2
 
3
3
  class WebsocketResponder < Responder
4
4
 
5
+ class << self
6
+
7
+ attr_writer :on_pong
8
+
9
+ def on_pong
10
+ @on_pong ||= ->(e){}
11
+ end
12
+
13
+ end
14
+
5
15
  def params
6
16
  @params ||= parse_query_string
7
17
  @params
@@ -19,6 +29,7 @@ module Angelo
19
29
  if @response_handler
20
30
  Angelo.log @connection, @request, @websocket, :switching_protocols
21
31
  @bound_response_handler ||= @response_handler.bind @base
32
+ @websocket.on_pong &WebsocketResponder.on_pong
22
33
  @bound_response_handler[@websocket]
23
34
  else
24
35
  raise NotImplementedError
@@ -6,7 +6,6 @@ module Angelo
6
6
  module Helpers
7
7
 
8
8
  HTTP_URL = 'http://%s:%d'
9
- WS_URL = 'ws://%s:%d'
10
9
 
11
10
  attr_reader :last_response
12
11
 
@@ -17,6 +16,7 @@ module Angelo
17
16
  app.class_eval { content_type :html } # reset
18
17
  app.class_eval &block
19
18
  @server = Angelo::Server.new app
19
+ app.server = @server
20
20
  $reactor = Reactor.new unless $reactor.alive?
21
21
  end
22
22
 
@@ -82,6 +82,8 @@ module Angelo
82
82
  def_delegator :@socket, :write
83
83
  def_delegators :@driver, :binary, :close, :text
84
84
 
85
+ WS_URL = 'ws://%s:%d'
86
+
85
87
  attr_reader :driver, :socket
86
88
  attr_writer :addr, :port, :path, :on_close, :on_message, :on_open
87
89
 
data/lib/angelo/server.rb CHANGED
@@ -4,8 +4,11 @@ require 'mime-types'
4
4
  module Angelo
5
5
 
6
6
  class Server < Reel::Server
7
+ extend Forwardable
7
8
  include Celluloid::Logger
8
9
 
10
+ def_delegator :@base, :websockets
11
+
9
12
  def initialize base, host = '127.0.0.1', port = 4567
10
13
  @base = base
11
14
  info "Angelo #{VERSION}"
@@ -22,6 +25,14 @@ module Angelo
22
25
  # RubyProf.pause
23
26
  end
24
27
 
28
+ def self.define_action name, &action
29
+ define_method name, &action
30
+ end
31
+
32
+ def self.remove_action name
33
+ remove_method name
34
+ end
35
+
25
36
  private
26
37
 
27
38
  def dispatch! meth, connection, request
@@ -1,3 +1,3 @@
1
1
  module Angelo
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: angelo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenichi Nakamura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-24 00:00:00.000000000 Z
11
+ date: 2014-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reel
@@ -113,3 +113,4 @@ test_files:
113
113
  - spec/test_app_root/public/what.png
114
114
  - spec/test_app_root/views/index.html.erb
115
115
  - spec/test_app_root/views/layout.html.erb
116
+ has_rdoc: