angstrom 0.5.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/Gemfile +5 -0
 - data/README.md +109 -0
 - data/Rakefile +22 -0
 - data/angstrom.gemspec +16 -0
 - data/bin/angstrom +108 -0
 - data/bin/armstrong +108 -0
 - data/demo/mongrel2.conf +32 -0
 - data/lib/angstrom.rb +130 -0
 - data/lib/angstrom/connection.rb +142 -0
 - data/lib/angstrom/data_structures.rb +10 -0
 - data/lib/angstrom/main_actors.rb +183 -0
 - data/lib/angstrom/version.rb +3 -0
 - metadata +137 -0
 
    
        data/Gemfile
    ADDED
    
    
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,109 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Angstrom #
         
     | 
| 
      
 2 
     | 
    
         
            +
            An evented, fiber-based server for Ruby. This project is heavily based on [Brubeck](http://brubeck.io). The goal was to make it really easy to make an evented server that acted quick and scaled infinitely. This is accomplished by using [Mongrel2](http://mongrel2.org), [ZeroMQ](http://zeromq.org) and [Rubinius](rubini.us). Rubinius has the actor gem included already so it makes it really convenient to just use rubinius. Also, the 2.0.0dev branch has super nice thread handling, allowing for true Ruby concurrency that MRI just can't offer with its GIL.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            ## Mongrel2 and ZeroMQ ##
         
     | 
| 
      
 5 
     | 
    
         
            +
            Although it seems like a strange direction to start writing servers in, eventually most companies end up in the realm of evented servers. This is because it offers nearly infinite scalability for free.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            This is possible because of Mongrel2 and ZeroMQ. Mongrel2 acts as your server and parses requests. It then sends out ZeroMQ messages to your handlers and proxies and then returns their responses. Because it uses ZeroMQ messages, Mongrel2 can send messages anywhere and to any language. Conversely, it sends messages in a round-robin style, so scalability is achieved by just starting up another instance of your server.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## setup ##
         
     | 
| 
      
 10 
     | 
    
         
            +
            #### Rubinius ####
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            	rvm install rbx
         
     | 
| 
      
 13 
     | 
    
         
            +
            	rvm use rbx
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            #### ZeroMQ ####
         
     | 
| 
      
 16 
     | 
    
         
            +
            Go grab the zip from [zeromq/zeromq2-1](https://github.com/zeromq/zeromq2-1), unzip it, and in the directory run:
         
     | 
| 
      
 17 
     | 
    
         
            +
            	
         
     | 
| 
      
 18 
     | 
    
         
            +
            	./autogen.sh; ./configure; make; sudo make install
         
     | 
| 
      
 19 
     | 
    
         
            +
            	
         
     | 
| 
      
 20 
     | 
    
         
            +
            #### Angstrom as a gem ####
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            	gem install angstrom
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            #### ZMQ and other gems ####
         
     | 
| 
      
 25 
     | 
    
         
            +
            	gem install ffi-rzmq
         
     | 
| 
      
 26 
     | 
    
         
            +
            	gem install lazy
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            it should also install `ffi` and `ffi-rzmq` which are to dynamically load libs and call functions from them. Interesting stuff, but out of the scope of this measly README.
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            #### Mongrel2 ####
         
     | 
| 
      
 31 
     | 
    
         
            +
            Finally, go grab a copy of mongrel2 (1.7.5 tested) from the [Mongrel2](http://mongrel2.org) website.
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            There's a sample `mongrel2.conf` and `config.sqlite` in the `demo` folder, feel free to use those. Otherwise, load the `mongrel2.conf` into `m2sh` and then start the server.
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            	m2sh load -config mongrel2.conf -db config.sqlite
         
     | 
| 
      
 36 
     | 
    
         
            +
            	m2sh start -host localhost
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            ## minimal example ##
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            	require 'angstrom'
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            	get "/" do
         
     | 
| 
      
 43 
     | 
    
         
            +
            		"hello world"
         
     | 
| 
      
 44 
     | 
    
         
            +
            	end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 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 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            Now you should run `ruby angstrom_test.rb` and then visit [localhost:6767](http://localhost:6767/) and relish in the 'Hello World'.
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            ## more functionality ##
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            commit e86c74aed added functionality for parameters in your path. These are simply demonstrated in the `demo/angstrom_test.rb` file. For instance, you can extract the id of a certain part of your path like so:
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            	require 'angstrom'
         
     | 
| 
      
 55 
     | 
    
         
            +
            	
         
     | 
| 
      
 56 
     | 
    
         
            +
            	get "/:id" do |env|
         
     | 
| 
      
 57 
     | 
    
         
            +
            		"id: #{env[:params]["id"]}"
         
     | 
| 
      
 58 
     | 
    
         
            +
            	end
         
     | 
| 
      
 59 
     | 
    
         
            +
            	
         
     | 
| 
      
 60 
     | 
    
         
            +
            The params are always going to be stored in `env`, naturally.
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            You can also return other codes and custom headers by returning an array with the signature:
         
     | 
| 
      
 63 
     | 
    
         
            +
            	[code, headers, response]
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            ## benchmarking ##
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            #### Armstrong ####
         
     | 
| 
      
 68 
     | 
    
         
            +
            	$ siege -d 1 -c 150 -t 10s localhost:6767/
         
     | 
| 
      
 69 
     | 
    
         
            +
            	** SIEGE 2.70
         
     | 
| 
      
 70 
     | 
    
         
            +
            	** Preparing 150 concurrent users for battle.
         
     | 
| 
      
 71 
     | 
    
         
            +
            	The server is now under siege...
         
     | 
| 
      
 72 
     | 
    
         
            +
            	Lifting the server siege...      done.
         
     | 
| 
      
 73 
     | 
    
         
            +
            	Transactions:		        	5029 hits
         
     | 
| 
      
 74 
     | 
    
         
            +
            	Availability:		      		100.00 %
         
     | 
| 
      
 75 
     | 
    
         
            +
            	Elapsed time:		        	9.06 secs
         
     | 
| 
      
 76 
     | 
    
         
            +
            	Data transferred:	        	0.05 MB
         
     | 
| 
      
 77 
     | 
    
         
            +
            	Response time:		        	0.26 secs
         
     | 
| 
      
 78 
     | 
    
         
            +
            	Transaction rate:	      		555.08 trans/sec
         
     | 
| 
      
 79 
     | 
    
         
            +
            	Throughput:		        		0.01 MB/sec
         
     | 
| 
      
 80 
     | 
    
         
            +
            	Concurrency:		      		146.56
         
     | 
| 
      
 81 
     | 
    
         
            +
            	Successful transactions:        5029
         
     | 
| 
      
 82 
     | 
    
         
            +
            	Failed transactions:	           0
         
     | 
| 
      
 83 
     | 
    
         
            +
            	Longest transaction:	        0.67
         
     | 
| 
      
 84 
     | 
    
         
            +
            	Shortest transaction:	        0.02
         
     | 
| 
      
 85 
     | 
    
         
            +
            	
         
     | 
| 
      
 86 
     | 
    
         
            +
            #### Sinatra ####
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            _These benchmarks were done using Rubinius as the Ruby interpreter. You will get much better results for sinatra with MRI 1.9.2 but the concurrency will still plateau at about 110. I could not start up more than 110 concurrent users without sinatra closing all connections and blowing up._
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            	$ siege -d1 -c 110 -t 10s localhost:4567/
         
     | 
| 
      
 91 
     | 
    
         
            +
            	** SIEGE 2.70
         
     | 
| 
      
 92 
     | 
    
         
            +
            	** Preparing 20 concurrent users for battle.
         
     | 
| 
      
 93 
     | 
    
         
            +
            	The server is now under siege...
         
     | 
| 
      
 94 
     | 
    
         
            +
            	Lifting the server siege...      done.
         
     | 
| 
      
 95 
     | 
    
         
            +
            	Transactions:		        	1192 hits
         
     | 
| 
      
 96 
     | 
    
         
            +
            	Availability:		       		97.23 %
         
     | 
| 
      
 97 
     | 
    
         
            +
            	Elapsed time:		        	9.39 secs
         
     | 
| 
      
 98 
     | 
    
         
            +
            	Data transferred:	        	0.01 MB
         
     | 
| 
      
 99 
     | 
    
         
            +
            	Response time:		        	0.70 secs
         
     | 
| 
      
 100 
     | 
    
         
            +
            	Transaction rate:	      		126.94 trans/sec
         
     | 
| 
      
 101 
     | 
    
         
            +
            	Throughput:		        		0.00 MB/sec
         
     | 
| 
      
 102 
     | 
    
         
            +
            	Concurrency:		       		88.98
         
     | 
| 
      
 103 
     | 
    
         
            +
            	Successful transactions:        1192
         
     | 
| 
      
 104 
     | 
    
         
            +
            	Failed transactions:	          34
         
     | 
| 
      
 105 
     | 
    
         
            +
            	Longest transaction:	        1.39
         
     | 
| 
      
 106 
     | 
    
         
            +
            	Shortest transaction:	        0.20
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
            ## License ##
         
     | 
| 
      
 109 
     | 
    
         
            +
            GPLv3
         
     | 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            desc "Push actors to github, switch to master, merge actors, push master to github"
         
     | 
| 
      
 2 
     | 
    
         
            +
            namespace :super do
         
     | 
| 
      
 3 
     | 
    
         
            +
              task :push do
         
     | 
| 
      
 4 
     | 
    
         
            +
                `go master`
         
     | 
| 
      
 5 
     | 
    
         
            +
                `git merge actors`
         
     | 
| 
      
 6 
     | 
    
         
            +
                `git push github`
         
     | 
| 
      
 7 
     | 
    
         
            +
                `go actors`
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            desc "Build gem"
         
     | 
| 
      
 12 
     | 
    
         
            +
            task :gb do
         
     | 
| 
      
 13 
     | 
    
         
            +
              `gem build angstrom.gemspec`
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            desc "Push gem"
         
     | 
| 
      
 17 
     | 
    
         
            +
            task :gp do
         
     | 
| 
      
 18 
     | 
    
         
            +
              Rake::Task[:gb].invoke
         
     | 
| 
      
 19 
     | 
    
         
            +
              gem_file = `ls *.gem`.to_a.last.chomp
         
     | 
| 
      
 20 
     | 
    
         
            +
              puts "pushing #{gem_file}"
         
     | 
| 
      
 21 
     | 
    
         
            +
              puts `gem push #{gem_file}`
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
    
        data/angstrom.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'angstrom/version'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            Gem::Specification.new 'angstrom', Aleph::VERSION do |s|
         
     | 
| 
      
 5 
     | 
    
         
            +
              s.description       = "Angstrom is an Mongrel2 fronted, actor-based web development framework similar in style to sinatra. With natively-threaded interpreters (Rubinius2), Angstrom provides true concurrency and high stability, by design."
         
     | 
| 
      
 6 
     | 
    
         
            +
              s.summary           = "Highly concurrent, sinatra-like framework"
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.author            = "Artem Titoulenko"
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.email             = "artem.titoulenko@gmail.com"
         
     | 
| 
      
 9 
     | 
    
         
            +
              s.homepage          = "https://www.github.com/artemtitoulenko/angstrom"
         
     | 
| 
      
 10 
     | 
    
         
            +
              s.files             = `git ls-files`.split("\n") - %w[.gitignore .travis.yml response_benchmark.rb demo/config.sqlite]
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.executables       = %w[ angstrom ]
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              s.add_dependency 'ffi',            '~> 1.0', '>= 1.0.10'
         
     | 
| 
      
 14 
     | 
    
         
            +
              s.add_dependency 'ffi-rzmq', '~> 0.9', '>= 0.9.0'
         
     | 
| 
      
 15 
     | 
    
         
            +
              s.add_dependency 'lazy', '>= 0.9.6'
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
    
        data/bin/angstrom
    ADDED
    
    | 
         @@ -0,0 +1,108 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 4 
     | 
    
         
            +
            include FileUtils
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # this is a cli tool to make getting running with angstrom easier.
         
     | 
| 
      
 7 
     | 
    
         
            +
            def inform
         
     | 
| 
      
 8 
     | 
    
         
            +
              puts """
         
     | 
| 
      
 9 
     | 
    
         
            +
                Angstrom is an asynchronous ruby web framework that's fronted by mongrel2
         
     | 
| 
      
 10 
     | 
    
         
            +
                and makes use of Actors in order to handle requests. It is preferred to use
         
     | 
| 
      
 11 
     | 
    
         
            +
                rubinius2.0.0dev in order to take advantage of true concurrency in Ruby.
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                usage: angstrom <command>
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                commands:
         
     | 
| 
      
 16 
     | 
    
         
            +
                  create <name> [port]      Creates an angstrom app with a sample 
         
     | 
| 
      
 17 
     | 
    
         
            +
                                            mongrel2.conf and config.sqlite running on
         
     | 
| 
      
 18 
     | 
    
         
            +
                                            [port] or the default 6767.
         
     | 
| 
      
 19 
     | 
    
         
            +
                                            
         
     | 
| 
      
 20 
     | 
    
         
            +
                  start [host] [db.sqlite]  Starts a mongrel2 server in this directory
         
     | 
| 
      
 21 
     | 
    
         
            +
                                            and then runs the app called by the current 
         
     | 
| 
      
 22 
     | 
    
         
            +
                                            directorys name. This is equivalent to:
         
     | 
| 
      
 23 
     | 
    
         
            +
                                              $m2sh start -host localhost -db config.sqlite >> /dev/null &
         
     | 
| 
      
 24 
     | 
    
         
            +
                                              $ruby app.rb
         
     | 
| 
      
 25 
     | 
    
         
            +
                                              
         
     | 
| 
      
 26 
     | 
    
         
            +
                  stop [host]               Kills the default running mongrel2 
         
     | 
| 
      
 27 
     | 
    
         
            +
                                            server or [host]. This is like:
         
     | 
| 
      
 28 
     | 
    
         
            +
                                              $m2sh stop -host localhost
         
     | 
| 
      
 29 
     | 
    
         
            +
                                              
         
     | 
| 
      
 30 
     | 
    
         
            +
                short commands:
         
     | 
| 
      
 31 
     | 
    
         
            +
                  c = create
         
     | 
| 
      
 32 
     | 
    
         
            +
                  s = start
         
     | 
| 
      
 33 
     | 
    
         
            +
                  t = stop
         
     | 
| 
      
 34 
     | 
    
         
            +
              """
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            mongrel2_conf = """
         
     | 
| 
      
 38 
     | 
    
         
            +
            angstrom_handler = Handler(
         
     | 
| 
      
 39 
     | 
    
         
            +
                send_spec='tcp://127.0.0.1:9999',
         
     | 
| 
      
 40 
     | 
    
         
            +
                send_ident='34f9ceee-cd52-4b7f-b197-88bf2f0ec378',
         
     | 
| 
      
 41 
     | 
    
         
            +
                recv_spec='tcp://127.0.0.1:9998', 
         
     | 
| 
      
 42 
     | 
    
         
            +
                recv_ident='')
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            media_dir = Dir(
         
     | 
| 
      
 45 
     | 
    
         
            +
                base='media/',
         
     | 
| 
      
 46 
     | 
    
         
            +
                index_file='index.html',
         
     | 
| 
      
 47 
     | 
    
         
            +
                default_ctype='text/plain')
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            angstrom_host = Host(
         
     | 
| 
      
 50 
     | 
    
         
            +
                name=\"localhost\", 
         
     | 
| 
      
 51 
     | 
    
         
            +
                routes={
         
     | 
| 
      
 52 
     | 
    
         
            +
                    '/media/': media_dir,
         
     | 
| 
      
 53 
     | 
    
         
            +
                    '/': angstrom_handler})
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            angstrom_serv = Server(
         
     | 
| 
      
 56 
     | 
    
         
            +
                uuid=\"%s\",
         
     | 
| 
      
 57 
     | 
    
         
            +
                access_log=\"/log/mongrel2.access.log\",
         
     | 
| 
      
 58 
     | 
    
         
            +
                error_log=\"/log/mongrel2.error.log\",
         
     | 
| 
      
 59 
     | 
    
         
            +
                chroot=\"./\",
         
     | 
| 
      
 60 
     | 
    
         
            +
                default_host=\"localhost\",
         
     | 
| 
      
 61 
     | 
    
         
            +
                name=\"angstrom test\",
         
     | 
| 
      
 62 
     | 
    
         
            +
                pid_file=\"/run/mongrel2.pid\",
         
     | 
| 
      
 63 
     | 
    
         
            +
                port=%i,
         
     | 
| 
      
 64 
     | 
    
         
            +
                hosts = [angstrom_host]
         
     | 
| 
      
 65 
     | 
    
         
            +
            )
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            settings = {\"zeromq.threads\": 2, \"limits.min_ping\": 15, \"limits.kill_limit\": 2}
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            servers = [angstrom_serv]
         
     | 
| 
      
 70 
     | 
    
         
            +
            """
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            inform if ARGV.empty?
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            case ARGV[0]
         
     | 
| 
      
 75 
     | 
    
         
            +
            when 'create', 'c'
         
     | 
| 
      
 76 
     | 
    
         
            +
              inform if ARGV[1].nil?
         
     | 
| 
      
 77 
     | 
    
         
            +
              port = (ARGV[2].nil? ? 6767 : ARGV[2])
         
     | 
| 
      
 78 
     | 
    
         
            +
              mkdir ARGV[1]
         
     | 
| 
      
 79 
     | 
    
         
            +
              cd ARGV[1]
         
     | 
| 
      
 80 
     | 
    
         
            +
              %w[log run tmp].map(&method(:mkdir))
         
     | 
| 
      
 81 
     | 
    
         
            +
              File.open("mongrel2.conf", 'w') {|file| file.puts(mongrel2_conf % [`m2sh uuid`.chomp, port]) }
         
     | 
| 
      
 82 
     | 
    
         
            +
              puts "loading in the mongrel2.conf config"
         
     | 
| 
      
 83 
     | 
    
         
            +
              puts `m2sh load -config mongrel2.conf -db config.sqlite`
         
     | 
| 
      
 84 
     | 
    
         
            +
              File.open("#{ARGV[1]}.rb", 'w') {|file| file.puts "require 'rubygems'\nrequire 'angstrom'\n\n"}
         
     | 
| 
      
 85 
     | 
    
         
            +
              puts "Created app #{ARGV[1]} that will run on port #{port}"
         
     | 
| 
      
 86 
     | 
    
         
            +
              
         
     | 
| 
      
 87 
     | 
    
         
            +
            when 'start', 's'
         
     | 
| 
      
 88 
     | 
    
         
            +
              host = (ARGV[1].nil? ? 'localhost' : ARGV[1])
         
     | 
| 
      
 89 
     | 
    
         
            +
              db = (ARGV[2].nil? ? 'config.sqlite' : ARGV[2])
         
     | 
| 
      
 90 
     | 
    
         
            +
              
         
     | 
| 
      
 91 
     | 
    
         
            +
              output = `m2sh start -host #{host} -config #{db} > /dev/null &`
         
     | 
| 
      
 92 
     | 
    
         
            +
              if output.match /Aborting/
         
     | 
| 
      
 93 
     | 
    
         
            +
                puts "Error starting up mongrel2.\n\nTrace:\n#{output}"
         
     | 
| 
      
 94 
     | 
    
         
            +
                break
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
              puts "Started #{host} using the #{db} db" 
         
     | 
| 
      
 97 
     | 
    
         
            +
              
         
     | 
| 
      
 98 
     | 
    
         
            +
              file = Dir.pwd.split('/').last
         
     | 
| 
      
 99 
     | 
    
         
            +
              puts "now running #{file}.rb"
         
     | 
| 
      
 100 
     | 
    
         
            +
              puts `ruby #{file}.rb`
         
     | 
| 
      
 101 
     | 
    
         
            +
              
         
     | 
| 
      
 102 
     | 
    
         
            +
            when 'stop', 't'
         
     | 
| 
      
 103 
     | 
    
         
            +
              host = (ARGV[1].nil? ? 'localhost' : ARGV[1])
         
     | 
| 
      
 104 
     | 
    
         
            +
              puts `m2sh stop -host #{host}`
         
     | 
| 
      
 105 
     | 
    
         
            +
              puts "Stopped #{host}, I think"
         
     | 
| 
      
 106 
     | 
    
         
            +
            else
         
     | 
| 
      
 107 
     | 
    
         
            +
              inform
         
     | 
| 
      
 108 
     | 
    
         
            +
            end
         
     | 
    
        data/bin/armstrong
    ADDED
    
    | 
         @@ -0,0 +1,108 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 4 
     | 
    
         
            +
            include FileUtils
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # this is a cli tool to make getting running with armstrong easier.
         
     | 
| 
      
 7 
     | 
    
         
            +
            def inform
         
     | 
| 
      
 8 
     | 
    
         
            +
              puts """
         
     | 
| 
      
 9 
     | 
    
         
            +
                Armstrong is an asynchronous ruby web framework that's fronted by mongrel2
         
     | 
| 
      
 10 
     | 
    
         
            +
                and makes use of Actors in order to handle requests. It is preferred to use
         
     | 
| 
      
 11 
     | 
    
         
            +
                rubinius2.0.0dev in order to take advantage of true concurrency in Ruby.
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                usage: armstrong <command>
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                commands:
         
     | 
| 
      
 16 
     | 
    
         
            +
                  create <name> [port]      Creates an armstrong app with a sample 
         
     | 
| 
      
 17 
     | 
    
         
            +
                                            mongrel2.conf and config.sqlite running on
         
     | 
| 
      
 18 
     | 
    
         
            +
                                            [port] or the default 6767.
         
     | 
| 
      
 19 
     | 
    
         
            +
                                            
         
     | 
| 
      
 20 
     | 
    
         
            +
                  start [host] [db.sqlite]  Starts a mongrel2 server in this directory
         
     | 
| 
      
 21 
     | 
    
         
            +
                                            and then runs the app called by the current 
         
     | 
| 
      
 22 
     | 
    
         
            +
                                            directorys name. This is equivalent to:
         
     | 
| 
      
 23 
     | 
    
         
            +
                                              $m2sh start -host localhost -db config.sqlite >> /dev/null &
         
     | 
| 
      
 24 
     | 
    
         
            +
                                              $ruby app.rb
         
     | 
| 
      
 25 
     | 
    
         
            +
                                              
         
     | 
| 
      
 26 
     | 
    
         
            +
                  stop [host]               Kills the default running mongrel2 
         
     | 
| 
      
 27 
     | 
    
         
            +
                                            server or [host]. This is like:
         
     | 
| 
      
 28 
     | 
    
         
            +
                                              $m2sh stop -host localhost
         
     | 
| 
      
 29 
     | 
    
         
            +
                                              
         
     | 
| 
      
 30 
     | 
    
         
            +
                short commands:
         
     | 
| 
      
 31 
     | 
    
         
            +
                  c = create
         
     | 
| 
      
 32 
     | 
    
         
            +
                  s = start
         
     | 
| 
      
 33 
     | 
    
         
            +
                  t = stop
         
     | 
| 
      
 34 
     | 
    
         
            +
              """
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            mongrel2_conf = """
         
     | 
| 
      
 38 
     | 
    
         
            +
            armstrong_handler = Handler(
         
     | 
| 
      
 39 
     | 
    
         
            +
                send_spec='tcp://127.0.0.1:9999',
         
     | 
| 
      
 40 
     | 
    
         
            +
                send_ident='34f9ceee-cd52-4b7f-b197-88bf2f0ec378',
         
     | 
| 
      
 41 
     | 
    
         
            +
                recv_spec='tcp://127.0.0.1:9998', 
         
     | 
| 
      
 42 
     | 
    
         
            +
                recv_ident='')
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            media_dir = Dir(
         
     | 
| 
      
 45 
     | 
    
         
            +
                base='media/',
         
     | 
| 
      
 46 
     | 
    
         
            +
                index_file='index.html',
         
     | 
| 
      
 47 
     | 
    
         
            +
                default_ctype='text/plain')
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            armstrong_host = Host(
         
     | 
| 
      
 50 
     | 
    
         
            +
                name=\"localhost\", 
         
     | 
| 
      
 51 
     | 
    
         
            +
                routes={
         
     | 
| 
      
 52 
     | 
    
         
            +
                    '/media/': media_dir,
         
     | 
| 
      
 53 
     | 
    
         
            +
                    '/': armstrong_handler})
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            armstrong_serv = Server(
         
     | 
| 
      
 56 
     | 
    
         
            +
                uuid=\"%s\",
         
     | 
| 
      
 57 
     | 
    
         
            +
                access_log=\"/log/mongrel2.access.log\",
         
     | 
| 
      
 58 
     | 
    
         
            +
                error_log=\"/log/mongrel2.error.log\",
         
     | 
| 
      
 59 
     | 
    
         
            +
                chroot=\"./\",
         
     | 
| 
      
 60 
     | 
    
         
            +
                default_host=\"localhost\",
         
     | 
| 
      
 61 
     | 
    
         
            +
                name=\"armstrong test\",
         
     | 
| 
      
 62 
     | 
    
         
            +
                pid_file=\"/run/mongrel2.pid\",
         
     | 
| 
      
 63 
     | 
    
         
            +
                port=%i,
         
     | 
| 
      
 64 
     | 
    
         
            +
                hosts = [armstrong_host]
         
     | 
| 
      
 65 
     | 
    
         
            +
            )
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            settings = {\"zeromq.threads\": 2, \"limits.min_ping\": 15, \"limits.kill_limit\": 2}
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            servers = [armstrong_serv]
         
     | 
| 
      
 70 
     | 
    
         
            +
            """
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            inform if ARGV.empty?
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            case ARGV[0]
         
     | 
| 
      
 75 
     | 
    
         
            +
            when 'create', 'c'
         
     | 
| 
      
 76 
     | 
    
         
            +
              inform if ARGV[1].nil?
         
     | 
| 
      
 77 
     | 
    
         
            +
              port = (ARGV[2].nil? ? 6767 : ARGV[2])
         
     | 
| 
      
 78 
     | 
    
         
            +
              mkdir ARGV[1]
         
     | 
| 
      
 79 
     | 
    
         
            +
              cd ARGV[1]
         
     | 
| 
      
 80 
     | 
    
         
            +
              %w[log run tmp].map(&method(:mkdir))
         
     | 
| 
      
 81 
     | 
    
         
            +
              File.open("mongrel2.conf", 'w') {|file| file.puts(mongrel2_conf % [`m2sh uuid`.chomp, port]) }
         
     | 
| 
      
 82 
     | 
    
         
            +
              puts "loading in the mongrel2.conf config"
         
     | 
| 
      
 83 
     | 
    
         
            +
              puts `m2sh load -config mongrel2.conf -db config.sqlite`
         
     | 
| 
      
 84 
     | 
    
         
            +
              File.open("#{ARGV[1]}.rb", 'w') {|file| file.puts "require 'rubygems'\nrequire 'armstrong'\n\n"}
         
     | 
| 
      
 85 
     | 
    
         
            +
              puts "Created app #{ARGV[1]} that will run on port #{port}"
         
     | 
| 
      
 86 
     | 
    
         
            +
              
         
     | 
| 
      
 87 
     | 
    
         
            +
            when 'start', 's'
         
     | 
| 
      
 88 
     | 
    
         
            +
              host = (ARGV[1].nil? ? 'localhost' : ARGV[1])
         
     | 
| 
      
 89 
     | 
    
         
            +
              db = (ARGV[2].nil? ? 'config.sqlite' : ARGV[2])
         
     | 
| 
      
 90 
     | 
    
         
            +
              
         
     | 
| 
      
 91 
     | 
    
         
            +
              output = `m2sh start -host #{host} -config #{db} > /dev/null &`
         
     | 
| 
      
 92 
     | 
    
         
            +
              if output.match /Aborting/
         
     | 
| 
      
 93 
     | 
    
         
            +
                puts "Error starting up mongrel2.\n\nTrace:\n#{output}"
         
     | 
| 
      
 94 
     | 
    
         
            +
                break
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
              puts "Started #{host} using the #{db} db" 
         
     | 
| 
      
 97 
     | 
    
         
            +
              
         
     | 
| 
      
 98 
     | 
    
         
            +
              file = Dir.pwd.split('/').last
         
     | 
| 
      
 99 
     | 
    
         
            +
              puts "now running #{file}.rb"
         
     | 
| 
      
 100 
     | 
    
         
            +
              puts `ruby #{file}.rb`
         
     | 
| 
      
 101 
     | 
    
         
            +
              
         
     | 
| 
      
 102 
     | 
    
         
            +
            when 'stop', 't'
         
     | 
| 
      
 103 
     | 
    
         
            +
              host = (ARGV[1].nil? ? 'localhost' : ARGV[1])
         
     | 
| 
      
 104 
     | 
    
         
            +
              puts `m2sh stop -host #{host}`
         
     | 
| 
      
 105 
     | 
    
         
            +
              puts "Stopped #{host}, I think"
         
     | 
| 
      
 106 
     | 
    
         
            +
            else
         
     | 
| 
      
 107 
     | 
    
         
            +
              inform
         
     | 
| 
      
 108 
     | 
    
         
            +
            end
         
     | 
    
        data/demo/mongrel2.conf
    ADDED
    
    | 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            angstrom_handler = Handler(
         
     | 
| 
      
 2 
     | 
    
         
            +
                send_spec='tcp://127.0.0.1:9999',
         
     | 
| 
      
 3 
     | 
    
         
            +
                send_ident='34f9ceee-cd52-4b7f-b197-88bf2f0ec378',
         
     | 
| 
      
 4 
     | 
    
         
            +
                recv_spec='tcp://127.0.0.1:9998', 
         
     | 
| 
      
 5 
     | 
    
         
            +
                recv_ident='')
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            media_dir = Dir(
         
     | 
| 
      
 8 
     | 
    
         
            +
                base='media/',
         
     | 
| 
      
 9 
     | 
    
         
            +
                index_file='index.html',
         
     | 
| 
      
 10 
     | 
    
         
            +
                default_ctype='text/plain')
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            angstrom_host = Host(
         
     | 
| 
      
 13 
     | 
    
         
            +
                name="localhost", 
         
     | 
| 
      
 14 
     | 
    
         
            +
                routes={
         
     | 
| 
      
 15 
     | 
    
         
            +
                    '/media/': media_dir,
         
     | 
| 
      
 16 
     | 
    
         
            +
                    '/': angstrom_handler})
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            angstrom_serv = Server(
         
     | 
| 
      
 19 
     | 
    
         
            +
                uuid="f400bf85-4538-4f7a-8908-67e313d515c2",
         
     | 
| 
      
 20 
     | 
    
         
            +
                access_log="/log/mongrel2.access.log",
         
     | 
| 
      
 21 
     | 
    
         
            +
                error_log="/log/mongrel2.error.log",
         
     | 
| 
      
 22 
     | 
    
         
            +
                chroot="./",
         
     | 
| 
      
 23 
     | 
    
         
            +
                default_host="localhost",
         
     | 
| 
      
 24 
     | 
    
         
            +
                name="angstrom test",
         
     | 
| 
      
 25 
     | 
    
         
            +
                pid_file="/run/mongrel2.pid",
         
     | 
| 
      
 26 
     | 
    
         
            +
                port=6767,
         
     | 
| 
      
 27 
     | 
    
         
            +
                hosts = [angstrom_host]
         
     | 
| 
      
 28 
     | 
    
         
            +
            )
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            settings = {"zeromq.threads": 2, "limits.min_ping": 15, "limits.kill_limit": 2}
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            servers = [angstrom_serv]
         
     | 
    
        data/lib/angstrom.rb
    ADDED
    
    | 
         @@ -0,0 +1,130 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'actor'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'lazy'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            libdir = File.dirname(__FILE__)
         
     | 
| 
      
 6 
     | 
    
         
            +
            $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require "angstrom/connection"
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'angstrom/data_structures'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'angstrom/main_actors'
         
     | 
| 
      
 11 
     | 
    
         
            +
              
         
     | 
| 
      
 12 
     | 
    
         
            +
            module Aleph
         
     | 
| 
      
 13 
     | 
    
         
            +
              class Base
         
     | 
| 
      
 14 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 15 
     | 
    
         
            +
                  attr_accessor :conn, :routes
         
     | 
| 
      
 16 
     | 
    
         
            +
                  
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def get(path, &block) route "GET", path, &block end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def put(path, &block)  route "PUT",  path, &block end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  def post(path, &block) route "POST", path, &block end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def head(path, &block) route "HEAD", path, &block end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def delete(path, &block) route "DELETE", path, &block end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def patch(path, &block) route "PATCH", path, &block end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def route(verb, path, &block)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @routes ||= {}
         
     | 
| 
      
 26 
     | 
    
         
            +
                    (@routes[verb] ||= []) << AddRoute.new(compile(path), block)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  
         
     | 
| 
      
 29 
     | 
    
         
            +
                  private
         
     | 
| 
      
 30 
     | 
    
         
            +
                    def compile(path)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      keys = []
         
     | 
| 
      
 32 
     | 
    
         
            +
                      if path.respond_to? :to_str
         
     | 
| 
      
 33 
     | 
    
         
            +
                        pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
         
     | 
| 
      
 34 
     | 
    
         
            +
                        pattern.gsub!(/((:\w+)|\*)/) do |match|
         
     | 
| 
      
 35 
     | 
    
         
            +
                          if match == "*"
         
     | 
| 
      
 36 
     | 
    
         
            +
                            keys << 'splat'
         
     | 
| 
      
 37 
     | 
    
         
            +
                            "(.*?)"
         
     | 
| 
      
 38 
     | 
    
         
            +
                          else
         
     | 
| 
      
 39 
     | 
    
         
            +
                            keys << $2[1..-1]
         
     | 
| 
      
 40 
     | 
    
         
            +
                            "([^/?#]+)"
         
     | 
| 
      
 41 
     | 
    
         
            +
                          end
         
     | 
| 
      
 42 
     | 
    
         
            +
                        end
         
     | 
| 
      
 43 
     | 
    
         
            +
                        [/^#{pattern}$/, keys]
         
     | 
| 
      
 44 
     | 
    
         
            +
                      elsif path.respond_to?(:keys) && path.respond_to?(:match)
         
     | 
| 
      
 45 
     | 
    
         
            +
                        [path, path.keys]
         
     | 
| 
      
 46 
     | 
    
         
            +
                      elsif path.respond_to?(:names) && path.respond_to?(:match)
         
     | 
| 
      
 47 
     | 
    
         
            +
                        [path, path.names]
         
     | 
| 
      
 48 
     | 
    
         
            +
                      elsif path.respond_to? :match
         
     | 
| 
      
 49 
     | 
    
         
            +
                        [path, keys]
         
     | 
| 
      
 50 
     | 
    
         
            +
                      else
         
     | 
| 
      
 51 
     | 
    
         
            +
                        raise TypeError, path
         
     | 
| 
      
 52 
     | 
    
         
            +
                      end
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    def encoded(char)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      enc = URI.encode(char)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      enc = "(?:#{Regexp.escape enc}|#{URI.encode char, /./})" if enc == char
         
     | 
| 
      
 58 
     | 
    
         
            +
                      enc = "(?:#{enc}|#{encoded('+')})" if char == " "
         
     | 
| 
      
 59 
     | 
    
         
            +
                      enc
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
              
         
     | 
| 
      
 64 
     | 
    
         
            +
              class Angstrom < Base
         
     | 
| 
      
 65 
     | 
    
         
            +
                
         
     | 
| 
      
 66 
     | 
    
         
            +
                # the kicker. It all gets launched from here.
         
     | 
| 
      
 67 
     | 
    
         
            +
                # this function makes a new connection object to handle the communication,
         
     | 
| 
      
 68 
     | 
    
         
            +
                # promises to start the replier, request handler, and their supervisor,
         
     | 
| 
      
 69 
     | 
    
         
            +
                # gives the replier the connection information, tells the request_handler
         
     | 
| 
      
 70 
     | 
    
         
            +
                # what routes it should be able to match, then checks that all of the services
         
     | 
| 
      
 71 
     | 
    
         
            +
                # are running correctly, gives us a launch time, then jumps into our main loop
         
     | 
| 
      
 72 
     | 
    
         
            +
                # that waits for an incoming message, parses it, and sends it off to be
         
     | 
| 
      
 73 
     | 
    
         
            +
                # operated on by the request handler. Boom.
         
     | 
| 
      
 74 
     | 
    
         
            +
                def self.run!
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #ensure that all actors are launched. Yea.
         
     | 
| 
      
 76 
     | 
    
         
            +
                  done = Lazy::demand(Lazy::promise do |done|
         
     | 
| 
      
 77 
     | 
    
         
            +
                    Actor.spawn(&Aleph::Base.supervisor_proc)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    done = true
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end)
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  if done
         
     | 
| 
      
 82 
     | 
    
         
            +
                    done2 = Lazy::demand(Lazy::Promise.new do |done2|
         
     | 
| 
      
 83 
     | 
    
         
            +
                      Actor[:supervisor] << SpawnRequestHandlers.new(4)
         
     | 
| 
      
 84 
     | 
    
         
            +
                      Actor[:supervisor] << SpawnReceivers.new(1)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      Actor[:supervisor] << AddRoutes.new(@routes)
         
     | 
| 
      
 86 
     | 
    
         
            +
                      done2 = true
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  if Aleph::Base.supervisor && Aleph::Base.replier && done2
         
     | 
| 
      
 91 
     | 
    
         
            +
                    puts "","="*56,"Angstrom has launched on #{Time.now}","="*56, ""
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                  
         
     | 
| 
      
 94 
     | 
    
         
            +
                  # main loop
         
     | 
| 
      
 95 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 96 
     | 
    
         
            +
                    gets
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
              end
         
     | 
| 
      
 100 
     | 
    
         
            +
              
         
     | 
| 
      
 101 
     | 
    
         
            +
              # thank you sinatra!
         
     | 
| 
      
 102 
     | 
    
         
            +
              # Sinatra delegation mixin. Mixing this module into an object causes all
         
     | 
| 
      
 103 
     | 
    
         
            +
              # methods to be delegated to the Aleph::Angstrom class. Used primarily
         
     | 
| 
      
 104 
     | 
    
         
            +
              # at the top-level.
         
     | 
| 
      
 105 
     | 
    
         
            +
              module Delegator
         
     | 
| 
      
 106 
     | 
    
         
            +
                def self.delegate(*methods)
         
     | 
| 
      
 107 
     | 
    
         
            +
                  methods.each do |method_name|
         
     | 
| 
      
 108 
     | 
    
         
            +
                    define_method(method_name) do |*args, &block|
         
     | 
| 
      
 109 
     | 
    
         
            +
                      return super(*args, &block) if respond_to? method_name
         
     | 
| 
      
 110 
     | 
    
         
            +
                      Delegator.target.send(method_name, *args, &block)
         
     | 
| 
      
 111 
     | 
    
         
            +
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
                    private method_name
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                delegate :get, :post, :put, :patch, :delete, :head
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 119 
     | 
    
         
            +
                  attr_accessor :target
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                self.target = Angstrom
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
              
         
     | 
| 
      
 125 
     | 
    
         
            +
              # Sinatras secret sauce.
         
     | 
| 
      
 126 
     | 
    
         
            +
              at_exit { Angstrom.run! }
         
     | 
| 
      
 127 
     | 
    
         
            +
            end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
            include Aleph::Delegator
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,142 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'ffi'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'ffi-rzmq'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'cgi'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class Connection
         
     | 
| 
      
 7 
     | 
    
         
            +
              attr_reader :app_id, :sub_addr, :pub_addr, :request_sock, :response_sock, :context
         
     | 
| 
      
 8 
     | 
    
         
            +
              
         
     | 
| 
      
 9 
     | 
    
         
            +
              def initialize(app_id, zmq_sub_pub_addr=["tcp://127.0.0.1", 9999, "tcp://127.0.0.1", 9998])
         
     | 
| 
      
 10 
     | 
    
         
            +
                @app_id = app_id
         
     | 
| 
      
 11 
     | 
    
         
            +
                @sub_addr = zmq_sub_pub_addr[0..1].join(":")
         
     | 
| 
      
 12 
     | 
    
         
            +
                @pub_addr = zmq_sub_pub_addr[2..3].join(":")
         
     | 
| 
      
 13 
     | 
    
         
            +
                
         
     | 
| 
      
 14 
     | 
    
         
            +
                @request_sock = @response_sock = nil
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
              
         
     | 
| 
      
 17 
     | 
    
         
            +
              def connect
         
     | 
| 
      
 18 
     | 
    
         
            +
                @context = ZMQ::Context.new 1
         
     | 
| 
      
 19 
     | 
    
         
            +
                @request_sock = @context.socket ZMQ::PULL
         
     | 
| 
      
 20 
     | 
    
         
            +
                @request_sock.connect @sub_addr
         
     | 
| 
      
 21 
     | 
    
         
            +
                
         
     | 
| 
      
 22 
     | 
    
         
            +
                @response_sock = @context.socket ZMQ::PUB
         
     | 
| 
      
 23 
     | 
    
         
            +
                @response_sock.setsockopt ZMQ::IDENTITY, @app_id
         
     | 
| 
      
 24 
     | 
    
         
            +
                @response_sock.connect @pub_addr
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
              
         
     | 
| 
      
 27 
     | 
    
         
            +
              #raw recv, unparsed message
         
     | 
| 
      
 28 
     | 
    
         
            +
              def recv
         
     | 
| 
      
 29 
     | 
    
         
            +
                msg = ""
         
     | 
| 
      
 30 
     | 
    
         
            +
                rc = @request_sock.recv_string(msg)
         
     | 
| 
      
 31 
     | 
    
         
            +
                puts "errno [#{ZMQ::Util.errno}] with description [#{ZMQ::Util.error_string}]" unless ZMQ::Util.resultcode_ok?(rc)
         
     | 
| 
      
 32 
     | 
    
         
            +
                msg
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
              
         
     | 
| 
      
 35 
     | 
    
         
            +
              #parse the request, this is the best way to get stuff back, as a Hash
         
     | 
| 
      
 36 
     | 
    
         
            +
              def receive
         
     | 
| 
      
 37 
     | 
    
         
            +
                parse(recv)
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
              
         
     | 
| 
      
 40 
     | 
    
         
            +
              # sends the message off, formatted for Mongrel2 to understand
         
     | 
| 
      
 41 
     | 
    
         
            +
              def send(uuid, conn_id, msg)
         
     | 
| 
      
 42 
     | 
    
         
            +
                header = "%s %d:%s" % [uuid, conn_id.join(' ').length, conn_id.join(' ')]
         
     | 
| 
      
 43 
     | 
    
         
            +
                string =  header + ', ' + msg 
         
     | 
| 
      
 44 
     | 
    
         
            +
                #puts "\t\treplying to #{conn_id} with: ", string
         
     | 
| 
      
 45 
     | 
    
         
            +
                rc = @response_sock.send_string string, ZMQ::NOBLOCK
         
     | 
| 
      
 46 
     | 
    
         
            +
                puts "errno [#{ZMQ::Util.errno}] with description [#{ZMQ::Util.error_string}]" unless ZMQ::Util.resultcode_ok?(rc)
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
              
         
     | 
| 
      
 49 
     | 
    
         
            +
              # reply to an env with `message` string
         
     | 
| 
      
 50 
     | 
    
         
            +
              def reply(env, message)
         
     | 
| 
      
 51 
     | 
    
         
            +
                self.send(env[:sender], [env[:conn_id]], message)
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              # reply to a req with a valid http header
         
     | 
| 
      
 55 
     | 
    
         
            +
              def reply_http(env, body, code=200, headers={"Content-type" => "text/html"})
         
     | 
| 
      
 56 
     | 
    
         
            +
                self.reply(env, http_response(body, code, headers))
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
              
         
     | 
| 
      
 59 
     | 
    
         
            +
              private
         
     | 
| 
      
 60 
     | 
    
         
            +
              def http_response(body, code, headers)
         
     | 
| 
      
 61 
     | 
    
         
            +
                headers['Content-Length'] = body.size
         
     | 
| 
      
 62 
     | 
    
         
            +
                headers_s = headers.map{|k, v| "%s: %s" % [k,v]}.join("\r\n")
         
     | 
| 
      
 63 
     | 
    
         
            +
                
         
     | 
| 
      
 64 
     | 
    
         
            +
                "HTTP/1.1 #{code} #{StatusMessage[code.to_i]}\r\n#{headers_s}\r\n\r\n#{body}"
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
              
         
     | 
| 
      
 67 
     | 
    
         
            +
              def parse_netstring(ns)
         
     | 
| 
      
 68 
     | 
    
         
            +
                len, rest = ns.split(':', 2)
         
     | 
| 
      
 69 
     | 
    
         
            +
                len = len.to_i
         
     | 
| 
      
 70 
     | 
    
         
            +
                raise "Netstring did not end in ','" unless rest[len].chr == ','
         
     | 
| 
      
 71 
     | 
    
         
            +
                [ rest[0...len], rest[(len+1)..-1] ]
         
     | 
| 
      
 72 
     | 
    
         
            +
              end
         
     | 
| 
      
 73 
     | 
    
         
            +
              
         
     | 
| 
      
 74 
     | 
    
         
            +
              def parse(msg)
         
     | 
| 
      
 75 
     | 
    
         
            +
                if msg.nil? || msg.empty?
         
     | 
| 
      
 76 
     | 
    
         
            +
                  return nil
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                env = {}
         
     | 
| 
      
 80 
     | 
    
         
            +
                env[:sender], env[:conn_id], env[:path], rest = msg.split(' ', 4)
         
     | 
| 
      
 81 
     | 
    
         
            +
                env[:headers], head_rest = parse_netstring(rest)
         
     | 
| 
      
 82 
     | 
    
         
            +
                env[:body], _ = parse_netstring(head_rest)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                env[:headers] = JSON.parse(env[:headers])
         
     | 
| 
      
 85 
     | 
    
         
            +
                if(env[:headers]["METHOD"] == "POST")
         
     | 
| 
      
 86 
     | 
    
         
            +
                  env[:post] = parse_params(env)
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
                
         
     | 
| 
      
 89 
     | 
    
         
            +
                return env
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
              
         
     | 
| 
      
 92 
     | 
    
         
            +
              def parse_params(env)
         
     | 
| 
      
 93 
     | 
    
         
            +
                r = {}
         
     | 
| 
      
 94 
     | 
    
         
            +
                env[:body].split('&').map{|x| x.scan(/(.*?)=(.*?)$/)}.each_slice(2) { |k| r[CGI::unescape(k[0].to_s)] = CGI::unescape(k[1]) }
         
     | 
| 
      
 95 
     | 
    
         
            +
                return r
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
              
         
     | 
| 
      
 98 
     | 
    
         
            +
              # From WEBrick: thanks dawg.
         
     | 
| 
      
 99 
     | 
    
         
            +
              StatusMessage = {
         
     | 
| 
      
 100 
     | 
    
         
            +
                100 => 'Continue',
         
     | 
| 
      
 101 
     | 
    
         
            +
                101 => 'Switching Protocols',
         
     | 
| 
      
 102 
     | 
    
         
            +
                200 => 'OK',
         
     | 
| 
      
 103 
     | 
    
         
            +
                201 => 'Created',
         
     | 
| 
      
 104 
     | 
    
         
            +
                202 => 'Accepted',
         
     | 
| 
      
 105 
     | 
    
         
            +
                203 => 'Non-Authoritative Information',
         
     | 
| 
      
 106 
     | 
    
         
            +
                204 => 'No Content',
         
     | 
| 
      
 107 
     | 
    
         
            +
                205 => 'Reset Content',
         
     | 
| 
      
 108 
     | 
    
         
            +
                206 => 'Partial Content',
         
     | 
| 
      
 109 
     | 
    
         
            +
                300 => 'Multiple Choices',
         
     | 
| 
      
 110 
     | 
    
         
            +
                301 => 'Moved Permanently',
         
     | 
| 
      
 111 
     | 
    
         
            +
                302 => 'Found',
         
     | 
| 
      
 112 
     | 
    
         
            +
                303 => 'See Other',
         
     | 
| 
      
 113 
     | 
    
         
            +
                304 => 'Not Modified',
         
     | 
| 
      
 114 
     | 
    
         
            +
                305 => 'Use Proxy',
         
     | 
| 
      
 115 
     | 
    
         
            +
                307 => 'Temporary Redirect',
         
     | 
| 
      
 116 
     | 
    
         
            +
                400 => 'Bad Request',
         
     | 
| 
      
 117 
     | 
    
         
            +
                401 => 'Unauthorized',
         
     | 
| 
      
 118 
     | 
    
         
            +
                402 => 'Payment Required',
         
     | 
| 
      
 119 
     | 
    
         
            +
                403 => 'Forbidden',
         
     | 
| 
      
 120 
     | 
    
         
            +
                404 => 'Not Found',
         
     | 
| 
      
 121 
     | 
    
         
            +
                405 => 'Method Not Allowed',
         
     | 
| 
      
 122 
     | 
    
         
            +
                406 => 'Not Acceptable',
         
     | 
| 
      
 123 
     | 
    
         
            +
                407 => 'Proxy Authentication Required',
         
     | 
| 
      
 124 
     | 
    
         
            +
                408 => 'Request Timeout',
         
     | 
| 
      
 125 
     | 
    
         
            +
                409 => 'Conflict',
         
     | 
| 
      
 126 
     | 
    
         
            +
                410 => 'Gone',
         
     | 
| 
      
 127 
     | 
    
         
            +
                411 => 'Length Required',
         
     | 
| 
      
 128 
     | 
    
         
            +
                412 => 'Precondition Failed',
         
     | 
| 
      
 129 
     | 
    
         
            +
                413 => 'Request Entity Too Large',
         
     | 
| 
      
 130 
     | 
    
         
            +
                414 => 'Request-URI Too Large',
         
     | 
| 
      
 131 
     | 
    
         
            +
                415 => 'Unsupported Media Type',
         
     | 
| 
      
 132 
     | 
    
         
            +
                416 => 'Request Range Not Satisfiable',
         
     | 
| 
      
 133 
     | 
    
         
            +
                417 => 'Expectation Failed',
         
     | 
| 
      
 134 
     | 
    
         
            +
                500 => 'Internal Server Error',
         
     | 
| 
      
 135 
     | 
    
         
            +
                501 => 'Not Implemented',
         
     | 
| 
      
 136 
     | 
    
         
            +
                502 => 'Bad Gateway',
         
     | 
| 
      
 137 
     | 
    
         
            +
                503 => 'Service Unavailable',
         
     | 
| 
      
 138 
     | 
    
         
            +
                504 => 'Gateway Timeout',
         
     | 
| 
      
 139 
     | 
    
         
            +
                505 => 'HTTP Version Not Supported'
         
     | 
| 
      
 140 
     | 
    
         
            +
              }
         
     | 
| 
      
 141 
     | 
    
         
            +
            end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,10 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            AddRoute = Struct.new :route, :method
         
     | 
| 
      
 2 
     | 
    
         
            +
            AddRoutes = Struct.new :routes
         
     | 
| 
      
 3 
     | 
    
         
            +
            Request = Struct.new :env
         
     | 
| 
      
 4 
     | 
    
         
            +
            ConnectionInformation = Struct.new :connection
         
     | 
| 
      
 5 
     | 
    
         
            +
            Reply = Struct.new :env, :code, :headers, :body
         
     | 
| 
      
 6 
     | 
    
         
            +
            MessageAndProc = Struct.new :env, :proccess
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            SpawnRequestHandlers = Struct.new :num
         
     | 
| 
      
 9 
     | 
    
         
            +
            SpawnReceivers = Struct.new :num
         
     | 
| 
      
 10 
     | 
    
         
            +
            Num = Struct.new :index
         
     | 
| 
         @@ -0,0 +1,183 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Aleph
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Base
         
     | 
| 
      
 3 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_accessor :replier, :supervisor
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_accessor :message_receiver_proc, :request_handler_proc, :supervisor_proc, :container_proc
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
            end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # take the route and pattern and keys and this function will match the keyworded params in
         
     | 
| 
      
 11 
     | 
    
         
            +
            # the url with the pattern. Example:
         
     | 
| 
      
 12 
     | 
    
         
            +
            # 
         
     | 
| 
      
 13 
     | 
    
         
            +
            #   url: /user/2/view/345
         
     | 
| 
      
 14 
     | 
    
         
            +
            #   pattern: /user/:id/view/:comment
         
     | 
| 
      
 15 
     | 
    
         
            +
            #
         
     | 
| 
      
 16 
     | 
    
         
            +
            # returns:
         
     | 
| 
      
 17 
     | 
    
         
            +
            #
         
     | 
| 
      
 18 
     | 
    
         
            +
            #   params = {id: 2, comment: 345}
         
     | 
| 
      
 19 
     | 
    
         
            +
            #
         
     | 
| 
      
 20 
     | 
    
         
            +
            def process_route(route, pattern, keys, values = [])
         
     | 
| 
      
 21 
     | 
    
         
            +
              return unless match = pattern.match(route)
         
     | 
| 
      
 22 
     | 
    
         
            +
              values += match.captures.map { |v| URI.decode(v) if v }
         
     | 
| 
      
 23 
     | 
    
         
            +
              params = {}
         
     | 
| 
      
 24 
     | 
    
         
            +
              
         
     | 
| 
      
 25 
     | 
    
         
            +
              if values.any?
         
     | 
| 
      
 26 
     | 
    
         
            +
                keys.zip(values) { |k,v| (params[k] ||= '') << v if v }
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
              params
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            # uuid generator. There's a pretty low chance of collision.
         
     | 
| 
      
 32 
     | 
    
         
            +
            def new_uuid
         
     | 
| 
      
 33 
     | 
    
         
            +
              values = [
         
     | 
| 
      
 34 
     | 
    
         
            +
                rand(0x0010000),
         
     | 
| 
      
 35 
     | 
    
         
            +
                rand(0x0010000),
         
     | 
| 
      
 36 
     | 
    
         
            +
                rand(0x0010000),
         
     | 
| 
      
 37 
     | 
    
         
            +
                rand(0x0010000),
         
     | 
| 
      
 38 
     | 
    
         
            +
                rand(0x0010000),  
         
     | 
| 
      
 39 
     | 
    
         
            +
                rand(0x1000000),
         
     | 
| 
      
 40 
     | 
    
         
            +
                rand(0x1000000),
         
     | 
| 
      
 41 
     | 
    
         
            +
              ]
         
     | 
| 
      
 42 
     | 
    
         
            +
              "%04x%04x-%04x-%04x-%04x%06x%06x" % values
         
     | 
| 
      
 43 
     | 
    
         
            +
            end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            # this nifty mess helps us just to use the output of the Procs that handle
         
     | 
| 
      
 52 
     | 
    
         
            +
            # the request instead of making the user manually catch messages and send 
         
     | 
| 
      
 53 
     | 
    
         
            +
            # them out to the replier. 
         
     | 
| 
      
 54 
     | 
    
         
            +
            Aleph::Base.container_proc = Proc.new do
         
     | 
| 
      
 55 
     | 
    
         
            +
              data = Actor.receive
         
     | 
| 
      
 56 
     | 
    
         
            +
              env, proccess = data.env, data.proccess
         
     | 
| 
      
 57 
     | 
    
         
            +
              response = proccess.call(env)
         
     | 
| 
      
 58 
     | 
    
         
            +
              puts "[container] using response: ", response
         
     | 
| 
      
 59 
     | 
    
         
            +
              if response.is_a? Array
         
     | 
| 
      
 60 
     | 
    
         
            +
                #just like Rack: env, code, headers, body. HINT HINT ( can't work because it's all async )
         
     | 
| 
      
 61 
     | 
    
         
            +
                env[:conn].reply_http(env, response[1], response[2], response[0])
         
     | 
| 
      
 62 
     | 
    
         
            +
              else
         
     | 
| 
      
 63 
     | 
    
         
            +
                puts "[container] sending http_reply"
         
     | 
| 
      
 64 
     | 
    
         
            +
                env[:conn].reply_http(env, response, 200, {"Content-Type", "text/html;charset=utf-8", "Connection", "keep-alive", "Server", "Angstrom", "X-Frame-Options", "sameorigin", "X-XSS_Protection", "1; mode=block"})
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            Aleph::Base.message_receiver_proc = Proc.new do
         
     | 
| 
      
 69 
     | 
    
         
            +
              @name = "message_receiver"
         
     | 
| 
      
 70 
     | 
    
         
            +
              puts "started (#{@name})"
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              uuid = new_uuid
         
     | 
| 
      
 73 
     | 
    
         
            +
              conn = Connection.new uuid
         
     | 
| 
      
 74 
     | 
    
         
            +
              conn.connect
         
     | 
| 
      
 75 
     | 
    
         
            +
              puts "replying as mongrel2 service #{uuid}"
         
     | 
| 
      
 76 
     | 
    
         
            +
              
         
     | 
| 
      
 77 
     | 
    
         
            +
              loop do
         
     | 
| 
      
 78 
     | 
    
         
            +
                puts "waiting..."
         
     | 
| 
      
 79 
     | 
    
         
            +
                env = conn.receive
         
     | 
| 
      
 80 
     | 
    
         
            +
                env[:conn] = conn
         
     | 
| 
      
 81 
     | 
    
         
            +
                puts "[message_receiver] got request, req = #{!req.nil?}"
         
     | 
| 
      
 82 
     | 
    
         
            +
                Actor[:supervisor] << Request.new(env) if !req.nil?
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
            end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            Aleph::Base.request_handler_proc = Proc.new do
         
     | 
| 
      
 89 
     | 
    
         
            +
              @name = "request_handler"
         
     | 
| 
      
 90 
     | 
    
         
            +
              @num = 0
         
     | 
| 
      
 91 
     | 
    
         
            +
              puts "started (#{@name})"
         
     | 
| 
      
 92 
     | 
    
         
            +
              Actor.trap_exit = true
         
     | 
| 
      
 93 
     | 
    
         
            +
              
         
     | 
| 
      
 94 
     | 
    
         
            +
              routes = {}
         
     | 
| 
      
 95 
     | 
    
         
            +
              loop do
         
     | 
| 
      
 96 
     | 
    
         
            +
                Actor.receive do |f|
         
     | 
| 
      
 97 
     | 
    
         
            +
                  f.when(Num) do |n|
         
     | 
| 
      
 98 
     | 
    
         
            +
                    @num = n.index
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
      
 100 
     | 
    
         
            +
                  
         
     | 
| 
      
 101 
     | 
    
         
            +
                  f.when(AddRoutes) do |r|
         
     | 
| 
      
 102 
     | 
    
         
            +
                    routes = r.routes
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  f.when(Request) do |r|
         
     | 
| 
      
 106 
     | 
    
         
            +
                    failure = true
         
     | 
| 
      
 107 
     | 
    
         
            +
                    verb = r.env[:headers]["METHOD"]
         
     | 
| 
      
 108 
     | 
    
         
            +
                    routes[verb].each do |route|
         
     | 
| 
      
 109 
     | 
    
         
            +
                      if route.route[0].match(r.env[:path])
         
     | 
| 
      
 110 
     | 
    
         
            +
                        puts "[request_handler:#{@num}] route matched! Making container..."
         
     | 
| 
      
 111 
     | 
    
         
            +
                        r.env[:params] = process_route(r.env[:path], route.route[0], route.route[1])
         
     | 
| 
      
 112 
     | 
    
         
            +
                        Actor.spawn(&Aleph::Base.container_proc) << MessageAndProc.new(r.env, route.method)
         
     | 
| 
      
 113 
     | 
    
         
            +
                        failure = false
         
     | 
| 
      
 114 
     | 
    
         
            +
                        break
         
     | 
| 
      
 115 
     | 
    
         
            +
                      end
         
     | 
| 
      
 116 
     | 
    
         
            +
                    end
         
     | 
| 
      
 117 
     | 
    
         
            +
                    env[:conn].reply_http(r.env, "<h1>404</h1>", 404, {'Content-type'=>'text/html'} ) if failure
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  f.when(Actor::DeadActorError) do |exit|
         
     | 
| 
      
 121 
     | 
    
         
            +
                    puts "[request_handler] #{exit.actor} died with reason: [#{exit.reason}]"
         
     | 
| 
      
 122 
     | 
    
         
            +
                  end
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
              end
         
     | 
| 
      
 125 
     | 
    
         
            +
            end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
            #if this dies, all hell will break loose
         
     | 
| 
      
 128 
     | 
    
         
            +
            Aleph::Base.supervisor_proc = Proc.new do
         
     | 
| 
      
 129 
     | 
    
         
            +
              Actor.register(:supervisor, Actor.current)
         
     | 
| 
      
 130 
     | 
    
         
            +
              Aleph::Base.supervisor = Actor.current
         
     | 
| 
      
 131 
     | 
    
         
            +
              Actor.trap_exit = true
         
     | 
| 
      
 132 
     | 
    
         
            +
              puts "started (supervisor)"
         
     | 
| 
      
 133 
     | 
    
         
            +
              
         
     | 
| 
      
 134 
     | 
    
         
            +
              handlers = []
         
     | 
| 
      
 135 
     | 
    
         
            +
              receivers = []
         
     | 
| 
      
 136 
     | 
    
         
            +
              handler_turn = 0
         
     | 
| 
      
 137 
     | 
    
         
            +
              receiver_turn = 0
         
     | 
| 
      
 138 
     | 
    
         
            +
                  
         
     | 
| 
      
 139 
     | 
    
         
            +
              loop do
         
     | 
| 
      
 140 
     | 
    
         
            +
                Actor.receive do |f|
         
     | 
| 
      
 141 
     | 
    
         
            +
                  
         
     | 
| 
      
 142 
     | 
    
         
            +
                  f.when(AddRoutes) do |r|
         
     | 
| 
      
 143 
     | 
    
         
            +
                    puts "Adding routes"
         
     | 
| 
      
 144 
     | 
    
         
            +
                    handlers.each { |h| h << r }
         
     | 
| 
      
 145 
     | 
    
         
            +
                  end
         
     | 
| 
      
 146 
     | 
    
         
            +
                  
         
     | 
| 
      
 147 
     | 
    
         
            +
                  f.when(SpawnRequestHandlers) do |r|
         
     | 
| 
      
 148 
     | 
    
         
            +
                    r.num.times do
         
     | 
| 
      
 149 
     | 
    
         
            +
                      puts "spawning a request_handler in handlers[#{handlers.size}]"
         
     | 
| 
      
 150 
     | 
    
         
            +
                      handlers << (Actor.spawn_link(&Aleph::Base.request_handler_proc) << Num.new(handlers.size))
         
     | 
| 
      
 151 
     | 
    
         
            +
                    end
         
     | 
| 
      
 152 
     | 
    
         
            +
                  end
         
     | 
| 
      
 153 
     | 
    
         
            +
                  
         
     | 
| 
      
 154 
     | 
    
         
            +
                  f.when(Request) do |req|
         
     | 
| 
      
 155 
     | 
    
         
            +
                    puts "[supervisor] routing request"
         
     | 
| 
      
 156 
     | 
    
         
            +
                    handlers[handler_turn] << req
         
     | 
| 
      
 157 
     | 
    
         
            +
                    if(handler_turn == handlers.size)
         
     | 
| 
      
 158 
     | 
    
         
            +
                      handler_turn = 0
         
     | 
| 
      
 159 
     | 
    
         
            +
                    else
         
     | 
| 
      
 160 
     | 
    
         
            +
                      handler_turn += 1
         
     | 
| 
      
 161 
     | 
    
         
            +
                    end
         
     | 
| 
      
 162 
     | 
    
         
            +
                  end
         
     | 
| 
      
 163 
     | 
    
         
            +
                  
         
     | 
| 
      
 164 
     | 
    
         
            +
                  f.when(SpawnReceivers) do |r|
         
     | 
| 
      
 165 
     | 
    
         
            +
                    r.num.times do
         
     | 
| 
      
 166 
     | 
    
         
            +
                      puts "spawning a receiver in receivers[#{receivers.size}]"
         
     | 
| 
      
 167 
     | 
    
         
            +
                      receivers << Actor.spawn_link(&Aleph::Base.message_receiver_proc)
         
     | 
| 
      
 168 
     | 
    
         
            +
                    end
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end
         
     | 
| 
      
 170 
     | 
    
         
            +
                  
         
     | 
| 
      
 171 
     | 
    
         
            +
                  f.when(Actor::DeadActorError) do |exit|
         
     | 
| 
      
 172 
     | 
    
         
            +
                    "[supervisor] #{exit.actor} died with reason: [#{exit.reason}]"
         
     | 
| 
      
 173 
     | 
    
         
            +
                    # case exit.actor.name
         
     | 
| 
      
 174 
     | 
    
         
            +
                    # when "request_handler"
         
     | 
| 
      
 175 
     | 
    
         
            +
                    #   # lets replace that failed request_handler with a new one. NO DOWN TIME
         
     | 
| 
      
 176 
     | 
    
         
            +
                    #   handlers[exit.actor.num] = (Actor.spawn_link(&Aleph::Base.request_handler_proc) << Num.new(exit.actor.num))
         
     | 
| 
      
 177 
     | 
    
         
            +
                    # when "message_receiver"
         
     | 
| 
      
 178 
     | 
    
         
            +
                    #   repliers[exit.actor.num] = (Actor.spawn_link(&Aleph::Base.message_receiver_proc) << Num.new(exit.actor.num))     
         
     | 
| 
      
 179 
     | 
    
         
            +
                    # end
         
     | 
| 
      
 180 
     | 
    
         
            +
                  end
         
     | 
| 
      
 181 
     | 
    
         
            +
                end
         
     | 
| 
      
 182 
     | 
    
         
            +
              end
         
     | 
| 
      
 183 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,137 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: angstrom
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 4 
     | 
    
         
            +
              hash: 1694540110752149318
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: 
         
     | 
| 
      
 6 
     | 
    
         
            +
              segments: 
         
     | 
| 
      
 7 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 8 
     | 
    
         
            +
              - 5
         
     | 
| 
      
 9 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 10 
     | 
    
         
            +
              version: 0.5.0
         
     | 
| 
      
 11 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 12 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Artem Titoulenko
         
     | 
| 
      
 14 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 15 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 16 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            date: 2011-12-01 00:00:00 Z
         
     | 
| 
      
 19 
     | 
    
         
            +
            dependencies: 
         
     | 
| 
      
 20 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 21 
     | 
    
         
            +
              name: ffi
         
     | 
| 
      
 22 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 23 
     | 
    
         
            +
              requirement: &id001 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 24 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 25 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 26 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 27 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 28 
     | 
    
         
            +
                    hash: 4428665182548103036
         
     | 
| 
      
 29 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 30 
     | 
    
         
            +
                    - 1
         
     | 
| 
      
 31 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 32 
     | 
    
         
            +
                    version: "1.0"
         
     | 
| 
      
 33 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 34 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 35 
     | 
    
         
            +
                    hash: 4428537186080172355
         
     | 
| 
      
 36 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 37 
     | 
    
         
            +
                    - 1
         
     | 
| 
      
 38 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 39 
     | 
    
         
            +
                    - 10
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: 1.0.10
         
     | 
| 
      
 41 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 42 
     | 
    
         
            +
              version_requirements: *id001
         
     | 
| 
      
 43 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 44 
     | 
    
         
            +
              name: ffi-rzmq
         
     | 
| 
      
 45 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 46 
     | 
    
         
            +
              requirement: &id002 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 47 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 48 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 49 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 50 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 51 
     | 
    
         
            +
                    hash: 2854635824043747355
         
     | 
| 
      
 52 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 53 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 54 
     | 
    
         
            +
                    - 9
         
     | 
| 
      
 55 
     | 
    
         
            +
                    version: "0.9"
         
     | 
| 
      
 56 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 57 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 58 
     | 
    
         
            +
                    hash: 1509009585894205680
         
     | 
| 
      
 59 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 60 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 61 
     | 
    
         
            +
                    - 9
         
     | 
| 
      
 62 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                    version: 0.9.0
         
     | 
| 
      
 64 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 65 
     | 
    
         
            +
              version_requirements: *id002
         
     | 
| 
      
 66 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 67 
     | 
    
         
            +
              name: lazy
         
     | 
| 
      
 68 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 69 
     | 
    
         
            +
              requirement: &id003 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 70 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 71 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 72 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 73 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 74 
     | 
    
         
            +
                    hash: 2770302470405238287
         
     | 
| 
      
 75 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 76 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 77 
     | 
    
         
            +
                    - 9
         
     | 
| 
      
 78 
     | 
    
         
            +
                    - 6
         
     | 
| 
      
 79 
     | 
    
         
            +
                    version: 0.9.6
         
     | 
| 
      
 80 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 81 
     | 
    
         
            +
              version_requirements: *id003
         
     | 
| 
      
 82 
     | 
    
         
            +
            description: Angstrom is an Mongrel2 fronted, actor-based web development framework similar in style to sinatra. With natively-threaded interpreters (Rubinius2), Angstrom provides true concurrency and high stability, by design.
         
     | 
| 
      
 83 
     | 
    
         
            +
            email: artem.titoulenko@gmail.com
         
     | 
| 
      
 84 
     | 
    
         
            +
            executables: 
         
     | 
| 
      
 85 
     | 
    
         
            +
            - angstrom
         
     | 
| 
      
 86 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 91 
     | 
    
         
            +
            - Gemfile
         
     | 
| 
      
 92 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 93 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 94 
     | 
    
         
            +
            - angstrom.gemspec
         
     | 
| 
      
 95 
     | 
    
         
            +
            - bin/angstrom
         
     | 
| 
      
 96 
     | 
    
         
            +
            - bin/armstrong
         
     | 
| 
      
 97 
     | 
    
         
            +
            - demo/mongrel2.conf
         
     | 
| 
      
 98 
     | 
    
         
            +
            - lib/angstrom.rb
         
     | 
| 
      
 99 
     | 
    
         
            +
            - lib/angstrom/connection.rb
         
     | 
| 
      
 100 
     | 
    
         
            +
            - lib/angstrom/data_structures.rb
         
     | 
| 
      
 101 
     | 
    
         
            +
            - lib/angstrom/main_actors.rb
         
     | 
| 
      
 102 
     | 
    
         
            +
            - lib/angstrom/version.rb
         
     | 
| 
      
 103 
     | 
    
         
            +
            homepage: https://www.github.com/artemtitoulenko/angstrom
         
     | 
| 
      
 104 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 107 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 110 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 111 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 112 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 113 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 114 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 115 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 116 
     | 
    
         
            +
                  hash: 2002549777813010636
         
     | 
| 
      
 117 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 118 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 119 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 120 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 121 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 122 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 123 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 124 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 125 
     | 
    
         
            +
                  hash: 2002549777813010636
         
     | 
| 
      
 126 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 127 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 128 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 129 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 132 
     | 
    
         
            +
            rubygems_version: 1.8.11
         
     | 
| 
      
 133 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 134 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 135 
     | 
    
         
            +
            summary: Highly concurrent, sinatra-like framework
         
     | 
| 
      
 136 
     | 
    
         
            +
            test_files: []
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     |