merb 0.0.3 → 0.0.4
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 +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
|