tynn 0.0.4 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tynn/all_methods.rb +17 -0
  3. data/lib/tynn/environment.rb +12 -8
  4. data/lib/tynn/matchers.rb +37 -19
  5. data/lib/tynn/request.rb +4 -0
  6. data/lib/tynn/response.rb +151 -0
  7. data/lib/tynn/secure_headers.rb +28 -4
  8. data/lib/tynn/session.rb +74 -3
  9. data/lib/tynn/ssl.rb +2 -2
  10. data/lib/tynn/static.rb +26 -0
  11. data/lib/tynn/test.rb +37 -2
  12. data/lib/tynn/version.rb +7 -2
  13. data/lib/tynn.rb +88 -8
  14. data/test/all_methods_test.rb +16 -0
  15. data/test/core_test.rb +0 -86
  16. data/test/matchers_test.rb +11 -8
  17. data/test/middleware_test.rb +79 -0
  18. data/test/protection_test.rb +37 -0
  19. data/test/session_test.rb +1 -6
  20. metadata +45 -52
  21. data/.gems +0 -10
  22. data/docs/bin/build +0 -38
  23. data/docs/guides/security.md +0 -1
  24. data/docs/index.md +0 -89
  25. data/docs/layout.html +0 -61
  26. data/docs/public/.gitignore +0 -1
  27. data/docs/public/css/styles.css +0 -111
  28. data/docs/public/guides/.gitignore +0 -2
  29. data/docs/syro/syro.rb +0 -112
  30. data/examples/composition.ru +0 -40
  31. data/examples/hello.ru +0 -9
  32. data/examples/protection.ru +0 -18
  33. data/examples/render.ru +0 -13
  34. data/examples/views/home.erb +0 -1
  35. data/examples/views/layout.erb +0 -5
  36. data/lib/tynn/csrf.rb +0 -48
  37. data/lib/tynn/options.rb +0 -9
  38. data/makefile +0 -15
  39. data/test/csrf_test.rb +0 -98
  40. data/test/options_test.rb +0 -16
  41. data/test/views/custom_layout.erb +0 -1
  42. data/test/views/custom_layout.mote +0 -1
  43. data/test/views/layout.erb +0 -1
  44. data/test/views/layout.mote +0 -1
  45. data/test/views/partial.erb +0 -1
  46. data/test/views/partial.mote +0 -1
  47. data/test/views/view.erb +0 -1
  48. data/test/views/view.mote +0 -1
  49. data/tynn.gemspec +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4782dd0580d83defcbda873cd5cd6286e9471dff
4
- data.tar.gz: 4084f3b30cec83e6147b1e2afdde38a381285fe2
3
+ metadata.gz: 4778b186382c25ab99b1687323ec3562c045a0ee
4
+ data.tar.gz: 3b8c8d0c9a80cf5cce5cfb235e27be3b34093197
5
5
  SHA512:
6
- metadata.gz: 99d8b65373259f09d6c340ea148c4da3f1c33c55bdd7686b4ac278a5ed609955bed273f31371842b6521e74580e29def9c6c155e26ab8cdda9da2702edba2d65
7
- data.tar.gz: e6571863d347e2c749582fa4db18abdf79938727e47cc4b8f1d912b650c26a8464c690995385e6f4fe1a34dd1c22e2add4720c4f7ec248c7b7aeff9acdf085a2
6
+ metadata.gz: ef2895b73ebb6b9180e747a28499de003fb430a6e6eaee6027f7d7ae5c2569c9161347aed31aacb25552ce010dff48a01ebdbdf39af026f61702316c38ac3036
7
+ data.tar.gz: 47e3ccf946f417ba6c444d4197d511eef8df1c987e83468214a5949d3ffa6c46a89e891da13d032e077bdd6b1781ec64313a60a3a399e5eb259ced0513fe6869
@@ -0,0 +1,17 @@
1
+ module Tynn::AllMethods
2
+ def head
3
+ if root? && req.head?
4
+ yield
5
+
6
+ halt(res.finish)
7
+ end
8
+ end
9
+
10
+ def options
11
+ if root? && req.options?
12
+ yield
13
+
14
+ halt(res.finish)
15
+ end
16
+ end
17
+ end
@@ -1,19 +1,23 @@
1
1
  # Adds helper methods to get and check the current environment.
2
2
  #
3
- # require "tynn"
4
- # require "tynn/environment"
3
+ # ```
4
+ # require "tynn"
5
+ # require "tynn/environment"
5
6
  #
