mwmitchell-pepper 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.
- data/LICENSE +19 -0
- data/README +103 -0
- data/lib/pepper.rb +377 -0
- data/lib/pepper/demo.rb +54 -0
- data/spec/common.rb +7 -0
- data/spec/demo.spec.rb +69 -0
- data/spec/request.spec.rb +23 -0
- metadata +68 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2008 Matt Mitchell - goodieboy@gmail.com
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
Welcome to the pepper wiki!
|
2
|
+
Pepper is a Ruby, DSL based framework for creating RESTful web applications. It adheres to the Ruby Rack specification.
|
3
|
+
|
4
|
+
Pepper treats every fragment in a URL as a "context". Each context can be mapped to a block or another Pepper enabled module. For example, a URL of:
|
5
|
+
|
6
|
+
<pre>/users/1/images/1</pre>
|
7
|
+
|
8
|
+
could translate to a context block tree of:
|
9
|
+
|
10
|
+
<pre>
|
11
|
+
<code>
|
12
|
+
map 'users' do
|
13
|
+
map :digit do
|
14
|
+
get{"The value of this user is #{value}"}
|
15
|
+
map 'images' do
|
16
|
+
map :digit do
|
17
|
+
get{"The ID of this image is #{value}"}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
</code>
|
23
|
+
</pre>
|
24
|
+
|
25
|
+
Because each block maps to a single url fragment, "routing" is not needed. The value of a context's url fragment can be accessed by its :value attribute. You can also name the fragment and propagate to the request params like:
|
26
|
+
|
27
|
+
<pre>
|
28
|
+
<code>
|
29
|
+
map :user_id=>/\d+/ do
|
30
|
+
puts params[:user_id]
|
31
|
+
end
|
32
|
+
</code>
|
33
|
+
</pre>
|
34
|
+
|
35
|
+
The :user_id param will then be available as a global request param to other contexts and "actions".
|
36
|
+
|
37
|
+
The fragment value passed to :map can be a string, a regular expression or a symbol that matches one of the built-in rules; :digit, :word, etc.
|
38
|
+
|
39
|
+
Standard HTTP methods (GET, POST, PUT, etc.) are used as the "actions". Each of the actions methods can accept a "rule" hash. The keys map to the REQUEST values (but down-cased and symbolized). So you could execute an action only if it matched the IP, or SERVER_NAME. The value of the rule can be a Regular Expression or a string.
|
40
|
+
|
41
|
+
Here is an example:
|
42
|
+
|
43
|
+
<pre>
|
44
|
+
<code>
|
45
|
+
require 'pepper'
|
46
|
+
|
47
|
+
module SeriousApp
|
48
|
+
|
49
|
+
class Contact # A delegate/sub-app
|
50
|
+
|
51
|
+
include Pepper::Context::Delegate
|
52
|
+
|
53
|
+
build do
|
54
|
+
before :post do
|
55
|
+
# a before :post block. Default is :all.
|
56
|
+
end
|
57
|
+
|
58
|
+
get{'GET request'}
|
59
|
+
post{'POST request'}
|
60
|
+
|
61
|
+
# request for /contact/:word
|
62
|
+
map :word do
|
63
|
+
get{"GET request for /contact/#{value}"}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Root
|
69
|
+
|
70
|
+
include Pepper::App
|
71
|
+
|
72
|
+
build do
|
73
|
+
get{'A simple GET to /'}
|
74
|
+
post{'A POST to /'}
|
75
|
+
|
76
|
+
# /about/
|
77
|
+
map 'about' do
|
78
|
+
get{'GET request for /about'}
|
79
|
+
end
|
80
|
+
|
81
|
+
# /notes
|
82
|
+
map 'notes' do
|
83
|
+
get(:server_name=>/^myspecialdomain\.com$/) do
|
84
|
+
'GET request for /notes, but only allowing a SERVER_NAME of "myspecialdomain.com"'}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# forwarding /contact to the Contact class
|
89
|
+
map 'contact', {}, Contact
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
</code>
|
95
|
+
</pre>
|
96
|
+
|
97
|
+
You then instantiate the Root class, and pass it to Rack's "run" method.
|
98
|
+
|
99
|
+
<pre>
|
100
|
+
<code>
|
101
|
+
run SeriousApp::Root.new
|
102
|
+
</code>
|
103
|
+
</pre>
|
data/lib/pepper.rb
ADDED
@@ -0,0 +1,377 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
$:.unshift File.dirname(__FILE__) unless $:.include? File.dirname(__FILE__)
|
5
|
+
|
6
|
+
module Pepper
|
7
|
+
|
8
|
+
VERSION='0.3.1'
|
9
|
+
|
10
|
+
class Request < Rack::Request
|
11
|
+
def method_sym
|
12
|
+
request_method.to_s.downcase.to_sym
|
13
|
+
end
|
14
|
+
def path_info_slices
|
15
|
+
['/'] + path_info.to_s.gsub(/\/+/, '/').sub(/^\/|\/$/, '').split('/')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Context
|
20
|
+
|
21
|
+
class Action
|
22
|
+
|
23
|
+
#
|
24
|
+
# The main context class needs to be able to resfresh this when reloading this action instance
|
25
|
+
#
|
26
|
+
attr_accessor :app
|
27
|
+
|
28
|
+
attr :method
|
29
|
+
attr :options
|
30
|
+
attr :block
|
31
|
+
|
32
|
+
def initialize(method, app, options={}, &block)
|
33
|
+
@app=app
|
34
|
+
@method=method
|
35
|
+
@options=options
|
36
|
+
@block=block
|
37
|
+
end
|
38
|
+
|
39
|
+
def app; @app end
|
40
|
+
def request; app.request end
|
41
|
+
def params; request.params end
|
42
|
+
|
43
|
+
def method_missing(m,*args,&block)
|
44
|
+
@app.send(m, *args, &block) if [:header, :headers].include?(m)
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute!; instance_eval(&@block) end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
#
|
53
|
+
#
|
54
|
+
module InstanceMethods
|
55
|
+
|
56
|
+
#
|
57
|
+
# The main app class needs to be able to set this
|
58
|
+
#
|
59
|
+
attr_accessor :app
|
60
|
+
|
61
|
+
#
|
62
|
+
# attributes that are set by the "initialize" method
|
63
|
+
#
|
64
|
+
# can be a string, regexp, hash or symbol
|
65
|
+
# * string - the current path slice will be compared using ==
|
66
|
+
# * regexp - the current path slice will be compared using =~
|
67
|
+
# * hash - if the value matches, the current slice is assigned to the request.params using the key
|
68
|
+
# * the value can in-turn be any string, regexp, hash or symbol
|
69
|
+
# * symbol - can be used to match preset rules found in the +PATTERNS+ hash
|
70
|
+
# if the symbol is not found in +PATTERNS+, it's converted to a string and uses ==
|
71
|
+
#
|
72
|
+
attr :pattern
|
73
|
+
# The parent context
|
74
|
+
attr :parent
|
75
|
+
# the options passed into +map+ method calls
|
76
|
+
attr :options
|
77
|
+
# optional module name to delegate logic for a given sub-path
|
78
|
+
attr :delegate
|
79
|
+
# the block that holds the executable context code
|
80
|
+
attr :block
|
81
|
+
|
82
|
+
def method_missing(m,*args,&block)
|
83
|
+
@app.send(m, *args, &block) if [:header, :headers].include?(m)
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Create the HTTP action methods...
|
88
|
+
#
|
89
|
+
HTTP_METHODS=%W(get post put delete)
|
90
|
+
HTTP_METHODS.each do |m|
|
91
|
+
class_eval <<-EOF
|
92
|
+
def #{m}(options={}, &block)
|
93
|
+
if a=actions.detect {|a|a.method==:#{m}.to_sym and a.options==options}
|
94
|
+
# refresh the app - this is needed because the root context is a "static" instance
|
95
|
+
a.app=@app
|
96
|
+
return
|
97
|
+
end
|
98
|
+
actions << Action.new(:#{m}, @app, options, &block)
|
99
|
+
end
|
100
|
+
EOF
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# "attributes" that are set after initialization
|
105
|
+
#
|
106
|
+
|
107
|
+
# returns the child contexts
|
108
|
+
def children; @children||=[]; end
|
109
|
+
# returns the value from the path slices that this context matched
|
110
|
+
def value; @value||=nil; end
|
111
|
+
# the base application instance
|
112
|
+
def app; @app; end
|
113
|
+
# returns instance of Rack::Request
|
114
|
+
def request; app.request; end
|
115
|
+
# shortcut for request.params
|
116
|
+
def params; request.params; end
|
117
|
+
#
|
118
|
+
def actions; @actions||=[] end
|
119
|
+
#
|
120
|
+
def before_blocks; @before_blocks||={} end
|
121
|
+
#
|
122
|
+
def after_blocks; @after_blocks||={} end
|
123
|
+
|
124
|
+
def before(action=:all, &block)
|
125
|
+
before_blocks[action]=block
|
126
|
+
end
|
127
|
+
|
128
|
+
def after(action=:all, &block)
|
129
|
+
after_blocks[action]=block
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Finds the action (get, post, put etc.) base on:
|
134
|
+
# * the method
|
135
|
+
# * the env/hash values
|
136
|
+
#
|
137
|
+
def find_action(method, env={})
|
138
|
+
actions.each do |a|
|
139
|
+
next if a.method != method
|
140
|
+
return a if a.options.nil? or a.options.size==0
|
141
|
+
a.options.each_pair do |k,v|
|
142
|
+
k=k.to_s.upcase
|
143
|
+
return a if env[k]==v or env[k]=~v
|
144
|
+
end
|
145
|
+
end
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
PATTERNS={
|
150
|
+
:digit=>/^\d+$/,
|
151
|
+
:word=>/^\w+$/
|
152
|
+
}
|
153
|
+
|
154
|
+
#
|
155
|
+
#
|
156
|
+
#
|
157
|
+
def match?(pattern, value)
|
158
|
+
ok = simple_match?(pattern, value)
|
159
|
+
return ok if ok
|
160
|
+
# if hash, use the value as the pattern and set the request param[key] to the value argument
|
161
|
+
return (params[pattern.keys.first] = match?(pattern[pattern.keys.first], value)) if pattern.is_a?(Hash)
|
162
|
+
# if symbol
|
163
|
+
if pattern.is_a?(Symbol)
|
164
|
+
# lookup the symbol in the PATTERNS hash
|
165
|
+
return value if PATTERNS[pattern] and match?(PATTERNS[pattern], value)
|
166
|
+
# There was no match, convert to string
|
167
|
+
simple_match?(pattern.to_s, value)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
#
|
173
|
+
#
|
174
|
+
def simple_match?(pattern, value)
|
175
|
+
# if string, simple comparison
|
176
|
+
return value if (pattern.is_a?(String) and value == pattern)
|
177
|
+
# if regexp, regx comparison
|
178
|
+
return value if (pattern.is_a?(Regexp) and value =~ pattern)
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# Recursively looks for matching context instances returning the last one that matches.
|
183
|
+
#
|
184
|
+
def resolve(slices)
|
185
|
+
return unless match?(pattern, slices.first)
|
186
|
+
@value=slices.shift
|
187
|
+
catch :delegate do
|
188
|
+
instance_eval(&@block) if @block
|
189
|
+
end
|
190
|
+
if delegate
|
191
|
+
raise 'Only module based delegates are allowed' unless delegate.class==Module
|
192
|
+
return delegate.delegate!(self, [@value]+slices)
|
193
|
+
end
|
194
|
+
return self if slices.size==0
|
195
|
+
children.each do |child|
|
196
|
+
if found=child.resolve(slices)
|
197
|
+
return found
|
198
|
+
end
|
199
|
+
end
|
200
|
+
nil
|
201
|
+
end
|
202
|
+
|
203
|
+
#
|
204
|
+
# Shortcut assigning a delegate
|
205
|
+
#
|
206
|
+
def delegate_to(handler)
|
207
|
+
@delegate=handler
|
208
|
+
throw :delegate
|
209
|
+
end
|
210
|
+
|
211
|
+
def child_exists?(pattern, options)
|
212
|
+
children.detect{|child|child.pattern==pattern and child.options==options}
|
213
|
+
end
|
214
|
+
|
215
|
+
#
|
216
|
+
# The main method for creating child contexts
|
217
|
+
#
|
218
|
+
def map(pattern, options={}, delegate=nil, &block)
|
219
|
+
if child=child_exists?(pattern, options)
|
220
|
+
# refresh the app
|
221
|
+
child.app=@app
|
222
|
+
return child
|
223
|
+
else
|
224
|
+
children << Context::Base.new(pattern, options, delegate, self, &block)
|
225
|
+
children.last.app=app
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end # end InstanceMethods
|
230
|
+
|
231
|
+
#
|
232
|
+
#
|
233
|
+
#
|
234
|
+
|
235
|
+
#
|
236
|
+
# Used for handling areas of your application, within the context of some path.
|
237
|
+
# For example, if you had a blog area of your site, you could off-load the logic
|
238
|
+
# from the main app to a "delegate" and keep the main app clear of too much clutter.
|
239
|
+
# It would actually be possible for your main app to do nothing but delegate.
|
240
|
+
#
|
241
|
+
module Delegate
|
242
|
+
|
243
|
+
#
|
244
|
+
# Shortcut for:
|
245
|
+
# extend Pepper::Context::Delegate::ClassMethods
|
246
|
+
#
|
247
|
+
def self.included(base)
|
248
|
+
base.extend ClassMethods
|
249
|
+
end
|
250
|
+
|
251
|
+
#
|
252
|
+
# Pepper::Context::Delegate::ClassMethods
|
253
|
+
#
|
254
|
+
module ClassMethods
|
255
|
+
|
256
|
+
#
|
257
|
+
# Bring in the standard context methods
|
258
|
+
#
|
259
|
+
include Context::InstanceMethods
|
260
|
+
|
261
|
+
#
|
262
|
+
# define the standard build method so the delegate can start creating the context tree
|
263
|
+
#
|
264
|
+
def build(options={}, &block)
|
265
|
+
@options=options
|
266
|
+
@block=block
|
267
|
+
end
|
268
|
+
|
269
|
+
#
|
270
|
+
# Sets up this module as a delegate handler
|
271
|
+
# delegating_for - Pepper::Context::Base instance
|
272
|
+
# slices - array of path fragments
|
273
|
+
#
|
274
|
+
def delegate!(delegating_for, slices)
|
275
|
+
@app=delegating_for.app
|
276
|
+
@parent=delegating_for.parent
|
277
|
+
# Set the @pattern to the delegating_for's value so that when resolve is called, there will be a match
|
278
|
+
@pattern=delegating_for.value
|
279
|
+
# add this delegate to the delegating_for.parent.children
|
280
|
+
delegating_for.parent.children.unshift self
|
281
|
+
# find the matching context based on the slices
|
282
|
+
return resolve(slices)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
#
|
289
|
+
# The base context class that gets instantiated for each "map" call
|
290
|
+
#
|
291
|
+
class Base
|
292
|
+
|
293
|
+
include Context::InstanceMethods
|
294
|
+
|
295
|
+
#
|
296
|
+
#
|
297
|
+
#
|
298
|
+
def initialize(pattern, options={}, delegate=nil, parent=nil, &block)
|
299
|
+
@pattern=pattern
|
300
|
+
@options=options
|
301
|
+
@delegate=delegate
|
302
|
+
@block=block
|
303
|
+
@parent=parent
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
|
310
|
+
#########
|
311
|
+
|
312
|
+
module App
|
313
|
+
|
314
|
+
#
|
315
|
+
# Shortcut for:
|
316
|
+
# include Pepper::App::InstanceMethods
|
317
|
+
# extend Pepper::App::ClassMethods
|
318
|
+
#
|
319
|
+
def self.included(base)
|
320
|
+
base.extend ClassMethods
|
321
|
+
base.send :include, InstanceMethods
|
322
|
+
end
|
323
|
+
|
324
|
+
#
|
325
|
+
# The class methods; provides the main "build" method
|
326
|
+
#
|
327
|
+
module ClassMethods
|
328
|
+
|
329
|
+
attr :root
|
330
|
+
|
331
|
+
#
|
332
|
+
# The initial build, class method
|
333
|
+
#
|
334
|
+
def build(options={}, &block)
|
335
|
+
@root=Context::Base.new('/', options, nil, nil, &block)
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
#
|
341
|
+
# The interface for Rack to connect to using the +call+ method
|
342
|
+
#
|
343
|
+
module InstanceMethods
|
344
|
+
|
345
|
+
class Error404 < RuntimeError; end
|
346
|
+
class ErrorMethodNotSupported < RuntimeError; end
|
347
|
+
class ErrorRootContextNotFound < RuntimeError; end
|
348
|
+
|
349
|
+
def root; self.class.root end
|
350
|
+
def request; @request end
|
351
|
+
|
352
|
+
def response; @response; end
|
353
|
+
|
354
|
+
def header(name, value)
|
355
|
+
response[name]=value
|
356
|
+
end
|
357
|
+
|
358
|
+
def headers(hash)
|
359
|
+
hash.each_pair {|k,v|header(k,v)}
|
360
|
+
end
|
361
|
+
|
362
|
+
def call(rack_env)
|
363
|
+
@response=Rack::Response.new
|
364
|
+
@request=Request.new(rack_env)
|
365
|
+
raise ErrorRootContextNotFound if root.nil?
|
366
|
+
root.app=self
|
367
|
+
context = root.resolve(request.path_info_slices)
|
368
|
+
raise Error404 if context.nil?
|
369
|
+
action = context.find_action(request.method_sym, request.env)
|
370
|
+
raise ErrorMethodNotSupported if action.nil?
|
371
|
+
response.write action.execute!
|
372
|
+
response.finish
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
data/lib/pepper/demo.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Pepper::Demo
|
2
|
+
|
3
|
+
module Canning
|
4
|
+
include Pepper::Context::Delegate
|
5
|
+
build do
|
6
|
+
get do
|
7
|
+
'CANNING PEPPERS: Small peppers may be left whole; large peppers may be quartered. Remove cores and seeds; blister and peel peppers; flatten whole peppers. Add ½ teaspoon of salt to each pint jar, if desired. Fill jars loosely with peppers and add fresh boiled water, leaving 1-inch headspace. Adjust lids and process half-pints or pints for 35 minutes at 11 pounds pressure in a dial gauge canner or at 10 pounds pressure in a weighted gauge canner.'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Pickled
|
13
|
+
|
14
|
+
include Pepper::App
|
15
|
+
|
16
|
+
build do
|
17
|
+
|
18
|
+
header 'Content-Type', 'text/html'
|
19
|
+
|
20
|
+
get do
|
21
|
+
header 'Content-Type', 'text/plain'
|
22
|
+
'CAUTION: When working with hot peppers, wear plastic gloves while handling them, or wash hands thoroughly with soap and water before touching your face.'
|
23
|
+
end
|
24
|
+
|
25
|
+
post{'We are creating a pickled pepper just for you. You will learn to love her.'}
|
26
|
+
put{'Adding a little more vinegar... removing some fuzz here and there.'}
|
27
|
+
delete{'Bad! Bad! Bad pickled pepper!'}
|
28
|
+
|
29
|
+
map :peter do
|
30
|
+
get{"I am Peter, the pickled-pepper-packer, I want to tell you my story."}
|
31
|
+
map :piper do
|
32
|
+
map :packed do
|
33
|
+
map :a do
|
34
|
+
map :pack do
|
35
|
+
map :of do
|
36
|
+
map :pickled do
|
37
|
+
map :peppers do
|
38
|
+
get{'zwiggle'}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
map(:how_to_can) {delegate_to Canning}
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/common.rb
ADDED
data/spec/demo.spec.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'common.rb')
|
2
|
+
require 'pepper/demo'
|
3
|
+
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
describe Pepper::Demo::Pickled do
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@app_class = Pepper::Demo::Pickled
|
11
|
+
@app=@app_class.new
|
12
|
+
end
|
13
|
+
|
14
|
+
it '(class) should respond to build' do
|
15
|
+
@app_class.should respond_to(:build)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should respond to :call" do
|
19
|
+
@app.should respond_to(:call)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have a nil :request attribute until :call is executed" do
|
23
|
+
@app.request.should == nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should have a :request attribute after calling the call method" do
|
27
|
+
@app.call(new_rack_env)
|
28
|
+
@app.should respond_to(:request)
|
29
|
+
@app.request.class.should == Pepper::Request
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not fail when requesting a POST, GET, PUT or DELETE to /" do
|
33
|
+
r=@app.call(new_rack_env)
|
34
|
+
r[0].should == 200
|
35
|
+
r[1].should == {'Content-Type'=>'text/plain'}
|
36
|
+
r[2].body.should == ['CAUTION: When working with hot peppers, wear plastic gloves while handling them, or wash hands thoroughly with soap and water before touching your face.']
|
37
|
+
#
|
38
|
+
r=@app.call(new_rack_env('/', {'REQUEST_METHOD'=>'POST'}))
|
39
|
+
r[0].should == 200
|
40
|
+
r[1].should == {'Content-Type'=>'text/html'}
|
41
|
+
r[2].body.should == ['We are creating a pickled pepper just for you. You will learn to love her.']
|
42
|
+
#
|
43
|
+
r=@app.call(new_rack_env('/', {'REQUEST_METHOD'=>'PUT'}))
|
44
|
+
r[0].should == 200
|
45
|
+
r[1].should == {'Content-Type'=>'text/html'}
|
46
|
+
r[2].body.should == ['Adding a little more vinegar... removing some fuzz here and there.']
|
47
|
+
#
|
48
|
+
r=@app.call(new_rack_env('/', {'REQUEST_METHOD'=>'DELETE'}))
|
49
|
+
r[0].should == 200
|
50
|
+
r[1].should == {'Content-Type'=>'text/html'}
|
51
|
+
r[2].body.should == ['Bad! Bad! Bad pickled pepper!']
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should raise Error404 when requesting an invalid path" do
|
55
|
+
lambda {@app.call(new_rack_env('/blah'))}.should raise_error(@app.class::Error404)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should raise ErrorMethodNotSupported when requesting a valid path, but no matching request method action" do
|
59
|
+
lambda {@app.call(new_rack_env('/peter', {'REQUEST_METHOD'=>'DELETE'}))}.should raise_error(@app.class::ErrorMethodNotSupported)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should support unlimited nesting depth' do
|
63
|
+
@app.call(new_rack_env('/peter/piper/packed/a/pack/of/pickled/peppers'))[2].body.should == ['zwiggle']
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should support delegating from one context block to another' do
|
67
|
+
@app.call(new_rack_env('/how_to_can'))[2].body.should == ['CANNING PEPPERS: Small peppers may be left whole; large peppers may be quartered. Remove cores and seeds; blister and peel peppers; flatten whole peppers. Add ½ teaspoon of salt to each pint jar, if desired. Fill jars loosely with peppers and add fresh boiled water, leaving 1-inch headspace. Adjust lids and process half-pints or pints for 35 minutes at 11 pounds pressure in a dial gauge canner or at 10 pounds pressure in a weighted gauge canner.']
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'common.rb')
|
2
|
+
|
3
|
+
#
|
4
|
+
#
|
5
|
+
#
|
6
|
+
describe Pepper::Request do
|
7
|
+
it '(path_info_fragments return) should return an array' do
|
8
|
+
r=Pepper::Request.new(new_rack_env('/'))
|
9
|
+
r.path_info_slices.class.should == Array
|
10
|
+
end
|
11
|
+
|
12
|
+
it '(path_info_fragments return) should always start with a /' do
|
13
|
+
r=Pepper::Request.new(new_rack_env('a/test//to/a/path/'))
|
14
|
+
r.path_info_slices[0].should == '/'
|
15
|
+
r=Pepper::Request.new(new_rack_env('/a/test//to/a/path/'))
|
16
|
+
r.path_info_slices[0].should == '/'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should remove duplicate slashes when responding to path_info_fragments' do
|
20
|
+
r=Pepper::Request.new(new_rack_env('a/test//to/a/path/'))
|
21
|
+
r.path_info_slices.should == ['/', 'a', 'test', 'to', 'a', 'path']
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mwmitchell-pepper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Mitchell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-06-11 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rack
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - "="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.3.0
|
23
|
+
version:
|
24
|
+
description: Pepper is a Ruby, DSL based framework for creating RESTful web applications. It adheres to the Ruby Rack specification.
|
25
|
+
email: goodieboy@gmail.com
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README
|
32
|
+
- LICENSE
|
33
|
+
files:
|
34
|
+
- lib/pepper.rb
|
35
|
+
- lib/pepper/demo.rb
|
36
|
+
- README
|
37
|
+
- LICENSE
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/mwmitchell/pepper
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --main
|
43
|
+
- README
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.0.1
|
62
|
+
signing_key:
|
63
|
+
specification_version: 2
|
64
|
+
summary: Pepper is a Ruby, DSL based framework for creating RESTful web applications. It adheres to the Ruby Rack specification.
|
65
|
+
test_files:
|
66
|
+
- spec/common.rb
|
67
|
+
- spec/demo.spec.rb
|
68
|
+
- spec/request.spec.rb
|