mwmitchell-snap 0.5.0 → 0.6.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 +4 -4
- data/README +53 -126
- data/bin/snap +4 -0
- data/lib/core_ext.rb +108 -0
- data/lib/snap.rb +23 -18
- data/lib/snap/context.rb +19 -5
- data/lib/snap/request.rb +43 -4
- data/lib/snap/response_helpers.rb +89 -0
- metadata +16 -13
- data/CHANGES +0 -0
- data/examples/demo.rb +0 -84
- data/lib/snap/app.rb +0 -56
- data/lib/snap/context/actions.rb +0 -64
- data/lib/snap/context/base.rb +0 -180
- data/lib/snap/context/events.rb +0 -43
- data/lib/snap/context/matcher.rb +0 -99
- data/lib/snap/demo.rb +0 -169
data/LICENSE
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
Copyright (c) 2008 Matt Mitchell - goodieboy@gmail.com
|
2
|
-
|
2
|
+
|
3
3
|
Permission is hereby granted, free of charge, to any person
|
4
4
|
obtaining a copy of this software and associated documentation
|
5
5
|
files (the "Software"), to deal in the Software without
|
@@ -8,10 +8,10 @@ copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
8
|
copies of the Software, and to permit persons to whom the
|
9
9
|
Software is furnished to do so, subject to the following
|
10
10
|
conditions:
|
11
|
-
|
11
|
+
|
12
12
|
The above copyright notice and this permission notice shall be
|
13
13
|
included in all copies or substantial portions of the Software.
|
14
|
-
|
14
|
+
|
15
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
16
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
17
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
@@ -19,4 +19,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
19
19
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
20
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
21
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
-
OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
CHANGED
@@ -1,132 +1,59 @@
|
|
1
|
-
=Snap
|
1
|
+
= Snap - REST made easy
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
==Examples
|
6
|
-
|
7
|
-
# Simple GET/POST handler for /
|
8
|
-
class GetPost
|
9
|
-
extend Snap::App
|
10
|
-
map do
|
11
|
-
get{}
|
12
|
-
post{}
|
13
|
-
end
|
14
|
-
end
|
3
|
+
== Overview
|
4
|
+
Snap is a small web framework for creating RESTful web applications. The heart of Snap is the controller layer; a contextual, block based DSL featuring RESTful action hooks, an event filter system and url param mapping.
|
15
5
|
|
16
|
-
|
17
|
-
class GetPost
|
18
|
-
extend Snap::App
|
19
|
-
map do
|
20
|
-
map :contact do
|
21
|
-
get{}
|
22
|
-
post{}
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
6
|
+
The start of any Snap application is a root Context. Contexts are sub-slices of a url and help keep routes DRY. The Context is responsible for defining the base URL path for it's actions, filters and child Contexts. One Context is equal to a set of URL path fragments. For example, a context could be /admin/transactions. Within that Context, there can be multiple actions, filters and sub-contexts.
|
26
7
|
|
27
|
-
|
8
|
+
The request and before/after handlers are able to negotiate different formats of content using the :format method within an action block.
|
28
9
|
|
29
|
-
|
10
|
+
Snap is very similar to Sinatra in that each action is a simple block. Snap is a little more structured and flexible than Sinatra. This can be a disadvantage or an advantage depending on the type of app you're trying to build.
|
30
11
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
could translate to a context block tree of:
|
38
|
-
|
39
|
-
<pre>
|
40
|
-
<code>
|
41
|
-
map 'users' do
|
42
|
-
map :digit do
|
43
|
-
get{"The value of this user is #{value}"}
|
44
|
-
map 'images' do
|
45
|
-
map :digit do
|
46
|
-
get{"The ID of this image is #{value}"}
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
</code>
|
52
|
-
</pre>
|
53
|
-
|
54
|
-
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:
|
55
|
-
|
56
|
-
<pre>
|
57
|
-
<code>
|
58
|
-
map :user_id=>/\d+/ do
|
59
|
-
puts params[:user_id]
|
60
|
-
end
|
61
|
-
</code>
|
62
|
-
</pre>
|
63
|
-
|
64
|
-
The :user_id param will then be available as a global request param to other contexts and "actions".
|
65
|
-
|
66
|
-
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.
|
67
|
-
|
68
|
-
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.
|
69
|
-
|
70
|
-
Here is an example:
|
71
|
-
|
72
|
-
<pre>
|
73
|
-
<code>
|
74
|
-
require 'snap'
|
75
|
-
|
76
|
-
module SeriousApp
|
77
|
-
|
78
|
-
class Contact # A delegate/sub-app
|
79
|
-
|
80
|
-
extend Snap::App
|
81
|
-
|
82
|
-
build do
|
83
|
-
before :post do
|
84
|
-
# a before :post block. Default is :all.
|
85
|
-
end
|
86
|
-
|
87
|
-
get{'GET request'}
|
88
|
-
post{'POST request'}
|
89
|
-
|
90
|
-
# request for /contact/:word
|
91
|
-
map :word do
|
92
|
-
get{"GET request for /contact/#{value}"}
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
class Root
|
98
|
-
|
99
|
-
extend Snap::App
|
100
|
-
|
101
|
-
build do
|
102
|
-
get{'A simple GET to /'}
|
103
|
-
post{'A POST to /'}
|
104
|
-
|
105
|
-
# /about/
|
106
|
-
map 'about' do
|
107
|
-
get{'GET request for /about'}
|
108
|
-
end
|
109
|
-
|
110
|
-
# /notes
|
111
|
-
map 'notes' do
|
112
|
-
get(:server_name=>/^myspecialdomain\.com$/) do
|
113
|
-
'GET request for /notes, but only allowing a SERVER_NAME of "myspecialdomain.com"'}
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# forwarding /contact to the Contact class
|
118
|
-
map 'contact' { use Contact }
|
119
|
-
end
|
120
|
-
|
121
|
-
end
|
122
|
-
end
|
123
|
-
</code>
|
124
|
-
</pre>
|
125
|
-
|
126
|
-
You then instantiate the Root class, and pass it to Rack's "run" method.
|
12
|
+
* Thanks to Merb and Sinatra for the little bits of code here and there
|
13
|
+
|
14
|
+
== Example
|
15
|
+
Here is a simple example:
|
16
|
+
|
17
|
+
module SimpleApp; end
|
127
18
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
19
|
+
class SimpleApp::Root
|
20
|
+
|
21
|
+
include Snap::App
|
22
|
+
|
23
|
+
map do
|
24
|
+
|
25
|
+
after do
|
26
|
+
nav = <<-EOF
|
27
|
+
<ul>
|
28
|
+
<li><a href="/">home</a></li>
|
29
|
+
<li><a href="/about">about</a></li>
|
30
|
+
<li><a href="/about/ruby">about ruby</a></li>
|
31
|
+
<li><a href="/markaby">markaby example</a></li>
|
32
|
+
</ul>
|
33
|
+
EOF
|
34
|
+
response.body = "<h1>Snap!</h1><hr/>#{nav}#{response.body}<hr/>"
|
35
|
+
end
|
36
|
+
|
37
|
+
get {'The root path using GET'}
|
38
|
+
|
39
|
+
get :markaby=>'markaby' do
|
40
|
+
markaby.div do
|
41
|
+
h3 "Render your HTML with Markaby?"
|
42
|
+
ul do
|
43
|
+
li "markaby is cool"
|
44
|
+
li "It's fun to put view logic in your controllers :)"
|
45
|
+
li "testing..."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
map 'about' do
|
51
|
+
get {"Snap is an experimental framework?!"}
|
52
|
+
get 'ruby' do
|
53
|
+
'Ruby -> http://www.ruby-lang.org'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/bin/snap
ADDED
data/lib/core_ext.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
class Symbol
|
2
|
+
def to_proc
|
3
|
+
proc { |obj, *args| obj.send(self, *args) }
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class Proc
|
8
|
+
|
9
|
+
def source
|
10
|
+
self.to_s.scan(/@(.*)>/)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class String
|
16
|
+
|
17
|
+
# Converts +self+ to an escaped URI parameter value
|
18
|
+
# 'Foo Bar'.to_param # => 'Foo%20Bar'
|
19
|
+
def to_param
|
20
|
+
URI.escape(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Converts +self+ from an escaped URI parameter value
|
24
|
+
# 'Foo%20Bar'.from_param # => 'Foo Bar'
|
25
|
+
def from_param
|
26
|
+
URI.unescape(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# :trim and :trim!
|
31
|
+
# Similar to PHP's trim function
|
32
|
+
# Aceepts multiple arguments for characters to trim
|
33
|
+
# from the beginning of the string and the end
|
34
|
+
#
|
35
|
+
%W(trim trim!).each do |m|
|
36
|
+
define_method m do |*args|
|
37
|
+
raise 'Characters argument missing' unless (chars=args.join(' '))
|
38
|
+
p = [/^[#{Regexp.escape(chars)}]+|[#{Regexp.escape(chars)}]+$/, '']
|
39
|
+
m[-1..-1] == '!' ? self.gsub!(*p) : self.gsub(*p)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Removes duplicate instances of characters
|
45
|
+
#
|
46
|
+
%W(dedup dedup!).each do |m|
|
47
|
+
define_method m do |*args|
|
48
|
+
raise 'Character argument missing' if args.nil?
|
49
|
+
value=self
|
50
|
+
args.each do |char|
|
51
|
+
p = [/#{Regexp.escape(char)}+/, char]
|
52
|
+
m[-1..-1] == '!' ? self.gsub!(*p) : (value = value.gsub(*p))
|
53
|
+
end
|
54
|
+
value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# removes starting and ending instances of the characters argument (calls :trim)
|
60
|
+
# replaces sets of duplicated characters with a single character (calls :dedup)
|
61
|
+
#
|
62
|
+
# puts 'testtktu'.cleanup('t', 'u') == 'estk'
|
63
|
+
#
|
64
|
+
%W(cleanup cleanup!).each do |m|
|
65
|
+
define_method m do |*args|
|
66
|
+
m[-1..-1] == '!' ? (trim!(*args) && dedup!(*args)) : (trim(*args).dedup(*args))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class Hash
|
73
|
+
|
74
|
+
def deep_merge(hash)
|
75
|
+
target = dup
|
76
|
+
hash.keys.each do |key|
|
77
|
+
if hash[key].is_a? Hash and self[key].is_a? Hash
|
78
|
+
target[key] = target[key].deep_merge(hash[key])
|
79
|
+
next
|
80
|
+
end
|
81
|
+
target[key] = hash[key]
|
82
|
+
end
|
83
|
+
target
|
84
|
+
end
|
85
|
+
|
86
|
+
def deep_merge!(second)
|
87
|
+
second.each_pair do |k,v|
|
88
|
+
if self[k].is_a?(Hash) and second[k].is_a?(Hash)
|
89
|
+
self[k].deep_merge!(second[k])
|
90
|
+
else
|
91
|
+
self[k] = second[k]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_params
|
97
|
+
map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
|
98
|
+
end
|
99
|
+
|
100
|
+
def symbolize_keys
|
101
|
+
self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
|
102
|
+
end
|
103
|
+
|
104
|
+
def pass(*keys)
|
105
|
+
reject { |k,v| !keys.include?(k) }
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
data/lib/snap.rb
CHANGED
@@ -1,27 +1,32 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
$:.
|
4
|
-
|
1
|
+
lambda do |p|
|
2
|
+
$:.unshift p unless
|
3
|
+
$:.include?(p) or $:.include?(File.expand_path(p))
|
4
|
+
end.call(File.dirname(__FILE__))
|
5
5
|
|
6
6
|
require 'rubygems'
|
7
7
|
require 'rack'
|
8
|
+
require 'core_ext'
|
8
9
|
|
9
|
-
|
10
|
+
module Snap
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
module Version
|
13
|
+
MAJOR = '0'
|
14
|
+
MINOR = '4'
|
15
|
+
REVISION = '1'
|
16
|
+
def self.combined
|
17
|
+
[MAJOR, MINOR, REVISION].join('.')
|
18
|
+
end
|
16
19
|
end
|
17
20
|
|
18
21
|
end
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
%W(
|
24
|
+
config
|
25
|
+
context
|
26
|
+
initializer
|
27
|
+
response_helpers
|
28
|
+
request
|
29
|
+
zone
|
30
|
+
view_helper
|
31
|
+
app
|
32
|
+
).each{|p|require "snap/#{p}"}
|
data/lib/snap/context.rb
CHANGED
@@ -1,9 +1,23 @@
|
|
1
1
|
#
|
2
|
-
#
|
2
|
+
# The "Context" is essentially the current request
|
3
3
|
#
|
4
4
|
module Snap::Context
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
|
6
|
+
# these will be accessable thru Snap::Context
|
7
|
+
M=%W(request response config)
|
8
|
+
|
9
|
+
# class instance attributes (ex. Snap::Context.config etc.)
|
10
|
+
class << self
|
11
|
+
M.each{|meth| attr_accessor meth}
|
12
|
+
end
|
13
|
+
|
14
|
+
# This makes the above methods available to the classes that "include Snap::Context"
|
15
|
+
M.each do |meth|
|
16
|
+
class_eval <<-EOF
|
17
|
+
def #{meth}(*args)
|
18
|
+
Snap::Context.send(:#{meth}, *args)
|
19
|
+
end
|
20
|
+
EOF
|
21
|
+
end
|
22
|
+
|
9
23
|
end
|
data/lib/snap/request.rb
CHANGED
@@ -1,11 +1,50 @@
|
|
1
1
|
class Snap::Request < Rack::Request
|
2
2
|
|
3
|
-
def
|
4
|
-
|
3
|
+
def initialize(env)
|
4
|
+
super(env)
|
5
|
+
pi=env['PATH_INFO'].to_s.split('.')
|
6
|
+
@path_info=pi.first.cleanup('/')
|
7
|
+
@format=pi.size==2 ? pi.last.to_sym : default_format
|
8
|
+
@request_method=env['REQUEST_METHOD'].downcase.to_sym
|
9
|
+
if post_tunnel_method_hack?
|
10
|
+
@request_method = params['_method'].downcase.to_sym
|
11
|
+
end
|
5
12
|
end
|
6
13
|
|
7
|
-
def
|
8
|
-
|
14
|
+
def default_format; :html end
|
15
|
+
|
16
|
+
def format
|
17
|
+
@format
|
18
|
+
end
|
19
|
+
|
20
|
+
def path_info
|
21
|
+
@path_info
|
9
22
|
end
|
10
23
|
|
24
|
+
# Set of request method names allowed via the _method parameter hack. By default,
|
25
|
+
# all request methods defined in RFC2616 are included, with the exception of
|
26
|
+
# TRACE and CONNECT.
|
27
|
+
POST_TUNNEL_METHODS_ALLOWED = [:put, :delete, :options, :head]
|
28
|
+
|
29
|
+
# Return the HTTP request method with support for method tunneling using the POST
|
30
|
+
# _method parameter hack. If the real request method is POST and a _method param is
|
31
|
+
# given and the value is one defined in +POST_TUNNEL_METHODS_ALLOWED+, return the value
|
32
|
+
# of the _method param instead.
|
33
|
+
def request_method
|
34
|
+
@request_method
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Return truthfully if and only if the following conditions are met: 1.) the
|
40
|
+
# *actual* request method is POST, 2.) the request content-type is one of
|
41
|
+
# 'application/x-www-form-urlencoded' or 'multipart/form-data', 3.) there is a
|
42
|
+
# "_method" parameter in the POST body (not in the query string), and 4.) the
|
43
|
+
# method parameter is one of the verbs listed in the POST_TUNNEL_METHODS_ALLOWED
|
44
|
+
# list.
|
45
|
+
def post_tunnel_method_hack?
|
46
|
+
@request_method == :post &&
|
47
|
+
POST_TUNNEL_METHODS_ALLOWED.include?(self.POST.fetch('_method', '').downcase.to_sym)
|
48
|
+
end
|
49
|
+
|
11
50
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Snap::ResponseHelpers
|
2
|
+
|
3
|
+
class FormatMapper
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@formats={}
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(m,&block)
|
10
|
+
# check with mime-types here...
|
11
|
+
@formats[m]=block
|
12
|
+
end
|
13
|
+
|
14
|
+
def resolve(format)
|
15
|
+
@formats[format]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def format_mapper
|
21
|
+
@format_mapper||=FormatMapper.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def format
|
25
|
+
format_mapper
|
26
|
+
end
|
27
|
+
|
28
|
+
def redirect(path, *args)
|
29
|
+
response.status=302
|
30
|
+
headers 'Location' => path
|
31
|
+
throw :halt, *args
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop(*args)
|
35
|
+
throw :halt, args
|
36
|
+
end
|
37
|
+
|
38
|
+
def response
|
39
|
+
@response
|
40
|
+
end
|
41
|
+
|
42
|
+
def headers(header = nil)
|
43
|
+
response.headers.merge!(header) if header
|
44
|
+
response.headers
|
45
|
+
end
|
46
|
+
|
47
|
+
alias :header :headers
|
48
|
+
|
49
|
+
def render(template=nil, variables={}, options={})
|
50
|
+
options[:renderer]||=config.default_renderer
|
51
|
+
m = "render_#{options[:renderer]}"
|
52
|
+
config.view_paths.each do |p|
|
53
|
+
template = File.read(File.join(p, "#{template}.#{request.format}.erb")) rescue nil
|
54
|
+
end
|
55
|
+
render_erb([self], erb_helpers, template, variables)
|
56
|
+
end
|
57
|
+
|
58
|
+
def erb_helpers
|
59
|
+
[Snap::ViewHelper::Erubis]
|
60
|
+
end
|
61
|
+
|
62
|
+
def render_erb(variable_bindings, extensions, template, data={})
|
63
|
+
variable_bindings.each do |o|
|
64
|
+
o.instance_variables.each do |k|
|
65
|
+
data[k[1..-1]]=o.instance_variable_get(k)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
context=Object.new
|
69
|
+
extensions.each do |e|
|
70
|
+
context.extend e
|
71
|
+
end
|
72
|
+
data.each do |k,v|
|
73
|
+
context.instance_variable_set("@#{k}", v)
|
74
|
+
end
|
75
|
+
require 'erubis_ext'
|
76
|
+
::Erubis::BlockAwareEruby.new(nil, :trim=>false).process(template, context)
|
77
|
+
end
|
78
|
+
|
79
|
+
def render_haml(content, options = {}, &b)
|
80
|
+
require 'haml'
|
81
|
+
::Haml::Engine.new(content).render(options[:scope] || self, options[:locals] || {}, &b)
|
82
|
+
end
|
83
|
+
|
84
|
+
def markaby
|
85
|
+
require 'markaby'
|
86
|
+
Markaby::Builder.new
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mwmitchell-snap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Mitchell
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-06
|
12
|
+
date: 2008-08-06 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -19,9 +19,9 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - "="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.
|
22
|
+
version: 0.4.0
|
23
23
|
version:
|
24
|
-
description: Snap
|
24
|
+
description: Snap - a minimalist framework for creating RESTful web applications
|
25
25
|
email: goodieboy@gmail.com
|
26
26
|
executables: []
|
27
27
|
|
@@ -33,17 +33,20 @@ extra_rdoc_files:
|
|
33
33
|
files:
|
34
34
|
- README
|
35
35
|
- LICENSE
|
36
|
-
-
|
37
|
-
-
|
36
|
+
- bin/snap
|
37
|
+
- lib/core_ext.rb
|
38
38
|
- lib/snap.rb
|
39
|
-
- lib/snap/
|
39
|
+
- lib/snap/action.rb
|
40
|
+
- lib/snap/context/action_methods.rb
|
41
|
+
- lib/snap/context/hooks.rb
|
40
42
|
- lib/snap/context.rb
|
41
|
-
- lib/snap/
|
43
|
+
- lib/snap/event.rb
|
44
|
+
- lib/snap/loader.rb
|
45
|
+
- lib/snap/negotiator.rb
|
46
|
+
- lib/snap/rack_runner.rb
|
47
|
+
- lib/snap/renderers.rb
|
42
48
|
- lib/snap/request.rb
|
43
|
-
- lib/snap/
|
44
|
-
- lib/snap/context/actions.rb
|
45
|
-
- lib/snap/context/events.rb
|
46
|
-
- lib/snap/context/matcher.rb
|
49
|
+
- lib/snap/response_helpers.rb
|
47
50
|
has_rdoc: true
|
48
51
|
homepage: http://github.com/mwmitchell/snap
|
49
52
|
post_install_message:
|
@@ -70,6 +73,6 @@ rubyforge_project:
|
|
70
73
|
rubygems_version: 1.2.0
|
71
74
|
signing_key:
|
72
75
|
specification_version: 2
|
73
|
-
summary: Snap
|
76
|
+
summary: Snap - a minimalist framework for creating RESTful web applications
|
74
77
|
test_files: []
|
75
78
|
|
data/CHANGES
DELETED
File without changes
|
data/examples/demo.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'snap')
|
2
|
-
|
3
|
-
app=Snap::Demo::Controllers::Root.new
|
4
|
-
|
5
|
-
rack_env=Rack::MockRequest.env_for('/admin/customers', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
6
|
-
puts app.call(rack_env)
|
7
|
-
|
8
|
-
puts "
|
9
|
-
...
|
10
|
-
|
11
|
-
"
|
12
|
-
|
13
|
-
|
14
|
-
rack_env=Rack::MockRequest.env_for('/admin/customers/henry', {'REQUEST_METHOD'=>'PUT', 'SERVER_PORT'=>'80'})
|
15
|
-
puts app.call(rack_env)
|
16
|
-
|
17
|
-
puts "
|
18
|
-
...
|
19
|
-
|
20
|
-
"
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
rack_env=Rack::MockRequest.env_for('/', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
25
|
-
puts app.call(rack_env)
|
26
|
-
|
27
|
-
puts "
|
28
|
-
...
|
29
|
-
|
30
|
-
"
|
31
|
-
|
32
|
-
rack_env=Rack::MockRequest.env_for('/admin', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
33
|
-
puts app.call(rack_env)
|
34
|
-
|
35
|
-
puts "
|
36
|
-
...
|
37
|
-
|
38
|
-
"
|
39
|
-
|
40
|
-
rack_env=Rack::MockRequest.env_for('/admin/orders', {'REQUEST_METHOD'=>'POST', 'SERVER_PORT'=>'80'})
|
41
|
-
puts app.call(rack_env)
|
42
|
-
|
43
|
-
puts "
|
44
|
-
...
|
45
|
-
|
46
|
-
"
|
47
|
-
|
48
|
-
|
49
|
-
rack_env=Rack::MockRequest.env_for('/admin/products', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
50
|
-
puts app.call(rack_env)
|
51
|
-
|
52
|
-
puts "
|
53
|
-
...
|
54
|
-
|
55
|
-
"
|
56
|
-
|
57
|
-
rack_env=Rack::MockRequest.env_for('/contact', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
58
|
-
puts app.call(rack_env)
|
59
|
-
|
60
|
-
puts "
|
61
|
-
...
|
62
|
-
|
63
|
-
"
|
64
|
-
|
65
|
-
rack_env=Rack::MockRequest.env_for('/contact/the-boss', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
66
|
-
puts app.call(rack_env)
|
67
|
-
|
68
|
-
puts "
|
69
|
-
...
|
70
|
-
|
71
|
-
"
|
72
|
-
|
73
|
-
rack_env=Rack::MockRequest.env_for('/admin/products/144/edit', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
74
|
-
puts app.call(rack_env)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
puts "
|
79
|
-
...
|
80
|
-
|
81
|
-
"
|
82
|
-
|
83
|
-
rack_env=Rack::MockRequest.env_for('/admin/products/144', {'REQUEST_METHOD'=>'GET', 'SERVER_PORT'=>'80'})
|
84
|
-
puts app.call(rack_env)
|
data/lib/snap/app.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
|
-
|
5
|
-
require 'monitor'
|
6
|
-
|
7
|
-
module Snap::App
|
8
|
-
|
9
|
-
attr :root
|
10
|
-
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
def map(options={}, &block)
|
15
|
-
@root=Snap::Context::Base.new('/', options, nil, &block)
|
16
|
-
end
|
17
|
-
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
def self.extended(base)
|
22
|
-
|
23
|
-
base.class_eval do
|
24
|
-
|
25
|
-
#
|
26
|
-
# Provides sync functionality when using Threads and shared resources
|
27
|
-
#
|
28
|
-
include MonitorMixin
|
29
|
-
|
30
|
-
attr_accessor :config
|
31
|
-
attr_accessor :request
|
32
|
-
attr_accessor :response
|
33
|
-
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
def call(rack_env)
|
38
|
-
synchronize do
|
39
|
-
@request = Snap::Request.new(rack_env)
|
40
|
-
@response = Rack::Response.new
|
41
|
-
c=self.class.root.resolve(@request.path_info_slices, self)
|
42
|
-
result = c.execute if c
|
43
|
-
response.finish
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class Base
|
51
|
-
|
52
|
-
extend Snap::App
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
data/lib/snap/context/actions.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
module Snap::Context::Actions
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
def index(options={}, &block)
|
7
|
-
get(options, &block)
|
8
|
-
end
|
9
|
-
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
def create(options={}, &block)
|
14
|
-
put(options, &block)
|
15
|
-
end
|
16
|
-
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
def new(options={}, &block)
|
21
|
-
map(:new) do
|
22
|
-
get(options, &block)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
def show(pattern=:digit, options={}, &block)
|
30
|
-
map(pattern) do
|
31
|
-
get(options, &block)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
def update(pattern=:digit, options={}, &block)
|
39
|
-
map(pattern) do
|
40
|
-
put(options, &block)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
def delete(pattern=:digit, options={}, &block)
|
48
|
-
map(pattern) do
|
49
|
-
delete(options, &block)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
def edit(pattern={:id=>:digit}, options={}, &block)
|
57
|
-
map(pattern) do
|
58
|
-
map(:edit) do
|
59
|
-
get(options, &block)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
data/lib/snap/context/base.rb
DELETED
@@ -1,180 +0,0 @@
|
|
1
|
-
###
|
2
|
-
class Snap::Context::Base
|
3
|
-
|
4
|
-
include Snap::Context::Events
|
5
|
-
include Snap::Context::Matcher
|
6
|
-
|
7
|
-
attr :pattern
|
8
|
-
attr :options
|
9
|
-
attr_accessor :parent
|
10
|
-
attr :block
|
11
|
-
attr :app
|
12
|
-
attr :slices
|
13
|
-
attr :value
|
14
|
-
|
15
|
-
def initialize(pattern, options={}, parent=nil, &block)
|
16
|
-
@pattern=pattern
|
17
|
-
@options=options
|
18
|
-
@parent=parent
|
19
|
-
@block=block
|
20
|
-
end
|
21
|
-
|
22
|
-
[:get,:post,:put,:delete].each do |m|
|
23
|
-
class_eval <<-EOF
|
24
|
-
def #{m}(options={},&block)
|
25
|
-
# don't add the action if the block is the same as existing action block
|
26
|
-
unless actions.detect{|a|a[2].source==block.source}
|
27
|
-
actions << [:#{m},options,block]
|
28
|
-
end
|
29
|
-
end
|
30
|
-
EOF
|
31
|
-
end
|
32
|
-
|
33
|
-
def app; @app end
|
34
|
-
def request; app.request end
|
35
|
-
def response; app.response end
|
36
|
-
def params; request.params end
|
37
|
-
def actions; @actions||=[] end
|
38
|
-
def children; @children||=[] end
|
39
|
-
|
40
|
-
#
|
41
|
-
# builds the url path to this context
|
42
|
-
#
|
43
|
-
def path
|
44
|
-
@path ||= ((parent ? parent.path : '') + '/' + value).gsub(/\/+/, '/')
|
45
|
-
end
|
46
|
-
|
47
|
-
#
|
48
|
-
# +use+ is the method that allows context to delegate
|
49
|
-
# to other contexts, alloing logic to be off-loaded
|
50
|
-
# to other classes. The klass argument
|
51
|
-
# must be the class name of an object that is
|
52
|
-
# using "extend Snap::App"
|
53
|
-
#
|
54
|
-
def use(klass,*args,&block)
|
55
|
-
usable_app=klass.new(*args,&block)
|
56
|
-
usable_app.class.root.parent=self
|
57
|
-
children<<usable_app
|
58
|
-
end
|
59
|
-
|
60
|
-
#
|
61
|
-
# Define a new sub-context block
|
62
|
-
# pattern can be a string, a symbol or a hash
|
63
|
-
#
|
64
|
-
def map(pattern,options={},&block)
|
65
|
-
# don't add the context if the block is the same as existing action block
|
66
|
-
return if children.detect{|c|c.block.source == block.source}
|
67
|
-
children<<self.class.new(pattern,options,self,&block)
|
68
|
-
end
|
69
|
-
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
def find_action(method, env={})
|
74
|
-
actions.detect{ |a| a[0] == method and options_match?(a[1], env)}
|
75
|
-
end
|
76
|
-
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
def can?(*args)
|
81
|
-
! find_action(*args).nil?
|
82
|
-
end
|
83
|
-
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
def method_missing(m,*a,&block)
|
88
|
-
puts "METHOD MISSING #{m.inspect}"
|
89
|
-
puts "APP IS #{@app} (should not be a context::base)"
|
90
|
-
@app.send(m,*a,&block)
|
91
|
-
end
|
92
|
-
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
def execute(method=request.m, env=request.env)
|
97
|
-
a=find_action(method,env)
|
98
|
-
result=nil
|
99
|
-
unless a.nil?
|
100
|
-
execute_before_and_after_blocks(method, env){result=instance_eval &a[2]}
|
101
|
-
end
|
102
|
-
result
|
103
|
-
end
|
104
|
-
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
def resolve(slices, app)
|
109
|
-
@app=app
|
110
|
-
|
111
|
-
method=app.request.m
|
112
|
-
env=app.request.env
|
113
|
-
|
114
|
-
#
|
115
|
-
# must clone the slices; in some cases we need to climb back up the context stack
|
116
|
-
# for example, if two contexts are defined with the same pattern
|
117
|
-
# but the first one doesn't define a matching action block,
|
118
|
-
# we need to be able to move back up to get the second one.
|
119
|
-
# if the slices aren't cloned, then the 2nd one
|
120
|
-
# would get a wrongly manipulated slices array - oh no!
|
121
|
-
#
|
122
|
-
slices=slices.clone
|
123
|
-
|
124
|
-
# try to match the @pattern with the given slices
|
125
|
-
# then extract the value
|
126
|
-
val = match?(@pattern,slices.first)
|
127
|
-
|
128
|
-
return unless val
|
129
|
-
|
130
|
-
#
|
131
|
-
# now make sure that the options can match the env
|
132
|
-
#
|
133
|
-
return unless options_match?(@options, env)
|
134
|
-
|
135
|
-
# if the returned value was a hash, merge it into the apps request.param hash
|
136
|
-
app.request.params.merge!(val) if val.is_a?(Hash)
|
137
|
-
|
138
|
-
#
|
139
|
-
# shift off the first item, save it as the value
|
140
|
-
# of this context this is needed so that the
|
141
|
-
# next context can have the next
|
142
|
-
# value in the slices array
|
143
|
-
#
|
144
|
-
@value=slices.shift
|
145
|
-
|
146
|
-
@slices=slices
|
147
|
-
|
148
|
-
result = catch :halt do
|
149
|
-
instance_eval &@block
|
150
|
-
end
|
151
|
-
|
152
|
-
#
|
153
|
-
# "and can?(method, env)" -> This seemed to allow falling back to an action that is a parent
|
154
|
-
#
|
155
|
-
return self if slices.empty?# and can?(method, env)
|
156
|
-
|
157
|
-
matching_child=nil
|
158
|
-
children.each do |c|
|
159
|
-
if c.is_a?(self.class)
|
160
|
-
# standard Snap::Context::Base instance set by +map+
|
161
|
-
matching_child=c.resolve(slices, app)
|
162
|
-
# matching_child.parent=self if matching_child
|
163
|
-
else
|
164
|
-
# if this is an object set by the +use+ method
|
165
|
-
# c is not a Snap::Context::Base; it
|
166
|
-
# should be a class calling "extend Snap::App"
|
167
|
-
# (a sort of sub-application)
|
168
|
-
# so it needs a root slash for it's
|
169
|
-
# own "root" context...
|
170
|
-
# set the sub-app's request and response instances
|
171
|
-
c.request=request
|
172
|
-
c.response=response
|
173
|
-
matching_child=c.class.root.resolve(['/']+slices,c)
|
174
|
-
end
|
175
|
-
return matching_child if matching_child
|
176
|
-
end
|
177
|
-
nil
|
178
|
-
end
|
179
|
-
|
180
|
-
end
|
data/lib/snap/context/events.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
|
-
module Snap::Context::Events
|
5
|
-
|
6
|
-
include Snap::Context::Matcher
|
7
|
-
|
8
|
-
def before_blocks; @before_blocks||={} end
|
9
|
-
def after_blocks; @after_blocks||={} end
|
10
|
-
|
11
|
-
def before(action=:all, options={}, &block)
|
12
|
-
before_blocks[action]=[options,block]
|
13
|
-
end
|
14
|
-
|
15
|
-
def after(action=:all, options={}, &block)
|
16
|
-
after_blocks[action]=[options,block]
|
17
|
-
end
|
18
|
-
|
19
|
-
def execute_before_and_after_blocks(method, options, &block)
|
20
|
-
execute_before_blocks method, options
|
21
|
-
yield
|
22
|
-
execute_after_blocks method, options
|
23
|
-
end
|
24
|
-
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
def execute_before_blocks(method, options={})
|
29
|
-
parent.execute_before_blocks(method, options) if parent
|
30
|
-
b=(@before_blocks[method] || @before_blocks[:all]) if @before_blocks
|
31
|
-
instance_eval(&b[1]) if b and options_match?(b[0], options)
|
32
|
-
end
|
33
|
-
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
def execute_after_blocks(method, options={})
|
38
|
-
b=(@after_blocks[method] || @after_blocks[:all]) if @after_blocks
|
39
|
-
instance_eval(&b[1]) if b and options_match?(b[0], options)
|
40
|
-
parent.execute_after_blocks(method, options) if parent
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
data/lib/snap/context/matcher.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
|
3
|
-
# TEST
|
4
|
-
|
5
|
-
include Snap::Context::Matcher
|
6
|
-
|
7
|
-
puts true == options_match?({}, {:server_name=>'localhost'})
|
8
|
-
|
9
|
-
puts true ==options_match?({:server_name=>'localhost'}, {:server_name=>'localhost'})
|
10
|
-
|
11
|
-
puts true == options_match?({:server_name=>/local/}, {:server_name=>'localhost'})
|
12
|
-
|
13
|
-
puts false == options_match?({:server_name=>/k/}, {:server_name=>'localhost'})
|
14
|
-
|
15
|
-
puts false == options_match?({:asdasd=>'x', :localhost=>'localhost'}, {:server_name=>'localhost'})
|
16
|
-
|
17
|
-
puts true == options_match?({}, {:server_name=>'localhost', :port=>8080})
|
18
|
-
|
19
|
-
puts false == options_match?({:port=>9}, {:port=>8080})
|
20
|
-
|
21
|
-
=end
|
22
|
-
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
module Snap::Context::Matcher
|
27
|
-
|
28
|
-
PATTERNS={
|
29
|
-
:digit=>/^\d+$/,
|
30
|
-
:word=>/^\w+$/
|
31
|
-
}
|
32
|
-
|
33
|
-
#
|
34
|
-
# Checks the pattern against the value
|
35
|
-
# The pattern can be a:
|
36
|
-
# * string - uses == to compare
|
37
|
-
# * regexp - uses =~ to compare
|
38
|
-
# * symbol - first looks up value in PATTERNS; if found, uses the PATTERN value; if not, converts to string
|
39
|
-
# * hash - assigns the value to the key within the params array if matches; the value can be any of the above
|
40
|
-
#
|
41
|
-
# ==Examples
|
42
|
-
# match?('hello', 'hello') == true
|
43
|
-
# match?(/h/, 'hello') == true
|
44
|
-
# match?(:digit, 1) == true - does a successfull lookup in PATTERNS
|
45
|
-
# match?(:admin, 'admin') == true - non-succesfull PATTERNS lookup, convert to string
|
46
|
-
# match?(:id=>:digit, 1) == true - also sets params[:id]=1
|
47
|
-
# match?(:name=>:word, 'sam') == true - sets params[:name]='sam'
|
48
|
-
# match?(:topic=>/^authors$|^publishers$/, 'publishers') - true - sets params[:topic]='publishers'
|
49
|
-
#
|
50
|
-
def match?(pattern, value)
|
51
|
-
ok = simple_match?(pattern, value)
|
52
|
-
return ok if ok
|
53
|
-
# if hash, use the value as the pattern and set the request param[key] to the value argument
|
54
|
-
return ({pattern.keys.first => match?(pattern[pattern.keys.first], value)}) if pattern.is_a?(Hash)
|
55
|
-
# if symbol
|
56
|
-
if pattern.is_a?(Symbol)
|
57
|
-
# lookup the symbol in the PATTERNS hash
|
58
|
-
if PATTERNS[pattern] and new_value=match?(PATTERNS[pattern], value)
|
59
|
-
return new_value
|
60
|
-
end
|
61
|
-
# There was no match, convert to string
|
62
|
-
simple_match?(pattern.to_s, value)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
#
|
67
|
-
# Non-nested comparisons using == or =~
|
68
|
-
#
|
69
|
-
def simple_match?(pattern, value)
|
70
|
-
# if regexp, regx comparison
|
71
|
-
return value if (pattern.is_a?(Regexp) and value.to_s =~ pattern)
|
72
|
-
# if string, simple comparison
|
73
|
-
return value if (pattern.is_a?(String) and value.to_s == pattern)
|
74
|
-
end
|
75
|
-
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
def options_match?(compare_from, compare_to)
|
80
|
-
if compare_from.to_s.empty?
|
81
|
-
return true
|
82
|
-
end
|
83
|
-
n={}
|
84
|
-
compare_to.each_pair{ |k,v| n[k.to_s.downcase.gsub('-','_').gsub(' ', '').gsub('.', '_').to_sym]=v }
|
85
|
-
matches=true
|
86
|
-
compare_from.each_pair do |k,v|
|
87
|
-
unless n.has_key?(k)
|
88
|
-
matches=false
|
89
|
-
break
|
90
|
-
end
|
91
|
-
unless match?(v,n[k]) or match?(v.to_s,n[k])
|
92
|
-
matches=false
|
93
|
-
break
|
94
|
-
end
|
95
|
-
end
|
96
|
-
matches
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
data/lib/snap/demo.rb
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
module Snap::Demo
|
2
|
-
|
3
|
-
module Controllers
|
4
|
-
|
5
|
-
module Admin
|
6
|
-
|
7
|
-
class Customers
|
8
|
-
|
9
|
-
extend Snap::App
|
10
|
-
|
11
|
-
map do
|
12
|
-
|
13
|
-
extend Snap::Context::Actions
|
14
|
-
|
15
|
-
index do
|
16
|
-
'Admin::Customers INDEX /admin/customers -> ' + path
|
17
|
-
end
|
18
|
-
|
19
|
-
update :customer_name=>/\w+/ do
|
20
|
-
app.response.write 'TESTING'
|
21
|
-
"Admin::Customers UPDATE /admin/customers/#{params[:customer_name]} -> " + path
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
class Products
|
29
|
-
|
30
|
-
extend Snap::App
|
31
|
-
|
32
|
-
map do
|
33
|
-
|
34
|
-
extend Snap::Context::Actions
|
35
|
-
|
36
|
-
before do
|
37
|
-
puts "Admin::Products BEFORE"
|
38
|
-
end
|
39
|
-
|
40
|
-
after do
|
41
|
-
puts "Admin::Products AFTER"
|
42
|
-
end
|
43
|
-
|
44
|
-
get{'Admin::Products GET /admin/products -> ' + path}
|
45
|
-
|
46
|
-
map :product_id=>:digit do
|
47
|
-
get do
|
48
|
-
out = "Admin::Products GET /admin/products/#{params[:product_id]} -> #{path}"
|
49
|
-
out += "\nThe parent path is #{parent.path}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
edit do
|
54
|
-
out = "Admin::Products EDIT /admin/products/#{params[:id]}/edit -> " + path
|
55
|
-
out += "\nThe parent path is #{parent.path}"
|
56
|
-
end
|
57
|
-
|
58
|
-
index do
|
59
|
-
'Admin::Products INDEX /admin/products'
|
60
|
-
end
|
61
|
-
|
62
|
-
new :server_name=>/example/ do
|
63
|
-
'Admin::Products NEW /admin/products'
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
|
71
|
-
class Contact
|
72
|
-
|
73
|
-
extend Snap::App
|
74
|
-
|
75
|
-
map do
|
76
|
-
|
77
|
-
before{puts "Contact BEFORE"}
|
78
|
-
after{puts "Contact AFTER"}
|
79
|
-
|
80
|
-
get{'Contact GET /'}
|
81
|
-
|
82
|
-
map 'the-boss' do
|
83
|
-
before do
|
84
|
-
puts 'Contact BEFORE /the-boss'
|
85
|
-
end
|
86
|
-
after do
|
87
|
-
puts 'Contact AFTER /the-boss'
|
88
|
-
end
|
89
|
-
get{'Contact GET /the-boss -> ' + path}
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
class Root
|
96
|
-
|
97
|
-
extend Snap::App
|
98
|
-
|
99
|
-
attr_accessor :count
|
100
|
-
|
101
|
-
map do
|
102
|
-
|
103
|
-
before do
|
104
|
-
puts 'Root BEFORE /'
|
105
|
-
end
|
106
|
-
|
107
|
-
after do
|
108
|
-
puts 'Root AFTER /'
|
109
|
-
end
|
110
|
-
|
111
|
-
get{'GET /'}
|
112
|
-
post{'POST /'}
|
113
|
-
put{'PUT /'}
|
114
|
-
delete{'DELETE /'}
|
115
|
-
|
116
|
-
map :contact do
|
117
|
-
before do
|
118
|
-
puts 'Root BEFORE /contact'
|
119
|
-
end
|
120
|
-
after do
|
121
|
-
puts 'Root AFTER /contact'
|
122
|
-
end
|
123
|
-
map 'the-boss' do
|
124
|
-
before :post do
|
125
|
-
puts 'Root BEFORE-POST /contact/the-boss'
|
126
|
-
end
|
127
|
-
after :post do
|
128
|
-
puts 'Root AFTER-POST /contact/the-boss'
|
129
|
-
end
|
130
|
-
post{'Root POST /contact'}
|
131
|
-
end
|
132
|
-
use Contact
|
133
|
-
end
|
134
|
-
|
135
|
-
map :admin do
|
136
|
-
map(:products){use Admin::Products}
|
137
|
-
map(:customers){use Admin::Customers}
|
138
|
-
map :orders do
|
139
|
-
post{'Root POST /admin/orders'}
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
map :admin do
|
147
|
-
before do
|
148
|
-
puts "Root BEFORE /admin"
|
149
|
-
end
|
150
|
-
after do
|
151
|
-
puts "Root AFTER /admin"
|
152
|
-
end
|
153
|
-
get{'Root GET /admin'}
|
154
|
-
map :orders, :server_port=>/^8/ do
|
155
|
-
get :server_port=>80 do
|
156
|
-
':80 GET /admin/orders'
|
157
|
-
end
|
158
|
-
get :server_port=>81 do
|
159
|
-
':81 GET /admin/orders'
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|