6
- # Tynn.helpers(Tynn::Environment)
7
+ # Tynn.helpers(Tynn::Environment)
7
8
  #
8
- # Tynn.environment # => :development
9
+ # Tynn.environment # => :development
9
10
  #
10
- # Tynn.development? # => true
11
- # Tynn.production? # => false
12
- # Tynn.test? # => false
11
+ # Tynn.development? # => true
12
+ # Tynn.production? # => false
13
+ # Tynn.test? # => false
14
+ # ```
13
15
  #
14
16
  # By default, the environment is based on `ENV["RACK_ENV"]`.
15
17
  #
16
- # Tynn.helpers(Tynn::Environment, env: ENV["RACK_ENV"])
18
+ # ```
19
+ # Tynn.helpers(Tynn::Environment, env: ENV["RACK_ENV"])
20
+ # ```
17
21
  #
18
22
  module Tynn::Environment
19
23
  def self.setup(app, env: ENV["RACK_ENV"]) # :nodoc:
data/lib/tynn/matchers.rb CHANGED
@@ -1,22 +1,26 @@
1
1
  # Adds extra matchers to Tynn.
2
2
  #
3
- # require "tynn"
4
- # require "tynn/matchers"
3
+ # ```
4
+ # require "tynn"
5
+ # require "tynn/matchers"
5
6
  #
6
- # Tynn.helpers(Tynn::Matchers)
7
+ # Tynn.helpers(Tynn::Matchers)
8
+ # ```
7
9
  #
8
10
  module Tynn::Matchers
9
11
  # A catch-all matcher.
10
12
  #
11
- # Tynn.define do
12
- # authenticated? do
13
- # # ...
14
- # end
13
+ # ```
14
+ # Tynn.define do
15
+ # authenticated? do
16
+ # # ...
17
+ # end
15
18
  #
16
- # default do # on true
17
- # # ...
18
- # end
19
- # end
19
+ # default do # on true
20
+ # # ...
21
+ # end
22
+ # end
23
+ # ```
20
24
  #
21
25
  # :call-seq: default(&block)
22
26
  #
@@ -26,15 +30,29 @@ module Tynn::Matchers
26
30
  halt(res.finish)
27
31
  end
28
32
 
29
- # Match if the given `params` are present.
33
+ # Match if the given `key` is present in `req.params`.
30
34
  #
31
- # Tynn.define do
32
- # on param?(:token) do
33
- # # ...
34
- # end
35
- # end
35
+ # ```
36
+ # Tynn.define do
37
+ # param(:user) do |params|
38
+ # user = User.create(params)
36
39
  #
37
- def param?(*params)
38
- return params.all? { |param| (v = req[param]) && !v.empty? }
40
+ # # ...
41
+ # end
42
+ #
43
+ # default do
44
+ # res.write("missing param")
45
+ # end
46
+ # end
47
+ # ```
48
+ #
49
+ # :call-seq: param(key, &block)
50
+ #
51
+ def param(key)
52
+ if (v = req[key]) && !v.empty?
53
+ yield(v)
54
+
55
+ halt(res.finish)
56
+ end
39
57
  end
40
58
  end
@@ -0,0 +1,4 @@
1
+ class Tynn
2
+ class Request < Rack::Request
3
+ end
4
+ end
@@ -0,0 +1,151 @@
1
+ class Tynn
2
+ class Response < Syro::Response
3
+ ##
4
+ # :method: []
5
+ #
6
+ # Returns the response header corresponding to `key`.
7
+ #
8
+ # res["Content-Type"] # => "text/html"
9
+ # res["Content-Length"] # => "42"
10
+
11
+ ##
12
+ # :method: []=
13
+ # :call-seq: []=(value)
14
+ #
15
+ # Sets the given `value` with the header corresponding to `key`.
16
+ #
17
+ # res["Content-Type"] = "application/json"
18
+ # res["Content-Type"] # => "application/json"
19
+
20
+ ##
21
+ # :method: body
22
+ #
23
+ # Returns the body of the response.
24
+ #
25
+ # res.body
26
+ # # => []
27
+ #
28
+ # res.write("there is")
29
+ # res.write("no try")
30
+ #
31
+ # res.body
32
+ # # => ["there is", "no try"]
33
+
34
+ ##
35
+ # :method: finish
36
+ #
37
+ # Returns an array with three elements: the status, headers and body.
38
+ # If the status is not set, the status is set to 404 if empty body,
39
+ # otherwise the status is set to 200 and updates the `Content-Type`
40
+ # header to `text/html`.
41
+ #
42
+ # res.status = 200
43
+ # res.finish
44
+ # # => [200, {}, []]
45
+ #
46
+ # res.status = nil
47
+ # res.finish
48
+ # # => [404, {}, []]
49
+ #
50
+ # res.status = nil
51
+ # res.write("yo!")
52
+ # res.finish
53
+ # # => [200, { "Content-Type" => "text/html" }, ["yo!"]]
54
+
55
+ ##
56
+ # :method: headers
57
+ #
58
+ # Returns a hash with the response headers.
59
+ #
60
+ # res.headers
61
+ # # => { "Content-Type" => "text/html", "Content-Length" => "42" }
62
+
63
+ ##
64
+ # :method: redirect
65
+ # :call-seq: redirect(path, 302)
66
+ #
67
+ # Sets the `Location` header to `path` and updates the status to
68
+ # `status`. By default, `status` is `302`.
69
+ #
70
+ # res.redirect("/path")
71
+ #
72
+ # res["Location"] # => "/path"
73
+ # res.status # => 302
74
+ #
75
+ # res.redirect("http://tynn.ru", 303)
76
+ #
77
+ # res["Location"] # => "http://tynn.ru"
78
+ # res.status # => 303
79
+
80
+ ##
81
+ # :method: status
82
+ #
83
+ # Returns the status of the response.
84
+ #
85
+ # res.status # => 200
86
+ #
87
+
88
+ ##
89
+ # :method: status=
90
+ # :call-seq: status=(status)
91
+ #
92
+ # Sets the status of the response.
93
+ #
94
+ # res.status = 200
95
+ #
96
+
97
+ ##
98
+ # :method: write
99
+ # :call-seq: write(str)
100
+ #
101
+ # Appends `str` to `body` and updates the `Content-Length` header.
102
+ #
103
+ # res.body # => []
104
+ #
105
+ # res.write("foo")
106
+ # res.write("bar")
107
+ #
108
+ # res.body
109
+ # # => ["foo", "bar"]
110
+ #
111
+ # res["Content-Length"]
112
+ # # => 6
113
+
114
+ ##
115
+ # :method: set_cookie
116
+ # :call-seq: set_cookie(key, value)
117
+ #
118
+ # Sets a cookie into the response.
119
+ #
120
+ # res.set_cookie("foo", "bar")
121
+ # res["Set-Cookie"] # => "foo=bar"
122
+ #
123
+ # res.set_cookie("foo2", "bar2")
124
+ # res["Set-Cookie"] # => "foo=bar\nfoo2=bar2"
125
+ #
126
+ # res.set_cookie("bar", {
127
+ # domain: ".example.com",
128
+ # path: "/",
129
+ # # max_age: 0,
130
+ # # expires: Time.now + 10_000,
131
+ # secure: true,
132
+ # httponly: true,
133
+ # value: "bar"
134
+ # })
135
+ #
136
+ # res["Set-Cookie"].split("\n").last
137
+ # # => "bar=bar; domain=.example.com; path=/; secure; HttpOnly
138
+ #
139
+ # **NOTE:** This method doesn't sign and/or encrypt the value of the cookie.
140
+
141
+ ##
142
+ # :method: delete_cookie
143
+ # :call-seq: delete_cookie(key, value = {})
144
+ #
145
+ # Deletes cookie.
146
+ #
147
+ # res.set_cookie("foo", "bar")
148
+ # res["Set-Cookie"]
149
+ # # => "foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
150
+ end
151
+ end
@@ -1,9 +1,33 @@
1
1
  # Adds security related HTTP headers.
2
2
  #
3
- # require "tynn"
4
- # require "tynn/secure_headers"
3
+ # ```
4
+ # require "tynn"
5
+ # require "tynn/secure_headers"
5
6
  #
