slight refactor in preparation for adding version 1.1 support

a couple breaking changes as part of this:

* the api version can no longer be set via configuration (since the API
itself now supports more than one version 1 and 1.1 this no longer makes
sense)
* removed module based support, all requests will require an Instapaper::Client from now on
* removed path_prefix configuration for same reasons that the api version was removed
This commit is contained in:
stve 2015-02-09 16:21:30 -05:00
parent 813cfc7426
commit 6271356847
20 changed files with 257 additions and 493 deletions

View file

@ -1,26 +1 @@
require 'instapaper/configuration'
require 'instapaper/client' require 'instapaper/client'
module Instapaper
extend Configuration
# Alias for Instapaper::Client.new
#
# @return [Instapaper::Client]
def self.client(options = {})
Instapaper::Client.new(options)
end
# Delegate to Instapaper::Client
def self.method_missing(method, *args, &block)
return super unless client.respond_to?(method)
client.send(method, *args, &block)
end
def self.respond_to?(method, include_private = false)
client.respond_to?(method, include_private) || super(method, include_private)
end
# Custom error class for rescuing from all Instapaper errors
class Error < StandardError; end
end

15
lib/instapaper/api.rb Normal file
View file

@ -0,0 +1,15 @@
require 'instapaper/api/account'
require 'instapaper/api/bookmark'
require 'instapaper/api/folder'
require 'instapaper/api/highlight'
require 'instapaper/api/user'
module Instapaper
module API
include Instapaper::API::Account
include Instapaper::API::Bookmark
include Instapaper::API::Folder
include Instapaper::API::Highlight
include Instapaper::API::User
end
end

View file

@ -1,10 +1,10 @@
module Instapaper module Instapaper
class Client module API
# Defines methods related to accounts # Defines methods related to accounts
module Account module Account
# Returns the currently logged in user. # Returns the currently logged in user.
def verify_credentials def verify_credentials
post('account/verify_credentials') post('/api/1/account/verify_credentials')
end end
end end
end end

View file

@ -1,5 +1,5 @@
module Instapaper module Instapaper
class Client module API
# Defines methods related to bookmarks # Defines methods related to bookmarks
module Bookmark module Bookmark
# Lists the user's unread bookmarks, and can also synchronize reading positions. # Lists the user's unread bookmarks, and can also synchronize reading positions.
@ -7,7 +7,7 @@ module Instapaper
# @option folder_id: Optional. Possible values are unread (default), starred, archive, or a folder_id value from /api/1/folders/list. # @option folder_id: Optional. Possible values are unread (default), starred, archive, or a folder_id value from /api/1/folders/list.
# @option have: Optional. A concatenation of bookmark_id values that the client already has from the specified folder. See below. # @option have: Optional. A concatenation of bookmark_id values that the client already has from the specified folder. See below.
def bookmarks(options = {}) def bookmarks(options = {})
post('bookmarks/list', options)[2..-1] post('/api/1/bookmarks/list', options)[2..-1]
end end
# Updates the user's reading progress on a single article. # Updates the user's reading progress on a single article.
@ -15,47 +15,47 @@ module Instapaper
# @param progress [Float] The user's progress, as a floating-point number between 0.0 and 1.0, defined as the top edge of the user's current viewport, expressed as a percentage of the article's total length. # @param progress [Float] The user's progress, as a floating-point number between 0.0 and 1.0, defined as the top edge of the user's current viewport, expressed as a percentage of the article's total length.
# @param progress_timestamp [Integer] The Unix timestamp value of the time that the progress was recorded. # @param progress_timestamp [Integer] The Unix timestamp value of the time that the progress was recorded.
def update_read_progress(bookmark_id, progress, progress_timestamp = Time.now) def update_read_progress(bookmark_id, progress, progress_timestamp = Time.now)
post('bookmarks/update_read_progress', bookmark_id: bookmark_id, progress: progress, progress_timestamp: progress_timestamp.to_i).first post('/api/1/bookmarks/update_read_progress', bookmark_id: bookmark_id, progress: progress, progress_timestamp: progress_timestamp.to_i).first
end end
# Adds a new unread bookmark to the user's account. # Adds a new unread bookmark to the user's account.
# @param url [String] The url of the bookmark. # @param url [String] The url of the bookmark.
def add_bookmark(url, options = {}) def add_bookmark(url, options = {})
post('bookmarks/add', options.merge(url: url)).first post('/api/1/bookmarks/add', options.merge(url: url)).first
end end
# Permanently deletes the specified bookmark. # Permanently deletes the specified bookmark.
# This is NOT the same as Archive. Please be clear to users if you're going to do this. # This is NOT the same as Archive. Please be clear to users if you're going to do this.
# @param bookmark_id [String] The id of the bookmark. # @param bookmark_id [String] The id of the bookmark.
def delete_bookmark(bookmark_id) def delete_bookmark(bookmark_id)
post('bookmarks/delete', bookmark_id: bookmark_id) post('/api/1/bookmarks/delete', bookmark_id: bookmark_id)
end end
# Stars the specified bookmark. # Stars the specified bookmark.
# @param bookmark_id [String] The id of the bookmark. # @param bookmark_id [String] The id of the bookmark.
def star(bookmark_id) def star(bookmark_id)
post('bookmarks/star', bookmark_id: bookmark_id).first post('/api/1/bookmarks/star', bookmark_id: bookmark_id).first
end end
alias_method :star_bookmark, :star alias_method :star_bookmark, :star
# Un-stars the specified bookmark. # Un-stars the specified bookmark.
# @param bookmark_id [String] The id of the bookmark. # @param bookmark_id [String] The id of the bookmark.
def unstar(bookmark_id) def unstar(bookmark_id)
post('bookmarks/unstar', bookmark_id: bookmark_id).first post('/api/1/bookmarks/unstar', bookmark_id: bookmark_id).first
end end
alias_method :unstar_bookmark, :unstar alias_method :unstar_bookmark, :unstar
# Moves the specified bookmark to the Archive. # Moves the specified bookmark to the Archive.
# @param bookmark_id [String] The id of the bookmark. # @param bookmark_id [String] The id of the bookmark.
def archive(bookmark_id) def archive(bookmark_id)
post('bookmarks/archive', bookmark_id: bookmark_id).first post('/api/1/bookmarks/archive', bookmark_id: bookmark_id).first
end end
alias_method :archive_bookmark, :archive alias_method :archive_bookmark, :archive
# Moves the specified bookmark to the top of the Unread folder. # Moves the specified bookmark to the top of the Unread folder.
# @param bookmark_id [String] The id of the bookmark. # @param bookmark_id [String] The id of the bookmark.
def unarchive(bookmark_id) def unarchive(bookmark_id)
post('bookmarks/unarchive', bookmark_id: bookmark_id).first post('/api/1/bookmarks/unarchive', bookmark_id: bookmark_id).first
end end
alias_method :unarchive_bookmark, :unarchive alias_method :unarchive_bookmark, :unarchive
@ -63,7 +63,7 @@ module Instapaper
# @param bookmark_id [String] The id of the bookmark. # @param bookmark_id [String] The id of the bookmark.
# @param folder_id [String] The id of the folder to move the bookmark to. # @param folder_id [String] The id of the folder to move the bookmark to.
def move(bookmark_id, folder_id) def move(bookmark_id, folder_id)
post('bookmarks/move', bookmark_id: bookmark_id, folder_id: folder_id).first post('/api/1/bookmarks/move', bookmark_id: bookmark_id, folder_id: folder_id).first
end end
alias_method :move_bookmark, :move alias_method :move_bookmark, :move
@ -71,7 +71,7 @@ module Instapaper
# always text/html encoded as UTF-8. # always text/html encoded as UTF-8.
# @param bookmark_id [String] The id of the bookmark. # @param bookmark_id [String] The id of the bookmark.
def text(bookmark_id) def text(bookmark_id)
post('bookmarks/get_text', {bookmark_id: bookmark_id}, true).body post('/api/1/bookmarks/get_text', {bookmark_id: bookmark_id}, true).body
end end
alias_method :get_text, :text alias_method :get_text, :text
end end

