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.
Files changed (65) hide show
  1. data/README +68 -58
  2. data/Rakefile +7 -6
  3. data/TODO +18 -0
  4. data/bin/merb +2 -2
  5. data/doc/rdoc/classes/Hash.html +148 -0
  6. data/doc/rdoc/classes/Merb/Config.html +161 -0
  7. data/doc/rdoc/classes/Merb/Controller.html +488 -0
  8. data/doc/rdoc/classes/Merb/RouteMatcher.html +354 -0
  9. data/doc/rdoc/classes/Merb/Server.html +288 -0
  10. data/doc/rdoc/classes/Merb.html +143 -0
  11. data/doc/rdoc/classes/MerbHandler.html +404 -0
  12. data/doc/rdoc/classes/MerbHash.html +457 -0
  13. data/doc/rdoc/classes/Noroutefound.html +172 -0
  14. data/doc/rdoc/classes/Object.html +149 -0
  15. data/doc/rdoc/classes/String.html +200 -0
  16. data/doc/rdoc/classes/Symbol.html +172 -0
  17. data/doc/rdoc/created.rid +1 -0
  18. data/doc/rdoc/files/LICENSE.html +129 -0
  19. data/doc/rdoc/files/README.html +330 -0
  20. data/doc/rdoc/files/TODO.html +154 -0
  21. data/doc/rdoc/files/lib/merb/merb_controller_rb.html +101 -0
  22. data/doc/rdoc/files/lib/merb/merb_daemon_rb.html +113 -0
  23. data/doc/rdoc/files/lib/merb/merb_handler_rb.html +108 -0
  24. data/doc/rdoc/files/lib/merb/merb_router_rb.html +101 -0
  25. data/doc/rdoc/files/lib/merb/merb_utils_rb.html +101 -0
  26. data/doc/rdoc/files/lib/merb/noroutefound_rb.html +101 -0
  27. data/doc/rdoc/files/lib/merb_config_rb.html +101 -0
  28. data/doc/rdoc/files/lib/merb_rb.html +112 -0
  29. data/doc/rdoc/fr_class_index.html +38 -0
  30. data/doc/rdoc/fr_file_index.html +38 -0
  31. data/doc/rdoc/fr_method_index.html +78 -0
  32. data/doc/rdoc/index.html +24 -0
  33. data/doc/rdoc/rdoc-style.css +208 -0
  34. data/examples/sample_app/{app → dist/app}/controllers/posts.rb +8 -8
  35. data/examples/sample_app/{app → dist/app}/controllers/test.rb +8 -5
  36. data/examples/sample_app/{app → dist/app}/controllers/upload.rb +13 -5
  37. data/examples/sample_app/dist/app/models/post.rb +13 -0
  38. data/examples/sample_app/dist/app/views/layout/application.rhtml +6 -0
  39. data/examples/sample_app/dist/app/views/layout/foo.rhtml +6 -0
  40. data/examples/sample_app/dist/app/views/layout/posts.rhtml +6 -0
  41. data/examples/sample_app/{app → dist/app}/views/posts/list.rhtml +1 -7
  42. data/examples/sample_app/{app → dist/app}/views/posts/new.rhtml +1 -7
  43. data/examples/sample_app/{app → dist/app}/views/posts/show.rhtml +1 -8
  44. data/examples/sample_app/dist/app/views/test/foo.rhtml +2 -0
  45. data/examples/sample_app/{app → dist/app}/views/test/hello.rhtml +1 -8
  46. data/examples/sample_app/{app → dist/app}/views/upload/start.rhtml +0 -8
  47. data/examples/sample_app/{app → dist/app}/views/upload/upload.rhtml +0 -0
  48. data/examples/sample_app/{conf → dist/conf}/merb_init.rb +5 -2
  49. data/examples/sample_app/{conf → dist/conf}/router.rb +6 -8
  50. data/examples/sample_app/test/test_helper.rb +1 -0
  51. data/examples/skeleton/dist/conf/merb_init.rb +21 -0
  52. data/examples/skeleton/dist/conf/router.rb +23 -0
  53. data/examples/skeleton/test/test_helper.rb +1 -0
  54. data/lib/merb/merb_controller.rb +54 -15
  55. data/lib/merb/merb_daemon.rb +35 -20
  56. data/lib/merb/merb_handler.rb +49 -38
  57. data/lib/merb/merb_router.rb +25 -28
  58. data/lib/merb/merb_utils.rb +98 -9
  59. data/{examples/sample_app/app/controllers → lib/merb}/noroutefound.rb +1 -1
  60. data/lib/merb.rb +4 -4
  61. data/lib/merb_config.rb +1 -1
  62. data/test/unit/route_matcher_test.rb +12 -31
  63. metadata +109 -28
  64. data/examples/sample_app/app/models/post.rb +0 -2
  65. data/examples/sample_app/app/views/test/foo.rhtml +0 -6
@@ -1,19 +1,21 @@
1
1
  module Merb
2
2
 
