distributed_demo 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/bin/distributed_demo CHANGED
@@ -1,7 +1,34 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'client'
4
+ require 'socket'
5
+ require 'net/dns/mdns-sd'
4
6
 
5
7
  Thread.abort_on_exception = true
6
- Dist::Client.new(ARGV[0], ARGV[1] || '9501')
8
+
9
+ if ARGV[0]
10
+ Dist::Client.new(ARGV[0])
11
+ else
12
+ address = nil
13
+
14
+ puts "Searching for server..."
15
+ service = Net::DNS::MDNSSD.browse('_demo_server._tcp') do |r|
16
+ Net::DNS::MDNSSD.resolve(r.name, r.type, r.domain) do |rr|
17
+ puts "Found!"
18
+ service.stop
19
+ address = "#{rr.target}:#{rr.port}"
20
+ end
21
+ end
22
+ 50.times do
23
+ sleep 0.1
24
+ break if address
25
+ end
26
+
27
+ if address
28
+ Dist::Client.new(address)
29
+ else
30
+ puts "Couldn't find a demo server."
31
+ end
32
+ end
33
+
7
34
 
@@ -3,6 +3,16 @@
3
3
  require 'server'
4
4
 
5
5
  Thread.abort_on_exception = true
6
+
7
+ require 'socket'
8
+ require 'net/dns/mdns-sd'
9
+
10
+ record = { 'description' => 'distributed_demo_server' }
11
+ Net::DNS::MDNSSD.register(Socket.gethostname, '_demo_server._tcp', 'local', ARGV[0].to_i, record) do |reply|
12
+ p reply
13
+ end
14
+
15
+
6
16
  server = Dist::Server.new(ARGV[0].to_i)
7
17
  Thread.new { server.listen }
8
18
  Rack::Handler::Thin.run MasterApp.new(server), :Port => 9505
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: distributed_demo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ hash: 17
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 3
10
+ version: 1.0.3
5
11
  platform: ruby
6
12
  authors:
7
13
  - Tyler McMullen
@@ -9,10 +15,50 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-03-26 00:00:00 +00:00
13
- default_executable:
14
- dependencies: []
15
-
18
+ date: 2010-03-25 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: mongrel
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rack
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: net-mdns
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
16
62
  description: "Demo for my talk at the Scottish Ruby Conference: Distributed Systems with Rack."
17
63
  email:
18
64
  executables:
@@ -23,32 +69,8 @@ extensions: []
23
69
  extra_rdoc_files: []
24
70
 
25
71
  files:
26
- - lib/array.rb
27
- - lib/client.rb
28
- - lib/dist.rb
29
- - lib/encoding.rb
30
- - lib/master_app.rb
31
- - lib/messages.rb
32
- - lib/server.rb
33
- - lib/tcp_socket.rb
34
- - roles/content/view.erb
35
- - roles/old_content/view.erb
36
- - roles/popular/view.erb
37
- - roles/recent_comments/view.erb
38
- - roles/recent_posts/view.erb
39
- - roles/content/app.rb
40
- - roles/old_content/app.rb
41
- - roles/popular/app.rb
42
- - roles/recent_comments/app.rb
43
- - roles/recent_posts/app.rb
44
- - nginx/mime.types
45
- - nginx/nginx.conf
46
72
  - bin/distributed_demo
47
73
  - bin/distributed_demo_server
48
- - lib/views/master.erb
49
- - lib/views/status.erb
50
- - README
51
- has_rdoc: false
52
74
  homepage:
53
75
  licenses: []
54
76
 
@@ -58,21 +80,27 @@ rdoc_options: []
58
80
  require_paths:
59
81
  - lib
60
82
  required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
61
84
  requirements:
62
85
  - - ">="
63
86
  - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
64
90
  version: "0"
65
- version:
66
91
  required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
67
93
  requirements:
68
94
  - - ">="
69
95
  - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
70
99
  version: "0"
71
- version:
72
100
  requirements: []
73
101
 
74
102
  rubyforge_project:
75
- rubygems_version: 1.3.5
103
+ rubygems_version: 1.8.6
76
104
  signing_key:
