tynn 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
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