Project

scopie

0.0
No commit activity in last 3 years
No release in over 3 years
Minimal mapping of incoming parameters to named scopes in your resources through OO design and pure Ruby classes
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

Scopie

Code Climate Build Status Coverage Status Dependency Status Gem Version

A has_scope alternative.

Scopie allows you to map incoming controller parameters to named scopes in your resources through OO design.

Motivation:

  • Dedicated class for scopes mapping, so that the logic is isolated and your controller is skinny.
  • Dependencies free. Please have a look at scopie_rails if you are using Ruby on Rails framework.
  • Ability to override default mapping behavior by definition of a method having the same name as a scope in the scopie class.
  • Ability to use the object oriented approach to DRY your custom scopes mapping logic and reuse the scopie class.

Imagine the following model called graduations:

class Graduation < ActiveRecord::Base

  scope :featured, -> { where(featured: true) }
  scope :by_degree, -> (degree) { where(degree: degree) }
  scope :by_period, -> (started_at, ended_at) { where('started_at = ? AND ended_at = ?', started_at, ended_at) }

  scope :created_at_greater_than, ->(date) { where('created_at >= ?', date.beginning_of_day) }
  scope :created_at_less_than, ->(date) { where('created_at <= ?', date.end_of_day) }

  scope :updated_at_greater_than, ->(date) { where('updated_at >= ?', date.beginning_of_day) }
  scope :updated_at_less_than, ->(date) { where('updated_at <= ?', date.end_of_day) }

end

You can use those named scopes as filters by declaring them on your scopie:

class GraduationsScopie < Scopie::Base

  has_scope :featured, type: :boolean
  has_scope :by_degree, :by_period

  has_scope :created_at_greater_than, in: :created_at, as: :start_at
  has_scope :created_at_less_than, in: :created_at, as: :end_at

  has_scope :updated_at_greater_than, in: :updated_at, as: :start_at, type: :date
  has_scope :updated_at_less_than, in: :updated_at, as: :end_at, type: :date

  has_scope :page, default: 1
  has_scope :per, default: 30
  
  def by_period(scope, value, _hash)
    started_at = value[:started_at]
    ended_at = value[:ended_at]

    started_at && ended_at && scope.by_period(started_at, ended_at)
  end

  def created_at_greater_than(scope, value, _hash)
    scope.created_at_greater_than(parse_date(value))
  end

  def created_at_less_than(scope, value, _hash)
    scope.created_at_less_than(parse_date(value))
  end

  def updated_at_greater_than(scope, value, _hash)
    scope.updated_at_greater_than(value)
  end

  def updated_at_less_than(scope, value, _hash)
    scope.updated_at_less_than(value)
  end

  private

  def parse_date(value)
    Date.parse(value)
  end

end

Now, if you want to apply them to an specific resource, you just need to call apply_scopes:

class GraduationsController < ApplicationController

  def index
    @graduations = Scopie.apply_scopes(Graduation, method: :index, scopie: GraduationsScopie.new).all
  end

end

Then for each request:

/graduations
#=> acts like a normal request

/graduations?featured=true
#=> calls the named scope and bring featured graduations

/graduations?by_period[started_at]=20100701&by_period[ended_at]=20101013
#=> brings graduations in the given period

/graduations?created_at[start_at]=2016-06-01&created_at[end_at]=2016-06-02
#=> brings graduations created in the given period

/graduations?featured=true&by_degree=phd
#=> brings featured graduations with phd degree

Installation

Add scopie to your Gemfile or install it from Rubygems.

gem 'scopie'

Mutation testing

mutant --include lib --require scopie --use rspec Scopie*

Options

Scopie supports several options:

  • :type - Coerces the type of the parameter sent. Available options: boolean, integer, float, date.

  • :only - In which actions the scope is applied.

  • :except - In which actions the scope is not applied.

  • :as - The key in the params hash expected to find the scope. Defaults to the scope name.

  • :in - The key in the params hash expected to contain a hash holding scope name as a value.

  • :allow_blank - Blank values are not sent to scopes by default. Set to true to overwrite.

  • :ignore_blank - Set to true to not apply the scope if blank value is given.

  • :default - Default value for the scope. Whenever supplied the scope is always called.

Thanks

Scopie was inspired by has_scope and pundit.

Thanks to both.