Might
Might utilizes the power of Ransack gem to provide models gathering api for Rails' controllers.
Installation
Add this line to your application's Gemfile:
gem 'might'
And then execute:
$ bundle
Or install it yourself as:
$ gem install might
Usage
Define a mighty fetcher
class ChannelFetcher < Might::Fetcher
# Specify resource to be fetched
self.resource_class = Channel
# Specify list of allowed filters
filter :genres_name
# Allowed sortings
sort :name
sort :created_at
end
And fetch resources by user input:
params = {
'filter' => {
'genres_name_in' => 'Horror,Drama',
},
'sort' => '-created_at,name',
'page' => {
'limit' => 50,
'offset' => 0
}
}
ChannelFetcher.run(params) do |result|
if result.success?
render json: result.get
else
render json: result.errors, status: :bad_request
end
end
# or
ChannelFetcher.run(params) #=> ChannelFetcher::Result
params
hash should follow the following conventions:
-
:filter
key is used for filtering -
:sort
key is used for sorting -
:page
key is used for pagination
The result of evaluating #run
method is Might::Result
. It may be either success or failure.
This container have the following api:
-
#success?
-true
if result isSuccess
, false otherwise -
#failure?
-true
if result isFailure
, false otherwise -
#get
- returns fetching result if result isSuccess
, raises exception if it isFailure
-
#errors
- returns parameters processing errors (as array of strings) if result isFailure
, raises exception if it isSuccess
NOTE: #errors
returns only errors in user input, and not includes exceptions thrown during fetching process.
Configuring filters
To filter resources using Mighty Fetcher you should provide white list of allowed filters. Internally it uses Ransack gem, so consult with its documentation in case of frustration.
Simplest filter definition looks as follows:
filter :name
It allows client to filter collection on its name. So if a client want to
filter on exec name it should pass a hash containing name
followed by _eq
predicate:
ChannelFetcher.run(
'filter' => {
'name_eq' => 'MTV'
}
)
If you need to pass list of values, specify them as comma separated list:
ChannelFetcher.run(
'filter' => {
'name_in' => 'MTV,A-One'
}
)
You may provide alias for an attributes using :as
option
filter :name, as: :title
The Channel#name
attribute would be exposed as title
to the fetcher api.
ChannelFetcher.run(
'filter' => {
'title_eq' => 'MTV'
}
)
Using power of Ransack we can filter of relations' attributes. For instance to search for channels on languages with specified iso2 code, you have two options.
filter :language_iso2
Query on this attribute as is and Ransack will handle all details:
ChannelFetcher.run(
'filter' => {
'language_iso2_eq' => 'ru'
}
)
The second option is:
filter :iso2, on: :language
ChannelFetcher.run(
'filter' => {
'iso2_eq => 'ru'
}
)
It's possible to coerce attribute before using it:
filter :resource_type, coerce: ->(value) { String(value).classify }
You can whitelist predicates for each filter:
filter id: [:in, :eq]
filter name: :eq
Filtering on polymorphic association needs more configuration.
class House < ActiveRecord::Base
has_one :location, as: :locatable
end
class Location < ActiveRecord::Base
belongs_to :locatable, polymorphic: true
end
class LocationFetcher < Might::Fetcher
filter :locatable_id, on: { resource: ['House'] }
end
User provided filters may be validated using ActiveModel::Validation
filter registration_number: :eq, validates: { length: { is: 6 } }
If value of the registration_number
is invalid it raises Might::FilterValidationFailed
.
List of all supported predicates
Predicate | Opposite Predicate | Meaning |
---|---|---|
eq |
not_eq |
field is exactly equal to a given value |
matches |
does_not_match |
field is like a given value |
lt |
gt |
field is less than a given value |
lteq |
gteq |
field is less than or equal to a given value |
in |
not_in |
field is within a specified list |
cont |
not_cont |
field contains a given value |
cont_any |
not_cont_any |
field contains any of given values |
start |
not_start |
field begins with a given value |
end |
not_end |
field ends with a given value |
true |
not_true |
field is true |
false |
not_false |
field is false |
present |
blank |
field is present (not null and not a blank string) |
null |
not_null |
field is null |
You may want to implement custom predicate. First of all you should implement predicate for Ransack gem. This process documented here. Then you have to register predicate.
# Predicate for a singular value (such as a string)
Might::FilterPredicates.register('is_upper_case', on: :value)
# Predicate for an array
Might::FilterPredicates.register('includes', on: :array)
Configuring sorting
Mighty Fetcher supports sorting resource collections according to one or more criteria.
sort :name
sort :created_at
To sort on one of the allowed fields provide sort
parameter:
ChannelFetcher.run('sort' => 'name')
If you need to sort against multiple fields separate them with comma. Sort fields would be applied in the order specified.
ChannelFetcher.run('sort' => 'name,created_at')
The sort order for each sort field is ascending unless it is prefixed with a minus, in which case it is descending.
ChannelFetcher.run('sort' => '-created_at,name')
The above example should return the newest channels first. Any articles created on the same date will then be sorted by their name in ascending alphabetical order.
Configuring components
Mighty Fetcher implements it's components as chain of middlewares (Using ibsciss-middleware)
You can disable sorting, pagination and filtering altering middleware chain.
class ChannelFetch < Fetch
middleware.delete Might::FilterMiddleware
middleware.insert_before Might::SortMiddleware, CustomFilterMiddleware
middleware.use AnotherMiddleware
end
If you need do perform some actions before or after middleware chain:
class FavoritesFetcher < Might::Fetcher
before do |favorites, params|
[favorites.by_user(params[:user]), params]
end
end
To get translated error messages load Rails Railtie
require 'might/railtie'
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake
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
. 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 tags, and push the .gem
file to rubygems.org.
We test Might
against different versions of ActiveRecord
and Ransack
using appraisal gem.
To regenerate gemfiles run:
$ appraisal install
To run specs against all versions:
$ appraisal rake spec
License
Copyright 2015 SPB TV AG
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.