tynn 1.0.0.rc2 → 1.0.0.rc3

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.
data/lib/tynn/json.rb CHANGED
@@ -1,42 +1,46 @@
1
1
  require "json"
2
2
 
3
3
  class Tynn
4
- # Adds helper methods for json generation.
4
+ # Public: Adds helper methods for json generation.
5
5
  #
6
- # ```
7
- # require "tynn"
8
- # require "tynn/json"
6
+ # Examples
9
7
  #
10
- # Tynn.helpers(Tynn::JSON)
11
- # ```
8
+ # require "tynn"
9
+ # require "tynn/json"
10
+ #
11
+ # Tynn.helpers(Tynn::JSON)
12
12
  #
13
13
  module JSON
14
- JSON_CONTENT_TYPE = "application/json".freeze # :nodoc:
14
+ CONTENT_TYPE = "application/json".freeze
15
15
 
16
- # Calls `to_json` on `data` and writes the generated \JSON object into
17
- # the response body. Also, It automatically sets the `Content-Type`
18
- # header to `application/json`.
19
- #
20
- # ```
21
- # Tynn.define do
22
- # on("hash") do
23
- # json(foo: "bar")
24
- # end
25
- #
26
- # on("array") do
27
- # json([1, 2, 3])
28
- # end
29
- #
30
- # on("to_json") do
31
- # json(Model.first)
32
- # end
33
- # end
34
- # ```
35
- #
36
- def json(data)
37
- res.headers[Rack::CONTENT_TYPE] = JSON_CONTENT_TYPE
16
+ module InstanceMethods
17
+ # Public: Calls +to_json+ on +data+ and writes the generated \JSON
18
+ # object into the response body. Also, It automatically sets the
19
+ # +Content-Type+ header to +application/json+.
20
+ #
21
+ # data - Any object that responds to +to_json+.
22
+ #
23
+ # Examples
24
+ #
25
+ # Tynn.define do
26
+ # on("hash") do
27
+ # json(foo: "bar")
28
+ # end
29
+ #
30
+ # on("array") do
31
+ # json([1, 2, 3])
32
+ # end
33
+ #
34
+ # on("to_json") do
35
+ # json(Model.first)
36
+ # end
37
+ # end
38
+ #
39
+ def json(data)
40
+ res.headers[Rack::CONTENT_TYPE] = Tynn::JSON::CONTENT_TYPE
38
41
 
39
- res.write(data.to_json)
42
+ res.write(data.to_json)
43
+ end
40
44
  end
41
45
  end
42
46
  end
data/lib/tynn/matchers.rb CHANGED
@@ -1,59 +1,61 @@
1
1
  class Tynn
2
- # Adds extra matchers to Tynn.
2
+ # Public: Adds extra matchers to Tynn.
3
3
  #
4
- # ```
5
- # require "tynn"
6
- # require "tynn/matchers"
4
+ # Examples
7
5
  #
8
- # Tynn.helpers(Tynn::Matchers)
9
- # ```
6
+ # require "tynn"
7
+ # require "tynn/matchers"
8
+ #
9
+ # Tynn.helpers(Tynn::Matchers)
10
10
  #
11
11
  module Matchers
12
- # A catch-all matcher.
13
- #
14
- # ```
15
- # Tynn.define do
16
- # authenticated? do
17
- # # ...
18
- # end
19
- #
20
- # default do # on true
21
- # # ...
22
- # end
23
- # end
24
- # ```
25
- #
26
- # :call-seq: default(&block)
27
- #
28
- def default
29
- yield
12
+ module InstanceMethods
13
+ # Public: A catch-all matcher.
14
+ #
15
+ # Examples
16
+ #
17
+ # Tynn.define do
18
+ # authenticated? do
19
+ # # ...
20
+ # end
21
+ #
22
+ # default do # on true
23
+ # # ...
24
+ # end
25
+ # end
26
+ #
27
+ # :call-seq: default(&block)
28
+ #
29
+ def default
30
+ yield
30
31
 
31
- halt(res.finish)
32
- end
32
+ halt(res.finish)
33
+ end
33
34
 