View file

@ -1,23 +1,23 @@
module Instapaper module Instapaper
class Client module API
# Defines methods related to folders # Defines methods related to folders
module Folder module Folder
# List the account's user-created folders. # List the account's user-created folders.
# @note This only includes organizational folders and does not include RSS-feed folders or starred-subscription folders # @note This only includes organizational folders and does not include RSS-feed folders or starred-subscription folders
def folders def folders
post('folders/list') post('/api/1/folders/list')
end end
# Creates an organizational folder. # Creates an organizational folder.
# @param title [String] The title of the folder to create # @param title [String] The title of the folder to create
def add_folder(title) def add_folder(title)
post('folders/add', title: title) post('/api/1/folders/add', title: title)
end end
# Deletes the folder and moves any articles in it to the Archive. # Deletes the folder and moves any articles in it to the Archive.
# @param folder_id [String] The id of the folder. # @param folder_id [String] The id of the folder.
def delete_folder(folder_id) def delete_folder(folder_id)
post('folders/delete', folder_id: folder_id) post('/api/1/folders/delete', folder_id: folder_id)
end end
# Re-orders a user's folders. # Re-orders a user's folders.
@ -25,7 +25,7 @@ module Instapaper
# @example Ordering folder_ids 100, 200, and 300 # @example Ordering folder_ids 100, 200, and 300
# Instapaper.set_order(['100:1','200:2','300:3']) # Instapaper.set_order(['100:1','200:2','300:3'])
def set_order(order = []) # rubocop:disable Style/AccessorMethodName def set_order(order = []) # rubocop:disable Style/AccessorMethodName
post('folders/set_order', order: order.join(',')) post('/api/1/folders/set_order', order: order.join(','))
end end
end end
end end

View file

@ -0,0 +1,7 @@
module Instapaper
module API
# Defines methods related to highlights
module Highlight
end
end
end

View file

@ -1,10 +1,10 @@
module Instapaper module Instapaper
class Client module API
# Defines methods related to users # Defines methods related to users
module User module User
# Gets an OAuth access token for a user. # Gets an OAuth access token for a user.
def access_token(username, password) def access_token(username, password)
response = post('oauth/access_token', {x_auth_username: username, x_auth_password: password, x_auth_mode: 'client_auth'}, true) response = post('/api/1/oauth/access_token', {x_auth_username: username, x_auth_password: password, x_auth_mode: 'client_auth'}, true)
params = response.body.split('&') params = response.body.split('&')
values = params.map { |part| part.split('=') }.flatten values = params.map { |part| part.split('=') }.flatten
values.unshift('error') if values.length == 1 values.unshift('error') if values.length == 1

View file

@ -1,32 +0,0 @@
module Instapaper
# @private
module Authentication
private
# Authentication hash
#
# @return [Hash]
def authentication
{
consumer_key: consumer_key,
consumer_secret: consumer_secret,
token: oauth_token,
token_secret: oauth_token_secret,
}
end
def consumer_tokens
{
consumer_key: consumer_key,
consumer_secret: consumer_secret,
}
end
# Check whether user is authenticated
#
# @return [Boolean]
def authenticated?
authentication.values.all?
end
end
end