6
- # Tynn.helpers(Tynn::SecureHeaders)
7
+ # Tynn.helpers(Tynn::SecureHeaders)
8
+ # ```
9
+ #
10
+ # This helper applies the following headers:
11
+ #
12
+ # * **X-Content-Type-Options:** Prevents IE and Chrome from
13
+ # [content type sniffing][mime-sniffing].
14
+ #
15
+ # * **X-Frame-Options (XFO):** Provides [Clickjacking][clickjacking]
16
+ # protection. Check the [X-Frame-Options draft][x-frame-options] for
17
+ # more information.
18
+ #
19
+ # * **X-Permitted-Cross-Domain-Policies:** Restricts Adobe Flash Player's
20
+ # access to data. Check this [article][pcdp] for more information.
21
+ #
22
+ # * **X-XSS-Protection:** Enables the [XSS][xss] protection filter built
23
+ # into IE, Chrome and Safari. This filter is usually enabled by default,
24
+ # the use of this header is to re-enable it if it was disabled by the user.
25
+ #
26
+ # [clickjacking]: https://www.owasp.org/index.php/Clickjacking
27
+ # [mime-sniffing]: https://msdn.microsoft.com/library/gg622941(v=vs.85).aspx
28
+ # [pcdp]: https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
29
+ # [x-frame-options]: https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-02
30
+ # [xss]: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
7
31
  #
8
32
  module Tynn::SecureHeaders
9
33
  HEADERS = {
@@ -13,7 +37,7 @@ module Tynn::SecureHeaders
13
37
  "X-XSS-Protection" => "1; mode=block"
14
38
  } # :nodoc:
15
39
 
16
- def default_headers
40
+ def default_headers # :nodoc:
17
41
  return super.merge(HEADERS)
18
42
  end
19
43
  end
data/lib/tynn/session.rb CHANGED
@@ -1,13 +1,84 @@
1
+ # Adds simple cookie based session management. If a secret token is
2
+ # given, it signs the cookie data to ensure that it cannot be altered
3
+ # by unauthorized means.
4
+ #
5
+ # ```
6
+ # require "tynn"
7
+ # require "tynn/session"
8
+ #
9
+ # Tynn.helpers(Tynn::Session, secret: "__change_me__")
10
+ #
11
+ # Tynn.define do
12
+ # root do
13
+ # res.write(sprintf("hei %s", session[:username]))
14
+ # end
15
+ #
16
+ # on(:username) do |username|
17
+ # session[:username] = username
18
+ # end
19
+ # end
20
+ # ```
21
+ #
22
+ # The following command generates a cryptographically secure secret ready
23
+ # to use:
24
+ #
25
+ # ```
26
+ # $ ruby -r securerandom -e "puts SecureRandom.hex(64)"
27
+ # ```
28
+ #
29
+ # It's important to keep the token secret. Knowing the token allows an
30
+ # attacker to tamper the data. So, it's recommended to load the token
31
+ # from the environment.
32
+ #
33
+ # ```
34
+ # Tynn.helpers(Tynn::Session, secret: ENV["SESSION_SECRET"])
35
+ # ```
36
+ #
37
+ # Under the hood, Tynn::Session uses the [Rack::Session::Cookie][rack-session]
38
+ # middleware. Thus, supports all the options available for this middleware.
39
+ #
40
+ # * `:key` - the name of the cookie. Defaults to `"rack.session"`.
41
+ # * `:expire_after` - sets the lifespan of the cookie. If `nil`,
42
+ # the cookie will be deleted after the user close the browser.
43
+ # Defaults to `nil`.
44
+ # * `:httponly` - if `true`, sets the [HttpOnly][cookie-httponly] attribute.
45
+ # This mitigates the risk of client side scripting accessing the cookie.
46
+ # Defaults to `true`.
47
+ # * `:secure` - if `true`, sets the [Secure][cookie-secure] attribute.
48
+ # This tells the browser to only transmit the cookie over HTTPS. Defaults
49
+ # to `false`.
50
+ #
51
+ # ```
52
+ # Tynn.helpers(
53
+ # Tynn::Session,
54
+ # key: "app",
55
+ # secret: ENV["SESSION_SECRET"],
56
+ # expire_after: 36_000, # seconds
57
+ # httponly: true,
58
+ # secure: true
59
+ # )
60
+ # ```
61
+ #
62
+ # [cookie-httponly]: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#HttpOnly_Attribute
63
+ # [cookie-secure]: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Secure_Attribute
64
+ # [rack-session]: http://www.rubydoc.info/gems/rack/Rack/Session/Cookie
65
+ #
1
66
  module Tynn::Session
2
67
  RACK_SESSION = "rack.session".freeze # :nodoc:
3
68
 
4
69
  def self.setup(app, options = {}) # :nodoc:
5
- options = options.dup
6
- options[:http_only] ||= true
7
-
8
70
  app.use(Rack::Session::Cookie, options)
9
71
  end
10
72
 
