activefunction 0.3.5 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 291381221a57e279bfad4dddf88cbeb3695a83e2b4882960b1255304e01a78db
4
- data.tar.gz: 8a2a8d432700fff63717b76198bc032986bc2c6656e7eb9dbf034f78552bcb29
3
+ metadata.gz: 777be794ab7e9eb091472b8f886b0b8da55c338c6c617afb9ee95acc9a946cba
4
+ data.tar.gz: faf687684919e61cd2bb9a2b8217f029945971898ae4469593f166d7ab34508a
5
5
  SHA512:
6
- metadata.gz: 70f3d994a4703b373f91a4df21e0b02a424f0a049a391caf04558ffbcebd80546a3f6683fce5c5a66111336d0827aaa2049ce4b5fbafa64fa7720e13278cf2b7
7
- data.tar.gz: fc0bd5b0c69e64147e7d1729c179c5ef44a931733b9b71b3adec4aa623a7f01f50d70553c7bd58ade6080c2f4e8dab52d2716ebea0b042556296a17d61276e0c
6
+ metadata.gz: 20bb5768a3dcf00582d6f32b99c600c51a9e83c9e5b7ef78ecf81c7e560d63f7d83ea02005d01ef19cd4e2a87a5161f47627e7c305c45d2c18dab6744cd5134a
7
+ data.tar.gz: 7e866bc6a9fe14cd56b8f08985a2a83ce10e57c54560e54e3c954116508638dc0128878d0dc6b2d16f7c8c2c11775ba2681515a58229632bc752e20b76fd79dd
data/CHANGELOG.md CHANGED
@@ -46,4 +46,13 @@
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
+ # [0.4.1] - 2024-01-14
57
+
58
+ - YARD Doc Added
data/README.md CHANGED
@@ -1,146 +1,259 @@
1
1
  # ActiveFunction