77
105
  specification_version: 3
78
106
  summary: Distributed systems demo
data/README DELETED
@@ -1,34 +0,0 @@
1
- Demo for the talk "Distributed Systems with Rack"
2
- -------------------------------------------------
3
-
4
- This is the complete code for my Scottish Ruby Conference talk. It demonstrates the idea
5
- of having a website generated by multiple distinct and distributed services. For the
6
- purposes of the demo, we have a server application which assigns roles to computers that
7
- connect. In reality, you'd probably have a legitimate proxy server sit in front of the
8
- clusters of nodes...
9
-
10
- In our case though, we keep a list of clients that are connected, and which roles
11
- they've been assigned to. When we want to generate a page, we determine which roles are
12
- necessary, pick one of the clients from each role at random, and use Nginx to combine
13
- the results into a single page.
14
-
15
-
16
-
17
- Client Startup
18
- --------------
19
-
20
- ruby client.rb 1.2.3.4:9876
21
-
22
-
23
-
24
- Server Startup
25
- --------------
26
-
27
- nginx -c /path/to/distributed_demo/nginx/nginx.conf
28
- ruby server.rb 9876
29
-
30
-
31
-
32
- Cheers,
33
-
34
- Tyler
data/lib/array.rb DELETED
@@ -1,5 +0,0 @@
1
- class Array
2
- def rand
3
- self[Kernel.rand * self.size]
4
- end
5
- end
data/lib/client.rb DELETED
@@ -1,71 +0,0 @@
1
- $: << File.join(File.dirname(__FILE__), '..')
2
-
3
- require 'dist'
4
- require 'encoding'
5
- require 'messages'
6
- require 'rubygems'
7
- require 'rack'
8
-
9
- module Dist
10
- class Client
11
- include Encoding
12
-
13
- def initialize(address, rack_port)
14
- leave_a_will
15
-
16
- host, port = address.split(':')
17
- @socket = TCPSocket.new(host, port.to_i)
18
-
19
- @socket << Messages::REQUEST_ROLE
20
-
21
- receive_message Messages::SEND_ROLE_CONFIRM
22
- role = receive_string
23
-
24
- puts "Your role: #{role.dump}"
25
-
26
- @socket << Messages::CONFIRM_ROLE
27
- send_string @socket, role
28
- send_string @socket, rack_port
29
-
30
- # spin up rack app
31
- require "roles/#{role}/app"
32
- @webserver = Thread.new do
33
- begin
34
- require 'mongrel'
35
- Rack::Handler::Mongrel.run App.new, :Port => rack_port.to_i
36
- rescue LoadError
37
- Rack::Handler::WEBrick.run App.new, :Port => rack_port.to_i
38
- end
39
- end
40
-
41
- while true
42
- receive_message Messages::REQUEST_HEARTBEAT
43
- @socket << Messages::HEARTBEAT
44
- end
45
- rescue UnexpectedMessage
46
- puts 'Disconnected.'
47
- end
48
-
49
- private
50
-
51
- def leave_a_will
52
- graceful_death = lambda {
53
- @socket.close rescue nil
54
- @webserver.kill! if @webserver
55
- }
56
- %w[INT TERM].each { |code| Signal.trap(code, &graceful_death) }
57
- end
58
-
59
- def receive_message(expected_message=nil)
60
- message = @socket.recv(1)
61
- raise UnexpectedMessage if expected_message && expected_message != message
62
- end
63
-
64
- def receive_string
65
- length = @socket.recv(2).unpack('n')[0]
66
- return '' if !length || length < 1
67
- return @socket.recv(length)
68
- end
69
- end
70
- end
71
-
data/lib/dist.rb DELETED
@@ -1,5 +0,0 @@
1
- require 'socket'
2
-
3
- module Dist
4
- class UnexpectedMessage < StandardError ; end
5
- end
data/lib/encoding.rb DELETED
@@ -1,8 +0,0 @@
1
- module Dist
2
- module Encoding
3
- def send_string(out, string)
4
- out << [string.size].pack('n')
5
- out << string
6
- end
7
- end
8
- end
data/lib/master_app.rb DELETED
@@ -1,33 +0,0 @@
1
- require 'erb'
2
- require 'array'
3
- require 'ruby-debug'
4
- require 'sinatra/base'
5
-
6
- class MasterApp < Sinatra::Base
7
- def initialize(demo_server)
8
- @demo_server = demo_server
9
- end
10
-
11
- set :views, File.join(File.dirname(__FILE__), 'views')
12
-
13
- get '/disconnect' do
14
- @demo_server.disconnect_ip params[:ip], params[:port]
15
- redirect '/status'
16
- end
17
-
18
- get '/status' do
19
- erb :status
20
- end
21
-
22
- get '/debug' do
23
- debugger
24
- end
25
-
26
- get '/' do
27
- @roles = @demo_server.pick_servers
28
-
29
- halt(503) unless @roles['content']
30
-
31
- erb :master
32
- end
33
- end
data/lib/messages.rb DELETED
@@ -1,9 +0,0 @@
1
- module Dist
2
- module Messages
3
- REQUEST_ROLE = 1.chr
4
- SEND_ROLE_CONFIRM = 2.chr
5
- CONFIRM_ROLE = 3.chr
6
- REQUEST_HEARTBEAT = 4.chr
7
- HEARTBEAT = 5.chr
8
- end
9
- end
data/lib/server.rb DELETED
@@ -1,134 +0,0 @@
1
- $: << File.join(File.dirname(__FILE__), '..')
2
-
3
- require 'rubygems'
4
- require 'dist'
5
- require 'encoding'
6
- require 'messages'
7
- require 'set'
8
- require 'rack'
9
- require 'master_app'
10
- require 'ruby-debug'
11
- require 'tcp_socket'
12
-
13
- module Dist
14
- class Server
15
- include Encoding
16
-
17
- def initialize(port)
18
- @server = TCPServer.new(port)
19
- @roles = Dir[File.join(File.dirname(__FILE__), '..', 'roles', '*')].map { |p| p.split('/').last }.inject({}) { |o,f| o[f] = Set.new; o }
20
- end
21
-
22
- attr_reader :roles
23
-
24
- def pick_servers
25
- roles.inject({}) do |o,(role,nodes)|
26
- node = nil
27
- while !node && nodes.size > 0
28
- node = nodes.to_a.rand
29
- if node.closed?
30
- nodes.delete(node)
31
- node = nil
32
- end
33
- end
34
- o[role] = "#{node.remote_ip}:#{node.rack_port}" if node
35
- o
36
- end
37
- end
38
-
39
- def disconnect_ip(ip,port)
40
- @roles.each do |role,sockets|
41
- sockets.select { |sock|
42
- begin
43
- sock.remote_ip == ip && sock.rack_port.to_i == port.to_i
44
- rescue IOError
45
- true
46
- end
47
- }.each do |sock|
48
- sockets.delete(sock)
49
- sock.close rescue nil
50
- end
51
- end
52
- end
53
-
54
- def listen
55
- puts 'Listening...'
56
-
57
- while (tcp_session = @server.accept)
58
-
59
- remote_ip = tcp_session.remote_ip
60
-
61
- Thread.new do
62
- puts "Client connected: #{tcp_session.peeraddr.inspect}"
63
-
64
- role = next_role
65
-
66
- begin
67
- receive_message tcp_session, Messages::REQUEST_ROLE
68
-
69
- tcp_session << Messages::SEND_ROLE_CONFIRM
70
- send_string tcp_session, role
71
-
72
- receive_message tcp_session, Messages::CONFIRM_ROLE
73
- confirmed_role = receive_string(tcp_session)
74
- remote_port = receive_string(tcp_session)
75
-
76
- disconnect_ip remote_ip, remote_port
77
-
78
- tcp_session.rack_port = remote_port
79
-
80
- if confirmed_role == role
81
- puts 'Role confirmation complete. Putting client into production.'
82
- @roles[role] << tcp_session
83
- else
84
- puts 'Role client sent back did not match role server sent... disconnecting client.'
85
- tcp_session.close rescue nil
86
- end
87
- rescue UnexpectedMessage
88
- puts 'Unexpected message... disconnecting client.'
89
- tcp_session.close rescue nil
90
- end
91
-
92
- p @roles
93
-
94
- begin
95
- while true
96
- tcp_session << Messages::REQUEST_HEARTBEAT
97
- if tcp_session.ready_to_read?(10)
98
- receive_message tcp_session, Messages::HEARTBEAT
99
- else
100
- puts "#{remote_ip} - #{role}: Timeout"
101
- break
102
- end
103
- sleep 15
104
- end
105
- rescue => e
106
- puts "#{remote_ip} - #{role}: #{e.message}"
107
- puts "#{remote_ip} - #{role}: Dead"
108
- ensure
109
- tcp_session.close rescue nil
110
- @roles[role].delete(tcp_session)
111
- end
112
- end
113
- end
114
- end
115
-
116
- def next_role
117
- sorted_roles = @roles.sort { |(k1,v1),(k2,v2)| v1.size <=> v2.size }
118
- min_size = sorted_roles[0][1].size
119
- sorted_roles.select { |_,socks| socks.size == min_size }.map { |role,_| role }.rand
120
- end
121
-
122
- def receive_message(session, expected_message=nil)
123
- message = session.recvfrom(1).first
124
- raise UnexpectedMessage if expected_message && expected_message != message
125
- end
126
-
127
- def receive_string(session)
128
- length = session.recvfrom(2).first.unpack('n')[0]
129
- return '' if length < 1
130
- return session.recvfrom(length).first
131
- end
132
- end
133
- end
134
-
data/lib/tcp_socket.rb DELETED
@@ -1,12 +0,0 @@
1
- class TCPSocket
2
- attr_accessor :rack_port
3
-
4
- def remote_ip
5
- peeraddr.last.split(':').last
6
- end
7
-
8
- def ready_to_read?(timeout=5)
9
- r,_,__ = IO.select([self], nil, nil, timeout)
10
- return r.first == self
11
- end
12
- end
data/lib/views/master.erb DELETED
@@ -1,125 +0,0 @@
1
- <html>
2
- <head>
3
- <style type="text/css">
4
- body {
5
- width: 900px;
6
- margin: 0 auto;
7
- }
8
-
9
- h1 {
10
- margin: 20px 0 25px;
11
- }
12
-
13
- ul#menu {
14
- padding: 0;
15
- margin: 0;
16
- overflow: hidden;
17
- }
18
-
19
- ul#menu li {
20
- float: left;
21
- width: 100px;
22
- font: 15px/15px Helvetica;
23
- color: #35D;
24
- text-decoration: underline;
25
- text-align: left;
26
- }
27
-
28
- h2 {
29
- margin-bottom: 5px;
30
- }
31
-
32
- #content span.date {
33
- font: 12px/15px Helvetica;
34
- }
35
-
36
- #content span.author {
37
- font: 13px/15px Helvetica;
38
- color: #333;
39
- }
40
-
41
- #content {
42
- margin-top: 20px;
43
- width: 600px;
44
- float: left;
45
- }
46
-
47
- #content p {
48
- line-height: 20px;
49
- }
50
-
51
- #content div.post {
52
- margin-bottom: 50px;
53
- }
54
-
55
- #sidebar {
56
- float: right;
57
- margin-top: 45px;
58
- width: 250px;
59
- }
60
-
61
- #sidebar > div {
62
- margin-bottom: 20px;
63
- }
64
-
65
- #sidebar h3 {
66
- margin: 0 0 10px;;
67
- }
68
-
69
- #sidebar div.post {
70
- margin-bottom: 5px;
71
- }
72
- #sidebar div.post span.date {
73
- display: block;
74
- font: 12px/16px Helvetica;
75
- }
76
- #sidebar div.post span.views {
77
- display: block;
78
- font: bold 12px/16px Helvetica;
79
- }
80
-
81
- #recent_comments p {
82
- margin: 0 0 10px;
83
- font: 12px/16px Helvetica;
84
- }
85
- </style>
86
- </head>
87
-
88
- <body>
89
- <h1>Some Technical Blog</h1>
90
-
91
- <ul id="menu">
92
- <li>Home</li>
93
- <li>Explore</li>
94
- <li>Something</li>
95
- </ul>
96
-
97
- <div id="content">
98
- <% if @roles['content'] %>
99
- <!--# include virtual="/remote/<%= @roles['content'] %>" -->
100
- <% end %>
101
-
102
- <% if @roles['old_content'] %>
103
- <!--# block name="old_content" --><!--# endblock -->
104
- <!--# include virtual="/remote/<%= @roles['old_content'] %>" stub="old_content" -->
105
- <% end %>
106
- </div>
107
-
108
- <div id="sidebar">
109
- <% if @roles['popular'] %>
110
- <!--# block name="popular" --><!--# endblock -->
111
- <!--# include virtual="/remote/<%= @roles['popular'] %>" stub="popular" -->
112
- <% end %>
113
-
114
- <% if @roles['recent_posts'] %>
115
- <!--# block name="recent_posts" --><!--# endblock -->
116
- <!--# include virtual="/remote/<%= @roles['recent_posts'] %>" stub="recent_posts" -->
117
- <% end %>
118
-
119
- <% if @roles['recent_comments'] %>
120
- <!--# block name="recent_comments" --><!--# endblock -->
121
- <!--# include virtual="/remote/<%= @roles['recent_comments'] %>" stub="recent_comments" -->
122
- <% end %>
123
- </div>
124
- </body>
125
- </html>
data/lib/views/status.erb DELETED
@@ -1,23 +0,0 @@
1
- <html>
2
- <head>
3
- <style type="text/css">
4
- </style>
5
- </head>
6
-
7
- <body>
8
- <h1>Connection Status</h1>
9
-
10
- <% @demo_server.roles.each do |role,clients| %>
11
-
12
- <h2><%= role %></h2>
13
- <ul>
14
- <% clients.each do |client| %>
15
- <li>
16
- <%= client.remote_ip %>:<%= client.rack_port %>
17
- (<a href="/disconnect?ip=<%= client.remote_ip %>&port=<%= client.rack_port %>">disconnect</a>)
18
- </li>
19
- <% end %>
20
- </ul>
21
- <% end %>
22
- </body>
23
- </html>
data/nginx/mime.types DELETED
@@ -1,73 +0,0 @@
1
-
2
- types {
3
- text/html html htm shtml;
4
- text/css css;
5
- text/xml xml;
6
- image/gif gif;
7
- image/jpeg jpeg jpg;
8
- application/x-javascript js;
9
- application/atom+xml atom;
10
- application/rss+xml rss;
11
-
12
- text/mathml mml;
13
- text/plain txt;
14
- text/vnd.sun.j2me.app-descriptor jad;
15
- text/vnd.wap.wml wml;
16
- text/x-component htc;
17
-
18
- image/png png;
19
- image/tiff tif tiff;
20
- image/vnd.wap.wbmp wbmp;
21
- image/x-icon ico;
22
- image/x-jng jng;
23
- image/x-ms-bmp bmp;
24
- image/svg+xml svg;
25
-
26
- application/java-archive jar war ear;
27
- application/mac-binhex40 hqx;
28
- application/msword doc;
29
- application/pdf pdf;
30
- application/postscript ps eps ai;
31
- application/rtf rtf;
32
- application/vnd.ms-excel xls;
33
- application/vnd.ms-powerpoint ppt;
34
- application/vnd.wap.wmlc wmlc;
35
- application/vnd.wap.xhtml+xml xhtml;
36
- application/vnd.google-earth.kml+xml kml;
37
- application/vnd.google-earth.kmz kmz;
38
- application/x-cocoa cco;
39
- application/x-java-archive-diff jardiff;
40
- application/x-java-jnlp-file jnlp;
41
- application/x-makeself run;
42
- application/x-perl pl pm;
43
- application/x-pilot prc pdb;
44
- application/x-rar-compressed rar;
45
- application/x-redhat-package-manager rpm;
46
- application/x-sea sea;
47
- application/x-shockwave-flash swf;
48
- application/x-stuffit sit;
49
- application/x-tcl tcl tk;
50
- application/x-x509-ca-cert der pem crt;
51
- application/x-xpinstall xpi;
52
- application/zip zip;
53
-
54
- application/octet-stream bin exe dll;
55
- application/octet-stream deb;
56
- application/octet-stream dmg;
57
- application/octet-stream eot;
58
- application/octet-stream iso img;
59
- application/octet-stream msi msp msm;
60
-
61
- audio/midi mid midi kar;
62
- audio/mpeg mp3;
63
- audio/x-realaudio ra;
64
-
65
- video/3gpp 3gpp 3gp;
66
- video/mpeg mpeg mpg;
67
- video/quicktime mov;
68
- video/x-flv flv;
69
- video/x-mng mng;
70
- video/x-ms-asf asx asf;
71
- video/x-ms-wmv wmv;
72
- video/x-msvideo avi;
73
- }
data/nginx/nginx.conf DELETED
@@ -1,36 +0,0 @@
1
- worker_processes 1;
2
-
3
- events {
4
- worker_connections 1024;
5
- }
6
-
7
- http {
8
- include mime.types;
9
- default_type text/html;
10
-
11
- sendfile on;
12
- keepalive_timeout 65;
13
-
14
- resolver 4.2.2.2;
15
-
16
- error_log "/Users/tyler/Code/dist-demo/logs/error.log" debug;
17
-
18
- server {
19
- listen 9510;
20
- server_name 127.0.0.1;
21
-
22
- root /Users/tyler/Code/dist-demo;
23
-
24
- location / {
25
- ssi on;
26
- proxy_pass http://127.0.0.1:9505/;
27
- }
28
-
29
- location ~* /remote/(.*)$ {
30
- proxy_connect_timeout 2;
31
- proxy_read_timeout 2;
32
- proxy_send_timeout 1;
33
- proxy_pass http://$1/;
34
- }
35
- }
36
- }
data/roles/content/app.rb DELETED
@@ -1,11 +0,0 @@
1
- require 'erb'
2
-
3
- class App
4
- def initialize
5
- @view = ERB.new(File.read(File.join(File.dirname(__FILE__), 'view.erb')))
6
- end
7
-
8
- def call(env)
9
- return [200, { 'Content-Type' => 'text/html' }, [@view.result(binding)]]
10
- end
11
- end
@@ -1,10 +0,0 @@
1
- <div class="post">
2
- <h2>A Recent Post</h2>
3
- <span class="date">March 24, 2010</span>
4
-
5
- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis libero et mi mollis bibendum. Pellentesque id enim vestibulum quam fermentum lacinia imperdiet eu sapien. Aenean eget nisl risus. Vivamus consequat ultricies sollicitudin. Nullam a nibh viverra odio suscipit mattis. Mauris tempus bibendum condimentum. Pellentesque non dui ac lacus euismod sodales et et nibh. Suspendisse purus urna, interdum eu placerat at, tristique id nulla. Aliquam metus orci, facilisis et accumsan vitae, imperdiet quis diam. Phasellus urna erat, dignissim sed varius vitae, tincidunt ac nisl. Morbi a sem eu sapien rhoncus rhoncus. Morbi aliquet est a nisl interdum ut molestie tellus pellentesque. Nunc eu nulla metus, at eleifend libero. Aenean ut odio eget felis sollicitudin aliquet at dapibus dolor. Suspendisse consectetur ipsum a enim rhoncus vehicula. In eleifend congue tortor id eleifend.</p>
6
-
7
- <p>Quisque nec magna tortor, at eleifend lacus. Sed vel tortor leo. Sed et urna quis nunc tristique condimentum ac sed leo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras varius scelerisque ligula, sit amet adipiscing mi gravida non. Pellentesque a pharetra risus. Nunc ut nunc nec tortor pharetra tristique vel nec arcu. Pellentesque sit amet eros sapien, eget ullamcorper nibh. Etiam sit amet dui at velit eleifend pellentesque vel at ligula. Curabitur dictum accumsan vulputate. Duis nibh lorem, tincidunt nec volutpat vitae, tincidunt sit amet libero. Cras tempor nisi non mi eleifend tristique. Vestibulum eu dolor sed dui iaculis tincidunt ut id mauris. Fusce accumsan nunc vel lectus tincidunt at auctor arcu volutpat. Mauris et aliquet purus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam ac lectus eget lacus gravida rhoncus id id justo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In in urna erat, id rutrum mauris.</p>
8
-
9
- <span class="author">Tyler McMullen</span>
10
- </div>
@@ -1,11 +0,0 @@
1
- require 'erb'
2
-
3
- class App
4
- def initialize
5
- @view = ERB.new(File.read(File.join(File.dirname(__FILE__), 'view.erb')))
6
- end
7
-
8
- def call(env)
9
- return [200, { 'Content-Type' => 'text/html' }, [@view.result(binding)]]
10
- end
11
- end
@@ -1,21 +0,0 @@
1
- <div class="post">
2
- <h2>An Older Writing</h2>
3
- <span class="date">March 15, 2010</span>
4
-
5
- <p>Quisque nec magna tortor, at eleifend lacus. Sed vel tortor leo. Sed et urna quis nunc tristique condimentum ac sed leo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras varius scelerisque ligula, sit amet adipiscing mi gravida non. Pellentesque a pharetra risus. Nunc ut nunc nec tortor pharetra tristique vel nec arcu. Pellentesque sit amet eros sapien, eget ullamcorper nibh. Etiam sit amet dui at velit eleifend pellentesque vel at ligula. Curabitur dictum accumsan vulputate. Duis nibh lorem, tincidunt nec volutpat vitae, tincidunt sit amet libero. Cras tempor nisi non mi eleifend tristique. Vestibulum eu dolor sed dui iaculis tincidunt ut id mauris. Fusce accumsan nunc vel lectus tincidunt at auctor arcu volutpat. Mauris et aliquet purus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam ac lectus eget lacus gravida rhoncus id id justo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In in urna erat, id rutrum mauris.</p>
6
-
7
- <p>Suspendisse mi libero, accumsan at porta in, mollis id eros. Etiam quis tristique lectus. Ut a diam sit amet eros rhoncus mollis id eu purus. Phasellus elit justo, feugiat a scelerisque aliquam, ultrices dapibus quam. Nam sagittis convallis nunc, ut scelerisque mauris bibendum quis. Phasellus auctor sem eleifend urna accumsan nec aliquam magna posuere. Duis turpis magna, accumsan sit amet mollis quis, vehicula sed est. Donec luctus egestas dui. Proin vulputate mauris et mi hendrerit porttitor. Etiam nec magna metus. Pellentesque faucibus tellus nec justo fringilla quis volutpat lectus malesuada. Suspendisse potenti. Nulla sed est quis mauris imperdiet dignissim vel non augue.</p>
8
-
9
- <span class="author">Tyler McMullen</span>
10
- </div>
11
-
12
- <div class="post">
13
- <h2>An Even Older Post</h2>
14
- <span class="date">March 9, 2010</span>
15
-
16
- <p>Suspendisse mi libero, accumsan at porta in, mollis id eros. Etiam quis tristique lectus. Ut a diam sit amet eros rhoncus mollis id eu purus. Phasellus elit justo, feugiat a scelerisque aliquam, ultrices dapibus quam. Nam sagittis convallis nunc, ut scelerisque mauris bibendum quis. Phasellus auctor sem eleifend urna accumsan nec aliquam magna posuere. Duis turpis magna, accumsan sit amet mollis quis, vehicula sed est. Donec luctus egestas dui. Proin vulputate mauris et mi hendrerit porttitor. Etiam nec magna metus. Pellentesque faucibus tellus nec justo fringilla quis volutpat lectus malesuada. Suspendisse potenti. Nulla sed est quis mauris imperdiet dignissim vel non augue.</p>
17
-
18
- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis libero et mi mollis bibendum. Pellentesque id enim vestibulum quam fermentum lacinia imperdiet eu sapien. Aenean eget nisl risus. Vivamus consequat ultricies sollicitudin. Nullam a nibh viverra odio suscipit mattis. Mauris tempus bibendum condimentum. Pellentesque non dui ac lacus euismod sodales et et nibh. Suspendisse purus urna, interdum eu placerat at, tristique id nulla. Aliquam metus orci, facilisis et accumsan vitae, imperdiet quis diam. Phasellus urna erat, dignissim sed varius vitae, tincidunt ac nisl. Morbi a sem eu sapien rhoncus rhoncus. Morbi aliquet est a nisl interdum ut molestie tellus pellentesque. Nunc eu nulla metus, at eleifend libero. Aenean ut odio eget felis sollicitudin aliquet at dapibus dolor. Suspendisse consectetur ipsum a enim rhoncus vehicula. In eleifend congue tortor id eleifend.</p>
19
-
20
- <span class="author">Tyler McMullen</span>
21
- </div>
data/roles/popular/app.rb DELETED
@@ -1,11 +0,0 @@
1
- require 'erb'
2
-
3
- class App
4
- def initialize
5
- @view = ERB.new(File.read(File.join(File.dirname(__FILE__), 'view.erb')))
6
- end
7
-
8
- def call(env)
9
- return [200, { 'Content-Type' => 'text/html' }, [@view.result(binding)]]
10
- end
11
- end
@@ -1,19 +0,0 @@
1
- <div id="popular">
2
- <h3>Popular Posts</h3>
3
-
4
- <div class="post">
5
- <a href="#" class="name">Something Extremely Controversial</a>
6
- <span class="date">10 months ago</span>
7
- <span class="views">12,029 views</span>
8
- </div>
9
- <div class="post">
10
- <a href="#" class="name">Obscure Language vs. Popular Language</a>
11
- <span class="date">10 months ago</span>
12
- <span class="views">10,399 views</span>
13
- </div>
14
- <div class="post">
15
- <a href="#" class="name">Why is Ruby cooler than your mom?</a>
16
- <span class="date">10 months ago</span>
17
- <span class="views">9,523 views</span>
18
- </div>
19
- </div>
@@ -1,11 +0,0 @@
1
- require 'erb'
2
-
3
- class App
4
- def initialize
5
- @view = ERB.new(File.read(File.join(File.dirname(__FILE__), 'view.erb')))
6
- end
7
-
8
- def call(env)
9
- return [200, { 'Content-Type' => 'text/html' }, [@view.result(binding)]]
10
- end
11
- end
@@ -1,16 +0,0 @@
1
- <div id="recent_comments">
2
- <h3>Recent Comments</h3>
3
-
4
- <div class="comment">
5
- <a href="#" class="name">Bob the Jerk</a>
6
- <p>This article makes no sense and you smell kinda like ...</p>
7
- </div>
8
- <div class="comment">
9
- <a href="#" class="name">Jill the Apologist</a>
10
- <p>Well, maybe Java isn't such a bad language if you're extremely ...</p>
11
- </div>
12
- <div class="comment">
13
- <a href="#" class="name">Tim the Fanboy</a>
14
- <p>OMG how coold any1 evar have used any other langiage, like, omg ...</p>
15
- </div>
16
- </div>
@@ -1,11 +0,0 @@
1
- require 'erb'
2
-
3
- class App
4
- def initialize
5
- @view = ERB.new(File.read(File.join(File.dirname(__FILE__), 'view.erb')))
6
- end
7
-
8
- def call(env)
9
- return [200, { 'Content-Type' => 'text/html' }, [@view.result(binding)]]
10
- end
11
- end
@@ -1,19 +0,0 @@
1
- <div id="recent_posts">
2
- <h3>Recent Posts</h3>
3
-
4
- <div class="post">
5
- <a href="#" class="name">Are Java programmers masochists?</a>
6
- <span class="date">1 week ago</span>
7
- <span class="views">2029 views</span>
8
- </div>
9
- <div class="post">
10
- <a href="#" class="name">Whatever happened to Ada?</a>
11
- <span class="date">2 weeks ago</span>
12
- <span class="views">103 views</span>
13
- </div>
14
- <div class="post">
15
- <a href="#" class="name">Should Matz get the Nobel prize?</a>
16
- <span class="date">1 month ago</span>
17
- <span class="views">523 views</span>
18
- </div>
19
- </div>