rails 0.8.5 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rails might be problematic. Click here for more details.

Files changed (66) hide show
  1. data/CHANGELOG +86 -0
  2. data/README +48 -8
  3. data/Rakefile +87 -108
  4. data/bin/breakpointer +3 -0
  5. data/bin/breakpointer_for_gem +4 -0
  6. data/bin/console +30 -0
  7. data/bin/generate +41 -0
  8. data/bin/rails +1 -1
  9. data/{dispatches/dispatch.servlet → bin/server} +15 -11
  10. data/configs/apache.conf +12 -27
  11. data/configs/database.yml +9 -2
  12. data/dispatches/dispatch.fcgi +2 -2
  13. data/dispatches/dispatch.rb +2 -2
  14. data/doc/index.html +12 -36
  15. data/environments/development.rb +5 -0
  16. data/environments/production.rb +3 -6
  17. data/environments/shared.rb +46 -20
  18. data/environments/shared_for_gem.rb +41 -8
  19. data/environments/test.rb +3 -6
  20. data/fresh_rakefile +25 -21
  21. data/generators/controller/USAGE +28 -0
  22. data/generators/controller/controller_generator.rb +22 -0
  23. data/generators/controller/templates/controller.rb +10 -0
  24. data/generators/{templates/controller_test.erb → controller/templates/functional_test.rb} +1 -1
  25. data/generators/{templates/helper.erb → controller/templates/helper.rb} +0 -0
  26. data/generators/controller/templates/view.rhtml +2 -0
  27. data/generators/mailer/USAGE +27 -0
  28. data/generators/mailer/mailer_generator.rb +22 -0
  29. data/generators/{templates/mailer_action.rhtml → mailer/templates/fixture.rhtml} +0 -0
  30. data/generators/{templates/mailer.erb → mailer/templates/mailer.rb} +4 -4
  31. data/generators/{templates/mailer_test.erb → mailer/templates/unit_test.rb} +2 -10
  32. data/generators/mailer/templates/view.rhtml +3 -0
  33. data/generators/model/USAGE +17 -0
  34. data/generators/model/model_generator.rb +10 -0
  35. data/generators/model/templates/fixtures.yml +1 -0
  36. data/generators/{templates/model.erb → model/templates/model.rb} +0 -2
  37. data/generators/{templates/model_test.erb → model/templates/unit_test.rb} +2 -3
  38. data/generators/scaffold/USAGE +25 -0
  39. data/generators/scaffold/scaffold_generator.rb +53 -0
  40. data/generators/scaffold/templates/controller.rb +57 -0
  41. data/generators/scaffold/templates/fixtures.yml +7 -0
  42. data/generators/scaffold/templates/functional_test.rb +79 -0
  43. data/generators/scaffold/templates/layout.rhtml +11 -0
  44. data/generators/scaffold/templates/style.css +53 -0
  45. data/generators/scaffold/templates/view_edit.rhtml +7 -0
  46. data/generators/scaffold/templates/view_list.rhtml +24 -0
  47. data/generators/scaffold/templates/view_new.rhtml +6 -0
  48. data/generators/scaffold/templates/view_show.rhtml +8 -0
  49. data/helpers/{abstract_application.rb → application.rb} +1 -4
  50. data/helpers/test_helper.rb +4 -5
  51. data/lib/binding_of_caller.rb +81 -0
  52. data/lib/breakpoint.rb +526 -0
  53. data/lib/breakpoint_client.rb +167 -0
  54. data/lib/dispatcher.rb +43 -12
  55. data/lib/rails_generator.rb +175 -0
  56. data/lib/webrick_server.rb +48 -52
  57. metadata +49 -21
  58. data/gem_snapshot +0 -14
  59. data/generators/new_controller.rb +0 -43
  60. data/generators/new_crud.rb +0 -34
  61. data/generators/new_mailer.rb +0 -43
  62. data/generators/new_model.rb +0 -31
  63. data/generators/templates/controller.erb +0 -24
  64. data/generators/templates/controller_view.rhtml +0 -10
  65. data/generators/templates/mailer_fixture.rhtml +0 -4
  66. data/lib/generator.rb +0 -112