34
- # Match if the given `key` is present in `req.params`.
35
- #
36
- # ```
37
- # Tynn.define do
38
- # param(:user) do |params|
39
- # user = User.create(params)
40
- #
41
- # # ...
42
- # end
43
- #
44
- # default do
45
- # res.write("missing user param")
46
- # end
47
- # end
48
- # ```
49
- #
50
- # :call-seq: param(key, &block)
51
- #
52
- def param(key)
53
- if (v = req[key]) && !v.empty?
54
- yield(v)
35
+ # Public: Match if the given +key+ is present in +req.params+.
36
+ #
37
+ # Examples
38
+ #
39
+ # Tynn.define do
40
+ # param(:user) do |params|
41
+ # user = User.create(params)
42
+ #
43
+ # # ...
44
+ # end
45
+ #
46
+ # default do
47
+ # res.write("missing user param")
48
+ # end
49
+ # end
50
+ #
51
+ # :call-seq: param(key, &block)
52
+ #
53
+ def param(key)
54
+ if (v = req[key]) && !v.empty?
55
+ yield(v)
55
56
 
56
- halt(res.finish)
57
+ halt(res.finish)
58
+ end
57
59
  end
58
60
  end
59
61
  end
@@ -1,18 +1,20 @@
1
1
  class Tynn
2
2
  module NotFound
3
- def call(*) # :nodoc:
4
- result = super
3
+ module InstanceMethods
4
+ def call(*) # :nodoc:
5
+ result = super
5
6
 
6
- if result[0] == 404 && result[2].empty?
7
- not_found
7
+ if result[0] == 404 && result[2].empty?
8
+ not_found
8
9
 
9
- return res.finish
10
- else
11
- return result
10
+ return res.finish
11
+ else
12
+ return result
13
+ end
12
14
  end
13
- end
14
15
 
15
- def not_found # :nodoc:
16
+ def not_found # :nodoc:
17
+ end
16
18
  end
17
19
  end
18
20
  end
@@ -1,41 +1,52 @@
1
1
  require_relative "secure_headers"
2
2
 
3
3
  class Tynn
4
- # Adds security measures against common attacks.
4
+ # Public: Adds security measures against common attacks.
5
5
  #
6
- # ```
7
- # require "tynn"
8
- # require "tynn/protection"
6
+ # Examples
9
7
  #
10
- # Tynn.helpers(Tynn::Protection)
11
- # ```
8
+ # require "tynn"
9
+ # require "tynn/protection"
10
+ #
11
+ # Tynn.helpers(Tynn::Protection)
12
12
  #
13
13
  # If you are using SSL/TLS (HTTPS), it's recommended to set
14
- # the `:ssl` option:
14
+ # the +:ssl+ option:
15
+ #
16
+ # Examples
15
17
  #
16
- # ```
17
- # require "tynn"
18
- # require "tynn/protection"
18
+ # require "tynn"
19
+ # require "tynn/protection"
19
20
  #
20
- # Tynn.helpers(Tynn::Protection, ssl: true)
21
- # ```
21
+ # Tynn.helpers(Tynn::Protection, ssl: true)
22
22
  #
23
23
  # By default, it includes the following security helpers:
24
24
  #
25
- # * Tynn::SecureHeaders
25
+ # - Tynn::SecureHeaders
26
26
  #
27
- # If the `:ssl` option is `true`, includes:
27
+ # If the +:ssl+ option is +true+, includes:
28
28
  #
29
- # * Tynn::SSL
29
+ # - Tynn::HSTS
30
+ #
31
+ # - Tynn::ForceSSL
30
32
  #
31
33
  module Protection
32
- def self.setup(app, ssl: false, hsts: {}) # :nodoc:
34
+ # Internal: Configures security related extensions.
35
+ def self.setup(app, ssl: false, force_ssl: ssl, hsts: {})
33
36
  app.helpers(Tynn::SecureHeaders)
34
37
 
35
38
  if ssl
36
- require_relative "ssl"
39
+ app.settings[:ssl] = true
40
+
41
+ require_relative "hsts"
42
+
43
+ app.helpers(Tynn::HSTS, hsts)
44
+ end
45
+
46
+ if force_ssl
47
+ require_relative "force_ssl"
37
48
 
38
- app.helpers(Tynn::SSL, hsts: hsts)
49
+ app.helpers(Tynn::ForceSSL)
39
50
  end
40
51
  end
41
52
  end
data/lib/tynn/render.rb CHANGED
@@ -8,41 +8,42 @@ class Tynn
8
8
  views: options.fetch(:views, File.expand_path("views", Dir.pwd)),
9
9
  engine: options.fetch(:engine, "erb"),
