Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cached_find_by(attribute: value) methods #15

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions active_remote-cached.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency "mocha"
gem.add_development_dependency "pry"
gem.add_development_dependency "rake"
gem.add_development_dependency "rspec"
end
20 changes: 20 additions & 0 deletions lib/active_remote/cached.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ def self.default_options(options = nil)
end

module ClassMethods
def cached_find_by(**args)
cache_options = args.delete(:cache_options) || {}
find_method_name = _cached_search_method_name(args.keys)
raise "cached_finders_for #{args.keys.map(&:to_sym)} not included in class definition" unless respond_to?(find_method_name)
send(find_method_name, args.values, cache_options).first
end

def cached_find_by!(**args)
result = self.cached_find_by(args)
raise ::ActiveRemote::RemoteRecordNotFound, self if result.nil?
result
end

def cached_exist_find_by?(**args)
cache_options = args.delete(:cache_options) || {}
exist_by_method_name = _cached_exist_search_method_name(args.keys) # ? is not necessary as the method is aliased both ways
raise "cached_finders_for #{args.keys.map(&:to_sym)} not included in class definition" unless respond_to?(exist_by_method_name)
send(exist_by_method_name, args.values, cache_options)
end

def cached_finders_for(*cached_finder_keys)
options = cached_finder_keys.extract_options!

Expand Down
160 changes: 160 additions & 0 deletions spec/active_remote/cached_find_by_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
require 'spec_helper'

class FindByMethodClass
include ::ActiveRemote::Cached

def self.find(**args); nil; end;
def self.search(**args); nil; end;

cached_finders_for :foo, :expires_in => 500
cached_finders_for :guid
cached_finders_for :guid, :user_guid
cached_finders_for [:user_guid, :client_guid]
cached_finders_for [:derp, :user_guid, :client_guid]
end

describe FindByMethodClass do
describe "API" do

context "cached_find_by" do
it "creates 'cached_find_by'" do
expect(FindByMethodClass).to respond_to(:cached_find_by)
end

it "raises an error if the cached_finders_for does not exist for a single attribute cached_find_by" do
expect do
FindByMethodClass.cached_find_by(not_an_attribute: "foo")
end.to raise_error("cached_finders_for [:not_an_attribute] not included in class definition")
end

it "raises an error if the cached_finders_for does not exist for a multiple attribute cached_find_by" do
expect do
FindByMethodClass.cached_find_by(not_an_attribute: "foo", other_not_an_attribute: "bar")
end.to raise_error("cached_finders_for [:not_an_attribute, :other_not_an_attribute] not included in class definition")
end
end

context "cached_find_by" do
it "creates 'cached_find_by!'" do
expect(FindByMethodClass).to respond_to(:cached_find_by!)
end

it "raises an error if the cached_finders_for does not exist for a single attribute cached_find_by!" do
expect do
FindByMethodClass.cached_find_by!(not_an_attribute: "foo")
end.to raise_error("cached_finders_for [:not_an_attribute] not included in class definition")
end

it "raises an error if the cached_finders_for does not exist for a multiple attribute cached_find_by!" do
expect do
FindByMethodClass.cached_find_by!(not_an_attribute: "foo", other_not_an_attribute: "bar")
end.to raise_error("cached_finders_for [:not_an_attribute, :other_not_an_attribute] not included in class definition")
end
end


context "cached_exist_find_by?" do
it "creates 'cached_exist_find_by?'" do
expect(FindByMethodClass).to respond_to(:cached_exist_find_by?)
end

it "raises an error if the cached_finders_for does not exist for a single attribute cached_exist_find_by" do
expect do
FindByMethodClass.cached_exist_find_by?(not_an_attribute: "foo")
end.to raise_error("cached_finders_for [:not_an_attribute] not included in class definition")
end

