equipment 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ProjectInfo +55 -0
- data/README +67 -0
- data/examples/basicauthtest.rb +59 -0
- data/examples/erubytest.rb +36 -0
- data/examples/flashtest.rb +46 -0
- data/examples/index.erb +9 -0
- data/examples/mounttest.rb +34 -0
- data/examples/ogtest.rb +41 -0
- data/examples/patchestest.rb +40 -0
- data/examples/sendfiletest.rb +29 -0
- data/lib/equipment.rb +195 -0
- data/lib/ext/app_util.rb +28 -0
- data/lib/ext/basic_auth.rb +71 -0
- data/lib/ext/controls.rb +26 -0
- data/lib/ext/eruby_view.rb +83 -0
- data/lib/ext/flash.rb +81 -0
- data/lib/ext/forms.rb +22 -0
- data/lib/ext/forward.rb +70 -0
- data/lib/ext/js_helpers.rb +50 -0
- data/lib/ext/mount.rb +152 -0
- data/lib/ext/og.rb +95 -0
- data/lib/ext/og_scaffold.rb +119 -0
- data/lib/ext/og_session.rb +76 -0
- data/lib/ext/patches.rb +130 -0
- data/lib/ext/ressource.rb +88 -0
- data/lib/ext/security.rb +83 -0
- data/lib/ext/sendfile.rb +84 -0
- data/lib/ext/template_view.rb +72 -0
- data/lib/ext/use_helper.rb +83 -0
- data/lib/ext/xml_view.rb +47 -0
- metadata +78 -0
data/lib/ext/app_util.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
|
3
|
+
module Ext
|
4
|
+
|
5
|
+
# Provides the Base#app method. It's used in extensions to get the
|
6
|
+
# application's module.
|
7
|
+
#
|
8
|
+
# == Dependency
|
9
|
+
#
|
10
|
+
# * Equipment
|
11
|
+
#
|
12
|
+
module AppUtil
|
13
|
+
extend Equipment
|
14
|
+
|
15
|
+
module Helpers
|
16
|
+
# this is a curious method that's needed because I can't access MyApp::C
|
17
|
+
# in extensions. I think this is because of how constants are resolved in ruby.
|
18
|
+
# I've seen that Rite will change that. Mabye it will be not needed then.
|
19
|
+
#
|
20
|
+
# In the meanwhile, you can use app::Controllers, app::Views, ...
|
21
|
+
def app
|
22
|
+
@__app ||= Kernel.const_get(/^\w+/.match(self.class.name).to_a.first)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
require 'ext/security'
|
3
|
+
|
4
|
+
module Ext
|
5
|
+
|
6
|
+
# Basic Http Authentication.
|
7
|
+
#
|
8
|
+
# Inspired by Manfred Stienstra (http://www.fngtps.com/2006/05/basic-authentication-for-camping).
|
9
|
+
#
|
10
|
+
# == Dependencies
|
11
|
+
#
|
12
|
+
# * Equipment
|
13
|
+
# * Security
|
14
|
+
#
|
15
|
+
# == TODO
|
16
|
+
# * More docs
|
17
|
+
#
|
18
|
+
module BasicAuth
|
19
|
+
extend Equipment
|
20
|
+
depends_on Security
|
21
|
+
|
22
|
+
def self.equip(app)
|
23
|
+
super
|
24
|
+
app::Controllers::Unauthenticated.send :include, app::Base
|
25
|
+
end
|
26
|
+
|
27
|
+
module Base
|
28
|
+
|
29
|
+
# Gets HTTP_Basic credendials from a request. It consists in an array
|
30
|
+
# of user and password.
|
31
|
+
def basic_credentials
|
32
|
+
puts "Authenticate"
|
33
|
+
if d = %w{X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION}.inject([]) \
|
34
|
+
{ |d,h| env.has_key?(h) ? env[h].to_s.split : [] }
|
35
|
+
return Base64.decode64(d[1]).split(':')[0..1] if d[0] == 'Basic'
|
36
|
+
end; [nil, nil]
|
37
|
+
end
|
38
|
+
alias :authenticate :basic_credentials
|
39
|
+
|
40
|
+
def authorize(user, pass, force=false)
|
41
|
+
puts "Authorize"
|
42
|
+
return super unless force
|
43
|
+
forward(app::Controller::BasicAuth)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
module Controllers
|
49
|
+
|
50
|
+
# Called when an url is not authorized. You can override this is your app.
|
51
|
+
class Unauthenticated < Security::Controllers::Unauthenticated
|
52
|
+
class << self
|
53
|
+
attr_accessor :realm
|
54
|
+
end
|
55
|
+
self.realm = 'Camping'
|
56
|
+
|
57
|
+
def get
|
58
|
+
@status = 401
|
59
|
+
@headers['Status'] = 'Unauthorized'
|
60
|
+
@headers['WWW-Authenticate'] = "Basic realm=\"#{self.class.realm}\""
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Unauthorized < Unauthenticated
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/lib/ext/controls.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
|
3
|
+
module Ext
|
4
|
+
# Not implemented yet
|
5
|
+
module Controls
|
6
|
+
extend Equipment
|
7
|
+
|
8
|
+
class Control
|
9
|
+
attr_reader :opts
|
10
|
+
def initialize(opts={})
|
11
|
+
@opts = opts
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
end
|
16
|
+
alias :to_s :render
|
17
|
+
end
|
18
|
+
|
19
|
+
module Helpers
|
20
|
+
def control(*args)
|
21
|
+
Control.new(*args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
require 'ext/template_view'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'erubis' # faster eruby parser
|
6
|
+
ERB = Erubis::Eruby
|
7
|
+
rescue LoadError
|
8
|
+
require 'erb'
|
9
|
+
end
|
10
|
+
|
11
|
+
module Ext
|
12
|
+
|
13
|
+
# Gives eruby templating capabilities to your app.
|
14
|
+
#
|
15
|
+
# == Dependencies
|
16
|
+
#
|
17
|
+
# * Equipment
|
18
|
+
# * TemplateView
|
19
|
+
# * `erubis` package. Available as a gem. If not available, it will use `erb`
|
20
|
+
#
|
21
|
+
# == Usage
|
22
|
+
#
|
23
|
+
# define the template_root to your views :
|
24
|
+
#
|
25
|
+
# YourApp::Views.template_root = 'tpl/'
|
26
|
+
#
|
27
|
+
# Then put your templates as tpl/xy.erb
|
28
|
+
#
|
29
|
+
# rendering occurs as usual.
|
30
|
+
#
|
31
|
+
# == Issues
|
32
|
+
#
|
33
|
+
# Rendering only occurs on regular views. Vars and blocks are not really
|
34
|
+
# integrated. You can still use them. Vars as *a and blocks as &b. Like
|
35
|
+
# <%= v[1] %> in your template.
|
36
|
+
#
|
37
|
+
module ErubyView
|
38
|
+
extend Equipment
|
39
|
+
depends_on TemplateView
|
40
|
+
|
41
|
+
module Base
|
42
|
+
|
43
|
+
# Generic wrapper. Forwards to #eruby_view? or ask parent.
|
44
|
+
def has_view?(view)
|
45
|
+
return true if eruby_view?(view)
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
# Checks if the view exists as an eruby template
|
50
|
+
def eruby_view?(view)
|
51
|
+
if /text\/html|\*\/\*/ =~ (env.HTTP_ACCEPT || '*/*')
|
52
|
+
return true if eruby_template(view)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the path or nil from a view name. Template extensions are .erb.
|
57
|
+
def eruby_template(view)
|
58
|
+
file = File.join(template_root, "#{view}.erb")
|
59
|
+
File.exists?(file) ? file : nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# Generic wrapper. Forwards to #eruby_view or ask parent.
|
63
|
+
def view(m, *a, &b)
|
64
|
+
if eruby_view?(m)
|
65
|
+
return eruby_view(m, *a, &b)
|
66
|
+
end
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
# Compiles an eruby template from current instance variables, helpers
|
71
|
+
# and the template
|
72
|
+
#
|
73
|
+
# TODO : Support splat and block
|
74
|
+
# TODO : Implement parse caching here
|
75
|
+
# NOTE : Override @headers['Content-Type'] if necessary here
|
76
|
+
def eruby_view(m, *a, &b)
|
77
|
+
e = ::ERB.new(File.read(eruby_template(m)))
|
78
|
+
e.result(ivs_send(app::V.new).bind(m,*a,&b)) # instance variables are wrapped in V
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
data/lib/ext/flash.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
|
3
|
+
module Ext
|
4
|
+
# Hilight some things for a short time. It's very convenient to have some
|
5
|
+
# look over things in the night. See Helpers#flash for more options.
|
6
|
+
#
|
7
|
+
# == Look :
|
8
|
+
#
|
9
|
+
# in your controller :
|
10
|
+
#
|
11
|
+
# def get
|
12
|
+
# flash.error = 'hey !'
|
13
|
+
# redirect R(Index)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# in your view :
|
17
|
+
#
|
18
|
+
# def index
|
19
|
+
# div.error(flash.error) if flash.error
|
20
|
+
# # ...
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# or in your layout.. like you want. The flashed data will then disappear
|
24
|
+
# on the next request.
|
25
|
+
#
|
26
|
+
# == Dependency
|
27
|
+
#
|
28
|
+
# * Equipment
|
29
|
+
#
|
30
|
+
# == Issues
|
31
|
+
#
|
32
|
+
# The cookies are not deleted until the end of the session. This is because
|
33
|
+
# Camping does not provide this facility and I didn't implement it. I think
|
34
|
+
# it is okay altrough.
|
35
|
+
#
|
36
|
+
module Flash
|
37
|
+
extend Equipment
|
38
|
+
|
39
|
+
module Helpers
|
40
|
+
# The flash helper. Use in your controller or view. Retuns
|
41
|
+
# a Flasher.
|
42
|
+
def flash
|
43
|
+
@__flash ||= Flasher.new(cookies)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# The flash class. Stores the data temporarily in the cookies.
|
48
|
+
# Only store Strings.
|
49
|
+
class Flasher
|
50
|
+
def initialize(cookies)
|
51
|
+
@cookies = cookies
|
52
|
+
@data = cookies.dup
|
53
|
+
end
|
54
|
+
|
55
|
+
def [](key)
|
56
|
+
key = to_key(key)
|
57
|
+
@cookies[key] = '' if @cookies.has_key? key
|
58
|
+
return (not @data[key].nil? and not @data[key].empty?) ? @data[key] : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def []=(key, value)
|
62
|
+
unless value.nil? or value.empty?
|
63
|
+
key = to_key(key)
|
64
|
+
@cookies[key] = value
|
65
|
+
return @data[key] = value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Allows fancy flash.xy or flash.xy = 'sdsd'
|
70
|
+
def method_missing(m, val='')
|
71
|
+
/=$/ =~ m.to_s ? send(:[]=, m.to_s.sub(/=$/,''), val) : send(:[], m)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def to_key(key)
|
76
|
+
"__flash__#{key}".to_sym
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
data/lib/ext/forms.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
require 'ext/controls'
|
3
|
+
|
4
|
+
module Ext
|
5
|
+
# Not implemented yet
|
6
|
+
module Forms
|
7
|
+
extend Equipment
|
8
|
+
depends_on Controls
|
9
|
+
|
10
|
+
module Helpers
|
11
|
+
def form_for(xy)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Mab
|
16
|
+
def form(*a, &b)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/ext/forward.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
|
3
|
+
module Ext
|
4
|
+
|
5
|
+
# Forwarding is like redirecting but internally. This won't send another
|
6
|
+
# request to the browser. See Base#forward for options.
|
7
|
+
#
|
8
|
+
# *HINT* : forwarding should be used rarely and only on GET. Use redirect on
|
9
|
+
# POST.
|
10
|
+
#
|
11
|
+
# == Workflows
|
12
|
+
#
|
13
|
+
# these are simplified workflows that illustrate the difference between
|
14
|
+
# redirecting and forwarding.
|
15
|
+
#
|
16
|
+
# === Workflow with redirect
|
17
|
+
#
|
18
|
+
# Http client -> YourApp::Controller1 -> Http client -> YourApp::Controller2
|
19
|
+
# -> Http client
|
20
|
+
#
|
21
|
+
# === Workflow with forwarding
|
22
|
+
#
|
23
|
+
# Http client -> YourApp::Controller1 -> YourApp::Controller2 -> Http client
|
24
|
+
#
|
25
|
+
# == Dependency
|
26
|
+
#
|
27
|
+
# * Equipment
|
28
|
+
#
|
29
|
+
# == TODO
|
30
|
+
#
|
31
|
+
# * Allow more options in forwarding
|
32
|
+
module Forward
|
33
|
+
extend Equipment
|
34
|
+
|
35
|
+
def self.equip(app) # :nodoc:
|
36
|
+
super
|
37
|
+
app.module_eval do
|
38
|
+
def self.run(r=$stdin,e=ENV)
|
39
|
+
k, a = self::Controllers.D un("/#{e['PATH_INFO']}".gsub(%r!/+!,'/'))
|
40
|
+
k.send :include, self, self::Base, self::Models
|
41
|
+
k.new(r,e,(m=e['REQUEST_METHOD']||"GET")).service(*a)
|
42
|
+
rescue Forwarder => fw
|
43
|
+
fw.controller.new(nil,H['HTTP_HOST','','SCRIPT_NAME','','HTTP_COOKIE',''],m.to_s).service(*fw.args)
|
44
|
+
rescue Exception => x
|
45
|
+
self::Controllers::ServerError.new(r,e,'get').service(k,m,x)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Forwarder < Exception # :nodoc:
|
51
|
+
attr_accessor :controller, :args
|
52
|
+
|
53
|
+
def initialize(controller, *args)
|
54
|
+
@controller, @args = controller, args
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module Base
|
59
|
+
# Forwards to another controller
|
60
|
+
#
|
61
|
+
# * controller : the other controller
|
62
|
+
# * *a : only for ruby hackers ;-)
|
63
|
+
#
|
64
|
+
# TODO : make *a accessible for non hackers
|
65
|
+
def forward(controller, *a)
|
66
|
+
raise Forwarder.new(controller, *a)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
require 'ext/app_util'
|
3
|
+
require 'ext/use_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Ext
|
7
|
+
module JsHelpers
|
8
|
+
extend Equipment
|
9
|
+
depends_on AppUtil
|
10
|
+
depends_on UseHelper
|
11
|
+
|
12
|
+
module Helpers
|
13
|
+
def join_scripts
|
14
|
+
if javascripts.size > 0
|
15
|
+
link = R(app::Controllers::JoinScripts, javascripts.map{|js| File.basename(js, '.js')}.join(';'))
|
16
|
+
app::Mab.new do
|
17
|
+
script :type=>'text/javascript', :src=>link
|
18
|
+
end.to_s
|
19
|
+
else "" end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Controllers
|
24
|
+
class JoinScripts
|
25
|
+
class << self
|
26
|
+
attr_accessor :root
|
27
|
+
def urls; ['/scripts/(.*).js'] end
|
28
|
+
end
|
29
|
+
|
30
|
+
self.root = '.'
|
31
|
+
|
32
|
+
def get(scripts)
|
33
|
+
scripts = scripts.split(';').uniq
|
34
|
+
data = ""
|
35
|
+
scripts.each do |script|
|
36
|
+
path = File.join(self.class.root, "#{script}.js")
|
37
|
+
begin
|
38
|
+
data << File.open(path).read
|
39
|
+
rescue Errno::ENOENT => ex
|
40
|
+
data << "// ERROR : #{ex}\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
data
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
data/lib/ext/mount.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'equipment'
|
2
|
+
require 'ext/sendfile'
|
3
|
+
require 'ext/patches'
|
4
|
+
|
5
|
+
module Ext
|
6
|
+
# That equipment allows you to mount whole directories as a camping
|
7
|
+
# controller. See ControllersClassMethods#mount for options.
|
8
|
+
#
|
9
|
+
# == Features
|
10
|
+
#
|
11
|
+
# * Easy to use
|
12
|
+
# * Directory listing (skinnable)
|
13
|
+
# * Resolving Content-Type on file extensions
|
14
|
+
#
|
15
|
+
# == Dependencies
|
16
|
+
#
|
17
|
+
# * Equipment
|
18
|
+
# * Sendfile
|
19
|
+
# * Patches
|
20
|
+
# * `mime-types` package
|
21
|
+
#
|
22
|
+
# == Example
|
23
|
+
#
|
24
|
+
# Camping.goes :YourApp
|
25
|
+
#
|
26
|
+
# module YourApp::Controllers
|
27
|
+
# mount File.dirname(File.expand_path(__FILE__)) #=> YourApp::Controllers::Somefolder
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# that will be available under the url : "/your_app/somefolder"
|
31
|
+
#
|
32
|
+
# Check the mount method in ControllersClassMethod for more informations.
|
33
|
+
# You can also redefine the dir listing template by redefining `mount_listing`
|
34
|
+
# or `yourcontroller_listing`
|
35
|
+
#
|
36
|
+
# eg.
|
37
|
+
#
|
38
|
+
# module YourApp::Views
|
39
|
+
#
|
40
|
+
# def mount_listing
|
41
|
+
# ul do
|
42
|
+
# @files.each { |f| li f }
|
43
|
+
# nil # array bug in markaby
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# def somefolder_listing # default
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# Controller assignation by hand :
|
53
|
+
#
|
54
|
+
# module YourApp::Controllers
|
55
|
+
# class Stylesheets < M File.dirname('../public/css'), :name => false, :listing => false
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# == Known bug
|
60
|
+
#
|
61
|
+
# There is a known bug where issued controllers appears as NilClass in the
|
62
|
+
# view. I really don't know why...
|
63
|
+
#
|
64
|
+
module Mount
|
65
|
+
extend Equipment
|
66
|
+
depends_on Patches
|
67
|
+
depends_on Sendfile
|
68
|
+
|
69
|
+
module ControllersClassMethods
|
70
|
+
# Mounts the path given by 'path'
|
71
|
+
#
|
72
|
+
# Options :
|
73
|
+
# * urls : list of accessible
|
74
|
+
# * name : name of the controller, false sets no controller
|
75
|
+
# * listing : set to false if you don't want directory listing.
|
76
|
+
#
|
77
|
+
# Option defaults for '/var/my_path' :
|
78
|
+
# * urls : /my_path(|/.*)
|
79
|
+
# * name : MyPath
|
80
|
+
#
|
81
|
+
# Renders the view Views#mount_listing if dir listing is enabled.
|
82
|
+
|
83
|
+
def mount(path, opts={})
|
84
|
+
defaults = {
|
85
|
+
:name => File.basename(path).gsub(/(^|_)(.)/){ $2.upcase },
|
86
|
+
:url => "/#{File.basename(path)}",
|
87
|
+
:listing => true
|
88
|
+
}
|
89
|
+
opts = defaults.merge(opts)
|
90
|
+
|
91
|
+
urls = ["#{opts[:url]}(|/.*)"]
|
92
|
+
|
93
|
+
klass = Class.new() do
|
94
|
+
meta_def(:urls) { urls }
|
95
|
+
meta_def(:path) { path }
|
96
|
+
meta_def(:listing?) { opts[:listing] }
|
97
|
+
end
|
98
|
+
klass.class_eval do
|
99
|
+
def get(file='') # :nodoc:
|
100
|
+
@path = File.join(self.class.path, file)
|
101
|
+
if File.directory?(@path) and self.class.listing?
|
102
|
+
@files = Dir[File.join(@path, '*')].map{|f| File.basename(f)}
|
103
|
+
if has_view?("#{self.class.name.downcase}_listing")
|
104
|
+
render("#{self.class.name.downcase}_listing")
|
105
|
+
else
|
106
|
+
render(:mount_listing)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
sendfile(@path)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
const_set(opts[:name], klass) if opts[:name]
|
114
|
+
|
115
|
+
if $DBG
|
116
|
+
puts "** mounted #{path}"
|
117
|
+
puts "Controller : #{klass}"
|
118
|
+
puts "Options : #{opts.inspect}"
|
119
|
+
end
|
120
|
+
|
121
|
+
klass
|
122
|
+
end
|
123
|
+
alias :M :mount
|
124
|
+
end
|
125
|
+
|
126
|
+
module Views
|
127
|
+
# Default dir listing. @files contains all the files.
|
128
|
+
def mount_listing
|
129
|
+
path_info = @env.PATH_INFO.sub /\/$/, ''
|
130
|
+
h1 "Dir listing of #{path_info}"
|
131
|
+
ul do
|
132
|
+
li { a '..', :href => path_info.sub(/\w*$/,'') }
|
133
|
+
@files.each do |file|
|
134
|
+
li { a file, :href => "#{path_info}/#{file}" }
|
135
|
+
end
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
### Mime-types extensions
|
145
|
+
|
146
|
+
js_type = MIME::Type.from_hash(
|
147
|
+
'Content-Type' => 'text/javascript',
|
148
|
+
'Extensions' => ['js']
|
149
|
+
)
|
150
|
+
|
151
|
+
MIME::Types.add(js_type)
|
152
|
+
|