Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
  bin
  lib
  Gemfile
  README.md
  uoy-faculty-sinatra.gemspec
Size: Mime:
  README.md

Sinatra Helpers Gem

Installation

Add these lines to your application's Gemfile:

source 'https://gem.fury.io/universityofyork/' do
  gem 'uoy-faculty-sinatra', '~> 0.1', require: 'sinatra'
end

And then execute:

$ bundle

Or install it yourself as:

$ gem install uoy-faculty-sinatra

Usage

Functions for use in configure block

  • initialise_db
    • Sets up the database connection in a standard way
    • Can be passed additional configuration options if required
  • no_master_web_library_in_production!
    • Will raise an exception if we are trying to use the master web library in production
configure do
  no_master_web_library_in_production!
  DB = initialise_db
end

Helpers

  • current_url?(path)
    • Checks if the given path is the current page being show
    • Useful in the navbar (e.g. current_url? '/staff')
  • full_url(relative_url)
    • Gets the full path of a relative URL with the hostname
    • e.g. on a local server, rel('/login') => https://app.domain/app/app_name/login
  • path_with_web_root
    • Returns the full path of the current page including the web root
    • Given to BootstrapNavbar so the current page can be highlighted correctly
  • rel(relative_url)
    • Gets the full path of a relative URL
    • e.g. on a local server, rel('/login') => /app/app_name/login
  • web_library(resource)
    • Generates a URL to the given resource in the web library
    • Will use S3 bucket if not local; otherwise, the /web-library subdirectory
    • e.g. web_library('css/app.css') => /path/to/library/css/app.css
  • web_root
    • Returns the root path of the app, without a trailing slash
    • e.g. '' in AWS; /app/app_name on a local server

Auth

For local development:

configure do
  register Sinatra::BasicPasswordlessAuth
end

We can register different Auth schemes depending on environment (e.g. Cognito auth in AWS)

HTTP Status

Define rescuable HTTP statuses.

get '/path/ do
  raise http_status(403) unless user_can_view_this_page?
end

We can define custom error pages in app.rb:

error_handler HTTPStatus::AnyStatus, :error
error_handler HTTPStatus::Forbidden, :'error/403'
error_handler HTTPStatus::NotFound, :'error/404'
error(Sinatra::NotFound) { erb :'error/404' }

Parameter Validation

validator :sample_form do |validator|
  validator.param :name, String, required: true
  validator.param :day, Date, required: true, message: 'Must provide a Date'
  validator.one_of :a, :b
end

post '/sample-form', validate: :sample_form do
  # ...your logic here...
end

param, one_of and any_of are valid methods on the validator object; see sinatra/param for details of these methods.

If the validator fails, it will redirect back to the form and flash the form params to the session. To make use of these params, you should use the form_value helper. There is also a form_error? helper for checking if a specific field had an error, and form_error to retrieve the error for the given field.

<input type="text" class="form-control <%= 'is-invalid' if form_error? :name %>" 
       name="name" id="name" required 
       value="<%= form_value :name %>">
<div class="invalid-feedback">
  Please provide an event name.
</div>

If your form should already have values populated, you can use the form_values helper in your route. This sets the default values for fields, which will be overridden by flashed values if they exist.

get '/sample-form' do
  form_values({ name: 'Foo' })
  erb :sample_form
end

The validator definition will be executed in the same scope as the route, so will have access to params, session etc.

Additional options

Multiple validators can be passed as an array:

post '/sample-form', validate: [:validator1, :validator2] do
  # ...your logic here...
end

raise_status can be defined on a validator if you wish to raise a certain status rather than redirect back to the form.

Each parameter check can take a block with extra checks to be run after the validation.

The following check is checking a :team_id parameter in a URL; if it is invalid, we raise a 403 forbidden error.

validator :team_id do |validator|
  validator.raise_status = 403
  validator.param :team_id, Integer, required: true, blank: false do
    @team = DB[:teams].where(team_id: params[:team_id]).first!
  rescue Sequel::NoMatchingRow
    raise http_status(403)
  end
end

Validators can take parameters, which can be passed by wrapping the validator identifier with valdr:

validator :sample_form do |validator, parameter|
  # ...
end

post '/sample-form', validate: valdr(:sample_form, parameter) do
  # ...your logic here...
end

Flash Messages

Pass messages to the next page through the session. This extends the standard sinatra-flash implementation and uses a single key for all flash messages, so we can define multiple messages with the same type.

Pass a message and a message type (a bootstrap contextual class, e.g. :success or :danger; defaults to :info) in your route:

flash_message 'The default type is "info".'
flash_message 'Added something successfully.', type: :success
flash_message 'We want to warn you about something.', type: :warning

And add the template to your view, where appropriate:

<%== erb :flash_messages %>

You can also pass an array of messages for a single type, which will be output in a single alert, one per line:

flash_message ['Added A.', 'Added B.']

Development

Versioning

Your Gem's version is picked up automatically from lib/sinatra.rb. When any changes are pushed to master, after the normal CI tasks the pipeline will push to gemfury automatically. The usual workflow is:

  • For minor changes, update VERSION and make the change in a single commit

  • For anything else, create a branch and set VERSION to the version you're aiming to release for. Make the changes; when the branch is merged, the gem will be uploaded.

Note that gemfury will never overwrite an existing gem version, even if the old one is yanked!

Running tests

Tests can be run via rake: bundle exec rake spec - this doesn't run performance tests; they can be run separately via bundle exec rake perf.

You can also run rspec normally e.g. bundle exec rspec -fd.

Identifying Performance Tests

If you have performance tests that take a while, tag the context / describe block like this:

context 'when foo is bar baz', :perf do
  ...
end

Contributing

Bug reports and pull requests are welcome at https://github.com/university-of-york/faculty-dev-sinatra-gem