ActiveFunction
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.
Features
- Ruby Version Compatibility: Implemented with most of ruby 3.2+ features, BUT supports Ruby versions >= 2.6 through the use of the RubyNext transpiler. (CI'ed)
-
Type Safety: Achieves type safety through the use of RBS and Steep. (CI'ed)
- Note: disabled due to the presence of Ruby::UnsupportedSyntax errors.
- Plugins System: Provides a simple Plugin system (inspired by Polishing Ruby Programming by Jeremy Evans) to load gem plugins as well as self-defined plugins.
- Gem Collection: Provides a collection of gems designed to be used within ActiveFunction or standalone.
Gems
- activefunction - Main gem, provides rails/action-controller like API with callbacks, strong parameters and rendering under plugins.
- activefunction-core - Provides RubyNext integration and External Standalone Plugins
- activefunction-orm - WIP (ORM around AWS PartiQL)
Quick Start
Here's a simple example of a function that uses ActiveFunction(w/o plugins):
require "active_function"
class AppFunction < ActiveFunction::Base
def index
response_with_error if @request[:data].nil?
return if performed?
@response.body = @request[:data]
end
private def response_with_error
@response.status = 400
@response.commit!
end
end
The #process
method is used to run the function.
AppFunction.process(:index, {data: {id: 1}}) # => {
# :statusCode => 200,
# :headers => { },
# :body => {id: 1}"
# }
Plugins
ActiveFunction supports plugins which can be loaded by ActiveFunction.config
method. Currently, there are 3 plugins available:
- :callbacks - provides
:before_action
,:after_action
&:set_callback
DSL with:if
,:unless
&:only
options. - :strong_parameters - provides strong parameters support via
#params
instance method around@request
object. - :rendering - provides rendering support via
#render
instance method around@response
object.
Configuration
To configure ActiveFunction with plugins, use the ActiveFunction.config
method.
# config/initializers/active_function.rb
require "active_function"
ActiveFunction.config do
plugin :callbacks
plugin :strong_parameters
plugin :rendering
end
# app/functions/app_function.rb
class AppFunction < ActiveFunction::Base
before_action :parse_user_data
def index
render json: @user_data
end
private def parse_user_data = @user_data = params.require(:data).permit(:id, :name).to_h
end
AppFunction.process(:index, {data: { id: 1, name: 2}}) # => {
# :statusCode => 200,
# :headers => {"Content-Type"=>"application/json"},
# :body=>"{\"id\":1,\"name\":2}"
# }
See Plugins Docs for more details.
Callbacks
Simple callbacks engined by ActiveFunctionCore::Plugins::Hooks external plugin and provides :before_action
and :after_action
which runs around provided action in #process
.
require "active_function"
ActiveFunction.config do
plugin :callbacks
end
class AppFunction < ActiveFunction::Base
before_action :set_user
after_action :log_response
def index
# some actions ...
end
private
def set_user = @_user ||= User.find(@request[:id])
def log_response = Logger.info(@response)
end
Callbacks options
Supports default ActiveFunctionCore::Plugins::Hooks::Hook::Callback options :if => Symbol
& :unless => Symbol
options.
Support custom defined in ActiveFunction::Function::Callbacks only: Array[Symbol]
option.
class AppFunction < ActiveFunction::Base
before_action :set_user, only: %i[show update destroy], if: :request_valid?
# some actions ...
private def request_valid? = true
end
Defining Custom Callbacks
External Plugin ActiveFunctionCore::Plugins::Hooks
provides :define_hooks_for
& :set_callback_options
DSL to define custom callbacks & options.
class MessagingApp < ActiveFunction::Base
set_callback_options retries: ->(times, context:) { context.retry if context.retries < times }
define_hooks_for :retry
after_action :retry, if: :failed?, only: %i[send_message], retries: 3
after_retry :increment_retries
def send_message
@response.status = 200 if SomeApi.send(@request[:message_content]).success?
end
def retry
@response.committed = false
process
end
private def increment_retries = @response.body[:tries] += 1
private def failed? = @response.status != 200
private def retries = @response.body[:tries] ||= 0
end
MessagingApp.process(:send_message, { sender_name: "Alice", message_content: "How are you?" })
See Callbacks Doc for more details.
Strong Parameters
ActiveFunction supports strong parameters which can be accessed by #params
instance method.
The #params
method represents a Ruby 3.2 Data class that allows the manipulation of request parameters. It supports the following methods:
-
[]
: Access parameters by key. -
permit
: Specify the allowed parameters. -
require
: Ensure the presence of a specific parameter. -
to_h
: Convert the parameters to a hash.
Usage Example:
require "active_function"
ActiveFunction.config do
plugin :strong_parameters
end
class PostsFunction < ActiveFunction::Base
def index
@response.body = permitted_params
end
def permitted_params = params
.require(:data)
.permit(:id, :name)
.to_h
end
PostFunction.process(:index, data: {id: 1, name: "Pupa"})
Strong params supports nested attributes
params.permit(:id, :name, :address => [:city, :street])
See StrongParameters Doc for more details.
Rendering
ActiveFunction supports rendering of JSON. The #render method is used for rendering responses. It accepts the following options:
-
head
: Set response headers. Defaults to{ "Content-Type" => "application/json" }
. -
json
: Set the response body with a JSON-like object. Defaults to{}
. -
status
: Set the HTTP status code. Defaults to200
.
Additionally, the method automatically commits the response and JSONifies the body.
Usage Example:
require "active_function"
ActiveFunction.config do
plugin :rendering
end
class PostsFunction < ActiveFunction::Base
def index
render json: {id: 1, name: "Pupa"}, status: 200, head: {"Some-Header" => "Some-Value"}
end
end
PostFunction.process(:index) # => { :statusCode=>200, :headers=> {"Content-Type"=>"application/json", "Some-Header" => "Some-Value"}, :body=>"{\"id\":1,\"name\":\"Pupa\"}"}
See Rendering Doc for more details.
Installation
Add this line to your application's Gemfile:
gem "activefunction"
Development
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.
To install this gem onto your local machine, run bundle exec rake install
.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/DanilMaximov/activefunction. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the ActiveFunction::Functions project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.