ruter 1.0.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.
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