Skip to content

Instantly share code, notes, and snippets.

@henrik
Last active March 25, 2020 08:04
Show Gist options
  • Save henrik/3666601 to your computer and use it in GitHub Desktop.
Save henrik/3666601 to your computer and use it in GitHub Desktop.
Share some parameter string filter between controller and view.
# Suitable when a controller and view share some parameter string.
#
# E.g. view:
#
# link_to_unless(filter.blank?, "All", filter: filter.blank)
# link_to_unless(filter.some?, "Some", filter: filter.some)
# link_to_unless(filter.others?, "Others", filter: filter.others)
#
# Or maybe:
#
# - filter.blank_and_values.each do |value|
# link_to_unless(filter.value == value, t(:"filters.#{value || :blank}"), filter: value)
#
# ApplicationController:
#
# extend StringFilter::ActionControllerExtension
#
# Controller:
#
# filter_with :some, :others
# filter_with :pear, :apple, name: :fruit_filter
#
# def index
# do_whatever if filter.some?
# do_another if fruit_filter.pear?
# end
class StringFilter
class UnsupportedValueError < StandardError; end
def initialize(values, current_value)
@current_value = current_value
@values = values
unless blank? || values.map(&:to_s).include?(current_value.to_s)
raise UnsupportedValueError
end
values.each do |value|
define_singleton_method(:"#{value}?") { # def foo?
value.to_s == current_value.to_s # :foo.to_s == current_value.to_s
} # end
define_singleton_method(value) { # def foo
value.to_sym # :foo
} # end
end
end
def blank
nil
end
def value
if blank?
blank
else
send(current_value)
end
end
def values
@values
end
def blank_and_values
[blank] + values
end
def blank?
current_value.nil? || current_value.empty?
end
private
def current_value
@current_value
end
end
# Do:
#
# class ApplicationController < ActionController::Base
# extend StringFilter::ActionControllerExtension
# end
#
# Now any controller can get a string filter with e.g.:
#
# class MyController < ApplicationController
# filter_with :foo, :bar
#
# # This gives you `fruit_filter.pear` etc.
# filter_with :pear, :apple, name: :fruit_filter
# end
module StringFilter::ActionControllerExtension
def filter_with(*filters, name: :filter)
define_method(name) do
StringFilter.new(filters, params[name])
end
private name
helper_method name
end
end
require "spec_helper"
require "string_filter"
describe StringFilter, "validating current value" do
it "allows known values" do
-> { StringFilter.new([:foo], "foo") }.should_not raise_exception
end
it "allows nil" do
-> { StringFilter.new([:foo], nil) }.should_not raise_exception
end
it "allows the empty string" do
-> { StringFilter.new([:foo], "") }.should_not raise_exception
end
it "disallows unknown values" do
-> {
StringFilter.new([:foo], "bar")
}.should raise_error(StringFilter::UnsupportedValueError)
end
end
describe StringFilter, "dynamic query methods" do
specify "they are true when the current value matches" do
StringFilter.new([:foo, :bar], "foo").foo?.should be_true
end
specify "they are false when the current value does not match" do
StringFilter.new([:foo, :bar], "foo").bar?.should be_false
end
end
describe StringFilter, "#value" do
specify "is the same as the current value as a symbol" do
StringFilter.new([:foo, :bar], "foo").value.should == :foo
end
specify "is nil when the current value is nil" do
StringFilter.new([:foo, :bar], nil).value.should be_nil
end
specify "is nil when the current value is the empty string" do
StringFilter.new([:foo, :bar], "").value.should be_nil
end
end
describe StringFilter, "#values" do
specify "lists values" do
StringFilter.new([:foo, :bar], "foo").values.should == [:foo, :bar]
end
end
describe StringFilter, "#blank_and_values" do
specify "lists nil and values" do
StringFilter.new([:foo, :bar], "foo").blank_and_values.should == [nil, :foo, :bar]
end
end
describe StringFilter, "dynamic value methods" do
specify "they return a symbol of that value" do
StringFilter.new([:foo], nil).foo.should == :foo
end
end
describe StringFilter, "#blank" do
it "returns nil" do
StringFilter.new([:foo], "foo").blank.should == nil
end
end
describe StringFilter, "#blank?" do
it "is true when the current value is nil" do
StringFilter.new([:foo], nil).blank?.should be_true
end
it "is true when the current value is the empty string" do
StringFilter.new([:foo], "").blank?.should be_true
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment