merb 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +68 -58
- data/Rakefile +7 -6
- data/TODO +18 -0
- data/bin/merb +2 -2
- data/doc/rdoc/classes/Hash.html +148 -0
- data/doc/rdoc/classes/Merb/Config.html +161 -0
- data/doc/rdoc/classes/Merb/Controller.html +488 -0
- data/doc/rdoc/classes/Merb/RouteMatcher.html +354 -0
- data/doc/rdoc/classes/Merb/Server.html +288 -0
- data/doc/rdoc/classes/Merb.html +143 -0
- data/doc/rdoc/classes/MerbHandler.html +404 -0
- data/doc/rdoc/classes/MerbHash.html +457 -0
- data/doc/rdoc/classes/Noroutefound.html +172 -0
- data/doc/rdoc/classes/Object.html +149 -0
- data/doc/rdoc/classes/String.html +200 -0
- data/doc/rdoc/classes/Symbol.html +172 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/LICENSE.html +129 -0
- data/doc/rdoc/files/README.html +330 -0
- data/doc/rdoc/files/TODO.html +154 -0
- data/doc/rdoc/files/lib/merb/merb_controller_rb.html +101 -0
- data/doc/rdoc/files/lib/merb/merb_daemon_rb.html +113 -0
- data/doc/rdoc/files/lib/merb/merb_handler_rb.html +108 -0
- data/doc/rdoc/files/lib/merb/merb_router_rb.html +101 -0
- data/doc/rdoc/files/lib/merb/merb_utils_rb.html +101 -0
- data/doc/rdoc/files/lib/merb/noroutefound_rb.html +101 -0
- data/doc/rdoc/files/lib/merb_config_rb.html +101 -0
- data/doc/rdoc/files/lib/merb_rb.html +112 -0
- data/doc/rdoc/fr_class_index.html +38 -0
- data/doc/rdoc/fr_file_index.html +38 -0
- data/doc/rdoc/fr_method_index.html +78 -0
- data/doc/rdoc/index.html +24 -0
- data/doc/rdoc/rdoc-style.css +208 -0
- data/examples/sample_app/{app → dist/app}/controllers/posts.rb +8 -8
- data/examples/sample_app/{app → dist/app}/controllers/test.rb +8 -5
- data/examples/sample_app/{app → dist/app}/controllers/upload.rb +13 -5
- data/examples/sample_app/dist/app/models/post.rb +13 -0
- data/examples/sample_app/dist/app/views/layout/application.rhtml +6 -0
- data/examples/sample_app/dist/app/views/layout/foo.rhtml +6 -0
- data/examples/sample_app/dist/app/views/layout/posts.rhtml +6 -0
- data/examples/sample_app/{app → dist/app}/views/posts/list.rhtml +1 -7
- data/examples/sample_app/{app → dist/app}/views/posts/new.rhtml +1 -7
- data/examples/sample_app/{app → dist/app}/views/posts/show.rhtml +1 -8
- data/examples/sample_app/dist/app/views/test/foo.rhtml +2 -0
- data/examples/sample_app/{app → dist/app}/views/test/hello.rhtml +1 -8
- data/examples/sample_app/{app → dist/app}/views/upload/start.rhtml +0 -8
- data/examples/sample_app/{app → dist/app}/views/upload/upload.rhtml +0 -0
- data/examples/sample_app/{conf → dist/conf}/merb_init.rb +5 -2
- data/examples/sample_app/{conf → dist/conf}/router.rb +6 -8
- data/examples/sample_app/test/test_helper.rb +1 -0
- data/examples/skeleton/dist/conf/merb_init.rb +21 -0
- data/examples/skeleton/dist/conf/router.rb +23 -0
- data/examples/skeleton/test/test_helper.rb +1 -0
- data/lib/merb/merb_controller.rb +54 -15
- data/lib/merb/merb_daemon.rb +35 -20
- data/lib/merb/merb_handler.rb +49 -38
- data/lib/merb/merb_router.rb +25 -28
- data/lib/merb/merb_utils.rb +98 -9
- data/{examples/sample_app/app/controllers → lib/merb}/noroutefound.rb +1 -1
- data/lib/merb.rb +4 -4
- data/lib/merb_config.rb +1 -1
- data/test/unit/route_matcher_test.rb +12 -31
- metadata +109 -28
- data/examples/sample_app/app/models/post.rb +0 -2
- data/examples/sample_app/app/views/test/foo.rhtml +0 -6
data/lib/merb/merb_controller.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
1
|
module Merb
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# and cookies and headers. If the request is a file upload it will
|
3
|
+
# All of your controllers will inherit from Merb::Controller. This
|
4
|
+
# superclass takes care of parsing the incoming headers and body into
|
5
|
+
# params and cookies and headers. If the request is a file upload it will
|
7
6
|
# stream it into a tempfile and pass in the filename and tempfile object
|
8
7
|
# to your controller via params. It also parses the ?query=string and
|
9
8
|
# puts that into params as well.
|
10
9
|
class Controller
|
10
|
+
|
11
11
|
attr_accessor :status
|
12
|
-
|
13
|
-
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(req, env, args, method=(env['REQUEST_METHOD']||"GET")) #:nodoc:
|
14
15
|
env = MerbHash[env.to_hash]
|
15
16
|
puts env.inspect if $DEBUG
|
16
17
|
puts req.inspect if $DEBUG
|
18
|
+
@layout = 'application'
|
17
19
|
@status, @method, @env, @headers, @root = 200, method.downcase, env,
|
18
20
|
{'Content-Type'=>'text/html'}, env['SCRIPT_NAME'].sub(/\/$/,'')
|
19
21
|
@k = query_parse(env['HTTP_COOKIE'], ';,')
|
@@ -54,7 +56,18 @@ module Merb
|
|
54
56
|
elsif @method == "post"
|
55
57
|
qs.merge!(query_parse(@in.read))
|
56
58
|
end
|
57
|
-
@cookies, @params = @k.dup, qs.dup
|
59
|
+
@cookies, @params = @k.dup, qs.dup.merge(args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def redirect(url)
|
63
|
+
@status = 302
|
64
|
+
@headers.merge!({'Location'=> url})
|
65
|
+
return ''
|
66
|
+
end
|
67
|
+
|
68
|
+
def send_file(file)
|
69
|
+
headers['X-SENDFILE'] = file
|
70
|
+
return
|
58
71
|
end
|
59
72
|
|
60
73
|
def params
|
@@ -85,20 +98,46 @@ module Merb
|
|
85
98
|
def unescape(s)
|
86
99
|
Mongrel::HttpRequest.unescape(s)
|
87
100
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
101
|
+
|
102
|
+
|
103
|
+
def template_dir(loc)
|
104
|
+
File.expand_path(Merb::Server.config[:merb_root] + "/dist/app/views/#{loc}")
|
91
105
|
end
|
92
106
|
|
93
107
|
def current_method_name(depth=0)
|
94
|
-
caller[depth] =~ /`(.*)'
|
95
|
-
|
108
|
+
caller[depth] =~ /`(.*)'$/; $1
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def layout(l)
|
113
|
+
@layout = l
|
96
114
|
end
|
97
115
|
|
98
|
-
def
|
99
|
-
template =
|
100
|
-
template.result(
|
116
|
+
def render_no_layout(template=current_method_name(1), b=binding)
|
117
|
+
template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/#{template}.rhtml" ) )
|
118
|
+
template.result(b)
|
101
119
|
end
|
120
|
+
|
121
|
+
def render(template=current_method_name(1), b=binding)
|
122
|
+
name = self.class.name.snake_case
|
123
|
+
template = Erubis::Eruby.new( IO.read( template_dir(name) + "/#{template}.rhtml" ) )
|
124
|
+
layout_content = template.result(b)
|
125
|
+
return layout_content if (@layout.to_s == 'none')
|
126
|
+
if ['application', name].include?(@layout.to_s)
|
127
|
+
if File.exist?(template_dir("layout/#{name}.rhtml"))
|
128
|
+
layout = name
|
129
|
+
else
|
130
|
+
layout = 'application'
|
131
|
+
end
|
132
|
+
else
|
133
|
+
layout = @layout.to_s
|
134
|
+
end
|
135
|
+
@layout_content = layout_content
|
136
|
+
layout_tmpl = Erubis::Eruby.new( IO.read( template_dir('layout') + "/#{layout}.rhtml" ) )
|
137
|
+
layout_tmpl.result(b)
|
138
|
+
end
|
139
|
+
|
140
|
+
|
102
141
|
end
|
103
142
|
|
104
143
|
end
|
data/lib/merb/merb_daemon.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'daemons'
|
3
2
|
require 'merb'
|
4
3
|
require 'optparse'
|
5
4
|
require 'ostruct'
|
@@ -7,11 +6,6 @@ require 'ostruct'
|
|
7
6
|
module Merb
|
8
7
|
end
|
9
8
|
|
10
|
-
module Daemons
|
11
|
-
class Controller
|
12
|
-
attr_accessor :app_part
|
13
|
-
end
|
14
|
-
end
|
15
9
|
|
16
10
|
class Merb::Server
|
17
11
|
|
@@ -36,7 +30,11 @@ class Merb::Server
|
|
36
30
|
end
|
37
31
|
|
38
32
|
opts.on("-m", "--merb-root [String]") do |merb_root|
|
39
|
-
options[:merb_root] = merb_root
|
33
|
+
options[:merb_root] = File.expand_path(merb_root)
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-i", "--irb-console [String]") do |console|
|
37
|
+
options[:console] = true
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
@@ -44,6 +42,8 @@ class Merb::Server
|
|
44
42
|
|
45
43
|
|
46
44
|
@@merb_opts = options
|
45
|
+
puts %{Merb started with these options:}
|
46
|
+
puts @@merb_opts.to_yaml; puts
|
47
47
|
end
|
48
48
|
|
49
49
|
def self.run
|
@@ -53,19 +53,33 @@ class Merb::Server
|
|
53
53
|
:dir_mode => :normal
|
54
54
|
}
|
55
55
|
|
56
|
-
@@merb_raw_opts =
|
56
|
+
@@merb_raw_opts = ARGV
|
57
57
|
merb_config
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
58
|
+
|
59
|
+
@@merb_opts[:dist_root] = @@merb_opts[:merb_root]+'/dist'
|
60
|
+
|
61
|
+
$LOAD_PATH.unshift( File.join(@@merb_opts[:merb_root] , '/dist/app/controllers') )
|
62
|
+
$LOAD_PATH.unshift( File.join(@@merb_opts[:merb_root] , '/dist/lib') )
|
63
|
+
require @@merb_opts[:merb_root]+'/dist/conf/router.rb'
|
64
|
+
require @@merb_opts[:merb_root]+'/dist/conf/merb_init.rb'
|
65
|
+
|
66
|
+
if @@merb_opts[:console]
|
67
|
+
ARGV.clear # Avoid passing args to IRB
|
68
|
+
require 'irb'
|
69
|
+
require 'irb/completion'
|
70
|
+
def exit
|
71
|
+
exit!
|
72
|
+
end
|
73
|
+
if File.exists? ".irbrc"
|
74
|
+
ENV['IRBRC'] = ".irbrc"
|
75
|
+
end
|
76
|
+
IRB.start
|
77
|
+
end
|
78
|
+
|
79
|
+
h = Mongrel::HttpServer.new((@@merb_opts[:host]||"0.0.0.0"), (@@merb_opts[:port]||4000))#, (@@merb_opts[:numprocs]||40))
|
80
|
+
h.register("/", MerbHandler.new(@@merb_opts[:dist_root]+'/public'))
|
81
|
+
h.register("/favicon.ico", Mongrel::Error404Handler.new(""))
|
82
|
+
h.run.join
|
69
83
|
|
70
84
|
end
|
71
85
|
|
@@ -73,4 +87,5 @@ class Merb::Server
|
|
73
87
|
@@merb_opts
|
74
88
|
end
|
75
89
|
|
76
|
-
end
|
90
|
+
end
|
91
|
+
|
data/lib/merb/merb_handler.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'sync'
|
1
2
|
class MerbHandler < Mongrel::HttpHandler
|
2
3
|
@@file_only_methods = ["GET","HEAD"]
|
3
4
|
|
@@ -6,6 +7,7 @@ class MerbHandler < Mongrel::HttpHandler
|
|
6
7
|
# by default. See merb_daemon.rb if you want to change this.
|
7
8
|
def initialize(dir, opts = {})
|
8
9
|
@files = Mongrel::DirHandler.new(dir,false)
|
10
|
+
@guard = Sync.new
|
9
11
|
end
|
10
12
|
|
11
13
|
# process incoming http requests and do a number of things
|
@@ -43,37 +45,37 @@ class MerbHandler < Mongrel::HttpHandler
|
|
43
45
|
@files.process(request,response)
|
44
46
|
else
|
45
47
|
begin
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
# This handles parsing the query string and post/file upload
|
49
|
+
# params and is outside of the synchronize call so that
|
50
|
+
# multiple file uploads can be done at once.
|
51
|
+
controller, action = handle(request)
|
52
|
+
p controller, action
|
53
|
+
output = nil
|
54
|
+
# synchronize here because this is where ActiveRecord or your db
|
55
|
+
# calls will be run in your controller methods.
|
56
|
+
@guard.synchronize(:EX) {
|
57
|
+
output = if (controller && controller.kind_of?(Merb::Controller))
|
58
|
+
if action
|
59
|
+
controller.send(action)
|
60
|
+
else
|
61
|
+
controller.to_s
|
62
|
+
end
|
50
63
|
else
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
nil
|
55
|
-
end
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
}
|
56
67
|
rescue Exception => e
|
57
68
|
response.start(500) do |head,out|
|
58
69
|
head["Content-Type"] = "text/html"
|
59
|
-
out <<
|
60
|
-
"\n" << "#{(e.backtrace or []).join('\n')}</pre></html>"
|
70
|
+
out << exception(e)
|
61
71
|
end
|
62
72
|
return
|
63
|
-
end
|
64
|
-
|
65
|
-
unless output
|
66
|
-
response.start(404) do |head,out|
|
67
|
-
head["Content-Type"] = "text/html"
|
68
|
-
out << "<html><body>Error: no merb controller found for this url.</body></html>"
|
69
|
-
end
|
70
|
-
return
|
71
|
-
end
|
73
|
+
end
|
72
74
|
|
73
75
|
sendfile, clength = nil
|
74
76
|
response.status = controller.status
|
75
77
|
|
76
|
-
# check for the X-SENDFILE
|
78
|
+
# check for the X-SENDFILE header from your Merb::Controller
|
77
79
|
# and serve the file directly instead of buffering.
|
78
80
|
controller.headers.each do |k, v|
|
79
81
|
if k =~ /^X-SENDFILE$/i
|
@@ -99,39 +101,48 @@ class MerbHandler < Mongrel::HttpHandler
|
|
99
101
|
response.write(output)
|
100
102
|
end
|
101
103
|
end
|
102
|
-
end
|
103
|
-
|
104
|
+
end
|
105
|
+
|
104
106
|
# This is where we grab the incoming request PATH_INFO
|
105
107
|
# and use that in the merb routematcher to determine
|
106
|
-
# which controller and method to run.
|
108
|
+
# which controller and method to run.
|
109
|
+
# returns a 2 element tuple of:
|
110
|
+
# [controller_object, method]
|
107
111
|
def handle(request)
|
108
|
-
path = request.params[
|
112
|
+
path = request.params[Mongrel::Const::PATH_INFO].sub(/\/+/, '/')
|
109
113
|
path = path[0..-2] if (path[-1] == ?/)
|
110
|
-
|
111
|
-
route
|
114
|
+
route = Merb::RouteMatcher.new.route_request(path)
|
115
|
+
puts route.inspect if $DEBUG
|
112
116
|
if route
|
113
|
-
[ instantiate_controller(route
|
114
|
-
route.
|
117
|
+
[ instantiate_controller(route[:controller], request.body, request.params,
|
118
|
+
route.dup.delete_if{|k,v| [:controller, :action].include? k}),
|
119
|
+
route[:action]]
|
115
120
|
else
|
116
|
-
["<html><body>Error: no route matches!</body></html>", nil
|
121
|
+
["<html><body>Error: no route matches!</body></html>", nil]
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
120
125
|
# take a controller class name string and reload or require
|
121
126
|
# the right controller file then upcase the first letter and
|
122
|
-
# trun it into a new object
|
127
|
+
# trun it into a new object passing in the request and response.
|
123
128
|
# this is where your Merb::Controller is instantiated.
|
124
|
-
def instantiate_controller(controller_name, req, env)
|
125
|
-
if !File.exist?(Merb::Server.config[:
|
126
|
-
return Object.const_get(:Noroutefound).new(req, env)
|
127
|
-
end
|
128
|
-
controller_name.import
|
129
|
+
def instantiate_controller(controller_name, req, env, params)
|
130
|
+
if !File.exist?(Merb::Server.config[:dist_root]+"/app/controllers/#{controller_name.snake_case}.rb")
|
131
|
+
return Object.const_get(:Noroutefound).new(req, env, params)
|
132
|
+
end
|
129
133
|
begin
|
130
|
-
|
134
|
+
controller_name.import
|
135
|
+
return Object.const_get( controller_name.camel_case ).new(req, env, params)
|
131
136
|
rescue Exception
|
132
|
-
warn "Error getting instance of '#{controller_name.
|
137
|
+
warn "Error getting instance of '#{controller_name.camel_case}': #{$!}"
|
133
138
|
raise $!
|
134
139
|
end
|
135
140
|
end
|
136
141
|
|
142
|
+
# format exception message for browser display
|
143
|
+
def exception(e)
|
144
|
+
"<html><h2>Merb Error!</h2><p>#{ e.message } - (#{ e.class })\n" <<
|
145
|
+
"#{(e.backtrace or []).join('<br />')}</p></html>"
|
146
|
+
end
|
147
|
+
|
137
148
|
end
|
data/lib/merb/merb_router.rb
CHANGED
@@ -1,34 +1,19 @@
|
|
1
1
|
module Merb
|
2
2
|
|
3
3
|
# Merb::RouteMatcher is the request routing mapper for the merb framework.
|
4
|
-
# You can define placeholder parts of the url with the :
|
4
|
+
# You can define placeholder parts of the url with the :symbol notation.
|
5
5
|
# so r.add '/foo/:bar/baz/:id', :class => 'Bar', :method => 'foo'
|
6
6
|
# will match against a request to /foo/123/baz/456. It will then
|
7
7
|
# use the class Bar as your merb controller and call the foo method on it.
|
8
8
|
# the foo method will recieve a hash with {:bar => '123', :id => '456'}
|
9
9
|
# as the content. So the :placeholders sections of your routes become
|
10
|
-
# a hash of arguments to your controller methods.
|
11
|
-
# the default /controller/action and /controller/action/id urls
|
12
|
-
#
|
13
|
-
# r.add '/:class/:method/:id', {}
|
14
|
-
# r.add '/:class/:method', {}
|
15
|
-
#
|
16
|
-
# This is what a route prepare definition looks like. I have added
|
17
|
-
# code that will spit out the compiled routing lambda so you can see
|
18
|
-
# what is generated:
|
19
|
-
#
|
20
|
-
# puts "Compiling routes: \n"
|
21
|
-
# Merb::RouteMatcher.prepare do |r|
|
22
|
-
# r.add '/foo/:bar/baz/:id', :class => 'Test', :method => 'foo'
|
23
|
-
# r.add '/:class/:method/:id', {}
|
24
|
-
# r.add '/:class/:method', {}
|
25
|
-
# end
|
26
|
-
# m = Merb::RouteMatcher.new
|
27
|
-
# puts m.compiled_statement
|
10
|
+
# a hash of arguments to your controller methods. This route maps
|
11
|
+
# the default /controller/action and /controller/action/id urls:
|
12
|
+
# r.add '/:class/:method/:id'
|
28
13
|
class RouteMatcher
|
29
14
|
|
30
15
|
attr_accessor :sections
|
31
|
-
|
16
|
+
@@section_regexp = /(?::([a-z*]+))/.freeze
|
32
17
|
|
33
18
|
def self.prepare
|
34
19
|
@@routes = Array.new
|
@@ -50,30 +35,42 @@ module Merb
|
|
50
35
|
end
|
51
36
|
|
52
37
|
def self.add(*route)
|
53
|
-
@@routes << route
|
38
|
+
@@routes << [route[0], (route[1] || {})]
|
54
39
|
end
|
55
40
|
|
56
41
|
def self.compile_router
|
57
|
-
router_lambda = @@routes.inject(
|
42
|
+
router_lambda = @@routes.inject("lambda{|path| \n case path\n") { |m,r|
|
58
43
|
m << compile(r)
|
59
|
-
} <<
|
44
|
+
} <<" else\n return {:controller=>'Noroutefound', :action=>'noroute'}\n end\n}"
|
60
45
|
@@compiled_statement = router_lambda
|
61
46
|
define_method(:route_request, &eval(router_lambda))
|
62
47
|
end
|
63
48
|
|
64
49
|
def self.compile(route)
|
65
50
|
raise ArgumentError unless String === route[0]
|
51
|
+
if route[0] == '/:controller/:action/:id'
|
52
|
+
return ' when /\A\/([^\/;.,?]+)(?:\/?\Z|\/([^\/;.,?]+)\/?)(?:\/?\Z|\/([^\/;.,?]+)\/?)\Z/
|
53
|
+
@sections[:controller] = $1
|
54
|
+
@sections[:action] = $2 || \'index\'
|
55
|
+
@sections[:id] = $3 || nil
|
56
|
+
return @sections'<<"\n"
|
57
|
+
end
|
66
58
|
code, count = '', 0
|
67
|
-
while route[0] =~
|
59
|
+
while route[0] =~ @@section_regexp
|
68
60
|
route[0] = route[0].dup
|
69
61
|
name = $1
|
62
|
+
(name =~ /(\*+)(\w+)/) ? (flag = true; name = $2) : (flag = false)
|
70
63
|
count += 1
|
71
|
-
|
64
|
+
if flag
|
65
|
+
route[0].sub!(@@section_regexp, "([^;.,?]+)")
|
66
|
+
else
|
67
|
+
route[0].sub!(@@section_regexp, "([^\/;.,?]+)")
|
68
|
+
end
|
72
69
|
code << " @sections[:#{name}] = $#{count}\n"
|
73
70
|
end
|
74
|
-
condition = "
|
75
|
-
statement = "
|
76
|
-
statement << " return #{route[1].inspect}.merge(@sections)\n
|
71
|
+
condition = " when Regexp.new('#{route[0]}')"
|
72
|
+
statement = "#{condition}\n#{code}"
|
73
|
+
statement << " return #{route[1].inspect}.merge(@sections)\n"
|
77
74
|
statement
|
78
75
|
end
|
79
76
|
|
data/lib/merb/merb_utils.rb
CHANGED
@@ -1,21 +1,110 @@
|
|
1
1
|
class String
|
2
2
|
def import
|
3
|
-
Merb::Server.config[:allow_reloading] ? load( self + '.rb' ) : require( self )
|
3
|
+
Merb::Server.config[:allow_reloading] ? load( self.snake_case + '.rb' ) : require( self.snake_case )
|
4
4
|
end
|
5
|
-
|
6
|
-
def
|
7
|
-
self
|
5
|
+
|
6
|
+
def snake_case
|
7
|
+
return self unless self =~ %r/[A-Z]/
|
8
|
+
self.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?/).reverse.map{|word| word.reverse.downcase}.join '_'
|
8
9
|
end
|
9
|
-
end
|
10
10
|
|
11
|
-
|
12
|
-
def
|
13
|
-
|
11
|
+
|
12
|
+
def camel_case
|
13
|
+
return self if self =~ %r/[A-Z]/ and self !~ %r/_/
|
14
|
+
words = self.strip.split %r/\s*_+\s*/
|
15
|
+
words.map!{|w| w.downcase.sub(%r/^./){|c| c.upcase}}
|
16
|
+
words.join
|
14
17
|
end
|
18
|
+
|
15
19
|
end
|
16
20
|
|
17
21
|
class Symbol
|
18
22
|
def to_s
|
19
23
|
@str_rep || (@str_rep = id2name.freeze)
|
20
24
|
end
|
21
|
-
|
25
|
+
def to_proc
|
26
|
+
Proc.new{|*args| args.shift.__send__(self, *args)}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Object
|
31
|
+
def returning(value)
|
32
|
+
yield(value)
|
33
|
+
value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Hash
|
38
|
+
def with_indifferent_access
|
39
|
+
MerbHash.new(self)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class MerbHash < Hash
|
44
|
+
def initialize(constructor = {})
|
45
|
+
if constructor.is_a?(Hash)
|
46
|
+
super()
|
47
|
+
update(constructor)
|
48
|
+
else
|
49
|
+
super(constructor)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def default(key)
|
54
|
+
self[key.to_s] if key.is_a?(Symbol)
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
58
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
59
|
+
|
60
|
+
def []=(key, value)
|
61
|
+
regular_writer(convert_key(key), convert_value(value))
|
62
|
+
end
|
63
|
+
|
64
|
+
def update(other_hash)
|
65
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_method :merge!, :update
|
70
|
+
|
71
|
+
def key?(key)
|
72
|
+
super(convert_key(key))
|
73
|
+
end
|
74
|
+
|
75
|
+
alias_method :include?, :key?
|
76
|
+
alias_method :has_key?, :key?
|
77
|
+
alias_method :member?, :key?
|
78
|
+
|
79
|
+
def fetch(key, *extras)
|
80
|
+
super(convert_key(key), *extras)
|
81
|
+
end
|
82
|
+
|
83
|
+
def values_at(*indices)
|
84
|
+
indices.collect {|key| self[convert_key(key)]}
|
85
|
+
end
|
86
|
+
|
87
|
+
def dup
|
88
|
+
MerbHash.new(self)
|
89
|
+
end
|
90
|
+
|
91
|
+
def merge(hash)
|
92
|
+
self.dup.update(hash)
|
93
|
+
end
|
94
|
+
|
95
|
+
def delete(key)
|
96
|
+
super(convert_key(key))
|
97
|
+
end
|
98
|
+
|
99
|
+
def method_missing(m,*a)
|
100
|
+
m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
def convert_key(key)
|
105
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
106
|
+
end
|
107
|
+
def convert_value(value)
|
108
|
+
value.is_a?(Hash) ? value.with_indifferent_access : value
|
109
|
+
end
|
110
|
+
end
|
data/lib/merb.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'mongrel'
|
3
3
|
require 'fileutils'
|
4
|
-
require '
|
4
|
+
require 'erubis'
|
5
5
|
require 'merb_config'
|
6
|
-
|
6
|
+
|
7
7
|
|
8
8
|
module Merb
|
9
|
-
VERSION='0.0.
|
9
|
+
VERSION='0.0.4' unless defined?VERSION
|
10
10
|
end
|
11
11
|
|
12
12
|
lib = File.join(File.dirname(__FILE__), 'merb')
|
13
|
-
Dir.foreach(lib) {|fn| require File.join(lib, fn) if fn =~ /\.rb$/}
|
13
|
+
Dir.foreach(lib) {|fn| require File.join(lib, fn) if fn =~ /\.rb$/}
|
data/lib/merb_config.rb
CHANGED
@@ -11,7 +11,7 @@ class Merb::Config
|
|
11
11
|
}
|
12
12
|
|
13
13
|
begin
|
14
|
-
options = defaults.merge(YAML.load(ERB.new(IO.read("#{defaults[:merb_root]}/conf/merb.yml")).result))
|
14
|
+
options = defaults.merge(YAML.load(ERB.new(IO.read("#{defaults[:merb_root]}/dist/conf/merb.yml")).result))
|
15
15
|
rescue
|
16
16
|
options = defaults
|
17
17
|
end
|
@@ -10,13 +10,9 @@ class RouteMatcherTest < Test::Unit::TestCase
|
|
10
10
|
|
11
11
|
def setup
|
12
12
|
Merb::RouteMatcher.prepare do |r|
|
13
|
-
r.add '/foo/:bar/baz/:id', :
|
14
|
-
r.add '
|
15
|
-
r.add '/
|
16
|
-
r.add '/h/:a/b/:c/d/:f/g/:id', :class => 'Test'
|
17
|
-
r.add '/:class/:method/:id', {}
|
18
|
-
r.add '/:class/:method', {}
|
19
|
-
r.add '.*', {:class => 'NoRouteFound', :method => 'noroute'}
|
13
|
+
r.add '/foo/:bar/baz/:id', :controller => 'Test', :action => 'foo'
|
14
|
+
r.add '/:controller/:action/:id'
|
15
|
+
r.add '/bar/:*rest', :controller => 'Test', :action => 'glob'
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
@@ -30,35 +26,20 @@ class RouteMatcherTest < Test::Unit::TestCase
|
|
30
26
|
|
31
27
|
def test_route_matching
|
32
28
|
routes = Merb::RouteMatcher.new
|
33
|
-
assert_same_hash( {:
|
29
|
+
assert_same_hash( {:controller=>"Test", :id =>"456", :bar =>"123", :action=>"foo"}, routes.route_request("/foo/123/baz/456"))
|
34
30
|
routes = Merb::RouteMatcher.new
|
35
|
-
assert_same_hash(
|
31
|
+
assert_same_hash({:controller=>"upload", :id=>"12", :action=>"test"},routes.route_request( "/upload/test/12"))
|
36
32
|
routes = Merb::RouteMatcher.new
|
37
|
-
assert_same_hash({:
|
33
|
+
assert_same_hash({:controller=>"upload", :id=>nil, :action=>"index"},routes.route_request( "/upload"))
|
38
34
|
routes = Merb::RouteMatcher.new
|
39
|
-
assert_same_hash({:
|
35
|
+
assert_same_hash({:controller=>"nik", :action=>"nak", :id => nil},routes.route_request( "/nik/nak"))
|
40
36
|
routes = Merb::RouteMatcher.new
|
41
|
-
assert_same_hash({:
|
42
|
-
routes = Merb::RouteMatcher.new
|
43
|
-
assert_same_hash({:class => 'NoRouteFound', :method => 'noroute'},routes.route_request( "/"))
|
44
|
-
routes = Merb::RouteMatcher.new
|
45
|
-
assert_same_hash({:class => 'NoRouteFound', :method => 'noroute'},routes.route_request( "/dfnasfnafn"))
|
46
|
-
routes = Merb::RouteMatcher.new
|
47
|
-
assert_same_hash( {:class=>"Upload", :routes=>"234", :sweet=>"yup", :method=>"start"}, routes.route_request( "/these/234/are/yup"))
|
48
|
-
routes = Merb::RouteMatcher.new
|
49
|
-
assert_same_hash( {:class=>"Test", :a=>"12", :c=>"red", :id=>"12", :f=>"blue"}, routes.route_request( '/h/12/b/red/d/blue/g/12'))
|
37
|
+
assert_same_hash({:controller=>"lots", :action=>"of", :id => "12"},routes.route_request( "/lots/of/12"))
|
50
38
|
end
|
51
39
|
|
52
|
-
def
|
53
|
-
routes = Merb::RouteMatcher.new
|
54
|
-
assert_same_hash({:class=>"NoRouteFound", :method=>"noroute"},routes.route_request( '/hdsfvsdfsdfdsf'))
|
55
|
-
end
|
56
|
-
|
57
|
-
def test_regexes_included_in_compiled_statement
|
40
|
+
def test_catch_all_glob_route
|
58
41
|
routes = Merb::RouteMatcher.new
|
59
|
-
routes.
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
|
42
|
+
assert_same_hash({:controller=>"Test", :action=>"glob", :rest => "hey/you/guys"},routes.route_request( "/bar/hey/you/guys"))
|
43
|
+
end
|
44
|
+
|
64
45
|
end
|