diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3467b..b510075 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Add params method to beer. Allows passing additional parameters in an additional way. * Add styles method & class. Allows querying of the /styles route * Add style method & class. Allows querying of the /style/:id route +# Add search method & class. Allows querying of the /search route ## v0.0.1 * Support for querying a beer diff --git a/lib/tankard/api/search.rb b/lib/tankard/api/search.rb new file mode 100644 index 0000000..e3a5d7c --- /dev/null +++ b/lib/tankard/api/search.rb @@ -0,0 +1,88 @@ +require 'hashie' +require 'tankard/api/utils/page_finders' + +module Tankard + module Api + class Search + include ::Enumerable + include Tankard::Api::Utils::PageFinders + + def initialize(request, options={}) + @request = request + @options = Hashie::Mash.new(options) + end + + def each(&block) + raise_if_required_options_not_set + + if options_have_page_set + find_on_single_page(uri_from_options_endpoint, @request, @options, block) + else + find_on_all_pages(uri_from_options_endpoint, @request, @options, block) + end + end + + def query(search_query) + @options.q = search_query + self + end + + def page(number) + @options.p = number + self + end + + def params(options={}) + options.each_pair do |key,value| + @options[key] = value + end + self + end + + def type(search_type) + @options.type = search_type + self + end + + def upc(upc_code) + @options.code = upc_code + @options.endpoint = "upc" + self + end + + def geo_point(latitude, longitude) + @options.lat = latitude + @options.lng = longitude + @options.endpoint = "geo/point" + self + end + + private + + def options_have_page_set + @options.has_key?(:p) + end + + def uri_from_options_endpoint + endpoint = "search" + + if @options.endpoint? + endpoint += "/#{@options.delete(:endpoint)}" + end + + endpoint + end + + def raise_if_required_options_not_set + case @options.endpoint + when nil + raise Tankard::Error::NoSearchQuery unless @options.q? + when "upc" + raise Tankard::Error::MissingParameter, "missing parameter: code" unless @options.code? + when "geo/point" + raise Tankard::Error::MissingParameter, "missing Parameters: lat, lng" unless @options.lat? && @options.lng? + end + end + end + end +end \ No newline at end of file diff --git a/lib/tankard/client.rb b/lib/tankard/client.rb index 145a74b..b07b0e4 100644 --- a/lib/tankard/client.rb +++ b/lib/tankard/client.rb @@ -1,6 +1,7 @@ require 'tankard/request' require 'tankard/api/beer' require 'tankard/api/beers' +require 'tankard/api/search' require 'tankard/api/styles' require 'tankard/api/style' @@ -23,6 +24,10 @@ def beers(options={}) Tankard::Api::Beers.new(@tankard_request, options) end + def search(options={}) + Tankard::Api::Search.new(@tankard_request, options) + end + def styles Tankard::Api::Styles.new(@tankard_request) end diff --git a/lib/tankard/error.rb b/lib/tankard/error.rb index c50930b..b61c687 100644 --- a/lib/tankard/error.rb +++ b/lib/tankard/error.rb @@ -3,6 +3,8 @@ module Error ConfigurationError = Class.new(::StandardError) NoBeerId = Class.new(::StandardError) NoStyleId = Class.new(::StandardError) + NoSearchQuery = Class.new(::StandardError) + MissingParameter = Class.new(::StandardError) HttpError = Class.new(::StandardError) LoadError = Class.new(::StandardError) InvalidResponse = Class.new(::StandardError) diff --git a/spec/tankard/api/search_spec.rb b/spec/tankard/api/search_spec.rb new file mode 100644 index 0000000..fd0623e --- /dev/null +++ b/spec/tankard/api/search_spec.rb @@ -0,0 +1,212 @@ +require 'spec_helper' + +describe Tankard::Api::Search do + + let(:search) { Tankard::Api::Search.new(@request) } + + before do + @request = mock("request") + end + + describe "#query" do + + it "sets options[:q] with the query the user wants to run" do + search.query("test") + search_options = search.instance_variable_get(:"@options") + expect(search_options[:q]).to eql("test") + end + + it "returns itself" do + expect(search.object_id).to eql(search.query("test").object_id) + end + + end + + describe "#params" do + + it "sets parameters" do + search.params(withSocialAccounts: "y", withGuilds: "n") + search_options = search.instance_variable_get(:"@options") + expect(search_options[:withSocialAccounts]).to eql("y") + expect(search_options[:withGuilds]).to eql("n") + end + + it "merges params when called multiple times" do + search.params(test: "n") + search.params(test: "y") + search_options = search.instance_variable_get(:"@options") + expect(search_options[:test]).to eql("y") + end + + it "returns itself" do + expect(search.object_id).to eql(search.params.object_id) + end + end + + describe "#type" do + + it "sets options[:type] with the type to search for" do + search.type("beer") + search_options = search.instance_variable_get(:"@options") + expect(search_options[:type]).to eql("beer") + end + + it "returns itself" do + expect(search.object_id).to eql (search.type("test").object_id) + end + end + + describe "#page" do + + it "sets options[:p] with the page number to load" do + search.page(1) + search_options = search.instance_variable_get(:"@options") + expect(search_options[:p]).to eql(1) + end + + it "returns itself" do + expect(search.object_id).to eql(search.page(1).object_id) + end + end + + describe "#upc" do + + before do + search.upc("123") + @search_options = search.instance_variable_get(:"@options") + end + + it "sets options[:endpoint] with the search endpoint to use" do + expect(@search_options[:endpoint]).to eql("upc") + end + + it "sets options[:code] with the upc code" do + expect(@search_options[:code]).to eql("123") + end + + it "returns itself" do + expect(search.object_id).to eql(search.upc("123").object_id) + end + end + + describe "#geo_point" do + + before do + search.geo_point(1.23, 4.56) + @search_options = search.instance_variable_get(:"@options") + end + + it "sets options[:endpoint] with the correct search endpoint to use" do + expect(@search_options[:endpoint]).to eql("geo/point") + end + + it "sets options[:lat] with the latitude" do + expect(@search_options[:lat]).to eql(1.23) + end + + it "sets options[:lng] with the longitude" do + expect(@search_options[:lng]).to eql(4.56) + end + + it "returns itself" do + expect(search.object_id).to eql(search.geo_point(1.3, 4.5).object_id) + end + end + + describe "when making a request" do + + context "and a page is set" do + + it "only queries a single page" do + search.should_receive(:find_on_single_page) + search.query("test").page(1).each { |x| x } + end + end + + context "and a page is not set" do + + it "queries multiple pages" do + search.should_receive(:find_on_all_pages) + search.query("test").each { |x| x } + end + end + + context "and the search query is not set" do + + it "raises Tankard::Error::NoSearchQuery" do + expect { search.each { |s| p s } }.to raise_error(Tankard::Error::NoSearchQuery) + end + end + + context "and the search query is set" do + + before do + @request.stub(:get).with("search", Hashie::Mash.new(p: 1, q: "test")).and_return("data" => ["search_result"]) + end + + it "uses the query in the request parameters" do + expect(search.query("test").collect { |x| x }).to eql(["search_result"]) + end + end + + context "the endpoint is set" do + + before do + @request.stub(:get).with("search/upc", Hashie::Mash.new(code: "123", p: 1)).and_return({ "data" => ["search_result"] }) + end + + it "adds the endpoint to the request" do + expect(search.upc("123").collect { |x| x }).to eql(["search_result"]) + end + end + + context "the upc endpoint is set without the code parameter" do + + it "raises Tankard::Error::MissingParameter" do + expect { search.params(endpoint: "upc").collect { |x| x } }. to raise_error(Tankard::Error::MissingParameter) + end + end + + context "the geo/point endpoint isset without lat or lng parameters" do + + it "raises Tankard::Error::MissingParameter when lat is set but lng isnt" do + expect { search.params(endpoint: "geo/point", lat: 123).collect { |x| x } }.to raise_error(Tankard::Error::MissingParameter) + end + + it "raises Tankard::Error::MissingParameter when lng is set but lat isnt" do + expect { search.params(endpoint: "geo/point", lng: 2).collect { |x| x } }.to raise_error(Tankard::Error::MissingParameter) + end + + it "raises Tankard::Error::MissingParameter when lat and lng are not set" do + expect { search.params(endpoint: "geo/point").collect { |x| x } }.to raise_error(Tankard::Error::MissingParameter) + end + end + + context "additional options are set" do + + before do + @search_with_options = Tankard::Api::Search.new(@request, test: "123", p: 1, q: "search") + @request.stub(:get).with("search", Hashie::Mash.new(test: "123", p: 1, q: "search")).and_return({ "data" => ["searched"] }) + end + + it "passes them to the request" do + expect(@search_with_options.collect { |x| x }).to eql(["searched"]) + end + end + + context "and no data is returned on a single page request" do + + before do + @request.stub(:get).with("search", Hashie::Mash.new(q: "test", p: 1)).and_return({}) + end + + it "raises Tankard::Error::InvalidResponse" do + expect { search.query("test").page(1).collect { |x| x } }.to raise_error(Tankard::Error::InvalidResponse) + end + end + + context "and no data is returned on a multi page request" do + + end + end +end \ No newline at end of file diff --git a/spec/tankard/client_spec.rb b/spec/tankard/client_spec.rb index 3af4dfc..ef9d484 100644 --- a/spec/tankard/client_spec.rb +++ b/spec/tankard/client_spec.rb @@ -52,6 +52,30 @@ end end + describe "#search" do + + context "when called" do + + it "does not reuse an existing search object" do + search = client.search + expect(search.object_id).not_to eql(client.search.object_id) + end + end + + context "when passed a hash of options" do + + before do + @request = mock("request") + Tankard::Request.stub!(:new).and_return(@request) + end + + it "passes the options to the search object" do + Tankard::Api::Search.should_receive(:new).with(@request, { test: 123 }) + client.search({ test: 123 }) + end + end + end + describe "#styles" do context "when called" do