@@ -0,0 +1,167 @@
1
+ require 'breakpoint'
2
+ require 'optparse'
3
+ require 'timeout'
4
+
5
+ options = {
6
+ :ClientURI => nil,
7
+ :ServerURI => "druby://localhost:42531",
8
+ :RetryDelay => 1,
9
+ :Verbose => false
10
+ }
11
+
12
+ ARGV.options do |opts|
13
+ script_name = File.basename($0)
14
+ opts.banner = [
15
+ "Usage: ruby #{script_name} [options] [server uri]",
16
+ "",
17
+ "This tool lets you connect to a breakpoint service ",
18
+ "which was started via Breakpoint.activate_drb.",
19
+ "",
20
+ "The server uri defaults to druby://localhost:42531"
21
+ ].join("\n")
22
+
23
+ opts.separator ""
24
+
25
+ opts.on("-c", "--client-uri=uri",
26
+ "Run the client on the specified uri.",
27
+ "This can be used to specify the port",
28
+ "that the client uses to allow for back",
29
+ "connections from the server.",
30
+ "Default: Find a good URI automatically.",
31
+ "Example: -c druby://localhost:12345"
32
+ ) { |options[:ClientURI]| }
33
+
34
+ opts.on("-s", "--server-uri=uri",
35
+ "Connect to the server specified at the",
36
+ "specified uri.",
37
+ "Default: druby://localhost:42531"
38
+ ) { |options[:ServerURI]| }
39
+
40
+ opts.on("-v", "--verbose",
41
+ "Report all connections and disconnections",
42
+ "Default: false"
43
+ ) { |options[:Verbose]| }
44
+
45
+ opts.on("-R", "--retry-delay=delay", Integer,
46
+ "Automatically try to reconnect to the",
47
+ "server after delay seconds when the",
48
+ "connection failed or timed out.",
49
+ "A value of 0 disables automatical",
50
+ "reconnecting completely.",
51
+ "Default: 10"
52
+ ) { |options[:RetryDelay]| }
53
+
54
+ opts.separator ""
55
+
56
+ opts.on("-h", "--help",
57
+ "Show this help message."
58
+ ) { puts opts; exit }
59
+
60
+ opts.parse!
61
+ end
62
+
63
+ options[:ServerURI] = ARGV[0] if ARGV[0]
64
+
65
+ puts "Waiting for initial breakpoint..."
66
+
67
+ loop do
68
+ DRb.start_service(options[:ClientURI])
69
+
70
+ begin
71
+ service = DRbObject.new(nil, options[:ServerURI])
72
+
73
+ begin
74
+ timeout(10) { service.ping }
75
+ rescue Timeout::Error, DRb::DRbConnError
76
+ if options[:Verbose]
77
+ puts "",
78
+ " *** Breakpoint service didn't respond to ping request ***",
79
+ " This likely happened because of a misconfigured ACL (see the",
80
+ " documentation of Breakpoint.activate_drb, note that by default",
81
+ " you can only connect to a remote Breakpoint service via a SSH",
82
+ " tunnel), but might also be caused by an extremely slow connection.",
83
+ ""
84
+ end
85
+ raise
86
+ end
87
+
88
+ begin
89
+ service.register_eval_handler do |code|
90
+ result = eval(code, TOPLEVEL_BINDING)
91
+ if result
92
+ DRbObject.new(result)
93
+ else
94
+ result
95
+ end
96
+ end
97
+
98
+ service.register_collision_handler do
99
+ msg = [
100
+ " *** Breakpoint service collision ***",
101
+ " Another Breakpoint service tried to use the",
102
+ " port already occupied by this one. It will",
103
+ " keep waiting until this Breakpoint service",
104
+ " is shut down.",
105
+ " ",
106
+ " If you are using the Breakpoint library for",
107
+ " debugging a Rails or other CGI application",
108
+ " this likely means that this Breakpoint",
109
+ " session belongs to an earlier, outdated",
110
+ " request and should be shut down via 'exit'."
111
+ ].join("\n")
112
+
113
+ if RUBY_PLATFORM["win"] then
114
+ # This sucks. Sorry, I'm not doing this because
115
+ # I like funky message boxes -- I need to do this
116
+ # because on Windows I have no way of displaying
117
+ # my notification via puts() when gets() is still
118
+ # being performed on STDIN. I have not found a
119
+ # better solution.
120
+ begin
121
+ require 'tk'
122
+ root = TkRoot.new { withdraw }
123
+ Tk.messageBox('message' => msg, 'type' => 'ok')
124
+ root.destroy
125
+ rescue Exception
126
+ puts "", msg, ""
127
+ end
128
+ else
129
+ puts "", msg, ""
130
+ end
131
+ end
132
+
133
+ service.register_handler do |workspace, message|
134
+ puts message
135
+ IRB.start(nil, nil, workspace)
136
+ puts "", "Resumed execution. Waiting for next breakpoint...", ""
137
+ end
138
+
139
+ puts "Connection established. Waiting for breakpoint...", "" if options[:Verbose]
140
+
141
+ loop do
142
+ begin
143
+ service.ping
144
+ rescue DRb::DRbConnError => error
145
+ puts "Server exited. Closing connection..." if options[:Verbose]
146
+ break
147
+ end
148
+
149
+ sleep(0.5)
150
+ end
151
+ ensure
152
+ service.unregister_handler
153
+ end
154
+ rescue Exception => error
155
+ if options[:RetryDelay] > 0 then
156
+ puts "No connection to breakpoint service at #{options[:ServerURI]}:", " (#{error.inspect})" if options[:Verbose]
157
+ error.backtrace if $DEBUG
158
+
159
+ puts " Reconnecting in #{options[:RetryDelay]} seconds..." if options[:Verbose]
160
+
161
+ sleep options[:RetryDelay]
162
+ retry
163
+ else
164
+ raise
165
+ end
166
+ end
167
+ end
@@ -21,26 +21,57 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
 
