armstrong 0.2.8 → 0.3.0

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