ruter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c3af735d50b376a560325bd054bf4c6102db10476e7ad775e42f064b17473c4f
4
+ data.tar.gz: 5ef9e856f019be81b88356fb633bd6796dd2338af22f80717b10a12f4d9039b0
5
+ SHA512:
6
+ metadata.gz: ca30469bc6740c48d41c4b4b865ed4521c85129262ab5460b1b8097f53a65c27bec99aebb78331d4caf7479a7a2cfc122ece02d1583151266d169748174c29d1
7
+ data.tar.gz: 37926e251e5a8bf557c4c9a4e734c0216b7663b1770a47e322b76ab82ae31d7eb07c983f4e3dbd4db016921cbceef95ab1b923e56a6132ff7bfc61801dcc7c02
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Francesco Rodríguez and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ Ruter [![Build Status](https://app.travis-ci.com/frodsan/ruter.svg?branch=main)](https://app.travis-ci.com/frodsan/ruter)
2
+ ====
3
+
4
+ "You must buy a ticket before entering the bus" - The Inspector 🕵🏻‍♂️
5
+
6
+ Description
7
+ -----------
8
+
9
+ Ruter is yet another [Rack][rack] based framework, made for fun and ~~profit~~, and built on top of [Syro][syro].
10
+
11
+ Usage
12
+ -----
13
+
14
+ Here's a minimal application:
15
+
16
+ ```ruby
17
+ # config.ru
18
+ require "ruter"
19
+
20
+ Ruter.define do
21
+ get do
22
+ res.write("Hello World!")
23
+ end
24
+ end
25
+
26
+ run(Ruter) # run!
27
+ ```
28
+
29
+ License
30
+ -------
31
+
32
+ Ruter is released under the [MIT License][mit].
33
+
34
+ [mit]: http://www.opensource.org/licenses/MIT
35
+ [rack]: https://github.com/rack/rack
36
+ [syro]: http://soveran.github.io/syro/
@@ -0,0 +1,57 @@
1
+ require "rack/request"
2
+
3
+ class Ruter
4
+ module Core
5
+ # Public: It provides convenience methods for pulling out information
6
+ # from a request.
7
+ #
8
+ # Examples
9
+ #
10
+ # env = {
11
+ # "REQUEST_METHOD" => "GET"
12
+ # "QUERY_STRING" => "email=me@ruter.xyz"
13
+ # }
14
+ #
15
+ # req = Ruter::Core::Request.new(env)
16
+ #
17
+ # req.get? # => true
18
+ # req.post? # => false
19
+ #
20
+ # req.params # => { "email" => "me@ruter.xyz" }
21
+ # req[:email] # => "me@ruter.xyz"
22
+ #
23
+ class Request < Rack::Request
24
+ # Public: Returns the value of the +key+ param.
25
+ #
26
+ # key - Any object that responds to +to_s+.
27
+ #
28
+ # Examples
29
+ #
30
+ # req.params
31
+ # # => { "username" => "bob" }
32
+ #
33
+ # req[:username] # => "bob"
34
+ # req["username"] # => "bob"
35
+ #
36
+ # Signature
37
+ #
38
+ # [](key)
39
+ #
40
+ # Inherited by Rack::Request.
41
+
42
+ # Public: Returns a Hash of parameters. Includes data from the query
43
+ # string and the response body.
44
+ #
45
+ # Examples
46
+ #
47
+ # req.params
48
+ # # => { "user" => { "username" => "bob" } }
49
+ #
50
+ # Signature
51
+ #
52
+ # params()
53
+ #
54
+ # Inherited by Rack::Request.
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,199 @@
1
+ class Ruter
2
+ module Core
3
+ # Public: It provides convenience methods to construct a Rack response.
4
+ #
5
+ # Examples
6
+ #
7
+ # res = Ruter::Core::Response.new
8
+ #
9
+ # res.status = 200
10
+ # res["Content-Type"] = "text/html"
11
+ # res.write("foo")
12
+ #
13
+ # res.finish
14
+ # # => [200, { "Content-Type" => "text/html", "Content-Length" => 3 }, ["foo"]]
15
+ #
16
+ class Response < Syro::Response
17
+ # Public: Initializes a new response object.
18
+ #
19
+ # headers - A Hash of initial headers. Defaults to <tt>{}</tt>.
20
+ #
21
+ # Examples
22
+ #
23
+ # Ruter::Core::Response.new.headers
24
+ # # => {}
25
+ #
26
+ # Ruter::Core::Response.new("Content-Type" => "text/plain").headers
27
+ # # => { "Content-Type" => "text/plain" }
28
+ #
29
+ # Signature
30
+ #
31
+ # new(headers = {})
32
+ #
33
+ # Inherited by Syro::Response.
34
+
35
+ # Public: Returns the response header corresponding to +key+.
36
+ #
37
+ # key - A String HTTP header field name.
38
+ #
39
+ # Examples
40
+ #
41
+ # res["Content-Type"] # => "text/html"
42
+ # res["Content-Length"] # => "42"
43
+ #
44
+ # Signature
45
+ #
46
+ # [](key)
47
+ #
48
+ # Inherited by Syro::Response.
49
+
50
+ # Public: Sets the given +value+ with the header corresponding to +key+.
51
+ #
52
+ # key - A String HTTP header field name.
53
+ # value - A String HTTP header field value.
54
+ #
55
+ # Examples
56
+ #
57
+ # res["Content-Type"] = "application/json"
58
+ # res["Content-Type"] # => "application/json"
59
+ #
60
+ # Signature
61
+ #
62
+ # []=(key, value)
63
+ #
64
+ # Inherited by Syro::Response.
65
+
66
+ # Public: Returns the body of the response.
67
+ #
68
+ # Examples
69
+ #
70
+ # res.body
71
+ # # => []
72
+ #
73
+ # res.write("there is")
74
+ # res.write("no try")
75
+ #
76
+ # res.body
77
+ # # => ["there is", "no try"]
78
+ #
79
+ # Signature
80
+ #
81
+ # body()
82
+ #
83
+ # Inherited by Syro::Response.
84
+
85
+ # Public: Returns an Array with three elements: the status, headers
86
+ # and body. If the status is not set, the status is set to +404+ if
87
+ # empty body, otherwise the status is set to +200+ and updates the
88
+ # +Content-Type+ header to +text/html+.
89
+ #
90
+ # Examples
91
+ #
92
+ # res.status = 200
93
+ # res.finish
94
+ # # => [200, {}, []]
95
+ #
96
+ # res.status = nil
97
+ # res.finish
98
+ # # => [404, {}, []]
99
+ #
100
+ # res.status = nil
101
+ # res.write("yo")
102
+ # res.finish
103
+ # # => [200, { "Content-Type" => "text/html", "Content-Length" => 2 }, ["yo"]]
104
+ #
105
+ # Signature
106
+ #
107
+ # finish()
108
+ #
109
+ # Inherited by Syro::Response.
110
+
111
+ # Public: Returns a Hash with the response headers.
112
+ #
113
+ # Examples
114
+ #
115
+ # res.headers
116
+ # # => { "Content-Type" => "text/html", "Content-Length" => "42" }
117
+ #
118
+ # Signature
119
+ #
120
+ # headers()
121
+ #
122
+ # Inherited by Syro::Response.
123
+
124
+ # Public: Sets the +Location+ header to +url+ and updates the status
125
+ # to +status+.
126
+ #
127
+ # url - A String URL (relative or absolute) to redirect to.
128
+ # status - An Integer status code. Defaults to +302+.
129
+ #
130
+ # Examples
131
+ #
132
+ # res.redirect("/path")
133
+ #
134
+ # res["Location"] # => "/path"
135
+ # res.status # => 302
136
+ #
137
+ # res.redirect("http://ruter.xyz", 303)
138
+ #
139
+ # res["Location"] # => "http://ruter.xyz"
140
+ # res.status # => 303
141
+ #
142
+ # Signature
143
+ #
144
+ # redirect(url, status = 302)
145
+ #
146
+ # Inherited by Syro::Response.
147
+
148
+ # Public: Returns the status of the response.
149
+ #
150
+ # Examples
151
+ #
152
+ # res.status # => 200
153
+ #
154
+ # Signature
155
+ #
156
+ # status()
157
+ #
158
+ # Inherited by Syro::Response.
159
+
160
+ # Public: Sets the status of the response.
161
+ #
162
+ # status - An Integer HTTP status code.
163
+ #
164
+ # Examples
165
+ #
166
+ # res.status = 200
167
+ #
168
+ # Signature
169
+ #
170
+ # status=(status)
171
+ #
172
+ # Inherited by Syro::Response.
173
+
174
+ # Public: Appends +str+ to the response body and updates the
175
+ # +Content-Length+ header.
176
+ #
177
+ # str - Any object that responds to +to_s+.
178
+ #
179
+ # Examples
180
+ #
181
+ # res.body # => []
182
+ #
183
+ # res.write("foo")
184
+ # res.write("bar")
185
+ #
186
+ # res.body
187
+ # # => ["foo", "bar"]
188
+ #
189
+ # res["Content-Length"]
190
+ # # => 6
191
+ #
192
+ # Signature
193
+ #
194
+ # write(str)
195
+ #
196
+ # Inherited by Syro::Response.
197
+ end
198
+ end
199
+ end
data/lib/ruter/core.rb ADDED
@@ -0,0 +1,75 @@
1
+ require "rack"
2
+ require "syro"
3
+ require_relative "core/request"
4
+ require_relative "core/response"
5
+
6
+ class Ruter
7
+ # Include Syro's API directly instead of using the plugin
8
+ # system to be able to override any of its methods.
9
+ include Syro::Deck::API
10
+
11
+ # Internal: Handles Syro integration.
12
+ module Core
13
+ module ClassMethods
14
+ # Public: Sets the application handler.
15
+ #
16
+ # Examples
17
+ #
18
+ # class Users < Ruter
19
+ # end
20
+ #
21
+ # Users.define do
22
+ # on("/users") do
23
+ # get do
24
+ # res.write("GET /users")
25
+ # end
26
+ #
27
+ # post do
28
+ # res.write("POST /users")
29
+ # end
30
+ # end
31
+ # end
32
+ #
33
+ def define(&block)
34
+ build_app(Syro.new(self, &block))
35
+ end
36
+
37
+ private def build_app(syro)
38
+ @_app = syro
39
+ end
40
+
41
+ # Internal: Required by the Syro API.
42
+ def implement(&code) # :nodoc:
43
+ Class.new(self) do
44
+ define_method(:dispatch!, code)
45
+
46
+ private :dispatch!
47
+
48
+ define_method(:inspect) do
49
+ self.class.superclass.inspect
50
+ end
51
+ end
52
+ end
53
+
54
+ # Internal: Required by the Rack API.
55
+ def call(env) # :nodoc:
56
+ @_app.call(env)
57
+ end
58
+
59
+ # Internal: Used for internal testing.
60
+ def reset! # :nodoc:
61
+ @_app = nil
62
+ end
63
+ end
64
+
65
+ module InstanceMethods
66
+ def request_class # :nodoc:
67
+ Ruter::Core::Request
68
+ end
69
+
70
+ def response_class # :nodoc:
71
+ Ruter::Core::Response
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,40 @@
1
+ class Ruter
2
+ # Public: Ruter's middleware system.
3
+ module Middleware
4
+ module ClassMethods
5
+ # Public: Adds given Rack +middleware+ to the stack.
6
+ #
7
+ # Examples
8
+ #
9
+ # require "rack/common_logger"
10
+ # require "rack/show_exceptions"
11
+ #
12
+ # Ruter.use(Rack::CommonLogger)
13
+ # Ruter.use(Rack::ShowExceptions)
14
+ #
15
+ def use(middleware, *args, &block)
16
+ self.middleware << proc { |app| middleware.new(app, *args, &block) }
17
+ end
18
+
19
+ # Internal: Returns middleware stack.
20
+ def middleware # :nodoc:
21
+ @_middleware ||= []
22
+ end
23
+
24
+ # Internal: Used for internal testing.
25
+ def reset! # :nodoc:
26
+ super
27
+ @_middleware = []
28
+ end
29
+
30
+ # Internal: Overrides app composition from core.
31
+ private def build_app(syro)
32
+ @_app = if middleware.empty?
33
+ syro
34
+ else
35
+ middleware.reverse.inject(syro) { |a, m| m.call(a) }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,67 @@
1
+ class Ruter
2
+ # Public: Ruter's plugin system.
3
+ module Plugin
4
+ # Public: Loads given +plugin+ into the application.
5
+ #
6
+ # Examples
7
+ #
8
+ # require "ruter"
9
+ # require "ruter/protection"
10
+ # require "ruter/session"
11
+ #
12
+ # # Use some standard plugins
13
+ #
14
+ # Ruter.plugin(Ruter::Protection)
15
+ # Ruter.plugin(Ruter::Session, secret: "__a_random_secret_key__")
16
+ #
17
+ # # Make your own custom plugin:
18
+ #
19
+ # require "redcarpet"
20
+ #
21
+ # class MarkdownPlugin
22
+ # def self.setup(app, options = {})
23
+ # app.settings[:markdown_options] = options
24
+ # end
25
+ #
26
+ # module ClassMethods
27
+ # def markdown_renderer
28
+ # @_markdown_renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, markdown_options)
29
+ # end
30
+ #
31
+ # def markdown_options
32
+ # settings[:markdown_options]
33
+ # end
34
+ # end
35
+ #
36
+ # module InstanceMethods
37
+ # def markdown(text)
38
+ # res.write(markdown_renderer.render(text))
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ # Ruter.plugin(MarkdownPlugin, fenced_code_blocks: true)
44
+ #
45
+ # Ruter.define do
46
+ # root do
47
+ # get do
48
+ # markdown("This is *bongos*, indeed.")
49
+ # end
50
+ # end
51
+ # end
52
+ #
53
+ def plugin(plugin, *args, &block)
54
+ if defined?(plugin::InstanceMethods)
55
+ include(plugin::InstanceMethods)
56
+ end
57
+
58
+ if defined?(plugin::ClassMethods)
59
+ extend(plugin::ClassMethods)
60
+ end
61
+
62
+ if plugin.respond_to?(:setup)
63
+ plugin.setup(self, *args, &block)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,90 @@
1
+ class Ruter
2
+ # Public: It provides a settings API for Ruter applications.
3
+ #
4
+ # Examples
5
+ #
6
+ # require "ruter"
7
+ #
8
+ # module AppName
9
+ # def self.setup(app, app_name)
10
+ # app.settings[:app_name] = app_name
11
+ # end
12
+ #
13
+ # module ClassMethods
14
+ # def app_name
15
+ # return settings[:app_name]
16
+ # end
17
+ # end
18
+ # end
19
+ #
20
+ # Ruter.plugin(AppName, "MyApp")
21
+ #
22
+ # Ruter.app_name
23
+ # # => "MyApp"
24
+ #
25
+ module Settings
26
+ # Internal: Returns a deep copy of a Hash.
27
+ def self.deepclone(hash) # :nodoc:
28
+ default_proc, hash.default_proc = hash.default_proc, nil
29
+
30
+ Marshal.load(Marshal.dump(hash))
31
+ ensure
32
+ hash.default_proc = default_proc
33
+ end
34
+
35
+ module ClassMethods
36
+ # Internal: Copies settings into the subclass.
37
+ # If a setting is not found, checks parent's settings.
38
+ def inherited(subclass)
39
+ subclass.settings.replace(Ruter::Settings.deepclone(settings))
40
+ subclass.settings.default_proc = proc { |h, k| h[k] = settings[k] }
41
+ end
42
+
43
+ # Public: Sets an +option+ to the given +value+.
44
+ #
45
+ # Examples
46
+ #
47
+ # Ruter.set(:environment, :staging)
48
+ #
49
+ # Ruter.environment
50
+ # # => :staging
51
+ #
52
+ def set(option, value)
53
+ settings[option] = value
54
+ end
55
+
56
+ # Returns a Hash with the application settings.
57
+ #
58
+ # Examples
59
+ #
60
+ # Ruter.set(:environment, :development)
61
+ #
62
+ # Ruter.settings
63
+ # # => { :environment => :development }
64
+ #
65
+ def settings
66
+ @settings ||= {}
67
+ end
68
+ end
69
+
70
+ module InstanceMethods
71
+ # Returns a Hash with the application settings.
72
+ #
73
+ # Examples
74
+ #
75
+ # Ruter.set(:environment, :development)
76
+ #
77
+ # Ruter.define do
78
+ # get do
79
+ # res.write(settings[:environment])
80
+ # end
81
+ # end
82
+ #
83
+ # GET / # => 200 "development"
84
+ #
85
+ def settings
86
+ self.class.settings
87
+ end
88
+ end
89
+ end
90
+ end
data/lib/ruter/test.rb ADDED
@@ -0,0 +1,199 @@
1
+ class Ruter
2
+ # Public: A simple helper class to simulate requests to your application.
3
+ #
4
+ # Examples
5
+ #
6
+ # require "ruter"
7
+ # require "ruter/test"
8
+ #
9
+ # Ruter.define do
10
+ # root do
11
+ # res.write("hei")
12
+ # end
13
+ # end
14
+ #
15
+ # app = Ruter::Test.new
16
+ # app.get("/")
17
+ #
18
+ # app.res.status # => 200
19
+ # app.res.body # => "hei"
20
+ #
21
+ class Test
22
+ # Public: Initializes a new Ruter::Test object.
23
+ #
24
+ # app - The application class to test (default: Ruter).
25
+ #
26
+ # Examples
27
+ #
28
+ # class API < Ruter
29
+ # end
30
+ #
31
+ # app = Ruter::Test.new(API)
32
+ # app.get("/json")
33
+ #
34
+ def initialize(app = Ruter)
35
+ @app = app
36
+ end
37
+
38
+ # Internal: Returns the application class that handles the
39
+ # mock requests. Required by Ruter::Test::InstanceMethods.
40
+ attr_reader :app
41
+
42
+ # Internal: This module provides the Ruter::Test API methods.
43
+ # If you don't like the stand-alone version, you can integrate
44
+ # this module to your preferred testing environment.
45
+ #
46
+ # The following example uses Minitest:
47
+ #
48
+ # class HomeTest < Minitest::Test
49
+ # include Ruter::Test::InstanceMethods
50
+ #
51
+ # def app
52
+ # return Ruter
53
+ # end
54
+ #
55
+ # def test_home
56
+ # get("/")
57
+ #
58
+ # assert_equal 200, res.status
59
+ # end
60
+ # end
61
+ #
62
+ module InstanceMethods
63
+ # Public: Returns the current request object or +nil+ if no requests
64
+ # have been issued yet.
65
+ #
66
+ # Examples
67
+ #
68
+ # app = Ruter::Test.new
69
+ # app.get("/", {}, { "HTTP_USER_AGENT" => "Ruter::Test" })
70
+ #
71
+ # app.req.get?
72
+ # # => true
73
+ #
74
+ # app.req.env["HTTP_USER_AGENT"]
75
+ # # => "Ruter::Test"
76
+ #
77
+ # The returned object is an instance of
78
+ # {Rack::Request}[http://www.rubydoc.info/gems/rack/Rack/Request].
79
+ #
80
+ def req
81
+ @__req
82
+ end
83
+
84
+ # Public: Returns the current response object or +nil+ if no requests
85
+ # have been issued yet.
86
+ #
87
+ # Examples
88
+ #
89
+ # app = Ruter::Test.new
90
+ # app.get("/", name: "alice")
91
+ #
92
+ # app.res.status
93
+ # # => 200
94
+ #
95
+ # app.res.body
96
+ # # => "Hello alice!"
97
+ #
98
+ # The returned object is an instance of
99
+ # {Rack::MockResponse}[http://www.rubydoc.info/gems/rack/Rack/MockResponse]
100
+ #
101
+ def res
102
+ @__res
103
+ end
104
+
105
+ # Public: Issues a GET request for the given +path+ with the given
106
+ # +params+ and Rack environment.
107
+ #
108
+ # Examples
109
+ #
110
+ # app = Ruter::Test.new
111
+ # app.get("/search", name: "alice")
112
+ #
113
+ def get(path, params = {}, env = {})
114
+ request(path, env.merge(method: Rack::GET, params: params))
115
+ end
116
+
117
+ # Public: Issues a POST request for the given +path+ with the given
118
+ # +params+ and Rack environment.
119
+ #
120
+ # Examples
121
+ #
122
+ # app = Ruter::Test.new
123
+ # app.post("/signup", username: "alice", password: "secret")
124
+ #
125
+ def post(path, params = {}, env = {})
126
+ request(path, env.merge(method: "POST".freeze, params: params))
127
+ end
128
+
129
+ # Public: Issues a PUT request for the given +path+ with the given
130
+ # +params+ and Rack environment.
131
+ #
132
+ # Examples
133
+ #
134
+ # app = Ruter::Test.new
135
+ # app.put("/users/1", username: "bob", name: "Bob")
136
+ #
137
+ def put(path, params = {}, env = {})
138
+ request(path, env.merge(method: "PUT".freeze, params: params))
139
+ end
140
+
141
+ # Public: Issues a PATCH request for the given +path+ with the given
142
+ # +params+ and Rack environment.
143
+ #
144
+ # Examples
145
+ #
146
+ # app = Ruter::Test.new
147
+ # app.patch("/users/1", username: "alice")
148
+ #
149
+ def patch(path, params = {}, env = {})
150
+ request(path, env.merge(method: "PATCH".freeze, params: params))
151
+ end
152
+
153
+ # Public: Issues a DELETE request for the given +path+ with the given
154
+ # +params+ and Rack environment.
155
+ #
156
+ # Examples
157
+ #
158
+ # app = Ruter::Test.new
159
+ # app.delete("/users/1")
160
+ #
161
+ def delete(path, params = {}, env = {})
162
+ request(path, env.merge(method: "DELETE".freeze, params: params))
163
+ end
164
+
165
+ # Public: Issues a HEAD request for the given +path+ with the given
166
+ # +params+ and Rack environment.
167
+ #
168
+ # Examples
169
+ #
170
+ # app = Ruter::Test.new
171
+ # app.head("/users/1")
172
+ #
173
+ def head(path, params = {}, env = {})
174
+ request(path, env.merge(method: Rack::HEAD, params: params))
175
+ end
176
+
177
+ # Public: Issues a OPTIONS request for the given +path+ with the given
178
+ # +params+ and Rack environment.
179
+ #
180
+ # Examples
181
+ #
182
+ # app = Ruter::Test.new
183
+ # app.options("/users")
184
+ #
185
+ def options(path, params = {}, env = {})
186
+ request(path, env.merge(method: "OPTIONS".freeze, params: params))
187
+ end
188
+
189
+ private
190
+
191
+ def request(path, opts = {})
192
+ @__req = Rack::Request.new(Rack::MockRequest.env_for(path, opts))
193
+ @__res = Rack::MockResponse.new(*app.call(@__req.env))
194
+ end
195
+ end
196
+
197
+ include InstanceMethods
198
+ end
199
+ end
@@ -0,0 +1,8 @@
1
+ class Ruter # :nodoc: all
2
+ VERSION = [
3
+ MAJOR_VERSION = 1,
4
+ MINOR_VERSION = 0,
5
+ PATCH_VERSION = 0,
6
+ PRE_VERSION = nil
7
+ ].compact.join(".")
8
+ end
data/lib/ruter.rb ADDED
@@ -0,0 +1,14 @@
1
+ require_relative "ruter/version"
2
+ require_relative "ruter/plugin"
3
+ require_relative "ruter/core"
4
+ require_relative "ruter/middleware"
5
+ require_relative "ruter/settings"
6
+
7
+ class Ruter
8
+ extend Ruter::Plugin
9
+
10
+ # Internal: Core plugins.
11
+ plugin(Ruter::Core)
12
+ plugin(Ruter::Middleware)
13
+ plugin(Ruter::Settings)
14
+ end
@@ -0,0 +1,90 @@
1
+ require_relative "../test_helper"
2
+
3
+ class CoreTest < Minitest::Test
4
+ setup do
5
+ Ruter.reset!
6
+ end
7
+
8
+ test "hello" do
9
+ Ruter.define do
10
+ get do
11
+ res.write("hello")
12
+ end
13
+ end
14
+
15
+ app = Ruter::Test.new
16
+ app.get("/")
17
+
18
+ assert_equal 200, app.res.status
19
+ assert_equal "hello", app.res.body
20
+ end
21
+
22
+ test "methods" do
23
+ [:get, :post, :put, :patch, :delete].each do |method|
24
+ Ruter.define do
25
+ send(method) { res.write "" }
26
+ end
27
+
28
+ app = Ruter::Test.new
29
+ app.send(method, "/")
30
+
31
+ assert_equal 200, app.res.status
32
+ end
33
+ end
34
+
35
+ test "captures" do
36
+ Ruter.define do
37
+ on :foo do
38
+ on :bar do
39
+ get do
40
+ res.write(sprintf("%s:%s", inbox[:foo], inbox[:bar]))
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ app = Ruter::Test.new
47
+ app.get("/foo/bar")
48
+
49
+ assert_equal 200, app.res.status
50
+ assert_equal "foo:bar", app.res.body
51
+ end
52
+
53
+ test "composition" do
54
+ nested = Class.new(Ruter)
55
+
56
+ nested.define do
57
+ get do
58
+ res.write(inbox[:foo])
59
+ end
60
+ end
61
+
62
+ Ruter.define do
63
+ on "foo" do
64
+ run(nested, foo: 42)
65
+ end
66
+ end
67
+
68
+ app = Ruter::Test.new
69
+ app.get("/foo")
70
+
71
+ assert_equal 200, app.res.status
72
+ assert_equal "42", app.res.body
73
+ end
74
+
75
+ test "use own request and response classes" do
76
+ Ruter.define do
77
+ get do
78
+ res.write(request_class.name)
79
+ res.write(response_class.name)
80
+ end
81
+ end
82
+
83
+ app = Ruter::Test.new
84
+ app.get("/")
85
+
86
+ assert_equal 200, app.res.status
87
+ assert_match Ruter::Core::Request.name, app.res.body
88
+ assert_match Ruter::Core::Response.name, app.res.body
89
+ end
90
+ end
@@ -0,0 +1,95 @@
1
+ require_relative "../test_helper"
2
+
3
+ class MiddlewareTest < Minitest::Test
4
+ class ReverseBody
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ status, headers, body = @app.call(env)
11
+
12
+ [status, headers, body.reverse]
13
+ end
14
+ end
15
+
16
+ setup do
17
+ Ruter.reset!
18
+ end
19
+
20
+ test "middleware in main application" do
21
+ Ruter.use(ReverseBody)
22
+
23
+ Ruter.define do
24
+ get do
25
+ res.write("1")
26
+ res.write("2")
27
+ end
28
+ end
29
+
30
+ app = Ruter::Test.new
31
+ app.get("/")
32
+
33
+ assert_equal 200, app.res.status
34
+ assert_equal "21", app.res.body
35
+ end
36
+
37
+ test "middleware with composition" do
38
+ Ruter.use(ReverseBody)
39
+
40
+ api = Class.new(Ruter) do
41
+ define do
42
+ get do
43
+ res.write("1")
44
+ res.write("2")
45
+ end
46
+ end
47
+ end
48
+
49
+ Ruter.define do
50
+ on "api" do
51
+ run(api)
52
+ end
53
+ end
54
+
55
+ app = Ruter::Test.new
56
+ app.get("/api")
57
+
58
+ assert_equal 200, app.res.status
59
+ assert_equal "21", app.res.body
60
+ end
61
+
62
+ test "middleware only in child application" do
63
+ api = Class.new(Ruter) do
64
+ use(ReverseBody)
65
+
66
+ define do
67
+ get do
68
+ res.write("1")
69
+ res.write("2")
70
+ end
71
+ end
72
+ end
73
+
74
+ Ruter.define do
75
+ on "api" do
76
+ run(api)
77
+ end
78
+
79
+ get do
80
+ res.write("not_reversed")
81
+ end
82
+ end
83
+
84
+ app = Ruter::Test.new
85
+ app.get("/api")
86
+
87
+ assert_equal 200, app.res.status
88
+ assert_equal "21", app.res.body
89
+
90
+ app.get("/")
91
+
92
+ assert_equal 200, app.res.status
93
+ assert_equal "not_reversed", app.res.body
94
+ end
95
+ end
@@ -0,0 +1,54 @@
1
+ require_relative "../test_helper"
2
+ require "redcarpet"
3
+
4
+ class MarkdownPlugin
5
+ def self.setup(app, options = {})
6
+ app.settings[:markdown_options] = options
7
+ end
8
+
9
+ module ClassMethods
10
+ def markdown_renderer
11
+ @_markdown_renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, markdown_options)
12
+ end
13
+
14
+ def markdown_options
15
+ settings[:markdown_options]
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+ def markdown(text)
21
+ res.write(self.class.markdown_renderer.render(text))
22
+ end
23
+ end
24
+ end
25
+
26
+ class MarkdownApp < Ruter
27
+ plugin(MarkdownPlugin, no_intra_emphasis: true)
28
+
29
+ define do
30
+ root do
31
+ get do
32
+ markdown("This is *bongos*, indeed.")
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ class PluginTest < Minitest::Test
39
+ test "setup" do
40
+ refute_nil(MarkdownApp.settings[:markdown_options])
41
+ end
42
+
43
+ test "class methods" do
44
+ assert_instance_of(Redcarpet::Markdown, MarkdownApp.markdown_renderer)
45
+ end
46
+
47
+ test "instance methods" do
48
+ app = Ruter::Test.new(MarkdownApp)
49
+ app.get("/")
50
+
51
+ assert_equal 200, app.res.status
52
+ assert_match "<p>This is <em>bongos</em>, indeed.</p>", app.res.body
53
+ end
54
+ end
@@ -0,0 +1,47 @@
1
+ require_relative "../test_helper"
2
+
3
+ class SettingsTest < MiniTest::Test
4
+ setup do
5
+ Ruter.reset!
6
+ Ruter.settings.clear
7
+ end
8
+
9
+ test "set and get setting" do
10
+ Ruter.set(:environment, "development")
11
+
12
+ assert_equal "development", Ruter.settings[:environment]
13
+ end
14
+
15
+ test "get setting inside handler" do
16
+ Ruter.set(:environment, "development")
17
+
18
+ Ruter.define do
19
+ get do
20
+ res.write(settings[:environment])
21
+ end
22
+ end
23
+
24
+ app = Ruter::Test.new
25
+ app.get("/")
26
+
27
+ assert_equal 200, app.res.status
28
+ assert_equal "development", app.res.body
29
+ end
30
+
31
+ test "inheritance" do
32
+ Ruter.set(:foo, "foo")
33
+ Ruter.set(:bar, "bar")
34
+
35
+ inherited = Class.new(Ruter)
36
+ inherited.set(:bar, "rab")
37
+ inherited.set(:baz, "baz")
38
+
39
+ assert_equal "foo", Ruter.settings[:foo]
40
+ assert_equal "bar", Ruter.settings[:bar]
41
+ assert_nil Ruter.settings[:baz]
42
+
43
+ assert_equal "foo", inherited.settings[:foo]
44
+ assert_equal "rab", inherited.settings[:bar]
45
+ assert_equal "baz", inherited.settings[:baz]
46
+ end
47
+ end
@@ -0,0 +1,8 @@
1
+ $VERBOSE = true
2
+
3
+ require "bundler/setup"
4
+ require "minitest/autorun"
5
+ require "minitest/pride"
6
+ require "minitest/sugar"
7
+ require_relative "../lib/ruter"
8
+ require_relative "../lib/ruter/test"
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Francesco Rodriguez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: syro
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: tilt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: erubi
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.10'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.8'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest-sugar
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '13.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '13.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: standard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.2'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.2'
125
+ description: "Another Rack based web framework \U0001F937\U0001F3FD‍♂️"
126
+ email: frodsan@me.com
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - LICENSE
132
+ - README.md
133
+ - lib/ruter.rb
134
+ - lib/ruter/core.rb
135
+ - lib/ruter/core/request.rb
136
+ - lib/ruter/core/response.rb
137
+ - lib/ruter/middleware.rb
138
+ - lib/ruter/plugin.rb
139
+ - lib/ruter/settings.rb
140
+ - lib/ruter/test.rb
141
+ - lib/ruter/version.rb
142
+ - test/ruter/core_test.rb
143
+ - test/ruter/middleware_test.rb
144
+ - test/ruter/plugin_test.rb
145
+ - test/ruter/settings_test.rb
146
+ - test/test_helper.rb
147
+ homepage: https://github.com/frodsan/ruter
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubygems_version: 3.2.26
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: "Another Rack based web framework \U0001F937\U0001F3FD‍♂️"
170
+ test_files:
171
+ - test/ruter/core_test.rb
172
+ - test/ruter/middleware_test.rb
173
+ - test/ruter/plugin_test.rb
174
+ - test/ruter/settings_test.rb
175
+ - test/test_helper.rb