roda 3.86.0 → 3.87.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1851a201539b728f1af90ea1b85b2b33a9026f71986cb65c1aabdd079f653ad
4
- data.tar.gz: f360e0bfeb3442df2fa1f96e28362eee9850c0f54d160bfbfc2b11d62d33ba39
3
+ metadata.gz: bee7ae126ad0e6e8081bba94be0ad60e2a5f7523c483b2441571549b1bd29274
4
+ data.tar.gz: f5441e96617877d1347f4a5b6e54e5d48713ffc91a8ad3b9282781c0c26ca108
5
5
  SHA512:
6
- metadata.gz: d2bef4abc3d5e08ddb5a1c9c27f8b626b631a5951cec7cee062c497074cb7f68e6c8b36e75261cc913a580a87d5111438e8a21488669812c9a3b227bf9609e0b
7
- data.tar.gz: e668a47039e529aa026e21ddfd6346b3e1fa224f432b46085b33242c9551ced88278328f2d2d471b2593f841da0a882d8f85dd2468277484409f679485a219df
6
+ metadata.gz: 141408b4d85e7467c0b753c05180201b1a63a3096f713241b94d379bad8d65224d96f1d60978ac336a7d8b9fb3093f35c1d2887d422276cf255cd1c64c557719
7
+ data.tar.gz: a64207e6d0a46d22c59bc0acc9d56ed43f4dbc75a9d22737908097564a2e4e801f3c19fd3bf95a28722dbf4a828d9930146dd088e421847709948d3a8f2cf24b
@@ -28,7 +28,16 @@ class Roda
28
28
  #
29
29
  # Note that custom block result handling only occurs if the types
30
30
  # are not handled by Roda itself. You cannot use this to modify
31
- # the handling of nil, false, or string results.
31
+ # the handling of nil, false, or string results. Additionally,
32
+ # if the response body has already been written to before the the
33
+ # route block exits, then the result of the block is ignored,
34
+ # and the related +handle_block_result+ block will not be called
35
+ # (this is standard Roda behavior).
36
+ #
37
+ # The return value of the +handle_block_result+ block is written
38
+ # to the body if the block return value is a String, similar to
39
+ # standard Roda handling of block results. Non-String return
40
+ # values are ignored.
32
41
  module CustomBlockResults
33
42
  def self.configure(app)
34
43
  app.opts[:custom_block_results] ||= {}
@@ -55,7 +64,15 @@ class Roda
55
64
  # to get the block result.
56
65
  def unsupported_block_result(result)
57
66
  roda_class.opts[:custom_block_results].each do |klass, meth|
58
- return scope.send(meth, result) if klass === result
67
+ if klass === result
68
+ result = scope.send(meth, result)
69
+
70
+ if String === result
71
+ return result
72
+ else
73
+ return
74
+ end
75
+ end
59
76
  end
60
77
 
61
78
  super
@@ -51,7 +51,8 @@ class Roda
51
51
 
52
52
  # Match if the given uppercase key is present inside the environment.
53
53
  def match_header(key)
54
- key = key.upcase.tr("-","_")
54
+ key = key.upcase
55
+ key.tr!("-","_")
55
56
  unless key == "CONTENT_TYPE" || key == "CONTENT_LENGTH"
56
57
  key = "HTTP_#{key}"
57
58
  end
@@ -75,8 +76,8 @@ class Roda
75
76
  # Match the submitted user agent to the given pattern, capturing any
76
77
  # regexp match groups.
77
78
  def match_user_agent(pattern)
78
- if (user_agent = @env["HTTP_USER_AGENT"]) && user_agent.to_s =~ pattern
79
- @captures.concat($~[1..-1])
79
+ if (user_agent = @env["HTTP_USER_AGENT"]) && (match = pattern.match(user_agent))
80
+ @captures.concat(match.captures)
80
81
  end
81
82
  end
82
83
  end
@@ -0,0 +1,190 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The host_routing plugin adds support for more routing requests based on
7
+ # the requested host. It also adds predicate methods for checking
8
+ # whether a request was requested with the given host.
9
+ #
10
+ # When loading the plugin, you pass a block, which is used for configuring
11
+ # the plugin. For example, if you want to treat requests to api.example.com
12
+ # or api2.example.com as api requests, and treat other requests as www
13
+ # requests, you could use:
14
+ #
15
+ # plugin :host_routing do |hosts|
16
+ # hosts.to :api, "api.example.com", "api2.example.com"
17
+ # hosts.default :www
18
+ # end
19
+ #
20
+ # With this configuration, in your routing tree, you can call the +r.api+ and
21
+ # +r.www+ methods for dispatching to routing blocks only for those types of
22
+ # requests:
23
+ #
24
+ # route do |r|
25
+ # r.api do
26
+ # # requests to api.example.com or api2.example.com
27
+ # end
28
+ #
29
+ # r.www do
30
+ # # requests to other domains
31
+ # end
32
+ # end
33
+ #
34
+ # In addition to the routing methods, predicate methods are also added to the
35
+ # request object:
36
+ #
37
+ # route do |r|
38
+ # "#{r.api?}-#{r.www?}"
39
+ # end
40
+ # # Requests to api.example.com or api2.example.com return "true-false"
41
+ # # Other requests return "false-true"
42
+ #
43
+ # If the +:scope_predicates+ plugin option is given, predicate methods are also
44
+ # created in route block scope:
45
+ #
46
+ # plugin :host_routing, scope_predicates: true do |hosts|
47
+ # hosts.to :api, "api.example.com"
48
+ # hosts.default :www
49
+ # end
50
+ #
51
+ # route do |r|
52
+ # "#{api?}-#{www?}"
53
+ # end
54
+ #
55
+ # To handle hosts that match a certain format (such as all subdomains),
56
+ # where the specific host names are not known up front, you can provide a block
57
+ # when calling +hosts.default+. This block is passed the host name, or an empty
58
+ # string if no host name is provided, and is evaluated in route block scope.
59
+ # When using this support, you should also call +hosts.register+
60
+ # to register host types that could be returned by the block. For example, to
61
+ # handle api subdomains differently:
62
+ #
63
+ # plugin :host_routing do |hosts|
64
+ # hosts.to :api, "api.example.com"
65
+ # hosts.register :api_sub
66
+ # hosts.default :www do |host|
67
+ # :api_sub if host.end_with?(".api.example.com")
68
+ # end
69
+ # end
70
+ #
71
+ # This plugin uses the host method on the request to get the hostname (this method
72
+ # is defined by Rack).
73
+ module HostRouting
74
+ # Setup the host routing support. The block yields an object used to
75
+ # configure the plugin. Options:
76
+ #
77
+ # :scope_predicates :: Setup predicate methods in route block scope
78
+ # in addition to request scope.
79
+ def self.configure(app, opts=OPTS, &block)
80
+ hosts, host_hash, default_block, default_host = DSL.new.process(&block)
81
+ app.opts[:host_routing_hash] = host_hash
82
+ app.opts[:host_routing_default_host] = default_host
83
+
84
+ app.send(:define_method, :_host_routing_default, &default_block) if default_block
85
+
86
+ app::RodaRequest.class_exec do
87
+ hosts.each do |host|
88
+ host_sym = host.to_sym
89
+ define_method(host_sym){|&blk| always(&blk) if _host_routing_host == host}
90
+ alias_method host_sym, host_sym
91
+
92
+ meth = :"#{host}?"
93
+ define_method(meth){_host_routing_host == host}
94
+ alias_method meth, meth
95
+ end
96
+ end
97
+
98
+ if opts[:scope_predicates]
99
+ app.class_exec do
100
+ hosts.each do |host|
101
+ meth = :"#{host}?"
102
+ define_method(meth){@_request.send(meth)}
103
+ alias_method meth, meth
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ class DSL
110
+ def initialize
111
+ @hosts = []
112
+ @host_hash = {}
113
+ end
114
+
115
+ # Run the DSL for the given block.
116
+ def process(&block)
117
+ instance_exec(self, &block)
118
+
119
+ if !@default_host
120
+ raise RodaError, "must call default method inside host_routing plugin block to set default host"
121
+ end
122
+
123
+ @hosts.concat(@host_hash.values)
124
+ @hosts << @default_host
125
+ @hosts.uniq!
126
+ [@hosts.freeze, @host_hash.freeze, @default_block, @default_host].freeze
127
+ end
128
+
129
+ # Register hosts that can be returned. This is only needed if
130
+ # calling register with a block, where the block can return
131
+ # a value that doesn't match a host given to +to+ or +default+.
132
+ def register(*hosts)
133
+ @hosts = hosts
134
+ end
135
+
136
+ # Treat all given hostnames as routing to the give host.
137
+ def to(host, *hostnames)
138
+ hostnames.each do |hostname|
139
+ @host_hash[hostname] = host
140
+ end
141
+ end
142
+
143
+ # Register the default hostname. If a block is provided, it is
144
+ # called with the host if there is no match for one of the hostnames
145
+ # provided to +to+. If the block returns nil/false, the hostname
146
+ # given to this method is used.
147
+ def default(hostname, &block)
148
+ @default_host = hostname
149
+ @default_block = block
150
+ end
151
+ end
152
+ private_constant :DSL
153
+
154
+ module InstanceMethods
155
+ # Handle case where plugin is used without providing a block to
156
+ # +hosts.default+. This returns nil, ensuring that the hostname
157
+ # provided to +hosts.default+ will be used.
158
+ def _host_routing_default(_)
159
+ nil
160
+ end
161
+ end
162
+
163
+ module RequestMethods
164
+ private
165
+
166
+ # Cache the host to use in the host routing support, so the processing
167
+ # is only done once per request.
168
+ def _host_routing_host
169
+ @_host_routing_host ||= _get_host_routing_host
170
+ end
171
+
172
+ # Determine the host to use for the host routing support. Tries the
173
+ # following, in order:
174
+ #
175
+ # * An exact match for a hostname given in +hosts.to+
176
+ # * The return value of the +hosts.default+ block, if given
177
+ # * The default value provided in the +hosts.default+ call
178
+ def _get_host_routing_host
179
+ host = self.host || ""
180
+
181
+ roda_class.opts[:host_routing_hash][host] ||
182
+ scope._host_routing_default(host) ||
183
+ roda_class.opts[:host_routing_default_host]
184
+ end
185
+ end
186
+ end
187
+
188
+ register_plugin(:host_routing, HostRouting)
189
+ end
190
+ end
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 86
7
+ RodaMinorVersion = 87
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.86.0
4
+ version: 3.87.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-12 00:00:00.000000000 Z
11
+ date: 2024-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -225,6 +225,7 @@ files:
225
225
  - lib/roda/plugins/hmac_paths.rb
226
226
  - lib/roda/plugins/hooks.rb
227
227
  - lib/roda/plugins/host_authorization.rb
228
+ - lib/roda/plugins/host_routing.rb
228
229
  - lib/roda/plugins/hsts.rb
229
230
  - lib/roda/plugins/indifferent_params.rb
230
231
  - lib/roda/plugins/inject_erb.rb