shark-on-lambda
shark-on-lambda provides a lightweight framework to write Ruby on AWS Lambda
whilst using a familiar approach to web services known from e. g. Ruby on Rails.
History
For a long time, "going serverless" on AWS Lambda was a world Ruby developers only could explore using methods such as packing their own Ruby binaries, going the JRuby way, running on the Java VM, or using Ruby On Jets.
When AWS went public with the Ruby 2.5 runtime for AWS Lambda at the end of 2018, that changed for Ruby developers and suddenly, a whole new world opened.
Since it is possible to run any Rack-based application on AWS Lambda, you can even run your own Rails application there, if you want to.
However, if you prefer a more lightweight solution, shark-on-lambda may be
of interest to you, as it offers you a similar approach to things whilst
maintaining a smaller memory footprint.
Changelog
Have a look at the actual changelog.
Upgrading?
If you are upgrading from a previous version of shark-on-lambda, these
upgrade guides might be of some help to you.
Installation
Add this line to your application's gems.rb:
gem 'shark-on-lambda'And then execute:
$ bundle
Or install it yourself:
$ gem install shark-on-lambda
Handlers
Handlers are the entry points for Lambda function invocation, e. g. when they are triggered by HTTP requests on the API Gateway. They also are responsible for responding with a well-formed response to the caller.
class MyHandler < SharkOnLambda::ApiGatewayHandler
endBy inheriting from SharkOnLambda::ApiGatewayHandler, your own handler
class is indirectly tied to your controller class by convention: It assumes
the controller name is MyController and it will dispatch events to that
controller.
If you however bring your own class with a different name, you can configure your handler to use that controller instead:
class MyHandler < SharkOnLambda::BaseHandler
self.controller_class = AnotherController
endAll controller methods are going to be automagically mapped to class methods on the handler class.
class MyController < SharkOnLambda::BaseController
def index
end
def show
end
end
class MyHandler < SharkOnLambda::ApiGatewayHandler
endMyHandler will respond to .index and .show class methods that accept the
event and context objects from the API Gateway. Those are passed to
MyController and eventually, the controller method that corresponds to the
handler class method is called.
Controllers
Controllers are similar to Rails controllers: You have access to params,
request, and response objects that contain informatoni retrieved from the
AWS Lambda event object.
"Basic" controllers
You also have access to the render and redirect_to.
class MyController < SharkOnLambda::BaseController
def index
# Make the API Gateway respond with a 201 response saying "Hello, World!"
# in the response body.
#
# The default status code for `render` is 200.
render 'Hello, World!', status: 201
end
def show
# Does what you think it does.
#
# The default status code for `redirect_to` is 302.
redirect_to 'https://github.com', status: 307
end
endbefore_action, around_action, and after_action filters also are available,
as well as rescue_from.
JSON API-compliant controllers
If you inherit your controller from SharkOnLambda::JsonapiController,
render and redirect_to will create JSON API-compliant responses.
You however must have a serialiser for the objects you want to render. Otherwise, rendering will fail and you will receive an Internal Server Error instead.
JSON API fields and include query string parameters are automagically
being parsed and used for rendering automagically by the JSON API renderer.
JSON API serialisers
We use jsonapi-serializable (and jsonapi-rb in general) for our
JSON API compatibility. Therefore, we expect the serialisers to be inherited
from ::JSONAPI::Serializable::Resource (or ::JSONAPI::Serializable::Error).
class SomethingSerializer < JSONAPI::Serializable::Resource
type :somethings
attributes :foo, :bar, :baz
endSerialiser lookup
Each object that is to be serialised requires a serialiser class that knows how to serialise it. We also implemented a convention over configuration approach here to determine which serialiser class to use:
-
If the object is an instance of
YourClassandYourClassSerializeris defined,YourClassSerializeris used as the serialiser class. -
If the object is an instance of
YourClassandYourClassSerializeris not defined, check whether 1) applies for any of the ancestors ofYourClass.
Example
If YourClass has YourBaseClass, Object, and BasicObject as ancestor
classes, the first existing one of YourClassSerializer,
YourBaseClassSerializer, ObjectSerializer, and BasicObjectSerializer
(in that order) is used. If none of those exist, serialisation will fail with
an Internal Server Error.
Configuration
You can "initialise" SharkOnLambda using its .initialize! class method.
SharkOnLambda.initialize! yields to a block with the config and secrets
objects where you can access and add to those two objects.
SharkOnLambda.initialize! do |config, secrets|
# Do things here.
endCalling SharkOnLambda.initialize! does these things (in order):
- Process the block passed to
.initialize!. - Load
config/settings.ymlandconfig/settings.local.ymlinto theconfigobject. - Load
config/database.ymlandconfig/database.local.ymlintoconfig.database. - Load
config/secrets.ymlandsecrets/secrets.local.ymlinto thesecretsobject. - Load all
config/initializers/*.rbfiles.
If SharkOnLambda.config.stage was set inside the block passed to
.initialize!, configurations and secrets for that stage will be merged into
the default set (the default node in the YAML files) of configuration and
secrets, overwriting values where applicable.
Test helpers
By including SharkOnLambda::RSpec::Helpers in your RSpec test suite, you can
use delete, get, patch, post, and put methods, which will return
a Rack::MockResponse object. You can also access that response object in your
test examples by calling response, but only after you've called either of the
aforementioned methods. Otherwise, an exception will be raised.
You can include the test helpers like this in your spec/spec_helper.rb.
RSpec.configure do |config|
config.include SharkOnLambda::RSpec::Helpers
endJSON API helpers
By including SharkOnLambda::RSpec::JsonapiHelpers, you gain all the goodies
from SharkOnLambda::RSpec::Helpers and access to jsonapi_data and
jsonapi_errors methods, which contain the data and errors keys of the
parsed response body respectively. In addition to that, there is
jsonapi_attributes, which returns the attributes key from jsonapi_data.
Rack compatibility
As SharkOnLambda.application is a Rack-compatible application, treating it
as such and using existing Rack middleware is straightforward.
Using Rack middleware
The middleware stack can be found at SharkOnLambda.config.middleware. Adding
middleware to your stack can be either done by calling #use:
SharkOnLambda.config.middleware.use Your::MiddlewareYou can also just set up your middleware stack during your
SharkOnLambda.initialize! call:
SharkOnLambda.initialize! do |config, secrets|
config.middleware.use Your::Middleware
endDevelopment
Clone this repository and change away. Then, once you are done, please submit a pull request at https://github.com/Skudo/shark-on-lambda/pulls.
However, please make sure the tests (bundle exec rake spec) and rubocop
(bundle exec rubocop) pass before submitting a pull request. Pull requests
that do not pass both on the CI system will not be merged. On the same note,
untested code will not be merged, either.