/
configuration.rb
389 lines (373 loc) · 11.7 KB
/
configuration.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
require 'lotus/utils/string'
require 'lotus/utils/class'
module Lotus
module Controller
# Configuration for the framework, controllers and actions.
#
# Lotus::Controller has its own global configuration that can be manipulated
# via `Lotus::Controller.configure`.
#
# Every time that `Lotus::Controller` and `Lotus::Action` are included, that
# global configuration is being copied to the recipient. The copy will
# inherit all the settings from the original, but all the subsequent changes
# aren't reflected from the parent to the children, and viceversa.
#
# This architecture allows to have a global configuration that capture the
# most common cases for an application, and let controllers and single
# actions to specify exceptions.
#
# @since 0.2.0
class Configuration
# Default HTTP code for server side errors
#
# @since 0.2.0
# @api private
DEFAULT_ERROR_CODE = 500
# Default Mime type to format mapping
#
# @since 0.2.0
# @api private
DEFAULT_FORMATS = {
'*/*' => :all,
'application/octet-stream' => :all,
'text/html' => :html
}.freeze
# Return a copy of the configuration of the framework instance associated
# with the given class.
#
# When multiple instances of Lotus::Controller are used in the same
# application, we want to make sure that a controller or an action will
# receive the expected configuration.
#
# @param base [Class, Module] a controller or an action
#
# @return [Lotus::Controller::Configuration] the configuration associated
# to the given class.
#
# @since 0.2.0
# @api private
#
# @example Direct usage of the framework
# require 'lotus/controller'
#
# class Show
# include Lotus::Action
# end
#
# Lotus::Controller::Configuration.for(Show)
# # => will duplicate from Lotus::Controller
#
# @example Multiple instances of the framework
# require 'lotus/controller'
#
# module MyApp
# Controller = Lotus::Controller.duplicate(self)
#
# module Controllers::Dashboard
# include MyApp::Controller
#
# action 'Index' do
# def call(params)
# # ...
# end
# end
# end
# end
#
# class Show
# include Lotus::Action
# end
#
# Lotus::Controller::Configuration.for(Show)
# # => will duplicate from Lotus::Controller
#
# Lotus::Controller::Configuration.for(MyApp::Controllers::Dashboard)
# # => will duplicate from MyApp::Controller
def self.for(base)
namespace = Utils::String.new(base).namespace
framework = Utils::Class.load!("(#{namespace}|Lotus)::Controller")
framework.configuration.duplicate
end
# Initialize a configuration instance
#
# @return [Lotus::Controller::Configuration] a new configuration's
# instance
#
# @since 0.2.0
def initialize
reset!
end
# @attr_writer handle_exceptions [TrueClass,FalseClass] Decide if handle
# exceptions with an HTTP status or not
#
# @since 0.2.0
#
# @see Lotus::Controller::Configuration#handle_exceptions
attr_writer :handle_exceptions
# Decide if handle exceptions with an HTTP status or let them uncaught
#
# If this value is set to `true`, the configured exceptions will return
# the specified HTTP status, the rest of them with `500`.
#
# If this value is set to `false`, the exceptions won't be caught.
#
# This is part of a DSL, for this reason when this method is called with
# an argument, it will set the corresponding instance variable. When
# called without, it will return the already set value, or the default.
#
# @overload handle_exceptions(value)
# Sets the given value
# @param value [TrueClass, FalseClass] true or false, default to true
#
# @overload handle_exceptions
# Gets the value
# @return [TrueClass, FalseClass]
#
# @since 0.2.0
#
# @see Lotus::Controller::Configuration#handle_exception
# @see Lotus::Controller#configure
# @see Lotus::Action::Throwable
# @see http://httpstatus.es/500
#
# @example Getting the value
# require 'lotus/controller'
#
# Lotus::Controller.configuration.handle_exceptions # => true
#
# @example Setting the value
# require 'lotus/controller'
#
# Lotus::Controller.configure do
# handle_exceptions false
# end
def handle_exceptions(value = nil)
if value.nil?
@handle_exceptions
else
@handle_exceptions = value
end
end
# Specify how to handle an exception with an HTTP status
#
# Raised exceptions will return the configured HTTP status, only if
# `handled_exceptions` is set on `true`.
#
# @param exception [Hash] the exception class must be the key and the HTTP
# status the value
#
# @since 0.2.0
#
# @see Lotus::Controller::Configuration#handle_exceptions
# @see Lotus::Controller#configure
# @see Lotus::Action::Throwable
#
# @example
# require 'lotus/controller'
#
# Lotus::Controller.configure do
# handle_exception ArgumentError => 400
# end
def handle_exception(exception)
@handled_exceptions.merge!(exception)
end
# Return the HTTP status for the given exception
#
# Raised exceptions will return the configured HTTP status, only if
# `handled_exceptions` is set on `true`.
#
# @param exception [Hash] the exception class must be the key and the HTTP
# status the value of the hash
#
# @since 0.2.0
# @api private
#
# @see Lotus::Controller::Configuration#handle_exception
def exception_code(exception)
@handled_exceptions.fetch(exception) { DEFAULT_ERROR_CODE }
end
# Specify which is the default action module to be included when we use
# the `Lotus::Controller.action` method.
#
# This setting is useful when we use multiple instances of the framework
# in the same process, so we want to ensure that the actions will include
# `MyApp::Action`, rather than `AnotherApp::Action`.
#
# If not set, the default value is `Lotus::Action`
#
# This is part of a DSL, for this reason when this method is called with
# an argument, it will set the corresponding instance variable. When
# called without, it will return the already set value, or the default.
#
# @overload action_module(value)
# Sets the given value
# @param value [Module] the module to be included in all the actions
#
# @overload action_module
# Gets the value
# @return [Module]
#
# @since 0.2.0
#
# @see Lotus::Controller::Dsl#action
# @see Lotus::Controller#duplicate
#
# @example Getting the value
# require 'lotus/controller'
#
# Lotus::Controller.configuration.action_module # => Lotus::Action
#
# @example Setting the value
# require 'lotus/controller'
#
# module MyAction
# end
#
# Lotus::Controller.configure do
# action_module MyAction
# end
#
# class DashboardController
# include Lotus::Controller
#
# # It includes MyAction, instead of Lotus::Action
# action 'Index' do
# def call(params)
# # ...
# end
# end
# end
#
# @example Duplicated framework
# require 'lotus/controller'
#
# module MyApp
# Controller = Lotus::Controller.duplicate(self)
#
# module Controllers::Dashboard
# include MyApp::Controller
#
# # It includes MyApp::Action, instead of Lotus::Action
# action 'Index' do
# def call(params)
# # ...
# end
# end
# end
# end
def action_module(value = nil)
if value.nil?
@action_module
else
@action_module = value
end
end
# Specify the default modules to be included when `Lotus::Action` (or the
# `action_module`) is included. This also works with
# `Lotus::Controller.action`.
#
# If not set, this option will be ignored.
#
# This is part of a DSL, for this reason when this method is called with
# an argument, it will set the corresponding instance variable. When
# called without, it will return the already set value, or the default.
#
# @overload modules(blk)
# Adds the given block
# @param value [Proc] specify the modules to be included
#
# @overload modules
# Gets the value
# @return [Array] the list of the specified procs
#
# @since 0.2.0
#
# @see Lotus::Controller::Dsl#action
# @see Lotus::Controller#duplicate
#
# @example Getting the value
# require 'lotus/controller'
#
# Lotus::Controller.configuration.modules # => []
#
# @example Setting the value
# require 'lotus/controller'
# require 'lotus/action/cookies'
# require 'lotus/action/session'
#
# Lotus::Controller.configure do
# modules do
# include Lotus::Action::Cookies
# include Lotus::Action::Session
# end
# end
#
# class DashboardController
# include Lotus::Controller
#
# # It includes:
# # * Lotus::Action
# # * Lotus::Action::Cookies
# # * Lotus::Action::Session
# action 'Index' do
# def call(params)
# # ...
# end
# end
# end
def modules(&blk)
if block_given?
@modules.push(blk)
else
@modules
end
end
def format(symbol, mime_type)
@formats.merge! mime_type => symbol
end
def format_for(mime_type)
@formats[mime_type]
end
# Duplicate by copying the settings in a new instance.
#
# @return [Lotus::Controller::Configuration] a copy of the configuration
#
# @since 0.2.0
# @api private
def duplicate
Configuration.new.tap do |c|
c.handle_exceptions = handle_exceptions
c.handled_exceptions = handled_exceptions.dup
c.action_module = action_module
c.modules = modules.dup
c.formats = formats.dup
end
end
# Reset all the values to the defaults
#
# @since 0.2.0
# @api private
def reset!
@handle_exceptions = true
@handled_exceptions = {}
@modules = []
@formats = DEFAULT_FORMATS.dup
@action_module = ::Lotus::Action
end
# Load the configuration for the given action
#
# @since 0.2.0
# @api private
def load!(base)
modules.each do |mod|
base.class_eval(&mod)
end
end
protected
attr_accessor :handled_exceptions
attr_accessor :formats
attr_writer :action_module
attr_writer :modules
end
end
end