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 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
- reply_string "hello world"
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
- req = get_request
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 the request, naturally.
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.2.8' do |s|
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
@@ -1,15 +1,13 @@
1
1
  require '../lib/armstrong'
2
2
 
3
3
  get "/" do
4
- reply_string "hello world"
4
+ "hello world"
5
5
  end
6
6
 
7
- get "/:id" do
8
- req = get_request
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
- req = get_request
14
- reply req, "do: #{req[:params]["id"]}"
11
+ get "/:id/do" do |env|
12
+ "do: #{env[:params]["id"]}"
15
13
  end
@@ -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, body, code=200, headers={"Content-type" => "text/html"})
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
- ShowRoutes = Struct.new :this
4
- Request = Struct.new :data
3
+ Request = Struct.new :env
5
4
  ConnectionInformation = Struct.new :connection
6
- Reply = Struct.new :data, :body, :code, :headers
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.replier = Proc.new do
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.data, m.body, m.code, m.headers)
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
- Aleph::Base.request_handler = Proc.new do
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.each do |k|
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
- routes.each do |route|
69
- if route[0][0].match(r.data[:path])
70
- r.data[:params] = process_route(r.data[:path], route[0][0], route[0][1])
71
- #puts r.data.inspect
72
- Actor.spawn_link(&route[1]) << r.data
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
- Actor[:replier] << Reply.new(r.data, "404", 404, {'Content-type'=>'text/html'}) if failure
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.supervisor = Proc.new do
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(&@request_handler)
112
+ Actor.spawn_link(&@request_handler_proc)
102
113
  when "replier"
103
- Actor.spawn_link(&@replier)
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, :pairs
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
- (@pairs ||= []) << AddRoute.new(compile(path), block)
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.replier)
88
+ Actor.spawn(&Aleph::Base.replier_proc)
92
89
  done = Lazy::demand(Lazy::promise do |done|
93
- Actor.spawn(&Aleph::Base.request_handler)
90
+ Actor.spawn(&Aleph::Base.request_handler_proc)
94
91
  done = Lazy::demand(Lazy::promise do |done|
95
- Actor.spawn(&Aleph::Base.supervisor)
92
+ Actor.spawn(&Aleph::Base.supervisor_proc)
96
93
  done = true
97
94
  end)
98
95
  end)
99
96
  end)
100
97
 
101
- Actor[:replier] << ConnectionInformation.new(@conn) if done
98
+ Aleph::Base.replier << ConnectionInformation.new(@conn) if done
102
99
 
103
100
  done = Lazy::demand(Lazy::Promise.new do |done|
104
- Actor[:request_handler] << AddRoutes.new(@pairs)
101
+ Aleph::Base.request_handler << AddRoutes.new(@routes)
105
102
  done = true
106
103
  end)
107
104
 
108
- if Actor[:supervisor] && Actor[:request_handler] && Actor[:replier] && done
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
- Actor[:request_handler] << Request.new(req) if !req.nil?
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: 2499712927119017196
4
+ hash: 3600368719662819624
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 8
10
- version: 0.2.8
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-16 00:00:00 Z
18
+ date: 2011-11-17 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: ffi