2
+ [![Build](https://github.com/DanilMaximov/activefunction/actions/workflows/build.yml/badge.svg)](DanilMaximov/activefunction/actions)
3
+ [![Gem Version](https://badge.fury.io/rb/activefunction.svg)](https://badge.fury.io/rb/activefunction)
4
+ [![RubyDoc](https://img.shields.io/badge/RubyDoc-Documentation-blue.svg)](https://rubydoc.info/gems/activefunction)
2
5
 
3
- rails/action_controller like gem which provides lightweight callbacks, strong parameters & rendering features. It's designed to be used with AWS Lambda functions, but can be also used with any Ruby application.
4
6
 
5
- Implemented with some of ruby 3.x features, but also supports ruby 2.6.x thanks to [RubyNext](https://github.com/ruby-next/ruby-next) transpiler. Type safety achieved by RBS and [Steep](https://github.com/soutaro/steep).
7
+ Playground gem for Ruby 3.2+ features initially designed to be used with FaaS (Function as a Service) computing instances. Inspired by aws-sdk v3 gem structure & rails/activesupport.
6
8
 
9
+ ## Features
7
10
 
11
+ - **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)
12
+ - **Type Safety:** Achieves type safety through the use of RBS and [Steep](https://github.com/soutaro/steep). (CI'ed)
13
+ - Note: disabled due to the presence of Ruby::UnsupportedSyntax errors.
14
+ - **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.
15
+ - **Gem Collection:** Provides a collection of gems designed to be used within ActiveFunction or standalone.
8
16
 
9
- ## A Short Example
17
+ # Gems
10
18
 
11
- Here's a simple example of a function that uses ActiveFunction:
19
+ - [activefunction](/) - Main gem, provides rails/action-controller like API with callbacks, strong parameters and rendering under plugins.
20
+ - [activefunction-core](/gems/activefunction-core/README.md) - Provides RubyNext integration and External Standalone Plugins
21
+ - activefunction-orm - WIP (ORM around AWS PartiQL)
22
+
23
+ ## Quick Start
24
+
25
+ Here's a simple example of a function that uses ActiveFunction(w/o plugins):
12
26
 
13
27
  ```ruby
14
- require 'active_function'
28
+ require "active_function"
15
29
 
16
30
  class AppFunction < ActiveFunction::Base
17
- def index
18
- render json: SomeTable.all
19
- end
31
+ def index
32
+ response_with_error if @request[:data].nil?
33
+
34
+ return if performed?
35
+
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
+
21
45
  ```
22
46
 
23
- Use `#process` method to proceed the request:
47
+ The `#process` method is used to run the function.
48
+
49
+ ```ruby
50
+ AppFunction.process(:index, {data: {id: 1}}) # => {
51
+ # :statusCode => 200,
52
+ # :headers => { },
53
+ # :body => {id: 1}"
54
+ # }
55
+ ```
24
56
 
25
- ```ruby
26
- AppFunction.process(:index) # processes index action of AppFunction instance
57
+ ## Plugins
58
+
59
+ ActiveFunction supports plugins which can be loaded by `ActiveFunction.config` method. Currently, there are 3 plugins available:
60
+ - [:callbacks](#callbacks) - provides `:before_action`, `:after_action` & `:set_callback` DSL with `:if`, `:unless` & `:only` options.
61
+ - [:strong_parameters](#strong-parameters) - provides strong parameters support via `#params` instance method around `@request` object.
62
+ - [:rendering](#rendering) - provides rendering support via `#render` instance method around `@response` object.
63
+
64
+ ## Configuration
65
+
66
+ To configure ActiveFunction with plugins, use the `ActiveFunction.config` method.
67
+
68
+ ```ruby
69
+ # config/initializers/active_function.rb
70
+ require "active_function"
71
+
72
+ ActiveFunction.config do
73
+ plugin :callbacks
74
+ plugin :strong_parameters
75
+ plugin :rendering
76
+ end
27
77
  ```
28
- Also check extended [example](https://github.com/DanilMaximov/activefunction/tree/master/active_function_example)
29
- ## Callbacks
30
- ActiveFunction supports simple callbacks `:before` and `:after` which runs around provided action in `#process`.
31
78
 
32
79
  ```ruby
80
+ # app/functions/app_function.rb
33
81
  class AppFunction < ActiveFunction::Base
34
- before_action :set_user
35
- after_action :log_response
36
-
37
- # some action ...
82
+ before_action :parse_user_data
38
83
 
39
- private
84
+ def index
85
+ render json: @user_data
86
+ end
40
87
 
41
- def set_user
42
- @user = User.first
43
- end
88
+ private def parse_user_data = @user_data = params.require(:data).permit(:id, :name).to_h
89
+ end
90
+
91
+ AppFunction.process(:index, {data: { id: 1, name: 2}}) # => {
92
+ # :statusCode => 200,
93
+ # :headers => {"Content-Type"=>"application/json"},
94
+ # :body=>"{\"id\":1,\"name\":2}"
95
+ # }
96
+ ```
97
+
98
+ [See Plugins Docs](https://rubydoc.info/gems/activefunction/ActiveFunction#plugin-class_method) for more details.
99
+
100
+ ## Callbacks
101
+
102
+ 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`.
103
+
104
+ ```ruby
105
+ require "active_function"
106
+
107
+ ActiveFunction.config do
108
+ plugin :callbacks
109
+ end
44
110
 
45
- def log_response
46
- Logger.info @response
111
+ class AppFunction < ActiveFunction::Base
112
+ before_action :set_user
113
+ after_action :log_response
114
+
115
+ def index
116
+ # some actions ...
47
117
  end
118
+
119
+ private
120
+
121
+ def set_user = @_user ||= User.find(@request[:id])
122
+ def log_response = Logger.info(@response)
48
123
  end
49
124
  ```
50
125
 
51
- Callbacks also can be user with `only: Array[Symbol]` and `if: Symbol` options.
126
+ ### Callbacks options
127
+
128
+ Supports default [ActiveFunctionCore::Plugins::Hooks::Hook::Callback options](https://github.com/DanilMaximov/activefunction/tree/master/gems/activefunction-core#options) `:if => Symbol` & `:unless => Symbol` options.
129
+
130
+ Support custom defined in ActiveFunction::Function::Callbacks `only: Array[Symbol]` option.
52
131
 
53
132
  ```ruby
54
133
  class AppFunction < ActiveFunction::Base
55
134
  before_action :set_user, only: %i[show update destroy], if: :request_valid?
56
-
135
+
57
136
  # some actions ...
58
-
137
+
59
138
  private def request_valid? = true
60
139
  end
61
140
  ```
62
141
 
63
- Callbacks are inheritable so all callbacks calls will be inherited from base class
142
+ ### Defining Custom Callbacks
143
+
144
+ External Plugin `ActiveFunctionCore::Plugins::Hooks` provides `:define_hooks_for` & `:set_callback_options` DSL to define custom callbacks & options.
145
+
64
146
  ```ruby
65
- class BaseFunction < ActiveFunction::Base
66
- before_action :set_current_user
147
+ class MessagingApp < ActiveFunction::Base
148
+ set_callback_options retries: ->(times, context:) { context.retry if context.retries < times }
149
+ define_hooks_for :retry
67
150
 
68
- def set_current_user
69
- @current_user = User.first
70
- end
71
- end
151
+ after_action :retry, if: :failed?, only: %i[send_message], retries: 3
152
+ after_retry :increment_retries
72
153
 
73
- class PostsFunction < BaseFunction
74
- def index
75
- render json: @current_user
154
+ def send_message
155
+ @response.status = 200 if SomeApi.send(@request[:message_content]).success?
156
+ end
157
+
158
+ def retry
159
+ @response.committed = false
160
+
161
+ process
76
162
  end
163
+
164
+ private def increment_retries = @response.body[:tries] += 1
165
+ private def failed? = @response.status != 200
166
+ private def retries = @response.body[:tries] ||= 0
77
167
  end
168
+
169
+ MessagingApp.process(:send_message, { sender_name: "Alice", message_content: "How are you?" })
78
170
  ```
171
+
172
+ [See Callbacks Doc](https://rubydoc.info/gems/activefunction/ActiveFunction/Functions/Callbacks) for more details.
173
+
79
174
  ## Strong Parameters
80
- ActiveFunction supports strong parameters which can be accessed by `#params` instance method. Strong parameters hash can be passed in `#process` as second argument.
81
175
 
82
- ```ruby
83
- PostFunction.process(:index, data: { id: 1, name: "Pupa" })
84
- ```
176
+ ActiveFunction supports strong parameters which can be accessed by `#params` instance method.
177
+
178
+ The `#params` method represents a Ruby 3.2 Data class that allows the manipulation of request parameters. It supports the following methods:
179
+
180
+ - `[]`: Access parameters by key.
181
+ - `permit`: Specify the allowed parameters.
182
+ - `require`: Ensure the presence of a specific parameter.
183
+ - `to_h`: Convert the parameters to a hash.
184
+
185
+ Usage Example:
85
186
 
86
- Simple usage:
87
187
  ```ruby
188
+ require "active_function"
189
+
190
+ ActiveFunction.config do
191
+ plugin :strong_parameters
192
+ end
193
+
88
194
  class PostsFunction < ActiveFunction::Base
89
- def index
90
- render json: permitted_params
91
- end
195
+ def index
196
+ @response.body = permitted_params
197
+ end
92
198
 
93
199
  def permitted_params = params
94
200
  .require(:data)
95
201
  .permit(:id, :name)
96
202
  .to_h
97
- end
203
+ end
204
+
205
+ PostFunction.process(:index, data: {id: 1, name: "Pupa"})
98
206
  ```
207
+
99
208
  Strong params supports nested attributes
100
- ```ruby
209
+ ```ruby
101
210
  params.permit(:id, :name, :address => [:city, :street])
102
211
  ```
103
212
 
213
+ [See StrongParameters Doc](https://rubydoc.info/gems/activefunction/ActiveFunction/Functions/StrongParameters) for more details.
214
+
104
215
  ## Rendering
105
- ActiveFunction supports rendering of JSON. Rendering is obligatory for any function naction and can be done by `#render` method.
106
- ```ruby
107
- class PostsFunction < ActiveFunction::Base
108
- def index
109
- render json: { id: 1, name: "Pupa" }
110
- end
111
- end
112
- ```
113
- default status code is 200, but it can be changed by `:status` option
216
+
217
+ ActiveFunction supports rendering of JSON. The #render method is used for rendering responses. It accepts the following options:
218
+
219
+ - `head`: Set response headers. Defaults to `{ "Content-Type" => "application/json" }`.
220
+ - `json`: Set the response body with a JSON-like object. Defaults to `{}`.
221
+ - `status`: Set the HTTP status code. Defaults to `200`.
222
+
223
+ Additionally, the method automatically commits the response and JSONifies the body.
224
+
225
+ Usage Example:
114
226
  ```ruby
115
- class PostsFunction < ActiveFunction::Base
116
- def index
117
- render json: { id: 1, name: "Pupa" }, status: 201
118
- end
227
+ require "active_function"
228
+
229
+ ActiveFunction.config do
230
+ plugin :rendering
119
231
  end
120
- ```
121
- Headers can be passed by `:headers` option. Default headers are `{"Content-Type" => "application/json"}`.
122
- ```ruby
232
+
123
233
  class PostsFunction < ActiveFunction::Base
124
- def index
125
- render json: { id: 1, name: "Pupa" }, headers: { "X-Request-Id" => "123" }
126
- end
234
+ def index
235
+ render json: {id: 1, name: "Pupa"}, status: 200, head: {"Some-Header" => "Some-Value"}
236
+ end
127
237
  end
238
+
239
+ PostFunction.process(:index) # => { :statusCode=>200, :headers=> {"Content-Type"=>"application/json", "Some-Header" => "Some-Value"}, :body=>"{\"id\":1,\"name\":\"Pupa\"}"}
128
240
  ```
129
241
 
242
+ [See Rendering Doc](https://rubydoc.info/gems/activefunction/ActiveFunction/Functions/Rendering) for more details.
130
243
 
131
244
  ## Installation
132
245
 
133
246
  Add this line to your application's Gemfile:
134
247
 
135
248
  ```ruby
136
- gem 'activefunction', git: "https://github.com/DanilMaximov/activefunction.git"
249
+ gem "activefunction"
137
250
  ```
138
251
 
139
252
  ## Development
140
253
 
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.
254
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake test:all` to run the tests and `bin/rake steep` to run type checker.
142
255
 
143
- 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).
256
+ To install this gem onto your local machine, run `bundle exec rake install`.
144
257
 
145
258
  ## Contributing
146
259
 
@@ -3,54 +3,80 @@
3
3
  require "forwardable"
4
4
 
5
5
  module ActiveFunction
6
- class ParameterMissingError < Error
7
- MESSAGE_TEMPLATE = "Missing parameter: %s"
8
-
9
- attr_reader :message
10
-
11
- def initialize(param)
12
- MESSAGE_TEMPLATE % param
13
- end
14
- end
6
+ module Functions
7
+ # Allows manipulations with {ActiveFunction::SuperBase#request} via {params} instance method and {Parameters} object.
8
+ #
9
+ # @example
10
+ # require "active_function"
11
+ #
12
+ # ActiveFunction.config do
13
+ # plugin :strong_parameters
14
+ # end
15
+ #
16
+ # class PostsFunction < ActiveFunction::Base
17
+ # def index
18
+ # @response.body = permitted_params
19
+ # end
20
+ #
21
+ # def permitted_params
22
+ # params.require(:data).permit(:id, :name).to_h
23
+ # end
24
+ # end
25
+ #
26
+ # PostsFunction.process(:index, data: { id: 1, name: "Pupa" })
27
+ module StrongParameters
28
+ ActiveFunction.register_plugin :strong_parameters, self
15
29
 
16
- class UnpermittedParameterError < Error
17
- MESSAGE_TEMPLATE = "Unpermitted parameter: %s"
30
+ Error = Class.new(StandardError)
31
+ # The Parameters class encapsulates the parameter handling logic.
32
+ class Parameters < Data.define(:params, :permitted)
33
+ class ParameterMissingError < Error
34
+ MESSAGE_TEMPLATE = "Missing parameter: %s"
18
35
 
19
- attr_reader :message
36
+ attr_reader :message
20
37
 
21
- def initialize(param)
22
- MESSAGE_TEMPLATE % param
23
- end
24
- end
38
+ def initialize(param)
39
+ MESSAGE_TEMPLATE % param
40
+ end
41
+ end
25
42
 
26
- module Functions
27
- module StrongParameters
28
- def params
29
- @_params ||= Parameters.new(@request)
30
- end
43
+ class UnpermittedParameterError < Error
44
+ MESSAGE_TEMPLATE = "Unpermitted parameter: %s"
31
45
 
32
- class Parameters
33
- extend Forwardable
34
- def_delegators :@parameters, :each, :map
35
- include Enumerable
46
+ attr_reader :message
36
47
 
37
- def initialize(parameters, permitted: false)
38
- @parameters = parameters
39
- @permitted = permitted
48
+ def initialize(param)
49
+ MESSAGE_TEMPLATE % param
50
+ end
40
51
  end
41
52
 
53
+ protected :params
54
+
55
+ # Allows access to parameters by key.
56
+ #
57
+ # @param attribute [Symbol] The key of the parameter.
58
+ # @return [Parameters, Object] The value of the parameter.
42
59
  def [](attribute)
43
- nested_attribute(parameters[attribute])
60
+ nested_attribute(params[attribute])
44
61
  end
45
62
 
63
+ # Requires the presence of a specific parameter.
64
+ #
65
+ # @param attribute [Symbol] The key of the required parameter.
66
+ # @return [Parameters, Object] The value of the required parameter.
67
+ # @raise [ParameterMissingError] if the required parameter is missing.
46
68
  def require(attribute)
47
- value = self[attribute]
48
-
49
- raise ParameterMissingError, attribute if value.nil?
50
-
51
- value
69
+ if (value = self[attribute])
70
+ value
71
+ else
72
+ raise ParameterMissingError, attribute
73
+ end
52
74
  end
53
75
 
76
+ # Specifies the allowed parameters.
77
+ #
78
+ # @param attributes [Array<Symbol, Hash<Symbol, Array<Symbol>>>] The attributes to permit.
79
+ # @return [Parameters] A new instance with permitted parameters.
54
80
  def permit(*attributes)
55
81
  pparams = {}
56
82
 
@@ -60,28 +86,40 @@ module ActiveFunction
60
86
  pparams[k] = process_nested(self[k], :permit, v)
61
87
  end
62
88
  else
63
- next unless parameters.key?(attribute)
89
+ next unless params.key?(attribute)
64
90
 
65
91
  pparams[attribute] = self[attribute]
66
92
  end
67
93
  end
68
94
 
69
- Parameters.new(pparams, permitted: true)
95
+ with(params: pparams, permitted: true)
70
96
  end
71
97
 
98
+ # Converts parameters to a hash.
99
+ #
100
+ # @return [Hash] The hash representation of the parameters.
101
+ # @raise [UnpermittedParameterError] if any parameters are unpermitted.
72
102
  def to_h
73
- raise UnpermittedParameterError, parameters.keys unless @permitted
103
+ raise UnpermittedParameterError, params.keys unless permitted
74
104
 
75
- parameters.transform_values { |_1| process_nested(_1, :to_h) }
105
+ params.transform_values { |_1| process_nested(_1, :to_h) }
106
+ end
107
+
108
+ def hash
109
+ @attributes.to_h.hash
110
+ end
111
+
112
+ def with(params:, permitted: false)
113
+ self.class.new(params, permitted)
76
114
  end
77
115
 
78
116
  private
79
117
 
80
118
  def nested_attribute(attribute)
81
119
  if attribute.is_a? Hash
82
- Parameters.new(attribute)
120
+ with(params: attribute)
83
121
  elsif attribute.is_a?(Array) && attribute[0].is_a?(Hash)
84
- attribute.map { |_1| Parameters.new(_1) }
122
+ attribute.map { |it| with(params: it) }
85
123
  else
86
124
  attribute
87
125
  end
@@ -96,8 +134,13 @@ module ActiveFunction
96
134
  attribute
97
135
  end
98
136
  end
137
+ end
99
138
 
100
- attr_reader :parameters
139
+ # Return params object with {ActiveFunction::SuperBase#request}.
140
+ #
141
+ # @return [Parameters] instance of {Parameters} class.
142
+ def params
143
+ @_params ||= Parameters.new(@request, false)
101
144
  end
102
145
  end
103
146
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFunction
4
+ # Abstract base class with request processing logic.
5
+ class SuperBase
6
+ attr_reader :action_name, :request, :response
7
+
8
+ def initialize(action_name, request, response)
9
+ @action_name = action_name
10
+ @request = request
11
+ @response = response
12
+ end
13
+
14
+ # Executes specified @action_name instance method and returns Hash'ed response object
15
+ def dispatch
16
+ process(action_name)
17
+
18
+ @response.commit! unless performed?
19
+
20
+ @response.to_h
21
+ end
22
+
23
+ def process(action) ; public_send(action); end
24
+
25
+ private def performed? ; @response.committed?; end
26
+ end
27
+
28
+ # The main base class for defining functions using the ActiveFunction framework.
29
+ # Public methods of this class are considered as actions and be proceeded on {ActiveFunction::Base.process} call.
30
+ #
31
+ # @example
32
+ # class MyFunction < ActiveFunction::Base
33
+ # def index
34
+ # if user = User.find(@request.dig(:data, :user, :id))
35
+ # @response.body = user.to_h
36
+ # else
37
+ # @response.status = 404
38
+ # end
39
+ # end
40
+ # end
41
+ class Base < SuperBase
42
+ Error = Class.new(StandardError)
43
+
44
+ # Processes specified action and returns Hash'ed {ActiveFunction::Functions::Response::Response} object.
45
+ #
46
+ # @example
47
+ # MyFunction.process :index, { data: { user: { id: 1 } } } # => { statusCode: 200, body: { id: 1, name: "Pupa" }, headers: {} }
48
+ #
49
+ # @param [String, Symbol] action_name - name of method to call
50
+ # @param [Hash] request - request parameters.
51
+ # @param [Response] response - Functions::Response response object.
52
+ def self.process(action_name, request = {}, response = Response.new)
53
+ raise ArgumentError, "Action method #{action_name} is not defined" unless method_defined?(action_name)
54
+
55
+ new(action_name, request, response).dispatch
56
+ end
57
+ end
58
+ end
@@ -2,29 +2,48 @@
2
2
 
3
3
  module ActiveFunction
4
4
  module Functions
5
- class Response
6
- attr_accessor :status, :headers, :body
5
+ # The only required plugin for {ActiveFunction::Base} to work.
6
+ # Provides a simple {Response} object to manage response details.
7
+ #
8
+ # @example
9
+ # response = Response.new.tap do |r|
10
+ # r.body = "Hello World!"
11
+ # r.headers = {"Content-Type" => "text/plain"}
12
+ # r.commit!
13
+ # end
14
+ #
15
+ # response.performed? # => true
16
+ # response.to_h # => { statusCode: 200, headers: { "Content-Type" => "text/plain" }, body: "Hello World!" }
17
+ module Response
18
+ ActiveFunction.register_plugin :response, self
7
19
 
8
- def initialize(status: 200, headers: {}, body: nil)
9
- @status = status
10
- @headers = headers
11
- @body = body
12
- @committed = false
13
- end
20
+ class Response < Struct.new(:status, :headers, :body, :committed)
21
+ # Initializes a new Response instance with default values.
22
+ #
23
+ # @param status [Integer] HTTP status code.
24
+ # @param headers [Hash] HTTP headers.
25
+ # @param body [Object] Response body.
26
+ # @param committed [Boolean] Indicates whether the response has been committed (default is false).
27
+ def initialize(status: 200, headers: {}, body: nil, committed: false) ; super(status, headers, body, committed); end
14
28
 
15
- def to_h
16
- {
17
- statusCode: status,
18
- headers: headers,
19
- body: body
20
- }
21
- end
29
+ # Converts the Response instance to a hash for JSON serialization.
30
+ #
31
+ # @return [Hash{statusCode: Integer, headers: Hash, body: Object}]
32
+ def to_h
33
+ {
34
+ statusCode: status,
35
+ headers: headers,
36
+ body: body
37
+ }
38
+ end
22
39
 
23
- def commit!
24
- @committed = true
25
- end
40
+ # Marks the response as committed.
41
+ def commit!
42
+ self.committed = true
43
+ end
26
44
 
27
- def committed? ; @committed; end
45
+ alias_method :committed?, :committed
46
+ end
28
47
  end
29
48
  end
30
49
  end