angelo 0.1.3 → 0.1.4

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.
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: