renee-core 0.2.0 → 0.3.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.
@@ -1,112 +0,0 @@
1
- class Renee
2
- class Core
3
- class Application
4
- # Collection of useful methods for responding within a {Renee::Core} app.
5
- module Responding
6
- # Codes used by Symbol lookup in interpret_response.
7
- # @example
8
- # halt :unauthorized # would return a 401.
9
- #
10
- HTTP_CODES = {
11
- :ok => 200,
12
- :created => 201,
13
- :accepted => 202,
14
- :no_content => 204,
15
- :no_content => 204,
16
- :bad_request => 400,
17
- :unauthorized => 401,
18
- :payment_required => 403,
19
- :not_found => 404,
20
- :method_not_found => 405,
21
- :not_acceptable => 406,
22
- :gone => 410,
23
- :error => 500,
24
- :not_implemented => 501}.freeze
25
-
26
- # Halts current processing to the top-level calling Renee application and uses that as a response.
27
- # @param [Object...] response The response to use.
28
- # @see #interpret_response
29
- def halt(*response)
30
- throw :halt, interpret_response(response.size == 1 ? response.first : response)
31
- end
32
-
33
- ##
34
- # Creates a response by allowing the response header, body and status to be passed into the block.
35
- #
36
- # @param [Array] body The contents to return.
37
- # @param [Integer] status The status code to return.
38
- # @param [Hash] header The headers to return.
39
- # @param [Proc] &blk The response options to specify
40
- #
41
- # @example
42
- # respond { status 200; body "Yay!" }
43
- # respond("Hello", 200, "foo" => "bar")
44
- #
45
- def respond(body=[], status=200, header={}, &blk)
46
- Renee::Core::Response.new(body, status, header).tap { |r| r.instance_eval(&blk) if block_given? }.finish
47
- end
48
-
49
- ##
50
- # Creates a response by allowing the response header, body and status to be passed into the block.
51
- #
52
- # @example
53
- # respond! { status 200; body "Yay!" }
54
- #
55
- # @param (see #respond)
56
- # @see #respond
57
- def respond!(*args, &blk)
58
- halt respond(*args, &blk)
59
- end
60
-
61
- # Interprets responses returns by #halt.
62
- #
63
- # * If it is a Symbol, it will be looked up in {HTTP_CODES}.
64
- # * If it is a Symbol, it will use Rack::Response to return the value.
65
- # * If it is a Symbol, it will either be used as a Rack response or as a body and status code.
66
- # * If it is an Integer, it will use Rack::Response to return the status code.
67
- # * Otherwise, #to_s will be called on it and it will be treated as a Symbol.
68
- #
69
- # @param [Object] response This can be either a Symbol, String, Array or any Object.
70
- #
71
- def interpret_response(response)
72
- case response
73
- when Array then
74
- case response.size
75
- when 3 then response
76
- when 2 then Renee::Core::Response.new(response[1], HTTP_CODES[response[0]] || response[0]).finish
77
- else raise "I don't know how to render #{response.inspect}"
78
- end
79
- when String then Renee::Core::Response.new(response).finish
80
- when Integer then Renee::Core::Response.new("Status code #{response}", response).finish
81
- when Symbol then interpret_response(HTTP_CODES[response] || response.to_s)
82
- when Proc then instance_eval(&response)
83
- else raise "Unable to render #{response.inspect}"
84
- end
85
- end
86
-
87
- # Returns a rack-based response for redirection.
88
- # @param [String] path The URL to redirect to.
89
- # @param [Integer] code The HTTP code to use.
90
- # @example
91
- # r = Renee::Core.new { get { halt redirect '/index' } }
92
- # r.call(Rack::MockResponse("/")) # => [302, {"Location" => "/index"}, []]
93
- def redirect(path, code = 302)
94
- response = ::Rack::Response.new
95
- response.redirect(path, code)
96
- response.finish
97
- end
98
-
99
- # Halts with a rack-based response for redirection.
100
- # @see #redirect
101
- # @param [String] path The URL to redirect to.
102
- # @param [Integer] code The HTTP code to use.
103
- # @example
104
- # r = Renee::Core.new { get { redirect! '/index' } }
105
- # r.call(Rack::MockResponse("/")) # => [302, {"Location" => "/index"}, []]
106
- def redirect!(path, code = 302)
107
- halt redirect(path, code)
108
- end
109
- end
110
- end
111
- end
112
- end
@@ -1,319 +0,0 @@
1
- class Renee
2
- class Core
3
- class Application
4
- # Collection of useful methods for routing within a {Renee::Core} app.
5
- module Routing
6
- include Chaining
7
-
8
- # Match a path to respond to.
9
- #
10
- # @param [String] p
11
- # path to match.
12
- # @param [Proc] blk
13
- # block to yield
14
- #
15
- # @example
16
- # path('/') { ... } #=> '/'
17
- # path('test') { ... } #=> '/test'
18
- #
19
- # path 'foo' do
20
- # path('bar') { ... } #=> '/foo/bar'
21
- # end
22
- #
23
- # @api public
24
- def path(p, &blk)
25
- p = p[1, p.size] if p[0] == ?/
26
- extension_part = detected_extension ? "|\\.#{Regexp.quote(detected_extension)}" : ""
27
- part(/^\/#{Regexp.quote(p)}(\/?|$)(?=\/|$#{extension_part})/, &blk)
28
- end
29
- chain_method :path
30
-
31
- # Like #path, but requires the entire path to be consumed.
32
- # @see #path
33
- def whole_path(p, &blk)
34
- path(p) { complete(&blk) }
35
- end
36
- chain_method :whole_path
37
-
38
- # Like #path, but doesn't automatically match trailing-slashes.
39
- # @see #path
40
- def exact_path(p, &blk)
41
- p = p[1, part.size] if p[0] == ?/
42
- part(/^\/#{Regexp.quote(p)}/, &blk)
43
- end
44
- chain_method :exact_path
45
-
46
- # Like #path, but doesn't look for leading slashes.
47
- def part(p, &blk)
48
- p = /\/?#{Regexp.quote(p)}/ if p.is_a?(String)
49
- if match = env['PATH_INFO'][p]
50
- with_path_part(match) { blk.call }
51
- end
52
- end
53
- chain_method :part
54
-
55
- # Match parts off the path as variables. The parts matcher can conform to either a regular expression, or be an Integer, or
56
- # simply a String.
57
- # @param[Object] type the type of object to match for. If you supply Integer, this will only match integers in addition to casting your variable for you.
58
- # @param[Object] default the default value to use if your param cannot be successfully matched.
59
- #
60
- # @example
61
- # path '/' do
62
- # variable { |id| halt [200, {}, id] }
63
- # end
64
- # GET /hey #=> [200, {}, 'hey']
65
- #
66
- # @example
67
- # path '/' do
68
- # variable(:integer) { |id| halt [200, {}, "This is a numeric id: #{id}"] }
69
- # end
70
- # GET /123 #=> [200, {}, 'This is a numeric id: 123']
71
- #
72
- # @example
73
- # path '/test' do
74
- # variable { |foo, bar| halt [200, {}, "#{foo}-#{bar}"] }
75
- # end
76
- # GET /test/hey/there #=> [200, {}, 'hey-there']
77
- #
78
- # @api public
79
- def variable(type = nil, &blk)
80
- complex_variable(type, '/', 1, &blk)
81
- end
82
- alias_method :var, :variable
83
- chain_method :variable, :var
84
-
85
- # Same as variable except you can match multiple variables with the same type.
86
- # @param [Range, Integer] count The number of parameters to capture.
87
- # @param [Symbol] type The type to use for match.
88
- def multi_variable(count, type = nil, &blk)
89
- complex_variable(type, '/', count, &blk)
90
- end
91
- alias_method :multi_var, :multi_variable
92
- alias_method :mvar, :multi_variable
93
- chain_method :multi_variable, :multi_var, :mvar
94
-
95
- # Same as variable except it matches indefinitely.
96
- # @param [Symbol] type The type to use for match.
97
- def repeating_variable(type = nil, &blk)
98
- complex_variable(type, '/', nil, &blk)
99
- end
100
- alias_method :glob, :repeating_variable
101
- chain_method :repeating_variable, :glob
102
-
103
- # Match parts off the path as variables without a leading slash.
104
- # @see #variable
105
- # @api public
106
- def partial_variable(type = nil, &blk)
107
- complex_variable(type, nil, 1, &blk)
108
- end
109
- alias_method :part_var, :partial_variable
110
- chain_method :partial_variable, :part_var
111
-
112
- # Match an extension.
113
- #
114
- # @example
115
- # extension('html') { |path| halt [200, {}, path] }
116
- #
117
- # @api public
118
- def extension(ext, &blk)
119
- if detected_extension && match = detected_extension[ext]
120
- if match == detected_extension
121
- (ext_match = env['PATH_INFO'][/\/?\.#{match}/]) ?
122
- with_path_part(ext_match, &blk) : blk.call
123
- end
124
- end
125
- end
126
- alias_method :ext, :extension
127
- chain_method :extension, :ext
128
-
129
- # Match no extension.
130
- #
131
- # @example
132
- # no_extension { |path| halt [200, {}, path] }
133
- #
134
- # @api public
135
- def no_extension(&blk)
136
- blk.call if detected_extension.nil?
137
- end
138
- chain_method :no_extension
139
-
140
- # Match any remaining path.
141
- #
142
- # @example
143
- # remainder { |path| halt [200, {}, path] }
144
- #
145
- # @api public
146
- def remainder(&blk)
147
- with_path_part(env['PATH_INFO']) { |var| blk.call(var) }
148
- end
149
- alias_method :catchall, :remainder
150
- chain_method :remainder, :catchall
151
-
152
- # Respond to a GET request and yield the block.
153
- #
154
- # @example
155
- # get { halt [200, {}, "hello world"] }
156
- #
157
- # @api public
158
- def get(path = nil, &blk)
159
- request_method('GET', path, &blk)
160
- end
161
- chain_method :get
162
-
163
- # Respond to a POST request and yield the block.
164
- #
165
- # @example
166
- # post { halt [200, {}, "hello world"] }
167
- #
168
- # @api public
169
- def post(path = nil, &blk)
170
- request_method('POST', path, &blk)
171
- end
172
- chain_method :post
173
-
174
- # Respond to a PUT request and yield the block.
175
- #
176
- # @example
177
- # put { halt [200, {}, "hello world"] }
178
- #
179
- # @api public
180
- def put(path = nil, &blk)
181
- request_method('PUT', path, &blk)
182
- end
183
- chain_method :put
184
-
185
- # Respond to a DELETE request and yield the block.
186
- #
187
- # @example
188
- # delete { halt [200, {}, "hello world"] }
189
- #
190
- # @api public
191
- def delete(path = nil, &blk)
192
- request_method('DELETE', path, &blk)
193
- end
194
- chain_method :delete
195
-
196
- # Match only when the path has been completely consumed.
197
- #
198
- # @example
199
- # complete { halt [200, {}, "hello world"] }
200
- #
201
- # @api public
202
- def complete(&blk)
203
- if env['PATH_INFO'] == '' || is_index_request
204
- with_path_part(env['PATH_INFO']) { blk.call }
205
- end
206
- end
207
- chain_method :complete
208
-
209
- # Match variables within the query string.
210
- #
211
- # @param [Array, Hash] q
212
- # Either an array or hash of things to match query string variables. If given
213
- # an array, if you pass the values for each key as parameters to the block given.
214
- # If given a hash, then every value must be able to be matched by a registered type.
215
- #
216
- # @example
217
- # query(:key => :integer) { |h| halt [200, {}, "hello world #{h[:key]}"] }
218
- #
219
- # @example
220
- # query(:key) { |val| halt [200, {}, "key is #{val}"] }
221
- #
222
- # @api public
223
- def query(q, &blk)
224
- case q
225
- when Hash then blk.call(Hash[q.map{|(k, v)| [k, transform(v, request[k.to_s]) || return]}])
226
- when Array then blk.call(*q.map{|qk| request[qk.to_s] or return })
227
- else query([q], &blk)
228
- end
229
- end
230
- chain_method :query
231
-
232
- # Yield block if the query string matches.
233
- #
234
- # @param [String] qs
235
- # The query string to match.
236
- #
237
- # @example
238
- # path 'test' do
239
- # query_string 'foo=bar' do
240
- # halt [200, {}, 'matched']
241
- # end
242
- # end
243
- # GET /test?foo=bar #=> 'matched'
244
- #
245
- # @api public
246
- def query_string(qs, &blk)
247
- blk.call if qs === env['QUERY_STRING']
248
- end
249
- chain_method :query_string
250
-
251
- private
252
- def complex_variable(type, prefix, count)
253
- matcher = variable_matcher_for_type(type)
254
- path = env['PATH_INFO'].dup
255
- vals = []
256
- var_index = 0
257
- variable_matching_loop(count) do
258
- path.start_with?(prefix) ? path.slice!(0, prefix.size) : break if prefix
259
- if match = matcher[path]
260
- path.slice!(0, match.first.size)
261
- vals << match.last
262
- end
263
- end
264
- return unless count.nil? || count === vals.size
265
- with_path_part(env['PATH_INFO'][0, env['PATH_INFO'].size - path.size]) do
266
- if count == 1
267
- yield(vals.first)
268
- else
269
- yield(vals)
270
- end
271
- end
272
- end
273
-
274
- def variable_matching_loop(count)
275
- case count
276
- when Range then count.max.times { break unless yield }
277
- when nil then loop { break unless yield }
278
- else count.times { break unless yield }
279
- end
280
- end
281
-
282
- def variable_matcher_for_type(type)
283
- if settings.variable_types.key?(type)
284
- settings.variable_types[type]
285
- else
286
- regexp = case type
287
- when nil, String
288
- detected_extension ?
289
- /(([^\/](?!#{Regexp.quote(detected_extension)}$))+)(?=$|\/|\.#{Regexp.quote(detected_extension)})/ :
290
- /([^\/]+)(?=$|\/)/
291
- when Regexp
292
- type
293
- else
294
- raise "Unexpected variable type #{type.inspect}"
295
- end
296
- proc do |path|
297
- if match = /^#{regexp.to_s}/.match(path)
298
- [match[0]]
299
- end
300
- end
301
- end
302
- end
303
-
304
- def with_path_part(part)
305
- old_path_info, old_script_name = env['PATH_INFO'], env['SCRIPT_NAME']
306
- script_part, env['PATH_INFO'] = old_path_info[0, part.size], old_path_info[part.size, old_path_info.size]
307
- env['SCRIPT_NAME'] += script_part
308
- yield script_part
309
- env['PATH_INFO'], env['SCRIPT_NAME'] = old_path_info, old_script_name
310
- end
311
-
312
- def request_method(method, path = nil, &blk)
313
- path ? whole_path(path) { blk.call } : complete { blk.call } if env['REQUEST_METHOD'] == method
314
- end
315
- chain_method :request_method
316
- end
317
- end
318
- end
319
- end
@@ -1,20 +0,0 @@
1
- class Renee
2
- class Core
3
- class Application
4
- # Module used for transforming arbitrary values using the registerd variable types.
5
- # @see #register_variable_name.
6
- #
7
- module Transform
8
- # Transforms a value according to the rules specified by #register_variable_name.
9
- # @param [Symbol] name The name of the variable type.
10
- # @param [String] value The value to transform.
11
- # @return The transformed value or nil.
12
- def transform(type, value)
13
- if settings.variable_types.key?(type) and m = settings.variable_types[type][value]
14
- m.first == value ? m.last : nil
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end