View file

@ -1,10 +1,54 @@
require 'instapaper/connection' require 'instapaper/api'
require 'instapaper/request' require 'instapaper/error'
require 'instapaper/authentication' require 'instapaper/version'
require 'faraday_middleware'
require 'faraday/response/raise_http_1xxx'
module Instapaper module Instapaper
# Wrapper for the Instapaper REST API # Wrapper for the Instapaper REST API
class Client class Client
include Instapaper::API
# An array of valid keys in the options hash when configuring a {Instapaper::API}
VALID_OPTIONS_KEYS = [
:adapter,
:consumer_key,
:consumer_secret,
:endpoint,
:oauth_token,
:oauth_token_secret,
:proxy,
:user_agent,
:connection_options].freeze
# The adapter that will be used to connect if none is set
#
# @note The default faraday adapter is Net::HTTP.
DEFAULT_ADAPTER = :net_http
# By default, don't set an application key
DEFAULT_CONSUMER_KEY = nil
# By default, don't set an application secret
DEFAULT_CONSUMER_SECRET = nil
# The endpoint that will be used to connect if none is set
DEFAULT_ENDPOINT = 'https://www.instapaper.com'.freeze
# By default, don't set a user oauth token
DEFAULT_OAUTH_TOKEN = nil
# By default, don't set a user oauth secret
DEFAULT_OAUTH_TOKEN_SECRET = nil
# By default, don't use a proxy server
DEFAULT_PROXY = nil
# The user agent that will be sent to the API endpoint if none is set
DEFAULT_USER_AGENT = "Instapaper Ruby Gem #{Instapaper::VERSION}".freeze
DEFAULT_CONNECTION_OPTIONS = {}
# @private # @private
attr_accessor :adapter attr_accessor :adapter
attr_accessor :consumer_key attr_accessor :consumer_key
@ -13,41 +57,100 @@ module Instapaper
attr_accessor :oauth_token attr_accessor :oauth_token
attr_accessor :oauth_token_secret attr_accessor :oauth_token_secret
attr_accessor :proxy attr_accessor :proxy
attr_accessor :version
attr_accessor :path_prefix
attr_accessor :user_agent attr_accessor :user_agent
attr_accessor :connection_options attr_accessor :connection_options
alias_method :api_endpoint, :endpoint
alias_method :api_version, :version
# Creates a new API # Creates a new API
def initialize(options = {}) def initialize(options = {})
options = Instapaper.options.merge(options) reset
Configuration::VALID_OPTIONS_KEYS.each do |key| options.keys.each do |key|
send("#{key}=", options[key]) send("#{key}=", options[key])
end end
end end
def endpoint_with_prefix private
api_endpoint + path_prefix
def connection(raw = false) # rubocop:disable AbcSize, CyclomaticComplexity, MethodLength, PerceivedComplexity
merged_options = connection_defaults.merge(@connection_options)
Faraday.new(merged_options) do |builder|
if authenticated?
builder.use Faraday::Request::OAuth, authentication
else
builder.use Faraday::Request::OAuth, consumer_tokens
end
builder.use Faraday::Request::Multipart
builder.use Faraday::Request::UrlEncoded
builder.use Faraday::Response::Rashify unless raw
builder.use Faraday::Response::ParseJson unless raw
builder.use Faraday::Response::RaiseHttp1xxx
builder.adapter(adapter)
end
end end
include Connection def connection_defaults
include Request {
include Authentication headers: {
'Accept' => 'application/json',
'User-Agent' => @user_agent,
},
proxy: @proxy,
url: @endpoint,
}
end
# Require client method modules after initializing the Client class in # Perform an HTTP POST request
# order to avoid a superclass mismatch error, allowing those modules to be def post(path, options = {}, raw = false)
# Client-namespaced. request(:post, path, options, raw)
require 'instapaper/client/account' end
require 'instapaper/client/user'
require 'instapaper/client/bookmark'
require 'instapaper/client/folder'
include Instapaper::Client::Account # Perform an HTTP request
include Instapaper::Client::User def request(method, path, options, raw = false)
include Instapaper::Client::Bookmark response = connection(raw).send(method) do |request|
include Instapaper::Client::Folder request.path = path
request.body = options unless options.empty?
end
raw ? response : response.body
end
# Authentication hash
#
# @return [Hash]
def authentication
{
consumer_key: @consumer_key,
consumer_secret: @consumer_secret,
token: @oauth_token,
token_secret: @oauth_token_secret,
}
end
def consumer_tokens
{
consumer_key: @consumer_key,
consumer_secret: @consumer_secret,
}
end
# Check whether user is authenticated
#
# @return [Boolean]
def authenticated?
authentication.values.all?
end
# Reset all configuration options to defaults
def reset # rubocop:disable MethodLength
@adapter = DEFAULT_ADAPTER
@consumer_key = DEFAULT_CONSUMER_KEY
@consumer_secret = DEFAULT_CONSUMER_SECRET
@endpoint = DEFAULT_ENDPOINT
@oauth_token = DEFAULT_OAUTH_TOKEN
@oauth_token_secret = DEFAULT_OAUTH_TOKEN_SECRET
@proxy = DEFAULT_PROXY
@user_agent = DEFAULT_USER_AGENT
@connection_options = DEFAULT_CONNECTION_OPTIONS
end
end end
end end

View file

