Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # routes.rb
- Rails.application.routes.draw do
- namespace :api do
- resources :streets, only: [:index]
- end
- end
- # app/controllers/api/streets_controller.rb
- module Api
- class StreetsController < ApplicationController
- include Pagination
- def index
- render(
- json: serialized_streets,
- status: :ok
- )
- end
- private
- def serialized_streets
- Rails.cache.fetch("api/streets/serialized_streets/#{cache_key}", expires_in: 1.hour) do
- StreetSerializer.new(streets, serializer_options).serialized_json
- end
- end
- def streets
- @streets ||= search_query.perform.page(current_page).per(per_page)
- end
- def search_query
- ::Search::StreetsQuery.new(streets_params)
- end
- def serializer_options
- {
- meta: {
- pagination: meta_pagination_for(streets)
- }
- }
- end
- def cache_key
- Digest::MD5.hexdigest(streets_params.to_s)
- end
- def streets_params
- params.permit(
- :street_name,
- :street_number,
- :city,
- :sort,
- :page
- )
- end
- end
- end
- # app/controllers/api/pagination.rb
- module Api
- module Pagination
- def per_page
- MyAwesomeApp.config.per_page
- end
- def current_page
- params[:page] || 1
- end
- def meta_pagination_for(collection)
- {
- current_page: collection.current_page,
- prev_page: collection.prev_page,
- next_page: collection.next_page,
- total_pages: collection.total_pages
- }
- end
- end
- end
- # app/serializers/street_serializer.rb
- class StreetSerializer
- include FastJsonapi::ObjectSerializer
- attributes *Street.attribute_names.map(&:to_sym)
- end
- # app/queries/search/streets_query.rb
- module Search
- class StreetsQuery
- attr_reader :params, :scope
- def initialize(params = {}, scope = Street.all)
- @params = params
- @scope = scope
- end
- def perform
- query = scope
- query = filter_by_street_name_like(query, params[:street_name])
- query = filter_by_street_number(query, params[:street_number])
- query = filter_by_city_like(query, params[:city])
- query = sort_by_street_name(query, sorts[:street_name])
- query = sort_by_city(query, sorts[:city])
- end
- private
- #
- # Filters
- #
- def filter_by_street_name_like(query, street_name)
- return query unless street_name.present?
- query.where_street_name_like(street_name.downcase)
- end
- def filter_by_street_number(query, street_number)
- return query unless street_number.present?
- query.where(
- low: -Float::INFINITY..street_number.to_i,
- high: street_number.to_i..Float::INFINITY
- )
- end
- def filter_by_city_like(query, city)
- return query unless city.present?
- query.where('city LIKE ?', "%#{city.downcase}%")
- end
- #
- # Sorts
- #
- def sort_by_street_name(query, direction)
- return query unless direction.present?
- query.order(street_name: direction)
- end
- def sort_by_city(query, direction)
- return query unless direction.present?
- query.order(city: direction)
- end
- def sorts
- @sorts ||= SortString.new(params[:sort]).to_h
- end
- end
- end
- # app/services/sort_string.rb
- class SortString
- attr_reader :sort
- def initialize(sort)
- @sort = sort
- end
- def to_h
- sorts.to_h
- end
- private
- # Example:
- #
- # 'name:asc,created_at:desc'
- #
- # => ['name:asc', 'created_at:desc']
- #
- def pieces
- sort&.split(',') || []
- end
- # Example:
- #
- # ['name:asc', 'created_at:desc']
- #
- # => [['name', 'asc'], ['created_at', 'desc']]
- #
- def sorts
- @sorts ||= pieces.map do |piece|
- slug, direction = piece.split(':')
- unless direction.in?(allowed_directions)
- direction = default_direction
- end
- [slug.to_sym, direction.to_sym]
- end
- end
- def allowed_directions
- %w(asc desc)
- end
- def default_direction
- 'asc'
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement