Parelation

Gem Version Code Climate Build Status

Parelation, for Rails/ActiveRecord 6.0+, allows you to query your ActiveRecord-mapped database easily, securely and quite flexibly using simple GET requests. It's used in your controller layer where it uses HTTP GET parameters to build on the ActiveRecord::Relation chain. This provides the client-side with the out-of-the-box flexibility to perform fairly dynamic queries without having to write boilerplate on the server.

This library was developed for- and extracted from HireFire.

The documentation can be found on RubyDoc.

Compatibility

  • Rails/ActiveRecord 6.0+
  • Ruby (MRI) 2.5+

Installation

Add the gem to your Gemfile and run bundle.

gem "parelation"

Example

Here's an example to get an idea of how it works. We'll fetch the 50 most recently created and open tickets, and we only want their id, name and message attributes.

var params = {
  "select[]": ["id", "name", "message"],
  "where[state]": "open",
  "order": "created_at:desc",
  "limit": "50"
}

$.get("https://api.ticket.app/tickets", params, function(tickets){
  console.log("Just fetched the 50 most recent and open tickets.")
  $.each(tickets, function(ticket){
    console.log("Ticket " + ticket.name + " loaded!")
  })
})

Simply include Parelation::Helpers and use the parelate method. This will ensure that the provided parameters are converted and applied to the Ticket.all criteria chain.

class Api::V1::TicketsController < ApplicationController
  include Parelation::Helpers

  def index
    render json: parelate(Ticket.all)
  end
end

You can also scope results to the current_user:

class Api::V1::TicketsController < ApplicationController
  include Parelation::Helpers

  def index
    render json: parelate(current_user.tickets)
  end
end

Using the same JavaScript, this'll now fetch the 50 most recent open tickets scoped to the current_user.

Parameter List (Reference)

Here follows a list of all possible query syntaxes. We'll assume we have a Ticket model to query on.

Select

/tickets?select[]=id&select[]=name&select[]=message

Translates to:

Ticket.select(:id, :name, :message)

Where

/tickets?where[state]=open

Translates to:

Ticket.where(state: "open")

You can also specify multiple multiple conditions:

/tickets?where[state][]=open&where[state][]=pending

Translates to:

Ticket.where(state: ["open", "pending"])

Where (directional)

  • where_gt (greater than >)
  • where_gte (greater than or equal to >=)
  • where_lt (less than <)
  • where_lte (less than or equal to <=)
/tickets?where_gt[created_at]=2014-01-01T00:00:00Z

Translates to:

Ticket.where("'tickets'.'created_at' > '2014-01-01 00:00:00.000000'")

You can also specify multiple conditions:

/tickets?where_gt[created_at]=2014-01-01T00:00:00Z&where_gt[updated_at]=2014-01-01T00:00:00Z

Translates to:

Ticket
  .where("'tickets'.'created_at' > '2014-01-01 00:00:00.000000'")
  .where("'tickets'.'updated_at' > '2014-01-01 00:00:00.000000'")

Query

/tickets?query[memory leak]=name

Translates to:

Ticket.where("'tickets'.'name' LIKE '%memory leak%'")

You can also specify multiple columns to scan:

/tickets?query[memory leak]=name&query[memory leak]=message

Translates to:

Ticket.where("(
  'tickets'.'name' LIKE '%memory leak%' OR
  'tickets'.'message' LIKE '%memory leak%'
)")

Order

/tickets?order=created_at:desc

Translates to:

Ticket.order(created_at: :desc)

You can also specify multiple order-operations:

/tickets?order[]=created_at:desc&order[]=name:asc

Translates to:

Ticket.order(created_at: :desc, name: :asc)

Limit

/tickets?limit=50

Translates to:

Ticket.limit(50)

Offset

/tickets?offset=25

Translates to:

Ticket.offset(25)

Error Handling

When invalid parameters were sent, you can rescue the exception and return a message to the client.

class Api::V1::TicketsController < ApplicationController
  include Parelation::Helpers

  def index
    render json: parelate(Ticket.all)
  rescue Parelation::Errors::Parameter => error
    render json: { error: error }, status: :bad_request
  end
end

This will tell client developers what parameter failed in the HTTP response. This exception generally occurs when there is a typo in the URL's parameters. Knowing which parameter failed (described in error) helps narrowing down the issue.

Contributing

Contributions are welcome, but please conform to these requirements:

  • Ruby (MRI) 2.5+
  • ActiveRecord 6.0+
  • 100% Spec Coverage
    • Generated by when running the test suite
  • 100% Passing Specs
    • Run test suite with $ rspec spec
  • 4.0 Code Climate Score
    • Run $ rubycritic lib to generate the score locally and receive tips
    • No code smells
    • No duplication

To start contributing, fork the project, clone it, and install the development dependencies:

git clone git@github.com:USERNAME/parelation.git
cd parelation
bundle

Ensure that everything works:

rspec spec
rubycritic lib

To run the local documentation server:

yard server --reload

Create a new branch and start hacking:

git checkout -b my-contributions

Submit a pull request.

Author / License

Released under the MIT License by Michael van Rooijen.