24
+ require 'breakpoint'
25
+
24
26
  class Dispatcher
25
- DEFAULT_SESSION_OPTIONS = { "database_manager" => CGI::Session::PStore, "prefix" => "ruby_sess.", "session_path" => "/" }
27
+ def self.dispatch(cgi = CGI.new, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS)
28
+ Breakpoint.activate_drb("druby://localhost:#{BREAKPOINT_SERVER_PORT}", nil, !defined?(FastCGI)) if defined?(BREAKPOINT_SERVER_PORT)
26
29
 
27
- def self.dispatch(cgi = CGI.new, session_options = DEFAULT_SESSION_OPTIONS, error_page = nil)
28
30
  begin
29
31
  request = ActionController::CgiRequest.new(cgi, session_options)
30
32
  response = ActionController::CgiResponse.new(cgi)
33
+
34
+ controller_name, module_name = controller_name(request.parameters), module_name(request.parameters)
31
35
 
32
- controller_name = request.parameters["controller"].gsub(/[^_a-zA-Z0-9]/, "").untaint
36
+ require_dependency("application")
37
+ require_dependency(controller_path(controller_name, module_name))
33
38
 
34
- require "#{Inflector.underscore(controller_name)}_controller"
35
- Object.const_get("#{Inflector.camelize(controller_name)}Controller").process(request, response).out
36
- rescue Exception => e
37
- begin
38
- ActionController::Base.logger.info "\n\nException throw during dispatch: #{e.message}\n#{e.backtrace.join("\n")}"
39
- rescue Exception
40
- # Couldn't log error
39
+ controller_class(controller_name).process(request, response).out
40
+ rescue Object => exception
41
+ ActionController::Base.process_with_exception(request, response, exception).out
42
+ ensure
43
+ if ActionController::Base.reload_dependencies
44
+ Object.send(:remove_const, "ApplicationController") if Object.const_defined?(:ApplicationController)
45
+ Object.send(:remove_const, controller_class_name(controller_name)) if Object.const_defined?(controller_class_name(controller_name))
46
+ ActiveRecord::Base.reset_associations_loaded
47
+ ActiveRecord::Base.reset_column_information_and_inheritable_attributes_for_all_subclasses
41
48
  end
42
49
 
43
- if error_page then cgi.out{ IO.readlines(error_page) } else raise e end
50
+ Breakpoint.deactivate_drb if defined?(BREAKPOINT_SERVER_PORT)
51
+ end
52
+ end
53
+
54
+ def self.controller_path(controller_name, module_name = nil)
55
+ if module_name
56
+ "#{module_name}/#{Inflector.underscore(controller_name)}_controller"
57
+ else
58
+ "#{Inflector.underscore(controller_name)}_controller"
44
59
  end
45
60
  end
