roda 1.1.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG +70 -0
- data/README.rdoc +261 -302
- data/Rakefile +1 -1
- data/doc/release_notes/1.2.0.txt +406 -0
- data/lib/roda.rb +206 -124
- data/lib/roda/plugins/all_verbs.rb +11 -10
- data/lib/roda/plugins/assets.rb +5 -5
- data/lib/roda/plugins/backtracking_array.rb +12 -5
- data/lib/roda/plugins/caching.rb +10 -8
- data/lib/roda/plugins/class_level_routing.rb +94 -0
- data/lib/roda/plugins/content_for.rb +6 -0
- data/lib/roda/plugins/default_headers.rb +4 -11
- data/lib/roda/plugins/delay_build.rb +42 -0
- data/lib/roda/plugins/delegate.rb +64 -0
- data/lib/roda/plugins/drop_body.rb +33 -0
- data/lib/roda/plugins/empty_root.rb +48 -0
- data/lib/roda/plugins/environments.rb +68 -0
- data/lib/roda/plugins/error_email.rb +1 -2
- data/lib/roda/plugins/error_handler.rb +1 -1
- data/lib/roda/plugins/halt.rb +7 -5
- data/lib/roda/plugins/head.rb +4 -2
- data/lib/roda/plugins/header_matchers.rb +17 -9
- data/lib/roda/plugins/hooks.rb +16 -32
- data/lib/roda/plugins/json.rb +4 -10
- data/lib/roda/plugins/mailer.rb +233 -0
- data/lib/roda/plugins/match_affix.rb +48 -0
- data/lib/roda/plugins/multi_route.rb +9 -11
- data/lib/roda/plugins/multi_run.rb +81 -0
- data/lib/roda/plugins/named_templates.rb +93 -0
- data/lib/roda/plugins/not_allowed.rb +43 -48
- data/lib/roda/plugins/path.rb +63 -2
- data/lib/roda/plugins/render.rb +79 -48
- data/lib/roda/plugins/render_each.rb +6 -0
- data/lib/roda/plugins/sinatra_helpers.rb +523 -0
- data/lib/roda/plugins/slash_path_empty.rb +25 -0
- data/lib/roda/plugins/static_path_info.rb +64 -0
- data/lib/roda/plugins/streaming.rb +1 -1
- data/lib/roda/plugins/view_subdirs.rb +12 -8
- data/lib/roda/version.rb +1 -1
- data/spec/integration_spec.rb +33 -0
- data/spec/plugin/backtracking_array_spec.rb +24 -18
- data/spec/plugin/class_level_routing_spec.rb +138 -0
- data/spec/plugin/delay_build_spec.rb +23 -0
- data/spec/plugin/delegate_spec.rb +20 -0
- data/spec/plugin/drop_body_spec.rb +20 -0
- data/spec/plugin/empty_root_spec.rb +14 -0
- data/spec/plugin/environments_spec.rb +31 -0
- data/spec/plugin/h_spec.rb +1 -3
- data/spec/plugin/header_matchers_spec.rb +14 -0
- data/spec/plugin/hooks_spec.rb +3 -5
- data/spec/plugin/mailer_spec.rb +191 -0
- data/spec/plugin/match_affix_spec.rb +22 -0
- data/spec/plugin/multi_run_spec.rb +31 -0
- data/spec/plugin/named_templates_spec.rb +65 -0
- data/spec/plugin/path_spec.rb +66 -2
- data/spec/plugin/render_spec.rb +46 -1
- data/spec/plugin/sinatra_helpers_spec.rb +534 -0
- data/spec/plugin/slash_path_empty_spec.rb +22 -0
- data/spec/plugin/static_path_info_spec.rb +50 -0
- data/spec/request_spec.rb +23 -0
- data/spec/response_spec.rb +12 -1
- metadata +48 -6
@@ -0,0 +1,48 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The match_affix plugin allows changing the default prefix and suffix used for
|
4
|
+
# match patterns. Roda's default behavior for a match pattern like <tt>"albums"</tt>
|
5
|
+
# is to use the pattern <tt>/\A\/(?:albums)(?=\/|\z)/</tt>. This prefixes the pattern
|
6
|
+
# with +/+ and suffixes it with <tt>(?=\/|\z)</tt>. With the match_affix plugin, you
|
7
|
+
# can change the prefix and suffix to use. So if you want to be explicit and require
|
8
|
+
# a leading +/+ in patterns, you can set the prefix to <tt>""</tt>. If you want to
|
9
|
+
# consume a trailing slash instead of leaving it, you can set the suffix to <tt>(\/|\z)</tt>.
|
10
|
+
#
|
11
|
+
# You set the prefix and suffix to use by passing arguments when loading the plugin:
|
12
|
+
#
|
13
|
+
# plugin :match_affix, ""
|
14
|
+
#
|
15
|
+
# will load the plugin and use an empty prefix.
|
16
|
+
#
|
17
|
+
# plugin :match_affix, "", /(\/|\z)/
|
18
|
+
#
|
19
|
+
# will use an empty prefix and change the suffix to consume a trailing slash.
|
20
|
+
#
|
21
|
+
# plugin :match_affix, nil, /(\/|\z)/
|
22
|
+
#
|
23
|
+
# will not modify the prefix and will change the suffix to consume a trailing slash.
|
24
|
+
module MatchAffix
|
25
|
+
PREFIX = "/".freeze
|
26
|
+
SUFFIX = "(?=\/|\z)".freeze
|
27
|
+
|
28
|
+
# Set the default prefix and suffix to use in match patterns, if a non-nil value
|
29
|
+
# is given.
|
30
|
+
def self.configure(app, prefix, suffix=nil)
|
31
|
+
app.opts[:match_prefix] = prefix if prefix
|
32
|
+
app.opts[:match_suffix] = suffix if suffix
|
33
|
+
end
|
34
|
+
|
35
|
+
module RequestClassMethods
|
36
|
+
private
|
37
|
+
|
38
|
+
# Use the match prefix and suffix provided when loading the plugin, or fallback
|
39
|
+
# to Roda's default prefix/suffix if one was not provided.
|
40
|
+
def consume_pattern(pattern)
|
41
|
+
/\A#{roda_class.opts[:match_prefix] || PREFIX}(?:#{pattern})#{roda_class.opts[:match_suffix] || SUFFIX}/
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
register_plugin(:match_affix, MatchAffix)
|
47
|
+
end
|
48
|
+
end
|
@@ -125,29 +125,27 @@ class Roda
|
|
125
125
|
module MultiRoute
|
126
126
|
# Initialize storage for the named routes.
|
127
127
|
def self.configure(app)
|
128
|
-
app.
|
129
|
-
|
130
|
-
app::RodaRequest.instance_variable_set(:@namespaced_route_regexps, {})
|
131
|
-
end
|
128
|
+
app.opts[:namespaced_routes] ||= {}
|
129
|
+
app::RodaRequest.instance_variable_set(:@namespaced_route_regexps, {})
|
132
130
|
end
|
133
131
|
|
134
132
|
module ClassMethods
|
135
133
|
# Copy the named routes into the subclass when inheriting.
|
136
134
|
def inherited(subclass)
|
137
135
|
super
|
138
|
-
nsr = subclass.
|
139
|
-
|
136
|
+
nsr = subclass.opts[:namespaced_routes]
|
137
|
+
opts[:namespaced_routes].each{|k, v| nsr[k] = v.dup}
|
140
138
|
subclass::RodaRequest.instance_variable_set(:@namespaced_route_regexps, {})
|
141
139
|
end
|
142
140
|
|
143
141
|
# The names for the currently stored named routes
|
144
142
|
def named_routes(namespace=nil)
|
145
|
-
|
143
|
+
opts[:namespaced_routes][namespace].keys
|
146
144
|
end
|
147
145
|
|
148
146
|
# Return the named route with the given name.
|
149
147
|
def named_route(name, namespace=nil)
|
150
|
-
|
148
|
+
opts[:namespaced_routes][namespace][name]
|
151
149
|
end
|
152
150
|
|
153
151
|
# If the given route has a name, treat it as a named route and
|
@@ -155,8 +153,8 @@ class Roda
|
|
155
153
|
# call super.
|
156
154
|
def route(name=nil, namespace=nil, &block)
|
157
155
|
if name
|
158
|
-
|
159
|
-
|
156
|
+
opts[:namespaced_routes][namespace] ||= {}
|
157
|
+
opts[:namespaced_routes][namespace][name] = block
|
160
158
|
self::RodaRequest.clear_named_route_regexp!(namespace)
|
161
159
|
else
|
162
160
|
super(&block)
|
@@ -199,7 +197,7 @@ class Roda
|
|
199
197
|
|
200
198
|
# Dispatch to the named route with the given name.
|
201
199
|
def route(name, namespace=nil)
|
202
|
-
scope.instance_exec(self, &
|
200
|
+
scope.instance_exec(self, &roda_class.named_route(name, namespace))
|
203
201
|
end
|
204
202
|
end
|
205
203
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The multi_run plugin provides the ability to easily dispatch to other
|
4
|
+
# rack applications based on the request path prefix.
|
5
|
+
# First, load the plugin:
|
6
|
+
#
|
7
|
+
# class App < Roda
|
8
|
+
# plugin :multi_run
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# Then, other rack applications can register with the multi_run plugin:
|
12
|
+
#
|
13
|
+
# App.run "ra", PlainRackApp
|
14
|
+
# App.run "ro", OtherRodaApp
|
15
|
+
# App.run "si", SinatraApp
|
16
|
+
#
|
17
|
+
# Inside your route block, you can call r.multi_run to dispatch to all
|
18
|
+
# three rack applications based on the prefix:
|
19
|
+
#
|
20
|
+
# App.route do |r|
|
21
|
+
# r.multi_run
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# This will dispatch routes starting with +/ra+ to +PlainRackApp+, routes
|
25
|
+
# starting with +/ro+ to +OtherRodaApp+, and routes starting with +/si+ to
|
26
|
+
# SinatraApp.
|
27
|
+
#
|
28
|
+
# The multi_run plugin is similar to the multi_route plugin, with the difference
|
29
|
+
# being the multi_route plugin keeps all routing subtrees in the same Roda app/class,
|
30
|
+
# while multi_run dispatches to other rack apps. If you want to isolate your routing
|
31
|
+
# subtrees, multi_run is a better approach, but it does not let you set instance
|
32
|
+
# variables in the main Roda app and have those instance variables usable in
|
33
|
+
# the routing subtrees.
|
34
|
+
module MultiRun
|
35
|
+
# Initialize the storage for the dispatched applications
|
36
|
+
def self.configure(app)
|
37
|
+
app.instance_eval do
|
38
|
+
@multi_run_apps ||= {}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
# Hash storing rack applications to dispatch to, keyed by the prefix
|
44
|
+
# for the application.
|
45
|
+
attr_reader :multi_run_apps
|
46
|
+
|
47
|
+
# Add a rack application to dispatch to for the given prefix when
|
48
|
+
# r.multi_run is called.
|
49
|
+
def run(prefix, app)
|
50
|
+
@multi_run_apps[prefix.to_s] = app
|
51
|
+
self::RodaRequest.refresh_multi_run_regexp!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module RequestClassMethods
|
56
|
+
# Refresh the multi_run_regexp, using the stored route prefixes,
|
57
|
+
# preferring longer routes before shorter routes.
|
58
|
+
def refresh_multi_run_regexp!
|
59
|
+
@multi_run_regexp = /(#{Regexp.union(roda_class.multi_run_apps.keys.sort.reverse)})/
|
60
|
+
end
|
61
|
+
|
62
|
+
# Refresh the multi_run_regexp if it hasn't been loaded yet.
|
63
|
+
def multi_run_regexp
|
64
|
+
@multi_run_regexp || refresh_multi_run_regexp!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module RequestMethods
|
69
|
+
# If one of the stored route prefixes match the current request,
|
70
|
+
# dispatch the request to the stored rack application.
|
71
|
+
def multi_run
|
72
|
+
on self.class.multi_run_regexp do |prefix|
|
73
|
+
run scope.class.multi_run_apps[prefix]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
register_plugin(:multi_run, MultiRun)
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The named_templates plugin allows you to specify templates by name,
|
4
|
+
# providing the template code to use for a given name:
|
5
|
+
#
|
6
|
+
# plugin :named_templates
|
7
|
+
#
|
8
|
+
# template :layout do
|
9
|
+
# "<html><body><%= yield %></body></html>"
|
10
|
+
# end
|
11
|
+
# template :index do
|
12
|
+
# "<p>Hello <%= @user %>!</p>"
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# route do |r|
|
16
|
+
# @user = 'You'
|
17
|
+
# render(:index)
|
18
|
+
# end
|
19
|
+
# # => "<html><body><p>Hello You!</p></body></html>"
|
20
|
+
#
|
21
|
+
# You can provide options for the template, for example to change the
|
22
|
+
# engine that the template uses:
|
23
|
+
#
|
24
|
+
# template :index, :engine=>:str do
|
25
|
+
# "<p>Hello #{@user}!</p>"
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# The block you use is reevaluted on every call, allowing you to easily
|
29
|
+
# include additional setup logic:
|
30
|
+
#
|
31
|
+
# template :index do
|
32
|
+
# greeting = ['hello', 'hi', 'howdy'].sample
|
33
|
+
# @user.downcase!
|
34
|
+
# "<p>#{greating} <%= @user %>!</p>"
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# This plugin also works with the view_subdirs plugin, as long as you
|
38
|
+
# prefix the template name with the view subdirectory:
|
39
|
+
#
|
40
|
+
# template "main/index" do
|
41
|
+
# "<html><body><%= yield %></body></html>"
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# route do |r|
|
45
|
+
# set_view_subdir("main")
|
46
|
+
# @user = 'You'
|
47
|
+
# render(:index)
|
48
|
+
# end
|
49
|
+
module NamedTemplates
|
50
|
+
# Depend on the render plugin
|
51
|
+
def self.load_dependencies(app)
|
52
|
+
app.plugin :render
|
53
|
+
end
|
54
|
+
|
55
|
+
# Initialize the storage for named templates.
|
56
|
+
def self.configure(app)
|
57
|
+
app.opts[:named_templates] ||= {}
|
58
|
+
end
|
59
|
+
|
60
|
+
module ClassMethods
|
61
|
+
# Store a new template block and options for the given template name.
|
62
|
+
def template(name, options=nil, &block)
|
63
|
+
opts[:named_templates][name.to_s] = [options, block]
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module InstanceMethods
|
69
|
+
private
|
70
|
+
|
71
|
+
# If a template name is given and it matches a named template, call
|
72
|
+
# the named template block to get the inline template to use.
|
73
|
+
def find_template(options)
|
74
|
+
if options[:template] && (template_opts, block = opts[:named_templates][template_name(options)]; block)
|
75
|
+
if template_opts
|
76
|
+
options = template_opts.merge(options)
|
77
|
+
else
|
78
|
+
options = options.dup
|
79
|
+
end
|
80
|
+
|
81
|
+
options[:inline] = instance_exec(&block)
|
82
|
+
|
83
|
+
super(options)
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
register_plugin(:named_templates, NamedTemplates)
|
92
|
+
end
|
93
|
+
end
|
@@ -59,48 +59,15 @@ class Roda
|
|
59
59
|
#
|
60
60
|
# In all cases where it uses a 405 response, it also sets the +Allow+
|
61
61
|
# header in the response to contain the request methods supported.
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
62
|
+
#
|
63
|
+
# This plugin depends on the all_verbs plugin. It works by overriding
|
64
|
+
# the verb methods, so it wouldn't work if loaded after the all_verbs
|
65
|
+
# plugin.
|
65
66
|
module NotAllowed
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
app::RodaRequest.def_verb_method(self, :post)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
module RequestClassMethods
|
75
|
-
# Define a method named +verb+ in the given module which will
|
76
|
-
# return a 405 response if the method is called with any
|
77
|
-
# arguments and the arguments terminally match but the
|
78
|
-
# request method does not.
|
79
|
-
#
|
80
|
-
# If called without any arguments, check to see if the call
|
81
|
-
# is inside a terminal match, and in that case record the
|
82
|
-
# request method used.
|
83
|
-
def def_verb_method(mod, verb)
|
84
|
-
mod.class_eval(<<-END, __FILE__, __LINE__+1)
|
85
|
-
def #{verb}(*args, &block)
|
86
|
-
if args.empty?
|
87
|
-
@_is_verbs << "#{verb.to_s.upcase}" if defined?(@_is_verbs)
|
88
|
-
always(&block) if #{verb == :get ? :is_get : verb}?
|
89
|
-
else
|
90
|
-
args << ::Roda::RodaPlugins::Base::RequestMethods::TERM
|
91
|
-
if_match(args) do |*a|
|
92
|
-
if #{verb == :get ? :is_get : verb}?
|
93
|
-
block_result(yield(*a))
|
94
|
-
throw :halt, response.finish
|
95
|
-
end
|
96
|
-
response.status = 405
|
97
|
-
response['Allow'] = '#{verb.to_s.upcase}'
|
98
|
-
nil
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
END
|
103
|
-
end
|
67
|
+
# Depend on the all_verbs plugin, as this plugin overrides methods
|
68
|
+
# defined by it and calls super.
|
69
|
+
def self.load_dependencies(app)
|
70
|
+
app.plugin :all_verbs
|
104
71
|
end
|
105
72
|
|
106
73
|
module RequestMethods
|
@@ -110,20 +77,19 @@ class Roda
|
|
110
77
|
# since there was already a successful terminal match, the
|
111
78
|
# request method must not be allowed, so return a 405
|
112
79
|
# response in that case.
|
113
|
-
def is(*
|
114
|
-
super(*
|
80
|
+
def is(*args)
|
81
|
+
super(*args) do
|
115
82
|
begin
|
116
|
-
@_is_verbs = []
|
83
|
+
is_verbs = @_is_verbs = []
|
117
84
|
|
118
|
-
ret = if
|
85
|
+
ret = if args.empty?
|
119
86
|
yield
|
120
87
|
else
|
121
88
|
yield(*captures)
|
122
89
|
end
|
123
90
|
|
124
|
-
unless
|
125
|
-
|
126
|
-
response['Allow'] = @_is_verbs.join(', ')
|
91
|
+
unless is_verbs.empty?
|
92
|
+
method_not_allowed(is_verbs.join(', '))
|
127
93
|
end
|
128
94
|
|
129
95
|
ret
|
@@ -132,6 +98,35 @@ class Roda
|
|
132
98
|
end
|
133
99
|
end
|
134
100
|
end
|
101
|
+
|
102
|
+
# Setup methods for all verbs. If inside an is block and not given
|
103
|
+
# arguments, record the verb used. If given an argument, add an is
|
104
|
+
# check with the argu
|
105
|
+
%w'get post delete head options link patch put trace unlink'.each do |verb|
|
106
|
+
if ::Rack::Request.method_defined?("#{verb}?")
|
107
|
+
class_eval(<<-END, __FILE__, __LINE__+1)
|
108
|
+
def #{verb}(*args, &block)
|
109
|
+
if (empty = args.empty?) && @_is_verbs
|
110
|
+
@_is_verbs << "#{verb.to_s.upcase}"
|
111
|
+
end
|
112
|
+
super
|
113
|
+
unless empty
|
114
|
+
is(*args){method_not_allowed("#{verb.to_s.upcase}")}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
END
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# Set the response status to 405 (Method Not Allowed), and set the Allow header
|
124
|
+
# to the given string of allowed request methods.
|
125
|
+
def method_not_allowed(verbs)
|
126
|
+
res = response
|
127
|
+
res.status = 405
|
128
|
+
res['Allow'] = verbs
|
129
|
+
end
|
135
130
|
end
|
136
131
|
end
|
137
132
|
|
data/lib/roda/plugins/path.rb
CHANGED
@@ -17,9 +17,31 @@ class Roda
|
|
17
17
|
# r.redirect bar_path(bar)
|
18
18
|
# end
|
19
19
|
# end
|
20
|
+
#
|
21
|
+
# The path method accepts the following options:
|
22
|
+
#
|
23
|
+
# :add_script_name :: Prefix the path generated with SCRIPT_NAME.
|
24
|
+
# :name :: Provide a different name for the method, instead of using <tt>*_path</tt>.
|
25
|
+
# :url :: Create a url method in addition to the path method, which will prefix the string generated
|
26
|
+
# with the appropriate scheme, host, and port. If true, creates a <tt>*_url</tt>
|
27
|
+
# method. If a Symbol or String, uses the value as the url method name.
|
28
|
+
# :url_only :: Do not create a path method, just a url method.
|
29
|
+
#
|
30
|
+
# Note that if :add_script_name, :url, or :url_only is used, this will also create a <tt>_*_path</tt>
|
31
|
+
# method. This is necessary in order to support path methods that accept blocks, as you can't pass
|
32
|
+
# a block to a block that is instance_execed.
|
20
33
|
module Path
|
34
|
+
DEFAULT_PORTS = {'http' => 80, 'https' => 443}.freeze
|
35
|
+
|
21
36
|
module ClassMethods
|
22
|
-
|
37
|
+
# Create a new instance method for the named path. See plugin module documentation for options.
|
38
|
+
def path(name, path=nil, opts={}, &block)
|
39
|
+
if path.is_a?(Hash)
|
40
|
+
raise RodaError, "cannot provide two option hashses to Roda.path" unless opts.empty?
|
41
|
+
opts = path
|
42
|
+
path = nil
|
43
|
+
end
|
44
|
+
|
23
45
|
raise RodaError, "cannot provide both path and block to Roda.path" if path && block
|
24
46
|
raise RodaError, "must provide either path or block to Roda.path" unless path || block
|
25
47
|
|
@@ -28,7 +50,46 @@ class Roda
|
|
28
50
|
block = lambda{path}
|
29
51
|
end
|
30
52
|
|
31
|
-
|
53
|
+
meth = opts[:name] || "#{name}_path"
|
54
|
+
url = opts[:url]
|
55
|
+
add_script_name = opts[:add_script_name]
|
56
|
+
|
57
|
+
if add_script_name || url || opts[:url_only]
|
58
|
+
_meth = "_#{meth}"
|
59
|
+
define_method(_meth, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
unless opts[:url_only]
|
63
|
+
if add_script_name
|
64
|
+
define_method(meth) do |*a, &blk|
|
65
|
+
request.script_name.to_s + send(_meth, *a, &blk)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
define_method(meth, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if url || opts[:url_only]
|
73
|
+
url_meth = if url.is_a?(String) || url.is_a?(Symbol)
|
74
|
+
url
|
75
|
+
else
|
76
|
+
"#{name}_url"
|
77
|
+
end
|
78
|
+
|
79
|
+
url_block = lambda do |*a, &blk|
|
80
|
+
r = request
|
81
|
+
scheme = r.scheme
|
82
|
+
port = r.port
|
83
|
+
uri = ["#{scheme}://#{r.host}#{":#{port}" unless DEFAULT_PORTS[scheme] == port}"]
|
84
|
+
uri << request.script_name.to_s if add_script_name
|
85
|
+
uri << send(_meth, *a, &blk)
|
86
|
+
File.join(uri)
|
87
|
+
end
|
88
|
+
|
89
|
+
define_method(url_meth, &url_block)
|
90
|
+
end
|
91
|
+
|
92
|
+
nil
|
32
93
|
end
|
33
94
|
end
|
34
95
|
end
|