armstrong 0.2.8 → 0.3.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.
- data/README.md +4 -7
- data/armstrong.gemspec +1 -1
- data/bin/armstrong +5 -6
- data/demo/armstrong_test.rb +5 -7
- data/lib/armstrong/connection.rb +2 -2
- data/lib/armstrong/data_structures.rb +3 -3
- data/lib/armstrong/main_actors.rb +36 -25
- data/lib/armstrong.rb +20 -23
- metadata +5 -5
data/README.md
CHANGED
@@ -40,13 +40,11 @@ There's a sample `mongrel2.conf` and `config.sqlite` in the `demo` folder, feel
|
|
40
40
|
require './lib/armstrong'
|
41
41
|
|
42
42
|
get "/" do
|
43
|
-
|
43
|
+
"hello world"
|
44
44
|
end
|
45
45
|
|
46
46
|
Just like in Sinatra, we state the verb we want to use, the path, and give it a block with the relevant code to execute. So far only 'GET' requests are supported but more will come out in later builds.
|
47
47
|
|
48
|
-
You can also call the `get_message` method which returns the request from the browser, and then reply to the request with `reply(request, message)`. `reply_string(message)` is a helper function that grabs the message and instantly replies to it with `message`.
|
49
|
-
|
50
48
|
Now you should run `ruby armstrong_test.rb` and then visit [localhost:6767](http://localhost:6767/) and relish in the 'Hello World'.
|
51
49
|
|
52
50
|
## more functionality ##
|
@@ -55,12 +53,11 @@ commit e86c74aed added functionality for parameters in your path. These are simp
|
|
55
53
|
|
56
54
|
require 'armstrong'
|
57
55
|
|
58
|
-
get "/:id" do
|
59
|
-
|
60
|
-
reply req, "id: #{req[:params]["id"]}"
|
56
|
+
get "/:id" do |env|
|
57
|
+
"id: #{env[:params]["id"]}"
|
61
58
|
end
|
62
59
|
|
63
|
-
The params are always going to be stored in
|
60
|
+
The params are always going to be stored in `env`, naturally.
|
64
61
|
|
65
62
|
## benchmarking ##
|
66
63
|
|
data/armstrong.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Gem::Specification.new 'armstrong', '0.
|
1
|
+
Gem::Specification.new 'armstrong', '0.3.0' do |s|
|
2
2
|
s.description = "Armstrong is an Mongrel2 fronted, actor-based web development framework similar in style to sinatra. With natively-threaded interpreters (Rubinius2), Armstrong provides true concurrency and high stability, by design."
|
3
3
|
s.summary = "Highly concurrent, sinatra-like framework"
|
4
4
|
s.author = "Artem Titoulenko"
|
data/bin/armstrong
CHANGED
@@ -72,8 +72,7 @@ servers = [armstrong_serv]
|
|
72
72
|
inform if ARGV.empty?
|
73
73
|
|
74
74
|
case ARGV[0]
|
75
|
-
when 'c'
|
76
|
-
when 'create'
|
75
|
+
when 'create', 'c'
|
77
76
|
inform if ARGV[1].nil?
|
78
77
|
port = (ARGV[2].nil? ? 6767 : ARGV[2])
|
79
78
|
mkdir ARGV[1]
|
@@ -85,8 +84,7 @@ when 'create'
|
|
85
84
|
File.open("#{ARGV[1]}.rb", 'w') {|file| file.puts "require 'rubygems'\nrequire 'armstrong'\n\n"}
|
86
85
|
puts "Created app #{ARGV[1]} that will run on port #{port}"
|
87
86
|
|
88
|
-
when 's'
|
89
|
-
when 'start'
|
87
|
+
when 'start', 's'
|
90
88
|
host = (ARGV[1].nil? ? 'localhost' : ARGV[1])
|
91
89
|
db = (ARGV[2].nil? ? 'config.sqlite' : ARGV[2])
|
92
90
|
|
@@ -101,9 +99,10 @@ when 'start'
|
|
101
99
|
puts "now running #{file}.rb"
|
102
100
|
puts `ruby #{file}.rb`
|
103
101
|
|
104
|
-
when 't'
|
105
|
-
when 'stop'
|
102
|
+
when 'stop', 't'
|
106
103
|
host = (ARGV[1].nil? ? 'localhost' : ARGV[1])
|
107
104
|
puts `m2sh stop -host #{host}`
|
108
105
|
puts "Stopped #{host}, I think"
|
106
|
+
else
|
107
|
+
inform
|
109
108
|
end
|
data/demo/armstrong_test.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
require '../lib/armstrong'
|
2
2
|
|
3
3
|
get "/" do
|
4
|
-
|
4
|
+
"hello world"
|
5
5
|
end
|
6
6
|
|
7
|
-
get "/:id" do
|
8
|
-
|
9
|
-
reply req, "id: #{req[:params]["id"]}"
|
7
|
+
get "/:id" do |env|
|
8
|
+
"id: #{env[:params]["id"]}"
|
10
9
|
end
|
11
10
|
|
12
|
-
get "/:id/do" do
|
13
|
-
|
14
|
-
reply req, "do: #{req[:params]["id"]}"
|
11
|
+
get "/:id/do" do |env|
|
12
|
+
"do: #{env[:params]["id"]}"
|
15
13
|
end
|
data/lib/armstrong/connection.rb
CHANGED
@@ -44,7 +44,7 @@ class Connection
|
|
44
44
|
self.send(request[:uuid], [request[:id]], message)
|
45
45
|
end
|
46
46
|
|
47
|
-
def reply_http(req,
|
47
|
+
def reply_http(req, code=200, headers={"Content-type" => "text/html"}, body="")
|
48
48
|
self.reply(req, http_response(body, code, headers))
|
49
49
|
end
|
50
50
|
|
@@ -52,7 +52,7 @@ class Connection
|
|
52
52
|
def http_response(body, code, headers)
|
53
53
|
headers['Content-Length'] = body.size
|
54
54
|
headers_s = headers.map{|k, v| "%s: %s" % [k,v]}.join("\r\n")
|
55
|
-
|
55
|
+
|
56
56
|
"HTTP/1.1 #{code} #{StatusMessage[code.to_i]}\r\n#{headers_s}\r\n\r\n#{body}"
|
57
57
|
end
|
58
58
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
AddRoute = Struct.new :route, :method
|
2
2
|
AddRoutes = Struct.new :routes
|
3
|
-
|
4
|
-
Request = Struct.new :data
|
3
|
+
Request = Struct.new :env
|
5
4
|
ConnectionInformation = Struct.new :connection
|
6
|
-
Reply = Struct.new :
|
5
|
+
Reply = Struct.new :env, :code, :headers, :body
|
6
|
+
MessageAndProc = Struct.new :env, :proccess
|
@@ -2,6 +2,7 @@ module Aleph
|
|
2
2
|
class Base
|
3
3
|
class << self
|
4
4
|
attr_accessor :replier, :request_handler, :supervisor
|
5
|
+
attr_accessor :replier_proc, :request_handler_proc, :supervisor_proc, :container_proc
|
5
6
|
end
|
6
7
|
end
|
7
8
|
end
|
@@ -17,21 +18,21 @@ def process_route(route, pattern, keys, values = [])
|
|
17
18
|
params
|
18
19
|
end
|
19
20
|
|
20
|
-
Aleph::Base.
|
21
|
+
Aleph::Base.replier_proc = Proc.new do
|
21
22
|
@name = "replier"
|
22
23
|
puts "started (#{@name})"
|
23
24
|
Actor.register(:replier, Actor.current)
|
25
|
+
Aleph::Base.replier = Actor.current
|
24
26
|
conn = nil
|
25
27
|
|
26
28
|
loop do
|
27
29
|
Actor.receive do |msg|
|
28
30
|
msg.when(ConnectionInformation) do |c|
|
29
|
-
#puts "replier: got connection information #{c.inspect}"
|
30
31
|
conn = c.connection
|
31
32
|
end
|
32
33
|
msg.when(Reply) do |m|
|
33
34
|
begin
|
34
|
-
conn.reply_http(m.
|
35
|
+
conn.reply_http(m.env, m.code, m.headers, m.body)
|
35
36
|
rescue Exception => e
|
36
37
|
puts "Actor[:replier]: I messed up with exception: #{e.message}"
|
37
38
|
end
|
@@ -40,40 +41,49 @@ Aleph::Base.replier = Proc.new do
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
|
44
|
+
# this nifty mess helps us just to use the output of the Procs that handle
|
45
|
+
# the request instead of making the user manually catch messages and send
|
46
|
+
# them out to the replier.
|
47
|
+
|
48
|
+
Aleph::Base.container_proc = Proc.new do
|
49
|
+
data = Actor.receive
|
50
|
+
env, proccess = data.env, data.proccess
|
51
|
+
response = proccess.call(env)
|
52
|
+
if response.is_a? Array
|
53
|
+
#just like Rack: env, code, headers, body. HINT HINT
|
54
|
+
Aleph::Base.replier << Reply.new(env, response[0], response[1], response[2])
|
55
|
+
else
|
56
|
+
Aleph::Base.replier << Reply.new(env, 200, {"Content-Type" => "text/html"}, response)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Aleph::Base.request_handler_proc = Proc.new do
|
44
61
|
@name = "request_handler"
|
45
62
|
puts "started (#{@name})"
|
46
63
|
Actor.register(:request_handler, Actor.current)
|
64
|
+
Aleph::Base.request_handler = Actor.current
|
47
65
|
Actor.trap_exit = true
|
48
66
|
|
49
|
-
routes =
|
67
|
+
routes = {}
|
50
68
|
loop do
|
51
69
|
Actor.receive do |f|
|
52
|
-
f.when(AddRoute) do |r|
|
53
|
-
routes << [r.route, r.method]
|
54
|
-
end
|
55
|
-
|
56
70
|
f.when(AddRoutes) do |r|
|
57
|
-
r.routes
|
58
|
-
routes << [k.route, k.method]
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
f.when(ShowRoutes) do |r|
|
63
|
-
routes.each {|s| puts s}
|
71
|
+
routes = r.routes
|
64
72
|
end
|
65
73
|
|
66
74
|
f.when(Request) do |r|
|
67
75
|
failure = true
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
76
|
+
r.env[:headers] = JSON.parse(r.env[:headers])
|
77
|
+
verb = r.env[:headers]["METHOD"]
|
78
|
+
routes[verb].each do |route|
|
79
|
+
if route.route[0].match(r.env[:path])
|
80
|
+
r.env[:params] = process_route(r.env[:path], route.route[0], route.route[1])
|
81
|
+
Actor.spawn(&Aleph::Base.container_proc) << MessageAndProc.new(r.env, route.method)
|
73
82
|
failure = false
|
83
|
+
break
|
74
84
|
end
|
75
85
|
end
|
76
|
-
|
86
|
+
Aleph::Base.replier << Reply.new(r.env, 404, {'Content-type'=>'text/html'}, "<h1>404</h1>") if failure
|
77
87
|
end
|
78
88
|
|
79
89
|
# f.when(Actor::DeadActorError) do |exit|
|
@@ -84,9 +94,10 @@ Aleph::Base.request_handler = Proc.new do
|
|
84
94
|
end
|
85
95
|
|
86
96
|
#if this dies, all hell will break loose
|
87
|
-
Aleph::Base.
|
97
|
+
Aleph::Base.supervisor_proc = Proc.new do
|
88
98
|
puts "started (supervisor)"
|
89
99
|
Actor.register(:supervisor, Actor.current)
|
100
|
+
Aleph::Base.supervisor = Actor.current
|
90
101
|
Actor.trap_exit = true
|
91
102
|
|
92
103
|
Actor.link(Actor[:replier])
|
@@ -98,9 +109,9 @@ Aleph::Base.supervisor = Proc.new do
|
|
98
109
|
"#{exit.actor.name} died with reason: #{exit.reason}"
|
99
110
|
case exit.actor.name
|
100
111
|
when "request_handler"
|
101
|
-
Actor.spawn_link(&@
|
112
|
+
Actor.spawn_link(&@request_handler_proc)
|
102
113
|
when "replier"
|
103
|
-
Actor.spawn_link(&@
|
114
|
+
Actor.spawn_link(&@replier_proc)
|
104
115
|
end
|
105
116
|
end
|
106
117
|
end
|
data/lib/armstrong.rb
CHANGED
@@ -2,6 +2,7 @@ require 'actor'
|
|
2
2
|
require 'rubygems'
|
3
3
|
require 'lazy'
|
4
4
|
require 'open-uri'
|
5
|
+
require 'json'
|
5
6
|
|
6
7
|
libdir = File.dirname(__FILE__)
|
7
8
|
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
@@ -9,23 +10,11 @@ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
|
9
10
|
require "armstrong/connection"
|
10
11
|
require 'armstrong/data_structures'
|
11
12
|
require 'armstrong/main_actors'
|
12
|
-
|
13
|
-
def get_request
|
14
|
-
return Actor.receive
|
15
|
-
end
|
16
|
-
|
17
|
-
def reply(request, body, code=200, headers={"Content-type" => "text/html"})
|
18
|
-
Actor[:replier] << Reply.new(request, body, code, headers)
|
19
|
-
end
|
20
|
-
|
21
|
-
def reply_string(body, code=200, headers={"Content-type" => "text/html"})
|
22
|
-
Actor[:replier] << Reply.new(Actor.receive, body, code, headers)
|
23
|
-
end
|
24
13
|
|
25
14
|
module Aleph
|
26
15
|
class Base
|
27
16
|
class << self
|
28
|
-
attr_accessor :conn, :routes
|
17
|
+
attr_accessor :conn, :routes
|
29
18
|
|
30
19
|
def new_uuid
|
31
20
|
values = [
|
@@ -40,8 +29,16 @@ module Aleph
|
|
40
29
|
"%04x%04x-%04x-%04x-%04x%06x%06x" % values
|
41
30
|
end
|
42
31
|
|
43
|
-
def get(path, &block)
|
44
|
-
|
32
|
+
def get(path, &block) route "GET", path, &block end
|
33
|
+
def put(path, &block) route "PUT", path, &block end
|
34
|
+
def post(path, &block) route "POST", path, &block end
|
35
|
+
def head(path, &block) route "HEAD", path, &block end
|
36
|
+
def delete(path, &block) route "DELETE", path, &block end
|
37
|
+
def patch(path, &block) route "PATCH", path, &block end
|
38
|
+
|
39
|
+
def route(verb, path, &block)
|
40
|
+
@routes ||= {}
|
41
|
+
(@routes[verb] ||= []) << AddRoute.new(compile(path), block)
|
45
42
|
end
|
46
43
|
|
47
44
|
private
|
@@ -88,31 +85,31 @@ module Aleph
|
|
88
85
|
|
89
86
|
#ensure that all actors are launched. Yea.
|
90
87
|
done = Lazy::demand(Lazy::promise do |done|
|
91
|
-
Actor.spawn(&Aleph::Base.
|
88
|
+
Actor.spawn(&Aleph::Base.replier_proc)
|
92
89
|
done = Lazy::demand(Lazy::promise do |done|
|
93
|
-
Actor.spawn(&Aleph::Base.
|
90
|
+
Actor.spawn(&Aleph::Base.request_handler_proc)
|
94
91
|
done = Lazy::demand(Lazy::promise do |done|
|
95
|
-
Actor.spawn(&Aleph::Base.
|
92
|
+
Actor.spawn(&Aleph::Base.supervisor_proc)
|
96
93
|
done = true
|
97
94
|
end)
|
98
95
|
end)
|
99
96
|
end)
|
100
97
|
|
101
|
-
|
98
|
+
Aleph::Base.replier << ConnectionInformation.new(@conn) if done
|
102
99
|
|
103
100
|
done = Lazy::demand(Lazy::Promise.new do |done|
|
104
|
-
|
101
|
+
Aleph::Base.request_handler << AddRoutes.new(@routes)
|
105
102
|
done = true
|
106
103
|
end)
|
107
104
|
|
108
|
-
if
|
105
|
+
if Aleph::Base.supervisor && Aleph::Base.request_handler && Aleph::Base.replier && done
|
109
106
|
puts "","="*56,"Armstrong has launched on #{Time.now}","="*56, ""
|
110
107
|
end
|
111
108
|
|
112
109
|
# main loop
|
113
110
|
loop do
|
114
111
|
req = @conn.receive
|
115
|
-
|
112
|
+
Aleph::Base.request_handler << Request.new(req) if !req.nil?
|
116
113
|
end
|
117
114
|
end
|
118
115
|
end
|
@@ -132,7 +129,7 @@ module Aleph
|
|
132
129
|
end
|
133
130
|
end
|
134
131
|
|
135
|
-
delegate :get
|
132
|
+
delegate :get, :post, :put, :patch, :delete, :head
|
136
133
|
|
137
134
|
class << self
|
138
135
|
attr_accessor :target
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: armstrong
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3600368719662819624
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Artem Titoulenko
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-17 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: ffi
|