46
- end
61
+
62
+ def self.controller_class(controller_name)
63
+ Object.const_get(controller_class_name(controller_name))
64
+ end
65
+
66
+ def self.controller_class_name(controller_name)
67
+ "#{Inflector.camelize(controller_name)}Controller"
68
+ end
69
+
70
+ def self.controller_name(parameters)
71
+ parameters["controller"].gsub(/[^_a-zA-Z0-9]/, "").untaint
72
+ end
73
+
74
+ def self.module_name(parameters)
75
+ parameters["module"].gsub(/[^_a-zA-Z0-9]/, "").untaint if parameters["module"]
76
+ end
77
+ end
@@ -0,0 +1,175 @@
1
+ require 'fileutils'
2
+
3
+ module Rails
4
+ module Generator
5
+ class GeneratorError < StandardError; end
6
+ class UsageError < GeneratorError; end
7
+
8
+ CONTRIB_ROOT = "#{RAILS_ROOT}/script/generators"
9
+ BUILTIN_ROOT = "#{File.dirname(__FILE__)}/../generators"
10
+ DEFAULT_SEARCH_PATHS = [CONTRIB_ROOT, BUILTIN_ROOT]
11
+
12
+ class << self
13
+ def instance(name, args = [], search_paths = DEFAULT_SEARCH_PATHS)
14
+ # RAILS_ROOT constant must be set.
15
+ unless Object.const_get(:RAILS_ROOT)
16
+ raise GeneratorError, "RAILS_ROOT must be set. Did you require 'config/environment'?"
17
+ end
18
+
19
+ # Force canonical name.
20
+ name = Inflector.underscore(name.downcase)
21
+
22
+ # Search for filesystem path to requested generator.
23
+ unless path = find_generator_path(name, search_paths)
24
+ raise GeneratorError, "#{name} generator not found."
25
+ end
26
+
27
+ # Check for templates directory.
28
+ template_root = "#{path}/templates"
29
+ unless File.directory?(template_root)
30
+ raise GeneratorError, "missing template directory #{template_root}"
31
+ end
32
+
33
+ # Require class file according to naming convention.
34
+ require "#{path}/#{name}_generator.rb"
35
+
36
+ # Find class according to naming convention. Allow Nesting::In::Modules.
37
+ class_name = Inflector.classify("#{name}_generator")
38
+ unless klass = find_generator_class(name)
39
+ raise GeneratorError, "no #{class_name} class defined in #{path}/#{name}_generator.rb"
40
+ end
41
+
42
+ # Instantiate and return generator.
43
+ klass.new(template_root, RAILS_ROOT, search_paths, args)
44
+ end
45
+
46
+
47
+ def builtin_generators
48
+ generators([BUILTIN_ROOT])
49
+ end
50
+
51
+ def contrib_generators
52
+ generators([CONTRIB_ROOT])
53
+ end
54
+
55
+ def generators(search_paths)
56
+ generator_paths(search_paths).keys.uniq.sort
57
+ end
58
+
59
+ # Find all generator paths.
60
+ def generator_paths(search_paths)
61
+ @paths ||= {}
62
+ unless @paths[search_paths]
63
+ paths = Hash.new { |h,k| h[k] = [] }
64
+ search_paths.each do |path|
65
+ Dir["#{path}/[a-z]*"].each do |dir|
66
+ paths[File.basename(dir)] << dir if File.directory?(dir)
67
+ end
68
+ end
69
+ @paths[search_paths] = paths
70
+ end
71
+ @paths[search_paths]
72
+ end
73
+
74
+ def find_generator_path(name, search_paths)
75
+ generator_paths(search_paths)[name].first
76
+ end
77
+
78
+ # Find all generator classes.
79
+ def generator_classes
80
+ classes = Hash.new { |h,k| h[k] = [] }
81
+ class_re = /([^:]+)Generator$/
82
+ ObjectSpace.each_object(Class) do |object|
83
+ if md = class_re.match(object.name) and object < Rails::Generator::Base
84
+ classes[Inflector.underscore(md.captures.first)] << object
85
+ end
86
+ end
87
+ classes
88
+ end
89
+
90
+ def find_generator_class(name)
91
+ generator_classes[name].first
92
+ end
93
+ end
94
+
95
+
96
+ # Talk about generators.
97
+ class Base
98
+ attr_reader :template_root, :destination_root, :args, :options,
99
+ :class_name, :singular_name, :plural_name
100
+
101
+ alias_method :file_name, :singular_name
102
+ alias_method :table_name, :plural_name
103
+
104
+ def self.generator_name
105
+ Inflector.underscore(name.gsub('Generator', ''))
106
+ end
107
+
108
+ def initialize(template_root, destination_root, search_paths, args)
109
+ @template_root, @destination_root = template_root, destination_root
110
+ usage if args.empty?
111
+ @search_paths, @original_args = search_paths, args.dup
112
+ @class_name, @singular_name, @plural_name = inflect_names(args.shift)
113
+ @options = extract_options!(args)
114
+ @args = args
115
+ end
116
+
117
+ protected
118
+ # Look up another generator with the same arguments.
119
+ def generator(name)
120
+ Rails::Generator.instance(name, @original_args, @search_paths)
121
+ end
122
+
123
+ # Generate a file for a Rails application using an ERuby template.
124
+ # Looks up and evalutes a template by name and writes the result
125
+ # to a file relative to +destination_root+. The template
126
+ # is evaluated in the context of the optional eval_binding argument.
127
+ #
128
+ # The ERB template uses explicit trim mode to best control the
129
+ # proliferation of whitespace in generated code. <%- trims leading
130
+ # whitespace; -%> trims trailing whitespace including one newline.
131
+ def template(template_name, destination_path, eval_binding = nil)
132
+ # Determine full paths for source and destination files.
133
+ template_path = find_template_path(template_name)
134
+ destination_path = File.join(destination_root, destination_path)
135
+
136
+ # Create destination directories.
137
+ FileUtils.mkdir_p(File.dirname(destination_path))
138
+
139
+ # Render template and write result.
140
+ eval_binding ||= binding
141
+ contents = ERB.new(File.read(template_path), nil, '-').result(eval_binding)
142
+ File.open(destination_path, 'w') { |file| file.write(contents) }
143
+ end
144
+
145
+ def usage
146
+ raise UsageError.new, File.read(usage_path)
147
+ end
148
+
149
+ private
150
+ def find_template_path(template_name)
151
+ name, path = template_name.split('/', 2)
152
+ if path.nil?
153
+ File.join(template_root, name)
154
+ elsif generator_path = Rails::Generator.find_generator_path(name, @search_paths)
155
+ File.join(generator_path, 'templates', path)
156
+ end
157
+ end
158
+
159
+ def inflect_names(name)
160
+ camel = Inflector.camelize(Inflector.underscore(name))
161
+ under = Inflector.underscore(camel)
162
+ plural = Inflector.pluralize(under)
163
+ [camel, under, plural]
164
+ end
165
+
166
+ def extract_options!(args)
167
+ if args.last.is_a?(Hash) then args.pop else {} end
168
+ end
169
+
170
+ def usage_path
171
+ "#{template_root}/../USAGE"
172
+ end
173
+ end
174
+ end
175
+ end
@@ -7,6 +7,8 @@ require 'stringio'
7
7
  include WEBrick