@ -1,97 +0,0 @@
require 'instapaper/version'
module Instapaper
module Configuration
# An array of valid keys in the options hash when configuring a {Instapaper::API}
VALID_OPTIONS_KEYS = [
:adapter,
:consumer_key,
:consumer_secret,
:endpoint,
:oauth_token,
:oauth_token_secret,
:proxy,
:version,
:path_prefix,
:user_agent,
:connection_options].freeze
# The adapter that will be used to connect if none is set
#
# @note The default faraday adapter is Net::HTTP.
DEFAULT_ADAPTER = :net_http
# By default, don't set an application key
DEFAULT_CONSUMER_KEY = nil
# By default, don't set an application secret
DEFAULT_CONSUMER_SECRET = nil
# The endpoint that will be used to connect if none is set
DEFAULT_ENDPOINT = 'https://www.instapaper.com/'.freeze
# The version of the API.
DEFAULT_VERSION = '1'
DEFAULT_PATH_PREFIX = 'api/' + DEFAULT_VERSION + '/'
# By default, don't set a user oauth token
DEFAULT_OAUTH_TOKEN = nil
# By default, don't set a user oauth secret
DEFAULT_OAUTH_TOKEN_SECRET = nil
# By default, don't use a proxy server
DEFAULT_PROXY = nil
# The user agent that will be sent to the API endpoint if none is set
DEFAULT_USER_AGENT = "Instapaper Ruby Gem #{Instapaper::VERSION}".freeze
DEFAULT_CONNECTION_OPTIONS = {}
attr_accessor :adapter
attr_accessor :consumer_key
attr_accessor :consumer_secret
attr_accessor :endpoint
attr_accessor :oauth_token
attr_accessor :oauth_token_secret
attr_accessor :proxy
attr_accessor :version
attr_accessor :path_prefix
attr_accessor :user_agent
attr_accessor :connection_options
# When this module is extended, set all configuration options to their default values
def self.extended(base)
base.reset
end
# Convenience method to allow configuration options to be set in a block
def configure
yield self
end
# Create a hash of options and their values
def options
options = {}
VALID_OPTIONS_KEYS.each { |k| options[k] = send(k) }
options
end
# Reset all configuration options to defaults
def reset # rubocop:disable MethodLength
self.adapter = DEFAULT_ADAPTER
self.consumer_key = DEFAULT_CONSUMER_KEY
self.consumer_secret = DEFAULT_CONSUMER_SECRET
self.endpoint = DEFAULT_ENDPOINT
self.oauth_token = DEFAULT_OAUTH_TOKEN
self.oauth_token_secret = DEFAULT_OAUTH_TOKEN_SECRET
self.proxy = DEFAULT_PROXY
self.user_agent = DEFAULT_USER_AGENT
self.version = DEFAULT_VERSION
self.path_prefix = DEFAULT_PATH_PREFIX
self.connection_options = DEFAULT_CONNECTION_OPTIONS
self
end
end
end

View file

@ -1,38 +0,0 @@
require 'faraday_middleware'
require 'faraday/response/raise_http_1xxx'
module Instapaper
# @private
module Connection
private
def connection(raw = false) # rubocop:disable AbcSize, CyclomaticComplexity, MethodLength, PerceivedComplexity
merged_options = connection_defaults.merge(connection_options)
Faraday.new(merged_options) do |builder|
if authenticated?
builder.use Faraday::Request::OAuth, authentication
else
builder.use Faraday::Request::OAuth, consumer_tokens
end
builder.use Faraday::Request::Multipart
builder.use Faraday::Request::UrlEncoded
builder.use Faraday::Response::Rashify unless raw
builder.use Faraday::Response::ParseJson unless raw
builder.use Faraday::Response::RaiseHttp1xxx
builder.adapter(adapter)
end
end
def connection_defaults
{
headers: {
'Accept' => 'application/json',
'User-Agent' => user_agent,
},
proxy: proxy,
url: api_endpoint,
}
end
end
end

View file

@ -1,20 +0,0 @@
module Instapaper
# Defines HTTP request methods
module Request
# Perform an HTTP POST request
def post(path, options = {}, raw = false)
request(:post, path, options, raw)
end
private
# Perform an HTTP request
def request(method, path, options, raw = false)
response = connection(raw).send(method) do |request|
request.path = path_prefix + path
request.body = options unless options.empty?
end
raw ? response : response.body
end
end
end

View file

@ -9,7 +9,7 @@ describe Faraday::Response do
1251, 1252, 1500, 1550].each do |status| 1251, 1252, 1500, 1550].each do |status|
context "when HTTP status is #{status}" do context "when HTTP status is #{status}" do
before do before do
stub_post('folders/list').to_return(status: status) stub_post('/api/1/folders/list').to_return(status: status)
end end
it 'should raise Instapaper::Error error' do it 'should raise Instapaper::Error error' do

View file