10
10
  engine_opts: {
11
- default_encoding: Encoding.default_external,
12
- outvar: "@_output"
11
+ escape_html: true
13
12
  }.merge!(options.fetch(:options, {}))
14
13
  )
15
14
  end
16
15
 
17
- def render(template, locals = {}, layout = settings[:layout])
18
- res.headers[Rack::CONTENT_TYPE] ||= Syro::Response::DEFAULT
16
+ module InstanceMethods
17
+ def render(template, locals = {}, layout = settings[:layout])
18
+ res.headers[Rack::CONTENT_TYPE] ||= Syro::Response::DEFAULT
19
19
 
20
- res.write(view(template, locals, layout))
21
- end
20
+ res.write(view(template, locals, layout))
21
+ end
22
22
 
23
- def view(template, locals = {}, layout = settings[:layout])
24
- return partial(layout, locals.merge(content: partial(template, locals)))
25
- end
23
+ def view(template, locals = {}, layout = settings[:layout])
24
+ return partial(layout, locals.merge(content: partial(template, locals)))
25
+ end
26
26
 
27
- def partial(template, locals = {})
28
- return tilt(template_path(template), locals, settings[:engine_opts])
29
- end
27
+ def partial(template, locals = {})
28
+ return tilt(template_path(template), locals, settings[:engine_opts])
29
+ end
30
30
 
31
- private
31
+ private
32
32
 
33
- def tilt(file, locals = {}, opts = {})
34
- return tilt_cache.fetch(file) { Tilt.new(file, 1, opts) }.render(self, locals)
35
- end
33
+ def tilt(file, locals = {}, opts = {})
34
+ return tilt_cache.fetch(file) { Tilt.new(file, 1, opts) }.render(self, locals)
35
+ end
36
36
 
37
- def tilt_cache
38
- return Thread.current[:tilt_cache] ||= Tilt::Cache.new
39
- end
37
+ def tilt_cache
38
+ return Thread.current[:tilt_cache] ||= Tilt::Cache.new
39
+ end
40
40
 
41
- def template_path(template)
42
- dir = settings[:views]
43
- ext = settings[:engine]
41
+ def template_path(template)
42
+ dir = settings[:views]
43
+ ext = settings[:engine]
44
44
 
45
- return File.join(dir, "#{ template }.#{ ext }")
45
+ return File.join(dir, "#{ template }.#{ ext }")
46
+ end
46
47
  end
47
48
  end
48
49
  end
data/lib/tynn/response.rb CHANGED
@@ -1,185 +1,197 @@
1
1
  class Tynn
2
- # It provides convenience methods to construct a Rack response.
2
+ # Public: It provides convenience methods to construct a Rack response.
3
3
  #
4
- # ```
5
- # res = Tynn::Response.new
6
- # res.status = 200
7
- # res["Content-Type"] = "text/html"
8
- # res.write("foo")
9
- # ```
4
+ # Examples
10
5
  #
11
- # [Tynn::Response#finish][finish] returns a response as per
12
- # [Rack's specification][rack-spec].
6
+ # res = Tynn::Response.new
13
7
  #
14
- # ```
15
- # res.finish
16
- # # => [200, { "Content-Type" => "text/html", "Content-Length" => 3 }, ["foo"]]
17
- # ```
8
+ # res.status = 200
9
+ # res["Content-Type"] = "text/html"
10
+ # res.write("foo")
18
11
  #
19
- # [finish]: #method-i-finish
20
- # [rack-spec]: http://www.rubydoc.info/github/rack/rack/master/file/SPEC
12
+ # res.finish
13
+ # # => [200, { "Content-Type" => "text/html", "Content-Length" => 3 }, ["foo"]]
21
14
  #
22
15
  class Response < Syro::Response
23
- ##
24
- # :method: new
25
- # :call-seq: new(headers = {})
16
+ # Public: Initializes a new response object.
26
17
  #
27
- # Initializes a new response object with the given `headers`.
18
+ # headers - A Hash of initial headers. Defaults to <tt>{}</tt>.
28
19
  #
29
- # ```
30
- # Tynn::Response.new.headers
31
- # # => {}
20
+ # Examples
32
21
  #
