moomerman-rambo 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +12 -0
- data/lib/rambo/base_controller.rb +71 -0
- data/lib/rambo/lock.rb +18 -0
- data/lib/rambo/proxy.rb +48 -0
- data/lib/rambo/request.rb +39 -0
- data/lib/rambo/response.rb +29 -0
- data/lib/rambo/server.rb +80 -0
- data/lib/rambo/templating.rb +68 -0
- data/lib/rambo/time.rb +11 -0
- data/lib/rambo/upload.rb +16 -0
- data/lib/rambo.rb +4 -0
- metadata +84 -0
data/README
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
To install the gem:
|
2
|
+
|
3
|
+
gem sources -a http://gems.github.com
|
4
|
+
sudo gem install moomerman-rambo
|
5
|
+
|
6
|
+
To run the example:
|
7
|
+
|
8
|
+
clone the repo (the examples are not part of the gem)
|
9
|
+
cd rambo/example/blog/
|
10
|
+
thin start -R server.ru -p 4000 --threaded
|
11
|
+
|
12
|
+
head over to http://localhost:4000/
|
@@ -0,0 +1,71 @@
|
|
1
|
+
load 'rambo/templating.rb'
|
2
|
+
|
3
|
+
module Rambo
|
4
|
+
class BaseController
|
5
|
+
include Templating
|
6
|
+
|
7
|
+
attr_accessor :params, :request, :response
|
8
|
+
|
9
|
+
def params
|
10
|
+
if !self.request.params.keys.join.include?('[')
|
11
|
+
@params ||= indifferent_hash.merge(self.request.params)
|
12
|
+
else
|
13
|
+
@params ||= self.request.params.inject indifferent_hash do |res, (key,val)|
|
14
|
+
if key.include?('[')
|
15
|
+
head = key.split(/[\]\[]+/)
|
16
|
+
last = head.pop
|
17
|
+
head.inject(res){ |hash,k| hash[k] ||= indifferent_hash }[last] = val
|
18
|
+
else
|
19
|
+
res[key] = val
|
20
|
+
end
|
21
|
+
res
|
22
|
+
end
|
23
|
+
end
|
24
|
+
if request.path_components.size() > 2
|
25
|
+
@params.merge!(:id => request.path_components[3])
|
26
|
+
end
|
27
|
+
@params
|
28
|
+
end
|
29
|
+
|
30
|
+
def fresh?(model, options={})
|
31
|
+
@@etags ||= {}
|
32
|
+
etag = Digest::SHA1.hexdigest(model.inspect)
|
33
|
+
response.header['ETag'] = "\"#{etag}\""
|
34
|
+
response.header['Expires'] = (MooTime.now + options[:expires_in]).httpdate if options[:expires_in]
|
35
|
+
response.header['Cache-Control'] = 'public'
|
36
|
+
if @@etags[request.uri] == etag
|
37
|
+
response.status = 304
|
38
|
+
response.body = ''
|
39
|
+
return true
|
40
|
+
else
|
41
|
+
@@etags[request.uri] = etag
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def controller
|
47
|
+
self.request.controller
|
48
|
+
end
|
49
|
+
|
50
|
+
def action
|
51
|
+
self.request.action
|
52
|
+
end
|
53
|
+
|
54
|
+
def indifferent_hash
|
55
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
56
|
+
end
|
57
|
+
|
58
|
+
def redirect(destination, options={})
|
59
|
+
destination = destination.to_s if destination.is_a? Symbol
|
60
|
+
unless destination[0,1] == "/"
|
61
|
+
destination = "/#{self.controller}/#{destination}"
|
62
|
+
end
|
63
|
+
puts "redirecting to #{destination}"
|
64
|
+
|
65
|
+
response.status = 302
|
66
|
+
response.header['Location'] = destination
|
67
|
+
return ""
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/rambo/lock.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rack
|
2
|
+
class Lock
|
3
|
+
FLAG = 'rack.multithread'.freeze
|
4
|
+
|
5
|
+
def initialize(app, lock = Mutex.new)
|
6
|
+
@app, @lock = app, lock
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
old, env[FLAG] = env[FLAG], false
|
11
|
+
@lock.synchronize { @app.call(env) }
|
12
|
+
ensure
|
13
|
+
env[FLAG] = old
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/lib/rambo/proxy.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'right_http_connection'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
class Proxy
|
6
|
+
def initialize(app, options={})
|
7
|
+
@app = app
|
8
|
+
@options = options
|
9
|
+
@@conn = Rightscale::HttpConnection.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
|
14
|
+
request = Request.new(env)
|
15
|
+
|
16
|
+
# tey out persistent connections using right http conn (need to make params aware?)
|
17
|
+
|
18
|
+
begin
|
19
|
+
|
20
|
+
backend = rand(@options[:backend].size+1)
|
21
|
+
|
22
|
+
if backend == @options[:backend].size
|
23
|
+
@app.call(env)
|
24
|
+
else
|
25
|
+
host = URI.parse("http://#{env['HTTP_HOST']}").host
|
26
|
+
port = @options[:backend][backend]
|
27
|
+
|
28
|
+
req = Net::HTTP::Get.new(env['REQUEST_URI'])
|
29
|
+
res = Net::HTTP.start(host, port) {|http|
|
30
|
+
http.request(req, request.params)
|
31
|
+
}
|
32
|
+
|
33
|
+
# need to be able to pass params into request below
|
34
|
+
#res = @@conn.req(:server => host, :port => port, :protocol => 'http', :request => request)
|
35
|
+
|
36
|
+
body = res.body || ''
|
37
|
+
|
38
|
+
[res.code.to_i, {'ETag' => res['ETag'], 'Cache-Control' => res['Cache-Control'], 'Content-Type' => res['Content-Type'], 'Location' => res['Location']}, body]
|
39
|
+
end
|
40
|
+
rescue Exception => e
|
41
|
+
puts e.message
|
42
|
+
@app.call(env)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Rambo
|
2
|
+
class Request < Rack::Request
|
3
|
+
def user_agent
|
4
|
+
@env['HTTP_USER_AGENT']
|
5
|
+
end
|
6
|
+
|
7
|
+
# Returns an array of acceptable media types for the response
|
8
|
+
def accept
|
9
|
+
@env['HTTP_ACCEPT'].to_s.split(',').map { |a| a.strip }
|
10
|
+
end
|
11
|
+
|
12
|
+
def path
|
13
|
+
env["REQUEST_PATH"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def uri
|
17
|
+
env["REQUEST_URI"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def path_components
|
21
|
+
@path_components ||= path.split('/')
|
22
|
+
end
|
23
|
+
|
24
|
+
def controller
|
25
|
+
path_components[1] || 'home'
|
26
|
+
end
|
27
|
+
|
28
|
+
def action
|
29
|
+
path_components[2] || 'index'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Override Rack 0.9.x's #params implementation (see #72 in lighthouse)
|
33
|
+
def params
|
34
|
+
self.GET.update(self.POST)
|
35
|
+
rescue EOFError => boom
|
36
|
+
self.GET
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rambo
|
2
|
+
class Response < Rack::Response
|
3
|
+
def initialize
|
4
|
+
@status, @body = 200, []
|
5
|
+
@header = Rack::Utils::HeaderHash.new({'Content-Type' => 'text/html'})
|
6
|
+
end
|
7
|
+
|
8
|
+
# def write(str)
|
9
|
+
# @body << str.to_s
|
10
|
+
# str
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# def finish
|
14
|
+
# @body = block if block_given?
|
15
|
+
# if [204, 304].include?(status.to_i)
|
16
|
+
# header.delete "Content-Type"
|
17
|
+
# [status.to_i, header.to_hash, []]
|
18
|
+
# else
|
19
|
+
# body = @body || []
|
20
|
+
# body = [body] if body.respond_to? :to_str
|
21
|
+
# if body.respond_to?(:to_ary)
|
22
|
+
# header["Content-Length"] = body.to_ary.
|
23
|
+
# inject(0) { |len, part| len + part.bytesize }.to_s
|
24
|
+
# end
|
25
|
+
# [status.to_i, header.to_hash, body]
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rambo/server.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'thin'
|
5
|
+
|
6
|
+
require 'rambo/lock'
|
7
|
+
require 'rambo/proxy'
|
8
|
+
require 'rambo/templating'
|
9
|
+
require 'rambo/base_controller'
|
10
|
+
require 'rambo/time'
|
11
|
+
require 'rambo/upload'
|
12
|
+
require 'rambo/request'
|
13
|
+
require 'rambo/response'
|
14
|
+
|
15
|
+
module Rambo
|
16
|
+
class Server
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
@options = options
|
20
|
+
# load from config - only do this IF database required
|
21
|
+
require 'dm-core'
|
22
|
+
require 'dm-validations'
|
23
|
+
require 'dm-timestamps'
|
24
|
+
#DataMapper.setup(:default, 'mysql://localhost/moo_development')
|
25
|
+
DataMapper.setup(:default, :adapter => :mysql, :host => 'localhost', :database => 'moo_development', :username => 'root', :password => '')
|
26
|
+
#DataObjects::Mysql.logger = DataObjects::Logger.new(STDOUT, :debug)
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(env)
|
30
|
+
begin
|
31
|
+
Dir["controller/*.rb"].each { |x| funkyload x }
|
32
|
+
Dir["model/*.rb"].each { |x| funkyload x }
|
33
|
+
Dir["lib/*.rb"].each { |x| funkyload x }
|
34
|
+
|
35
|
+
request = Request.new(env)
|
36
|
+
response = Response.new
|
37
|
+
|
38
|
+
ctl_string = (request.controller.downcase.gsub(/^[a-z]|\s+[a-z]/) { |a| a.upcase } + 'Controller')
|
39
|
+
begin
|
40
|
+
obj = Object.module_eval("::#{ctl_string}", __FILE__, __LINE__).new
|
41
|
+
rescue Exception => e
|
42
|
+
return [404, response.header, "<h1>Controller #{ctl_string} Not Found</h1>"]
|
43
|
+
end
|
44
|
+
obj.request = request
|
45
|
+
obj.response = response
|
46
|
+
|
47
|
+
#begin
|
48
|
+
result = obj.send(request.action)
|
49
|
+
#rescue Exception => e
|
50
|
+
#return [404, response.header, "<h1>Action #{request.action} Not Found</h1>"]
|
51
|
+
#end
|
52
|
+
response.body = result if result
|
53
|
+
|
54
|
+
[response.status, response.header, response.body]
|
55
|
+
rescue Exception => e
|
56
|
+
puts e.message
|
57
|
+
return [500, response.header, "<pre><b>#{e.message.gsub("<","<")}</b>\n#{e.backtrace.join("\n")}</pre>"]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
# turn this into a thread that checks every x seconds
|
63
|
+
# (any chance of a callback?) so it is outside of the
|
64
|
+
# request/response cycle
|
65
|
+
def funkyload(file)
|
66
|
+
@@loadcache ||= {}
|
67
|
+
if cache = @@loadcache[file]
|
68
|
+
if (mtime = File.mtime(file)) > cache
|
69
|
+
puts "reloading: #{file}"
|
70
|
+
load file
|
71
|
+
@@loadcache[file] = mtime
|
72
|
+
end
|
73
|
+
else
|
74
|
+
puts "loading: #{file}"
|
75
|
+
load file
|
76
|
+
@@loadcache[file] = File.mtime(file)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Templating
|
2
|
+
|
3
|
+
@@template_cache = {}
|
4
|
+
|
5
|
+
def erb(template, options={}, locals={})
|
6
|
+
require 'erb' unless defined? ::ERB
|
7
|
+
render :erb, template, options, locals
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def render(engine, template, options={}, locals={})
|
12
|
+
# extract generic options
|
13
|
+
layout = options.delete(:layout)
|
14
|
+
layout = :layout if layout.nil? || layout == true
|
15
|
+
views = options.delete(:views) || "./view"
|
16
|
+
locals = options.delete(:locals) || locals || {}
|
17
|
+
|
18
|
+
# render template
|
19
|
+
data = lookup_template(engine, template, views)
|
20
|
+
output = __send__("render_#{engine}", template, data, options, locals)
|
21
|
+
|
22
|
+
# render layout
|
23
|
+
if layout && data = lookup_layout(engine, layout, views)
|
24
|
+
__send__("render_#{engine}", layout, data, options, {}) { output }
|
25
|
+
else
|
26
|
+
output
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def lookup_template(engine, template, views_dir)
|
31
|
+
case template
|
32
|
+
when Symbol
|
33
|
+
# if cached = @@template_cache[template]
|
34
|
+
# cached
|
35
|
+
# else
|
36
|
+
path = ::File.join(views_dir, "#{template}.#{engine}")
|
37
|
+
res = ::File.read(path)
|
38
|
+
# @@template_cache[template] = res
|
39
|
+
# res
|
40
|
+
#end
|
41
|
+
when Proc
|
42
|
+
template.call
|
43
|
+
when String
|
44
|
+
template
|
45
|
+
else
|
46
|
+
raise ArgumentError
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def lookup_layout(engine, template, views_dir)
|
51
|
+
lookup_template(engine, template, views_dir)
|
52
|
+
rescue Errno::ENOENT
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_erb(template, data, options, locals, &block)
|
57
|
+
original_out_buf = @_out_buf
|
58
|
+
data = data.call if data.kind_of? Proc
|
59
|
+
|
60
|
+
instance = ::ERB.new(data, nil, nil, '@_out_buf')
|
61
|
+
locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
|
62
|
+
|
63
|
+
src = "#{locals_assigns.join("\n")}\n#{instance.src}"
|
64
|
+
eval src, binding, '(__ERB__)', locals_assigns.length + 1
|
65
|
+
@_out_buf, result = original_out_buf, @_out_buf
|
66
|
+
result
|
67
|
+
end
|
68
|
+
end
|
data/lib/rambo/time.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
class MooTime < Time
|
2
|
+
RFC2822_DAY_NAME = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
|
3
|
+
RFC2822_MONTH_NAME = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
|
4
|
+
def httpdate
|
5
|
+
t = dup.utc
|
6
|
+
sprintf('%s, %02d %s %d %02d:%02d:%02d GMT',
|
7
|
+
RFC2822_DAY_NAME[t.wday],
|
8
|
+
t.day, RFC2822_MONTH_NAME[t.mon-1], t.year,
|
9
|
+
t.hour, t.min, t.sec)
|
10
|
+
end
|
11
|
+
end
|
data/lib/rambo/upload.rb
ADDED
data/lib/rambo.rb
ADDED
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moomerman-rambo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.3"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Richard Taylor
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-17 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: oauth
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.3.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.2
|
34
|
+
version:
|
35
|
+
description: rambo is an experimental ruby web framework
|
36
|
+
email: moomerman@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- README
|
45
|
+
- lib/rambo.rb
|
46
|
+
- lib/rambo
|
47
|
+
- lib/rambo/base_controller.rb
|
48
|
+
- lib/rambo/lock.rb
|
49
|
+
- lib/rambo/server.rb
|
50
|
+
- lib/rambo/proxy.rb
|
51
|
+
- lib/rambo/request.rb
|
52
|
+
- lib/rambo/response.rb
|
53
|
+
- lib/rambo/templating.rb
|
54
|
+
- lib/rambo/time.rb
|
55
|
+
- lib/rambo/upload.rb
|
56
|
+
has_rdoc: false
|
57
|
+
homepage: http://github.com/moomerman/rambo
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options:
|
60
|
+
- --inline-source
|
61
|
+
- --charset=UTF-8
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project: rambo
|
79
|
+
rubygems_version: 1.2.0
|
80
|
+
signing_key:
|
81
|
+
specification_version: 2
|
82
|
+
summary: rambo is an experimental ruby web framework
|
83
|
+
test_files: []
|
84
|
+
|