73
+ # Returns the session hash.
74
+ #
75
+ # ```
76
+ # session # => {}
77
+ #
78
+ # session[:foo] = "foo"
79
+ # session[:foo] # => "foo"
80
+ # ```
81
+ #
11
82
  def session
12
83
  return env[RACK_SESSION]
13
84
  end
data/lib/tynn/ssl.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Tynn::SSL
2
- def self.setup(app, options = {}) # :nodoc:
3
- app.use(Tynn::SSL::Middleware, options)
2
+ def self.setup(app, hsts: {}) # :nodoc:
3
+ app.use(Tynn::SSL::Middleware, hsts: hsts)
4
4
  end
5
5
 
6
6
  class Middleware # :nodoc:
data/lib/tynn/static.rb CHANGED
@@ -1,3 +1,29 @@
1
+ # Adds support for static files (javascript files, images, stylesheets, etc).
2
+ #
3
+ # ```
4
+ # require "tynn"
5
+ # require "tynn/static"
6
+ #
7
+ # Tynn.helpers(Tynn::Static, ["/js", "/css"])
8
+ # ```
9
+ #
10
+ # By default, serve all requests beginning with the given paths from the folder
11
+ # `public` in the current directory (e.g. `public/js/*`, `public/css/*`). You
12
+ # can change the default by passing the `:root` option.
13
+ #
14
+ # ```
15
+ # Tynn.helpers(Tynn::Static, ["/js", "/css"], root: "assets")
16
+ # ```
17
+ #
18
+ # Under the hood, it uses the [Rack::Static][rack-static] middleware.
19
+ # Thus, supports all the options available for this middleware.
20
+ #
21
+ # ```
22
+ # Tynn.helpers(Tynn::Static, ["/js", "/css"], index: "index.html")
23
+ # ```
24
+ #
25
+ # [rack-static]: http://www.rubydoc.info/gems/rack/Rack/Static
26
+ #
1
27
  module Tynn::Static
2
28
  def self.setup(app, urls, options = {}) # :nodoc:
3
29
  options = options.dup
data/lib/tynn/test.rb CHANGED
@@ -1,10 +1,45 @@
1
1
  require "rack/test"
2
2
 
3
+ # A simple helper class that uses [rack-test][rack-test] to simulate requests
4
+ # to your application.
5
+ #
6
+ # ```
7
+ # require "tynn"
8
+ # require "tynn/test"
9
+ #
10
+ # Tynn.define do
11
+ # root do
12
+ # res.write("hei")
13
+ # end
14
+ # end
15
+ #
16
+ # app = Tynn::Test.new
17
+ # app.get("/")
18
+ #
19
+ # 200 == app.res.status # => true
20
+ # "hei" == app.res.body # => true
21
+ # ```
22
+ #
23
+ # **NOTE:** Tynn doesn't ship with [rack-test][rack-test]. In order to
24
+ # use this plugin, you need to install it first.
25
+ #
26
+ # [rack-test]: http://rubygems.org/gems/rack-test
27
+ #
3
28
  class Tynn::Test
4
29
  include Rack::Test::Methods
5
30
 
6
- def initialize(app = Tynn) # :nodoc:
7
- @app = app
31
+ # Instantiates a new Tynn::Test object with the given `application` to test.
32
+ #
33
+ # ```
34
+ # class API < Tynn
35
+ # end
36
+ #
37
+ # app = Tynn::Test.new(API)
38
+ # app.get("/json")
39
+ # ```
40
+ #
41
+ def initialize(application = Tynn)
42
+ @app = application
8
43
  end
9
44
 
10
45
  def app # :nodoc:
data/lib/tynn/version.rb CHANGED
@@ -1,3 +1,8 @@
1
- class Tynn
2
- VERSION = "0.0.4"
1
+ class Tynn # :nodoc:
2
+ VERSION = [
3
+ MAJOR_VERSION = 1,
4
+ MINOR_VERSION = 0,
5
+ PATCH_VERSION = 0,
6
+ PRE_VERSION = "rc1"
7
+ ].compact.join(".")
3
8
  end
data/lib/tynn.rb CHANGED
@@ -1,15 +1,48 @@
1
1
  require "seteable"
2
2
  require "syro"
3
+ require_relative "tynn/request"
4
+ require_relative "tynn/response"
5
+ require_relative "tynn/version"
3
6
 
4
- class Tynn < Syro::Deck
7
+ class Tynn
5
8
  include Seteable
