hanami-controller 0.0.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +155 -0
  3. data/LICENSE.md +22 -0
  4. data/README.md +1180 -9
  5. data/hanami-controller.gemspec +19 -12
  6. data/lib/hanami-controller.rb +1 -0
  7. data/lib/hanami/action.rb +85 -0
  8. data/lib/hanami/action/cache.rb +174 -0
  9. data/lib/hanami/action/cache/cache_control.rb +70 -0
  10. data/lib/hanami/action/cache/conditional_get.rb +93 -0
  11. data/lib/hanami/action/cache/directives.rb +99 -0
  12. data/lib/hanami/action/cache/expires.rb +73 -0
  13. data/lib/hanami/action/callable.rb +94 -0
  14. data/lib/hanami/action/callbacks.rb +210 -0
  15. data/lib/hanami/action/configurable.rb +49 -0
  16. data/lib/hanami/action/cookie_jar.rb +181 -0
  17. data/lib/hanami/action/cookies.rb +85 -0
  18. data/lib/hanami/action/exposable.rb +115 -0
  19. data/lib/hanami/action/flash.rb +182 -0
  20. data/lib/hanami/action/glue.rb +66 -0
  21. data/lib/hanami/action/head.rb +122 -0
  22. data/lib/hanami/action/mime.rb +493 -0
  23. data/lib/hanami/action/params.rb +285 -0
  24. data/lib/hanami/action/rack.rb +270 -0
  25. data/lib/hanami/action/rack/callable.rb +47 -0
  26. data/lib/hanami/action/rack/file.rb +33 -0
  27. data/lib/hanami/action/redirect.rb +59 -0
  28. data/lib/hanami/action/request.rb +86 -0
  29. data/lib/hanami/action/session.rb +154 -0
  30. data/lib/hanami/action/throwable.rb +194 -0
  31. data/lib/hanami/action/validatable.rb +128 -0
  32. data/lib/hanami/controller.rb +250 -2
  33. data/lib/hanami/controller/configuration.rb +705 -0
  34. data/lib/hanami/controller/error.rb +7 -0
  35. data/lib/hanami/controller/version.rb +4 -1
  36. data/lib/hanami/http/status.rb +62 -0
  37. metadata +124 -16
  38. data/.gitignore +0 -9
  39. data/Gemfile +0 -4
  40. data/Rakefile +0 -2
  41. data/bin/console +0 -14
  42. data/bin/setup +0 -8
