sansom 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/README.md +14 -11
- data/changelog.md +7 -1
- data/lib/sansom/pine.rb +68 -67
- data/lib/sansom.rb +33 -72
- data/sansom.gemspec +7 -6
- metadata +7 -9
- data/examples/before.rb +0 -20
- data/examples/generic.rb +0 -79
- data/examples/mixin.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ea8ea9f12513294f151757423264d44870d210f
|
4
|
+
data.tar.gz: 405f6c890367eaa2c961cc4520f58b1bdc7ddc6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f773bca14fef8bdfb91ba0147d97d952fc72189bd4c8d368e76c5920cf7d9c57c4073fdbd216e97c560f58c1e86481ca9737898d6090fb1246096a01e3e87532
|
7
|
+
data.tar.gz: da9038bc8791abbbf2aed558613d3b922e6dd95eb8feea4cc7e2d09e668536c565e0bd6b5d8fecfcc00907adfd0c0bb1c7b2bc7302152fdccd837831d0c33097
|
data/README.md
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
Sansom
|
2
2
|
===
|
3
3
|
|
4
|
-
|
4
|
+
Scientific, philosophical, abstract web 'picowork' named after Sansom street in Philly, where it was made.
|
5
5
|
|
6
|
-
|
6
|
+
Philosophy
|
7
7
|
-
|
8
8
|
|
9
|
-
|
9
|
+
*A piece of software should not limit you to one way of thinking.*
|
10
10
|
|
11
|
-
|
11
|
+
You can write a `Sansomable` for each logical unit of your API, but you also don't have to.
|
12
12
|
|
13
|
-
|
13
|
+
You can also mount existing Rails/Sinatra apps in your `Sansomable`. But you also don't have to.
|
14
14
|
|
15
|
-
|
15
|
+
You can write one `Sansomable` for your entire API.
|
16
16
|
|
17
|
-
|
17
|
+
Installation
|
18
|
+
-
|
18
19
|
|
19
|
-
|
20
|
+
`gem install sansom`
|
20
21
|
|
21
22
|
Usage
|
22
23
|
-
|
@@ -148,18 +149,20 @@ Matching
|
|
148
149
|
|
149
150
|
`Sansom` uses trees to match routes. It follows a certain set of rules:
|
150
151
|
|
152
|
+
- Wildcard routes can't have any siblings
|
151
153
|
- A matching order is enforced:
|
152
154
|
1. The route matching the path and verb
|
153
155
|
2. The first Subsansom that matches the route & verb
|
154
156
|
3. The first mounted non-`Sansom` rack app matching the route
|
157
|
+
|
155
158
|
|
156
159
|
Notes
|
157
160
|
-
|
158
161
|
|
159
162
|
- `Sansom` does not pollute _any_ `Object` methods, including `initialize`
|
160
|
-
- `Sansom` is under **
|
161
|
-
*
|
162
|
-
* Custom routing
|
163
|
+
- `Sansom` is under **190** lines of code at the time of writing. This includes
|
164
|
+
* Rack conformity & the DSL (`sansom.rb`)
|
165
|
+
* Custom tree-based routing (`pine.rb`)
|
163
166
|
|
164
167
|
Contributing
|
165
168
|
-
|
data/changelog.md
CHANGED
@@ -31,4 +31,10 @@ Here's an example
|
|
31
31
|
|
32
32
|
0.0.4
|
33
33
|
|
34
|
-
- Fixed bug with with requiring pine
|
34
|
+
- Fixed bug with with requiring pine
|
35
|
+
|
36
|
+
0.0.5
|
37
|
+
|
38
|
+
- Parameterized URLs!!! (Stuff like `/user/:id/profile`)
|
39
|
+
* Parameterized URLs work with mounted Rack/Sansom apps
|
40
|
+
- Improved matching efficiency
|
data/lib/sansom/pine.rb
CHANGED
@@ -2,115 +2,116 @@
|
|
2
2
|
|
3
3
|
# Path routing tree
|
4
4
|
|
5
|
-
# Custom tree implementation for path routing
|
6
|
-
|
7
|
-
# Custom features:
|
8
|
-
# 1. Trimming: Limit a node to a single element
|
9
|
-
|
10
5
|
module Pine
|
11
6
|
class Node
|
12
|
-
attr_reader :name
|
7
|
+
attr_reader :name, :parent
|
13
8
|
attr_accessor :content
|
14
|
-
attr_accessor :parent
|
15
9
|
|
16
10
|
def initialize name, content=Content.new
|
17
11
|
@name = name
|
18
12
|
@content = content
|
19
13
|
@children = {}
|
20
14
|
@parent = nil
|
21
|
-
@trimmed = false
|
22
15
|
end
|
23
16
|
|
24
|
-
|
25
|
-
|
26
|
-
if trimmed?
|
27
|
-
# Add to first child
|
28
|
-
children.first << node
|
29
|
-
else
|
30
|
-
node.parent = self
|
31
|
-
@children[node.name] = node
|
32
|
-
node
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def create_if_necessary name
|
37
|
-
unless @children.keys.include? name
|
38
|
-
child = self.class.new name
|
39
|
-
child.parent = self
|
40
|
-
@children[name] = child
|
41
|
-
end
|
42
|
-
@children[name]
|
17
|
+
def root?
|
18
|
+
@parent.nil?
|
43
19
|
end
|
44
20
|
|
45
|
-
def
|
46
|
-
|
47
|
-
n = n.parent while !n.root?
|
48
|
-
n
|
21
|
+
def leaf?
|
22
|
+
@children.count == 0
|
49
23
|
end
|
50
24
|
|
51
|
-
def
|
52
|
-
@
|
25
|
+
def wildcard?
|
26
|
+
@wildcard
|
53
27
|
end
|
54
28
|
|
55
|
-
def
|
56
|
-
@
|
57
|
-
|
58
|
-
|
59
|
-
|
29
|
+
def [] k
|
30
|
+
return @children[k] || @children.values.first
|
31
|
+
# child = @children[k] || @child.values.first
|
32
|
+
# return child unless child.nil?
|
33
|
+
|
34
|
+
# @children[k] || @children.values.first.wildcard? ? @children.values.first : nil
|
60
35
|
end
|
61
36
|
|
62
|
-
def
|
63
|
-
|
37
|
+
def create_and_save comp
|
38
|
+
child = self.class.new comp
|
39
|
+
child.instance_variable_set "@parent", self
|
40
|
+
@children[comp] = child
|
41
|
+
child
|
64
42
|
end
|
65
|
-
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
43
|
+
|
44
|
+
def << comp
|
45
|
+
if comp.start_with? ":"
|
46
|
+
@wildcard = true
|
47
|
+
@children.clear
|
48
|
+
create_and_save comp
|
49
|
+
else
|
50
|
+
child = @children[comp]
|
51
|
+
child = create_and_save comp if !child || (!child && child.leaf? && !child.wildcard?)
|
52
|
+
child
|
53
|
+
end
|
72
54
|
end
|
73
|
-
|
74
|
-
def
|
75
|
-
|
55
|
+
|
56
|
+
def parse_path path
|
57
|
+
c = path.split "/"
|
58
|
+
c[0] = '/'
|
59
|
+
c.delete_at(-1) if c[-1].empty?
|
60
|
+
c
|
76
61
|
end
|
77
62
|
|
78
|
-
def
|
79
|
-
|
63
|
+
def map_path path, item, key
|
64
|
+
parse_path(path).inject(self) { |node, comp| node << comp }.content[key] = item
|
65
|
+
path
|
80
66
|
end
|
81
67
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
68
|
+
def match path, verb
|
69
|
+
matched_comps = []
|
70
|
+
matched_params = {}
|
71
|
+
|
72
|
+
walk = parse_path(path).inject self do |node, comp|
|
73
|
+
break node if node.leaf?
|
74
|
+
matched_comps << comp unless comp == "/"
|
75
|
+
child = node[comp]
|
76
|
+
matched_params[child.name[1..-1]] = comp if node.wildcard?
|
77
|
+
child
|
91
78
|
end
|
92
79
|
|
93
|
-
|
80
|
+
return nil if walk.root? rescue true
|
81
|
+
|
82
|
+
c = walk.content
|
83
|
+
subpath = path.sub "/#{matched_comps.join("/")}", ""
|
84
|
+
|
85
|
+
match = c.map[verb.downcase.to_sym]
|
86
|
+
match ||= c.items.detect { |i| sansom?(i) && i.tree.match(subpath, verb) }
|
87
|
+
match ||= c.items.detect { |i| !sansom?(i) }
|
94
88
|
|
95
|
-
|
89
|
+
return nil if match.nil?
|
90
|
+
|
91
|
+
Result.new match, subpath, matched_params
|
92
|
+
end
|
93
|
+
|
94
|
+
def sansom? obj
|
95
|
+
obj.singleton_class.include? Sansomable
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
Result = Struct.new :item, :remaining_path, :url_params
|
100
|
+
|
99
101
|
class Content
|
100
|
-
attr_accessor :items
|
101
|
-
attr_accessor :map
|
102
|
+
attr_accessor :items, :map
|
102
103
|
|
103
104
|
def initialize
|
104
105
|
@items = []
|
105
106
|
@map = {}
|
106
107
|
end
|
107
108
|
|
108
|
-
def []=
|
109
|
+
def []= k,v
|
109
110
|
@items << v if k == :map
|
110
111
|
@map[k] = v unless k == :map
|
111
112
|
end
|
112
113
|
|
113
|
-
def []
|
114
|
+
def [] k
|
114
115
|
@items[k] if Numeric === k
|
115
116
|
@map[k] unless Numeric === k
|
116
117
|
end
|
data/lib/sansom.rb
CHANGED
@@ -1,109 +1,70 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "rack"
|
4
|
-
|
4
|
+
require_relative "./sansom/pine"
|
5
5
|
|
6
6
|
module Sansomable
|
7
7
|
InvalidRouteError = Class.new StandardError
|
8
|
-
|
9
8
|
HTTP_VERBS = [:get,:head, :post, :put, :delete, :patch, :options].freeze
|
10
9
|
HANDLERS = ["puma", "unicorn", "thin", "webrick"].freeze
|
11
|
-
NOT_FOUND = [404, {
|
12
|
-
|
10
|
+
NOT_FOUND = [404, {}, ["Not found."]].freeze
|
11
|
+
|
13
12
|
def tree
|
14
|
-
@tree ||= nil
|
15
13
|
if @tree.nil?
|
16
|
-
@tree = Pine::Node.new
|
14
|
+
@tree = Pine::Node.new "ROOT"
|
17
15
|
template if respond_to? :template
|
18
16
|
end
|
19
17
|
@tree
|
20
18
|
end
|
21
19
|
|
22
|
-
def before_block
|
23
|
-
@before_block ||= nil
|
24
|
-
end
|
25
|
-
|
26
|
-
def match verb, path
|
27
|
-
components = s_parse_path(path)
|
28
|
-
matched_components = []
|
29
|
-
matched_parameters = {}
|
30
|
-
|
31
|
-
walk = components.inject(tree) do |node, component|
|
32
|
-
if node.leaf?
|
33
|
-
node
|
34
|
-
else
|
35
|
-
matched_components << component unless component == "/"
|
36
|
-
node[component]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
return nil if walk.root?
|
41
|
-
|
42
|
-
c = walk.content
|
43
|
-
matched_path = "/" + matched_components.join("/")
|
44
|
-
|
45
|
-
match = c[verb.downcase.to_sym] # Check for route
|
46
|
-
match ||= c.items.select(&method(:sansom?)).reject { |item| item.match(verb, path.sub(matched_path, "")).nil? }.first rescue nil # Check subsansoms
|
47
|
-
match ||= c.items.reject(&method(:sansom?)).first rescue nil # Check for mounted rack apps
|
48
|
-
[match, matched_path]
|
49
|
-
end
|
50
|
-
|
51
20
|
def call env
|
52
21
|
return NOT_FOUND if tree.leaf?
|
53
22
|
|
54
|
-
r = Rack::Request.new env
|
23
|
+
r = Rack::Request.new env.dup
|
24
|
+
|
25
|
+
if @before_block
|
26
|
+
res = @before_block.call r
|
27
|
+
return res if [Fixnum, Hash, Array]-res.map(&:class) == 0
|
28
|
+
end
|
55
29
|
|
56
|
-
m = match r.
|
57
|
-
item = m.first
|
30
|
+
m = tree.match r.path_info, r.request_method
|
58
31
|
|
59
|
-
if
|
32
|
+
if m.url_params.count > 0
|
33
|
+
q = r.params.merge(m.url_params)
|
34
|
+
s = q.map { |p| p.join '=' }.join("&")
|
35
|
+
r.env["rack.request.query_hash"] = q
|
36
|
+
r.env["rack.request.query_string"] = s
|
37
|
+
r.env["QUERY_STRING"] = s
|
38
|
+
r.instance_variable_set "@params", r.POST.merge(q)
|
39
|
+
end
|
40
|
+
|
41
|
+
if !m
|
60
42
|
NOT_FOUND
|
43
|
+
elsif m.item.is_a? Proc
|
44
|
+
m.item.call r
|
45
|
+
elsif m.item.respond_to? :call
|
46
|
+
r.env["PATH_INFO"] = m.remaining_path
|
47
|
+
m.item.call r.env
|
61
48
|
else
|
62
|
-
|
63
|
-
res = before_block.call r
|
64
|
-
return res if res[0].is_a?(Numeric) && res[1].is_a?(Hash) && res[2].respond_to?(:each) rescue false
|
65
|
-
end
|
66
|
-
|
67
|
-
if item.is_a? Proc
|
68
|
-
item.call r
|
69
|
-
elsif sansom? item
|
70
|
-
r.path_info.sub! m[1], ""
|
71
|
-
item.call(r.env)
|
72
|
-
else
|
73
|
-
raise InvalidRouteError, "Invalid route handler, it must be a block (proc/lambda) or a subclass of Sansom."
|
74
|
-
end
|
49
|
+
raise InvalidRouteError, "Route handlers must be blocks or valid rack apps."
|
75
50
|
end
|
76
51
|
end
|
77
52
|
|
78
53
|
def start port=3001
|
79
|
-
raise NoRoutesError if tree.
|
54
|
+
raise NoRoutesError if tree.leaf?
|
80
55
|
Rack::Handler.pick(HANDLERS).run self, :Port => port
|
81
56
|
end
|
82
57
|
|
83
|
-
def before
|
58
|
+
def before &block
|
84
59
|
@before_block = block
|
85
60
|
end
|
86
61
|
|
87
|
-
def method_missing
|
62
|
+
def method_missing meth, *args, &block
|
88
63
|
path, item = *args.dup.push(block)
|
89
64
|
return super unless path && item
|
90
|
-
return super
|
91
|
-
return super unless HTTP_VERBS.include?(meth)
|
92
|
-
|
93
|
-
n = s_parse_path(path).inject(tree) { |node, comp| node.create_if_necessary comp }
|
94
|
-
n.content[meth] = item
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def sansom? obj
|
100
|
-
return true if obj.is_a? Sansom
|
101
|
-
return true if obj.class.included_modules.include? Sansomable
|
102
|
-
false
|
103
|
-
end
|
104
|
-
|
105
|
-
def s_parse_path path
|
106
|
-
path.split("/").reject(&:empty?).unshift("/")
|
65
|
+
return super unless item != self
|
66
|
+
return super unless (HTTP_VERBS+[:map]).include?(meth)
|
67
|
+
tree.map_path path, item, meth
|
107
68
|
end
|
108
69
|
end
|
109
70
|
|
data/sansom.gemspec
CHANGED
@@ -4,17 +4,18 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "sansom"
|
7
|
-
s.version = "0.0.
|
7
|
+
s.version = "0.0.5"
|
8
8
|
s.authors = ["Nathaniel Symer"]
|
9
9
|
s.email = ["nate@natesymer.com"]
|
10
|
-
s.summary = "
|
11
|
-
s.description = s.summary + "It's under
|
10
|
+
s.summary = "Scientific, philosophical, abstract web 'picowork' named after Sansom street in Philly, near where it was made."
|
11
|
+
s.description = s.summary + " " + "It's under 200 lines of code & it's lightning fast. It uses tree-based route resolution."
|
12
12
|
s.homepage = "http://github.com/fhsjaagshs/sansom"
|
13
13
|
s.license = "MIT"
|
14
14
|
|
15
|
-
|
16
|
-
s.
|
17
|
-
s.
|
15
|
+
allfiles = `git ls-files -z`.split("\x0")
|
16
|
+
s.files = allfiles.grep(%r{(^[^\/]*$|^lib\/)}) # Match all lib files AND files in the root
|
17
|
+
s.executables = allfiles.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
s.test_files = allfiles.grep(%r{^(test|spec|features)/})
|
18
19
|
s.require_paths = ["lib"]
|
19
20
|
|
20
21
|
s.add_development_dependency "bundler", "~> 1.6"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sansom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathaniel Symer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,9 +38,9 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1'
|
41
|
-
description:
|
42
|
-
Philly.It's under
|
43
|
-
route resolution.
|
41
|
+
description: Scientific, philosophical, abstract web 'picowork' named after Sansom
|
42
|
+
street in Philly, near where it was made. It's under 200 lines of code & it's lightning
|
43
|
+
fast. It uses tree-based route resolution.
|
44
44
|
email:
|
45
45
|
- nate@natesymer.com
|
46
46
|
executables: []
|
@@ -52,9 +52,6 @@ files:
|
|
52
52
|
- LICENSE.txt
|
53
53
|
- README.md
|
54
54
|
- changelog.md
|
55
|
-
- examples/before.rb
|
56
|
-
- examples/generic.rb
|
57
|
-
- examples/mixin.rb
|
58
55
|
- lib/sansom.rb
|
59
56
|
- lib/sansom/pine.rb
|
60
57
|
- sansom.gemspec
|
@@ -81,5 +78,6 @@ rubyforge_project:
|
|
81
78
|
rubygems_version: 2.2.2
|
82
79
|
signing_key:
|
83
80
|
specification_version: 4
|
84
|
-
summary:
|
81
|
+
summary: Scientific, philosophical, abstract web 'picowork' named after Sansom street
|
82
|
+
in Philly, near where it was made.
|
85
83
|
test_files: []
|
data/examples/before.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require_relative "../lib/sansom" rescue require "sansom"
|
4
|
-
|
5
|
-
s = Sansom.new
|
6
|
-
|
7
|
-
s.before do |r|
|
8
|
-
puts "(#{s.class.to_s}) #{r.request_method.upcase} #{r.path_info}"
|
9
|
-
[200, {}, ["Hijacked by before!"]] if Random.new.rand(2) == 1
|
10
|
-
end
|
11
|
-
|
12
|
-
s.get "/" do |r|
|
13
|
-
[200, { "Content-Type" => "text/plain"}, ["root"]]
|
14
|
-
end
|
15
|
-
|
16
|
-
s.get "/something" do |r|
|
17
|
-
[200, { "Content-Type" => "text/plain" }, ["something"]]
|
18
|
-
end
|
19
|
-
|
20
|
-
s.start 2000
|
data/examples/generic.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require_relative "../lib/sansom" rescue require "sansom"
|
5
|
-
|
6
|
-
class Sansom
|
7
|
-
def food_response r
|
8
|
-
[200, { "Content-Type" => "text/plain"}, [{ :type => self.class.to_s, :name => r.path_info.split("/").reject(&:empty?).last}.to_json]]
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
class Meat < Sansom
|
13
|
-
def template
|
14
|
-
get "/pork" do |r|
|
15
|
-
food_response r
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class NonAnimal < Sansom
|
21
|
-
def template
|
22
|
-
get "/quinoa" do |r|
|
23
|
-
food_response r
|
24
|
-
end
|
25
|
-
|
26
|
-
get "/tahini" do |r|
|
27
|
-
food_response r
|
28
|
-
end
|
29
|
-
|
30
|
-
get "/squash" do |r|
|
31
|
-
food_response r
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class AnimalProducts < Sansom
|
37
|
-
def template
|
38
|
-
get "/eggs" do |r|
|
39
|
-
food_response r
|
40
|
-
end
|
41
|
-
|
42
|
-
get "/milk" do |r|
|
43
|
-
food_response r
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class FoodTypes < Sansom
|
49
|
-
def template
|
50
|
-
map "/carnivorous", Meat.new
|
51
|
-
map "/vegetarian", NonAnimal.new
|
52
|
-
map "/vegetarian", AnimalProducts.new
|
53
|
-
map "/vegan", NonAnimal.new
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class Food < Sansom
|
58
|
-
def template
|
59
|
-
get "/sushi" do |r|
|
60
|
-
[200, { "Content-Type" => "text/plain"}, ["Quite delicious, especially cucumber"]]
|
61
|
-
end
|
62
|
-
|
63
|
-
map "/types", FoodTypes.new
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
s = Sansom.new
|
68
|
-
|
69
|
-
s.get "/" do |r|
|
70
|
-
[200, { "Content-Type" => "text/plain"}, ["root"]]
|
71
|
-
end
|
72
|
-
|
73
|
-
s.get "/something" do |r|
|
74
|
-
[200, { "Content-Type" => "text/plain"}, ["something"]]
|
75
|
-
end
|
76
|
-
|
77
|
-
s.map "/food", Food.new
|
78
|
-
|
79
|
-
s.start 2000
|
data/examples/mixin.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require_relative "../lib/sansom" rescue require "sansom"
|
4
|
-
|
5
|
-
class Mixin < Hash
|
6
|
-
include Sansomable
|
7
|
-
|
8
|
-
def template
|
9
|
-
get "/sansomable" do |r|
|
10
|
-
[200, { "Content-Type" => "text/plain"}, ["Sansomable Hash"]]
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
s = Sansom.new
|
16
|
-
|
17
|
-
s.get "/" do |r|
|
18
|
-
[200, { "Content-Type" => "text/plain"}, ["root"]]
|
19
|
-
end
|
20
|
-
|
21
|
-
s.map "/mixins", Mixin.new
|
22
|
-
s.start 3002
|