3
- # controller class for the merb pocket-framework. All of your
4
- # controllers will inherit from Merb::Controller. This superclass
5
- # takes care of parsing the incoming headers and body into params
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
- # Stolen from Camping without a twinge of remorse ;)
13
- def initialize(req, env, method=(env['REQUEST_METHOD']||"GET")) #:nodoc:
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
- def self.template_dir(loc)
90
- @@template_dir = File.expand_path(Merb::Server.config[:merb_root] + "/app/views/#{loc}")
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
- $1
108
+ caller[depth] =~ /`(.*)'$/; $1
109
+ end
110
+
111
+
112
+ def layout(l)
113
+ @layout = l
96
114
  end
97
115
 
98
- def render(template=current_method_name(1),b=binding)
99
- template = ERB.new( IO.read( @@template_dir + "/#{template}.rhtml" ) )
100
- template.result( b )
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
@@ -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 = Daemons::Controller.new(options, ARGV).app_part
56
+ @@merb_raw_opts = ARGV
57
57
  merb_config
58
-
59
- require @@merb_opts[:merb_root]+'/conf/router.rb'
60
- require @@merb_opts[:merb_root]+'/conf/merb_init.rb'
61
- $LOAD_PATH.unshift( File.join(@@merb_opts[:merb_root] , 'app/controllers') )
62
- $LOAD_PATH.unshift( File.join(@@merb_opts[:merb_root] , 'lib') )
63
-
64
-
65
- h = Mongrel::HttpServer.new((@@merb_opts[:host]||"0.0.0.0"), (@@merb_opts[:port]||4000), (@@merb_opts[:numprocs]||40))
66
- h.register("/", MerbHandler.new(@@merb_opts[:merb_root]+'/public'))
67
- h.register("/favicon.ico", Mongrel::Error404Handler.new(""))
68
- h.run.join
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
+
@@ -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
- controller, method, args = handle(request)
47
- output = if (controller && controller.kind_of?(Merb::Controller))
48
- if method
49
- ( args ? controller.send( method, args ) : controller.send(method) )
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
- controller.to_s
52
- end
53
- else
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 <<"<html><h2>Merb Error!</h2><pre>#{ e.message } - (#{ e.class })" <<
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 heaqder from your Merb::Controller
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["PATH_INFO"].sub(/\/+/, '/')
112
+ path = request.params[Mongrel::Const::PATH_INFO].sub(/\/+/, '/')
109
113
  path = path[0..-2] if (path[-1] == ?/)
110
- routes = Merb::RouteMatcher.new
111
- route = routes.route_request(path)
114
+ route = Merb::RouteMatcher.new.route_request(path)
115
+ puts route.inspect if $DEBUG
112
116
  if route
113
- [ instantiate_controller(route.delete(:class), request.body, request.params),
114
- route.delete(:method), 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, 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 p[assing in the request and response.
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[:merb_root]+"/app/controllers/#{controller_name}.rb")
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
- return Object.const_get( controller_name.controller_class_name ).new(req, env)
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.controller_class_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
@@ -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 :smbol notation.
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. These two methods map
11
- # the default /controller/action and /controller/action/id urls you will
12
- # use so you will want these two by default, in this order:
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
- SECTION_REGEX = /(?::([a-z]+))/ unless defined?SECTION_REGEX
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('lambda{|path| ') { |m,r|
42
+ router_lambda = @@routes.inject("lambda{|path| \n case path\n") { |m,r|
58
43
  m << compile(r)
59
- } << "\n return {:class=>'NoRouteFound', :method=>'noroute'}\n}"
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] =~ SECTION_REGEX
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
- route[0].sub!(SECTION_REGEX, '(.+)')
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 = " if Regexp.new('#{route[0]}') =~ path"
75
- statement = "\n#{condition}\n#{code}"
76
- statement << " return #{route[1].inspect}.merge(@sections)\n end\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
 
@@ -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 controller_class_name
7
- self.capitalize
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
- class MerbHash < HashWithIndifferentAccess
12
- def method_missing(m,*a)
13
- m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")
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
- end
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
@@ -1,6 +1,6 @@
1
1
  class Noroutefound < Merb::Controller
2
2
 
3
- def method_missing(*args)
3
+ def method_missing(sym, *args, &blk)
4
4
  "<html><body><h1>No Matching Route</h1></body></html>"
5
5
  end
6
6
 
data/lib/merb.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  require 'rubygems'
2
2
  require 'mongrel'
3
3
  require 'fileutils'
4
- require 'erb'
4
+ require 'erubis'
5
5
  require 'merb_config'
6
- require 'active_support'
6
+
7
7
 
8
8
  module Merb
9
- VERSION='0.0.3' unless defined?VERSION
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', :class => 'Test', :method => 'foo'
14
- r.add '/hey/:there/you/:guys', :class => 'Test', :method => 'hello'
15
- r.add '/these/:routes/are/:sweet', :class => 'Upload', :method => 'start'
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( {:class=>"Test", :id =>"456", :bar =>"123", :method=>"foo"}, routes.route_request("/foo/123/baz/456"))
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( {:class=>"Test", :guys =>"girls", :there =>"jon", :method=>"hello"}, routes.route_request( "/hey/jon/you/girls"))
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({:class=>"upload", :id=>"12", :method=>"test"},routes.route_request( "/upload/test/12"))
33
+ assert_same_hash({:controller=>"upload", :id=>nil, :action=>"index"},routes.route_request( "/upload"))
38
34
  routes = Merb::RouteMatcher.new
39
- assert_same_hash({:class=>"nik", :method=>"nak"},routes.route_request( "/nik/nak"))
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({:class=>"lots", :method=>"of", :id => "12"},routes.route_request( "/lots/of/12"))
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 test_route_doesnt_match
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.routes.each do |r|
60
- assert Regexp.new(r[0]) =~ routes.compiled_statement
61
- end
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