sansom 0.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7c54015af146ba89239c6199fa38f6d32674f274
4
+ data.tar.gz: 01b09122f5014d586311449942d84962a51b1a89
5
+ SHA512:
6
+ metadata.gz: 806566785d4903fb29413b9c4606f259b154b2f6dc946139e20341ed69a08385c533818bbd1a5eab8553c3622387a22e51261a910b309e0e22810c0eba4f6733
7
+ data.tar.gz: 5805d9c95195d8b5b9ce80f20b0dbee5df425877f28ff63dba3f34dee3198969fd3ce4ee57282ec84e8f2c5f82aca291aa71be03fd8df5bc1aedb6542b4d3914
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ..gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Nathaniel Symer
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,173 @@
1
+ Sansom
2
+ ===
3
+
4
+ Flexible, versatile, light web framework named after Sansom street in Philly.
5
+
6
+ Installation
7
+ -
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem '.'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install .
20
+
21
+ Usage
22
+ -
23
+
24
+ It's pretty simple. Instead of `Class`s storing routes, `Object`s store routes.
25
+
26
+ There are two ways you can deploy `Sansom`:
27
+
28
+ # app.rb
29
+
30
+ #!/usr/bin/env ruby
31
+
32
+ require "sansom"
33
+
34
+ s = Sansom.new
35
+ s.get "/" do |r|
36
+ # r is a Rack::Request
37
+ [200, { "Content-Type" => "text/plain" }, ["Hello Sansom"]]
38
+ end
39
+ s.start
40
+
41
+ Or, the more production-ready version:
42
+
43
+ # config.ru
44
+
45
+ require "sansom"
46
+
47
+ s = Sansom.new
48
+
49
+ s.get "/" do |r|
50
+ # r is a Rack::Request
51
+ [200, { "Content-Type" => "text/plain" }, ["Hello Sansom"]]
52
+ end
53
+
54
+ run s
55
+
56
+ But `Sansom` can do more than just that:
57
+
58
+ It can be used in a similar fashion to Sinatra:
59
+
60
+ # myapi.rb
61
+
62
+ #!/usr/bin/env ruby
63
+
64
+ require "sansom"
65
+
66
+ class MyAPI < Sansom
67
+ # This method is used to define Sansom routes
68
+ def template
69
+ get "/" do |r|
70
+ [200, { "Content-Type" => "text/plain" }, ["Hello Sansom"]]
71
+ # r is a Rack::Request
72
+ end
73
+ end
74
+ end
75
+
76
+ And your `config.ru` file
77
+
78
+ # config.ru
79
+
80
+ require "sansom"
81
+ require "./myapi"
82
+
83
+ run MyAPI.new
84
+
85
+ Sansom can also map other instances of Sansom to a route. Check this:
86
+
87
+ # myapi.rb
88
+
89
+ #!/usr/bin/env ruby
90
+
91
+ require "sansom"
92
+
93
+ class MyAPI < Sansom
94
+ # This method is used to define Sansom routes
95
+ def template
96
+ get "/" do |r|
97
+ [200, { "Content-Type" => "text/plain" }, ["Hello Sansom"]]
98
+ # r is a Rack::Request
99
+ end
100
+ end
101
+ end
102
+
103
+ Let's say you've written a new version of your api. No problem.
104
+
105
+ # app.rb
106
+
107
+ require "sansom"
108
+
109
+ s = Sansom.new
110
+ s.map "/v1", MyAPI.new
111
+ s.map "/v2", MyNewAPI.new
112
+ s.start
113
+
114
+ Or maybe you want to mount your Rails/Sinatra/whatever app
115
+
116
+ # app.rb
117
+
118
+ require "sansom"
119
+
120
+ s = Sansom.new
121
+ s.map "/api", SinatraApp.new
122
+ s.map "/", RailsApp.new
123
+ s.start
124
+
125
+ Lastly, any object can become a "`Sansom`" through a mixin:
126
+
127
+ # mixin.ru
128
+
129
+ class Mixin < Hash
130
+ include Sansomable
131
+
132
+ def template
133
+ get "/sansomable" do |r|
134
+ [200, { "Content-Type" => "text/plain"}, ["Sansomable Hash"]]
135
+ end
136
+ end
137
+ end
138
+
139
+ run Mixin.new
140
+
141
+ If you look at how `Sansom` is defined, it makes sense:
142
+
143
+ Sansom = Class.new Object
144
+ Sansom.include Sansomable
145
+
146
+ Matching
147
+ -
148
+
149
+ `Sansom` uses trees to match routes. It follows a certain set of rules:
150
+
151
+ - A matching order is enforced:
152
+ 1. The route matching the path and verb
153
+ 2. The first Subsansom that matches the route & verb
154
+ 3. The first mounted non-`Sansom` rack app matching the route
155
+
156
+ Notes
157
+ -
158
+
159
+ - `Sansom` does not pollute _any_ `Object` methods, including `initialize`
160
+ - `Sansom` is under **140** lines of code at the time of writing. This includes
161
+ * Everything above
162
+ * Custom routing
163
+
164
+ Contributing
165
+ -
166
+
167
+ 1. Fork it
168
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
169
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
170
+ 4. Push to the branch (`git push origin my-new-feature`)
171
+ 5. Create a new Pull Request
172
+
173
+ **Please make sure you don't add tons and tons of code. Part of `Sansom`'s beauty is is brevity.**
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "json"
4
+ 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
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ 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
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rack"
4
+ require "tree" # rubytree
5
+
6
+ module Sansomable
7
+ class TreeContent
8
+ attr_accessor :items
9
+ def initialize
10
+ @items = []
11
+ @map = {}
12
+ end
13
+
14
+ def []=(k,v)
15
+ @items << v if k == :map
16
+ @map[k] = v unless k == :map
17
+ end
18
+
19
+ def [](k)
20
+ @items[k] if Numeric === k
21
+ @map[k] unless Numeric === k
22
+ end
23
+ end
24
+
25
+ InvalidRouteError = Class.new StandardError
26
+ NoRoutesError = Class.new StandardError
27
+ InclusionError = Class.new StandardError
28
+
29
+ HTTP_VERBS = ["GET","HEAD","POST","PUT","DELETE","PATCH","OPTIONS"].freeze
30
+ HANDLERS = ["puma", "unicorn", "thin", "webrick"].freeze
31
+ NOT_FOUND = [404, {"Content-Type" => "text/plain"}, ["Not found."]].freeze
32
+
33
+ def tree
34
+ @tree ||= nil
35
+ if @tree.nil?
36
+ @tree = Tree::TreeNode.new("ROOT", "ROOT")
37
+ template if respond_to? :template
38
+ end
39
+ @tree
40
+ end
41
+
42
+ def match http_method, path
43
+ components = s_parse_path(path)
44
+ matched_components = []
45
+
46
+ walk = components.inject(tree) do |node, component|
47
+ child = node[component]
48
+
49
+ if child.nil?
50
+ node
51
+ else
52
+ matched_components << component unless component == "/"
53
+ child
54
+ end
55
+ end
56
+
57
+ tc = walk.content
58
+
59
+ return nil if tc == "ROOT"
60
+
61
+ matched_path = "/" + matched_components.join("/")
62
+
63
+ match = tc[http_method] # Check for route
64
+ match ||= tc.items.select(&method(:sansom?)).reject { |item| item.match(http_method, s_truncate_path(path, matched_path)).nil? }.first rescue nil # Check subsansoms
65
+ match ||= tc.items.reject(&method(:sansom?)).first rescue nil # Check for mounted rack apps
66
+ [match, matched_path]
67
+ end
68
+
69
+ def call env
70
+ return NOT_FOUND if tree.children.empty?
71
+
72
+ r = Rack::Request.new env
73
+
74
+ m = match r.request_method, r.path_info
75
+ item = m.first
76
+
77
+ if item.nil?
78
+ NOT_FOUND
79
+ elsif Proc === item
80
+ item.call r
81
+ elsif sansom? item
82
+ item.call(env.dup.merge({ "PATH_INFO" => s_truncate_path(r.path_info, m.last) }))
83
+ else
84
+ raise InvalidRouteError, "Invalid route handler, it must be a block (proc/lambda) or a subclass of Sansom."
85
+ end
86
+ end
87
+
88
+ def start port=3001
89
+ raise NoRoutesError if tree.children.empty?
90
+ Rack::Handler.pick(HANDLERS).run self, :Port => port
91
+ end
92
+
93
+ def method_missing(meth, *args, &block)
94
+ _args = args.dup.push block
95
+ super unless _args.count >= 2
96
+
97
+ path = _args[0].dup
98
+ item = _args[1].dup
99
+
100
+ return super if item == self
101
+
102
+ verb = meth.to_s.strip.upcase
103
+ return super unless HTTP_VERBS.include?(verb) || meth == :map
104
+ verb = :map if meth == :map
105
+
106
+ components = s_parse_path path
107
+ components.each_with_index.inject(tree) do |node,(component, idx)|
108
+ child = node[component]
109
+
110
+ if child.nil?
111
+ newvalue = Tree::TreeNode.new(component, TreeContent.new)
112
+ node << newvalue
113
+ child = newvalue
114
+ end
115
+
116
+ child.content[verb] = item if idx == components.count-1
117
+ child
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ def sansom? obj
124
+ return true if Sansom === obj
125
+ return true if obj.class.included_modules.include? Sansomable
126
+ false
127
+ end
128
+
129
+ def s_parse_path path
130
+ path.split("/").reject(&:empty?).unshift("/")
131
+ end
132
+
133
+ def s_truncate_path truncated, truncator
134
+ "/" + s_parse_path(truncated)[s_parse_path(truncator).count..-1].join("/")
135
+ end
136
+ end
137
+
138
+ Sansom = Class.new Object
139
+ Sansom.include Sansomable
@@ -0,0 +1,3 @@
1
+ module Sansom
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sansom/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sansom"
8
+ spec.version = Sansom::VERSION
9
+ spec.authors = ["Nathaniel Symer"]
10
+ spec.email = ["nate@natesymer.com"]
11
+ spec.summary = %q{Flexible, versatile, light web framework named after Sansom street in Philly.}
12
+ spec.description = %q{Flexible, versatile, light web framework named after Sansom street in Philly. It's under 140 lines of code & and it's lightning fast.}
13
+ spec.homepage = "http://github.com/fhsjaagshs/sansom"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_dependency "rubytree", "~> 0.9"
23
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sansom
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Nathaniel Symer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubytree
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ description: Flexible, versatile, light web framework named after Sansom street in
42
+ Philly. It's under 140 lines of code & and it's lightning fast.
43
+ email:
44
+ - nate@natesymer.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - examples/generic.rb
54
+ - examples/mixin.rb
55
+ - lib/sansom.rb
56
+ - lib/sansom/version.rb
57
+ - sansom.gemspec
58
+ homepage: http://github.com/fhsjaagshs/sansom
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.2.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Flexible, versatile, light web framework named after Sansom street in Philly.
82
+ test_files: []