lotus-controller 0.4.0 → 0.4.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/CHANGELOG.md +10 -0
- data/README.md +47 -14
- data/lib/lotus/action/cookie_jar.rb +13 -1
- data/lib/lotus/action/cookies.rb +26 -2
- data/lib/lotus/action/head.rb +0 -2
- data/lib/lotus/action/mime.rb +31 -1
- data/lib/lotus/action/redirect.rb +1 -1
- data/lib/lotus/controller.rb +0 -1
- data/lib/lotus/controller/version.rb +1 -1
- metadata +2 -3
- data/lib/rack-patch.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40bc827a76215d889abc5fd33c3b102cb626732e
|
4
|
+
data.tar.gz: 3ea7a9a16a40c1e00e2861684076f1a7c543efa8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ff6ac5d43e155662c2318ba55184260d4ffdebbb6d0b6eec607d3563439bd809bf8dee089fdb342779eaa23c10d1d7bb5074ae11ead2c5c832bd64ee9506272
|
7
|
+
data.tar.gz: 8e6e4c60d239bd321aa0a1aec3aad3120fbef86dc86f33e8c50603f18013650b63ff184572379068fb2f734b7faf593586fcf0c97b9d3f5feb6d6e0167151df2
|
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.1 - 2015-05-15
|
5
|
+
### Fixed
|
6
|
+
- [Luca Guidi] Ensure proper automatic `Content-Type` working well with Internet Explorer.
|
7
|
+
- [Luca Guidi] Ensure `Lotus::Action#redirect_to` to return `::String` for Rack servers compatibility.
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- [Alfonso Uceda Pompa] Prevent `Content-Type` and `Content-Lenght` to be sent when status code requires no body (eg. `204`).
|
11
|
+
This is for compatibility with `Rack::Lint`, not with RFC 2016.
|
12
|
+
- [Luca Guidi] Ensure `Lotus::Action::Params#to_h` to return `::Hash`
|
13
|
+
|
4
14
|
## v0.4.0 - 2015-03-23
|
5
15
|
### Added
|
6
16
|
- [Erol Fornoles] `Action.use` now accepts a block
|
data/README.md
CHANGED
@@ -75,7 +75,7 @@ __An action is an object__. That's important because __you have the full control
|
|
75
75
|
In other words, you have the freedom to instantiate, inject dependencies and test it, both at the unit and integration level.
|
76
76
|
|
77
77
|
In the example below, the default repository is `Article`. During a unit test we can inject a stubbed version, and invoke `#call` with the params.
|
78
|
-
__We're avoiding HTTP calls__, we're
|
78
|
+
__We're avoiding HTTP calls__, we're also going to avoid hitting the database (it depends on the stubbed repository), __we're just dealing with message passing__.
|
79
79
|
Imagine how **fast** the unit test could be.
|
80
80
|
|
81
81
|
```ruby
|
@@ -99,7 +99,7 @@ action.call({ id: 23 })
|
|
99
99
|
|
100
100
|
The request params are passed as an argument to the `#call` method.
|
101
101
|
If routed with *Lotus::Router*, it extracts the relevant bits from the Rack `env` (eg the requested `:id`).
|
102
|
-
Otherwise everything passed as is: the full Rack `env` in production, and the given `Hash` for unit tests.
|
102
|
+
Otherwise everything is passed as is: the full Rack `env` in production, and the given `Hash` for unit tests.
|
103
103
|
|
104
104
|
With Lotus::Router:
|
105
105
|
|
@@ -158,6 +158,12 @@ class Signup
|
|
158
158
|
param :first_name
|
159
159
|
param :last_name
|
160
160
|
param :email
|
161
|
+
|
162
|
+
param :address do
|
163
|
+
param :line_one
|
164
|
+
param :state
|
165
|
+
param :country
|
166
|
+
end
|
161
167
|
end
|
162
168
|
|
163
169
|
def call(params)
|
@@ -168,6 +174,10 @@ class Signup
|
|
168
174
|
# Whitelist :first_name, but not :admin
|
169
175
|
puts params[:first_name] # => "Luca"
|
170
176
|
puts params[:admin] # => nil
|
177
|
+
|
178
|
+
# Whitelist nested params [:address][:line_one], not [:address][:line_two]
|
179
|
+
puts params[:address][:line_one] # => '69 Tender St'
|
180
|
+
puts params[:address][:line_two] # => nil
|
171
181
|
end
|
172
182
|
end
|
173
183
|
```
|
@@ -232,7 +242,7 @@ action = Show.new
|
|
232
242
|
action.call({}) # => [200, {}, [""]]
|
233
243
|
```
|
234
244
|
|
235
|
-
It has private accessors to explicitly set status, headers and body:
|
245
|
+
It has private accessors to explicitly set status, headers, and body:
|
236
246
|
|
237
247
|
```ruby
|
238
248
|
class Show
|
@@ -251,17 +261,17 @@ action.call({}) # => [201, { "X-Custom" => "OK" }, ["Hi!"]]
|
|
251
261
|
|
252
262
|
### Exposures
|
253
263
|
|
254
|
-
We know that actions are objects and Lotus::Action respects one of the pillars of OOP: __encapsulation__.
|
264
|
+
We know that actions are objects and `Lotus::Action` respects one of the pillars of OOP: __encapsulation__.
|
255
265
|
Other frameworks extract instance variables (`@ivar`) and make them available to the view context.
|
256
266
|
|
257
|
-
Lotus::Action's solution is the simple and powerful DSL: `expose`.
|
267
|
+
`Lotus::Action`'s solution is the simple and powerful DSL: `expose`.
|
258
268
|
It's a thin layer on top of `attr_reader`.
|
259
269
|
|
260
270
|
Using `expose` creates a getter for the given attribute, and adds it to the _exposures_.
|
261
271
|
Exposures (`#exposures`) are a set of attributes exposed to the view.
|
262
272
|
That is to say the variables necessary for rendering a view.
|
263
273
|
|
264
|
-
By default, all Lotus::
|
274
|
+
By default, all `Lotus::Action` objects expose `#params` and `#errors`.
|
265
275
|
|
266
276
|
```ruby
|
267
277
|
class Show
|
@@ -526,10 +536,34 @@ class RemoveCookies
|
|
526
536
|
end
|
527
537
|
end
|
528
538
|
|
529
|
-
action =
|
539
|
+
action = RemoveCookies.new
|
530
540
|
action.call({}) # => [200, {'Set-Cookie' => "foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"}, '...']
|
531
541
|
```
|
532
542
|
|
543
|
+
Default values can be set in configuration, but overriden case by case.
|
544
|
+
|
545
|
+
```ruby
|
546
|
+
require 'lotus/controller'
|
547
|
+
require 'lotus/action/cookies'
|
548
|
+
|
549
|
+
Lotus::Controller.configure do
|
550
|
+
cookies max_age: 300 # 5 minutes
|
551
|
+
end
|
552
|
+
|
553
|
+
class SetCookies
|
554
|
+
include Lotus::Action
|
555
|
+
include Lotus::Action::Cookies
|
556
|
+
|
557
|
+
def call(params)
|
558
|
+
# ...
|
559
|
+
cookies[:foo] = { value: 'bar', max_age: 100 }
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
action = SetCookies.new
|
564
|
+
action.call({}) # => [200, {'Set-Cookie' => "foo=bar; max-age=100;"}, '...']
|
565
|
+
```
|
566
|
+
|
533
567
|
### Sessions
|
534
568
|
|
535
569
|
It has builtin support for Rack sessions:
|
@@ -722,7 +756,7 @@ action.call({ article: { title: 'Hello' }}) # => [301, {'Location' => '/articles
|
|
722
756
|
|
723
757
|
### Mime Types
|
724
758
|
|
725
|
-
Lotus::Action automatically sets the `Content-Type` header, according to the request.
|
759
|
+
`Lotus::Action` automatically sets the `Content-Type` header, according to the request.
|
726
760
|
|
727
761
|
```ruby
|
728
762
|
class Show
|
@@ -872,7 +906,7 @@ Articles::Index.new.call({})
|
|
872
906
|
### Lotus::Router integration
|
873
907
|
|
874
908
|
While Lotus::Router works great with this framework, Lotus::Controller doesn't depend on it.
|
875
|
-
You,
|
909
|
+
You, the developer, are free to choose your own routing system.
|
876
910
|
|
877
911
|
But, if you use them together, the **only constraint is that an action must support _arity 0_ in its constructor**.
|
878
912
|
The following examples are valid constructors:
|
@@ -908,9 +942,8 @@ While a Lotus application's architecture is more web oriented, this framework is
|
|
908
942
|
|
909
943
|
### Rack middleware
|
910
944
|
|
911
|
-
Rack middleware can be configured globally in `config.ru
|
912
|
-
unnecessary overhead for all
|
913
|
-
certain middleware.
|
945
|
+
Rack middleware can be configured globally in `config.ru`. However, consider that they often add
|
946
|
+
unnecessary overhead for *all* endpoints that aren't direct users of all the configured middleware.
|
914
947
|
|
915
948
|
Think about a middleware to create sessions, where only `SessionsController::Create` needs that middleware, but every other action pays the performance price for that middleware.
|
916
949
|
|
@@ -1067,8 +1100,8 @@ end
|
|
1067
1100
|
run Action
|
1068
1101
|
```
|
1069
1102
|
|
1070
|
-
Lotus::Controller heavely depends on class configuration
|
1071
|
-
in deployment environments,
|
1103
|
+
Lotus::Controller heavely depends on class configuration. To ensure immutability
|
1104
|
+
in deployment environments, use `Lotus::Controller.load!`.
|
1072
1105
|
|
1073
1106
|
## Versioning
|
1074
1107
|
|
@@ -68,11 +68,22 @@ module Lotus
|
|
68
68
|
# Associate the given value with the given key and store them
|
69
69
|
#
|
70
70
|
# @param key [Symbol] the key
|
71
|
-
# @param value [
|
71
|
+
# @param value [#to_s,Hash] value that can be serialized as a string or
|
72
|
+
# expressed as a Hash
|
73
|
+
# @option value [String] :domain - The domain
|
74
|
+
# @option value [String] :path - The path
|
75
|
+
# @option value [Integer] :max_age - Duration expressed in seconds
|
76
|
+
# @option value [Time] :expires - Expiration time
|
77
|
+
# @option value [TrueClass,FalseClass] :secure - Restrict cookie to secure
|
78
|
+
# connections
|
79
|
+
# @option value [TrueClass,FalseClass] :httponly - Restrict JavaScript
|
80
|
+
# access
|
72
81
|
#
|
73
82
|
# @return [void]
|
74
83
|
#
|
75
84
|
# @since 0.2.0
|
85
|
+
#
|
86
|
+
# @see http://en.wikipedia.org/wiki/HTTP_cookie
|
76
87
|
def []=(key, value)
|
77
88
|
@cookies[key] = value
|
78
89
|
end
|
@@ -93,6 +104,7 @@ module Lotus
|
|
93
104
|
end
|
94
105
|
@default_options.merge cookies_options
|
95
106
|
end
|
107
|
+
|
96
108
|
# Extract the cookies from the raw Rack env.
|
97
109
|
#
|
98
110
|
# This implementation is borrowed from Rack::Request#cookies.
|
data/lib/lotus/action/cookies.rb
CHANGED
@@ -14,12 +14,21 @@ module Lotus
|
|
14
14
|
|
15
15
|
# Gets the cookies from the request and expose them as an Hash
|
16
16
|
#
|
17
|
-
#
|
17
|
+
# It automatically sets options from global configuration, but it allows to
|
18
|
+
# override values case by case.
|
19
|
+
#
|
20
|
+
# For a list of options please have a look at <tt>Lotus::Controller::Configuration</tt>,
|
21
|
+
# and <tt>Lotus::Action::CookieJar</tt>.
|
22
|
+
#
|
23
|
+
# @return [Lotus::Action::CookieJar] cookies
|
18
24
|
#
|
19
25
|
# @since 0.1.0
|
20
26
|
# @api public
|
21
27
|
#
|
22
|
-
# @
|
28
|
+
# @see Lotus::Controller::Configuration#cookies
|
29
|
+
# @see Lotus::Action::CookieJar#[]=
|
30
|
+
#
|
31
|
+
# @example Basic Usage
|
23
32
|
# require 'lotus/controller'
|
24
33
|
# require 'lotus/action/cookies'
|
25
34
|
#
|
@@ -40,6 +49,21 @@ module Lotus
|
|
40
49
|
# cookies[:bax] = nil
|
41
50
|
# end
|
42
51
|
# end
|
52
|
+
#
|
53
|
+
# @example Cookies Options
|
54
|
+
# require 'lotus/controller'
|
55
|
+
# require 'lotus/action/cookies'
|
56
|
+
#
|
57
|
+
# class Show
|
58
|
+
# include Lotus::Action
|
59
|
+
# include Lotus::Action::Cookies
|
60
|
+
#
|
61
|
+
# def call(params)
|
62
|
+
# # ...
|
63
|
+
# # set a value
|
64
|
+
# cookies[:foo] = { value: 'bar', max_age: 300, path: '/dashboard' }
|
65
|
+
# end
|
66
|
+
# end
|
43
67
|
def cookies
|
44
68
|
@cookies ||= CookieJar.new(@_env.dup, headers, configuration.cookies)
|
45
69
|
end
|
data/lib/lotus/action/head.rb
CHANGED
@@ -30,11 +30,9 @@ module Lotus
|
|
30
30
|
'Allow' => true,
|
31
31
|
'Content-Encoding' => true,
|
32
32
|
'Content-Language' => true,
|
33
|
-
'Content-Length' => true,
|
34
33
|
'Content-Location' => true,
|
35
34
|
'Content-MD5' => true,
|
36
35
|
'Content-Range' => true,
|
37
|
-
'Content-Type' => true,
|
38
36
|
'Expires' => true,
|
39
37
|
'Last-Modified' => true,
|
40
38
|
'extension-header' => true
|
data/lib/lotus/action/mime.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
require 'lotus/utils'
|
1
3
|
require 'lotus/utils/kernel'
|
2
4
|
|
3
5
|
module Lotus
|
@@ -415,7 +417,7 @@ module Lotus
|
|
415
417
|
# @api private
|
416
418
|
def accepts
|
417
419
|
unless accept == DEFAULT_ACCEPT
|
418
|
-
|
420
|
+
best_q_match(accept, ::Rack::Mime::MIME_TYPES.values)
|
419
421
|
end
|
420
422
|
end
|
421
423
|
|
@@ -446,6 +448,34 @@ module Lotus
|
|
446
448
|
"#{content_type}; charset=#{charset}"
|
447
449
|
end
|
448
450
|
|
451
|
+
# Patched version of <tt>Rack::Utils.best_q_match</tt>.
|
452
|
+
#
|
453
|
+
# @since x.x.x
|
454
|
+
# @api private
|
455
|
+
#
|
456
|
+
# @see http://www.rubydoc.info/gems/rack/Rack/Utils#best_q_match-class_method
|
457
|
+
# @see https://github.com/rack/rack/pull/659
|
458
|
+
# @see https://github.com/lotus/controller/issues/59
|
459
|
+
# @see https://github.com/lotus/controller/issues/104
|
460
|
+
def best_q_match(q_value_header, available_mimes)
|
461
|
+
values = ::Rack::Utils.q_values(q_value_header)
|
462
|
+
|
463
|
+
values = values.map do |req_mime, quality|
|
464
|
+
match = available_mimes.find { |am| ::Rack::Mime.match?(am, req_mime) }
|
465
|
+
next unless match
|
466
|
+
[match, quality]
|
467
|
+
end.compact
|
468
|
+
|
469
|
+
# See https://github.com/lotus/controller/issues/59
|
470
|
+
# See https://github.com/lotus/controller/issues/104
|
471
|
+
values = values.reverse unless Lotus::Utils.jruby?
|
472
|
+
|
473
|
+
value = values.sort_by do |match, quality|
|
474
|
+
(match.split('/', 2).count('*') * -10) + quality
|
475
|
+
end.last
|
476
|
+
|
477
|
+
value.first if value
|
478
|
+
end
|
449
479
|
end
|
450
480
|
end
|
451
481
|
end
|
data/lib/lotus/controller.rb
CHANGED
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.
|
4
|
+
version: 0.4.1
|
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-05-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -149,7 +149,6 @@ files:
|
|
149
149
|
- lib/lotus/controller/configuration.rb
|
150
150
|
- lib/lotus/controller/version.rb
|
151
151
|
- lib/lotus/http/status.rb
|
152
|
-
- lib/rack-patch.rb
|
153
152
|
- lotus-controller.gemspec
|
154
153
|
homepage: http://lotusrb.org
|
155
154
|
licenses:
|
data/lib/rack-patch.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# see https://github.com/rack/rack/pull/659
|
2
|
-
require 'rack'
|
3
|
-
require 'lotus/utils'
|
4
|
-
|
5
|
-
if Rack.release <= '1.5'
|
6
|
-
require 'rack/utils'
|
7
|
-
|
8
|
-
Rack::Utils.class_eval do
|
9
|
-
def self.best_q_match(q_value_header, available_mimes)
|
10
|
-
values = q_values(q_value_header)
|
11
|
-
|
12
|
-
values = values.map do |req_mime, quality|
|
13
|
-
match = available_mimes.find { |am| Rack::Mime.match?(am, req_mime) }
|
14
|
-
next unless match
|
15
|
-
[match, quality]
|
16
|
-
end.compact
|
17
|
-
|
18
|
-
# See https://github.com/lotus/controller/issues/59
|
19
|
-
values = values.reverse if RUBY_VERSION >= '2.2.0' || Lotus::Utils.rubinius?
|
20
|
-
|
21
|
-
value = values.sort_by do |match, quality|
|
22
|
-
(match.split('/', 2).count('*') * -10) + quality
|
23
|
-
end.last
|
24
|
-
|
25
|
-
value.first if value
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|