lotus-controller 0.3.2 → 0.4.0
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/CHANGELOG.md +10 -0
- data/lib/lotus/action/callable.rb +1 -1
- data/lib/lotus/action/cookie_jar.rb +20 -4
- data/lib/lotus/action/cookies.rb +1 -1
- data/lib/lotus/action/flash.rb +45 -4
- data/lib/lotus/action/head.rb +27 -0
- data/lib/lotus/action/params.rb +54 -0
- data/lib/lotus/action/rack.rb +2 -2
- data/lib/lotus/action/rack/callable.rb +3 -3
- data/lib/lotus/action/redirect.rb +4 -2
- data/lib/lotus/action/session.rb +1 -1
- data/lib/lotus/controller/configuration.rb +74 -7
- data/lib/lotus/controller/version.rb +1 -1
- data/lotus-controller.gemspec +2 -2
- metadata +7 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69d59a1b6b0f96e6af7bf468a3003a45992b17a3
|
4
|
+
data.tar.gz: 93d926cb9fdf962ef3e55d6c5c1efb6de4b546ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 129a9981a8f46af6d2cd149b9c811a81011cbc3e68b92af5e428e9d9cdc5afc1bb6070b605900acf231a4612d0eaf2c23088c7c17097c1c83aec9c030d5daa27
|
7
|
+
data.tar.gz: 35da235765f394db0fbcbbe3770732ee3e308825981f9634c0f6e6a626e72acbd3188c00939dfcce097a689e706f1de8ea90814cc435bc30be4d74ac7511f765
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# Lotus::Controller
|
2
2
|
Complete, fast and testable actions for Rack
|
3
3
|
|
4
|
+
## v0.4.0 - 2015-03-23
|
5
|
+
### Added
|
6
|
+
- [Erol Fornoles] `Action.use` now accepts a block
|
7
|
+
- [Alfonso Uceda Pompa] Introduced `Lotus::Controller::Configuration#cookies` as default cookie options.
|
8
|
+
- [Alfonso Uceda Pompa] Introduced `Lotus::Controller::Configuration#default_headers` as default HTTP headers to return in all the responses.
|
9
|
+
- [Luca Guidi] Introduced `Lotus::Action::Params#get` as a safe API to access nested params.
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
- [Alfonso Uceda Pompa] `redirect_to` now is a flow control method: it terminates the execution of an action, including the callbacks.
|
13
|
+
|
4
14
|
## v0.3.2 - 2015-01-30
|
5
15
|
### Added
|
6
16
|
- [Alfonso Uceda Pompa] Callbacks: introduced `append_before` (alias of `before`), `append_after` (alias of `after`), `prepend_before` and `prepend_after`.
|
@@ -36,9 +36,10 @@ module Lotus
|
|
36
36
|
# @return [CookieJar]
|
37
37
|
#
|
38
38
|
# @since 0.1.0
|
39
|
-
def initialize(env, headers)
|
40
|
-
@_headers
|
41
|
-
@cookies
|
39
|
+
def initialize(env, headers, default_options)
|
40
|
+
@_headers = headers
|
41
|
+
@cookies = Utils::Hash.new(extract(env)).symbolize!
|
42
|
+
@default_options = default_options
|
42
43
|
end
|
43
44
|
|
44
45
|
# Finalize itself, by setting the proper headers to add and remove
|
@@ -50,7 +51,7 @@ module Lotus
|
|
50
51
|
#
|
51
52
|
# @see Lotus::Action::Cookies#finish
|
52
53
|
def finish
|
53
|
-
@cookies.each {|k,v| v.nil? ? delete_cookie(k) : set_cookie(k, v) }
|
54
|
+
@cookies.each { |k,v| v.nil? ? delete_cookie(k) : set_cookie(k, _merge_default_values(v)) }
|
54
55
|
end
|
55
56
|
|
56
57
|
# Returns the object associated with the given key
|
@@ -77,6 +78,21 @@ module Lotus
|
|
77
78
|
end
|
78
79
|
|
79
80
|
private
|
81
|
+
|
82
|
+
# Merge default cookies options with values provided by user
|
83
|
+
#
|
84
|
+
# Cookies values provided by user are respected
|
85
|
+
#
|
86
|
+
# @since 0.4.0
|
87
|
+
# @api private
|
88
|
+
def _merge_default_values(value)
|
89
|
+
cookies_options = if value.is_a? Hash
|
90
|
+
value
|
91
|
+
else
|
92
|
+
{ value: value }
|
93
|
+
end
|
94
|
+
@default_options.merge cookies_options
|
95
|
+
end
|
80
96
|
# Extract the cookies from the raw Rack env.
|
81
97
|
#
|
82
98
|
# This implementation is borrowed from Rack::Request#cookies.
|
data/lib/lotus/action/cookies.rb
CHANGED
data/lib/lotus/action/flash.rb
CHANGED
@@ -12,6 +12,12 @@ module Lotus
|
|
12
12
|
# @api private
|
13
13
|
SESSION_KEY = :__flash
|
14
14
|
|
15
|
+
# Session key where the last request_id is stored
|
16
|
+
#
|
17
|
+
# @since 0.4.0
|
18
|
+
# @api private
|
19
|
+
LAST_REQUEST_KEY = :__last_request_id
|
20
|
+
|
15
21
|
# Initialize a new Flash instance
|
16
22
|
#
|
17
23
|
# @param session [Rack::Session::Abstract::SessionHash] the session
|
@@ -22,8 +28,9 @@ module Lotus
|
|
22
28
|
# @see http://www.rubydoc.info/gems/rack/Rack/Session/Abstract/SessionHash
|
23
29
|
# @see Lotus::Action::Rack#session_id
|
24
30
|
def initialize(session, request_id)
|
25
|
-
@session
|
26
|
-
@request_id
|
31
|
+
@session = session
|
32
|
+
@request_id = request_id
|
33
|
+
@last_request_id = session[LAST_REQUEST_KEY]
|
27
34
|
|
28
35
|
session[SESSION_KEY] ||= {}
|
29
36
|
session[SESSION_KEY][request_id] ||= {}
|
@@ -47,7 +54,7 @@ module Lotus
|
|
47
54
|
# @since 0.3.0
|
48
55
|
# @api private
|
49
56
|
def [](key)
|
50
|
-
data.fetch(key) do
|
57
|
+
last_request_flash.merge(data).fetch(key) do
|
51
58
|
_values.find {|data| !data[key].nil? }
|
52
59
|
end
|
53
60
|
end
|
@@ -66,6 +73,7 @@ module Lotus
|
|
66
73
|
# It may happen that `#flash` is nil, and those two methods will fail
|
67
74
|
unless flash.nil?
|
68
75
|
expire_stale!
|
76
|
+
set_last_request_id!
|
69
77
|
remove!
|
70
78
|
end
|
71
79
|
end
|
@@ -111,7 +119,7 @@ module Lotus
|
|
111
119
|
# @api private
|
112
120
|
def expire_stale!
|
113
121
|
flash.each do |request_id, _|
|
114
|
-
flash.delete(request_id) if
|
122
|
+
flash.delete(request_id) if delete?(request_id)
|
115
123
|
end
|
116
124
|
end
|
117
125
|
|
@@ -136,6 +144,39 @@ module Lotus
|
|
136
144
|
def _values
|
137
145
|
flash.values
|
138
146
|
end
|
147
|
+
|
148
|
+
# Determine if delete data from flash for the given Request ID
|
149
|
+
#
|
150
|
+
# @return [TrueClass,FalseClass] the result of the check
|
151
|
+
#
|
152
|
+
# @since 0.4.0
|
153
|
+
# @api private
|
154
|
+
#
|
155
|
+
# @see Lotus::Action::Flash#expire_stale!
|
156
|
+
def delete?(request_id)
|
157
|
+
![@request_id, @session[LAST_REQUEST_KEY]].include?(request_id)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Get the last request session flash
|
161
|
+
#
|
162
|
+
# @return [Hash] the flash of last request
|
163
|
+
#
|
164
|
+
# @since 0.4.0
|
165
|
+
# @api private
|
166
|
+
def last_request_flash
|
167
|
+
flash[@last_request_id] || {}
|
168
|
+
end
|
169
|
+
|
170
|
+
# Store the last request_id to create the next flash with its values
|
171
|
+
# is current flash is not empty.
|
172
|
+
#
|
173
|
+
# @return [void]
|
174
|
+
# @since 0.4.0
|
175
|
+
# @api private
|
176
|
+
def set_last_request_id!
|
177
|
+
@session[LAST_REQUEST_KEY] = @request_id if !empty?
|
178
|
+
end
|
179
|
+
|
139
180
|
end
|
140
181
|
end
|
141
182
|
end
|
data/lib/lotus/action/head.rb
CHANGED
@@ -14,6 +14,32 @@ module Lotus
|
|
14
14
|
# @api private
|
15
15
|
HTTP_STATUSES_WITHOUT_BODY = Set.new((100..199).to_a << 204 << 205 << 304).freeze
|
16
16
|
|
17
|
+
|
18
|
+
# Entity headers allowed in blank body responses, according to
|
19
|
+
# RFC 2616 - Section 10 (HTTP 1.1).
|
20
|
+
#
|
21
|
+
# "The response MAY include new or updated metainformation in the form
|
22
|
+
# of entity-headers".
|
23
|
+
#
|
24
|
+
# @since 0.4.0
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
|
28
|
+
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html
|
29
|
+
ENTITY_HEADERS = {
|
30
|
+
'Allow' => true,
|
31
|
+
'Content-Encoding' => true,
|
32
|
+
'Content-Language' => true,
|
33
|
+
'Content-Length' => true,
|
34
|
+
'Content-Location' => true,
|
35
|
+
'Content-MD5' => true,
|
36
|
+
'Content-Range' => true,
|
37
|
+
'Content-Type' => true,
|
38
|
+
'Expires' => true,
|
39
|
+
'Last-Modified' => true,
|
40
|
+
'extension-header' => true
|
41
|
+
}.freeze
|
42
|
+
|
17
43
|
# Ensures to not send body or headers for HEAD requests and/or for status
|
18
44
|
# codes that doesn't allow them.
|
19
45
|
#
|
@@ -26,6 +52,7 @@ module Lotus
|
|
26
52
|
|
27
53
|
if _requires_no_body?
|
28
54
|
@_body = nil
|
55
|
+
@headers.reject! { |header,_| !ENTITY_HEADERS.include?(header) }
|
29
56
|
end
|
30
57
|
end
|
31
58
|
|
data/lib/lotus/action/params.rb
CHANGED
@@ -26,6 +26,14 @@ module Lotus
|
|
26
26
|
# @since 0.1.0
|
27
27
|
ROUTER_PARAMS = 'router.params'.freeze
|
28
28
|
|
29
|
+
# Separator for #get
|
30
|
+
#
|
31
|
+
# @since 0.4.0
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
# @see Lotus::Action::Params#get
|
35
|
+
GET_SEPARATOR = '.'.freeze
|
36
|
+
|
29
37
|
# Whitelist and validate a parameter
|
30
38
|
#
|
31
39
|
# @param name [#to_sym] The name of the param to whitelist
|
@@ -137,6 +145,52 @@ module Lotus
|
|
137
145
|
@attributes.get(key)
|
138
146
|
end
|
139
147
|
|
148
|
+
# Get an attribute value associated with the given key.
|
149
|
+
# Nested attributes are reached with a dot notation.
|
150
|
+
#
|
151
|
+
# @param key [String] the key
|
152
|
+
#
|
153
|
+
# @return [Object,NilClass] return the associated value, if found
|
154
|
+
#
|
155
|
+
# @since 0.4.0
|
156
|
+
#
|
157
|
+
# @example
|
158
|
+
# require 'lotus/controller'
|
159
|
+
#
|
160
|
+
# module Deliveries
|
161
|
+
# class Create
|
162
|
+
# include Lotus::Action
|
163
|
+
#
|
164
|
+
# params do
|
165
|
+
# param :customer_name
|
166
|
+
# param :address do
|
167
|
+
# param :city
|
168
|
+
# end
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# def call(params)
|
172
|
+
# params.get('customer_name') # => "Luca"
|
173
|
+
# params.get('uknown') # => nil
|
174
|
+
#
|
175
|
+
# params.get('address.city') # => "Rome"
|
176
|
+
# params.get('address.unknown') # => nil
|
177
|
+
#
|
178
|
+
# params.get(nil) # => nil
|
179
|
+
# end
|
180
|
+
# end
|
181
|
+
# end
|
182
|
+
def get(key)
|
183
|
+
key, *keys = key.to_s.split(GET_SEPARATOR)
|
184
|
+
result = self[key]
|
185
|
+
|
186
|
+
Array(keys).each do |k|
|
187
|
+
break if result.nil?
|
188
|
+
result = result[k]
|
189
|
+
end
|
190
|
+
|
191
|
+
result
|
192
|
+
end
|
193
|
+
|
140
194
|
# Returns the Ruby's hash
|
141
195
|
#
|
142
196
|
# @return [Hash]
|
data/lib/lotus/action/rack.rb
CHANGED
@@ -9,7 +9,7 @@ module Lotus
|
|
9
9
|
# @param env [Hash] the full Rack env or the params. This value may vary,
|
10
10
|
# see the examples below.
|
11
11
|
#
|
12
|
-
# @since
|
12
|
+
# @since 0.4.0
|
13
13
|
#
|
14
14
|
# @see Lotus::Action::Rack::ClassMethods#rack_builder
|
15
15
|
# @see Lotus::Action::Rack::ClassMethods#use
|
@@ -21,7 +21,7 @@ module Lotus
|
|
21
21
|
# def initialize(app)
|
22
22
|
# @app = app
|
23
23
|
# end
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# def call(env)
|
26
26
|
# #...
|
27
27
|
# end
|
@@ -44,4 +44,4 @@ module Lotus
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
-
end
|
47
|
+
end
|
@@ -12,13 +12,15 @@ module Lotus
|
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
|
-
# Redirect to the given URL
|
15
|
+
# Redirect to the given URL and halt the request
|
16
16
|
#
|
17
17
|
# @param url [String] the destination URL
|
18
18
|
# @param status [Fixnum] the http code
|
19
19
|
#
|
20
20
|
# @since 0.1.0
|
21
21
|
#
|
22
|
+
# @see Lotus::Action::Throwable#halt
|
23
|
+
#
|
22
24
|
# @example With default status code (302)
|
23
25
|
# require 'lotus/controller'
|
24
26
|
#
|
@@ -50,7 +52,7 @@ module Lotus
|
|
50
52
|
# action.call({}) # => [301, {'Location' => '/articles/23'}, '']
|
51
53
|
def redirect_to(url, status: 302)
|
52
54
|
headers[LOCATION] = url
|
53
|
-
|
55
|
+
halt(status)
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
data/lib/lotus/action/session.rb
CHANGED
@@ -112,8 +112,8 @@ module Lotus
|
|
112
112
|
# # The validation errors caused by Comments::Create are available
|
113
113
|
# # **after the redirect** in the context of Comments::Index.
|
114
114
|
def redirect_to(*args)
|
115
|
-
super
|
116
115
|
flash[ERRORS_KEY] = errors.to_a unless params.valid?
|
116
|
+
super
|
117
117
|
end
|
118
118
|
|
119
119
|
# Read errors from flash or delegate to the superclass
|
@@ -469,6 +469,67 @@ module Lotus
|
|
469
469
|
end
|
470
470
|
end
|
471
471
|
|
472
|
+
# Set default headers for all responses
|
473
|
+
#
|
474
|
+
# By default this value is an empty hash.
|
475
|
+
#
|
476
|
+
# @since 0.4.0
|
477
|
+
#
|
478
|
+
# @example Getting the value
|
479
|
+
# require 'lotus/controller'
|
480
|
+
#
|
481
|
+
# Lotus::Controller.configuration.default_headers # => {}
|
482
|
+
#
|
483
|
+
# @example Setting the value
|
484
|
+
# require 'lotus/controller'
|
485
|
+
#
|
486
|
+
# Lotus::Controller.configure do
|
487
|
+
# default_headers({
|
488
|
+
# 'X-Frame-Options' => 'DENY'
|
489
|
+
# })
|
490
|
+
# end
|
491
|
+
def default_headers(headers = nil)
|
492
|
+
if headers
|
493
|
+
@default_headers.merge!(
|
494
|
+
headers.reject {|_,v| v.nil? }
|
495
|
+
)
|
496
|
+
else
|
497
|
+
@default_headers
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
# Set default cookies options for all responses
|
502
|
+
#
|
503
|
+
# By default this value is an empty hash.
|
504
|
+
#
|
505
|
+
# @since 0.4.0
|
506
|
+
#
|
507
|
+
# @example Getting the value
|
508
|
+
# require 'lotus/controller'
|
509
|
+
#
|
510
|
+
# Lotus::Controller.configuration.cookies # => {}
|
511
|
+
#
|
512
|
+
# @example Setting the value
|
513
|
+
# require 'lotus/controller'
|
514
|
+
#
|
515
|
+
# Lotus::Controller.configure do
|
516
|
+
# cookies({
|
517
|
+
# domain: 'lotusrb.org',
|
518
|
+
# path: '/controller',
|
519
|
+
# secure: true,
|
520
|
+
# httponly: true
|
521
|
+
# })
|
522
|
+
# end
|
523
|
+
def cookies(options = nil)
|
524
|
+
if options
|
525
|
+
@cookies.merge!(
|
526
|
+
options.reject { |_, v| v.nil? }
|
527
|
+
)
|
528
|
+
else
|
529
|
+
@cookies
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
472
533
|
# Returns a format for the given mime type
|
473
534
|
#
|
474
535
|
# @param mime_type [#to_s,#to_str] A mime type
|
@@ -503,13 +564,15 @@ module Lotus
|
|
503
564
|
# @api private
|
504
565
|
def duplicate
|
505
566
|
Configuration.new.tap do |c|
|
506
|
-
c.handle_exceptions
|
507
|
-
c.handled_exceptions
|
508
|
-
c.action_module
|
509
|
-
c.modules
|
510
|
-
c.formats
|
511
|
-
c.default_format
|
512
|
-
c.default_charset
|
567
|
+
c.handle_exceptions = handle_exceptions
|
568
|
+
c.handled_exceptions = handled_exceptions.dup
|
569
|
+
c.action_module = action_module
|
570
|
+
c.modules = modules.dup
|
571
|
+
c.formats = formats.dup
|
572
|
+
c.default_format = default_format
|
573
|
+
c.default_charset = default_charset
|
574
|
+
c.default_headers = default_headers.dup
|
575
|
+
c.cookies = cookies.dup
|
513
576
|
end
|
514
577
|
end
|
515
578
|
|
@@ -534,6 +597,8 @@ module Lotus
|
|
534
597
|
@formats = DEFAULT_FORMATS.dup
|
535
598
|
@default_format = nil
|
536
599
|
@default_charset = nil
|
600
|
+
@default_headers = {}
|
601
|
+
@cookies = {}
|
537
602
|
@action_module = ::Lotus::Action
|
538
603
|
end
|
539
604
|
|
@@ -569,6 +634,8 @@ module Lotus
|
|
569
634
|
attr_writer :modules
|
570
635
|
attr_writer :default_format
|
571
636
|
attr_writer :default_charset
|
637
|
+
attr_writer :default_headers
|
638
|
+
attr_writer :cookies
|
572
639
|
end
|
573
640
|
end
|
574
641
|
end
|
data/lotus-controller.gemspec
CHANGED
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.required_ruby_version = '>= 2.0.0'
|
21
21
|
|
22
22
|
spec.add_dependency 'rack', '~> 1.5'
|
23
|
-
spec.add_dependency 'lotus-utils', '~> 0.
|
24
|
-
spec.add_dependency 'lotus-validations', '~> 0.
|
23
|
+
spec.add_dependency 'lotus-utils', '~> 0.4'
|
24
|
+
spec.add_dependency 'lotus-validations', '~> 0.3'
|
25
25
|
|
26
26
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
27
27
|
spec.add_development_dependency 'minitest', '~> 5'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lotus-controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-03-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -31,40 +31,28 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '0.
|
35
|
-
- - ">="
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: 0.3.4
|
34
|
+
version: '0.4'
|
38
35
|
type: :runtime
|
39
36
|
prerelease: false
|
40
37
|
version_requirements: !ruby/object:Gem::Requirement
|
41
38
|
requirements:
|
42
39
|
- - "~>"
|
43
40
|
- !ruby/object:Gem::Version
|
44
|
-
version: '0.
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 0.3.4
|
41
|
+
version: '0.4'
|
48
42
|
- !ruby/object:Gem::Dependency
|
49
43
|
name: lotus-validations
|
50
44
|
requirement: !ruby/object:Gem::Requirement
|
51
45
|
requirements:
|
52
46
|
- - "~>"
|
53
47
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
55
|
-
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: 0.2.4
|
48
|
+
version: '0.3'
|
58
49
|
type: :runtime
|
59
50
|
prerelease: false
|
60
51
|
version_requirements: !ruby/object:Gem::Requirement
|
61
52
|
requirements:
|
62
53
|
- - "~>"
|
63
54
|
- !ruby/object:Gem::Version
|
64
|
-
version: '0.
|
65
|
-
- - ">="
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: 0.2.4
|
55
|
+
version: '0.3'
|
68
56
|
- !ruby/object:Gem::Dependency
|
69
57
|
name: bundler
|
70
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -183,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
171
|
version: '0'
|
184
172
|
requirements: []
|
185
173
|
rubyforge_project:
|
186
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.4.5
|
187
175
|
signing_key:
|
188
176
|
specification_version: 4
|
189
177
|
summary: Complete, fast and testable actions for Rack and Lotus
|