From c17bdba8d138c695b96d31e80048a5aac47d12a6 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Wed, 11 Jul 2012 14:41:01 -0700 Subject: [PATCH 01/30] Script to use custom api keys without having to commit them --- .gitignore | 1 + Rakefile | 18 ++++++++++++++++++ spec/default_api_credentials.rb | 6 ++++++ 3 files changed, 25 insertions(+) create mode 100644 spec/default_api_credentials.rb diff --git a/.gitignore b/.gitignore index 4f35372..3c74ae1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ Gemfile.lock .rvmrc *.sw* *.orig +spec/api_credentials.rb diff --git a/Rakefile b/Rakefile index e114e86..ba22c3a 100644 --- a/Rakefile +++ b/Rakefile @@ -10,3 +10,21 @@ Rake::TestTask.new do |t| t.test_files = FileList["test/**/*_test.rb"] t.verbose = true end + +desc "Cleans out the api credentials from the code" +task :clean do + puts "Cleaning out api credentials from project...." + require File.join(File.dirname(__FILE__), 'spec/api_credentials') + + `find spec/cassettes/*.yml -exec sed -i '' 's/#{API_KEY}/API_KEY/' {} \\;` + puts "DONE" +end + +desc "Restores the api keys from the api_credentials file" +task :unclean do + puts "Restoring credentials" + require File.join(File.dirname(__FILE__), 'spec/api_credentials') + + `find spec/cassettes/*.yml -exec sed -i '' 's/API_KEY/#{API_KEY}/' {} \\;` + puts "DONE" +end diff --git a/spec/default_api_credentials.rb b/spec/default_api_credentials.rb new file mode 100644 index 0000000..cecb18f --- /dev/null +++ b/spec/default_api_credentials.rb @@ -0,0 +1,6 @@ +custom_credentials = File.join(File.dirname(__FILE__), "api_credentials.rb") +if File.exists? custom_credentials + require custom_credentials +else + API_KEY = "API_KEY" +end From 6c2144027b2ccd64de458e0278f3ca71d8fc3ec7 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Wed, 11 Jul 2012 14:42:59 -0700 Subject: [PATCH 02/30] Start of an Etsy::Client class beginning to implement Etsy module level methods here and replacing complicated Net::HTTP logic with Faraday --- etsy.gemspec | 8 ++++++-- lib/etsy.rb | 4 ++++ lib/etsy/client.rb | 36 ++++++++++++++++++++++++++++++++++ spec/cassettes/Etsy_Client.yml | 36 ++++++++++++++++++++++++++++++++++ spec/client_spec.rb | 32 ++++++++++++++++++++++++++++++ spec/spec_helper.rb | 17 ++++++++++++++++ 6 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 lib/etsy/client.rb create mode 100644 spec/cassettes/Etsy_Client.yml create mode 100644 spec/client_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/etsy.gemspec b/etsy.gemspec index 81d2616..0d37ae6 100644 --- a/etsy.gemspec +++ b/etsy.gemspec @@ -20,9 +20,13 @@ Gem::Specification.new do |gem| gem.add_dependency "json", ">= 1.5.0" gem.add_dependency "oauth", "~> 0.4.0" + gem.add_dependency "faraday", "~> 0.8.0" + gem.add_dependency "faraday_middleware", "~> 0.8.8" gem.add_development_dependency "rake", "~> 0.9.2.2" gem.add_development_dependency "jnunemaker-matchy", "~> 0.4.0" - gem.add_development_dependency 'shoulda', '~> 3.0.0' - gem.add_development_dependency 'mocha', '~> 0.12.0' + gem.add_development_dependency "shoulda", "~> 3.0.0" + gem.add_development_dependency "mocha", "~> 0.12.0" + gem.add_development_dependency "rspec", "~> 2.11.0" + gem.add_development_dependency "vcr", "~> 2.2.3" end diff --git a/lib/etsy.rb b/lib/etsy.rb index c298d9a..473df06 100644 --- a/lib/etsy.rb +++ b/lib/etsy.rb @@ -1,10 +1,14 @@ $:.unshift File.dirname(__FILE__) require 'net/http' +require 'faraday' +require 'faraday_middleware' require 'json' require 'oauth' require 'uri' +require 'etsy/client' + require 'etsy/request' require 'etsy/response' diff --git a/lib/etsy/client.rb b/lib/etsy/client.rb new file mode 100644 index 0000000..e1d652b --- /dev/null +++ b/lib/etsy/client.rb @@ -0,0 +1,36 @@ +module Etsy + class Client + def initialize(options={}) + @api_key = options[:api_key] + end + + def user(username) + get("users", :keywords => username).body["results"].first + end + + def get(path, options={}) + connection.get(path, options) + end + + private + + def connection + @connection ||= Faraday.new( + :url => "http://openapi.etsy.com/v2", + :params => default_params + ) do |conn| + conn.request :json + conn.response :json, :content_type => /\bjson$/ + + conn.adapter Faraday.default_adapter + end + end + + def default_params + params = {} + params[:api_key] = @api_key if @api_key + + params + end + end +end diff --git a/spec/cassettes/Etsy_Client.yml b/spec/cassettes/Etsy_Client.yml new file mode 100644 index 0000000..e97cdd4 --- /dev/null +++ b/spec/cassettes/Etsy_Client.yml @@ -0,0 +1,36 @@ +--- +http_interactions: +- request: + method: get + uri: http://openapi.etsy.com/v2/users?api_key=API_KEY&keywords=jonS2011 + body: + encoding: US-ASCII + string: '' + headers: {} + response: + status: + code: 200 + message: + headers: + date: + - Wed, 11 Jul 2012 20:01:25 GMT + server: + - Apache + set-cookie: + - etala=259736404.859312951.1342036885.1342036885.1342036885.1.0; expires=Sat, + 12-Jul-2014 07:38:57 GMT; path=/; domain=.etsy.com, etalb=259736404.1.10.1342036885; + expires=Wed, 11-Jul-2012 20:31:25 GMT; path=/; domain=.etsy.com + x-etsy-request-uuid: + - X2QRoCqywu18qCJRQhN3m7prM_8n + content-length: + - '355' + connection: + - close + content-type: + - application/json + body: + encoding: US-ASCII + string: ! '{"count":1,"results":[{"user_id":17974443,"login_name":"jonS2011","creation_tsz":1322586362,"referred_by_user_id":null,"feedback_info":{"count":0,"score":null}}],"params":{"keywords":"jonS2011","limit":25,"offset":0,"page":null},"type":"User","pagination":{"effective_limit":25,"effective_offset":0,"next_offset":null,"effective_page":1,"next_page":null}}' + http_version: + recorded_at: Wed, 11 Jul 2012 20:01:26 GMT +recorded_with: VCR 2.2.3 diff --git a/spec/client_spec.rb b/spec/client_spec.rb new file mode 100644 index 0000000..42bbca7 --- /dev/null +++ b/spec/client_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +module Etsy + describe Client do + use_vcr_cassette + + subject { Client.new(:api_key => API_KEY) } + + it "should be able to find a user" do + subject.user("jonS2011").should == { + "user_id" => 17974443, + "login_name" => "jonS2011", + "creation_tsz" => 1322586362, + "referred_by_user_id" => nil, + "feedback_info" => { + "count" => 0, + "score" => nil + } + } + end + + it "should be able to get an api endpoint" do + subject.get('users', :keywords => "jonS2011").body.should == { + "count" => 1, + "pagination" => {"effective_limit"=>25, "effective_offset"=>0, "next_offset"=>nil, "effective_page"=>1, "next_page"=>nil}, + "params" => {"keywords"=>"jonS2011", "limit"=>25, "offset"=>0, "page"=>nil}, + "results" => [{"user_id"=>17974443, "login_name"=>"jonS2011", "creation_tsz"=>1322586362, "referred_by_user_id"=>nil, "feedback_info"=>{"count"=>0, "score"=>nil}}], + "type" => "User" + } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..f8b52de --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,17 @@ +require 'etsy' +require 'vcr' + +require 'default_api_credentials' + +VCR.configure do |c| + c.cassette_library_dir = 'spec/cassettes' + c.hook_into :faraday +end + +RSpec.configure do |c| + c.extend VCR::RSpec::Macros + c.color_enabled = true + c.tty = true + c.formatter = :documentation + c.mock_framework = :mocha +end From 63bfe73e6a5127fee2ddfc9e32cf5064e116d553 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Wed, 11 Jul 2012 14:44:45 -0700 Subject: [PATCH 03/30] Changing Rakefile output a little --- Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index ba22c3a..7b95805 100644 --- a/Rakefile +++ b/Rakefile @@ -13,7 +13,7 @@ end desc "Cleans out the api credentials from the code" task :clean do - puts "Cleaning out api credentials from project...." + print "Cleaning out api credentials from project.... " require File.join(File.dirname(__FILE__), 'spec/api_credentials') `find spec/cassettes/*.yml -exec sed -i '' 's/#{API_KEY}/API_KEY/' {} \\;` @@ -22,7 +22,7 @@ end desc "Restores the api keys from the api_credentials file" task :unclean do - puts "Restoring credentials" + print "Restoring credentials.... " require File.join(File.dirname(__FILE__), 'spec/api_credentials') `find spec/cassettes/*.yml -exec sed -i '' 's/API_KEY/#{API_KEY}/' {} \\;` From cbdf30b5d762ca993f404f3e825605eb0b8df22c Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sat, 14 Jul 2012 07:42:02 -0700 Subject: [PATCH 04/30] Make specs run in default task also MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I know two test suites right now, but it'll get better… --- Rakefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 7b95805..8c27cbc 100644 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1,10 @@ #!/usr/bin/env rake require 'rubygems' require 'rake/testtask' +require 'rspec/core/rake_task' require "bundler/gem_tasks" -task :default => :test +task :default => [:test, :spec] Rake::TestTask.new do |t| t.libs << 'test' @@ -11,6 +12,11 @@ Rake::TestTask.new do |t| t.verbose = true end +desc "Run specs" +RSpec::Core::RakeTask.new do |t| + t.pattern = "./spec/**/*_spec.rb" +end + desc "Cleans out the api credentials from the code" task :clean do print "Cleaning out api credentials from project.... " From c256775f1bb9b3ac11f1d1f39219b207032bd053 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sat, 14 Jul 2012 07:42:20 -0700 Subject: [PATCH 05/30] Adding SimpleCov coverage --- .gitignore | 1 + etsy.gemspec | 1 + spec/spec_helper.rb | 7 +++++-- test/test_helper.rb | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3c74ae1..7b127a2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ Gemfile.lock *.sw* *.orig spec/api_credentials.rb +coverage/ diff --git a/etsy.gemspec b/etsy.gemspec index 0d37ae6..24ffedc 100644 --- a/etsy.gemspec +++ b/etsy.gemspec @@ -29,4 +29,5 @@ Gem::Specification.new do |gem| gem.add_development_dependency "mocha", "~> 0.12.0" gem.add_development_dependency "rspec", "~> 2.11.0" gem.add_development_dependency "vcr", "~> 2.2.3" + gem.add_development_dependency "simplecov", "~> 0.6.4" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f8b52de..d2bc90f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,11 @@ -require 'etsy' -require 'vcr' +require 'simplecov' +SimpleCov.start +require 'vcr' require 'default_api_credentials' +require 'etsy' + VCR.configure do |c| c.cassette_library_dir = 'spec/cassettes' c.hook_into :faraday diff --git a/test/test_helper.rb b/test/test_helper.rb index 83dd406..09eacef 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,6 +2,8 @@ $:.reject! { |e| e.include? 'TextMate' } require 'rubygems' +require 'simplecov' +SimpleCov.start require 'test/unit' require 'shoulda' require 'matchy' From fb2155496e010430b7b16f997de9ac25d56e0ba6 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sat, 14 Jul 2012 10:18:04 -0700 Subject: [PATCH 06/30] Fix failure from jruby, need openssl gem --- etsy.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/etsy.gemspec b/etsy.gemspec index 24ffedc..49c60f2 100644 --- a/etsy.gemspec +++ b/etsy.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |gem| gem.add_dependency "oauth", "~> 0.4.0" gem.add_dependency "faraday", "~> 0.8.0" gem.add_dependency "faraday_middleware", "~> 0.8.8" + gem.add_dependency "jruby-openssl", "~> 0.7.7" if RUBY_PLATFORM == 'java' gem.add_development_dependency "rake", "~> 0.9.2.2" gem.add_development_dependency "jnunemaker-matchy", "~> 0.4.0" From ae71d9a406c255f87673e2a32eca47a9b2e315cf Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sat, 14 Jul 2012 10:36:20 -0700 Subject: [PATCH 07/30] Match requests on parsed query strings rather than URI, which can be different across platforms --- spec/spec_helper.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d2bc90f..a3cee24 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,8 +6,15 @@ require 'etsy' +require 'uri' +require 'cgi' + VCR.configure do |c| c.cassette_library_dir = 'spec/cassettes' + c.register_request_matcher :parsed_query do |req_1, req_2| + CGI.parse(URI(req_1.uri).query) == CGI.parse(URI(req_2.uri).query) + end + c.default_cassette_options = { :match_requests_on => [:method, :host, :path, :parsed_query] } c.hook_into :faraday end From 58a89e57b38f22616e94e0205a866448b754956b Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sat, 14 Jul 2012 10:40:24 -0700 Subject: [PATCH 08/30] Turn of simplecov in travis to avoid issues with 1.9 mode on rbx --- spec/spec_helper.rb | 6 ++++-- test/test_helper.rb | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a3cee24..96dbdfb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ -require 'simplecov' -SimpleCov.start +if ENV["TRAVIS"] != "true" + require 'simplecov' + SimpleCov.start +end require 'vcr' require 'default_api_credentials' diff --git a/test/test_helper.rb b/test/test_helper.rb index 09eacef..77d9e77 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,8 +2,12 @@ $:.reject! { |e| e.include? 'TextMate' } require 'rubygems' -require 'simplecov' -SimpleCov.start + +if ENV["TRAVIS"] != "true" + require 'simplecov' + SimpleCov.start +end + require 'test/unit' require 'shoulda' require 'matchy' From fef0eddc4ab98f58a492cb3532e4fa050fcd9799 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sat, 14 Jul 2012 10:55:03 -0700 Subject: [PATCH 09/30] Use VCR's builtin filtering for keys --- Rakefile | 18 ------------------ spec/api_credentials.rb | 5 +++++ spec/cassettes/Etsy_Client.yml | 12 ++++++------ spec/client_spec.rb | 2 +- spec/default_api_credentials.rb | 6 ------ spec/spec_helper.rb | 9 ++++++++- 6 files changed, 20 insertions(+), 32 deletions(-) create mode 100644 spec/api_credentials.rb delete mode 100644 spec/default_api_credentials.rb diff --git a/Rakefile b/Rakefile index 8c27cbc..2ddf787 100644 --- a/Rakefile +++ b/Rakefile @@ -16,21 +16,3 @@ desc "Run specs" RSpec::Core::RakeTask.new do |t| t.pattern = "./spec/**/*_spec.rb" end - -desc "Cleans out the api credentials from the code" -task :clean do - print "Cleaning out api credentials from project.... " - require File.join(File.dirname(__FILE__), 'spec/api_credentials') - - `find spec/cassettes/*.yml -exec sed -i '' 's/#{API_KEY}/API_KEY/' {} \\;` - puts "DONE" -end - -desc "Restores the api keys from the api_credentials file" -task :unclean do - print "Restoring credentials.... " - require File.join(File.dirname(__FILE__), 'spec/api_credentials') - - `find spec/cassettes/*.yml -exec sed -i '' 's/API_KEY/#{API_KEY}/' {} \\;` - puts "DONE" -end diff --git a/spec/api_credentials.rb b/spec/api_credentials.rb new file mode 100644 index 0000000..7c8d410 --- /dev/null +++ b/spec/api_credentials.rb @@ -0,0 +1,5 @@ +module Etsy + module Test + API_KEY = "API_KEY" + end +end diff --git a/spec/cassettes/Etsy_Client.yml b/spec/cassettes/Etsy_Client.yml index e97cdd4..8d0301e 100644 --- a/spec/cassettes/Etsy_Client.yml +++ b/spec/cassettes/Etsy_Client.yml @@ -13,15 +13,15 @@ http_interactions: message: headers: date: - - Wed, 11 Jul 2012 20:01:25 GMT + - Sat, 14 Jul 2012 17:50:58 GMT server: - Apache set-cookie: - - etala=259736404.859312951.1342036885.1342036885.1342036885.1.0; expires=Sat, - 12-Jul-2014 07:38:57 GMT; path=/; domain=.etsy.com, etalb=259736404.1.10.1342036885; - expires=Wed, 11-Jul-2012 20:31:25 GMT; path=/; domain=.etsy.com + - etala=259736404.304634992.1342288258.1342288258.1342288258.1.0; expires=Tue, + 15-Jul-2014 05:28:30 GMT; path=/; domain=.etsy.com, etalb=259736404.1.10.1342288258; + expires=Sat, 14-Jul-2012 18:20:58 GMT; path=/; domain=.etsy.com x-etsy-request-uuid: - - X2QRoCqywu18qCJRQhN3m7prM_8n + - 5rZUuxJe6Ho0Cu2ukDddNkAuNHHo content-length: - '355' connection: @@ -32,5 +32,5 @@ http_interactions: encoding: US-ASCII string: ! '{"count":1,"results":[{"user_id":17974443,"login_name":"jonS2011","creation_tsz":1322586362,"referred_by_user_id":null,"feedback_info":{"count":0,"score":null}}],"params":{"keywords":"jonS2011","limit":25,"offset":0,"page":null},"type":"User","pagination":{"effective_limit":25,"effective_offset":0,"next_offset":null,"effective_page":1,"next_page":null}}' http_version: - recorded_at: Wed, 11 Jul 2012 20:01:26 GMT + recorded_at: Sat, 14 Jul 2012 17:51:00 GMT recorded_with: VCR 2.2.3 diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 42bbca7..920b0c7 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -4,7 +4,7 @@ module Etsy describe Client do use_vcr_cassette - subject { Client.new(:api_key => API_KEY) } + subject { Client.new(:api_key => Etsy::Test::API_KEY) } it "should be able to find a user" do subject.user("jonS2011").should == { diff --git a/spec/default_api_credentials.rb b/spec/default_api_credentials.rb deleted file mode 100644 index cecb18f..0000000 --- a/spec/default_api_credentials.rb +++ /dev/null @@ -1,6 +0,0 @@ -custom_credentials = File.join(File.dirname(__FILE__), "api_credentials.rb") -if File.exists? custom_credentials - require custom_credentials -else - API_KEY = "API_KEY" -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 96dbdfb..0688c8e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,7 +4,7 @@ end require 'vcr' -require 'default_api_credentials' +require 'api_credentials' require 'etsy' @@ -17,6 +17,13 @@ CGI.parse(URI(req_1.uri).query) == CGI.parse(URI(req_2.uri).query) end c.default_cassette_options = { :match_requests_on => [:method, :host, :path, :parsed_query] } + + # Add sensitive fields here + # Constants in Etsy::Test will be auto filtered + Etsy::Test.constants.each do |const| + c.filter_sensitive_data(const.to_s) { Etsy::Test.const_get(const) } + end + c.hook_into :faraday end From 49c9bbfa179b81efeda19bf18b5ca7d532127638 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sat, 14 Jul 2012 11:02:08 -0700 Subject: [PATCH 10/30] Remove credentials file from ignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7b127a2..1f297a2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,4 @@ Gemfile.lock .rvmrc *.sw* *.orig -spec/api_credentials.rb coverage/ From 31d128b1943651683ae8d02592f66f2abb641018 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Wed, 18 Jul 2012 11:36:23 -0700 Subject: [PATCH 11/30] Pattern for using ENV variables for keys --- spec/api_credentials.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_credentials.rb b/spec/api_credentials.rb index 7c8d410..2c7cdb7 100644 --- a/spec/api_credentials.rb +++ b/spec/api_credentials.rb @@ -1,5 +1,5 @@ module Etsy module Test - API_KEY = "API_KEY" + API_KEY = ENV["ETSY_API_KEY"] || "API_KEY" end end From 32e46ad70372c3aaf5ce9cb076c6b07acf903cbb Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Thu, 19 Jul 2012 08:08:18 -0700 Subject: [PATCH 12/30] Support basic oauth flow, still want to make testing this oauth stuff easier. * Split vcr cassettes into multiple * Added more logic for empty query strings in the 'parsed_query' matcher * Hook vcr into web mock for Net::HTTP and Oauth * Support basic flow for getting an oauth token and secret --- etsy.gemspec | 2 + lib/etsy/client.rb | 34 ++++++- spec/api_credentials.rb | 6 +- .../{Etsy_Client.yml => Etsy_Client/_get.yml} | 14 +-- spec/cassettes/Etsy_Client/_myself.yml | 40 ++++++++ spec/cassettes/Etsy_Client/_user.yml | 36 +++++++ .../oauth_authentication/_oauth_token.yml | 93 ++++++++++++++++++ .../oauth_authentication/_request_token.yml | 48 +++++++++ spec/client_spec.rb | 98 ++++++++++++++----- spec/spec_helper.rb | 15 ++- 10 files changed, 351 insertions(+), 35 deletions(-) rename spec/cassettes/{Etsy_Client.yml => Etsy_Client/_get.yml} (70%) create mode 100644 spec/cassettes/Etsy_Client/_myself.yml create mode 100644 spec/cassettes/Etsy_Client/_user.yml create mode 100644 spec/cassettes/Etsy_Client/oauth_authentication/_oauth_token.yml create mode 100644 spec/cassettes/Etsy_Client/oauth_authentication/_request_token.yml diff --git a/etsy.gemspec b/etsy.gemspec index ea1ac90..f123f83 100644 --- a/etsy.gemspec +++ b/etsy.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |gem| gem.add_dependency "oauth", "~> 0.4.0" gem.add_dependency "faraday", "~> 0.8.0" gem.add_dependency "faraday_middleware", "~> 0.8.8" + gem.add_dependency "simple_oauth", "~> 0.1.8" gem.add_dependency "jruby-openssl", "~> 0.7.7" if RUBY_PLATFORM == 'java' gem.add_development_dependency "rake", "~> 0.9.2.2" @@ -30,5 +31,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency "mocha", "~> 0.12.0" gem.add_development_dependency "rspec", "~> 2.11.0" gem.add_development_dependency "vcr", "~> 2.2.3" + gem.add_development_dependency "webmock", "~> 1.8.7" gem.add_development_dependency "simplecov", "~> 0.6.4" end diff --git a/lib/etsy/client.rb b/lib/etsy/client.rb index e1d652b..b3876bd 100644 --- a/lib/etsy/client.rb +++ b/lib/etsy/client.rb @@ -1,13 +1,36 @@ module Etsy class Client + attr_accessor :oauth_token, :oauth_secret + def initialize(options={}) - @api_key = options[:api_key] + @api_key = options[:api_key] + @api_secret = options[:api_secret] + @oauth_token = options[:oauth_token] + @oauth_secret = options[:oauth_secret] + end + + def myself + get("users/__SELF__").body["results"].first end def user(username) get("users", :keywords => username).body["results"].first end + def request_token + consumer = OAuth::Consumer.new(@api_key, @api_secret, + :site => "http://openapi.etsy.com", + :request_token_path => "/v2/oauth/request_token", + :access_token_path => "/v2/oauth/access_token" + ) + consumer.get_request_token + end + + def oauth_token(req, verifier) + access_token = req.get_access_token :oauth_verifier => verifier + [access_token.token, access_token.secret] + end + def get(path, options={}) connection.get(path, options) end @@ -20,6 +43,13 @@ def connection :params => default_params ) do |conn| conn.request :json + conn.request :oauth, { + :token => @oauth_token, + :token_secret => @oauth_secret, + :consumer_key => @api_key, + :consumer_secret => @api_secret + } if @oauth_token + conn.response :json, :content_type => /\bjson$/ conn.adapter Faraday.default_adapter @@ -28,7 +58,7 @@ def connection def default_params params = {} - params[:api_key] = @api_key if @api_key + params[:api_key] = @api_key if @api_key && !@oauth_token params end diff --git a/spec/api_credentials.rb b/spec/api_credentials.rb index 2c7cdb7..bf595ff 100644 --- a/spec/api_credentials.rb +++ b/spec/api_credentials.rb @@ -1,5 +1,9 @@ module Etsy module Test - API_KEY = ENV["ETSY_API_KEY"] || "API_KEY" + API_KEY = ENV["ETSY_API_KEY"] || "API_KEY" + API_SECRET = ENV["ETSY_API_SECRET"] || "API_SECRET" + OAUTH_TOKEN = ENV["ETSY_OAUTH_TOKEN"] || "OAUTH_TOKEN" + OAUTH_SECRET = ENV["ETSY_OAUTH_SECRET"] || "OAUTH_SECRET" + OAUTH_VERIFIER = ENV["ETSY_OAUTH_VERIFIER"] || "OAUTH_VERIFIER" end end diff --git a/spec/cassettes/Etsy_Client.yml b/spec/cassettes/Etsy_Client/_get.yml similarity index 70% rename from spec/cassettes/Etsy_Client.yml rename to spec/cassettes/Etsy_Client/_get.yml index 8d0301e..6ecb0d7 100644 --- a/spec/cassettes/Etsy_Client.yml +++ b/spec/cassettes/Etsy_Client/_get.yml @@ -13,18 +13,18 @@ http_interactions: message: headers: date: - - Sat, 14 Jul 2012 17:50:58 GMT + - Wed, 18 Jul 2012 23:35:46 GMT server: - Apache set-cookie: - - etala=259736404.304634992.1342288258.1342288258.1342288258.1.0; expires=Tue, - 15-Jul-2014 05:28:30 GMT; path=/; domain=.etsy.com, etalb=259736404.1.10.1342288258; - expires=Sat, 14-Jul-2012 18:20:58 GMT; path=/; domain=.etsy.com + - etala=259736404.363588809.1342654546.1342654546.1342654546.1.0; expires=Sat, + 19-Jul-2014 11:13:18 GMT; path=/; domain=.etsy.com, etalb=259736404.1.10.1342654546; + expires=Thu, 19-Jul-2012 00:05:46 GMT; path=/; domain=.etsy.com x-etsy-request-uuid: - - 5rZUuxJe6Ho0Cu2ukDddNkAuNHHo + - IeUpkcxnZQKn9z6GHdcg4a35_fo_ content-length: - '355' - connection: + x-cnection: - close content-type: - application/json @@ -32,5 +32,5 @@ http_interactions: encoding: US-ASCII string: ! '{"count":1,"results":[{"user_id":17974443,"login_name":"jonS2011","creation_tsz":1322586362,"referred_by_user_id":null,"feedback_info":{"count":0,"score":null}}],"params":{"keywords":"jonS2011","limit":25,"offset":0,"page":null},"type":"User","pagination":{"effective_limit":25,"effective_offset":0,"next_offset":null,"effective_page":1,"next_page":null}}' http_version: - recorded_at: Sat, 14 Jul 2012 17:51:00 GMT + recorded_at: Wed, 18 Jul 2012 23:35:45 GMT recorded_with: VCR 2.2.3 diff --git a/spec/cassettes/Etsy_Client/_myself.yml b/spec/cassettes/Etsy_Client/_myself.yml new file mode 100644 index 0000000..176cf9d --- /dev/null +++ b/spec/cassettes/Etsy_Client/_myself.yml @@ -0,0 +1,40 @@ +--- +http_interactions: +- request: + method: get + uri: http://openapi.etsy.com/v2/users/__SELF__ + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - OAuth oauth_consumer_key="API_KEY", oauth_nonce="82844e5416174cd1776949f4d41a0fdd", + oauth_signature="ygVkHHnvYU%2F5DDAVqUNCHWSyi08%3D", oauth_signature_method="HMAC-SHA1", + oauth_timestamp="1342654545", oauth_token="OAUTH_TOKEN", oauth_version="1.0" + response: + status: + code: 200 + message: + headers: + date: + - Wed, 18 Jul 2012 23:35:46 GMT + server: + - Apache + set-cookie: + - etala=259736404.773457575.1342654546.1342654546.1342654546.1.0; expires=Sat, + 19-Jul-2014 11:13:18 GMT; path=/; domain=.etsy.com, etalb=259736404.1.10.1342654546; + expires=Thu, 19-Jul-2012 00:05:46 GMT; path=/; domain=.etsy.com + x-etsy-request-uuid: + - R5-d23BmJI20Q_wuyTWipPqxlBaU + content-length: + - '262' + x-cnection: + - close + content-type: + - application/json + body: + encoding: US-ASCII + string: ! '{"count":1,"results":[{"user_id":17974443,"login_name":"jonS2011","primary_email":"example@example.com","creation_tsz":1322586362,"referred_by_user_id":null,"feedback_info":{"count":0,"score":null}}],"params":{"user_id":"__SELF__"},"type":"User","pagination":{}}' + http_version: + recorded_at: Wed, 18 Jul 2012 23:35:45 GMT +recorded_with: VCR 2.2.3 diff --git a/spec/cassettes/Etsy_Client/_user.yml b/spec/cassettes/Etsy_Client/_user.yml new file mode 100644 index 0000000..b9856c2 --- /dev/null +++ b/spec/cassettes/Etsy_Client/_user.yml @@ -0,0 +1,36 @@ +--- +http_interactions: +- request: + method: get + uri: http://openapi.etsy.com/v2/users?api_key=API_KEY&keywords=jonS2011 + body: + encoding: US-ASCII + string: '' + headers: {} + response: + status: + code: 200 + message: + headers: + date: + - Wed, 18 Jul 2012 23:35:45 GMT + server: + - Apache + set-cookie: + - etala=259736404.894137008.1342654545.1342654545.1342654545.1.0; expires=Sat, + 19-Jul-2014 11:13:17 GMT; path=/; domain=.etsy.com, etalb=259736404.1.10.1342654545; + expires=Thu, 19-Jul-2012 00:05:45 GMT; path=/; domain=.etsy.com + x-etsy-request-uuid: + - g3BFvXNE1sQkNTQ4pnmp8qTNGtns + content-length: + - '355' + x-cnection: + - close + content-type: + - application/json + body: + encoding: US-ASCII + string: ! '{"count":1,"results":[{"user_id":17974443,"login_name":"jonS2011","creation_tsz":1322586362,"referred_by_user_id":null,"feedback_info":{"count":0,"score":null}}],"params":{"keywords":"jonS2011","limit":25,"offset":0,"page":null},"type":"User","pagination":{"effective_limit":25,"effective_offset":0,"next_offset":null,"effective_page":1,"next_page":null}}' + http_version: + recorded_at: Wed, 18 Jul 2012 23:35:44 GMT +recorded_with: VCR 2.2.3 diff --git a/spec/cassettes/Etsy_Client/oauth_authentication/_oauth_token.yml b/spec/cassettes/Etsy_Client/oauth_authentication/_oauth_token.yml new file mode 100644 index 0000000..0002570 --- /dev/null +++ b/spec/cassettes/Etsy_Client/oauth_authentication/_oauth_token.yml @@ -0,0 +1,93 @@ +--- +http_interactions: +- request: + method: post + uri: http://openapi.etsy.com/v2/oauth/request_token + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - ! '*/*' + User-Agent: + - OAuth gem v0.4.6 + Content-Length: + - '0' + Authorization: + - OAuth oauth_body_hash="2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D", oauth_callback="oob", + oauth_consumer_key="API_KEY", oauth_nonce="7WdBjh8hY5jACA2OiDlVVruJ9Na0D1yPRIXTzqDDu8", + oauth_signature="y1YFjp5DZLkCDrMtw%2FuEMrOXb1E%3D", oauth_signature_method="HMAC-SHA1", + oauth_timestamp="1342710012", oauth_version="1.0" + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 19 Jul 2012 15:00:11 GMT + Server: + - Apache + Set-Cookie: + - etala=259736404.806750375.1342710011.1342710011.1342710011.1.0; expires=Sun, + 20-Jul-2014 02:37:43 GMT; path=/; domain=.etsy.com + - etalb=259736404.1.10.1342710011; expires=Thu, 19-Jul-2012 15:30:11 GMT; path=/; + domain=.etsy.com + X-Etsy-Request-Uuid: + - oX5g8eUPX87kx0tLqERD1Z7nP8SK + Content-Length: + - '334' + X-Cnection: + - close + Content-Type: + - application/x-www-form-urlencoded + body: + encoding: US-ASCII + string: login_url=https%3A%2F%2Fwww.etsy.com%2Foauth%2Fsignin%3Foauth_consumer_key%3DAPI_KEY%26oauth_token%3De1b0bd62cc6d16a5783cd2bcf20a23%26service%3Dv2_prod&oauth_token=e1b0bd62cc6d16a5783cd2bcf20a23&oauth_token_secret=c5b0077b78&oauth_callback_confirmed=true&oauth_consumer_key=API_KEY&oauth_callback=oob + http_version: + recorded_at: Thu, 19 Jul 2012 15:00:13 GMT +- request: + method: post + uri: http://openapi.etsy.com/v2/oauth/access_token + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - ! '*/*' + User-Agent: + - OAuth gem v0.4.6 + Content-Length: + - '0' + Authorization: + - OAuth oauth_body_hash="2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D", oauth_consumer_key="API_KEY", + oauth_nonce="IgJEx28ih8Kg2MuDJRwOw8G0z5gSC7y9g5D1JKgI", oauth_signature="IouzF5lG9uW1YD5Z%2BG03%2F9dt4sI%3D", + oauth_signature_method="HMAC-SHA1", oauth_timestamp="1342710133", oauth_token="e1b0bd62cc6d16a5783cd2bcf20a23", + oauth_verifier="OAUTH_VERIFIER", oauth_version="1.0" + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 19 Jul 2012 15:02:11 GMT + Server: + - Apache + Set-Cookie: + - etala=259736404.415337487.1342710131.1342710131.1342710131.1.0; expires=Sun, + 20-Jul-2014 02:39:43 GMT; path=/; domain=.etsy.com + - etalb=259736404.1.10.1342710131; expires=Thu, 19-Jul-2012 15:32:11 GMT; path=/; + domain=.etsy.com + X-Etsy-Request-Uuid: + - ZOibyVmi0nEwENLRqb9LFHTKGpEc + Content-Length: + - '72' + X-Cnection: + - close + Content-Type: + - application/x-www-form-urlencoded + body: + encoding: US-ASCII + string: oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_SECRET + http_version: + recorded_at: Thu, 19 Jul 2012 15:02:13 GMT +recorded_with: VCR 2.2.3 diff --git a/spec/cassettes/Etsy_Client/oauth_authentication/_request_token.yml b/spec/cassettes/Etsy_Client/oauth_authentication/_request_token.yml new file mode 100644 index 0000000..805c0bd --- /dev/null +++ b/spec/cassettes/Etsy_Client/oauth_authentication/_request_token.yml @@ -0,0 +1,48 @@ +--- +http_interactions: +- request: + method: post + uri: http://openapi.etsy.com/v2/oauth/request_token + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - ! '*/*' + User-Agent: + - OAuth gem v0.4.6 + Content-Length: + - '0' + Authorization: + - OAuth oauth_body_hash="2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D", oauth_callback="oob", + oauth_consumer_key="API_KEY", oauth_nonce="uBxtNbTNUYRNpMbu8NRwJwreiyj9Qopstcg3lqDxu3Y", + oauth_signature="H4kri%2B6ij%2FgVp6bIBL3MlVEq0VY%3D", oauth_signature_method="HMAC-SHA1", + oauth_timestamp="1342654544", oauth_version="1.0" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 18 Jul 2012 23:35:45 GMT + Server: + - Apache + Set-Cookie: + - etala=259736404.216431785.1342654545.1342654545.1342654545.1.0; expires=Sat, + 19-Jul-2014 11:13:17 GMT; path=/; domain=.etsy.com + - etalb=259736404.1.10.1342654545; expires=Thu, 19-Jul-2012 00:05:45 GMT; path=/; + domain=.etsy.com + X-Etsy-Request-Uuid: + - FdZZkUiGen0epHTvjiekvry4_5Js + Content-Length: + - '334' + X-Cnection: + - close + Content-Type: + - application/x-www-form-urlencoded + body: + encoding: US-ASCII + string: login_url=https%3A%2F%2Fwww.etsy.com%2Foauth%2Fsignin%3Foauth_consumer_key%3DAPI_KEY%26oauth_token%3Dd4abfe6a86c719a1f7757a1a7a8366%26service%3Dv2_prod&oauth_token=d4abfe6a86c719a1f7757a1a7a8366&oauth_token_secret=8d09c046d8&oauth_callback_confirmed=true&oauth_consumer_key=API_KEY&oauth_callback=oob + http_version: + recorded_at: Wed, 18 Jul 2012 23:35:44 GMT +recorded_with: VCR 2.2.3 diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 920b0c7..270c134 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -2,31 +2,85 @@ module Etsy describe Client do - use_vcr_cassette - - subject { Client.new(:api_key => Etsy::Test::API_KEY) } - - it "should be able to find a user" do - subject.user("jonS2011").should == { - "user_id" => 17974443, - "login_name" => "jonS2011", - "creation_tsz" => 1322586362, - "referred_by_user_id" => nil, - "feedback_info" => { - "count" => 0, - "score" => nil + subject do + Client.new( + :api_key => Etsy::Test::API_KEY, + :api_secret => Etsy::Test::API_SECRET + ) + end + + describe "#user" do + use_vcr_cassette + + it "should be able to find a user" do + subject.user("jonS2011").should == { + "user_id" => 17974443, + "login_name" => "jonS2011", + "creation_tsz" => 1322586362, + "referred_by_user_id" => nil, + "feedback_info" => { + "count" => 0, + "score" => nil + } + } + end + end + + context "oauth authentication" do + describe "#request_token" do + use_vcr_cassette + + it "should be able to get a request token" do + subject.request_token.should be_a(OAuth::RequestToken) + end + end + + describe "#oauth_token" do + use_vcr_cassette :record => :new_episodes + + it "should be able to get an oauth token" do + request_token = subject.request_token + p request_token.params[:login_url] + verifier = Etsy::Test::OAUTH_VERIFIER + subject.oauth_token(request_token, verifier).should == [Etsy::Test::OAUTH_TOKEN, Etsy::Test::OAUTH_SECRET] + end + end + end + + describe "#myself" do + use_vcr_cassette + before(:each) do + subject.oauth_token = Etsy::Test::OAUTH_TOKEN + subject.oauth_secret = Etsy::Test::OAUTH_SECRET + end + + it "should be able to find the authenticated user" do + subject.myself.should == { + "user_id" => 17974443, + "login_name" => "jonS2011", + "primary_email" => "example@example.com", + "creation_tsz" => 1322586362, + "referred_by_user_id" => nil, + "feedback_info" => { + "count" => 0, + "score" => nil + } } - } + end end - it "should be able to get an api endpoint" do - subject.get('users', :keywords => "jonS2011").body.should == { - "count" => 1, - "pagination" => {"effective_limit"=>25, "effective_offset"=>0, "next_offset"=>nil, "effective_page"=>1, "next_page"=>nil}, - "params" => {"keywords"=>"jonS2011", "limit"=>25, "offset"=>0, "page"=>nil}, - "results" => [{"user_id"=>17974443, "login_name"=>"jonS2011", "creation_tsz"=>1322586362, "referred_by_user_id"=>nil, "feedback_info"=>{"count"=>0, "score"=>nil}}], - "type" => "User" - } + describe "#get" do + use_vcr_cassette + + it "should be able to get an api endpoint" do + subject.get('users', :keywords => "jonS2011").body.should == { + "count" => 1, + "pagination" => {"effective_limit"=>25, "effective_offset"=>0, "next_offset"=>nil, "effective_page"=>1, "next_page"=>nil}, + "params" => {"keywords"=>"jonS2011", "limit"=>25, "offset"=>0, "page"=>nil}, + "results" => [{"user_id"=>17974443, "login_name"=>"jonS2011", "creation_tsz"=>1322586362, "referred_by_user_id"=>nil, "feedback_info"=>{"count"=>0, "score"=>nil}}], + "type" => "User" + } + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0688c8e..c71f3cf 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,17 +14,26 @@ VCR.configure do |c| c.cassette_library_dir = 'spec/cassettes' c.register_request_matcher :parsed_query do |req_1, req_2| - CGI.parse(URI(req_1.uri).query) == CGI.parse(URI(req_2.uri).query) + req_1_query = URI(req_1.uri).query + req_2_query = URI(req_2.uri).query + + if req_1_query.nil? == req_2_query.nil? + true + elsif req_1_query.nil? || req_2_query.nil? + false + else + CGI.parse(req_1_query) == CGI.parse(req_2_query) + end end c.default_cassette_options = { :match_requests_on => [:method, :host, :path, :parsed_query] } # Add sensitive fields here # Constants in Etsy::Test will be auto filtered - Etsy::Test.constants.each do |const| + Etsy::Test.constants.reverse.each do |const| c.filter_sensitive_data(const.to_s) { Etsy::Test.const_get(const) } end - c.hook_into :faraday + c.hook_into :faraday, :webmock end RSpec.configure do |c| From 44b85a125173a21898b7c0306e26410bbfa3a5f4 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Thu, 19 Jul 2012 08:13:13 -0700 Subject: [PATCH 13/30] Removing a puts --- spec/client_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 270c134..520b9bb 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -40,7 +40,6 @@ module Etsy it "should be able to get an oauth token" do request_token = subject.request_token - p request_token.params[:login_url] verifier = Etsy::Test::OAUTH_VERIFIER subject.oauth_token(request_token, verifier).should == [Etsy::Test::OAUTH_TOKEN, Etsy::Test::OAUTH_SECRET] end From 54be01182336f43eaeb0ca362f0cad2d912a3a52 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Thu, 19 Jul 2012 22:58:58 +0200 Subject: [PATCH 14/30] Add sandbox endpoint --- lib/etsy/client.rb | 7 ++++++- spec/api_credentials.rb | 1 + spec/client_spec.rb | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/etsy/client.rb b/lib/etsy/client.rb index b3876bd..988a295 100644 --- a/lib/etsy/client.rb +++ b/lib/etsy/client.rb @@ -7,6 +7,7 @@ def initialize(options={}) @api_secret = options[:api_secret] @oauth_token = options[:oauth_token] @oauth_secret = options[:oauth_secret] + @environment = options[:environment] || :production end def myself @@ -39,7 +40,7 @@ def get(path, options={}) def connection @connection ||= Faraday.new( - :url => "http://openapi.etsy.com/v2", + :url => endpoint, :params => default_params ) do |conn| conn.request :json @@ -56,6 +57,10 @@ def connection end end + def endpoint + @environment == :production ? "http://openapi.etsy.com/v2" : "http://sandbox.openapi.etsy.com/v2" + end + def default_params params = {} params[:api_key] = @api_key if @api_key && !@oauth_token diff --git a/spec/api_credentials.rb b/spec/api_credentials.rb index bf595ff..9439738 100644 --- a/spec/api_credentials.rb +++ b/spec/api_credentials.rb @@ -5,5 +5,6 @@ module Test OAUTH_TOKEN = ENV["ETSY_OAUTH_TOKEN"] || "OAUTH_TOKEN" OAUTH_SECRET = ENV["ETSY_OAUTH_SECRET"] || "OAUTH_SECRET" OAUTH_VERIFIER = ENV["ETSY_OAUTH_VERIFIER"] || "OAUTH_VERIFIER" + ENVIRONMENT = ENV["ETSY_ENVIRONMENT"] || 'production' end end diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 520b9bb..87b1ff9 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -5,7 +5,8 @@ module Etsy subject do Client.new( :api_key => Etsy::Test::API_KEY, - :api_secret => Etsy::Test::API_SECRET + :api_secret => Etsy::Test::API_SECRET, + :environment => Etsy::Test::ENVIRONMENT.to_sym ) end From 362646db49b434f01608142a43eed11524097ce5 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Thu, 19 Jul 2012 22:59:29 +0200 Subject: [PATCH 15/30] Add rake task to help set up specs --- Rakefile | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Rakefile b/Rakefile index 2ddf787..e8a5fdf 100644 --- a/Rakefile +++ b/Rakefile @@ -16,3 +16,48 @@ desc "Run specs" RSpec::Core::RakeTask.new do |t| t.pattern = "./spec/**/*_spec.rb" end + +namespace :spec do + desc "Setup specs" + task :setup do + require 'oauth' + require './lib/etsy/client' + key = ENV["ETSY_API_KEY"] + unless key + puts + puts "No API key found. Run (or add to .bashrc):" + puts " export ETSY_API_KEY=" + end + + secret = ENV["ETSY_API_SECRET"] + unless secret + puts + puts "For authenticated (oauth) requests, you also need a secret" + puts " export ETSY_API_SECRET=" + end + + environment = ENV["ETSY_ENVIRONMENT"] + unless environment + puts + puts "Defaulting to production environment. To override:" + puts " export ETSY_ENVIRONMENT=sandbox" + end + + client = Etsy::Client.new(:api_key => key, :api_secret => secret) + + req = client.request_token + + puts "Go to:" + puts req.params[:login_url] + print "What is the verifier key? " + verifier = STDIN.gets.chomp.strip + token, secret = client.oauth_token(req, verifier) + + puts + puts "These are your environment variables: " + puts "export ETSY_OAUTH_TOKEN=#{token}" + puts "export ETSY_OAUTH_SECRET=#{secret}" + puts "export ETSY_OAUTH_VERIFIER=#{verifier}" + puts + end +end From 5be5c43dbebe1dd9c9fa59cb71a1c22414ac9a22 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Thu, 19 Jul 2012 23:22:56 +0200 Subject: [PATCH 16/30] Tweak sandbox endpoints --- lib/etsy/client.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/etsy/client.rb b/lib/etsy/client.rb index 988a295..f311c97 100644 --- a/lib/etsy/client.rb +++ b/lib/etsy/client.rb @@ -20,7 +20,7 @@ def user(username) def request_token consumer = OAuth::Consumer.new(@api_key, @api_secret, - :site => "http://openapi.etsy.com", + :site => endpoint, :request_token_path => "/v2/oauth/request_token", :access_token_path => "/v2/oauth/access_token" ) @@ -40,7 +40,7 @@ def get(path, options={}) def connection @connection ||= Faraday.new( - :url => endpoint, + :url => "#{endpoint}/v2", :params => default_params ) do |conn| conn.request :json @@ -58,7 +58,7 @@ def connection end def endpoint - @environment == :production ? "http://openapi.etsy.com/v2" : "http://sandbox.openapi.etsy.com/v2" + @environment == :production ? "http://openapi.etsy.com" : "http://sandbox.openapi.etsy.com" end def default_params From 2aa220fe226dca8ce1b672f2af86f7d7adf93486 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Thu, 26 Jul 2012 13:03:56 +0200 Subject: [PATCH 17/30] Implement basic query object. --- lib/etsy/query.rb | 63 ++++++++++++++++++++++++++++++++++++++++++++++ spec/query_spec.rb | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 lib/etsy/query.rb create mode 100644 spec/query_spec.rb diff --git a/lib/etsy/query.rb b/lib/etsy/query.rb new file mode 100644 index 0000000..55af084 --- /dev/null +++ b/lib/etsy/query.rb @@ -0,0 +1,63 @@ +require 'cgi' + +module Etsy + class Query + + attr_reader :resource, :key, :sub_resource, :options + def initialize(resource = nil, options = {}) + @resource = resource.to_s + @key = options.delete(:key) + @sub_resource = options.delete(:resource) + @options = options + end + + def limit + options.fetch(:limit) { 25 } + end + + def limit=(value) + options[:limit] = value + end + + def offset + options.fetch(:offset) { 0 } + end + + def offset=(value) + options[:offset] = value + end + + def page + options.fetch(:page) { 1 } + end + + def page=(value) + options[:page] = value + end + + def fields=(values) + options[:fields] = values + end + + def to_s + endpoint + query + end + + private + + def endpoint + ["", resource, key, sub_resource].compact.join("/") + end + + def query + return '' unless options.size > 0 + + '?' + options.map { |k,v| "#{k}=#{list(v)}" }.join + end + + def list(values) + Array(values).join(',') + end + + end +end diff --git a/spec/query_spec.rb b/spec/query_spec.rb new file mode 100644 index 0000000..51b264a --- /dev/null +++ b/spec/query_spec.rb @@ -0,0 +1,54 @@ +require 'etsy/query' + +describe Etsy::Query do + specify do + expect(Etsy::Query.new.to_s).to eq("/") + end + + describe "a simple user query" do + subject { Etsy::Query.new(:users) } + its(:limit) { should eq(25) } + its(:offset) { should eq(0) } + its(:page) { should eq(1) } + + its(:to_s) { should eq('/users') } + + it "overrides limit" do + subject.limit = 10 + expect(subject.limit).to eq(10) + expect(subject.to_s).to eq('/users?limit=10') + end + + it "overrides offset" do + subject.offset = 3 + expect(subject.offset).to eq(3) + expect(subject.to_s).to eq('/users?offset=3') + end + + it "overrides page" do + subject.page = 2 + expect(subject.page).to eq(2) + expect(subject.to_s).to eq('/users?page=2') + end + + it "selects specific fields" do + subject.fields = %w(login_name user_id) + expect(subject.to_s).to eq('/users?fields=login_name,user_id') + end + end + + describe "a specific user" do + subject { Etsy::Query.new(:users, :key => 'cavetroll') } + its(:to_s) { should eq('/users/cavetroll') } + end + + describe "a user's stuff" do + subject { Etsy::Query.new(:users, :key => 'cavetroll', :resource => :avatar) } + its(:to_s) { should eq('/users/cavetroll/avatar') } + end + + describe "a user's more specific stuff" do + subject { Etsy::Query.new(:users, :key => 'cavetroll', :resource => [:avatar, :src]) } + its(:to_s) { should eq('/users/cavetroll/avatar/src') } + end +end From 5b36d1c15af1ebce254ad5eaef2ec920e2d0c8b8 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 29 Jul 2012 18:02:10 +0200 Subject: [PATCH 18/30] Deliver params and endpoint separately in query. --- lib/etsy/query.rb | 11 +++++++---- spec/query_spec.rb | 16 ++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/etsy/query.rb b/lib/etsy/query.rb index 55af084..604aeef 100644 --- a/lib/etsy/query.rb +++ b/lib/etsy/query.rb @@ -43,18 +43,21 @@ def to_s endpoint + query end - private - def endpoint ["", resource, key, sub_resource].compact.join("/") end def query return '' unless options.size > 0 - - '?' + options.map { |k,v| "#{k}=#{list(v)}" }.join + params = {} + options.each do |k, v| + params[k] = list(v) + end + params end + private + def list(values) Array(values).join(',') end diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 51b264a..0ca23e6 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -11,44 +11,44 @@ its(:offset) { should eq(0) } its(:page) { should eq(1) } - its(:to_s) { should eq('/users') } + its(:endpoint) { should eq('/users') } it "overrides limit" do subject.limit = 10 expect(subject.limit).to eq(10) - expect(subject.to_s).to eq('/users?limit=10') + expect(subject.query).to eq(:limit => '10') end it "overrides offset" do subject.offset = 3 expect(subject.offset).to eq(3) - expect(subject.to_s).to eq('/users?offset=3') + expect(subject.query).to eq(:offset => '3') end it "overrides page" do subject.page = 2 expect(subject.page).to eq(2) - expect(subject.to_s).to eq('/users?page=2') + expect(subject.query).to eq(:page => '2') end it "selects specific fields" do subject.fields = %w(login_name user_id) - expect(subject.to_s).to eq('/users?fields=login_name,user_id') + expect(subject.query).to eq(:fields => 'login_name,user_id') end end describe "a specific user" do subject { Etsy::Query.new(:users, :key => 'cavetroll') } - its(:to_s) { should eq('/users/cavetroll') } + its(:endpoint) { should eq('/users/cavetroll') } end describe "a user's stuff" do subject { Etsy::Query.new(:users, :key => 'cavetroll', :resource => :avatar) } - its(:to_s) { should eq('/users/cavetroll/avatar') } + its(:endpoint) { should eq('/users/cavetroll/avatar') } end describe "a user's more specific stuff" do subject { Etsy::Query.new(:users, :key => 'cavetroll', :resource => [:avatar, :src]) } - its(:to_s) { should eq('/users/cavetroll/avatar/src') } + its(:endpoint) { should eq('/users/cavetroll/avatar/src') } end end From bb7512c858008ff5087dcbfff970b7f2b1dce19c Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 29 Jul 2012 18:11:25 +0200 Subject: [PATCH 19/30] Wrap hashes in curly braces --- spec/query_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 0ca23e6..1e8ee3c 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -16,24 +16,24 @@ it "overrides limit" do subject.limit = 10 expect(subject.limit).to eq(10) - expect(subject.query).to eq(:limit => '10') + expect(subject.query).to eq({:limit => '10'}) end it "overrides offset" do subject.offset = 3 expect(subject.offset).to eq(3) - expect(subject.query).to eq(:offset => '3') + expect(subject.query).to eq({:offset => '3'}) end it "overrides page" do subject.page = 2 expect(subject.page).to eq(2) - expect(subject.query).to eq(:page => '2') + expect(subject.query).to eq({:page => '2'}) end it "selects specific fields" do subject.fields = %w(login_name user_id) - expect(subject.query).to eq(:fields => 'login_name,user_id') + expect(subject.query).to eq({:fields => 'login_name,user_id'}) end end From 93c0702da723153931a5ee3590d019bc87deee9b Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 29 Jul 2012 19:31:14 +0200 Subject: [PATCH 20/30] Remove to_s override on query --- lib/etsy/query.rb | 4 ---- spec/query_spec.rb | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/etsy/query.rb b/lib/etsy/query.rb index 604aeef..d953c3f 100644 --- a/lib/etsy/query.rb +++ b/lib/etsy/query.rb @@ -39,10 +39,6 @@ def fields=(values) options[:fields] = values end - def to_s - endpoint + query - end - def endpoint ["", resource, key, sub_resource].compact.join("/") end diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 1e8ee3c..3a44a50 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -2,7 +2,7 @@ describe Etsy::Query do specify do - expect(Etsy::Query.new.to_s).to eq("/") + expect(Etsy::Query.new.endpoint).to eq("/") end describe "a simple user query" do From 7c27be533785bc4fdc0b22df80559858b27e448f Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 29 Jul 2012 19:38:35 +0200 Subject: [PATCH 21/30] Add rudimentary support for associations in query. --- lib/etsy/query.rb | 12 ++++++++++-- spec/query_spec.rb | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/etsy/query.rb b/lib/etsy/query.rb index d953c3f..3b1901f 100644 --- a/lib/etsy/query.rb +++ b/lib/etsy/query.rb @@ -3,12 +3,13 @@ module Etsy class Query - attr_reader :resource, :key, :sub_resource, :options + attr_reader :resource, :key, :sub_resource, :options, :associations def initialize(resource = nil, options = {}) @resource = resource.to_s @key = options.delete(:key) @sub_resource = options.delete(:resource) @options = options + @associations = [] end def limit @@ -39,16 +40,23 @@ def fields=(values) options[:fields] = values end + def include(resource) + @associations << resource.to_s.split('_').map(&:capitalize).join + end + def endpoint ["", resource, key, sub_resource].compact.join("/") end def query - return '' unless options.size > 0 + return {} if options.size == 0 && associations.empty? params = {} options.each do |k, v| params[k] = list(v) end + unless associations.empty? + params[:includes] = associations.join(',') + end params end diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 3a44a50..a412ba6 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -40,6 +40,24 @@ describe "a specific user" do subject { Etsy::Query.new(:users, :key => 'cavetroll') } its(:endpoint) { should eq('/users/cavetroll') } + + context "with associations" do + it "takes a resource" do + subject.include(:addresses) + expect(subject.query).to eq({:includes => 'Addresses'}) + end + + it "takes more than one resource" do + subject.include(:addresses) + subject.include(:profile) + expect(subject.query).to eq({:includes => 'Addresses,Profile'}) + end + + it "takes multi-word resources" do + subject.include(:other_stuff) + expect(subject.query).to eq({:includes => 'OtherStuff'}) + end + end end describe "a user's stuff" do @@ -51,4 +69,5 @@ subject { Etsy::Query.new(:users, :key => 'cavetroll', :resource => [:avatar, :src]) } its(:endpoint) { should eq('/users/cavetroll/avatar/src') } end + end From 07b71c0a52bb39f13af15bc4b9f2b6c95e06c09e Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 29 Jul 2012 21:18:46 +0200 Subject: [PATCH 22/30] Add support for arbitrary query parameters. --- lib/etsy/query.rb | 33 +++++++++++++++++---------------- spec/query_spec.rb | 5 +++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/etsy/query.rb b/lib/etsy/query.rb index 3b1901f..bd101c9 100644 --- a/lib/etsy/query.rb +++ b/lib/etsy/query.rb @@ -16,30 +16,14 @@ def limit options.fetch(:limit) { 25 } end - def limit=(value) - options[:limit] = value - end - def offset options.fetch(:offset) { 0 } end - def offset=(value) - options[:offset] = value - end - def page options.fetch(:page) { 1 } end - def page=(value) - options[:page] = value - end - - def fields=(values) - options[:fields] = values - end - def include(resource) @associations << resource.to_s.split('_').map(&:capitalize).join end @@ -62,6 +46,23 @@ def query private + def method_missing(method, *args, &block) + if assignment?(method) + key = method.to_s[0..-2].to_sym + options[key] = args.length == 1 ? args.first : args + elsif options.has_key?(method) + options[method] + else + super + end + end + + def assignment?(method) + # Depending on the version of Ruby, + # s[-1] will return a character or a Fixnum + method.to_s[-1] == '='[0] + end + def list(values) Array(values).join(',') end diff --git a/spec/query_spec.rb b/spec/query_spec.rb index a412ba6..d1be0bd 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -35,6 +35,11 @@ subject.fields = %w(login_name user_id) expect(subject.query).to eq({:fields => 'login_name,user_id'}) end + + it "accepts arbitrary parameters" do + subject.keywords = 'zombie+party' + subject.keywords.should eq('zombie+party') + end end describe "a specific user" do From fcf1c17b1098512fe67c49c52e26da9f731bf502 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 29 Jul 2012 21:39:12 +0200 Subject: [PATCH 23/30] Tweak syntax for specifying resources in query --- lib/etsy/query.rb | 12 +++++------- spec/query_spec.rb | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/etsy/query.rb b/lib/etsy/query.rb index bd101c9..f42e9e6 100644 --- a/lib/etsy/query.rb +++ b/lib/etsy/query.rb @@ -3,12 +3,10 @@ module Etsy class Query - attr_reader :resource, :key, :sub_resource, :options, :associations - def initialize(resource = nil, options = {}) - @resource = resource.to_s - @key = options.delete(:key) - @sub_resource = options.delete(:resource) - @options = options + attr_reader :resource, :options, :associations + def initialize(*resource) + @resource = resource.join('/') + @options = {} @associations = [] end @@ -29,7 +27,7 @@ def include(resource) end def endpoint - ["", resource, key, sub_resource].compact.join("/") + "/#{resource}" end def query diff --git a/spec/query_spec.rb b/spec/query_spec.rb index d1be0bd..99ac68a 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -43,7 +43,7 @@ end describe "a specific user" do - subject { Etsy::Query.new(:users, :key => 'cavetroll') } + subject { Etsy::Query.new(:users, 'cavetroll') } its(:endpoint) { should eq('/users/cavetroll') } context "with associations" do @@ -66,12 +66,12 @@ end describe "a user's stuff" do - subject { Etsy::Query.new(:users, :key => 'cavetroll', :resource => :avatar) } + subject { Etsy::Query.new(:users, 'cavetroll', :avatar) } its(:endpoint) { should eq('/users/cavetroll/avatar') } end describe "a user's more specific stuff" do - subject { Etsy::Query.new(:users, :key => 'cavetroll', :resource => [:avatar, :src]) } + subject { Etsy::Query.new(:users, 'cavetroll', :avatar, :src) } its(:endpoint) { should eq('/users/cavetroll/avatar/src') } end From f5f2370f29be92f8e4be68b271143a14ca33812b Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Tue, 21 Aug 2012 20:55:03 +0200 Subject: [PATCH 24/30] Add basic query syntax for associated resources --- lib/etsy/query.rb | 5 +++-- lib/etsy/resource.rb | 35 +++++++++++++++++++++++++++++++++++ spec/query_spec.rb | 16 ++++------------ spec/resource_spec.rb | 43 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 lib/etsy/resource.rb create mode 100644 spec/resource_spec.rb diff --git a/lib/etsy/query.rb b/lib/etsy/query.rb index f42e9e6..9d7d582 100644 --- a/lib/etsy/query.rb +++ b/lib/etsy/query.rb @@ -1,4 +1,5 @@ require 'cgi' +require 'etsy/resource' module Etsy class Query @@ -22,8 +23,8 @@ def page options.fetch(:page) { 1 } end - def include(resource) - @associations << resource.to_s.split('_').map(&:capitalize).join + def include(resource, options = {}) + @associations << Resource.new(resource, options) #.to_s.split('_').map(&:capitalize).join end def endpoint diff --git a/lib/etsy/resource.rb b/lib/etsy/resource.rb new file mode 100644 index 0000000..6af4f2a --- /dev/null +++ b/lib/etsy/resource.rb @@ -0,0 +1,35 @@ +module Etsy + class Resource + + attr_reader :name, :limit, :offset, :fields, :scope + def initialize(name, options = {}) + @name = name.to_s.split('_').map(&:capitalize).join + @limit = options[:limit] + @offset = options[:offset] + @fields = options[:fields] || [] + @scope = options[:scope] + end + + def range + return unless offset || limit + + if limit + ":#{offset || 0}:#{limit}" + else + ":#{offset}" + end + end + + def selected_fields + fields.empty? ? '' : "(#{fields.join(',')})" + end + + def scoped + return ":#{scope}" if scope + end + + def to_s + "#{name}#{selected_fields}#{scoped}#{range}" + end + end +end diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 99ac68a..8347eb3 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -47,21 +47,13 @@ its(:endpoint) { should eq('/users/cavetroll') } context "with associations" do - it "takes a resource" do - subject.include(:addresses) - expect(subject.query).to eq({:includes => 'Addresses'}) - end - it "takes more than one resource" do - subject.include(:addresses) - subject.include(:profile) - expect(subject.query).to eq({:includes => 'Addresses,Profile'}) + it "takes resources" do + subject.include(:addresses, :scope => :european) + subject.include(:profile, :limit => 1, :offset => 2, :fields => [:picture, :name]) + expect(subject.query).to eq({:includes => 'Addresses:european,Profile(picture,name):2:1'}) end - it "takes multi-word resources" do - subject.include(:other_stuff) - expect(subject.query).to eq({:includes => 'OtherStuff'}) - end end end diff --git a/spec/resource_spec.rb b/spec/resource_spec.rb new file mode 100644 index 0000000..5261253 --- /dev/null +++ b/spec/resource_spec.rb @@ -0,0 +1,43 @@ +require 'etsy/resource' + +describe Etsy::Resource do + + it "takes a resource" do + Etsy::Resource.new(:knives).to_s.should eq('Knives') + end + + it "takes multi-word resources" do + Etsy::Resource.new(:oven_mitts).to_s.should eq('OvenMitts') + end + + it "specifies offset" do + Etsy::Resource.new(:drain, :offset => 3).to_s.should eq('Drain:3') + end + + it "specifies limit" do + Etsy::Resource.new(:drain, :limit => 4).to_s.should eq('Drain:0:4') + end + + it "handles a full range" do + Etsy::Resource.new(:dish_soap, :offset => 1, :limit => 7).to_s.should eq('DishSoap:1:7') + end + + it "specifies fields" do + Etsy::Resource.new(:fridge, :fields => [:cheese, :milk]).to_s.should eq('Fridge(cheese,milk)') + end + + it "takes a scope" do + Etsy::Resource.new(:pantry, :scope => :stale).to_s.should eq('Pantry:stale') + end + + it "does it all" do + options = { + :limit => 42, + :offset => 1337, + :fields => [:water, :soap], + :scope => :dirty + } + Etsy::Resource.new(:kitchen_sink, options).to_s.should eq('KitchenSink(water,soap):dirty:1337:42') + end + +end From 0c5074b044d76c517ff820b5f084da9799276e17 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Tue, 21 Aug 2012 21:00:17 +0200 Subject: [PATCH 25/30] Add spec helper to query and resource specs --- spec/query_spec.rb | 1 + spec/resource_spec.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 8347eb3..5819da9 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -1,3 +1,4 @@ +require 'spec_helper' require 'etsy/query' describe Etsy::Query do diff --git a/spec/resource_spec.rb b/spec/resource_spec.rb index 5261253..a8523d7 100644 --- a/spec/resource_spec.rb +++ b/spec/resource_spec.rb @@ -1,3 +1,4 @@ +require 'spec_helper' require 'etsy/resource' describe Etsy::Resource do From 3e20b95dd15bdf6eff736892ae781ea43b789b52 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Wed, 22 Aug 2012 21:21:37 +0200 Subject: [PATCH 26/30] Add nested resources --- lib/etsy/resource.rb | 22 +++++++++++++++++----- spec/resource_spec.rb | 8 ++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/etsy/resource.rb b/lib/etsy/resource.rb index 6af4f2a..55e394c 100644 --- a/lib/etsy/resource.rb +++ b/lib/etsy/resource.rb @@ -1,7 +1,7 @@ module Etsy class Resource - attr_reader :name, :limit, :offset, :fields, :scope + attr_reader :name, :limit, :offset, :fields, :scope, :child def initialize(name, options = {}) @name = name.to_s.split('_').map(&:capitalize).join @limit = options[:limit] @@ -10,6 +10,17 @@ def initialize(name, options = {}) @scope = options[:scope] end + def to_s + "#{name}#{selected_fields}#{scoped}#{range}#{child_resource}" + end + + def include(resource) + @child = resource + self + end + + private + def range return unless offset || limit @@ -21,15 +32,16 @@ def range end def selected_fields - fields.empty? ? '' : "(#{fields.join(',')})" + "(#{fields.join(',')})" unless fields.empty? end def scoped - return ":#{scope}" if scope + ":#{scope}" if scope end - def to_s - "#{name}#{selected_fields}#{scoped}#{range}" + def child_resource + "/#{child}" if child end + end end diff --git a/spec/resource_spec.rb b/spec/resource_spec.rb index a8523d7..acba8c3 100644 --- a/spec/resource_spec.rb +++ b/spec/resource_spec.rb @@ -41,4 +41,12 @@ Etsy::Resource.new(:kitchen_sink, options).to_s.should eq('KitchenSink(water,soap):dirty:1337:42') end + it "nests resources" do + dinner = Etsy::Resource.new(:dinner, :fields => [:entre, :desert]) + veggies = Etsy::Resource.new(:veggies, :limit => 3) + dressing = Etsy::Resource.new(:dressing, :scope => 'salty') + meal = dinner.include(veggies.include(dressing)) + meal.to_s.should eq('Dinner(entre,desert)/Veggies:0:3/Dressing:salty') + end + end From a947cd42ef99326b86c3073db09032576aeb0e28 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 23 Sep 2012 15:00:07 +0200 Subject: [PATCH 27/30] Downgrade rspec. When running the specs on jruby, srand is initialized incorrectly. Downgrading to rspec 2.7.x allows the specs to be run. --- etsy.gemspec | 3 ++- spec/query_spec.rb | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/etsy.gemspec b/etsy.gemspec index 888f8cd..4001b03 100644 --- a/etsy.gemspec +++ b/etsy.gemspec @@ -29,7 +29,8 @@ Gem::Specification.new do |gem| gem.add_development_dependency "jnunemaker-matchy", "~> 0.4.0" gem.add_development_dependency "shoulda", "~> 3.1.0" gem.add_development_dependency "mocha", "~> 0.12.0" - gem.add_development_dependency "rspec", "~> 2.11.0" + # rspec version > 2.7.x breaks with jruby + gem.add_development_dependency "rspec", "~> 2.7.0" gem.add_development_dependency "vcr", "~> 2.2.3" gem.add_development_dependency "webmock", "~> 1.8.7" gem.add_development_dependency "simplecov", "~> 0.6.4" diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 5819da9..6db6f9e 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -3,7 +3,7 @@ describe Etsy::Query do specify do - expect(Etsy::Query.new.endpoint).to eq("/") + Etsy::Query.new.endpoint.should eq("/") end describe "a simple user query" do @@ -16,25 +16,25 @@ it "overrides limit" do subject.limit = 10 - expect(subject.limit).to eq(10) - expect(subject.query).to eq({:limit => '10'}) + subject.limit.should eq(10) + subject.query.should eq({:limit => '10'}) end it "overrides offset" do subject.offset = 3 - expect(subject.offset).to eq(3) - expect(subject.query).to eq({:offset => '3'}) + subject.offset.should eq(3) + subject.query.should eq({:offset => '3'}) end it "overrides page" do subject.page = 2 - expect(subject.page).to eq(2) - expect(subject.query).to eq({:page => '2'}) + subject.page.should eq(2) + subject.query.should eq({:page => '2'}) end it "selects specific fields" do subject.fields = %w(login_name user_id) - expect(subject.query).to eq({:fields => 'login_name,user_id'}) + subject.query.should eq({:fields => 'login_name,user_id'}) end it "accepts arbitrary parameters" do @@ -52,7 +52,7 @@ it "takes resources" do subject.include(:addresses, :scope => :european) subject.include(:profile, :limit => 1, :offset => 2, :fields => [:picture, :name]) - expect(subject.query).to eq({:includes => 'Addresses:european,Profile(picture,name):2:1'}) + subject.query.should eq({:includes => 'Addresses:european,Profile(picture,name):2:1'}) end end From d29fb0998efa4822993ebfce911a5c656ba711c3 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 23 Sep 2012 15:03:39 +0200 Subject: [PATCH 28/30] Add link to JRuby issue --- etsy.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/etsy.gemspec b/etsy.gemspec index 4001b03..dcc6cfd 100644 --- a/etsy.gemspec +++ b/etsy.gemspec @@ -30,6 +30,7 @@ Gem::Specification.new do |gem| gem.add_development_dependency "shoulda", "~> 3.1.0" gem.add_development_dependency "mocha", "~> 0.12.0" # rspec version > 2.7.x breaks with jruby + # see http://jira.codehaus.org/browse/JRUBY-6324 gem.add_development_dependency "rspec", "~> 2.7.0" gem.add_development_dependency "vcr", "~> 2.2.3" gem.add_development_dependency "webmock", "~> 1.8.7" From ebcab8a2dd12b3e9c5c35eff38ed69c3c0f7d1a0 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Sun, 23 Sep 2012 19:11:15 +0200 Subject: [PATCH 29/30] Add spec for method missing --- spec/query_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 6db6f9e..d05fd3c 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -68,4 +68,10 @@ its(:endpoint) { should eq('/users/cavetroll/avatar/src') } end + it "properly delegates method missing" do + lambda { + subject.some_random_method + }.should raise_error NoMethodError + end + end From 51d75ea77c57ec09257c25db64c4b5318339cc28 Mon Sep 17 00:00:00 2001 From: Isaac Rosenberg Date: Mon, 18 Nov 2013 10:55:48 -0500 Subject: [PATCH 30/30] fixed broken rspec, may cause problems with jruby --- etsy.gemspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etsy.gemspec b/etsy.gemspec index 890ce76..0b1b6f8 100644 --- a/etsy.gemspec +++ b/etsy.gemspec @@ -30,7 +30,8 @@ Gem::Specification.new do |gem| gem.add_development_dependency "jnunemaker-matchy", "~> 0.4.0" # rspec version > 2.7.x breaks with jruby # see http://jira.codehaus.org/browse/JRUBY-6324 - gem.add_development_dependency "rspec", "~> 2.7.0" + # apparently this has been fixed. See http://jira.codehaus.org/browse/JRUBY-6324?focusedCommentId=287912&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-287912 + gem.add_development_dependency "rspec", "~> 2.12.0" gem.add_development_dependency "vcr", "~> 2.2.3" gem.add_development_dependency "webmock", "~> 1.8.7" gem.add_development_dependency "simplecov", "~> 0.6.4"