rambo 0.5.2
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 +32 -0
- data/lib/rambo.rb +4 -0
- data/lib/rambo/application_context.rb +52 -0
- data/lib/rambo/application_request.rb +25 -0
- data/lib/rambo/controller.rb +36 -0
- data/lib/rambo/controller/cache.rb +17 -0
- data/lib/rambo/controller/params.rb +26 -0
- data/lib/rambo/controller/redirect.rb +14 -0
- data/lib/rambo/controller/template.rb +80 -0
- data/lib/rambo/env.rb +43 -0
- data/lib/rambo/middleware.rb +3 -0
- data/lib/rambo/middleware/lock.rb +18 -0
- data/lib/rambo/middleware/proxy.rb +48 -0
- data/lib/rambo/middleware/upload.rb +16 -0
- data/lib/rambo/request.rb +61 -0
- data/lib/rambo/response.rb +29 -0
- data/lib/rambo/server.rb +67 -0
- data/lib/rambo/time.rb +11 -0
- metadata +73 -0
data/README
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
To install the gem:
|
2
|
+
|
3
|
+
gem sources -a http://gems.github.com
|
4
|
+
sudo gem install moomerman-rambo
|
5
|
+
|
6
|
+
Smallest example (see the hello app in the example folder)
|
7
|
+
|
8
|
+
class HomeController < Rambo::Controller
|
9
|
+
def index
|
10
|
+
"hello world"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
To run the Blog example:
|
15
|
+
|
16
|
+
git clone git://github.com/moomerman/rambo.git
|
17
|
+
cd rambo/example/blog/
|
18
|
+
rake db:setup # check config.yml for your specific db settings
|
19
|
+
rake server
|
20
|
+
|
21
|
+
head over to http://localhost:4000/
|
22
|
+
|
23
|
+
Features:
|
24
|
+
= No Ruby object base class modifications
|
25
|
+
= Lightweight and fast
|
26
|
+
= Rack-based so works with most web servers (thin, mongrel, passenger)
|
27
|
+
= Ability to proxy request to n backend app servers [experimental]
|
28
|
+
= Request caching built in
|
29
|
+
= fast static file serving
|
30
|
+
= works with erb or haml templates (more to come)
|
31
|
+
= DataMapper integration
|
32
|
+
= Library only 60k
|
data/lib/rambo.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rambo
|
2
|
+
class ApplicationContext
|
3
|
+
attr_accessor :application_name
|
4
|
+
|
5
|
+
def initialize(application_name = nil)
|
6
|
+
@application_name = application_name
|
7
|
+
puts "Initializing application: #{application_name || 'default'}"
|
8
|
+
@prefix = "#{self.application_name}/" if self.application_name
|
9
|
+
@prefix ||= ''
|
10
|
+
load_classes
|
11
|
+
end
|
12
|
+
|
13
|
+
def load_classes
|
14
|
+
Dir["#{@prefix}controller/*.rb"].each { |x| funkyload x; }
|
15
|
+
Dir["#{@prefix}model/*.rb"].each { |x| funkyload x }
|
16
|
+
Dir["#{@prefix}lib/*.rb"].each { |x| funkyload x }
|
17
|
+
Dir["#{@prefix}*.rb"].each { |x| funkyload x unless x == 'Rakefile.rb' }
|
18
|
+
end
|
19
|
+
|
20
|
+
def reload
|
21
|
+
load_classes
|
22
|
+
end
|
23
|
+
|
24
|
+
def view_path
|
25
|
+
"./#{@prefix}view"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def funkyload(file)
|
30
|
+
@@loadcache ||= {}
|
31
|
+
begin
|
32
|
+
if cache = @@loadcache[file]
|
33
|
+
return if Env.config['rambo'] and Env.config['rambo']['reload_classes'] == false
|
34
|
+
if (mtime = File.mtime(file)) > cache
|
35
|
+
puts "rambo: reloading: #{file}"
|
36
|
+
load file
|
37
|
+
@@loadcache[file] = mtime
|
38
|
+
end
|
39
|
+
else
|
40
|
+
puts " -> loading: #{file}"
|
41
|
+
load file
|
42
|
+
@@loadcache[file] = File.mtime(file)
|
43
|
+
end
|
44
|
+
rescue Exception => e
|
45
|
+
puts "Exception loading class [#{file}]: #{e.message}"
|
46
|
+
puts e.backtrace.join("\n")
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rambo
|
2
|
+
class ApplicationRequest < Rambo::Request
|
3
|
+
|
4
|
+
def initialize(env)
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
def application
|
9
|
+
path_components[1]
|
10
|
+
end
|
11
|
+
|
12
|
+
def controller
|
13
|
+
path_components[2] || default_controller
|
14
|
+
end
|
15
|
+
|
16
|
+
def action
|
17
|
+
path_components[3] || 'index'
|
18
|
+
end
|
19
|
+
|
20
|
+
def controller_class
|
21
|
+
self.application.gsub(/^[a-z]|\s+[a-z]/) { |a| a.upcase } + '::' + super
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rambo/controller/template'
|
2
|
+
require 'rambo/controller/cache'
|
3
|
+
require 'rambo/controller/params'
|
4
|
+
require 'rambo/controller/redirect'
|
5
|
+
|
6
|
+
module Rambo
|
7
|
+
class Controller
|
8
|
+
attr_accessor :request, :response
|
9
|
+
|
10
|
+
include Template
|
11
|
+
include Cache
|
12
|
+
include Params
|
13
|
+
include Redirect
|
14
|
+
|
15
|
+
def already_rendered?
|
16
|
+
@rendered
|
17
|
+
end
|
18
|
+
|
19
|
+
def controller
|
20
|
+
self.request.controller
|
21
|
+
end
|
22
|
+
|
23
|
+
def action
|
24
|
+
self.request.action
|
25
|
+
end
|
26
|
+
|
27
|
+
def session
|
28
|
+
request.env['rack.session'] ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def host
|
32
|
+
self.request.env['HTTP_HOST']
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Cache
|
2
|
+
def fresh?(model, options={})
|
3
|
+
@@etags ||= {}
|
4
|
+
etag = Digest::SHA1.hexdigest(model.inspect)
|
5
|
+
response.header['ETag'] = "\"#{etag}\""
|
6
|
+
response.header['Expires'] = (MooTime.now + options[:expires_in]).httpdate if options[:expires_in]
|
7
|
+
response.header['Cache-Control'] = 'public'
|
8
|
+
if @@etags[request.uri] == etag
|
9
|
+
response.status = 304
|
10
|
+
response.body = ''
|
11
|
+
return true
|
12
|
+
else
|
13
|
+
@@etags[request.uri] = etag
|
14
|
+
return false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Params
|
2
|
+
def params
|
3
|
+
if !self.request.params.keys.join.include?('[')
|
4
|
+
@params ||= indifferent_hash.merge(self.request.params)
|
5
|
+
else
|
6
|
+
@params ||= self.request.params.inject indifferent_hash do |res, (key,val)|
|
7
|
+
if key.include?('[')
|
8
|
+
head = key.split(/[\]\[]+/)
|
9
|
+
last = head.pop
|
10
|
+
head.inject(res){ |hash,k| hash[k] ||= indifferent_hash }[last] = val
|
11
|
+
else
|
12
|
+
res[key] = val
|
13
|
+
end
|
14
|
+
res
|
15
|
+
end
|
16
|
+
end
|
17
|
+
if request.path_components.size() > 2
|
18
|
+
@params.merge!(:id => request.path_components[3])
|
19
|
+
end
|
20
|
+
@params
|
21
|
+
end
|
22
|
+
|
23
|
+
def indifferent_hash
|
24
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Redirect
|
2
|
+
def redirect(destination, options={})
|
3
|
+
destination = destination.to_s if destination.is_a? Symbol
|
4
|
+
unless destination[0,1] == "/" or destination =~ /^http:\/\// or destination =~ /^file:\/\//
|
5
|
+
destination = "/#{self.controller}/#{destination}"
|
6
|
+
end
|
7
|
+
puts "redirecting to #{destination}"
|
8
|
+
|
9
|
+
@rendered = true
|
10
|
+
response.status = 302
|
11
|
+
response.header['Location'] = destination
|
12
|
+
return ""
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Template
|
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
|
+
def haml(template, options={}, locals={})
|
11
|
+
require 'haml' unless defined? ::Haml
|
12
|
+
render :haml, template, options, locals
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def render(engine, template, options={}, locals={})
|
17
|
+
# extract generic options
|
18
|
+
layout = options.delete(:layout)
|
19
|
+
layout = :layout if layout.nil? || layout == true
|
20
|
+
views = options.delete(:views) || self.request.application_context.view_path
|
21
|
+
locals = options.delete(:locals) || locals || {}
|
22
|
+
|
23
|
+
# render template
|
24
|
+
data, options[:filename], options[:line] = lookup_template(engine, template, views)
|
25
|
+
output = __send__("render_#{engine}", data, options, locals)
|
26
|
+
|
27
|
+
# render layout
|
28
|
+
if layout
|
29
|
+
data, options[:filename], options[:line] = lookup_layout(engine, layout, views)
|
30
|
+
if data
|
31
|
+
output = __send__("render_#{engine}", data, options, locals) { output }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
output
|
36
|
+
end
|
37
|
+
|
38
|
+
def lookup_template(engine, template, views_dir)
|
39
|
+
case template
|
40
|
+
when Symbol
|
41
|
+
# if cached = @@template_cache[template]
|
42
|
+
# cached
|
43
|
+
# else
|
44
|
+
path = ::File.join(views_dir, "#{template}.#{engine}")
|
45
|
+
[ ::File.read(path), path, 1 ]
|
46
|
+
# @@template_cache[template] = res
|
47
|
+
# res
|
48
|
+
#end
|
49
|
+
when Proc
|
50
|
+
[template.call, template, 1]
|
51
|
+
when String
|
52
|
+
[template, template, 1]
|
53
|
+
else
|
54
|
+
raise ArgumentError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def lookup_layout(engine, template, views_dir)
|
59
|
+
lookup_template(engine, template, views_dir)
|
60
|
+
rescue Errno::ENOENT
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def render_erb(data, options, locals, &block)
|
65
|
+
original_out_buf = @_out_buf
|
66
|
+
data = data.call if data.kind_of? Proc
|
67
|
+
|
68
|
+
instance = ::ERB.new(data, nil, nil, '@_out_buf')
|
69
|
+
locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
|
70
|
+
|
71
|
+
src = "#{locals_assigns.join("\n")}\n#{instance.src}"
|
72
|
+
eval src, binding, '(__ERB__)', locals_assigns.length + 1
|
73
|
+
@_out_buf, result = original_out_buf, @_out_buf
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
def render_haml(data, options, locals, &block)
|
78
|
+
::Haml::Engine.new(data, options).render(self, locals, &block)
|
79
|
+
end
|
80
|
+
end
|
data/lib/rambo/env.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Env handles all the configuration loading, database initialization and class (re)loading
|
2
|
+
# would be nice to allow the user to specify their own init though if they want
|
3
|
+
# Env.new can be called anywhere in any application and therefore acts as a global config
|
4
|
+
module Rambo
|
5
|
+
class Env
|
6
|
+
def self.config
|
7
|
+
@@config ||= YAML.load_file("config.yml") rescue nil
|
8
|
+
@@config ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
begin
|
13
|
+
# TODO: config reload
|
14
|
+
|
15
|
+
if dbconf = Env.config['mongodb']
|
16
|
+
require 'mongomapper'
|
17
|
+
@@database ||= MongoMapper.database = dbconf['database']
|
18
|
+
end
|
19
|
+
|
20
|
+
if dbconf = Env.config['datamapper']
|
21
|
+
require 'dm-core'
|
22
|
+
require 'dm-validations'
|
23
|
+
require 'dm-timestamps'
|
24
|
+
@@connection ||= DataMapper.setup(
|
25
|
+
:default,
|
26
|
+
:adapter => :mysql,
|
27
|
+
:host => dbconf['host'],
|
28
|
+
:database => dbconf['database'],
|
29
|
+
:username => dbconf['username'],
|
30
|
+
:password => dbconf['password']
|
31
|
+
)
|
32
|
+
@@dblogger ||= DataObjects::Mysql.logger = DataObjects::Logger.new(STDOUT, dbconf['logging']) if dbconf['logging']
|
33
|
+
end
|
34
|
+
rescue Exception => e
|
35
|
+
puts "Exception initializing environment: #{e.message}"
|
36
|
+
puts e.backtrace.join("\n")
|
37
|
+
raise e
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rack
|
2
|
+
class Lock
|
3
|
+
FLAG = 'rack.multithread'.freeze unless defined? FLAG
|
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
|
+
|
@@ -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,61 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Rambo
|
4
|
+
class Request < Rack::Request
|
5
|
+
def user_agent
|
6
|
+
@env['HTTP_USER_AGENT']
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns an array of acceptable media types for the response
|
10
|
+
def accept
|
11
|
+
@env['HTTP_ACCEPT'].to_s.split(',').map { |a| a.strip }
|
12
|
+
end
|
13
|
+
|
14
|
+
def path
|
15
|
+
@env["REQUEST_PATH"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def uri
|
19
|
+
@env["REQUEST_URI"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def path_components
|
23
|
+
@path_components ||= path.split('/')
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_controller
|
27
|
+
if rambo_conf = Rambo::Env.config['rambo']
|
28
|
+
rambo_conf['default_controller'] || 'home'
|
29
|
+
else
|
30
|
+
'home'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def application_context
|
35
|
+
@application_context
|
36
|
+
end
|
37
|
+
|
38
|
+
def application_context=(ctx)
|
39
|
+
@application_context = ctx
|
40
|
+
end
|
41
|
+
|
42
|
+
def controller
|
43
|
+
path_components[1] || default_controller
|
44
|
+
end
|
45
|
+
|
46
|
+
def action
|
47
|
+
path_components[2] || 'index'
|
48
|
+
end
|
49
|
+
|
50
|
+
def controller_class
|
51
|
+
self.controller.downcase.gsub(/^[a-z]|\s+[a-z]/) { |a| a.upcase } + 'Controller'
|
52
|
+
end
|
53
|
+
|
54
|
+
# Override Rack 0.9.x's #params implementation (see #72 in lighthouse)
|
55
|
+
def params
|
56
|
+
self.GET.update(self.POST)
|
57
|
+
rescue EOFError => boom
|
58
|
+
self.GET
|
59
|
+
end
|
60
|
+
end
|
61
|
+
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,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rambo/env'
|
4
|
+
require 'rambo/controller'
|
5
|
+
require 'rambo/middleware'
|
6
|
+
require 'rambo/time'
|
7
|
+
require 'rambo/request'
|
8
|
+
require 'rambo/application_request'
|
9
|
+
require 'rambo/response'
|
10
|
+
require 'rambo/application_context'
|
11
|
+
|
12
|
+
# Server simply routes the request to the correct controller/action and returns the result
|
13
|
+
module Rambo
|
14
|
+
class Server
|
15
|
+
|
16
|
+
def initialize(options = {})
|
17
|
+
@options = options
|
18
|
+
env = Rambo::Env.new
|
19
|
+
@contexts = {
|
20
|
+
'default' => Rambo::ApplicationContext.new(),
|
21
|
+
#'blog' => Rambo::ApplicationContext.new('blog')
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(env)
|
26
|
+
begin
|
27
|
+
|
28
|
+
@contexts.each { |key, context| context.reload } if Rambo::Env.config
|
29
|
+
|
30
|
+
request = Request.new(env)
|
31
|
+
response = Response.new
|
32
|
+
|
33
|
+
if @contexts.keys.include? request.controller.downcase
|
34
|
+
current_context = @contexts[request.controller.downcase]
|
35
|
+
request = Rambo::ApplicationRequest.new(env)
|
36
|
+
end
|
37
|
+
current_context ||= @contexts['default']
|
38
|
+
request.application_context = current_context
|
39
|
+
|
40
|
+
#puts "rambo: looking for #{request.controller_class}"
|
41
|
+
|
42
|
+
begin
|
43
|
+
controller = Object.module_eval("::#{request.controller_class}", __FILE__, __LINE__).new
|
44
|
+
rescue Exception => e
|
45
|
+
return [404, response.header, "<h2>Routing error: controller <span style='color:grey'>#{request.controller}</span> not found</h2>"]
|
46
|
+
end
|
47
|
+
controller.request = request
|
48
|
+
controller.response = response
|
49
|
+
|
50
|
+
controller.init if controller.respond_to? :init
|
51
|
+
|
52
|
+
unless controller.respond_to? request.action
|
53
|
+
return [404, response.header, "<h2>Routing error: action <span style='color:grey'>#{request.action}</span> not found in <span style='color:grey'>#{request.controller}</span></h2>"]
|
54
|
+
end
|
55
|
+
|
56
|
+
result = controller.send(request.action) unless controller.already_rendered?
|
57
|
+
|
58
|
+
response.body = result if result
|
59
|
+
|
60
|
+
[response.status, response.header, response.body]
|
61
|
+
rescue Exception => e
|
62
|
+
puts e.message
|
63
|
+
return [500, {}, "<pre><b>#{e.message.gsub("<","<")}</b>\n#{e.backtrace.join("\n")}</pre>"]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
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
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rambo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Richard Taylor
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-30 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: rambo is an experimental ruby web framework
|
17
|
+
email: moomerman@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- lib/rambo.rb
|
27
|
+
- lib/rambo/application_context.rb
|
28
|
+
- lib/rambo/application_request.rb
|
29
|
+
- lib/rambo/controller/cache.rb
|
30
|
+
- lib/rambo/controller/params.rb
|
31
|
+
- lib/rambo/controller/redirect.rb
|
32
|
+
- lib/rambo/controller/template.rb
|
33
|
+
- lib/rambo/controller.rb
|
34
|
+
- lib/rambo/env.rb
|
35
|
+
- lib/rambo/middleware/lock.rb
|
36
|
+
- lib/rambo/middleware/proxy.rb
|
37
|
+
- lib/rambo/middleware/upload.rb
|
38
|
+
- lib/rambo/middleware.rb
|
39
|
+
- lib/rambo/request.rb
|
40
|
+
- lib/rambo/response.rb
|
41
|
+
- lib/rambo/server.rb
|
42
|
+
- lib/rambo/time.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/moomerman/rambo
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --inline-source
|
50
|
+
- --charset=UTF-8
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: rambo
|
68
|
+
rubygems_version: 1.3.5
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: rambo is an experimental ruby web framework based on rack
|
72
|
+
test_files: []
|
73
|
+
|