mwmitchell-snap 0.5.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/CHANGES ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2008 Matt Mitchell - goodieboy@gmail.com
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,132 @@
1
+ =Snap
2
+
3
+ Snap is a ruby framework for creating `RESTful` web applications. Using Rack: http://rack.rubyforge.org - "Rack: a Ruby Webserver Interface", it's designed to be flexible, fast and easy to understand.
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
15
+
16
+ # Nested GET/POST handler for /contact
17
+ class GetPost
18
+ extend Snap::App
19
+ map do
20
+ map :contact do
21
+ get{}
22
+ post{}
23
+ end
24
+ end
25
+ end
26
+
27
+ ==Logic Flow
28
+
29
+ ================================================================
30
+
31
+ Snap is a Ruby, DSL based framework for creating RESTful web applications. It adheres to the Ruby Rack specification.
32
+
33
+ Snap treats every fragment in a URL as a "context". Each context can be mapped to a block or another Snap enabled module. For example, a URL of:
34
+
35
+ <pre>/users/1/images/1</pre>
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.
127
+
128
+ <pre>
129
+ <code>
130
+ run SeriousApp::Root.new
131
+ </code>
132
+ </pre>
data/examples/demo.rb ADDED
@@ -0,0 +1,84 @@
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 ADDED
@@ -0,0 +1,56 @@
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
@@ -0,0 +1,64 @@
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
@@ -0,0 +1,180 @@
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
@@ -0,0 +1,43 @@
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
@@ -0,0 +1,99 @@
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
@@ -0,0 +1,9 @@
1
+ #
2
+ #
3
+ #
4
+ module Snap::Context
5
+ autoload :Base, 'snap/context/base'
6
+ autoload :Events, 'snap/context/events'
7
+ autoload :Matcher, 'snap/context/matcher'
8
+ autoload :Actions, 'snap/context/actions'
9
+ end
data/lib/snap/demo.rb ADDED
@@ -0,0 +1,169 @@
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
@@ -0,0 +1,11 @@
1
+ class Snap::Request < Rack::Request
2
+
3
+ def m
4
+ request_method.to_s.downcase.to_sym
5
+ end
6
+
7
+ def path_info_slices
8
+ ['/'] + path_info.to_s.gsub(/\/+/, '/').sub(/^\/|\/$/, '').split('/')
9
+ end
10
+
11
+ end
data/lib/snap.rb ADDED
@@ -0,0 +1,27 @@
1
+ $:.delete_if {|v|v=~/TextMate/}
2
+
3
+ $:.unshift File.dirname(__FILE__) unless
4
+ $:.include?(File.dirname(__FILE__)) or $:.include?(File.expand_path(File.dirname(__FILE__)))
5
+
6
+ require 'rubygems'
7
+ require 'rack'
8
+
9
+ class Proc
10
+
11
+ #
12
+ # grab the file and line number from to_s
13
+ #
14
+ def source
15
+ @source ||= self.to_s.scan(/@(.*:\d+)\>/)
16
+ end
17
+
18
+ end
19
+
20
+ module Snap
21
+ autoload :App, 'snap/app'
22
+ autoload :Context, 'snap/context'
23
+ autoload :Request, 'snap/request'
24
+ autoload :Demo, 'snap/demo'
25
+ autoload :Config, 'snap/config'
26
+ autoload :HashBlock, 'snap/hash_block'
27
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mwmitchell-snap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Mitchell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-21 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: Snap is a Ruby, DSL based framework for creating RESTful web applications.
25
+ email: goodieboy@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README
32
+ - LICENSE
33
+ files:
34
+ - README
35
+ - LICENSE
36
+ - CHANGES
37
+ - examples/demo.rb
38
+ - lib/snap.rb
39
+ - lib/snap/app.rb
40
+ - lib/snap/context.rb
41
+ - lib/snap/demo.rb
42
+ - lib/snap/request.rb
43
+ - lib/snap/context/base.rb
44
+ - lib/snap/context/actions.rb
45
+ - lib/snap/context/events.rb
46
+ - lib/snap/context/matcher.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/mwmitchell/snap
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --main
52
+ - README
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.2.0
71
+ signing_key:
72
+ specification_version: 2
73
+ summary: Snap is a Ruby, DSL based framework for creating RESTful web applications.
74
+ test_files: []
75
+