kawaii-core 0.2.0 → 0.2.1
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 +4 -4
- data/README.md +21 -0
- data/examples/json.ru +21 -0
- data/lib/kawaii.rb +1 -0
- data/lib/kawaii/formats.rb +185 -0
- data/lib/kawaii/route_handler.rb +3 -2
- data/lib/kawaii/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ac5408912341100edad70023e719dc15b735d92
|
4
|
+
data.tar.gz: 2a765e3f6108277eec769b70498b6a9fb71e7194
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f53adf2a8620017251471e3ef461b9af0ba93aaf0f35529be3661ceae9dacb8597f1128b0877a67d264fd615df908292a000e569705bf74e38ed274063bb5d65
|
7
|
+
data.tar.gz: 910405f211b29dfd8bbf6857e6bbfd982e750fbaed1f2ea797a1a8ddea257aadca61d34144cbf4454f83408d0868ce992f77ff522148a79333956055e287dc67
|
data/README.md
CHANGED
@@ -166,6 +166,27 @@ get '/' do
|
|
166
166
|
end
|
167
167
|
```
|
168
168
|
|
169
|
+
### MIME types
|
170
|
+
|
171
|
+
You can use `respond_to` in a way similar to Rails in your handlers to match MIME types.
|
172
|
+
|
173
|
+
Example:
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
post '/users' do
|
177
|
+
respond_to do |format|
|
178
|
+
format.json do
|
179
|
+
{ foo: 'bar' }
|
180
|
+
end
|
181
|
+
format.html do
|
182
|
+
'Hello, world'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
188
|
+
**Important:** Content negotiation is currently only based on the `Content-Type` header field of the HTTP request.
|
189
|
+
|
169
190
|
### View templates
|
170
191
|
|
171
192
|
View templates must currently be stored in `views/` directory of the project using Kawaii. They can be rendered using the `render` method:
|
data/examples/json.ru
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'kawaii'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
class App < Kawaii::Base
|
5
|
+
get '/' do
|
6
|
+
respond_to do |format|
|
7
|
+
format.json { {foo: :bar} }
|
8
|
+
format.html { "Hello, world" }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
post '/' do
|
13
|
+
respond_to do |format|
|
14
|
+
format.json { params }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
run App
|
20
|
+
|
21
|
+
|
data/lib/kawaii.rb
CHANGED
@@ -0,0 +1,185 @@
|
|
1
|
+
# MIME formats.
|
2
|
+
module Kawaii
|
3
|
+
# Registered formats.
|
4
|
+
class FormatRegistry
|
5
|
+
@formats = {}
|
6
|
+
class << self
|
7
|
+
attr_accessor :formats
|
8
|
+
end
|
9
|
+
|
10
|
+
# Registers a new format. Formats are preferred in the order they are
|
11
|
+
# registered; the first format has the highest priority.
|
12
|
+
def self.register!(format)
|
13
|
+
@formats[format.key] = format
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Core format-handling class.
|
18
|
+
class FormatHandler
|
19
|
+
# Creates a format handler for a route handler
|
20
|
+
# @param [Kawaii::RouteHandler] current route handler
|
21
|
+
# @return {FormatHandler}
|
22
|
+
def initialize(route_handler)
|
23
|
+
@route_handler = route_handler
|
24
|
+
@matches = []
|
25
|
+
@blocks = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Matches method invoked in end-user code with {FormatBase#key}.
|
29
|
+
# If format matches the current request, it saves it for negotiation
|
30
|
+
# in {FormatHandler#response}.
|
31
|
+
def method_missing(meth, *_args, &block)
|
32
|
+
format = FormatRegistry.formats[meth]
|
33
|
+
return unless format && format.match?(@route_handler.request)
|
34
|
+
@candidates << format
|
35
|
+
@blocks[meth] = block
|
36
|
+
end
|
37
|
+
|
38
|
+
# Encoded response based on matched format (see
|
39
|
+
# {FormatHandler#method_missing}).
|
40
|
+
# @return {Array} Rack response array or nil if no format was matched
|
41
|
+
def response
|
42
|
+
format, block = find_best_match
|
43
|
+
return if format.nil?
|
44
|
+
# @note Request is mutated here!
|
45
|
+
new_params = format.parse_params(@route_handler.request)
|
46
|
+
@route_handler.params.merge!(new_params) if new_params
|
47
|
+
response = @route_handler.instance_exec(&block)
|
48
|
+
format.encode(response)
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def find_best_match
|
54
|
+
# Find matching format trying to match the first registered format
|
55
|
+
# then the second one and so on.
|
56
|
+
registered_fmts = FormatRegistry.formats
|
57
|
+
_, format = registered_fmts.find { |_, fmt| @candidates.include?(fmt) }
|
58
|
+
[format, @blocks[format.key]] if format
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @abstract Base class for MIME format handlers.
|
63
|
+
class FormatBase
|
64
|
+
# Unique key for the format and at the same time the name of the method used
|
65
|
+
# in end-user-code to define format handler.
|
66
|
+
#
|
67
|
+
# @example Format handler for :json key
|
68
|
+
# respond_to do |format|
|
69
|
+
# format.json { ... }
|
70
|
+
# end
|
71
|
+
def key
|
72
|
+
fail NotImplementedError
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns true if the format is compatible with the request.
|
76
|
+
# @param _request [Rack::Request] current HTTP request
|
77
|
+
# @return true if there's a match.
|
78
|
+
def match?(_request)
|
79
|
+
fail NotImplementedError
|
80
|
+
end
|
81
|
+
|
82
|
+
# Parses params in request body in a way specific to the given format.
|
83
|
+
# @param _request [Rack::Request] contains information about the current
|
84
|
+
# HTTP request
|
85
|
+
# @return {Hash} including parsed params or nil
|
86
|
+
def parse_params(_request)
|
87
|
+
# Optional.
|
88
|
+
end
|
89
|
+
|
90
|
+
# Encodes response appropriately for the given format.
|
91
|
+
# @param _response [String, Hash, Array] response from format handler block.
|
92
|
+
# @return Rack response {Array}
|
93
|
+
def encode(_response)
|
94
|
+
fail NotImplementedError
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
require 'json'
|
99
|
+
|
100
|
+
# JSON MIME format (application/json).
|
101
|
+
class JsonFormat < FormatBase
|
102
|
+
# Unique key for the format and at the same time the name of the method used
|
103
|
+
# in end-user-code to define format handler.
|
104
|
+
#
|
105
|
+
# @example Format handler for :json key
|
106
|
+
# respond_to do |format|
|
107
|
+
# format.json do
|
108
|
+
# { foo: 'bar' }
|
109
|
+
# end
|
110
|
+
# end
|
111
|
+
def key
|
112
|
+
:json
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns true if the format is compatible with the request.
|
116
|
+
# @param request [Rack::Request] current HTTP request
|
117
|
+
# @return true if there's a match.
|
118
|
+
def match?(request)
|
119
|
+
request.content_type =~ %r{^application/json.*}
|
120
|
+
end
|
121
|
+
|
122
|
+
# Parses JSON string in request body if present and converts it to a hash.
|
123
|
+
# @param request [Rack::Request] contains information about the current HTTP
|
124
|
+
# request
|
125
|
+
# @return {Hash} including parsed params or nil
|
126
|
+
def parse_params(request)
|
127
|
+
json = request.body.read
|
128
|
+
JSON.parse(json).symbolize_keys if json.is_a?(String) && !json.empty?
|
129
|
+
end
|
130
|
+
|
131
|
+
# Encodes response appropriately by converting it to a JSON string.
|
132
|
+
# @param response [String, Hash, Array] response from format handler block.
|
133
|
+
# @return Rack response {Array}
|
134
|
+
def encode(response)
|
135
|
+
json = response.to_json
|
136
|
+
[200,
|
137
|
+
{ Rack::CONTENT_TYPE => 'application/json',
|
138
|
+
Rack::CONTENT_LENGTH => json.length.to_s },
|
139
|
+
[json]]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# HTML MIME format.
|
144
|
+
class HtmlFormat < FormatBase
|
145
|
+
# Unique key for the format and at the same time the name of the method used
|
146
|
+
# in end-user-code to define format handler.
|
147
|
+
#
|
148
|
+
# @example Format handler for :html key
|
149
|
+
# respond_to do |format|
|
150
|
+
# format.html { 'Hello, world' }
|
151
|
+
# end
|
152
|
+
def key
|
153
|
+
:html
|
154
|
+
end
|
155
|
+
|
156
|
+
# Always matches. This is why this format needs to be the last to
|
157
|
+
# be registered so more specific formats are before it.
|
158
|
+
def match?(_request)
|
159
|
+
true
|
160
|
+
end
|
161
|
+
|
162
|
+
# Response with text/html response.
|
163
|
+
# @param response [String] response from format handler block.
|
164
|
+
# @return Rack response {Array}
|
165
|
+
def encode(response)
|
166
|
+
[200,
|
167
|
+
{ Rack::CONTENT_TYPE => 'text/html',
|
168
|
+
Rack::CONTENT_LENGTH => response.size.to_s },
|
169
|
+
[response]]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Include this module to support 'respond_to'.
|
174
|
+
module FormatMethods
|
175
|
+
def respond_to(&block)
|
176
|
+
format_handler = FormatHandler.new(self)
|
177
|
+
instance_exec(format_handler, &block)
|
178
|
+
format_handler.response
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Register supported MIME formats.
|
183
|
+
FormatRegistry.register!(JsonFormat.new)
|
184
|
+
FormatRegistry.register!(HtmlFormat.new)
|
185
|
+
end
|
data/lib/kawaii/route_handler.rb
CHANGED
@@ -9,6 +9,7 @@ module Kawaii
|
|
9
9
|
class RouteHandler
|
10
10
|
include MethodChain
|
11
11
|
include RenderMethods
|
12
|
+
include FormatMethods
|
12
13
|
|
13
14
|
# Params based on request visible in the route handler scope.
|
14
15
|
attr_reader :params
|
@@ -39,7 +40,7 @@ module Kawaii
|
|
39
40
|
class ResponseError < RuntimeError; end
|
40
41
|
|
41
42
|
def process_response(response)
|
42
|
-
if response.is_a?(String)
|
43
|
+
if response.is_a?(String) # @todo Use HtmlFormat
|
43
44
|
[200,
|
44
45
|
{ Rack::CONTENT_TYPE => 'text/html',
|
45
46
|
Rack::CONTENT_LENGTH => response.size.to_s },
|
@@ -47,7 +48,7 @@ module Kawaii
|
|
47
48
|
elsif response.is_a?(Array)
|
48
49
|
response
|
49
50
|
else
|
50
|
-
fail ResponseError, "Unsupported handler
|
51
|
+
fail ResponseError, "Unsupported handler response: #{response.inspect}"
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
data/lib/kawaii/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kawaii-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcin Bilski
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -156,6 +156,7 @@ files:
|
|
156
156
|
- examples/controller.ru
|
157
157
|
- examples/hello_world.rb
|
158
158
|
- examples/hello_world.ru
|
159
|
+
- examples/json.ru
|
159
160
|
- examples/modular.ru
|
160
161
|
- examples/modular/first_app.rb
|
161
162
|
- examples/modular/second_app.rb
|
@@ -168,6 +169,7 @@ files:
|
|
168
169
|
- lib/kawaii/controller.rb
|
169
170
|
- lib/kawaii/core_ext/hash.rb
|
170
171
|
- lib/kawaii/core_ext/string.rb
|
172
|
+
- lib/kawaii/formats.rb
|
171
173
|
- lib/kawaii/matchers.rb
|
172
174
|
- lib/kawaii/method_chain.rb
|
173
175
|
- lib/kawaii/render_methods.rb
|
@@ -199,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
201
|
version: '0'
|
200
202
|
requirements: []
|
201
203
|
rubyforge_project:
|
202
|
-
rubygems_version: 2.
|
204
|
+
rubygems_version: 2.4.8
|
203
205
|
signing_key:
|
204
206
|
specification_version: 4
|
205
207
|
summary: A simple web framework based on Rack
|