has-scope-0.7.2/0000755000175000017500000000000013467464770012760 5ustar samyaksamyakhas-scope-0.7.2/lib/0000755000175000017500000000000013467464770013526 5ustar samyaksamyakhas-scope-0.7.2/lib/has_scope.rb0000644000175000017500000001522113467464770016020 0ustar samyaksamyakmodule HasScope TRUE_VALUES = ["true", true, "1", 1] ALLOWED_TYPES = { :array => [[ Array ]], :hash => [[Hash, ActionController::Parameters]], :boolean => [[ Object ], -> v { TRUE_VALUES.include?(v) }], :default => [[ String, Numeric ]], } def self.included(base) base.class_eval do extend ClassMethods class_attribute :scopes_configuration, :instance_writer => false self.scopes_configuration = {} end end module ClassMethods # Detects params from url and apply as scopes to your classes. # # == Options # # * :type - Checks the type of the parameter sent. If set to :boolean # it just calls the named scope, without any argument. By default, # it does not allow hashes or arrays to be given, except if type # :hash or :array are set. # # * :only - In which actions the scope is applied. By default is :all. # # * :except - In which actions the scope is not applied. By default is :none. # # * :as - The key in the params hash expected to find the scope. # Defaults to the scope name. # # * :using - If type is a hash, you can provide :using to convert the hash to # a named scope call with several arguments. # # * :if - Specifies a method, proc or string to call to determine # if the scope should apply # # * :unless - Specifies a method, proc or string to call to determine # if the scope should NOT apply. # # * :default - Default value for the scope. Whenever supplied the scope # is always called. # # * :allow_blank - Blank values are not sent to scopes by default. Set to true to overwrite. # # == Block usage # # has_scope also accepts a block. The controller, current scope and value are yielded # to the block so the user can apply the scope on its own. This is useful in case we # need to manipulate the given value: # # has_scope :category do |controller, scope, value| # value != "all" ? scope.by_category(value) : scope # end # # has_scope :not_voted_by_me, :type => :boolean do |controller, scope| # scope.not_voted_by(controller.current_user.id) # end # def has_scope(*scopes, &block) options = scopes.extract_options! options.symbolize_keys! options.assert_valid_keys(:type, :only, :except, :if, :unless, :default, :as, :using, :allow_blank, :in) if options.key?(:in) options[:as] = options[:in] options[:using] = scopes end if options.key?(:using) if options.key?(:type) && options[:type] != :hash raise "You cannot use :using with another :type different than :hash" else options[:type] = :hash end options[:using] = Array(options[:using]) end options[:only] = Array(options[:only]) options[:except] = Array(options[:except]) self.scopes_configuration = scopes_configuration.dup scopes.each do |scope| scopes_configuration[scope] ||= { :as => scope, :type => :default, :block => block } scopes_configuration[scope] = self.scopes_configuration[scope].merge(options) end end end protected # Receives an object where scopes will be applied to. # # class GraduationsController < InheritedResources::Base # has_scope :featured, :type => true, :only => :index # has_scope :by_degree, :only => :index # # def index # @graduations = apply_scopes(Graduation).all # end # end # def apply_scopes(target, hash=params) scopes_configuration.each do |scope, options| next unless apply_scope_to_action?(options) key = options[:as] if hash.key?(key) value, call_scope = hash[key], true elsif options.key?(:default) value, call_scope = options[:default], true if value.is_a?(Proc) value = value.arity == 0 ? value.call : value.call(self) end end value = parse_value(options[:type], key, value) value = normalize_blanks(value) if call_scope && (value.present? || options[:allow_blank]) current_scopes[key] = value target = call_scope_by_type(options[:type], scope, target, value, options) end end target end # Set the real value for the current scope if type check. def parse_value(type, key, value) #:nodoc: klasses, parser = ALLOWED_TYPES[type] if klasses.any? { |klass| value.is_a?(klass) } parser ? parser.call(value) : value end end # Screens pseudo-blank params. def normalize_blanks(value) #:nodoc: case value when Array value.select { |v| v.present? } when Hash value.select { |k, v| normalize_blanks(v).present? }.with_indifferent_access when ActionController::Parameters normalize_blanks(value.to_unsafe_h) else value end end # Call the scope taking into account its type. def call_scope_by_type(type, scope, target, value, options) #:nodoc: block = options[:block] if type == :boolean && !options[:allow_blank] block ? block.call(self, target) : target.send(scope) elsif value && options.key?(:using) value = value.values_at(*options[:using]) block ? block.call(self, target, value) : target.send(scope, *value) else block ? block.call(self, target, value) : target.send(scope, value) end end # Given an options with :only and :except arrays, check if the scope # can be performed in the current action. def apply_scope_to_action?(options) #:nodoc: return false unless applicable?(options[:if], true) && applicable?(options[:unless], false) if options[:only].empty? options[:except].empty? || !options[:except].include?(action_name.to_sym) else options[:only].include?(action_name.to_sym) end end # Evaluates the scope options :if or :unless. Returns true if the proc # method, or string evals to the expected value. def applicable?(string_proc_or_symbol, expected) #:nodoc: case string_proc_or_symbol when String eval(string_proc_or_symbol) == expected when Proc string_proc_or_symbol.call(self) == expected when Symbol send(string_proc_or_symbol) == expected else true end end # Returns the scopes used in this action. def current_scopes @current_scopes ||= {} end end ActiveSupport.on_load :action_controller do include HasScope helper_method :current_scopes if respond_to?(:helper_method) end has-scope-0.7.2/lib/has_scope/0000755000175000017500000000000013467464770015472 5ustar samyaksamyakhas-scope-0.7.2/lib/has_scope/version.rb0000644000175000017500000000005013467464770017477 0ustar samyaksamyakmodule HasScope VERSION = "0.7.2" end has-scope-0.7.2/test/0000755000175000017500000000000013467464770013737 5ustar samyaksamyakhas-scope-0.7.2/test/has_scope_test.rb0000644000175000017500000002773313467464770017303 0ustar samyaksamyakrequire 'test_helper' HasScope::ALLOWED_TYPES[:date] = [[String], -> v { Date.parse(v) rescue nil }] class Tree; end class TreesController < ApplicationController has_scope :color, :unless => :show_all_colors? has_scope :only_tall, :type => :boolean, :only => :index, :if => :restrict_to_only_tall_trees? has_scope :shadown_range, :default => 10, :except => [ :index, :show, :new ] has_scope :root_type, :as => :root, :allow_blank => true has_scope :planted_before, :default => proc { Date.today } has_scope :planted_after, :type => :date has_scope :calculate_height, :default => proc {|c| c.session[:height] || 20 }, :only => :new has_scope :paginate, :type => :hash has_scope :args_paginate, :type => :hash, :using => [:page, :per_page] has_scope :categories, :type => :array has_scope :title, :in => :q has_scope :content, :in => :q has_scope :conifer, type: :boolean, :allow_blank => true has_scope :only_short, :type => :boolean do |controller, scope| scope.only_really_short!(controller.object_id) end has_scope :by_category do |controller, scope, value| scope.by_given_category(controller.object_id, value + "_id") end def index @trees = apply_scopes(Tree).all end def new @tree = apply_scopes(Tree).new end def show @tree = apply_scopes(Tree).find(params[:id]) end alias :edit :show protected def restrict_to_only_tall_trees? true end def show_all_colors? false end if ActionPack::VERSION::MAJOR == 5 def default_render render body: action_name end else # TODO: Remove this when we only support Rails 5. def default_render render text: action_name end end end class BonsaisController < TreesController has_scope :categories, :if => :categories? protected def categories? false end end class HasScopeTest < ActionController::TestCase tests TreesController def test_boolean_scope_is_called_when_boolean_param_is_true Tree.expects(:only_tall).with().returns(Tree).in_sequence Tree.expects(:all).returns([mock_tree]).in_sequence get :index, :only_tall => 'true' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :only_tall => true }, current_scopes) end def test_boolean_scope_is_not_called_when_boolean_param_is_false Tree.expects(:only_tall).never Tree.expects(:all).returns([mock_tree]) get :index, :only_tall => 'false' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_true Tree.expects(:conifer).with(true).returns(Tree).in_sequence Tree.expects(:all).returns([mock_tree]).in_sequence get :index, :conifer => 'true' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :conifer => true }, current_scopes) end def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_false Tree.expects(:conifer).with(false).returns(Tree).in_sequence Tree.expects(:all).returns([mock_tree]).in_sequence get :index, :conifer => 'not_true' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :conifer => false }, current_scopes) end def test_boolean_scope_with_allow_blank_is_not_called_when_boolean_param_is_not_present Tree.expects(:conifer).never Tree.expects(:all).returns([mock_tree]) get :index assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_is_called_only_on_index Tree.expects(:only_tall).never Tree.expects(:find).with('42').returns(mock_tree) get :show, :only_tall => 'true', :id => '42' assert_equal(mock_tree, assigns(:@tree)) assert_equal({ }, current_scopes) end def test_scope_is_skipped_when_if_option_is_false @controller.stubs(:restrict_to_only_tall_trees?).returns(false) Tree.expects(:only_tall).never Tree.expects(:all).returns([mock_tree]) get :index, :only_tall => 'true' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_is_skipped_when_unless_option_is_true @controller.stubs(:show_all_colors?).returns(true) Tree.expects(:color).never Tree.expects(:all).returns([mock_tree]) get :index, :color => 'blue' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_is_called_except_on_index Tree.expects(:shadown_range).never Tree.expects(:all).returns([mock_tree]) get :index, :shadown_range => 20 assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_is_called_with_arguments Tree.expects(:color).with('blue').returns(Tree).in_sequence Tree.expects(:all).returns([mock_tree]).in_sequence get :index, :color => 'blue' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :color => 'blue' }, current_scopes) end def test_scope_is_not_called_if_blank Tree.expects(:color).never Tree.expects(:all).returns([mock_tree]).in_sequence get :index, :color => '' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_is_called_when_blank_if_allow_blank_is_given Tree.expects(:root_type).with('').returns(Tree) Tree.expects(:all).returns([mock_tree]).in_sequence get :index, :root => '' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :root => '' }, current_scopes) end def test_multiple_scopes_are_called Tree.expects(:only_tall).with().returns(Tree) Tree.expects(:color).with('blue').returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, :color => 'blue', :only_tall => 'true' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :color => 'blue', :only_tall => true }, current_scopes) end def test_scope_of_type_hash hash = { "page" => "1", "per_page" => "10" } Tree.expects(:paginate).with(hash).returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, :paginate => hash assert_equal([mock_tree], assigns(:@trees)) assert_equal({ paginate: hash }, current_scopes) end def test_scope_of_type_hash_with_using hash = { "page" => "1", "per_page" => "10" } Tree.expects(:args_paginate).with("1", "10").returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, :args_paginate => hash assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :args_paginate => hash }, current_scopes) end def test_hash_with_blank_values_is_ignored hash = { "page" => "", "per_page" => "" } Tree.expects(:paginate).never Tree.expects(:all).returns([mock_tree]) get :index, :paginate => hash assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_nested_hash_with_blank_values_is_ignored hash = { "parent" => {"children" => ""} } Tree.expects(:paginate).never Tree.expects(:all).returns([mock_tree]) get :index, :paginate => hash assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_nested_blank_array_param_is_ignored hash = { "parent" => [""] } Tree.expects(:paginate).never Tree.expects(:all).returns([mock_tree]) get :index, :paginate => hash assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_of_type_array array = %w(book kitchen sport) Tree.expects(:categories).with(array).returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, :categories => array assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :categories => array }, current_scopes) end def test_array_of_blank_values_is_ignored Tree.expects(:categories).never Tree.expects(:all).returns([mock_tree]) get :index, :categories => [""] assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_of_invalid_type_silently_fails Tree.expects(:all).returns([mock_tree]) get :index, :paginate => "1" assert_equal([mock_tree], assigns(:@trees)) assert_equal({ }, current_scopes) end def test_scope_is_called_with_default_value Tree.expects(:shadown_range).with(10).returns(Tree).in_sequence Tree.expects(:find).with('42').returns(mock_tree).in_sequence get :edit, :id => '42' assert_equal(mock_tree, assigns(:@tree)) assert_equal({ :shadown_range => 10 }, current_scopes) end def test_default_scope_value_can_be_overwritten Tree.expects(:shadown_range).with('20').returns(Tree).in_sequence Tree.expects(:find).with('42').returns(mock_tree).in_sequence get :edit, :id => '42', :shadown_range => '20' assert_equal(mock_tree, assigns(:@tree)) assert_equal({ :shadown_range => '20' }, current_scopes) end def test_scope_with_different_key Tree.expects(:root_type).with('outside').returns(Tree).in_sequence Tree.expects(:find).with('42').returns(mock_tree).in_sequence get :show, :id => '42', :root => 'outside' assert_equal(mock_tree, assigns(:@tree)) assert_equal({ :root => 'outside' }, current_scopes) end def test_scope_with_default_value_as_a_proc_without_argument Date.expects(:today).returns("today") Tree.expects(:planted_before).with("today").returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :planted_before => "today" }, current_scopes) end def test_scope_with_default_value_as_proc_with_argument session[:height] = 100 Tree.expects(:calculate_height).with(100).returns(Tree).in_sequence Tree.expects(:new).returns(mock_tree).in_sequence get :new assert_equal(mock_tree, assigns(:@tree)) assert_equal({ :calculate_height => 100 }, current_scopes) end def test_scope_with_custom_type parsed = Date.civil(2014,11,11) Tree.expects(:planted_after).with(parsed).returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, :planted_after => "2014-11-11" assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :planted_after => parsed }, current_scopes) end def test_scope_with_boolean_block Tree.expects(:only_really_short!).with(@controller.object_id).returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, :only_short => 'true' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :only_short => true }, current_scopes) end def test_scope_with_other_block_types Tree.expects(:by_given_category).with(@controller.object_id, 'for_id').returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, :by_category => 'for' assert_equal([mock_tree], assigns(:@trees)) assert_equal({ :by_category => 'for' }, current_scopes) end def test_scope_with_nested_hash_and_in_option hash = { 'title' => 'the-title', 'content' => 'the-content' } Tree.expects(:title).with('the-title').returns(Tree) Tree.expects(:content).with('the-content').returns(Tree) Tree.expects(:all).returns([mock_tree]) get :index, q: hash assert_equal([mock_tree], assigns(:@trees)) assert_equal({ q: hash }, current_scopes) end def test_overwritten_scope assert_nil(TreesController.scopes_configuration[:categories][:if]) assert_equal(:categories?, BonsaisController.scopes_configuration[:categories][:if]) end protected if ActionPack::VERSION::MAJOR == 5 # TODO: Remove this when we only support Rails 5. def get(action, params = {}) super action, params: params end end def mock_tree(stubs={}) @mock_tree ||= mock(stubs) end def current_scopes @controller.send :current_scopes end def assigns(ivar) @controller.instance_variable_get(ivar) end end class TreeHugger include HasScope has_scope :color def by_color apply_scopes(Tree, :color => 'blue') end end class HasScopeOutsideControllerTest < ActiveSupport::TestCase def test_has_scope_usable_outside_controller Tree.expects(:color).with('blue') TreeHugger.new.by_color end end has-scope-0.7.2/test/test_helper.rb0000644000175000017500000000131513467464770016602 0ustar samyaksamyakrequire 'bundler/setup' require 'minitest/autorun' require 'mocha' require 'mocha/mini_test' # Configure Rails ENV['RAILS_ENV'] = 'test' require 'active_support' require 'active_support/core_ext/string/strip' require 'action_controller' require 'action_dispatch/middleware/flash' $:.unshift File.expand_path('../../lib', __FILE__) require 'has_scope' HasScope::Routes = ActionDispatch::Routing::RouteSet.new HasScope::Routes.draw do get '/:controller(/:action(/:id))' end class ApplicationController < ActionController::Base include HasScope::Routes.url_helpers end class ActiveSupport::TestCase self.test_order = :random if respond_to?(:test_order=) setup do @routes = HasScope::Routes end end has-scope-0.7.2/MIT-LICENSE0000644000175000017500000000211413467464770014412 0ustar samyaksamyakCopyright 2009-2017 Plataforma Tecnologia. http://blog.plataformatec.com.br Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. has-scope-0.7.2/has_scope.gemspec0000644000175000017500000000420013467464770016265 0ustar samyaksamyak######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: has_scope 0.7.2 ruby lib Gem::Specification.new do |s| s.name = "has_scope".freeze s.version = "0.7.2" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Jos\u00E9 Valim".freeze] s.date = "2018-04-10" s.description = "Maps controller filters to your resource scopes".freeze s.email = "opensource@plataformatec.com.br".freeze s.extra_rdoc_files = ["README.md".freeze] s.files = ["MIT-LICENSE".freeze, "README.md".freeze, "lib/has_scope.rb".freeze, "lib/has_scope/version.rb".freeze, "test/has_scope_test.rb".freeze, "test/test_helper.rb".freeze] s.homepage = "http://github.com/plataformatec/has_scope".freeze s.licenses = ["MIT".freeze] s.rdoc_options = ["--charset=UTF-8".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.1.7".freeze) s.rubygems_version = "2.7.6.2".freeze s.summary = "Maps controller filters to your resource scopes.".freeze s.test_files = ["test/has_scope_test.rb".freeze, "test/test_helper.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q.freeze, [">= 4.1"]) s.add_runtime_dependency(%q.freeze, [">= 4.1"]) s.add_development_dependency(%q.freeze, ["~> 1.0.0"]) s.add_development_dependency(%q.freeze, [">= 0"]) else s.add_dependency(%q.freeze, [">= 4.1"]) s.add_dependency(%q.freeze, [">= 4.1"]) s.add_dependency(%q.freeze, ["~> 1.0.0"]) s.add_dependency(%q.freeze, [">= 0"]) end else s.add_dependency(%q.freeze, [">= 4.1"]) s.add_dependency(%q.freeze, [">= 4.1"]) s.add_dependency(%q.freeze, ["~> 1.0.0"]) s.add_dependency(%q.freeze, [">= 0"]) end end has-scope-0.7.2/README.md0000644000175000017500000001204113467464770014235 0ustar samyaksamyak## HasScope [![Gem Version](https://fury-badge.herokuapp.com/rb/has_scope.svg)](http://badge.fury.io/rb/has_scope) [![Build Status](https://api.travis-ci.org/plataformatec/has_scope.svg?branch=master)](http://travis-ci.org/plataformatec/has_scope) [![Code Climate](https://codeclimate.com/github/plataformatec/has_scope.svg)](https://codeclimate.com/github/plataformatec/has_scope) Has scope allows you to map incoming controller parameters to named scopes in your resources. Imagine the following model called graduations: ```ruby 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) } end ``` You can use those named scopes as filters by declaring them on your controller: ```ruby class GraduationsController < ApplicationController has_scope :featured, :type => :boolean has_scope :by_degree end ``` Now, if you want to apply them to an specific resource, you just need to call `apply_scopes`: ```ruby class GraduationsController < ApplicationController has_scope :featured, :type => :boolean has_scope :by_degree has_scope :by_period, :using => [:started_at, :ended_at], :type => :hash def index @graduations = apply_scopes(Graduation).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?featured=true&by_degree=phd #=> brings featured graduations with phd degree ``` You can retrieve all the scopes applied in one action with `current_scopes` method. In the last case, it would return: { :featured => true, :by_degree => "phd" }. ## Installation Add `has_scope` to your Gemfile or install it from Rubygems. ```ruby gem 'has_scope' ``` ## Options HasScope supports several options: * `:type` - Checks the type of the parameter sent. By default, it does not allow hashes or arrays to be given, except if type `:hash` or `:array` are set. Symbols are never permitted to prevent memory leaks, so ensure any routing constraints you have that add parameters use string values. * `: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. * `:using` - The subkeys to be used as args when type is a hash. * `:if` - Specifies a method, proc or string to call to determine if the scope should apply. * `:unless` - Specifies a method, proc or string to call to determine if the scope should NOT apply. * `:default` - Default value for the scope. Whenever supplied the scope is always called. * `:allow_blank` - Blank values are not sent to scopes by default. Set to true to overwrite. * `:in` - A shortcut for combining the `:using` option with nested hashes. ## Boolean usage If `type: :boolean` is set it just calls the named scope, without any arguments, when parameter is set to a "true" value. `'true'` and `'1'` are parsed as `true`, everything else as `false`. When boolean scope is set up with `allow_blank: true`, it will call the scope with the value as usual scope. ```ruby has_scope :visible, type: :boolean has_scope :active, type: :boolean, allow_blank: true # and models with scope :visible, -> { where(visible: true) } scope :active, ->(value = true) { where(active: value) } ``` ## Block usage `has_scope` also accepts a block. The controller, current scope and value are yielded to the block so the user can apply the scope on its own. This is useful in case we need to manipulate the given value: ```ruby has_scope :category do |controller, scope, value| value != "all" ? scope.by_category(value) : scope end ``` When used with booleans without `:allow_blank`, it just receives two arguments and is just invoked if true is given: ```ruby has_scope :not_voted_by_me, :type => :boolean do |controller, scope| scope.not_voted_by(controller.current_user.id) end ``` ## Keyword arguments Scopes with keyword arguments need to be called in a block: ```ruby # in the model scope :for_course, lambda { |course_id:| where(course_id: course_id) } # in the controller has_scope :for_course do |controller, scope, value| scope.for_course(course_id: value) end ``` ## Apply scope on every request To apply scope on every request set default value and `allow_blank: true`: ```ruby has_scope :available, default: nil, allow_blank: true, only: :show, unless: :admin? # model: scope :available, ->(*) { where(blocked: false) } ``` This will allow usual users to get only available items, but admins will be able to access blocked items too. ## Bugs and Feedback If you discover any bugs or want to drop a line, feel free to create an issue on GitHub. http://github.com/plataformatec/has_scope/issues MIT License. Copyright 2009-2016 Plataformatec. http://blog.plataformatec.com.br