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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34266b059482427cf4b508d369a3a82eee1c13b9
4
- data.tar.gz: a44b19872324040f54441b625dd107452e7e88af
3
+ metadata.gz: 4ac5408912341100edad70023e719dc15b735d92
4
+ data.tar.gz: 2a765e3f6108277eec769b70498b6a9fb71e7194
5
5
  SHA512:
6
- metadata.gz: f608c27a33e224eee46f7a3f6d9adb3b9e5cda66c1d98847077ec80b6667b5aba4fbdcf953f685a879f1fdf33db9cdc195485fe541dfa24c4d1a18fc232ae3f8
7
- data.tar.gz: 62f772e16fd0b43d6d7adfc3a038eb247a3c3100dc1999d1f6fc8abafc15be4bb714e2c8f6286c2d4c3b613493901291b76960a3bfe16fd4e1166e9ecfbbe64d
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
@@ -7,6 +7,7 @@ require 'kawaii/method_chain'
7
7
  require 'kawaii/render_methods'
8
8
  require 'kawaii/matchers'
9
9
  require 'kawaii/route_mapping'
10
+ require 'kawaii/formats'
10
11
  require 'kawaii/route_handler'
11
12
  require 'kawaii/route'
12
13
  require 'kawaii/routing_methods'
@@ -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
@@ -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 aresponse: #{response.inspect}"
51
+ fail ResponseError, "Unsupported handler response: #{response.inspect}"
51
52
  end
52
53
  end
53
54
  end
@@ -1,4 +1,4 @@
1
1
  # Gem version.
2
2
  module Kawaii
3
- VERSION = '0.2.0'
3
+ VERSION = '0.2.1'
4
4
  end
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.0
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-27 00:00:00.000000000 Z
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.2.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