usher 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/History.txt +3 -0
  2. data/Manifest.txt +35 -0
  3. data/README.rdoc +126 -0
  4. data/Rakefile +72 -0
  5. data/VERSION.yml +4 -0
  6. data/lib/usher/exceptions.rb +5 -0
  7. data/lib/usher/generate.rb +131 -0
  8. data/lib/usher/grapher.rb +65 -0
  9. data/lib/usher/interface/email_interface.rb +27 -0
  10. data/lib/usher/interface/merb_interface.rb +61 -0
  11. data/lib/usher/interface/rack_interface/mapper.rb +0 -0
  12. data/lib/usher/interface/rack_interface/route.rb +9 -0
  13. data/lib/usher/interface/rack_interface.rb +37 -0
  14. data/lib/usher/interface/rails2_2_interface/mapper.rb +44 -0
  15. data/lib/usher/interface/rails2_2_interface.rb +135 -0
  16. data/lib/usher/interface/rails2_3_interface.rb +135 -0
  17. data/lib/usher/interface.rb +27 -0
  18. data/lib/usher/node.rb +138 -0
  19. data/lib/usher/route/path.rb +24 -0
  20. data/lib/usher/route/request_method.rb +22 -0
  21. data/lib/usher/route/variable.rb +37 -0
  22. data/lib/usher/route.rb +58 -0
  23. data/lib/usher/splitter.rb +159 -0
  24. data/lib/usher.rb +184 -0
  25. data/rails/init.rb +8 -0
  26. data/spec/private/email/recognize_spec.rb +38 -0
  27. data/spec/private/generate_spec.rb +141 -0
  28. data/spec/private/grapher_spec.rb +41 -0
  29. data/spec/private/path_spec.rb +68 -0
  30. data/spec/private/rack/dispatch_spec.rb +29 -0
  31. data/spec/private/rails2_2/compat.rb +1 -0
  32. data/spec/private/rails2_2/generate_spec.rb +28 -0
  33. data/spec/private/rails2_2/path_spec.rb +16 -0
  34. data/spec/private/rails2_2/recognize_spec.rb +79 -0
  35. data/spec/private/rails2_3/compat.rb +1 -0
  36. data/spec/private/rails2_3/generate_spec.rb +28 -0
  37. data/spec/private/rails2_3/path_spec.rb +16 -0
  38. data/spec/private/rails2_3/recognize_spec.rb +79 -0
  39. data/spec/private/recognize_spec.rb +178 -0
  40. data/spec/private/request_method_spec.rb +15 -0
  41. data/spec/private/split_spec.rb +76 -0
  42. data/spec/spec.opts +7 -0
  43. metadata +120 -0
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ == 0.0.1
2
+
3
+ Initial release as gem
data/Manifest.txt ADDED
@@ -0,0 +1,35 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/compat.rb
6
+ lib/usher.rb
7
+ lib/usher/exceptions.rb
8
+ lib/usher/grapher.rb
9
+ lib/usher/interface.rb
10
+ lib/usher/interface/merb_interface.rb
11
+ lib/usher/interface/rack_interface.rb
12
+ lib/usher/interface/rack_interface/mapper.rb
13
+ lib/usher/interface/rack_interface/route.rb
14
+ lib/usher/interface/rails2_interface.rb
15
+ lib/usher/interface/rails2_interface/mapper.rb
16
+ lib/usher/node.rb
17
+ lib/usher/node/lookup.rb
18
+ lib/usher/route.rb
19
+ lib/usher/route/http.rb
20
+ lib/usher/route/path.rb
21
+ lib/usher/route/separator.rb
22
+ lib/usher/route/splitter.rb
23
+ lib/usher/route/variable.rb
24
+ rails/init.rb
25
+ spec/generate_spec.rb
26
+ spec/node/lookup_spec.rb
27
+ spec/path_spec.rb
28
+ spec/rack/dispatch_spec.rb
29
+ spec/rails/generate_spec.rb
30
+ spec/rails/path_spec.rb
31
+ spec/rails/recognize_spec.rb
32
+ spec/recognize_spec.rb
33
+ spec/spec.opts
34
+ spec/split_spec.rb
35
+ usher.gemspec
data/README.rdoc ADDED
@@ -0,0 +1,126 @@
1
+ = Usher
2
+
3
+ Tree-based router library. Useful for (specifically) for Rails and Rack, but probably generally useful for anyone interested in doing routing. Based on Ilya Grigorik suggestion, turns out looking up in a hash and following a tree is faster than Krauter's massive regex approach.
4
+
5
+ == Features
6
+
7
+ * Understands single and path-globbing variables
8
+ * Arbitrary HTTP header requirements
9
+ * No optimization phase, so routes are always alterable after the fact
10
+ * Understands Proc and Regex transformations, validations
11
+ * Really, really fast
12
+ * Relatively light and happy code-base, should be easy and fun to alter
13
+ * Interface and implementation are separate, encouraging cross-pollination
14
+
15
+ == Route format
16
+
17
+ From the rdoc:
18
+
19
+ Creates a route from +path+ and +options+
20
+
21
+ === +path+
22
+ A path consists a mix of dynamic and static parts delimited by <tt>/</tt>
23
+
24
+ ==== Dynamic
25
+ Dynamic parts are prefixed with either :, *. :variable matches only one part of the path, whereas *variable can match one or
26
+ more parts.
27
+
28
+ <b>Example:</b>
29
+ <tt>/path/:variable/path</tt> would match
30
+
31
+ * <tt>/path/test/path</tt>
32
+ * <tt>/path/something_else/path</tt>
33
+ * <tt>/path/one_more/path</tt>
34
+
35
+ In the above examples, 'test', 'something_else' and 'one_more' respectively would be bound to the key <tt>:variable</tt>.
36
+ However, <tt>/path/test/one_more/path</tt> would not be matched.
37
+
38
+ <b>Example:</b>
39
+ <tt>/path/*variable/path</tt> would match
40
+
41
+ * <tt>/path/one/two/three/path</tt>
42
+ * <tt>/path/four/five/path</tt>
43
+
44
+ In the above examples, ['one', 'two', 'three'] and ['four', 'five'] respectively would be bound to the key :variable.
45
+
46
+ As well, variables can have a regex matcher.
47
+
48
+ <b>Example:</b>
49
+ <tt>/product/{:id,\d+}</tt> would match
50
+
51
+ * <tt>/product/123</tt>
52
+ * <tt>/product/4521</tt>
53
+
54
+ But not
55
+ * <tt>/product/AE-35</tt>
56
+
57
+ As well, the same logic applies for * variables as well, where only parts matchable by the supplied regex will
58
+ actually be bound to the variable
59
+
60
+ ==== Static
61
+
62
+ Static parts of literal character sequences. For instance, <tt>/path/something.html</tt> would match only the same path.
63
+ As well, static parts can have a regex pattern in them as well, such as <tt>/path/something.{html|xml}</tt> which would match only
64
+ <tt>/path/something.html</tt> and <tt>/path/something.xml</tt>
65
+
66
+ ==== Optional sections
67
+
68
+ Sections of a route can be marked as optional by surrounding it with brackets. For instance, in the above static example, <tt>/path/something(.html)</tt> would match both <tt>/path/something</tt> and <tt>/path/something.html</tt>.
69
+
70
+ ==== One and only one sections
71
+
72
+ Sections of a route can be marked as "one and only one" by surrounding it with brackets and separating parts of the route with pipes.
73
+ For instance, the path, <tt>/path/something(.xml|.html)</tt> would only match <tt>/path/something.xml</tt> and
74
+ <tt>/path/something.html</tt>. Generally its more efficent to use one and only sections over using regex.
75
+
76
+ === +options+
77
+ * +requirements+ - After transformation, tests the condition using ===. If it returns false, it raises an <tt>Usher::ValidationException</tt>
78
+ * +conditions+ - Accepts any of the +request_methods+ specificied in the construction of Usher. This can be either a <tt>string</tt> or a regular expression.
79
+ * Any other key is interpreted as a requirement for the variable of its name.
80
+
81
+ == Rails
82
+
83
+ script/plugin install git://github.com/joshbuddy/usher.git
84
+
85
+ == Rack
86
+
87
+ === config.ru
88
+
89
+ require 'usher'
90
+ app = proc do |env|
91
+ body = "Hi there #{env['usher.params'][:name]}"
92
+ [
93
+ 200, # Status code
94
+ { # Response headers
95
+ 'Content-Type' => 'text/plain',
96
+ 'Content-Length' => body.size.to_s,
97
+ },
98
+ [body] # Response body
99
+ ]
100
+ end
101
+
102
+ routes = Usher::Interface.for(:rack) do
103
+ add('/hello/:name').to(app)
104
+ end
105
+
106
+ run routes
107
+
108
+ ------------
109
+
110
+ >> curl http://127.0.0.1:3000/hello/samueltanders
111
+ << Hi there samueltanders
112
+
113
+ == DONE
114
+
115
+ * add support for () optional parts
116
+ * Add support for arbitrary HTTP header checks
117
+ * Emit exceptions inline with relevant interfaces
118
+ * More RDoc! (optionally cowbell)
119
+
120
+ == TODO
121
+
122
+ * Make it integrate with merb
123
+ * Make it integrate with rails3
124
+ * Create decent DSL for use with rack
125
+
126
+ (Let me show you to your request)
data/Rakefile ADDED
@@ -0,0 +1,72 @@
1
+ require 'lib/usher'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "usher"
7
+ s.description = s.summary = "A general purpose routing library"
8
+ s.email = "joshbuddy@gmail.com"
9
+ s.homepage = "http://github.com/joshbuddy/usher"
10
+ s.authors = ["Joshua Hull"]
11
+ s.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"]
12
+ s.add_dependency 'fuzzyhash', '>=0.0.3'
13
+ s.rubyforge_project = 'joshbuddy-usher' # This line would be new
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
17
+ end
18
+
19
+ require 'spec'
20
+ require 'spec/rake/spectask'
21
+ task :spec => 'spec:all'
22
+ namespace(:spec) do
23
+ Spec::Rake::SpecTask.new(:all) do |t|
24
+ t.spec_opts ||= []
25
+ t.spec_opts << "-rubygems"
26
+ t.spec_opts << "--options" << "spec/spec.opts"
27
+ t.spec_files = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ end
31
+
32
+ desc "Run all examples with RCov"
33
+ Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
34
+ t.spec_files = FileList['spec/**/*.rb']
35
+ t.rcov = true
36
+ t.rcov_opts = ['--exclude', 'spec']
37
+ end
38
+
39
+ require 'rake/rdoctask'
40
+ desc "Generate documentation"
41
+ Rake::RDocTask.new do |rd|
42
+ rd.main = "README.rdoc"
43
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
44
+ rd.rdoc_dir = 'rdoc'
45
+ end
46
+
47
+ # These are new tasks
48
+ begin
49
+ require 'rake/contrib/sshpublisher'
50
+ namespace :rubyforge do
51
+
52
+ desc "Release gem and RDoc documentation to RubyForge"
53
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
54
+
55
+ namespace :release do
56
+ desc "Publish RDoc to RubyForge."
57
+ task :docs => [:rdoc] do
58
+ config = YAML.load(
59
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
60
+ )
61
+
62
+ host = "#{config['username']}@rubyforge.org"
63
+ remote_dir = "/var/www/gforge-projects/joshbuddy-usher/"
64
+ local_dir = 'rdoc'
65
+
66
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
67
+ end
68
+ end
69
+ end
70
+ rescue LoadError
71
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
72
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 4
3
+ :patch: 8
4
+ :major: 0
@@ -0,0 +1,5 @@
1
+ class Usher
2
+ class UnrecognizedException < RuntimeError; end
3
+ class ValidationException < RuntimeError; end
4
+ class MissingParameterException < RuntimeError; end
5
+ end
@@ -0,0 +1,131 @@
1
+ require 'rack'
2
+
3
+ unless Rack::Utils.respond_to?(:uri_escape)
4
+ module Rack
5
+
6
+ module Utils
7
+
8
+ def uri_escape(s)
9
+ s.to_s.gsub(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) {
10
+ '%'<<$1.unpack('H2'*$1.size).join('%').upcase
11
+ }.tr(' ', '+')
12
+ end
13
+ module_function :uri_escape
14
+
15
+ def uri_unescape(s)
16
+ gsub(/((?:%[0-9a-fA-F]{2})+)/n){
17
+ [$1.delete('%')].pack('H*')
18
+ }
19
+ end
20
+ module_function :uri_unescape
21
+
22
+ end
23
+ end
24
+ end
25
+
26
+ class Usher
27
+ class Generators
28
+
29
+ class URL
30
+
31
+ def initialize(usher)
32
+ @usher = usher
33
+ end
34
+
35
+ def generate_full(routing_lookup, request, params = nil)
36
+ path = path_for_routing_lookup(routing_lookup, params)
37
+ result = generate_start(path, request)
38
+ result << generate_path(path, params)
39
+ end
40
+
41
+ def generate(routing_lookup, params = nil)
42
+ generate_path(path_for_routing_lookup(routing_lookup, params), params)
43
+ end
44
+
45
+ def generate_start(path, request)
46
+ result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup
47
+ result << '://'
48
+ result << (path.route.generate_with && path.route.generate_with.host) ? path.route.generate_with.host : request.host
49
+ port = path.route.generate_with && path.route.generate_with.port || request.port
50
+ if result[4] == ?s
51
+ result << ':' << port.to_s if port != 443
52
+ else
53
+ result << ':' << port.to_s if port != 80
54
+ end
55
+ result
56
+ end
57
+
58
+ def path_for_routing_lookup(routing_lookup, params)
59
+ path = case routing_lookup
60
+ when Symbol
61
+ route = @usher.named_routes[routing_lookup]
62
+ params.is_a?(Hash) ? route.find_matching_path(params) : route.paths.first
63
+ when Route
64
+ params.is_a?(Hash) ? routing_lookup.find_matching_path(params) : routing_lookup.paths.first
65
+ when nil
66
+ params.is_a?(Hash) ? @usher.path_for_options(params) : raise
67
+ when Route::Path
68
+ routing_lookup
69
+ end
70
+ end
71
+
72
+ # Generates a completed URL based on a +route+ or set of optional +params+
73
+ #
74
+ # set = Usher.new
75
+ # route = set.add_named_route(:test_route, '/:controller/:action')
76
+ # set.generate_url(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true
77
+ # set.generate_url(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true
78
+ # set.generate_url(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true
79
+ def generate_path(path, params = nil)
80
+ raise UnrecognizedException.new unless path
81
+
82
+ params = Array(params) if params.is_a?(String)
83
+ if params.is_a?(Array)
84
+ given_size = params.size
85
+ extra_params = params.last.is_a?(Hash) ? params.pop : nil
86
+ params = Hash[*path.dynamic_parts.inject([]){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new("got #{given_size}, expected #{path.dynamic_parts.size} parameters"))]); a}]
87
+ params.merge!(extra_params) if extra_params
88
+ end
89
+
90
+ result = ''
91
+ path.parts.each do |part|
92
+ case part
93
+ when Route::Variable
94
+ value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new)
95
+ case part.type
96
+ when :*
97
+ value.each_with_index do |current_value, index|
98
+ current_value = current_value.to_s unless current_value.is_a?(String)
99
+ part.valid!(current_value)
100
+ result << current_value
101
+ result << '/' if index != value.size - 1
102
+ end
103
+ when :':'
104
+ value = value.to_s unless value.is_a?(String)
105
+ part.valid!(value)
106
+ result << value
107
+ end
108
+ else
109
+ result << part
110
+ end
111
+ end
112
+ result = Rack::Utils.uri_escape(result)
113
+
114
+ if params && !params.empty?
115
+ has_query = result[??]
116
+ params.each do |k,v|
117
+ case v
118
+ when Array
119
+ v.each do |v_part|
120
+ result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s)
121
+ end
122
+ else
123
+ result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s)
124
+ end
125
+ end
126
+ end
127
+ result
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,65 @@
1
+ class Usher
2
+ class Grapher
3
+
4
+ def initialize
5
+ reset!
6
+ end
7
+
8
+ def reset!
9
+ @significant_keys = nil
10
+ @orders = Hash.new{|h,k| h[k] = Hash.new{|h2, k2| h2[k2] = []}}
11
+ @key_count = Hash.new(0)
12
+ @cache = {}
13
+ end
14
+
15
+ def add_route(route)
16
+ route.paths.each do |path|
17
+ unless path.dynamic_keys.size.zero?
18
+ path.dynamic_keys.each do |k|
19
+ @orders[path.dynamic_keys.size][k] << path
20
+ @key_count[k] += 1
21
+ end
22
+
23
+ dynamic_parts_with_defaults = path.dynamic_parts.select{|part| part.default_value }.map{|dp| dp.name}
24
+ dynamic_parts_without_defaults = path.dynamic_parts.select{|part| !part.default_value }.map{|dp| dp.name}
25
+
26
+ (1...(2 ** (dynamic_parts_with_defaults.size))).each do |i|
27
+ current_set = dynamic_parts_without_defaults.dup
28
+ dynamic_parts_with_defaults.each_with_index do |dp, index|
29
+ current_set << dp unless (index & i) == 0
30
+ end
31
+
32
+ current_set.each do |k|
33
+ @orders[current_set.size][k] << path
34
+ @key_count[k] += 1
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def significant_keys
42
+ @significant_keys ||= @key_count.keys.uniq
43
+ end
44
+
45
+ def find_matching_path(params)
46
+ unless params.empty?
47
+ set = params.keys & significant_keys
48
+ if cached = @cache[set]
49
+ return cached
50
+ end
51
+ set.size.downto(1) do |o|
52
+ set.each do |k|
53
+ @orders[o][k].each { |r|
54
+ if r.can_generate_from?(set)
55
+ @cache[set] = r
56
+ return r
57
+ end
58
+ }
59
+ end
60
+ end
61
+ nil
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,27 @@
1
+ class Usher
2
+ module Interface
3
+ class EmailInterface
4
+
5
+ def initialize(&blk)
6
+ @routes = Usher.new(:delimiters => ['@', '-', '.'], :valid_regex => '[\+a-zA-Z0-9]+', :globs_capture_separators => true)
7
+ instance_eval(&blk) if blk
8
+ end
9
+
10
+ def for(path, &block)
11
+ @routes.add_route(path).to(block)
12
+ end
13
+
14
+ def reset!
15
+ @routes.reset!
16
+ end
17
+
18
+ def act(email)
19
+ response = @routes.recognize(email, email)
20
+ if response.path
21
+ response.path.route.destination.call(response.params.inject({}){|h,(k,v)| h[k]=v.to_s; h })
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ require 'merb-core'
2
+ require 'merb-core/dispatch/router/behavior'
3
+
4
+ class Usher
5
+ module Interface
6
+ class MerbInterface
7
+
8
+ # merb does everything with class methods.
9
+
10
+ @root_behavior = ::Merb::Router::Behavior.new.defaults(:action => "index")
11
+
12
+ class << self
13
+ attr_accessor :root_behavior
14
+
15
+ UsherRoutes = Usher.new
16
+
17
+ def prepare(first = [], last = [], &block)
18
+ @routes = []
19
+ root_behavior._with_proxy(&block)
20
+ @routes = first + @routes + last
21
+ compile
22
+ self
23
+ end
24
+
25
+ def compile
26
+ routes.each do |r|
27
+ r.segments
28
+ end
29
+
30
+ #puts r.inspect; UsherRoutes.add_route(r) }
31
+ #routes.each {|r| }
32
+ end
33
+
34
+ def named_routes
35
+ UsherRoutes.named_routes
36
+ end
37
+
38
+ def routes
39
+ UsherRoutes.routes
40
+ end
41
+
42
+ def route_for(request)
43
+ p request
44
+ p UsherRoutes.tree
45
+ UsherRoutes.recognize(request)
46
+ end
47
+
48
+ end
49
+
50
+ #class BootLoader < ::Merb::BootLoader
51
+ #end
52
+
53
+ def load_into_merb!
54
+ ::Merb.send(:remove_const, "Router")
55
+ ::Merb.const_set("Router", Usher::Interface::MerbInterface)
56
+ #::Merb::BootLoader.const_set("Router", Usher::Interface::Merb::BootLoader)
57
+ end
58
+
59
+ end
60
+ end
61
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ class Usher
2
+ module Interface
3
+ class RackInterface
4
+ module Route
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ require 'rack'
2
+
3
+ class Usher
4
+ module Interface
5
+ class RackInterface
6
+
7
+ attr_accessor :routes
8
+
9
+ def initialize(&blk)
10
+ @routes = Usher.new(:request_methods => [:method, :host, :port, :scheme])
11
+ @generator = Usher::Generators::URL.new(@routes)
12
+ instance_eval(&blk) if blk
13
+ end
14
+
15
+ def add(path, options = nil)
16
+ @routes.add_route(path, options)
17
+ end
18
+
19
+ def reset!
20
+ @routes.reset!
21
+ end
22
+
23
+ def call(env)
24
+ response = @routes.recognize(Rack::Request.new(env))
25
+ params = {}
26
+ response.params.each{ |hk| params[hk.first] = hk.last}
27
+ env['usher.params'] = params
28
+ response.path.route.destination.call(env)
29
+ end
30
+
31
+ def generate(route, params = nil, options = nil)
32
+ @generator.generate(route, params, options)
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ class Usher
2
+ module Interface
3
+ class Rails2_2Interface
4
+
5
+ class Mapper #:doc:
6
+ def initialize(set) #:nodoc:
7
+ @set = set
8
+ end
9
+
10
+ def connect(path, options = nil)
11
+ @set.add_route(path, options)
12
+ end
13
+
14
+ def root(options = {})
15
+ if options.is_a?(Symbol)
16
+ if source_route = @set.named_routes[options]
17
+ options = source_route.conditions.blank? ?
18
+ source_route.options.merge({ :conditions => source_route.conditions }) : source_route.options
19
+ end
20
+ end
21
+ named_route(:root, '/', options)
22
+ end
23
+
24
+ def named_route(name, path, options = nil)
25
+ @set.add_named_route(name, path, options)
26
+ end
27
+
28
+ def namespace(name, options = {}, &block)
29
+ if options[:namespace]
30
+ with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
31
+ else
32
+ with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
33
+ end
34
+ end
35
+
36
+ def method_missing(route_name, *args, &proc) #:nodoc:
37
+ super unless args.length >= 1 && proc.nil?
38
+ @set.add_named_route(route_name, *args)
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end