8
8
 
9
9
  class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
10
+ REQUEST_MUTEX = Mutex.new
11
+
10
12
  def self.dispatch(options = {})
11
13
  Socket.do_not_reverse_lookup = true # patch for OS X
12
14
 
@@ -24,14 +26,19 @@ class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
24
26
  end
25
27
 
26
28
  def do_GET(req, res)
27
- unless handle_index(req, res)
28
- unless handle_dispatch(req, res)
29
- unless handle_file(req, res)
30
- unless handle_mapped(req, res)
31
- raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
29
+ begin
30
+ unless handle_index(req, res)
31
+ unless handle_dispatch(req, res)
32
+ unless handle_file(req, res)
33
+ REQUEST_MUTEX.lock
34
+ unless handle_mapped(req, res)
35
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
36
+ end
32
37
  end
33
38
  end
34
39
  end
40
+ ensure
41
+ REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked?
35
42
  end
36
43
  end
37
44
 
@@ -42,7 +49,7 @@ class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
42
49
  if @server_options[:index_controller]
43
50
  res.set_redirect WEBrick::HTTPStatus::MovedPermanently, "/#{@server_options[:index_controller]}/"
44
51
  else
45
- res.set_redirect WEBrick::HTTPStatus::MovedPermanently, "/_doc/index.html"
52
+ res.set_redirect WEBrick::HTTPStatus::MovedPermanently, "/_doc/"
46
53
  end
47
54
 
48
55
  return true
@@ -59,15 +66,13 @@ class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
59
66
  res.set_error(err)
60
67
  return true
61
68
  rescue => err
62
- p err
63
69
  return false
64
70
  end
65
71
  end
66
72
 
67
73
  def handle_mapped(req, res)
68
- parsed_ok, controller, action, id = DispatchServlet.parse_uri(req.request_uri.path)
69
- if parsed_ok
70
- query = "controller=#{controller}&action=#{action}&id=#{id}"
74
+ if mappings = DispatchServlet.parse_uri(req.request_uri.path)
75
+ query = mappings.collect { |pair| "#{pair.first}=#{pair.last}" }.join("&")
71
76
  query << "&#{req.request_uri.query}" if req.request_uri.query
