Skip to content

Instantly share code, notes, and snippets.

@msuzoagu
Forked from mamantoha/experience.rb
Created March 3, 2021 00:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msuzoagu/8c2d9ebe73a31178684c98b8b34d0abc to your computer and use it in GitHub Desktop.
Save msuzoagu/8c2d9ebe73a31178684c98b8b34d0abc to your computer and use it in GitHub Desktop.
Rails API Filtering and Sorting
# app/models/experience.rb
#
# == Schema Information
#
# Table name: experiences
#
# id :integer not null, primary key
# title :string
# description :text
# created_at :datetime not null
# updated_at :datetime not null
# city_id :integer
# price :integer default(0)
# distance :integer
# duration :integer
#
class Experience < ActiveRecord::Base
belongs_to :city
has_many :categorizations
has_many :categories, through: :categorizations
scope :by_city, -> (city_ids) { where(city_id: city_ids) }
scope :by_price, -> (from, to) { where("price >= ? AND price <= ?", from, to) }
scope :by_duration, -> (from, to) { where("duration >= ? AND duration <= ?", from, to) }
scope :by_distance, -> (from, to) { where("distance >= ? AND distance <= ?", from, to) }
scope :by_category, -> (category_ids) { joins(:categories).where(categories: { id: category_ids }) }
end
# app/serializers/experience_serializer.rb
class ExperienceSerializer < ActiveModel::Serializer
attribute :id
attribute :title
attribute :description
attribute :price
attribute :distance
attribute :duration
belongs_to :city
has_many :categories
end
# app/controllers/api/v1/experiences_controller.rb
class Api::V1::ExperiencesController < Api::V1::BaseController
include Orderable
before_filter :authenticate_user!
# Filters:
# /api/v1/experiences?by_price[from]=100&by_price[to]=999
# /api/v1/experiences?by_category=1,2,3
# /api/v1/experiences?by_city=1,2,3
# /api/v1/experiences?by_duration[from]=10&by_duration[to]=60
#
has_scope :by_category, only: :index
has_scope :by_city, only: :index
has_scope :by_price, using: [:from, :to], only: :index
has_scope :by_duration, using: [:from, :to], only: :index
has_scope :by_distance, using: [:from, :to], only: :index
# GET /api/v1/experiences
def index
@experiences = apply_scopes(Experience).
order(ordering_params(params)).
# fix N+1 query problem
includes(:city, :user, :categories).
all
render json: @experiences
end
end
# app/controllers/concerns/orderable.rb
module Orderable
extend ActiveSupport::Concern
module ClassMethods
end
# A list of the param names that can be used for ordering the model list
def ordering_params(params)
# For example it retrieves a list of experiences in descending order of price.
# Within a specific price, older experiences are ordered first
#
# GET /api/v1/experiences?sort=-price,created_at
# ordering_params(params) # => { price: :desc, created_at: :asc }
# Experience.order(price: :desc, created_at: :asc)
#
ordering = {}
if params[:sort]
sort_order = { '+' => :asc, '-' => :desc }
sorted_params = params[:sort].split(',')
sorted_params.each do |attr|
sort_sign = (attr =~ /\A[+-]/) ? attr.slice!(0) : '+'
model = controller_name.classify.constantize
if model.attribute_names.include?(attr)
ordering[attr] = sort_order[sort_sign]
end
end
end
return ordering
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment