activefunction 0.3.5 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/README.md +141 -72
- data/lib/.rbnext/2.7/active_function/functions/strong_parameters.rb +44 -40
- data/lib/.rbnext/3.0/active_function/base.rb +38 -0
- data/lib/.rbnext/3.0/active_function/functions/response.rb +16 -19
- data/lib/.rbnext/3.0/active_function.rb +50 -0
- data/lib/.rbnext/3.1/active_function/functions/response.rb +27 -0
- data/lib/active_function/base.rb +32 -14
- data/lib/active_function/functions/callbacks.rb +5 -59
- data/lib/active_function/functions/rendering.rb +13 -9
- data/lib/active_function/functions/response.rb +16 -19
- data/lib/active_function/functions/strong_parameters.rb +44 -40
- data/lib/active_function/version.rb +1 -1
- data/lib/active_function.rb +41 -3
- metadata +24 -60
- data/lib/.rbnext/3.0/active_function/functions/core.rb +0 -49
- data/lib/active_function/functions/core.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f2bbae1e08a02af0dbabf4b235ece3823d17aec26e76b24ad7f4b514ea8526c
|
4
|
+
data.tar.gz: 6097f177bb4b5dff0583c21c40d45c67aeb4bd81eb6551b52e59c440b3e5e027
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51cfd314a65879813c1e8c8c23ea80b32621a1479a5c4ea09c789557ddcc80b05b29af2d84181fec90984c4137f8cc4735bd24e76a6eb2e38d81cfa543605ea7
|
7
|
+
data.tar.gz: 2ea0d89e66aa2a3c89f3b011a84bed5628a9e2e1de78c506f24d93393ab389922581ba1429509c3f8e30883a6788216069078aec3d38cd692c6eda1314b6a69f
|
data/CHANGELOG.md
CHANGED
@@ -46,4 +46,11 @@
|
|
46
46
|
|
47
47
|
- Start gem restructuring for modularizaition
|
48
48
|
- Introduce `activefunction-core` gem with RubyNext integration
|
49
|
-
|
49
|
+
|
50
|
+
# [0.4.0] - 2024-01-11
|
51
|
+
|
52
|
+
- Replace `ActiveFunction::Functions::Callbacks` with `ActiveFunctionCore::Plugins::Hooks`
|
53
|
+
- Introduce Plugin system
|
54
|
+
- Global refactoring
|
55
|
+
|
56
|
+
|
data/README.md
CHANGED
@@ -1,144 +1,213 @@
|
|
1
1
|
# ActiveFunction
|
2
2
|
|
3
|
-
|
3
|
+
Playground gem for Ruby 3.2+ features and more.
|
4
4
|
|
5
|
-
|
5
|
+
Collection of gems designed to be used with FaaS (Function as a Service) computing instances. Inspired by aws-sdk v3 gem structure & rails/activesupport.
|
6
6
|
|
7
|
+
## Features
|
7
8
|
|
9
|
+
- **Ruby Version Compatibility:** Implemented with most of ruby 3.2+ features, BUT supports Ruby versions >= 2.6 through the use of the [RubyNext](https://github.com/ruby-next/ruby-next) transpiler. (CI'ed)
|
10
|
+
- **Type Safety:** Achieves type safety through the use of RBS and [Steep](https://github.com/soutaro/steep). (CI'ed)
|
11
|
+
- Note: disabled due to the presence of Ruby::UnsupportedSyntax errors.
|
12
|
+
- **Plugins System:** Provides a simple Plugin system (inspired by [Polishing Ruby Programming by Jeremy Evans](https://github.com/PacktPublishing/Polished-Ruby-Programming)) to load gem plugins as well as self-defined plugins.
|
13
|
+
- **Gem Collection** Provides a collection of gems designed to be used within ActiveFunction or standalone.
|
8
14
|
|
9
|
-
|
15
|
+
# Gems
|
16
|
+
|
17
|
+
- [activefunction](/) - Main gem, provides rails/action-controller like API with callbacks, strong parameters and rendering under plugins.
|
18
|
+
- [activefunction-core](/gems/activefunction-core/README.md) - Provides RubyNext integration and External Standalone Plugins
|
19
|
+
- activefunction-orm - WIP (ORM around AWS PartiQL)
|
20
|
+
|
21
|
+
## Quick Start
|
10
22
|
|
11
23
|
Here's a simple example of a function that uses ActiveFunction:
|
12
24
|
|
13
25
|
```ruby
|
14
|
-
require
|
26
|
+
require "active_function"
|
15
27
|
|
16
28
|
class AppFunction < ActiveFunction::Base
|
17
|
-
def index
|
18
|
-
|
19
|
-
|
29
|
+
def index
|
30
|
+
response_with_error if @request[:data].nil?
|
31
|
+
|
32
|
+
return if performed?
|
33
|
+
|
34
|
+
@response.status = 200
|
35
|
+
@response.headers = {"Content-Type" => "application/json"}
|
36
|
+
@response.body = @request[:data]
|
37
|
+
end
|
38
|
+
|
39
|
+
private def response_with_error
|
40
|
+
@response.status = 400
|
41
|
+
@response.commit!
|
42
|
+
end
|
20
43
|
end
|
44
|
+
|
45
|
+
AppFunction.process(:index, {data: {id: 1}}) # => { :statusCode=>200, :headers=> {"Content-Type"=>"application/json"}, :body=>{id: 1}"}
|
21
46
|
```
|
22
47
|
|
23
|
-
|
48
|
+
## Plugins
|
24
49
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
50
|
+
ActiveFunction supports plugins which can be loaded by `ActiveFunction.config` method. Currently, there are 3 plugins available:
|
51
|
+
- [:callbacks](#callbacks) - provides `:before_action` and `:after_action` callbacks with `:if`, `:unless` & `:only` options.
|
52
|
+
- [:strong_parameters](#strong-parameters) - provides strong parameters support via `#params` instance method.
|
53
|
+
- [:rendering](#rendering) - provides rendering support via `#render` instance method.
|
54
|
+
|
55
|
+
## Configuration
|
56
|
+
|
57
|
+
To configure ActiveFunction with plugins, use the `ActiveFunction.config` method.
|
31
58
|
|
32
59
|
```ruby
|
33
|
-
|
34
|
-
|
35
|
-
after_action :log_response
|
36
|
-
|
37
|
-
# some action ...
|
60
|
+
# config/initializers/active_function.rb
|
61
|
+
require "active_function"
|
38
62
|
|
39
|
-
|
63
|
+
ActiveFunction.config do
|
64
|
+
plugin :callbacks
|
65
|
+
plugin :strong_parameters
|
66
|
+
plugin :rendering
|
67
|
+
end
|
68
|
+
```
|
40
69
|
|
41
|
-
|
42
|
-
|
43
|
-
|
70
|
+
```ruby
|
71
|
+
# app/functions/app_function.rb
|
72
|
+
class AppFunction < ActiveFunction::Base
|
73
|
+
before_action :parse_user_data
|
44
74
|
|
45
|
-
def
|
46
|
-
|
75
|
+
def index
|
76
|
+
render json: @user_data
|
47
77
|
end
|
78
|
+
|
79
|
+
private def parse_user_data = @user_data = params.require(:data).permit(:id, :name).to_h
|
48
80
|
end
|
49
81
|
```
|
50
82
|
|
51
|
-
|
83
|
+
Use `#process` method to proceed the request:
|
52
84
|
|
53
85
|
```ruby
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
# some actions ...
|
58
|
-
|
59
|
-
private def request_valid? = true
|
60
|
-
end
|
86
|
+
# handler.rb
|
87
|
+
AppFunction.process(:index, {data: { id: 1, name: 2}}) # => { :statusCode=>200, :headers=> {"Content-Type"=>"application/json"}, :body=>"{\"id\":1,\"name\":2}"}
|
61
88
|
```
|
62
89
|
|
63
|
-
|
90
|
+
|
91
|
+
## Callbacks
|
92
|
+
|
93
|
+
Simple callbacks engined by [ActiveFunctionCore::Plugins::Hooks](https://github.com/DanilMaximov/activefunction/tree/master/gems/activefunction-core#hooks) external plugin and provides `:before_action` and `:after_action` which runs around provided action in `#process`.
|
94
|
+
|
64
95
|
```ruby
|
65
|
-
|
66
|
-
before_action :set_current_user
|
96
|
+
require "active_function"
|
67
97
|
|
68
|
-
|
69
|
-
|
70
|
-
end
|
98
|
+
ActiveFunction.config do
|
99
|
+
plugin :callbacks
|
71
100
|
end
|
72
101
|
|
73
|
-
class
|
102
|
+
class AppFunction < ActiveFunction::Base
|
103
|
+
before_action :set_user
|
104
|
+
after_action :log_response
|
105
|
+
|
74
106
|
def index
|
75
|
-
|
107
|
+
# some actions ...
|
76
108
|
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def set_user = @_user ||= User.find(@request[:id])
|
113
|
+
def log_response = Logger.info(@response)
|
77
114
|
end
|
78
115
|
```
|
79
|
-
|
80
|
-
|
116
|
+
|
117
|
+
### Callbacks options
|
118
|
+
|
119
|
+
Supports default [ActiveFunctionCore::Plugins::Hooks::Hook::Callback options](https://github.com/DanilMaximov/activefunction/tree/master/gems/activefunction-core#options) `:if => Symbol` & `:unless => Symbol` options.
|
120
|
+
|
121
|
+
Support custom defined in ActiveFunction::Function::Callbacks `only: Array[Symbol]` option.
|
81
122
|
|
82
123
|
```ruby
|
83
|
-
|
124
|
+
class AppFunction < ActiveFunction::Base
|
125
|
+
before_action :set_user, only: %i[show update destroy], if: :request_valid?
|
126
|
+
|
127
|
+
# some actions ...
|
128
|
+
|
129
|
+
private def request_valid? = true
|
130
|
+
end
|
84
131
|
```
|
132
|
+
More details in [ActiveFunctionCore::Plugins::Hooks readme](https://github.com/DanilMaximov/activefunction/tree/master/gems/activefunction-core#hooks)
|
133
|
+
|
134
|
+
## Strong Parameters
|
135
|
+
|
136
|
+
ActiveFunction supports strong parameters which can be accessed by `#params` instance method.
|
137
|
+
|
138
|
+
The `#params` method represents a Ruby 3.2 Data class that allows the manipulation of request parameters. It supports the following methods:
|
139
|
+
|
140
|
+
- `[]`: Access parameters by key.
|
141
|
+
- `permit`: Specify the allowed parameters.
|
142
|
+
- `require`: Ensure the presence of a specific parameter.
|
143
|
+
- `to_h`: Convert the parameters to a hash.
|
144
|
+
|
145
|
+
Usage Example:
|
85
146
|
|
86
|
-
Simple usage:
|
87
147
|
```ruby
|
148
|
+
require "active_function"
|
149
|
+
|
150
|
+
ActiveFunction.config do
|
151
|
+
plugin :strong_parameters
|
152
|
+
end
|
153
|
+
|
88
154
|
class PostsFunction < ActiveFunction::Base
|
89
|
-
def index
|
90
|
-
|
91
|
-
end
|
155
|
+
def index
|
156
|
+
@response.body = permitted_params
|
157
|
+
end
|
92
158
|
|
93
159
|
def permitted_params = params
|
94
160
|
.require(:data)
|
95
161
|
.permit(:id, :name)
|
96
162
|
.to_h
|
97
|
-
end
|
163
|
+
end
|
164
|
+
|
165
|
+
PostFunction.process(:index, data: {id: 1, name: "Pupa"})
|
98
166
|
```
|
167
|
+
|
99
168
|
Strong params supports nested attributes
|
100
|
-
```ruby
|
169
|
+
```ruby
|
101
170
|
params.permit(:id, :name, :address => [:city, :street])
|
102
171
|
```
|
103
172
|
|
104
173
|
## Rendering
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
174
|
+
|
175
|
+
ActiveFunction supports rendering of JSON. The #render method is used for rendering responses. It accepts the following options:
|
176
|
+
|
177
|
+
- `head`: Set response headers. Defaults to `{ "Content-Type" => "application/json" }`.
|
178
|
+
- `json`: Set the response body with a JSON-like object. Defaults to `{}`.
|
179
|
+
- `status`: Set the HTTP status code. Defaults to `200`.
|
180
|
+
|
181
|
+
Additionally, the method automatically commits the response and JSONifies the body.
|
182
|
+
|
183
|
+
Usage Example:
|
114
184
|
```ruby
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
185
|
+
require "active_function"
|
186
|
+
|
187
|
+
ActiveFunction.config do
|
188
|
+
plugin :rendering
|
119
189
|
end
|
120
|
-
|
121
|
-
Headers can be passed by `:headers` option. Default headers are `{"Content-Type" => "application/json"}`.
|
122
|
-
```ruby
|
190
|
+
|
123
191
|
class PostsFunction < ActiveFunction::Base
|
124
|
-
def index
|
125
|
-
render json: {
|
126
|
-
end
|
192
|
+
def index
|
193
|
+
render json: {id: 1, name: "Pupa"}, status: 200, head: {"Some-Header" => "Some-Value"}
|
194
|
+
end
|
127
195
|
end
|
128
|
-
```
|
129
196
|
|
197
|
+
PostFunction.process(:index) # => { :statusCode=>200, :headers=> {"Content-Type"=>"application/json", "Some-Header" => "Some-Value"}, :body=>"{\"id\":1,\"name\":\"Pupa\"}"}
|
198
|
+
```
|
130
199
|
|
131
200
|
## Installation
|
132
201
|
|
133
202
|
Add this line to your application's Gemfile:
|
134
203
|
|
135
204
|
```ruby
|
136
|
-
gem
|
205
|
+
gem "activefunction"
|
137
206
|
```
|
138
207
|
|
139
208
|
## Development
|
140
209
|
|
141
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake test` to run the tests and `bin/rake steep` to run type checker.
|
210
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake test` to run the tests and `bin/rake steep` to run type checker.
|
142
211
|
|
143
212
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
144
213
|
|
@@ -3,52 +3,45 @@
|
|
3
3
|
require "forwardable"
|
4
4
|
|
5
5
|
module ActiveFunction
|
6
|
-
|
7
|
-
|
6
|
+
module Functions
|
7
|
+
module StrongParameters
|
8
|
+
ActiveFunction.register_plugin :strong_parameters, self
|
8
9
|
|
9
|
-
|
10
|
+
Error = Class.new(StandardError)
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
12
|
+
class Parameters < Data.define(:params, :permitted)
|
13
|
+
class ParameterMissingError < Error
|
14
|
+
MESSAGE_TEMPLATE = "Missing parameter: %s"
|
15
15
|
|
16
|
-
|
17
|
-
MESSAGE_TEMPLATE = "Unpermitted parameter: %s"
|
16
|
+
attr_reader :message
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
end
|
18
|
+
def initialize(param)
|
19
|
+
MESSAGE_TEMPLATE % param
|
20
|
+
end
|
21
|
+
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
def params
|
29
|
-
@_params ||= Parameters.new(@request)
|
30
|
-
end
|
23
|
+
class UnpermittedParameterError < Error
|
24
|
+
MESSAGE_TEMPLATE = "Unpermitted parameter: %s"
|
31
25
|
|
32
|
-
|
33
|
-
extend Forwardable
|
34
|
-
def_delegators :@parameters, :each, :map
|
35
|
-
include Enumerable
|
26
|
+
attr_reader :message
|
36
27
|
|
37
|
-
|
38
|
-
|
39
|
-
|
28
|
+
def initialize(param)
|
29
|
+
MESSAGE_TEMPLATE % param
|
30
|
+
end
|
40
31
|
end
|
41
32
|
|
33
|
+
protected :params
|
34
|
+
|
42
35
|
def [](attribute)
|
43
|
-
nested_attribute(
|
36
|
+
nested_attribute(params[attribute])
|
44
37
|
end
|
45
38
|
|
46
39
|
def require(attribute)
|
47
|
-
value = self[attribute]
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
40
|
+
if (value = self[attribute])
|
41
|
+
value
|
42
|
+
else
|
43
|
+
raise ParameterMissingError, attribute
|
44
|
+
end
|
52
45
|
end
|
53
46
|
|
54
47
|
def permit(*attributes)
|
@@ -60,28 +53,37 @@ module ActiveFunction
|
|
60
53
|
pparams[k] = process_nested(self[k], :permit, v)
|
61
54
|
end
|
62
55
|
else
|
63
|
-
next unless
|
56
|
+
next unless params.key?(attribute)
|
64
57
|
|
65
58
|
pparams[attribute] = self[attribute]
|
66
59
|
end
|
67
60
|
end
|
68
61
|
|
69
|
-
|
62
|
+
with(params: pparams, permitted: true)
|
70
63
|
end
|
71
64
|
|
65
|
+
# Redefines RubyNext::Core::Data instance methods
|
72
66
|
def to_h
|
73
|
-
raise UnpermittedParameterError,
|
67
|
+
raise UnpermittedParameterError, params.keys unless permitted
|
74
68
|
|
75
|
-
|
69
|
+
params.transform_values { |_1| process_nested(_1, :to_h) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def hash
|
73
|
+
@attributes.to_h.hash
|
74
|
+
end
|
75
|
+
|
76
|
+
def with(params:, permitted: false)
|
77
|
+
self.class.new(params, permitted)
|
76
78
|
end
|
77
79
|
|
78
80
|
private
|
79
81
|
|
80
82
|
def nested_attribute(attribute)
|
81
83
|
if attribute.is_a? Hash
|
82
|
-
|
84
|
+
with(params: attribute)
|
83
85
|
elsif attribute.is_a?(Array) && attribute[0].is_a?(Hash)
|
84
|
-
attribute.map { |
|
86
|
+
attribute.map { |it| with(params: it) }
|
85
87
|
else
|
86
88
|
attribute
|
87
89
|
end
|
@@ -96,8 +98,10 @@ module ActiveFunction
|
|
96
98
|
attribute
|
97
99
|
end
|
98
100
|
end
|
101
|
+
end
|
99
102
|
|
100
|
-
|
103
|
+
def params
|
104
|
+
@_params ||= Parameters.new(@request, false)
|
101
105
|
end
|
102
106
|
end
|
103
107
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveFunction
|
4
|
+
class SuperBase
|
5
|
+
attr_reader :action_name, :request, :response
|
6
|
+
|
7
|
+
def initialize(action_name, request, response)
|
8
|
+
@action_name = action_name
|
9
|
+
@request = request
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
process(action_name)
|
15
|
+
|
16
|
+
@response.commit! unless performed?
|
17
|
+
|
18
|
+
@response.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def process(action) ; public_send(action); end
|
22
|
+
|
23
|
+
private def performed? ; @response.committed?; end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Base < SuperBase
|
27
|
+
Error = Class.new(StandardError)
|
28
|
+
|
29
|
+
# @param [String, Symbol] action_name - name of method to call
|
30
|
+
# @param [Hash] request - request params, accessible through `#params` method
|
31
|
+
# @param [Response] response - response object
|
32
|
+
def self.process(action_name, request = {}, response = Response.new)
|
33
|
+
raise ArgumentError, "Action method #{action_name} is not defined" unless method_defined?(action_name)
|
34
|
+
|
35
|
+
new(action_name, request, response).dispatch
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -2,29 +2,26 @@
|
|
2
2
|
|
3
3
|
module ActiveFunction
|
4
4
|
module Functions
|
5
|
-
|
6
|
-
|
5
|
+
module Response
|
6
|
+
ActiveFunction.register_plugin :response, self
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
@headers = headers
|
11
|
-
@body = body
|
12
|
-
@committed = false
|
13
|
-
end
|
8
|
+
class Response < Struct.new(:status, :headers, :body, :committed)
|
9
|
+
def initialize(status: 200, headers: {}, body: nil, committed: false) ; super(status, headers, body, committed); end
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
def to_h
|
12
|
+
{
|
13
|
+
statusCode: status,
|
14
|
+
headers: headers,
|
15
|
+
body: body
|
16
|
+
}
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
def commit!
|
20
|
+
self.committed = true
|
21
|
+
end
|
26
22
|
|
27
|
-
|
23
|
+
alias_method :committed?, :committed
|
24
|
+
end
|
28
25
|
end
|
29
26
|
end
|
30
27
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_function_core"
|
4
|
+
require "active_function/version"
|
5
|
+
require "active_function/base"
|
6
|
+
|
7
|
+
RubyNext::Language.setup_gem_load_path(transpile: true)
|
8
|
+
|
9
|
+
module ActiveFunction
|
10
|
+
class << self
|
11
|
+
# Configure ActiveFunction.
|
12
|
+
#
|
13
|
+
# @param block [Proc]
|
14
|
+
# @return [void]
|
15
|
+
def config(&block)
|
16
|
+
class_eval(&block)
|
17
|
+
@_plugins.freeze
|
18
|
+
self::Base.freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def plugins ; @_plugins ||= {}; end
|
22
|
+
|
23
|
+
# Register plugin.
|
24
|
+
#
|
25
|
+
# @param symbol [Symbol]
|
26
|
+
# @param mod [Module]
|
27
|
+
def register_plugin(symbol, mod)
|
28
|
+
plugins[symbol] = mod
|
29
|
+
end
|
30
|
+
|
31
|
+
# Monkey patch ActiveFunction::Base with provided plugin.
|
32
|
+
#
|
33
|
+
# @param mod [Symbol, Module]
|
34
|
+
# @return [void]
|
35
|
+
def plugin(mod)
|
36
|
+
if mod.is_a? Symbol
|
37
|
+
begin
|
38
|
+
require "active_function/functions/#{mod}"
|
39
|
+
mod = plugins.fetch(mod)
|
40
|
+
rescue LoadError
|
41
|
+
raise ArgumentError, "Unknown plugin #{mod}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
self::Base.include(mod)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
plugin :response
|
50
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveFunction
|
4
|
+
module Functions
|
5
|
+
module Response
|
6
|
+
ActiveFunction.register_plugin :response, self
|
7
|
+
|
8
|
+
class Response < Struct.new(:status, :headers, :body, :committed)
|
9
|
+
def initialize(status: 200, headers: {}, body: nil, committed: false) = super(status, headers, body, committed)
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
{
|
13
|
+
statusCode: status,
|
14
|
+
headers: headers,
|
15
|
+
body: body
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def commit!
|
20
|
+
self.committed = true
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :committed?, :committed
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/active_function/base.rb
CHANGED
@@ -1,20 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveFunction
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
4
|
+
class SuperBase
|
5
|
+
attr_reader :action_name, :request, :response
|
6
|
+
|
7
|
+
def initialize(action_name, request, response)
|
8
|
+
@action_name = action_name
|
9
|
+
@request = request
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
process(action_name)
|
15
|
+
|
16
|
+
@response.commit! unless performed?
|
17
|
+
|
18
|
+
@response.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def process(action) = public_send(action)
|
22
|
+
|
23
|
+
private def performed? = @response.committed?
|
24
|
+
end
|
25
|
+
|
26
|
+
class Base < SuperBase
|
27
|
+
Error = Class.new(StandardError)
|
28
|
+
|
29
|
+
# @param [String, Symbol] action_name - name of method to call
|
30
|
+
# @param [Hash] request - request params, accessible through `#params` method
|
31
|
+
# @param [Response] response - response object
|
32
|
+
def self.process(action_name, request = {}, response = Response.new)
|
33
|
+
raise ArgumentError, "Action method #{action_name} is not defined" unless method_defined?(action_name)
|
34
|
+
|
35
|
+
new(action_name, request, response).dispatch
|
18
36
|
end
|
19
37
|
end
|
20
38
|
end
|
@@ -1,68 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveFunction
|
4
|
-
class MissingCallbackContext < Error
|
5
|
-
MESSAGE_TEMPLATE = "Missing callback context: %s"
|
6
|
-
|
7
|
-
attr_reader :message
|
8
|
-
|
9
|
-
def initialize(context)
|
10
|
-
@message = MESSAGE_TEMPLATE % context
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
4
|
module Functions
|
15
5
|
module Callbacks
|
16
|
-
|
17
|
-
base.extend(ClassMethods)
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def process(*)
|
23
|
-
_run_callbacks :before
|
24
|
-
|
25
|
-
super
|
26
|
-
|
27
|
-
_run_callbacks :after
|
28
|
-
end
|
6
|
+
ActiveFunction.register_plugin :callbacks, self
|
29
7
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
send(callback_method) if _executable?(options)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def _executable?(options)
|
39
|
-
return false if options[:only] && !options[:only].include?(@action_name)
|
40
|
-
return false if options[:if] && !send(options[:if])
|
41
|
-
true
|
42
|
-
end
|
43
|
-
|
44
|
-
module ClassMethods
|
45
|
-
def inherited(subclass)
|
46
|
-
subclass.instance_variable_set(:@__callbacks, @__callbacks)
|
47
|
-
end
|
48
|
-
|
49
|
-
def before_action(method, options = {})
|
50
|
-
set_callback :before, method, options
|
51
|
-
end
|
52
|
-
|
53
|
-
def after_action(method, options = {})
|
54
|
-
set_callback :after, method, options
|
55
|
-
end
|
56
|
-
|
57
|
-
def set_callback(type, method, options = {})
|
58
|
-
callbacks[type][method] = options
|
59
|
-
end
|
60
|
-
|
61
|
-
def callbacks
|
62
|
-
@__callbacks ||= {before: {}, after: {}}
|
63
|
-
|
64
|
-
@__callbacks
|
65
|
-
end
|
8
|
+
def self.included(base)
|
9
|
+
base.include ActiveFunctionCore::Plugins::Hooks
|
10
|
+
base.define_hooks_for :process, name: :action
|
11
|
+
base.set_callback_options only: ->(args, context:) { args.to_set === context.action_name }
|
66
12
|
end
|
67
13
|
end
|
68
14
|
end
|
@@ -3,18 +3,22 @@
|
|
3
3
|
require "json"
|
4
4
|
|
5
5
|
module ActiveFunction
|
6
|
-
|
7
|
-
|
6
|
+
module Functions
|
7
|
+
module Rendering
|
8
|
+
ActiveFunction.register_plugin :rendering, self
|
8
9
|
|
9
|
-
|
10
|
+
Error = Class.new(StandardError)
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
class DoubleRenderError < Error
|
13
|
+
MESSAGE_TEMPLATE = "#render was called multiple times in action: %s"
|
14
|
+
|
15
|
+
attr_reader :message
|
16
|
+
|
17
|
+
def initialize(context)
|
18
|
+
@message = MESSAGE_TEMPLATE % context
|
19
|
+
end
|
20
|
+
end
|
15
21
|
|
16
|
-
module Functions
|
17
|
-
module Rendering
|
18
22
|
DEFAULT_HEADER = {"Content-Type" => "application/json"}.freeze
|
19
23
|
|
20
24
|
def render(status: 200, json: {}, head: {})
|
@@ -2,29 +2,26 @@
|
|
2
2
|
|
3
3
|
module ActiveFunction
|
4
4
|
module Functions
|
5
|
-
|
6
|
-
|
5
|
+
module Response
|
6
|
+
ActiveFunction.register_plugin :response, self
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
@headers = headers
|
11
|
-
@body = body
|
12
|
-
@committed = false
|
13
|
-
end
|
8
|
+
class Response < Struct.new(:status, :headers, :body, :committed)
|
9
|
+
def initialize(status: 200, headers: {}, body: nil, committed: false) = super(status, headers, body, committed)
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
def to_h
|
12
|
+
{
|
13
|
+
statusCode: status,
|
14
|
+
headers:,
|
15
|
+
body:
|
16
|
+
}
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
def commit!
|
20
|
+
self.committed = true
|
21
|
+
end
|
26
22
|
|
27
|
-
|
23
|
+
alias_method :committed?, :committed
|
24
|
+
end
|
28
25
|
end
|
29
26
|
end
|
30
27
|
end
|
@@ -3,52 +3,45 @@
|
|
3
3
|
require "forwardable"
|
4
4
|
|
5
5
|
module ActiveFunction
|
6
|
-
|
7
|
-
|
6
|
+
module Functions
|
7
|
+
module StrongParameters
|
8
|
+
ActiveFunction.register_plugin :strong_parameters, self
|
8
9
|
|
9
|
-
|
10
|
+
Error = Class.new(StandardError)
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
12
|
+
class Parameters < Data.define(:params, :permitted)
|
13
|
+
class ParameterMissingError < Error
|
14
|
+
MESSAGE_TEMPLATE = "Missing parameter: %s"
|
15
15
|
|
16
|
-
|
17
|
-
MESSAGE_TEMPLATE = "Unpermitted parameter: %s"
|
16
|
+
attr_reader :message
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
end
|
18
|
+
def initialize(param)
|
19
|
+
MESSAGE_TEMPLATE % param
|
20
|
+
end
|
21
|
+
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
def params
|
29
|
-
@_params ||= Parameters.new(@request)
|
30
|
-
end
|
23
|
+
class UnpermittedParameterError < Error
|
24
|
+
MESSAGE_TEMPLATE = "Unpermitted parameter: %s"
|
31
25
|
|
32
|
-
|
33
|
-
extend Forwardable
|
34
|
-
def_delegators :@parameters, :each, :map
|
35
|
-
include Enumerable
|
26
|
+
attr_reader :message
|
36
27
|
|
37
|
-
|
38
|
-
|
39
|
-
|
28
|
+
def initialize(param)
|
29
|
+
MESSAGE_TEMPLATE % param
|
30
|
+
end
|
40
31
|
end
|
41
32
|
|
33
|
+
protected :params
|
34
|
+
|
42
35
|
def [](attribute)
|
43
|
-
nested_attribute(
|
36
|
+
nested_attribute(params[attribute])
|
44
37
|
end
|
45
38
|
|
46
39
|
def require(attribute)
|
47
|
-
value = self[attribute]
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
40
|
+
if (value = self[attribute])
|
41
|
+
value
|
42
|
+
else
|
43
|
+
raise ParameterMissingError, attribute
|
44
|
+
end
|
52
45
|
end
|
53
46
|
|
54
47
|
def permit(*attributes)
|
@@ -60,28 +53,37 @@ module ActiveFunction
|
|
60
53
|
pparams[k] = process_nested(self[k], :permit, v)
|
61
54
|
end
|
62
55
|
else
|
63
|
-
next unless
|
56
|
+
next unless params.key?(attribute)
|
64
57
|
|
65
58
|
pparams[attribute] = self[attribute]
|
66
59
|
end
|
67
60
|
end
|
68
61
|
|
69
|
-
|
62
|
+
with(params: pparams, permitted: true)
|
70
63
|
end
|
71
64
|
|
65
|
+
# Redefines RubyNext::Core::Data instance methods
|
72
66
|
def to_h
|
73
|
-
raise UnpermittedParameterError,
|
67
|
+
raise UnpermittedParameterError, params.keys unless permitted
|
74
68
|
|
75
|
-
|
69
|
+
params.transform_values { process_nested(_1, :to_h) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def hash
|
73
|
+
@attributes.to_h.hash
|
74
|
+
end
|
75
|
+
|
76
|
+
def with(params:, permitted: false)
|
77
|
+
self.class.new(params, permitted)
|
76
78
|
end
|
77
79
|
|
78
80
|
private
|
79
81
|
|
80
82
|
def nested_attribute(attribute)
|
81
83
|
if attribute.is_a? Hash
|
82
|
-
|
84
|
+
with(params: attribute)
|
83
85
|
elsif attribute.is_a?(Array) && attribute[0].is_a?(Hash)
|
84
|
-
attribute.map {
|
86
|
+
attribute.map { |it| with(params: it) }
|
85
87
|
else
|
86
88
|
attribute
|
87
89
|
end
|
@@ -96,8 +98,10 @@ module ActiveFunction
|
|
96
98
|
attribute
|
97
99
|
end
|
98
100
|
end
|
101
|
+
end
|
99
102
|
|
100
|
-
|
103
|
+
def params
|
104
|
+
@_params ||= Parameters.new(@request, false)
|
101
105
|
end
|
102
106
|
end
|
103
107
|
end
|
data/lib/active_function.rb
CHANGED
@@ -1,12 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_function_core"
|
4
|
+
require "active_function/version"
|
5
|
+
require "active_function/base"
|
4
6
|
|
5
7
|
RubyNext::Language.setup_gem_load_path(transpile: true)
|
6
8
|
|
7
9
|
module ActiveFunction
|
8
|
-
class
|
10
|
+
class << self
|
11
|
+
# Configure ActiveFunction.
|
12
|
+
#
|
13
|
+
# @param block [Proc]
|
14
|
+
# @return [void]
|
15
|
+
def config(&block)
|
16
|
+
class_eval(&block)
|
17
|
+
@_plugins.freeze
|
18
|
+
self::Base.freeze
|
19
|
+
end
|
9
20
|
|
10
|
-
|
11
|
-
|
21
|
+
def plugins = @_plugins ||= {}
|
22
|
+
|
23
|
+
# Register plugin.
|
24
|
+
#
|
25
|
+
# @param symbol [Symbol]
|
26
|
+
# @param mod [Module]
|
27
|
+
def register_plugin(symbol, mod)
|
28
|
+
plugins[symbol] = mod
|
29
|
+
end
|
30
|
+
|
31
|
+
# Monkey patch ActiveFunction::Base with provided plugin.
|
32
|
+
#
|
33
|
+
# @param mod [Symbol, Module]
|
34
|
+
# @return [void]
|
35
|
+
def plugin(mod)
|
36
|
+
if mod.is_a? Symbol
|
37
|
+
begin
|
38
|
+
require "active_function/functions/#{mod}"
|
39
|
+
mod = plugins.fetch(mod)
|
40
|
+
rescue LoadError
|
41
|
+
raise ArgumentError, "Unknown plugin #{mod}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
self::Base.include(mod)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
plugin :response
|
12
50
|
end
|
metadata
CHANGED
@@ -1,76 +1,39 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activefunction
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nerbyk
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activefunction-core
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.0.1
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 0.0.1
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '13.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '13.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: minitest
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 5.15.0
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
16
|
requirements:
|
52
17
|
- - "~>"
|
53
18
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
|
56
|
-
name: minitest-reporters
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.4.3
|
62
|
-
type: :development
|
19
|
+
version: 0.2.2
|
20
|
+
type: :runtime
|
63
21
|
prerelease: false
|
64
22
|
version_requirements: !ruby/object:Gem::Requirement
|
65
23
|
requirements:
|
66
24
|
- - "~>"
|
67
25
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
69
|
-
description: "\n
|
70
|
-
|
71
|
-
|
72
|
-
with
|
73
|
-
transpiler
|
26
|
+
version: 0.2.2
|
27
|
+
description: "\n ActiveFunction is a collection of gems designed to be used with
|
28
|
+
Function as a Service (FaaS) computing instances. Inspired by aws-sdk v3 gem structure
|
29
|
+
and rails/activesupport.\n\n Features:\n - Ruby Version Compatibility: Implemented
|
30
|
+
with most of Ruby 3.2+ features, with support for Ruby versions >= 2.6 through the
|
31
|
+
RubyNext transpiler (CI'ed).\n - Type Safety: Achieves type safety through the
|
32
|
+
use of RBS and Steep (CI'ed) [Note: disabled due to the presence of Ruby::UnsupportedSyntax
|
33
|
+
errors].\n - Plugins System: Provides a simple Plugin system inspired by Polishing
|
34
|
+
Ruby Programming by Jeremy Evans to load gem plugins and self-defined plugins.\n
|
35
|
+
\ - Gem Collection: Offers a collection of gems designed for use within ActiveFunction
|
36
|
+
or as standalone components.\n "
|
74
37
|
email:
|
75
38
|
- danil.maximov2000@gmail.com
|
76
39
|
executables: []
|
@@ -86,12 +49,13 @@ files:
|
|
86
49
|
- bin/ruby-next
|
87
50
|
- bin/setup
|
88
51
|
- lib/.rbnext/2.7/active_function/functions/strong_parameters.rb
|
89
|
-
- lib/.rbnext/3.0/active_function
|
52
|
+
- lib/.rbnext/3.0/active_function.rb
|
53
|
+
- lib/.rbnext/3.0/active_function/base.rb
|
90
54
|
- lib/.rbnext/3.0/active_function/functions/response.rb
|
55
|
+
- lib/.rbnext/3.1/active_function/functions/response.rb
|
91
56
|
- lib/active_function.rb
|
92
57
|
- lib/active_function/base.rb
|
93
58
|
- lib/active_function/functions/callbacks.rb
|
94
|
-
- lib/active_function/functions/core.rb
|
95
59
|
- lib/active_function/functions/rendering.rb
|
96
60
|
- lib/active_function/functions/response.rb
|
97
61
|
- lib/active_function/functions/strong_parameters.rb
|
@@ -105,7 +69,7 @@ metadata:
|
|
105
69
|
homepage_uri: https://github.com/DanilMaximov/activefunction
|
106
70
|
source_code_uri: https://github.com/DanilMaximov/activefunction
|
107
71
|
changelog_uri: https://github.com/DanilMaximov/activefunction/CHANGELOG.md
|
108
|
-
post_install_message:
|
72
|
+
post_install_message:
|
109
73
|
rdoc_options: []
|
110
74
|
require_paths:
|
111
75
|
- lib
|
@@ -120,9 +84,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
84
|
- !ruby/object:Gem::Version
|
121
85
|
version: '0'
|
122
86
|
requirements: []
|
123
|
-
rubygems_version: 3.4
|
124
|
-
signing_key:
|
87
|
+
rubygems_version: 3.5.4
|
88
|
+
signing_key:
|
125
89
|
specification_version: 4
|
126
|
-
summary:
|
127
|
-
|
90
|
+
summary: Playground gem for Ruby 3.2+ features and more, designed for FaaS computing
|
91
|
+
instances, but mostly used for experiments.
|
128
92
|
test_files: []
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveFunction
|
4
|
-
class MissingRouteMethod < Error
|
5
|
-
MESSAGE_TEMPLATE = "Missing function route: %s"
|
6
|
-
|
7
|
-
attr_reader :message
|
8
|
-
|
9
|
-
def initialize(context)
|
10
|
-
@message = MESSAGE_TEMPLATE % context
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class NotRenderedError < Error
|
15
|
-
MESSAGE_TEMPLATE = "render was not called: %s"
|
16
|
-
|
17
|
-
attr_reader :message
|
18
|
-
|
19
|
-
def initialize(context)
|
20
|
-
@message = MESSAGE_TEMPLATE % context
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
module Functions
|
25
|
-
module Core
|
26
|
-
attr_reader :action_name, :request, :response
|
27
|
-
|
28
|
-
def dispatch(action_name, request, response)
|
29
|
-
@action_name = action_name
|
30
|
-
@request = request
|
31
|
-
@response = response
|
32
|
-
|
33
|
-
raise MissingRouteMethod, @action_name unless respond_to?(action_name)
|
34
|
-
|
35
|
-
process(@action_name)
|
36
|
-
|
37
|
-
raise NotRenderedError, @action_name unless performed?
|
38
|
-
|
39
|
-
@response.to_h
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def process(action) ; public_send(action); end
|
45
|
-
|
46
|
-
def performed? ; @response.committed?; end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveFunction
|
4
|
-
class MissingRouteMethod < Error
|
5
|
-
MESSAGE_TEMPLATE = "Missing function route: %s"
|
6
|
-
|
7
|
-
attr_reader :message
|
8
|
-
|
9
|
-
def initialize(context)
|
10
|
-
@message = MESSAGE_TEMPLATE % context
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class NotRenderedError < Error
|
15
|
-
MESSAGE_TEMPLATE = "render was not called: %s"
|
16
|
-
|
17
|
-
attr_reader :message
|
18
|
-
|
19
|
-
def initialize(context)
|
20
|
-
@message = MESSAGE_TEMPLATE % context
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
module Functions
|
25
|
-
module Core
|
26
|
-
attr_reader :action_name, :request, :response
|
27
|
-
|
28
|
-
def dispatch(action_name, request, response)
|
29
|
-
@action_name = action_name
|
30
|
-
@request = request
|
31
|
-
@response = response
|
32
|
-
|
33
|
-
raise MissingRouteMethod, @action_name unless respond_to?(action_name)
|
34
|
-
|
35
|
-
process(@action_name)
|
36
|
-
|
37
|
-
raise NotRenderedError, @action_name unless performed?
|
38
|
-
|
39
|
-
@response.to_h
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def process(action) = public_send(action)
|
45
|
-
|
46
|
-
def performed? = @response.committed?
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|