Add this line to your application's Gemfile:
gem 'salestation'
And then execute:
$ bundle
First include Salestation::Web
. This will provide a method called process
to execute a request and Responses module for return codes.
class Webapp < Sinatra::Base
include Salestation::Web.new
end
Create Salestation application:
def app
@_app ||= Salestation::App.new(env: ENVIRONMENT)
end
Define a route
post '/hello/:name' do
process(
HelloUser.call(app.create_request(
name: params['name']
)).map(Responses.to_ok)
)
end
Define chain
class HelloUser
def self.call(request)
request >> upcase >> format
end
def self.upcase
-> (request) do
input.with_input(name: input.fetch(:name).upcase)
end
end
def self.format
-> (request) do
name = request.input.fetch(:name)
Deterministic::Result::Success(message: "Hello #{name}")
end
end
end
Salestation allows and recommends you to define your own custom errors. This is useful when your app has error classes that are not general enough for the salestation library.
include Salestation::Web.new(errors: {
CustomError => -> (error) { CustomResponse.new(error) }
})
If you need to specify additional error fields you can use from
method.
from
accepts base error on which the rest of the response is built.
Base error must be a hash or implement to_h
method.
Example:
App::Errors::Conflict.from({details: 'details'}, message: 'message', debug_message: 'debug_message')
Response:
{
"details": "details",
"message": "message",
"debug_message": "debug_message"
}
Salestation provides extractors to fetch parameters from the request and pass them to the chain.
Available extractors are BodyParamExtractor
, QueryParamExtractor
, ConstantInput
, HeadersExtractor
.
Multiple extractors can be merged together. If two or more extractors use the same key, the value will be from the last extractor in the merge chain.
coercions
can optionally be provided to BodyParamExtractor
and QueryParamExtractor
. These can be used to transform the values of the extracted parameters.
ParamExtractor, which is also used by BodyParamExtractor
and QueryParamExtractor
,
extracts only the content of such root level keys that are specified when creating the
extractor instance. All other root level keys are discarded. Everything inside the
whitelisted root level keys is automatically whitelisted.
Define a route
include Salestation::Web.new
include Salestation::Web::Extractors
APP = Salestation::App.new(env: {})
def create_app_request
-> (input) { App.create_request(input) }
end
post '/hello/:name' do |name|
extractor = BodyParamExtractor[:age]
.merge(ConstantInput[name: name])
.merge(HeadersExtractor[
'accept' => :accept,
'content-type' => :content_type,
'x-my-value' => :my_value
])
.coerce(age: ->(age) { age.to_s })
validate_input = Salestation::Web::InputValidator[
accept: Salestation::Web::InputValidators::AcceptHeader['application/json', 'application/xml'],
content_type: Salestation::Web::InputValidators::ContentTypeHeader['application/json'],
my_value: lambda do |my_value|
if my_value == 'foo'
Deterministic::Result::Success(nil)
else
Deterministic::Result::Failure('invalid value')
end
end
]
response = extractor.call(request)
.map(validate_input)
.map(create_app_request)
.map(HelloUser)
.map(Responses.to_ok)
process(response)
end
Salestation provides a rack logging middleware which can be used to log structured objects.
class Webapp < Sinatra::Base
# ...
use Salestation::Web::RequestLogger, my_logger
end
Salestation provides a StatsD middleware which can be used record request
execution time. A timing
call with elapsed seconds is made to the provided
StatsD instance with path
, method
, status
and status_class
tags.
class Webapp < Sinatra::Base
# ...
use Salestation::Web::StatsdMiddleware,
Statsd.new(host, port),
metric: 'my_service.response.time'
end
You can configure per-request tags by defining salestation.statsd.tags
in sinatra env
:
def my_handler(env)
env['salestation.statsd.tags'] = ['foo:bar']
# ...
end
To use Salestation RSpec matchers you first need to include them:
require 'salestation/rspec'
RSpec.configure do |config|
config.include Salestation::RSpec::Matchers
end
After that it's possible to match against error results like this:
expect(result).to be_failure
.with_conflict
.containing(message: 'Oh noes')
See lib/salestation/rspec.rb for more examples.
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. A new version is created when a change is merged into the master branch that changes the version number in salestation.gemspec
. A Github Action will create a tag for the version and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/salestation.
The gem is available as open source under the terms of the MIT License.