activefunction 0.3.5 → 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 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