@@ -0,0 +1,99 @@
1
+ module Hanami
2
+ module Action
3
+ module Cache
4
+
5
+ # Cache-Control directives which have values
6
+ #
7
+ # @since 0.3.0
8
+ # @api private
9
+ VALUE_DIRECTIVES = %i(max_age s_maxage min_fresh max_stale).freeze
10
+
11
+ # Cache-Control directives which are implicitly true
12
+ #
13
+ # @since 0.3.0
14
+ # @api private
15
+ NON_VALUE_DIRECTIVES = %i(public private no_cache no_store no_transform must_revalidate proxy_revalidate).freeze
16
+
17
+ # Class representing value directives
18
+ #
19
+ # ex: max-age=600
20
+ #
21
+ # @since 0.3.0
22
+ # @api private
23
+ class ValueDirective
24
+ attr_reader :name
25
+
26
+ def initialize(name, value)
27
+ @name, @value = name, value
28
+ end
29
+
30
+ def to_str
31
+ "#{@name.to_s.tr('_', '-')}=#{@value.to_i}"
32
+ end
33
+
34
+ def valid?
35
+ VALUE_DIRECTIVES.include? @name
36
+ end
37
+ end
38
+
39
+ # Class representing non value directives
40
+ #
41
+ # ex: no-cache
42
+ #
43
+ # @since 0.3.0
44
+ # @api private
45
+ class NonValueDirective
46
+ attr_reader :name
47
+
48
+ def initialize(name)
49
+ @name = name
50
+ end
51
+
52
+ def to_str
53
+ @name.to_s.tr('_', '-')
54
+ end
55
+
56
+ def valid?
57
+ NON_VALUE_DIRECTIVES.include? @name
58
+ end
59
+ end
60
+
61
+ # Collection of value and non value directives
62
+ #
63
+ # @since 0.3.0
64
+ # @api private
65
+ class Directives
66
+ include Enumerable
67
+
68
+ def initialize(*values)
69
+ @directives = []
70
+ values.each do |directive_key|
71
+ if directive_key.kind_of? Hash
72
+ directive_key.each { |name, value| self.<< ValueDirective.new(name, value) }
73
+ else
74
+ self.<< NonValueDirective.new(directive_key)
75
+ end
76
+ end
77
+ end
78
+
79
+ def each
80
+ @directives.each { |d| yield d }
81
+ end
82
+
83
+ def <<(directive)
84
+ @directives << directive if directive.valid?
85
+ end
86
+
87
+ def values
88
+ @directives.delete_if do |directive|
89
+ directive.name == :public && @directives.map(&:name).include?(:private)
90
+ end
91
+ end
92
+
93
+ def join(separator)
94
+ values.join(separator)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,73 @@
1
+ require 'hanami/action/cache/cache_control'
2
+
3
+ module Hanami
4
+ module Action
5
+ module Cache
6
+
7
+ # Module with Expires logic
8
+ #
9
+ # @since 0.3.0
10
+ # @api private
11
+ module Expires
12
+
13
+ # The HTTP header for Expires
14
+ #
15
+ # @since 0.3.0
16
+ # @api private
17
+ HEADER = 'Expires'.freeze
18
+
19
+ def self.included(base)
20
+ base.extend ClassMethods
21
+ end
22
+
23
+ module ClassMethods
24
+ def expires(amount, *values)
25
+ @expires_directives ||= Directives.new(amount, *values)
26
+ end
27
+
28
+ def expires_directives
29
+ @expires_directives || Object.new.tap do |null_object|
30
+ def null_object.headers
31
+ Hash.new
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # Finalize the response including default cache headers into the response
38
+ #
39
+ # @since 0.3.0
40
+ # @api private
41
+ #
42
+ # @see Hanami::Action#finish
43
+ def finish
44
+ super
45
+ headers.merge!(self.class.expires_directives.headers) unless headers.include? HEADER
46
+ end
47
+
48
+ # Class which stores Expires directives
49
+ #
50
+ # @since 0.3.0
51
+ #
52
+ # @api private
53
+ #
54
+ class Directives
55
+ def initialize(amount, *values)
56
+ @amount = amount
57
+ @cache_control = Hanami::Action::Cache::CacheControl::Directives.new(*(values << { max_age: amount }))
58
+ end
59
+
60
+ def headers
61
+ { HEADER => time.httpdate }.merge(@cache_control.headers)
62
+ end
63
+
64
+ private
65
+
66
+ def time
67
+ Time.now + @amount.to_i
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,94 @@
1
+ require 'hanami/action/params'
2
+
3
+ module Hanami
4
+ module Action
5
+ module Callable
6
+ # Execute application logic.
7
+ # It implements the Rack protocol.
8
+ #
9
+ # The request params are passed as an argument to the `#call` method.
10
+ #
11
+ # If routed with Hanami::Router, it extracts the relevant bits from the
12
+ # Rack `env` (eg the requested `:id`).
13
+ #
14
+ # Otherwise everything it's passed as it is: the full Rack `env`
15
+ # in production, and the given `Hash` for unit tests. See the examples
16
+ # below.
17
+ #
18
+ # Application developers are forced to implement this method in their
19
+ # actions.
20
+ #
21
+ # @param env [Hash] the full Rack env or the params. This value may vary,
22
+ # see the examples below.
23
+ #
24
+ # @return [Array] a serialized Rack response (eg. `[200, {}, ["Hi!"]]`)
25
+ #
26
+ # @since 0.1.0
27
+ #
28
+ # @example with Hanami::Router
29
+ # require 'hanami/controller'
30
+ #
31
+ # class Show
32
+ # include Hanami::Action
33
+ #
34
+ # def call(params)
35
+ # # ...
36
+ # puts params # => { id: 23 } extracted from Rack env
37
+ # end
38
+ # end
39
+ #
40
+ # @example Standalone
41
+ # require 'hanami/controller'
42
+ #
43
+ # class Show
44
+ # include Hanami::Action
45
+ #
46
+ # def call(params)
47
+ # # ...
48
+ # puts params
49
+ # # => { :"rack.version"=>[1, 2],
50
+ # # :"rack.input"=>#<StringIO:0x007fa563463948>, ... }
51
+ # end
52
+ # end
53
+ #
54
+ # @example Unit Testing
55
+ # require 'hanami/controller'
56
+ #
57
+ # class Show
58
+ # include Hanami::Action
59
+ #
60
+ # def call(params)
61
+ # # ...
62
+ # puts params # => { id: 23, key: 'value' } passed as it is from testing
63
+ # end
64
+ # end
65
+ #
66
+ # action = Show.new
67
+ # response = action.call({ id: 23, key: 'value' })
68
+ def call(env)
69
+ _rescue do
70
+ @_env = env
71
+ @headers = ::Rack::Utils::HeaderHash.new(configuration.default_headers)
72
+ @params = self.class.params_class.new(@_env)
73
+ super @params
74
+ end
75
+
76
+ finish
77
+ end
78
+
79
+ private
80
+
81
+ # Prepare the Rack response before the control is returned to the
82
+ # webserver.
83
+ #
84
+ # @since 0.1.0
85
+ # @api private
86
+ #
87
+ # @see Hanami::Action#finish
88
+ def finish
89
+ super
90
+ response
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,210 @@
1
+ require 'hanami/utils/class_attribute'
2
+ require 'hanami/utils/callbacks'
3
+
4
+ module Hanami
5
+ module Action
6
+ # Before and after callbacks
7
+ #
8
+ # @since 0.1.0
9
+ # @see Hanami::Action::ClassMethods#before
10
+ # @see Hanami::Action::ClassMethods#after
11
+ module Callbacks
12
+ # Override Ruby's hook for modules.
13
+ # It includes callbacks logic
14
+ #
15
+ # @param base [Class] the target action
16
+ #
17
+ # @since 0.1.0
18
+ # @api private
19
+ #
20
+ # @see http://www.ruby-doc.org/core/Module.html#method-i-included
21
+ def self.included(base)
22
+ base.class_eval do
23
+ extend ClassMethods
24
+ prepend InstanceMethods
25
+ end
26
+ end
27
+
28
+ # Callbacks API class methods
29
+ #
30
+ # @since 0.1.0
31
+ # @api private
32
+ module ClassMethods
33
+ # Override Ruby's hook for modules.
34
+ # It includes callbacks logic
35
+ #
36
+ # @param base [Class] the target action
37
+ #
38
+ # @since 0.1.0
39
+ # @api private
40
+ #
41
+ # @see http://www.ruby-doc.org/core/Module.html#method-i-extended
42
+ def self.extended(base)
43
+ base.class_eval do
44
+ include Utils::ClassAttribute
45
+
46
+ class_attribute :before_callbacks
47
+ self.before_callbacks = Utils::Callbacks::Chain.new
48
+
49
+ class_attribute :after_callbacks
50
+ self.after_callbacks = Utils::Callbacks::Chain.new
51
+ end
52
+ end
53
+
54
+ # Define a callback for an Action.
55
+ # The callback will be executed **before** the action is called, in the
56
+ # order they are added.
57
+ #
58
+ # @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
59
+ # each of them is representing a name of a method available in the
60
+ # context of the Action.
61
+ #
62
+ # @param blk [Proc] an anonymous function to be executed
63
+ #
64
+ # @return [void]
65
+ #
66
+ # @since 0.3.2
67
+ #
68
+ # @see Hanami::Action::Callbacks::ClassMethods#append_after
69
+ #
70
+ # @example Method names (symbols)
71
+ # require 'hanami/controller'
72
+ #
73
+ # class Show
74
+ # include Hanami::Action
75
+ #
76
+ # before :authenticate, :set_article
77
+ #
78
+ # def call(params)
79
+ # end
80
+ #
81
+ # private
82
+ # def authenticate
83
+ # # ...
84
+ # end
85
+ #
86
+ # # `params` in the method signature is optional
87
+ # def set_article(params)
88
+ # @article = Article.find params[:id]
89
+ # end
90
+ # end
91
+ #
92
+ # # The order of execution will be:
93
+ # #
94
+ # # 1. #authenticate
95
+ # # 2. #set_article
96
+ # # 3. #call
97
+ #
98
+ # @example Anonymous functions (Procs)
99
+ # require 'hanami/controller'
100
+ #
101
+ # class Show
102
+ # include Hanami::Action
103
+ #
104
+ # before { ... } # 1 do some authentication stuff
105
+ # before {|params| @article = Article.find params[:id] } # 2
106
+ #
107
+ # def call(params)
108
+ # end
109
+ # end
110
+ #
111
+ # # The order of execution will be:
112
+ # #
113
+ # # 1. authentication
114
+ # # 2. set the article
115
+ # # 3. #call
116
+ def append_before(*callbacks, &blk)
117
+ before_callbacks.append(*callbacks, &blk)
118
+ end
119
+
120
+ # @since 0.1.0
121
+ alias_method :before, :append_before
122
+
123
+ # Define a callback for an Action.
124
+ # The callback will be executed **after** the action is called, in the
125
+ # order they are added.
126
+ #
127
+ # @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
128
+ # each of them is representing a name of a method available in the
129
+ # context of the Action.
130
+ #
131
+ # @param blk [Proc] an anonymous function to be executed
132
+ #
133
+ # @return [void]
134
+ #
135
+ # @since 0.3.2
136
+ #
137
+ # @see Hanami::Action::Callbacks::ClassMethods#append_before
138
+ def append_after(*callbacks, &blk)
139
+ after_callbacks.append(*callbacks, &blk)
140
+ end
141
+
142
+ # @since 0.1.0
143
+ alias_method :after, :append_after
144
+
145
+ # Define a callback for an Action.
146
+ # The callback will be executed **before** the action is called.
147
+ # It will add the callback at the beginning of the callbacks' chain.
148
+ #
149
+ # @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
150
+ # each of them is representing a name of a method available in the
151
+ # context of the Action.
152
+ #
153
+ # @param blk [Proc] an anonymous function to be executed
154
+ #
155
+ # @return [void]
156
+ #
157
+ # @since 0.3.2
158
+ #
159
+ # @see Hanami::Action::Callbacks::ClassMethods#prepend_after
160
+ def prepend_before(*callbacks, &blk)
161
+ before_callbacks.prepend(*callbacks, &blk)
162
+ end
163
+
164
+ # Define a callback for an Action.
165
+ # The callback will be executed **after** the action is called.
166
+ # It will add the callback at the beginning of the callbacks' chain.
167
+ #
168
+ # @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
169
+ # each of them is representing a name of a method available in the
170
+ # context of the Action.
171
+ #
172
+ # @param blk [Proc] an anonymous function to be executed
173
+ #
174
+ # @return [void]
175
+ #
176
+ # @since 0.3.2
177
+ #
178
+ # @see Hanami::Action::Callbacks::ClassMethods#prepend_before
179
+ def prepend_after(*callbacks, &blk)
180
+ after_callbacks.prepend(*callbacks, &blk)
181
+ end
182
+ end
183
+
184
+ # Callbacks API instance methods
185
+ #
186
+ # @since 0.1.0
187
+ # @api private
188
+ module InstanceMethods
189
+ # Implements the Rack/Hanami::Action protocol
190
+ #
191
+ # @since 0.1.0
192
+ # @api private
193
+ def call(params)
194
+ _run_before_callbacks(params)
195
+ super
196
+ _run_after_callbacks(params)
197
+ end
198
+
199
+ private
200
+ def _run_before_callbacks(params)
201
+ self.class.before_callbacks.run(self, params)
202
+ end
203
+
204
+ def _run_after_callbacks(params)
205
+ self.class.after_callbacks.run(self, params)
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end