9
+ include Syro::Deck::API
6
10
 
11
+ # Sets the application handler.
12
+ #
13
+ # ```
14
+ # class Users < Tynn
15
+ # end
16
+ #
17
+ # Users.define do
18
+ # on(:id) do |id|
19
+ # get do
20
+ # res.write("GET /users")
21
+ # end
22
+ #
23
+ # post do
24
+ # res.write("POST /users")
25
+ # end
26
+ # end
27
+ # end
28
+ # ```
29
+ #
7
30
  def self.define(&block)
8
31
  @syro = Syro.new(self, &block)
9
32
  end
10
33
 
11
- def self.use(_middleware, *args, &block)
12
- middleware << proc { |app| _middleware.new(app, *args, &block) }
34
+ # Adds given Rack `middleware` to the stack.
35
+ #
36
+ # ```
37
+ # require "rack/common_logger"
38
+ # require "rack/show_exceptions"
39
+ #
40
+ # Tynn.use(Rack::CommonLogger)
41
+ # Tynn.use(Rack::ShowExceptions)
42
+ # ```
43
+ #
44
+ def self.use(middleware, *args, &block)
45
+ __middleware << proc { |app| middleware.new(app, *args, &block) }
13
46
  end
14
47
 
15
48
  def self.call(env) # :nodoc:
@@ -19,14 +52,14 @@ class Tynn < Syro::Deck
19
52
  def self.to_app # :nodoc:
20
53
  fail("Missing application handler. Try #{ self }.define") unless @syro
21
54
 
22
- if middleware.empty?
55
+ if __middleware.empty?
23
56
  return @syro
24
57
  else
25
- return middleware.reverse.inject(@syro) { |a, m| m.call(a) }
58
+ return __middleware.reverse.inject(@syro) { |a, m| m.call(a) }
26
59
  end
27
60
  end
28
61
 
29
- def self.middleware # :nodoc:
62
+ def self.__middleware # :nodoc:
30
63
  return @middleware ||= []
31
64
  end
32
65
 
@@ -35,6 +68,47 @@ class Tynn < Syro::Deck
35
68
  @middleware = []
36
69
  end
37
70
 
71
+ # Extends Tynn functionality with the given `helper` module.
72
+ #
73
+ # ```
74
+ # module AppName
75
+ # def self.setup(app, name)
76
+ # app.settings[:app_name] = name
77
+ # end
78
+ #
79
+ # def app_name
80
+ # return self.class.app_name
81
+ # end
82
+ #
83
+ # module ClassMethods
84
+ # def app_name=(new_name)
85
+ # settings[:app_name] = new_name
86
+ # end
87
+ #
88
+ # def app_name
89
+ # return settings[:app_name]
90
+ # end
91
+ # end
92
+ # end
93
+ #
94
+ # Tynn.helpers(AppName, "MyApplication")
95
+ #
96
+ # Tynn.app_name # => "MyApplication"
97
+ #
98
+ # Tynn.app_name = "MyGreatestApp"
99
+ # Tynn.app_name # => "MyGreatestApp"
100
+ #
101
+ # Tynn.define do
102
+ # root do
103
+ # res.write(app_name)
104
+ # end
105
+ # end
106
+ # ```
107
+ #
108
+ # Check the [helpers][examples] that come with tynn for more examples.
109
+ #
110
+ # [examples]: https://github.com/harmoni/tynn/tree/master/lib/tynn
111
+ #
38
112
  def self.helpers(helper, *args, &block)
39
113
  self.include(helper)
40
114
 
@@ -46,6 +120,12 @@ class Tynn < Syro::Deck
46
120
  helper.setup(self, *args, &block)
47
121
  end
48
122
  end
49
- end
50
123
 
51
- require_relative "tynn/version"
124
+ def request_class # :nodoc:
125
+ return Tynn::Request
126
+ end
127
+
128
+ def response_class # :nodoc:
129
+ return Tynn::Response
130
+ end
131
+ end
@@ -0,0 +1,16 @@
1
+ require_relative "../lib/tynn/all_methods"
2
+
3
+ Tynn.helpers(Tynn::AllMethods)
4
+
5
+ test "methods" do
6
+ [:head, :options].each do |method|
7
+ Tynn.define do
8
+ send(method) { res.write "" }
9
+ end
10
+
11
+ app = Tynn::Test.new
12
+ app.send(method, "/")
13
+
14
+ assert_equal 200, app.res.status
15
+ end
16
+ end