it "raises an error if the cached_finders_for does not exist for a multiple attribute cached_exist_find_by?" do
expect do
FindByMethodClass.cached_exist_find_by?(not_an_attribute: "foo", other_not_an_attribute: "bar")
end.to raise_error("cached_finders_for [:not_an_attribute, :other_not_an_attribute] not included in class definition")
end
end
end

describe "#cached_find_by(:attribute => value)" do
context "guid" do
it "calls cached_find_by_guid and returns 1" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {}).and_return([1])
expect(FindByMethodClass.cached_find_by(:guid => "foobar")).to eq(1)
end

it "calls cached_find_by_guid and nothing found" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {}).and_return([])
expect(FindByMethodClass.cached_find_by(:guid => "foobar")).to be nil
end

it "calls cached_find_by_guid and nothing found does not raise an error" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {}).and_return([])
expect do
FindByMethodClass.cached_find_by(:guid => "foobar")
end.not_to raise_error
end

it "calls cached_find_by_guid with cache options" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], { :expires_in => 1 }).and_return([1])
expect(FindByMethodClass.cached_find_by(:guid => "foobar", :cache_options => { :expires_in => 1 })).to eq(1)
end
end

context "user_guid, client_guid" do
it "calls cached_find_by_guid and returns 1" do
expect(FindByMethodClass).to receive(:cached_search_by_user_guid_and_client_guid).with(["foo", "bar"], {}).and_return([1])
expect(FindByMethodClass.cached_find_by(:user_guid => "foo", :client_guid => "bar")).to eq(1)
end

it "calls cached_search_by_user_guid_and_client_guid with options returns 1" do
expect(FindByMethodClass).to receive(:cached_search_by_user_guid_and_client_guid).with(["foo", "bar"], { :expires_in => 1 }).and_return([1])
expect(FindByMethodClass.cached_find_by(:user_guid => "foo", :client_guid => "bar", :cache_options => { :expires_in => 1 })).to eq(1)
end

it "calls cached_search_by_user_guid_and_client_guid with out of order attribute keys" do
# cached_search_by_client_guid_and_user_guid is inverse of `cached_finders_for [:user_guid, :client_guid]` because it permutes
# all possible combinations of the attributes
expect(FindByMethodClass).to receive(:cached_search_by_client_guid_and_user_guid).with(["bar", "foo"], {}).and_return([1])
expect(FindByMethodClass.cached_find_by(:client_guid => "bar", :user_guid => "foo")).to eq(1)
end

it "calls cached_find_by_guid and nothing found" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {}).and_return([])
expect(FindByMethodClass.cached_find_by(:guid => "foobar")).to be nil
end

it "calls cached_find_by_guid and nothing found does not raise an error" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {}).and_return([])
expect do
FindByMethodClass.cached_find_by(:guid => "foobar")
end.not_to raise_error
end
end

context "derp, user_guid, client_guid" do
it "calls cached_search_by_user_guid_and_client_guid with out of order attribute keys" do
expect(FindByMethodClass).to receive(:cached_search_by_derp_and_user_guid_and_client_guid).with(["foo", "bar", "baz"], {}).and_return([1])
expect(FindByMethodClass.cached_find_by(:derp => "foo", :user_guid => "bar", :client_guid => "baz")).to eq(1)
end
end
end

describe "#cached_find_by!(:attribute => value)" do
context "guid" do
it "calls cached_find_by_guid! and returns 1" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {}).and_return([1])
expect(FindByMethodClass.cached_find_by!(:guid => "foobar")).to eq(1)
end

it "calls cached_find_by_guid! and nothing found to raise an error" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {}).and_return([])
expect do
FindByMethodClass.cached_find_by!(:guid => "foobar")
end.to raise_error(::ActiveRemote::RemoteRecordNotFound)
end

it "calls cached_find_by_guid! with cache options" do
expect(FindByMethodClass).to receive(:cached_search_by_guid).with(["foobar"], {:expires_in => 500}).and_return([1])
FindByMethodClass.cached_find_by!(:guid => "foobar", :cache_options => {:expires_in => 500})
end
end
end

end