@ -1,24 +1,22 @@
require 'spec_helper' require 'spec_helper'
describe Instapaper::Client::Account do describe Instapaper::Client::Account do
before(:each) do let(:client) { Instapaper::Client.new }
@client = Instapaper::Client.new
end
describe '.verify_credentials' do describe '.verify_credentials' do
before do before do
stub_post('account/verify_credentials') stub_post('/api/1/account/verify_credentials')
.to_return(body: fixture('verify_credentials.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('verify_credentials.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.verify_credentials client.verify_credentials
expect(a_post('account/verify_credentials')) expect(a_post('/api/1/account/verify_credentials'))
.to have_been_made .to have_been_made
end end
it 'should return the user' do it 'should return the user' do
user = @client.verify_credentials.first user = client.verify_credentials.first
expect(user).to be_a Hashie::Rash expect(user).to be_a Hashie::Rash
expect(user.username).to eq('TestUserOMGLOL') expect(user.username).to eq('TestUserOMGLOL')
end end

View file

@ -1,30 +1,28 @@
require 'spec_helper' require 'spec_helper'
describe Instapaper::Client::Bookmark do describe Instapaper::Client::Bookmark do
before(:each) do let(:client) { Instapaper::Client.new(consumer_key: 'CK', consumer_secret: 'CS', oauth_token: 'OT', oauth_token_secret: 'OS') }
@client = Instapaper::Client.new(consumer_key: 'CK', consumer_secret: 'CS', oauth_token: 'OT', oauth_token_secret: 'OS')
end
describe '.bookmarks' do describe '.bookmarks' do
before do before do
stub_post('bookmarks/list') stub_post('/api/1/bookmarks/list')
.to_return(body: fixture('bookmarks_list.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_list.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.bookmarks client.bookmarks
expect(a_post('bookmarks/list')) expect(a_post('/api/1/bookmarks/list'))
.to have_been_made .to have_been_made
end end
it 'should return an array containing bookmarks on success' do it 'should return an array containing bookmarks on success' do
bookmarks = @client.bookmarks bookmarks = client.bookmarks
expect(bookmarks).to be_an Array expect(bookmarks).to be_an Array
expect(bookmarks.size).to eq(2) expect(bookmarks.size).to eq(2)
end end
it 'should remove the meta and current user objects from the array' do it 'should remove the meta and current user objects from the array' do
bookmarks = @client.bookmarks bookmarks = client.bookmarks
bookmarks.each do |bookmark| bookmarks.each do |bookmark|
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
@ -35,18 +33,18 @@ describe Instapaper::Client::Bookmark do
describe '.update_read_progress' do describe '.update_read_progress' do
before do before do
@time = Time.now @time = Time.now
stub_post('bookmarks/update_read_progress') stub_post('/api/1/bookmarks/update_read_progress')
.to_return(body: fixture('bookmarks_update_read_progress.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_update_read_progress.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.update_read_progress(123, 0.5, @time) client.update_read_progress(123, 0.5, @time)
expect(a_post('bookmarks/update_read_progress').with(body: {bookmark_id: '123', progress: '0.5', progress_timestamp: @time.to_i.to_s})) expect(a_post('/api/1/bookmarks/update_read_progress').with(body: {bookmark_id: '123', progress: '0.5', progress_timestamp: @time.to_i.to_s}))
.to have_been_made .to have_been_made
end end
it 'should return an array containing bookmarks on success' do it 'should return an array containing bookmarks on success' do
bookmark = @client.update_read_progress(123, 0.5, @time) bookmark = client.update_read_progress(123, 0.5, @time)
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
expect(bookmark.progress).to eq('0.5') expect(bookmark.progress).to eq('0.5')
@ -55,18 +53,18 @@ describe Instapaper::Client::Bookmark do
describe '.add_bookmark' do describe '.add_bookmark' do
before do before do
stub_post('bookmarks/add') stub_post('/api/1/bookmarks/add')
.to_return(body: fixture('bookmarks_add.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_add.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.add_bookmark('http://someurl.com', title: 'This is the title', description: 'This is the description') client.add_bookmark('http://someurl.com', title: 'This is the title', description: 'This is the description')
expect(a_post('bookmarks/add').with(body: {url: 'http://someurl.com', title: 'This is the title', description: 'This is the description'})) expect(a_post('/api/1/bookmarks/add').with(body: {url: 'http://someurl.com', title: 'This is the title', description: 'This is the description'}))
.to have_been_made .to have_been_made
end end
it 'should return the bookmark on success' do it 'should return the bookmark on success' do
bookmark = @client.add_bookmark('http://someurl.com', title: 'This is the title', description: 'This is the description') bookmark = client.add_bookmark('http://someurl.com', title: 'This is the title', description: 'This is the description')
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
end end
@ -74,18 +72,18 @@ describe Instapaper::Client::Bookmark do
describe '.delete_bookmark' do describe '.delete_bookmark' do
before do before do
stub_post('bookmarks/delete') stub_post('/api/1/bookmarks/delete')
.to_return(body: '[]', headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: '[]', headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.delete_bookmark(123) client.delete_bookmark(123)
expect(a_post('bookmarks/delete').with(body: {bookmark_id: '123'})) expect(a_post('/api/1/bookmarks/delete').with(body: {bookmark_id: '123'}))
.to have_been_made .to have_been_made
end end
it 'should return an array containing bookmarks on success' do it 'should return an array containing bookmarks on success' do
confirm = @client.delete_bookmark(123) confirm = client.delete_bookmark(123)
expect(confirm).to be_an Array expect(confirm).to be_an Array
expect(confirm).to be_empty expect(confirm).to be_empty
end end
@ -93,141 +91,141 @@ describe Instapaper::Client::Bookmark do
describe '.star' do describe '.star' do
before do before do
stub_post('bookmarks/star') stub_post('/api/1/bookmarks/star')
.to_return(body: fixture('bookmarks_star.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_star.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.star(123) client.star(123)
expect(a_post('bookmarks/star').with(body: {bookmark_id: '123'})) expect(a_post('/api/1/bookmarks/star').with(body: {bookmark_id: '123'}))
.to have_been_made .to have_been_made
end end
it 'should return a starred bookmark on success' do it 'should return a starred bookmark on success' do
bookmark = @client.star(123) bookmark = client.star(123)
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
expect(bookmark.starred).to eq('1') expect(bookmark.starred).to eq('1')
end end
it 'should be aliased as .star_bookmark' do it 'should be aliased as .star_bookmark' do
expect(@client.star(123)).to eq(@client.star_bookmark(123)) expect(client.star(123)).to eq(client.star_bookmark(123))
end end
end end
describe '.unstar' do describe '.unstar' do
before do before do
stub_post('bookmarks/unstar') stub_post('/api/1/bookmarks/unstar')
.to_return(body: fixture('bookmarks_unstar.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_unstar.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.unstar(123) client.unstar(123)
expect(a_post('bookmarks/unstar').with(body: {bookmark_id: '123'})) expect(a_post('/api/1/bookmarks/unstar').with(body: {bookmark_id: '123'}))
.to have_been_made .to have_been_made
end end
it 'should return an unstarred bookmark on success' do it 'should return an unstarred bookmark on success' do
bookmark = @client.unstar(123) bookmark = client.unstar(123)
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
expect(bookmark.starred).to eq('0') expect(bookmark.starred).to eq('0')
end end
it 'should be aliased as .unstar_bookmark' do it 'should be aliased as .unstar_bookmark' do
expect(@client.unstar(123)).to eq(@client.unstar_bookmark(123)) expect(client.unstar(123)).to eq(client.unstar_bookmark(123))
end end
end end
describe '.archive' do describe '.archive' do
before do before do
stub_post('bookmarks/archive') stub_post('/api/1/bookmarks/archive')
.to_return(body: fixture('bookmarks_archive.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_archive.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.archive(123) client.archive(123)
expect(a_post('bookmarks/archive').with(body: {bookmark_id: '123'})) expect(a_post('/api/1/bookmarks/archive').with(body: {bookmark_id: '123'}))
.to have_been_made .to have_been_made
end end
it 'should return the bookmark on success' do it 'should return the bookmark on success' do
bookmark = @client.archive(123) bookmark = client.archive(123)
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
end end
it 'should be aliased as .archive_bookmark' do it 'should be aliased as .archive_bookmark' do
expect(@client.archive(123)).to eq(@client.archive_bookmark(123)) expect(client.archive(123)).to eq(client.archive_bookmark(123))
end end
end end
describe '.unarchive' do describe '.unarchive' do
before do before do
stub_post('bookmarks/unarchive') stub_post('/api/1/bookmarks/unarchive')
.to_return(body: fixture('bookmarks_unarchive.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_unarchive.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.unarchive(123) client.unarchive(123)
expect(a_post('bookmarks/unarchive').with(body: {bookmark_id: '123'})) expect(a_post('/api/1/bookmarks/unarchive').with(body: {bookmark_id: '123'}))
.to have_been_made .to have_been_made
end end
it 'should return the bookmark on success' do it 'should return the bookmark on success' do
bookmark = @client.unarchive(123) bookmark = client.unarchive(123)
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
end end
it 'should be aliased as .unarchive_bookmark' do it 'should be aliased as .unarchive_bookmark' do
expect(@client.unarchive(123)).to eq(@client.unarchive_bookmark(123)) expect(client.unarchive(123)).to eq(client.unarchive_bookmark(123))
end end
end end
describe '.move' do describe '.move' do
before do before do
stub_post('bookmarks/move') stub_post('/api/1/bookmarks/move')
.to_return(body: fixture('bookmarks_move.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('bookmarks_move.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.move(123, 12_345) client.move(123, 12_345)
expect(a_post('bookmarks/move').with(body: {bookmark_id: '123', folder_id: '12345'})) expect(a_post('/api/1/bookmarks/move').with(body: {bookmark_id: '123', folder_id: '12345'}))
.to have_been_made .to have_been_made
end end
it 'should return the bookmark on success' do it 'should return the bookmark on success' do
bookmark = @client.move(123, 12_345) bookmark = client.move(123, 12_345)
expect(bookmark).to be_a Hashie::Rash expect(bookmark).to be_a Hashie::Rash
expect(bookmark.type).to eq('bookmark') expect(bookmark.type).to eq('bookmark')
end end
it 'should be aliased as .move_bookmark' do it 'should be aliased as .move_bookmark' do
expect(@client.move(123, 12_345)).to eq(@client.move_bookmark(123, 12_345)) expect(client.move(123, 12_345)).to eq(client.move_bookmark(123, 12_345))
end end
end end
describe '.text' do describe '.text' do
before do before do
stub_post('bookmarks/get_text') stub_post('/api/1/bookmarks/get_text')
.to_return(body: fixture('bookmarks_get_text.txt'), headers: {content_type: 'text/html; charset=utf-8'}) .to_return(body: fixture('bookmarks_get_text.txt'), headers: {content_type: 'text/html; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.text(123) client.text(123)
expect(a_post('bookmarks/get_text').with(body: {bookmark_id: '123'})) expect(a_post('/api/1/bookmarks/get_text').with(body: {bookmark_id: '123'}))
.to have_been_made .to have_been_made
end end
it "should return the bookmark's html on success" do it "should return the bookmark's html on success" do
bookmark = @client.text(123) bookmark = client.text(123)
expect(bookmark.length).to be > 0 expect(bookmark.length).to be > 0
expect(bookmark).to include("Ideo's Axioms for Starting Disruptive New Businesses") expect(bookmark).to include("Ideo's Axioms for Starting Disruptive New Businesses")
end end
it 'should be aliased as .get_text' do it 'should be aliased as .get_text' do
expect(@client.text(123)).to eq(@client.get_text(123)) expect(client.text(123)).to eq(client.get_text(123))
end end
end end
end end

View file

@ -1,24 +1,22 @@
require 'spec_helper' require 'spec_helper'
describe Instapaper::Client::Folder do describe Instapaper::Client::Folder do
before(:each) do let(:client) { Instapaper::Client.new(consumer_key: 'CK', consumer_secret: 'CS', oauth_token: 'OT', oauth_token_secret: 'OS') }
@client = Instapaper::Client.new
end
describe '.folders' do describe '.folders' do
before do before do
stub_post('folders/list') stub_post('/api/1/folders/list')
.to_return(body: fixture('folders_list.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('folders_list.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.folders client.folders
expect(a_post('folders/list')) expect(a_post('/api/1/folders/list'))
.to have_been_made .to have_been_made
end end
it 'should return an array containing folders on success' do it 'should return an array containing folders on success' do
folders = @client.folders folders = client.folders
expect(folders).to be_an Array expect(folders).to be_an Array
expect(folders.size).to eq(2) expect(folders.size).to eq(2)
expect(folders.first).to be_a Hashie::Rash expect(folders.first).to be_a Hashie::Rash
@ -28,18 +26,18 @@ describe Instapaper::Client::Folder do
describe '.add_folder' do describe '.add_folder' do
before do before do
stub_post('folders/add').with(body: {title: 'Ruby'}) stub_post('/api/1/folders/add').with(body: {title: 'Ruby'})
.to_return(body: fixture('folders_add.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('folders_add.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.add_folder('Ruby') client.add_folder('Ruby')
expect(a_post('folders/add')) expect(a_post('/api/1/folders/add'))
.to have_been_made .to have_been_made
end end
it 'should return an array containing the new folder on success' do it 'should return an array containing the new folder on success' do
folders = @client.add_folder('Ruby') folders = client.add_folder('Ruby')
expect(folders).to be_an Array expect(folders).to be_an Array
expect(folders).not_to be_empty expect(folders).not_to be_empty
expect(folders.first).to be_a Hashie::Rash expect(folders.first).to be_a Hashie::Rash
@ -49,18 +47,18 @@ describe Instapaper::Client::Folder do
describe '.delete_folder' do describe '.delete_folder' do
before do before do
stub_post('folders/delete'). with(body: {folder_id: '1'}) stub_post('/api/1/folders/delete'). with(body: {folder_id: '1'})
.to_return(body: fixture('folders_delete.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('folders_delete.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.delete_folder('1') client.delete_folder('1')
expect(a_post('folders/delete')) expect(a_post('/api/1/folders/delete'))
.to have_been_made .to have_been_made
end end
it 'should return an empty array on success' do it 'should return an empty array on success' do
confirm = @client.delete_folder('1') confirm = client.delete_folder('1')
expect(confirm).to be_an Array expect(confirm).to be_an Array
expect(confirm).to be_empty expect(confirm).to be_empty
end end
@ -68,18 +66,18 @@ describe Instapaper::Client::Folder do
describe '.set_order' do describe '.set_order' do
before do before do
stub_post('folders/set_order'). with(body: {order: '1121173:2,1121174:1'}) stub_post('/api/1/folders/set_order'). with(body: {order: '1121173:2,1121174:1'})
.to_return(body: fixture('folders_set_order.json'), headers: {content_type: 'application/json; charset=utf-8'}) .to_return(body: fixture('folders_set_order.json'), headers: {content_type: 'application/json; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.set_order(['1121173:2', '1121174:1']) client.set_order(['1121173:2', '1121174:1'])
expect(a_post('folders/set_order')) expect(a_post('/api/1/folders/set_order'))
.to have_been_made .to have_been_made
end end
it 'should return an array reflecting the new order on success' do it 'should return an array reflecting the new order on success' do
folders = @client.set_order(['1121173:2', '1121174:1']) folders = client.set_order(['1121173:2', '1121174:1'])
expect(folders).to be_an Array expect(folders).to be_an Array
expect(folders.first).to be_a Hashie::Rash expect(folders.first).to be_a Hashie::Rash
expect(folders.first['position']).to eq(1) expect(folders.first['position']).to eq(1)

View file

@ -1,33 +1,31 @@
require 'spec_helper' require 'spec_helper'
describe Instapaper::Client::User do describe Instapaper::Client::User do
before(:each) do let(:client) { Instapaper::Client.new }
@client = Instapaper::Client.new
end
describe '.access_token' do describe '.access_token' do
before do before do
stub_post('oauth/access_token').with(body: {x_auth_username: 'ohai', x_auth_password: 'p455w0rd', x_auth_mode: 'client_auth'}) stub_post('/api/1/oauth/access_token').with(body: {x_auth_username: 'ohai', x_auth_password: 'p455w0rd', x_auth_mode: 'client_auth'})
.to_return(body: fixture('access_token.qline'), headers: {content_type: 'text/plain; charset=utf-8'}) .to_return(body: fixture('access_token.qline'), headers: {content_type: 'text/plain; charset=utf-8'})
stub_post('oauth/access_token').with(body: {x_auth_username: 'inval1d', x_auth_password: 'cr3dentials', x_auth_mode: 'client_auth'}) stub_post('/api/1/oauth/access_token').with(body: {x_auth_username: 'inval1d', x_auth_password: 'cr3dentials', x_auth_mode: 'client_auth'})
.to_return(body: fixture('invalid_credentials.qline'), headers: {content_type: 'text/plain; charset=utf-8'}) .to_return(body: fixture('invalid_credentials.qline'), headers: {content_type: 'text/plain; charset=utf-8'})
end end
it 'should get the correct resource' do it 'should get the correct resource' do
@client.access_token('ohai', 'p455w0rd') client.access_token('ohai', 'p455w0rd')
expect(a_post('oauth/access_token')) expect(a_post('/api/1/oauth/access_token'))
.to have_been_made .to have_been_made
end end
it 'should return the a hash containing an oauth token and secret' do it 'should return the a hash containing an oauth token and secret' do
tokens = @client.access_token('ohai', 'p455w0rd') tokens = client.access_token('ohai', 'p455w0rd')
expect(tokens).to be_a Hash expect(tokens).to be_a Hash
expect(tokens.key?('oauth_token')).to be true expect(tokens.key?('oauth_token')).to be true
expect(tokens.key?('oauth_token_secret')).to be true expect(tokens.key?('oauth_token_secret')).to be true
end end
it 'should return a hash containing the error on invalid credentials' do it 'should return a hash containing the error on invalid credentials' do
tokens = @client.access_token('inval1d', 'cr3dentials') tokens = client.access_token('inval1d', 'cr3dentials')
expect(tokens).to be_a Hash expect(tokens).to be_a Hash
expect(tokens.key?('error')).to be true expect(tokens.key?('error')).to be true
end end

View file

@ -1,64 +1,5 @@
require 'spec_helper' require 'spec_helper'
describe Instapaper::Client do describe Instapaper::Client do
before do let(:client) { Instapaper::Client.new }
@options = {adapter: :em_synchrony, user_agent: 'Instapaper::Client spec'}
@keys = Instapaper::Configuration::VALID_OPTIONS_KEYS
end
describe '.new' do
before(:each) do
@keys.each do |key|
Instapaper.send("#{key}=", key)
end
end
after do
Instapaper.reset
end
context 'with module configuration' do
it 'should inherit module configuration' do
api = Instapaper::Client.new
@keys.each do |key|
expect(api.send(key)).to eq(key)
end
end
end
context 'with class configuration' do
context 'during initialization' do
it 'should override module configuration' do
api = Instapaper::Client.new(@options)
@keys.each do |key|
h = @options.key?(key) ? @options : Instapaper.options
expect(api.send(key)).to eq(h[key])
end
end
end
context 'after initialization' do
it 'should override module configuration after initialization' do
api = Instapaper::Client.new
@options.each do |key, value|
api.send("#{key}=", value)
end
@keys.each do |key|
h = @options.key?(key) ? @options : Instapaper.options
expect(api.send(key)).to eq(h[key])
end
end
end
end
end
describe '.endpoint_with_prefix' do
before(:each) do
@client = Instapaper::Client.new
end
it 'should return the ' do
expect(@client.endpoint_with_prefix).to eq(Instapaper.endpoint + Instapaper.path_prefix)
end
end
end end

View file

@ -1,82 +0,0 @@
require 'spec_helper'
describe Instapaper do
after do
Instapaper.reset
end
describe '.respond_to?' do
it 'takes an optional include private argument' do
expect(Instapaper.respond_to?(:client, true)).to be true
end
end
describe '.client' do
it 'should be a Instapaper::Client' do
expect(Instapaper.client).to be_a Instapaper::Client
end
end
describe '.adapter' do
it 'should return the default adapter' do
expect(Instapaper.adapter).to eq(Instapaper::Configuration::DEFAULT_ADAPTER)
end
end
describe '.adapter=' do
it 'should set the adapter' do
Instapaper.adapter = :typhoeus
expect(Instapaper.adapter).to eq(:typhoeus)
end
end
describe '.endpoint' do
it 'should return the default endpoint' do
expect(Instapaper.endpoint).to eq(Instapaper::Configuration::DEFAULT_ENDPOINT)
end
end
describe '.endpoint=' do
it 'should set the endpoint' do
Instapaper.endpoint = 'http://tumblr.com/'
expect(Instapaper.endpoint).to eq('http://tumblr.com/')
end
end
describe '.user_agent' do
it 'should return the default user agent' do
expect(Instapaper.user_agent).to eq(Instapaper::Configuration::DEFAULT_USER_AGENT)
end
end
describe '.user_agent=' do
it 'should set the user_agent' do
Instapaper.user_agent = 'Custom User Agent'
expect(Instapaper.user_agent).to eq('Custom User Agent')
end
end
describe '.version' do
it 'should return the default version' do
expect(Instapaper.version).to eq(Instapaper::Configuration::DEFAULT_VERSION)
end
end
describe '.version=' do
it 'should set the user_agent' do
Instapaper.version = '2'
expect(Instapaper.version).to eq('2')
end
end
describe '.configure' do
Instapaper::Configuration::VALID_OPTIONS_KEYS.each do |key|
it "should set the #{key}" do
Instapaper.configure do |config|
config.send("#{key}=", key)
expect(Instapaper.send(key)).to eq(key)
end
end
end
end
end

View file

@ -12,35 +12,35 @@ require 'rspec'
require 'webmock/rspec' require 'webmock/rspec'
def a_delete(path) def a_delete(path)
a_request(:delete, Instapaper.endpoint_with_prefix + path) a_request(:delete, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def a_get(path) def a_get(path)
a_request(:get, Instapaper.endpoint_with_prefix + path) a_request(:get, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def a_post(path) def a_post(path)
a_request(:post, Instapaper.endpoint_with_prefix + path) a_request(:post, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def a_put(path) def a_put(path)
a_request(:put, Instapaper.endpoint_with_prefix + path) a_request(:put, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def stub_delete(path) def stub_delete(path)
stub_request(:delete, Instapaper.endpoint_with_prefix + path) stub_request(:delete, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def stub_get(path) def stub_get(path)
stub_request(:get, Instapaper.endpoint_with_prefix + path) stub_request(:get, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def stub_post(path) def stub_post(path)
stub_request(:post, Instapaper.endpoint_with_prefix + path) stub_request(:post, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def stub_put(path) def stub_put(path)
stub_request(:put, Instapaper.endpoint_with_prefix + path) stub_request(:put, Instapaper::Client::DEFAULT_ENDPOINT + path)
end end
def fixture_path def fixture_path