33
- # Tynn::Response.new("Content-Type" => "text/plain").headers
34
- # # => { "Content-Type" => "text/plain" }
35
- # ```
22
+ # Tynn::Response.new.headers
23
+ # # => {}
24
+ #
25
+ # Tynn::Response.new("Content-Type" => "text/plain").headers
26
+ # # => { "Content-Type" => "text/plain" }
27
+ #
28
+ # Signature
29
+ #
30
+ # new(headers = {})
31
+ #
32
+ # Inherited by Syro::Response.
36
33
 
37
- ##
38
- # :method: []
34
+ # Public: Returns the response header corresponding to +key+.
35
+ #
36
+ # key - A String HTTP header field name.
39
37
  #
40
- # Returns the response header corresponding to `key`.
38
+ # Examples
41
39
  #
42
40
  # res["Content-Type"] # => "text/html"
43
41
  # res["Content-Length"] # => "42"
42
+ #
43
+ # Signature
44
+ #
45
+ # [](key)
46
+ #
47
+ # Inherited by Syro::Response.
44
48
 
45
- ##
46
- # :method: []=
47
- # :call-seq: []=(value)
49
+ # Public: Sets the given +value+ with the header corresponding to +key+.
50
+ #
51
+ # key - A String HTTP header field name.
52
+ # value - A String HTTP header field value.
48
53
  #
49
- # Sets the given `value` with the header corresponding to `key`.
54
+ # Examples
50
55
  #
51
56
  # res["Content-Type"] = "application/json"
52
57
  # res["Content-Type"] # => "application/json"
58
+ #
59
+ # Signature
60
+ #
61
+ # []=(key, value)
62
+ #
63
+ # Inherited by Syro::Response.
53
64
 
54
- ##
55
- # :method: body
65
+ # Public: Returns the body of the response.
56
66
  #
57
- # Returns the body of the response.
67
+ # Examples
58
68
  #
59
- # res.body
60
- # # => []
69
+ # res.body
70
+ # # => []
61
71
  #
62
- # res.write("there is")
63
- # res.write("no try")
72
+ # res.write("there is")
73
+ # res.write("no try")
64
74
  #
65
- # res.body
66
- # # => ["there is", "no try"]
67
-
68
- ##
69
- # :method: finish
70
- #
71
- # Returns an array with three elements: the status, headers and body.
72
- # If the status is not set, the status is set to 404 if empty body,
73
- # otherwise the status is set to 200 and updates the `Content-Type`
74
- # header to `text/html`.
75
- #
76
- # res.status = 200
77
- # res.finish
78
- # # => [200, {}, []]
79
- #
80
- # res.status = nil
81
- # res.finish
82
- # # => [404, {}, []]
83
- #
84
- # res.status = nil
85
- # res.write("yo")
86
- # res.finish
87
- # # => [200, { "Content-Type" => "text/html", "Content-Length" => 2 }, ["yo"]]
88
-
89
- ##
90
- # :method: headers
75
+ # res.body
76
+ # # => ["there is", "no try"]
77
+ #
78
+ # Signature
91
79
  #
92
- # Returns a hash with the response headers.
80
+ # body()
93
81
  #
94
- # res.headers
95
- # # => { "Content-Type" => "text/html", "Content-Length" => "42" }
82
+ # Inherited by Syro::Response.
96
83
 
97
- ##
98
- # :method: redirect
99
- # :call-seq: redirect(path, 302)
84
+ # Public: Returns an Array with three elements: the status, headers
85
+ # and body. If the status is not set, the status is set to +404+ if
86
+ # empty body, otherwise the status is set to +200+ and updates the
87
+ # +Content-Type+ header to +text/html+.
88
+ #
89
+ # Examples
90
+ #
91
+ # res.status = 200
92
+ # res.finish
93
+ # # => [200, {}, []]
100
94
  #
101
- # Sets the `Location` header to `path` and updates the status to
102
- # `status`. By default, `status` is `302`.
95
+ # res.status = nil
96
+ # res.finish
97
+ # # => [404, {}, []]
103
98
  #
104
- # res.redirect("/path")
99
+ # res.status = nil
100
+ # res.write("yo")
101
+ # res.finish
102
+ # # => [200, { "Content-Type" => "text/html", "Content-Length" => 2 }, ["yo"]]
105
103
  #
106
- # res["Location"] # => "/path"
107
- # res.status # => 302
104
+ # Signature
108
105
  #
109
- # res.redirect("http://tynn.ru", 303)
106
+ # finish()
110
107
  #
