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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6ca9d4aeba96621f489499f6dc67a58b62b5dd66
4
- data.tar.gz: f86c1e2d695ac2fb3bc595a6fb6b587edd4f6bc1
3
+ metadata.gz: 69d59a1b6b0f96e6af7bf468a3003a45992b17a3
4
+ data.tar.gz: 93d926cb9fdf962ef3e55d6c5c1efb6de4b546ad
5
5
  SHA512:
6
- metadata.gz: 5597d8bc2fe5a15577a6698577adb1f9895c5c37353faed252b045974010b351994cfadcadf3d49215a76b5477aab5d95487d84e570b8913a8a0669ae0dee473
7
- data.tar.gz: a8e9f5b71b887a6db599e93e7eb555bb1fa556d33ac0ae5f9c095c18f2b55b29bdf53822b9fa459615c03cedf965d564ef4ee52e347d56bc412aaa1e3ff98133
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`.
@@ -68,7 +68,7 @@ module Lotus
68
68
  def call(env)
69
69
  _rescue do
70
70
  @_env = env
71
- @headers = ::Rack::Utils::HeaderHash.new
71
+ @headers = ::Rack::Utils::HeaderHash.new(configuration.default_headers)
72
72
  @params = self.class.params_class.new(@_env)
73
73
  super @params
74
74
  end
@@ -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 = headers
41
- @cookies = Utils::Hash.new(extract(env)).symbolize!
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.
@@ -41,7 +41,7 @@ module Lotus
41
41
  # end
42
42
  # end
43
43
  def cookies
44
- @cookies ||= CookieJar.new(@_env.dup, headers)
44
+ @cookies ||= CookieJar.new(@_env.dup, headers, configuration.cookies)
45
45
  end
46
46
 
47
47
  private
@@ -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 = session
26
- @request_id = 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 @request_id != request_id
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
@@ -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
 
@@ -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]
@@ -94,8 +94,8 @@ module Lotus
94
94
  # end
95
95
  # end
96
96
  # end
97
- def use(middleware, *args)
98
- rack_builder.use middleware, *args
97
+ def use(middleware, *args, &block)
98
+ rack_builder.use middleware, *args, &block
99
99
  end
100
100
  end
101
101
 
@@ -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 x.x.x
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
- self.status = status
55
+ halt(status)
54
56
  end
55
57
  end
56
58
  end
@@ -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 = handle_exceptions
507
- c.handled_exceptions = handled_exceptions.dup
508
- c.action_module = action_module
509
- c.modules = modules.dup
510
- c.formats = formats.dup
511
- c.default_format = default_format
512
- c.default_charset = 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
@@ -3,6 +3,6 @@ module Lotus
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '0.3.2'.freeze
6
+ VERSION = '0.4.0'.freeze
7
7
  end
8
8
  end
@@ -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.3', '>= 0.3.4'
24
- spec.add_dependency 'lotus-validations', '~> 0.2', '>= 0.2.4'
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.3.2
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-01-30 00:00:00.000000000 Z
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.3'
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.3'
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.2'
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.2'
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.2.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