72
77
  origin = req.request_uri.path + "?" + query
73
78
  req.request_uri.path = "/dispatch.rb"
@@ -86,43 +91,19 @@ class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
86
91
  env["REQUEST_URI"] = origin if origin
87
92
 
88
93
  data = nil
89
- if @server_options[:cache_classes]
90
- old_stdin, old_stdout = $stdin, $stdout
91
- $stdin, $stdout = StringIO.new(req.body || ""), StringIO.new
94
+ $old_stdin, $old_stdout = $stdin, $stdout
95
+ $stdin, $stdout = StringIO.new(req.body || ""), StringIO.new
92
96
 
93
- begin
94
- require 'cgi'
95
- CGI.send(:define_method, :env_table) { env }
97
+ begin
98
+ require 'cgi'
99
+ CGI.send(:define_method, :env_table) { env }
96
100
 
97
- load File.join(@server_options[:server_root], "dispatch.rb")
101
+ load File.join(@server_options[:server_root], "dispatch.rb")
98
102
 
99
- $stdout.rewind
100
- data = $stdout.read
101
- ensure
102
- $stdin, $stdout = old_stdin, old_stdout
103
- end
104
- else
105
- begin
106
- require 'rbconfig'
107
- ruby_interpreter = Config::CONFIG['ruby_install_name'] || 'ruby'
108
- rescue Object
109
- ruby_interpreter = 'ruby'
110
- end
111
-
112
- dispatch_rb_path = File.expand_path(File.join(@server_options[:server_root], "dispatch.rb"))
113
- IO.popen(ruby_interpreter, "rb+") do |ruby|
114
- ruby.puts <<-END
115
- require 'cgi'
116
- require 'stringio'
117
- env = #{env.inspect}
118
- CGI.send(:define_method, :env_table) { env }
119
- $stdin = StringIO.new(#{(req.body || "").inspect})
120
-
121
- eval "load '#{dispatch_rb_path}'", binding, #{dispatch_rb_path.inspect}
122
- END
123
- ruby.close_write
124
- data = ruby.read
125
- end
103
+ $stdout.rewind
104
+ data = $stdout.read
105
+ ensure
106
+ $stdin, $stdout = $old_stdin, $old_stdout
126
107
  end
127
108
 
128
109
  raw_header, body = *data.split(/^[\xd\xa]+/on, 2)
@@ -141,18 +122,33 @@ class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
141
122
  end
142
123
 
143
124
  def self.parse_uri(path)
144
- component = /([-_a-zA-Z0-9]+)/
125
+ component, id = /([-_a-zA-Z0-9]+)/, /([0-9]+)/
145
126
 
146
127
  case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/")
147
128
  when %r{^/#{component}/?$} then
148
- [true, $1, "index", nil]
149
- when %r{^/#{component}/#{component}/?$} then
150
- [true, $1, $2, nil]
151
- when %r{^/#{component}/#{component}/#{component}/?$} then
152
- [true, $1, $2, $3]
129
+ { :controller => $1, :action => "index" }
130
+ when %r{^/#{component}/#{component}$} then
131
+ { :controller => $1, :action => $2 }
132
+ when %r{^/#{component}/#{component}/#{id}$} then
133
+ { :controller => $1, :action => $2, :id => $3 }
134
+
135
+ when %r{^/#{component}/#{component}/$} then
136
+ { :module => $1, :controller => $2, :action => "index" }
137
+ when %r{^/#{component}/#{component}/#{component}$} then
138
+ if DispatchServlet.modules(component).include?($1)
139
+ { :module => $1, :controller => $2, :action => $3 }
140
+ else
141
+ { :controller => $1, :action => $2, :id => $3 }
142
+ end
143
+ when %r{^/#{component}/#{component}/#{component}/#{id}$} then
144
+ { :module => $1, :controller => $2, :action => $3, :id => $4 }
153
145
  else
154
- [false, nil, nil, nil]
146
+ false
155
147
  end
148
+ end
149
+
150
+ def self.modules(module_pattern = '[^.]+')
151
+ path = RAILS_ROOT + '/app/controllers'
152
+ Dir.entries(path).grep(/^#{module_pattern}$/).find_all {|e| File.directory?("#{path}/#{e}")}
156
153
  end
157
-
158
154
  end