gazette 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +20 -0
- data/README.md +67 -0
- data/Rakefile +37 -0
- data/VERSION +1 -0
- data/gazette.gemspec +69 -0
- data/lib/gazette/api.rb +10 -0
- data/lib/gazette/client.rb +94 -0
- data/lib/gazette/response.rb +39 -0
- data/lib/gazette.rb +5 -0
- data/spec/client/add_spec.rb +98 -0
- data/spec/client/authenticate_spec.rb +60 -0
- data/spec/client/client_spec.rb +84 -0
- data/spec/response/success_spec.rb +33 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +32 -0
- data/todo.txt +1 -0
- metadata +110 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
bluecloth (2.0.9)
|
5
|
+
fakeweb (1.2.8)
|
6
|
+
gemcutter (0.6.1)
|
7
|
+
git (1.2.5)
|
8
|
+
jeweler (1.4.0)
|
9
|
+
gemcutter (>= 0.1.0)
|
10
|
+
git (>= 1.2.5)
|
11
|
+
rubyforge (>= 2.0.0)
|
12
|
+
json_pure (1.4.6)
|
13
|
+
rake (0.8.7)
|
14
|
+
rcov (0.9.9)
|
15
|
+
rspec (1.3.0)
|
16
|
+
rubyforge (2.0.4)
|
17
|
+
json_pure (>= 1.1.7)
|
18
|
+
yard (0.6.1)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
bluecloth
|
25
|
+
fakeweb
|
26
|
+
jeweler
|
27
|
+
rake
|
28
|
+
rcov
|
29
|
+
rspec
|
30
|
+
yard
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jeff Pollard
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Gazette
|
2
|
+
|
3
|
+
Gazette is a Ruby gem to interact with the [Instapaper API](http://www.instapaper.com/api). It offers complete functionality with all API features, including authentication and adding URLs to read later. Gazette operates over HTTP and HTTPS, and uses (Instapaper-preferred) HTTP basic authentication.
|
4
|
+
|
5
|
+
**NOTE: This gem is still in beta. Production use not suggested, but encouraged.**
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
At your favorite shell:
|
10
|
+
|
11
|
+
gem install gazette
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
All interaction with the Instapaper API is done with an instance of `Gazette::Client` object. The constructor requires one argument, the user's Instapaper email or username. The 2nd argument is an optional hash, which can contain a `:password => "user_pass"` and/or `:https => true` if you would like to use HTTPS
|
16
|
+
|
17
|
+
@client = Gazette::Client.new("user@eample.com", :password => "seeecrets")
|
18
|
+
=> #<Gazette::Client:0x101f41cb0 @password=nil, @https=false, @username="user@eample.com", @options={}>
|
19
|
+
|
20
|
+
By default Gazette communicates with the Instapaper API over good 'ol HTTP. You are strongly encouraged to use HTTPS if at all possible.
|
21
|
+
|
22
|
+
|
23
|
+
### Authentication
|
24
|
+
|
25
|
+
With a client in hand, you can call `@client.authenticate` to authenticate the user's credentials. Per the API documentation, authentication is totally optional. It may be useful if you want to verify the credentials provided by a user, but is not a required step before adding URLs.
|
26
|
+
|
27
|
+
>> @client.authenticate
|
28
|
+
=> #<Gazette::Response::Success:0x101f37260 @instapaper_title=nil, @content_location=nil>
|
29
|
+
|
30
|
+
### Responses
|
31
|
+
|
32
|
+
All valid responses to the Instapaper API return a `Gazette::Response::Success` object. All invalid response *raise* one of the following exceptions:
|
33
|
+
|
34
|
+
* `Gazette::Response::InvalidCredentials` - Invalid user credentials.
|
35
|
+
* `Gazette::Response::ServerError` - API encountered an error. Please try again later.
|
36
|
+
* `Gazette::Response::UnknownError` - Some other unknown error. File a bug...maybe.
|
37
|
+
|
38
|
+
Thus, for proper error checking, please `rescue` any/all of the above exceptions.
|
39
|
+
|
40
|
+
### Adding URLs
|
41
|
+
|
42
|
+
To add URLs to the client's Instapaper account, call `@client.add(url)`:
|
43
|
+
|
44
|
+
>> @client.add("http://patmaddox.com/blog/2010/5/9/a-response-to-unit-and-functional-tests-are-as-useful-as-100.html", :title => "How do you write 100% correct code?", :selection => "How do you guarantee an application's correctness?")
|
45
|
+
=> #<Gazette::Response::Success:0x101f17a00 @instapaper_title="\"How do you write 100% correct code?\"", @content_location="http://patmaddox.com/blog/2010/5/9/a-response-to-unit-and-functional-tests-are-as-useful-as-100.html">
|
46
|
+
|
47
|
+
The `#add` method takes only one required parameter, a string of the URL you want to add. The 2nd parameter is an optional hash, which can contain any of the following. See the [Instapaper API documentation](http://www.instapaper.com/api) for details:
|
48
|
+
|
49
|
+
* `:title => "string"`
|
50
|
+
* `:selection => "string"`
|
51
|
+
* `:redirect => :close` which attempts to close the window after a short delay.
|
52
|
+
* `:jsonp => "string"`
|
53
|
+
|
54
|
+
|
55
|
+
## Note on Patches/Pull Requests
|
56
|
+
|
57
|
+
* Fork the project.
|
58
|
+
* Make your feature addition or bug fix.
|
59
|
+
* Add tests for it. This is important so I don't break it in a
|
60
|
+
future version unintentionally.
|
61
|
+
* Commit, do not mess with rakefile, version, or history.
|
62
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
63
|
+
* Send me a pull request. Bonus points for topic branches.
|
64
|
+
|
65
|
+
## Copyright
|
66
|
+
|
67
|
+
Copyright (c) 2010 Jeff Pollard. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts "!! Bundler not found. Please run `gem install bundler` to install and preface commands with `bundle exec`"
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "gazette"
|
10
|
+
gem.summary = %Q{Ruby library to interact with the Instapaper API.}
|
11
|
+
gem.description = %Q{Simple Ruby wrapper gem to interact with the Instapaper API. Supports authenticate and add API methods, as well as https, jsonp and redirect featres of the API.}
|
12
|
+
gem.email = "jeff.pollard@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/Fluxx/gazette"
|
14
|
+
gem.authors = ["Jeff Pollard"]
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
gem.add_development_dependency "yard", ">= 0"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
task :default => :spec
|
35
|
+
|
36
|
+
require 'yard'
|
37
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/gazette.gemspec
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{gazette}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jeff Pollard"]
|
12
|
+
s.date = %q{2010-10-22}
|
13
|
+
s.description = %q{Simple Ruby wrapper gem to interact with the Instapaper API. Supports authenticate and add API methods, as well as https, jsonp and redirect featres of the API.}
|
14
|
+
s.email = %q{jeff.pollard@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE",
|
24
|
+
"README.md",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"gazette.gemspec",
|
28
|
+
"lib/gazette.rb",
|
29
|
+
"lib/gazette/api.rb",
|
30
|
+
"lib/gazette/client.rb",
|
31
|
+
"lib/gazette/response.rb",
|
32
|
+
"spec/client/add_spec.rb",
|
33
|
+
"spec/client/authenticate_spec.rb",
|
34
|
+
"spec/client/client_spec.rb",
|
35
|
+
"spec/response/success_spec.rb",
|
36
|
+
"spec/spec.opts",
|
37
|
+
"spec/spec_helper.rb",
|
38
|
+
"todo.txt"
|
39
|
+
]
|
40
|
+
s.homepage = %q{http://github.com/Fluxx/gazette}
|
41
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
42
|
+
s.require_paths = ["lib"]
|
43
|
+
s.rubygems_version = %q{1.3.6}
|
44
|
+
s.summary = %q{Ruby library to interact with the Instapaper API.}
|
45
|
+
s.test_files = [
|
46
|
+
"spec/client/add_spec.rb",
|
47
|
+
"spec/client/authenticate_spec.rb",
|
48
|
+
"spec/client/client_spec.rb",
|
49
|
+
"spec/response/success_spec.rb",
|
50
|
+
"spec/spec_helper.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
59
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
62
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
63
|
+
end
|
64
|
+
else
|
65
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
66
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
data/lib/gazette/api.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "net/https"
|
3
|
+
require "openssl"
|
4
|
+
require "pp"
|
5
|
+
|
6
|
+
module Gazette
|
7
|
+
# The Client class interacts with the Instapaper API. Client hold user authentication
|
8
|
+
# information, as well as provide methods to authenticate user credentials and add URLs
|
9
|
+
# to their user's Instapaper account.
|
10
|
+
#
|
11
|
+
# @author Jeff Pollard
|
12
|
+
# @see http://www.instapaper.com/api Instapaper-provided API documenation.
|
13
|
+
class Client
|
14
|
+
|
15
|
+
attr_reader :username
|
16
|
+
attr_reader :password
|
17
|
+
|
18
|
+
# Create a new client. Instapaper requires a user username. Most Instapaper users
|
19
|
+
# <b>don't</b> have a password, as such it is optional.
|
20
|
+
#
|
21
|
+
# @param [String] username Instapaper username
|
22
|
+
# @param [Hash] options Additional client options
|
23
|
+
# @option options [String] :password Instapaper username
|
24
|
+
# @option options [Boolean] :https (false) Interact with the Instapaper API over
|
25
|
+
# HTTPS.
|
26
|
+
def initialize(username, options = {})
|
27
|
+
raise ArgumentError.new("2nd parameter must be a Hash") unless options.is_a?(Hash)
|
28
|
+
@username = username
|
29
|
+
@password = options.delete(:password)
|
30
|
+
@options = options
|
31
|
+
@https = !!(options.delete(:https))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Attempts to authenticate the client's user credentials with Instapaper.
|
35
|
+
#
|
36
|
+
# @param [Hash] options Additional authentication options
|
37
|
+
# @option options [String] :jsonp (nil) Returns results as JSON to the specified
|
38
|
+
# Javascript callback.
|
39
|
+
# @return [Response::Success] Successful response from the Instapaper API.
|
40
|
+
# @raise [Response::InvalidCredentials]
|
41
|
+
# @raise [Response::ServerError]
|
42
|
+
# @raise [Response::UnknownError]
|
43
|
+
def authenticate(options = {})
|
44
|
+
parse_response_for request(:authenticate, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds a URL to a user's instapaper account.
|
48
|
+
#
|
49
|
+
# @param [String] url URL of the content to add.
|
50
|
+
# @param [Hash] options Additional add options
|
51
|
+
# @option options [String] :title Title of the content. If omitted, Instapaper will
|
52
|
+
# generate a title.
|
53
|
+
# @option options [String] :select Sample/selection of content.
|
54
|
+
# @option options [:close] :redirect Response returns "Saved!" string and attempts to
|
55
|
+
# close its own window after a short delay.
|
56
|
+
# @option options [String] :jsonp (nil) Returns results as JSON to the specified
|
57
|
+
# Javascript callback.
|
58
|
+
# @return [Response::Success] Successful response from the Instapaper API.
|
59
|
+
# @raise [Response::InvalidCredentials]
|
60
|
+
# @raise [Response::ServerError]
|
61
|
+
# @raise [Response::UnknownError]
|
62
|
+
def add(url, options = {})
|
63
|
+
parse_response_for request(:add, options.merge(:url => url))
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# Handles the response from Instapaper.
|
69
|
+
#
|
70
|
+
# @todo Put the raising logic in the Api class/module, then leave the response return
|
71
|
+
# to this method
|
72
|
+
def parse_response_for(response)
|
73
|
+
case response
|
74
|
+
when Net::HTTPOK, Net::HTTPCreated then return Response::Success.new(response)
|
75
|
+
when Net::HTTPForbidden then raise Response::InvalidCredentials
|
76
|
+
when Net::HTTPInternalServerError then raise Response::ServerError
|
77
|
+
else raise Response::UnknownError
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Actually heads out to the internet and performs the request
|
82
|
+
# @todo Perhaps put me in the Api class/module?
|
83
|
+
def request(method, params = {})
|
84
|
+
http = Net::HTTP.new(Api::ADDRESS, (@https ? 443 : 80))
|
85
|
+
http.use_ssl = @https
|
86
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
87
|
+
request = Net::HTTP::Post.new(Api::ENDPOINT+method.to_s)
|
88
|
+
request.basic_auth @username, @password
|
89
|
+
request.set_form_data(params)
|
90
|
+
http.start { http.request(request) }
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Gazette
|
2
|
+
# Container class for response from the Instapaper API.
|
3
|
+
# @author Jeff Pollard
|
4
|
+
class Response
|
5
|
+
# Successful response from the Instapaper API
|
6
|
+
# @author Jeff Pollard
|
7
|
+
class Success < Response
|
8
|
+
|
9
|
+
# Final saved URL of the content returned from the Instapaper API.
|
10
|
+
attr_reader :content_location
|
11
|
+
|
12
|
+
# Saved title of the content returned from the Instapaper API.
|
13
|
+
attr_reader :instapaper_title
|
14
|
+
|
15
|
+
# Create a new Success object out of a net/http response.
|
16
|
+
def initialize(response)
|
17
|
+
unless response.is_a?(Net::HTTPResponse)
|
18
|
+
# Require a net/http response object
|
19
|
+
raise ArgumentError.new("Argument must be a Net::HTTPResponse object")
|
20
|
+
end
|
21
|
+
|
22
|
+
# Build our ivars from the headers
|
23
|
+
@content_location = response.header['Content-Location']
|
24
|
+
@instapaper_title = response.header['X-Instapaper-Title']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Response raised of the Instapaper returned a response indicating invalid user
|
29
|
+
# credentials.
|
30
|
+
class InvalidCredentials < Exception; end
|
31
|
+
|
32
|
+
# Retponse raised if the Instapaper returned a server error (500).
|
33
|
+
class ServerError < Exception; end
|
34
|
+
|
35
|
+
# Response returned if there was some other type of unknown error.
|
36
|
+
class UnknownError < Exception; end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/gazette.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
INTERESTING_ARTICLE = "http://blog.instapaper.com/post/1256471940"
|
4
|
+
|
5
|
+
describe Gazette::Client, "#add" do
|
6
|
+
before(:each) do
|
7
|
+
# @todo Abstract me out
|
8
|
+
@my_post = stub_http_client('/api/add')
|
9
|
+
@client = Gazette::Client.new("foo")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should raise an ArgumentError if no first argument is provided" do
|
13
|
+
lambda do
|
14
|
+
@client.add
|
15
|
+
end.should raise_error(ArgumentError)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "with a 201 OK response" do
|
19
|
+
before(:each) do
|
20
|
+
stub_instapaper_api(:add => {:status => 201})
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns a Gazette::Response::Success object" do
|
24
|
+
@client.add(INTERESTING_ARTICLE).should be_a Gazette::Response::Success
|
25
|
+
end
|
26
|
+
|
27
|
+
it "calls the add instapaper API call" do
|
28
|
+
Net::HTTP::Post.should_receive(:new).with(/add/).and_return(@my_post)
|
29
|
+
@client.add(INTERESTING_ARTICLE)
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "parameters" do
|
33
|
+
|
34
|
+
it "passes the URL as a parameter" do
|
35
|
+
@my_post.should_receive(:set_form_data).with(hash_including(:url => INTERESTING_ARTICLE))
|
36
|
+
@client.add(INTERESTING_ARTICLE)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "passes the title as a parameter if specified" do
|
40
|
+
@my_post.should_receive(:set_form_data).with(hash_including(:title => "my title"))
|
41
|
+
@client.add(INTERESTING_ARTICLE, :title => "my title")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "passes the selection as a parameter if specified" do
|
45
|
+
@my_post.should_receive(:set_form_data).with(hash_including(:selection => "read me!"))
|
46
|
+
@client.add(INTERESTING_ARTICLE, :selection => "read me!")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "passes redirect as a parameter if specified" do
|
50
|
+
@my_post.should_receive(:set_form_data).with(hash_including(:redirect => :close))
|
51
|
+
@client.add(INTERESTING_ARTICLE, :redirect => :close)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "passes the jsonp as a parameter if specified" do
|
55
|
+
@my_post.should_receive(:set_form_data).with(hash_including(:jsonp => "myfunc"))
|
56
|
+
@client.add(INTERESTING_ARTICLE, :jsonp => "myfunc")
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "with a 403 response" do
|
63
|
+
before(:each) do
|
64
|
+
stub_instapaper_api(:add => {:status => 403})
|
65
|
+
end
|
66
|
+
|
67
|
+
it "rasises a Gazette::Response::InvalidCredentials" do
|
68
|
+
lambda {
|
69
|
+
@client.add(INTERESTING_ARTICLE)
|
70
|
+
}.should raise_error(Gazette::Response::InvalidCredentials)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "with a 500 response" do
|
75
|
+
before(:each) do
|
76
|
+
stub_instapaper_api(:add => {:status => 500})
|
77
|
+
end
|
78
|
+
|
79
|
+
it "rasises a Gazette::Response::ServerError" do
|
80
|
+
lambda {
|
81
|
+
@client.add(INTERESTING_ARTICLE)
|
82
|
+
}.should raise_error(Gazette::Response::ServerError)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "with a XXX response" do
|
87
|
+
before(:each) do
|
88
|
+
stub_instapaper_api(:add => {:status => 999})
|
89
|
+
end
|
90
|
+
|
91
|
+
it "rasises a Gazette::Response::UnknownError" do
|
92
|
+
lambda {
|
93
|
+
@client.add(INTERESTING_ARTICLE)
|
94
|
+
}.should raise_error(Gazette::Response::UnknownError)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Gazette::Client, "#authenticate" do
|
4
|
+
before(:each) do
|
5
|
+
@client = Gazette::Client.new("foo")
|
6
|
+
@my_post = stub_http_client('/api/authenticate')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "with a 200 OK response" do
|
10
|
+
before(:each) do
|
11
|
+
stub_instapaper_api(:authenticate => {:status => 200})
|
12
|
+
end
|
13
|
+
|
14
|
+
it "returns a Gazette::Response::Success object" do
|
15
|
+
@client.authenticate.should be_a Gazette::Response::Success
|
16
|
+
end
|
17
|
+
|
18
|
+
it "calls the 'authenticate' instapaper API call" do
|
19
|
+
Net::HTTP::Post.should_receive(:new).with(/authenticate/).and_return(@my_post)
|
20
|
+
@client.authenticate
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "with a 403 response" do
|
25
|
+
before(:each) do
|
26
|
+
stub_instapaper_api(:authenticate => {:status => 403})
|
27
|
+
end
|
28
|
+
|
29
|
+
it "rasises a Gazette::Response::InvalidCredentials" do
|
30
|
+
lambda {
|
31
|
+
@client.authenticate
|
32
|
+
}.should raise_error(Gazette::Response::InvalidCredentials)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with a 500 response" do
|
37
|
+
before(:each) do
|
38
|
+
stub_instapaper_api(:authenticate => {:status => 500})
|
39
|
+
end
|
40
|
+
|
41
|
+
it "rasises a Gazette::Response::ServerError" do
|
42
|
+
lambda {
|
43
|
+
@client.authenticate
|
44
|
+
}.should raise_error(Gazette::Response::ServerError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "with a XXX response" do
|
49
|
+
before(:each) do
|
50
|
+
stub_instapaper_api(:authenticate => {:status => 999})
|
51
|
+
end
|
52
|
+
|
53
|
+
it "rasises a Gazette::Response::UnknownError" do
|
54
|
+
lambda {
|
55
|
+
@client.authenticate
|
56
|
+
}.should raise_error(Gazette::Response::UnknownError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Gazette::Client, ".new" do
|
4
|
+
|
5
|
+
it "raises an error if no fist argument it passed" do
|
6
|
+
lambda { Gazette::Client.new }.should raise_error(ArgumentError)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "raises an argument error if the 2nd parameter is not a hash" do
|
10
|
+
lambda { Gazette::Client.new("foo", 56) }.should raise_error(ArgumentError)
|
11
|
+
end
|
12
|
+
|
13
|
+
shared_examples_for "a properlty constructed client" do
|
14
|
+
it "sets the username as the first argument" do
|
15
|
+
@client.username.should eql("foo")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "with a single string argument" do
|
20
|
+
before(:each) do
|
21
|
+
@client = Gazette::Client.new("foo")
|
22
|
+
end
|
23
|
+
|
24
|
+
it_should_behave_like "a properlty constructed client"
|
25
|
+
|
26
|
+
it "has no password set" do
|
27
|
+
@client.password.should be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "with a string and hash containing a :password" do
|
33
|
+
before(:each) do
|
34
|
+
@client = Gazette::Client.new("foo", :password => "bar")
|
35
|
+
end
|
36
|
+
|
37
|
+
it_should_behave_like "a properlty constructed client"
|
38
|
+
|
39
|
+
it "has a password set" do
|
40
|
+
@client.password.should eql("bar")
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
describe Gazette::Client, "HTTP basic auth" do
|
48
|
+
before(:each) do
|
49
|
+
stub_instapaper_api(:authenticate => {:status => 200})
|
50
|
+
@client = Gazette::Client.new("foo")
|
51
|
+
@my_post = Net::HTTP::Post.new('/api/authenticate')
|
52
|
+
Net::HTTP::Post.stub!(:new).and_return(@my_post)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "passes along the username" do
|
56
|
+
@my_post.should_receive(:basic_auth).with("foo", nil)
|
57
|
+
@client.authenticate
|
58
|
+
end
|
59
|
+
|
60
|
+
it "passes along the password if specified" do
|
61
|
+
@client = Gazette::Client.new("foo", :password => "bar")
|
62
|
+
@my_post.should_receive(:basic_auth).with("foo", "bar")
|
63
|
+
@client.authenticate
|
64
|
+
end
|
65
|
+
|
66
|
+
it "passes the jsonp as a parameter if specified" do
|
67
|
+
@my_post.should_receive(:set_form_data).with(hash_including(:jsonp => "myfunc"))
|
68
|
+
@client.authenticate(:jsonp => "myfunc")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe Gazette::Client, "over HTTPS" do
|
73
|
+
before(:each) do
|
74
|
+
@client = Gazette::Client.new("foo", :https => true)
|
75
|
+
stub_instapaper_api(:authenticate => {:status => 200})
|
76
|
+
@my_http = Net::HTTP.new(Gazette::Api::ADDRESS, 443)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "tells the HTTP client to use ssl" do
|
80
|
+
Net::HTTP.should_receive(:new).with(anything, 443).and_return(@my_http)
|
81
|
+
@client.authenticate
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Gazette::Response::Success do
|
4
|
+
before(:each) do
|
5
|
+
@response = mock("response", :is_a? => true, :header => {})
|
6
|
+
end
|
7
|
+
|
8
|
+
it "raises an argument error unless the first argument is a Net::HTTPResponse object" do
|
9
|
+
lambda do
|
10
|
+
Gazette::Response::Success.new(:nonse)
|
11
|
+
end.should raise_error(ArgumentError)
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
it "has a content_location attribute" do
|
16
|
+
Gazette::Response::Success.new(@response).content_location.should be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it "has an instapaper_title attribute" do
|
20
|
+
Gazette::Response::Success.new(@response).instapaper_title.should be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it "sets content_location if the 'Content-Location' header is present" do
|
24
|
+
@response.stub!(:header).and_return({'Content-Location' => 'http://www.example.com/'})
|
25
|
+
Gazette::Response::Success.new(@response).content_location.should == 'http://www.example.com/'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets instapaper_title if the 'X-Instapaper-Title' header is present" do
|
29
|
+
@response.stub!(:header).and_return({'X-Instapaper-Title' => 'nonse'})
|
30
|
+
Gazette::Response::Success.new(@response).instapaper_title.should == 'nonse'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'gazette'
|
5
|
+
require 'spec'
|
6
|
+
require 'spec/autorun'
|
7
|
+
require 'fakeweb'
|
8
|
+
|
9
|
+
Spec::Runner.configure do |config|
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
# Stubs out the instapaper API using a simple configuration syntax:
|
14
|
+
#
|
15
|
+
# :method => {:hash_of => :fakeweb_options}
|
16
|
+
def stub_instapaper_api(configuration)
|
17
|
+
configuration.each do |method, opts|
|
18
|
+
["", "foo@", "foo:bar@"].each do |hba|
|
19
|
+
base = "#{hba}#{Gazette::Api::ADDRESS}#{Gazette::Api::ENDPOINT}"
|
20
|
+
FakeWeb.register_uri(:any, "http://"+base+method.to_s, opts.merge(:body => "lol"))
|
21
|
+
FakeWeb.register_uri(:any, "https://"+base+method.to_s, opts)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Stubs out the HTTP client and builds a @my_post variable, which is our own mock we can
|
27
|
+
# use to verify HTTP actions
|
28
|
+
def stub_http_client(path)
|
29
|
+
@my_post = Net::HTTP::Post.new(path)
|
30
|
+
Net::HTTP::Post.stub!(:new).and_return(@my_post)
|
31
|
+
@my_post
|
32
|
+
end
|
data/todo.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
- Add `gazette` bin
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gazette
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jeff Pollard
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-22 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
prerelease: false
|
22
|
+
type: :development
|
23
|
+
name: rspec
|
24
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 2
|
31
|
+
- 9
|
32
|
+
version: 1.2.9
|
33
|
+
requirement: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
prerelease: false
|
36
|
+
type: :development
|
37
|
+
name: yard
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
requirement: *id002
|
46
|
+
description: Simple Ruby wrapper gem to interact with the Instapaper API. Supports authenticate and add API methods, as well as https, jsonp and redirect featres of the API.
|
47
|
+
email: jeff.pollard@gmail.com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- LICENSE
|
54
|
+
- README.md
|
55
|
+
files:
|
56
|
+
- .gitignore
|
57
|
+
- Gemfile
|
58
|
+
- Gemfile.lock
|
59
|
+
- LICENSE
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- VERSION
|
63
|
+
- gazette.gemspec
|
64
|
+
- lib/gazette.rb
|
65
|
+
- lib/gazette/api.rb
|
66
|
+
- lib/gazette/client.rb
|
67
|
+
- lib/gazette/response.rb
|
68
|
+
- spec/client/add_spec.rb
|
69
|
+
- spec/client/authenticate_spec.rb
|
70
|
+
- spec/client/client_spec.rb
|
71
|
+
- spec/response/success_spec.rb
|
72
|
+
- spec/spec.opts
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- todo.txt
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://github.com/Fluxx/gazette
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options:
|
81
|
+
- --charset=UTF-8
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.6
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: Ruby library to interact with the Instapaper API.
|
105
|
+
test_files:
|
106
|
+
- spec/client/add_spec.rb
|
107
|
+
- spec/client/authenticate_spec.rb
|
108
|
+
- spec/client/client_spec.rb
|
109
|
+
- spec/response/success_spec.rb
|
110
|
+
- spec/spec_helper.rb
|