111
- # res["Location"] # => "http://tynn.ru"
112
- # res.status # => 303
108
+ # Inherited by Syro::Response.
113
109
 
114
- ##
115
- # :method: status
110
+ # Public: Returns a Hash with the response headers.
116
111
  #
117
- # Returns the status of the response.
112
+ # Examples
118
113
  #
119
- # res.status # => 200
114
+ # res.headers
115
+ # # => { "Content-Type" => "text/html", "Content-Length" => "42" }
116
+ #
117
+ # Signature
118
+ #
119
+ # headers()
120
120
  #
121
+ # Inherited by Syro::Response.
121
122
 
122
- ##
123
- # :method: status=
124
- # :call-seq: status=(status)
123
+ # Public: Sets the +Location+ header to +url+ and updates the status
124
+ # to +status+.
125
+ #
126
+ # url - A String URL (relative or absolute) to redirect to.
127
+ # status - An Integer status code. Defaults to +302+.
128
+ #
129
+ # Examples
130
+ #
131
+ # res.redirect("/path")
132
+ #
133
+ # res["Location"] # => "/path"
134
+ # res.status # => 302
135
+ #
136
+ # res.redirect("http://tynn.ru", 303)
137
+ #
138
+ # res["Location"] # => "http://tynn.ru"
139
+ # res.status # => 303
125
140
  #
126
- # Sets the status of the response.
141
+ # Signature
127
142
  #
128
- # res.status = 200
143
+ # redirect(url, status = 302)
129
144
  #
145
+ # Inherited by Syro::Response.
130
146
 
131
- ##
132
- # :method: write
133
- # :call-seq: write(str)
147
+ # Public: Returns the status of the response.
134
148
  #
135
- # Appends `str` to `body` and updates the `Content-Length` header.
149
+ # Examples
136
150
  #
137
- # res.body # => []
151
+ # res.status # => 200
138
152
  #
139
- # res.write("foo")
140
- # res.write("bar")
153
+ # Signature
141
154
  #
142
- # res.body
143
- # # => ["foo", "bar"]
155
+ # status()
144
156
  #
145
- # res["Content-Length"]
146
- # # => 6
157
+ # Inherited by Syro::Response.
147
158
 
148
- ##
149
- # :method: set_cookie
150
- # :call-seq: set_cookie(key, value)
159
+ # Public: Sets the status of the response.
151
160
  #
152
- # Sets a cookie into the response.
161
+ # status - An Integer HTTP status code.
153
162
  #
154
- # res.set_cookie("foo", "bar")
155
- # res["Set-Cookie"] # => "foo=bar"
163
+ # Examples
156
164
  #
157
- # res.set_cookie("foo2", "bar2")
158
- # res["Set-Cookie"] # => "foo=bar\nfoo2=bar2"
165
+ # res.status = 200
159
166
  #
160
- # res.set_cookie("bar", {
161
- # domain: ".example.com",
162
- # path: "/",
163
- # # max_age: 0,
164
- # # expires: Time.now + 10_000,
165
- # secure: true,
166
- # httponly: true,
167
- # value: "bar"
168
- # })
167
+ # Signature
169
168
  #
170
- # res["Set-Cookie"].split("\n").last
171
- # # => "bar=bar; domain=.example.com; path=/; secure; HttpOnly
169
+ # status=(status)
172
170
  #
173
- # **NOTE:** This method doesn't sign and/or encrypt the value of the cookie.
171
+ # Inherited by Syro::Response.
174
172
 
175
- ##
176
- # :method: delete_cookie
177
- # :call-seq: delete_cookie(key, value = {})
173
+ # Public: Appends +str+ to the response body and updates the
174
+ # +Content-Length+ header.
175
+ #
176
+ # str - Any object that responds to +to_s+.
177
+ #
178
+ # Examples
179
+ #
180
+ # res.body # => []
181
+ #
182
+ # res.write("foo")
183
+ # res.write("bar")
184
+ #
185
+ # res.body
186
+ # # => ["foo", "bar"]
187
+ #
188
+ # res["Content-Length"]
189
+ # # => 6
190
+ #
191
+ # Signature
178
192
  #
179
- # Deletes cookie.
193
+ # write(str)
180
194
  #
181
- # res.set_cookie("foo", "bar")
182
- # res["Set-Cookie"]
183
- # # => "foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
195
+ # Inherited by Syro::Response.
184
196
  end
185
197
  end