timespeople 0.2.0
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 +3 -0
- data/LICENSE +23 -0
- data/README +8 -0
- data/Rakefile +51 -0
- data/VERSION.yml +4 -0
- data/features/steps/timespeople_steps.rb +0 -0
- data/features/support/env.rb +13 -0
- data/features/timespeople.feature +9 -0
- data/lib/times_people.rb +3 -0
- data/lib/times_people/base.rb +98 -0
- data/lib/times_people/exceptions.rb +41 -0
- data/lib/times_people/user.rb +47 -0
- data/lib/timespeople.rb +1 -0
- data/script/console +10 -0
- data/test/test_helper.rb +20 -0
- data/test/times_people/test_base.rb +86 -0
- metadata +72 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2008 Jacob Harris
|
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.
|
21
|
+
|
22
|
+
THESE LICENSE TERMS APPLY TO THE GEM SOFTWARE ONLY AND DO NOT SUPPLEMENT
|
23
|
+
OR ABROGATE THE TERMS OF SERVICE FOR THE TIMESPEOPLE API.
|
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "timespeople"
|
7
|
+
s.summary = "A simple gem for the TimesPeople API"
|
8
|
+
s.email = "jharris@nytimes.com"
|
9
|
+
s.homepage = "http://github.com/harrisj/timespeople"
|
10
|
+
s.description = "A simple gem for the TimesPeople API"
|
11
|
+
s.authors = ["Jacob Harris"]
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'rake/rdoctask'
|
18
|
+
Rake::RDocTask.new do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'timespeople'
|
21
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
+
rdoc.rdoc_files.include('README*')
|
23
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
Rake::TestTask.new(:test) do |t|
|
28
|
+
t.libs << 'lib' << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
require 'rcov/rcovtask'
|
35
|
+
Rcov::RcovTask.new do |t|
|
36
|
+
t.libs << 'test'
|
37
|
+
t.test_files = FileList['test/**/*_test.rb']
|
38
|
+
t.verbose = true
|
39
|
+
end
|
40
|
+
rescue LoadError
|
41
|
+
puts "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
42
|
+
end
|
43
|
+
|
44
|
+
# begin
|
45
|
+
# require 'cucumber/rake/task'
|
46
|
+
# Cucumber::Rake::Task.new(:features)
|
47
|
+
# rescue LoadError
|
48
|
+
# puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
49
|
+
# end
|
50
|
+
|
51
|
+
task :default => :test
|
data/VERSION.yml
ADDED
File without changes
|
data/lib/times_people.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'json'
|
3
|
+
require 'htmlentities'
|
4
|
+
|
5
|
+
module TimesPeople
|
6
|
+
class Base
|
7
|
+
API_SERVER = 'api.nytimes.com'
|
8
|
+
API_VERSION = 'v1'
|
9
|
+
API_NAME = 'timespeople'
|
10
|
+
|
11
|
+
@@api_key = nil
|
12
|
+
@@debug = false
|
13
|
+
|
14
|
+
##
|
15
|
+
# Set the API key used for operations. This needs to be called before any requests against the API. To obtain an API key, go to http://developer.nytimes.com/
|
16
|
+
def self.api_key=(key)
|
17
|
+
@@api_key = key
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.debug=(flag)
|
21
|
+
@@debug = flag
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Returns the current value of the API Key
|
26
|
+
def self.api_key
|
27
|
+
@@api_key
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Builds a request URI to call the API server
|
32
|
+
def self.build_request_url(path, params)
|
33
|
+
URI::HTTP.build :host => API_SERVER,
|
34
|
+
:path => "/svc/#{API_NAME}/api/#{API_VERSION}/#{path}.js",
|
35
|
+
:query => params.map {|k,v| "#{URI.escape(k)}=#{URI.escape(v)}"}.join('&')
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.text_field(value)
|
39
|
+
return nil if value.nil?
|
40
|
+
coder = HTMLEntities.new
|
41
|
+
coder.decode(value)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.integer_field(value)
|
45
|
+
return nil if value.nil?
|
46
|
+
value.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.date_field(value)
|
50
|
+
return nil unless value =~ /^\d{8}$/
|
51
|
+
Date.strptime(value, "%Y%m%d")
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.invoke(path, params={})
|
55
|
+
begin
|
56
|
+
if @@api_key.nil?
|
57
|
+
raise AuthenticationError, "You must initialize the API key before you run any API queries"
|
58
|
+
end
|
59
|
+
|
60
|
+
full_params = params.merge 'api-key' => @@api_key
|
61
|
+
uri = build_request_url(path, full_params)
|
62
|
+
|
63
|
+
puts "REQUEST: #{uri}" if @@debug
|
64
|
+
|
65
|
+
reply = uri.read
|
66
|
+
parsed_reply = JSON.parse reply
|
67
|
+
|
68
|
+
if parsed_reply.nil?
|
69
|
+
raise BadResponseError, "Empty reply returned from API"
|
70
|
+
end
|
71
|
+
|
72
|
+
#case parsed_reply['status']
|
73
|
+
# FIXME
|
74
|
+
#end
|
75
|
+
|
76
|
+
parsed_reply
|
77
|
+
rescue OpenURI::HTTPError => e
|
78
|
+
# FIXME: Return message from body?
|
79
|
+
case e.message
|
80
|
+
when /^400/
|
81
|
+
raise BadRequestError
|
82
|
+
when /^403/
|
83
|
+
raise AuthenticationError
|
84
|
+
when /^404/
|
85
|
+
return nil
|
86
|
+
when /^500/
|
87
|
+
raise ServerError
|
88
|
+
else
|
89
|
+
raise ConnectionError
|
90
|
+
end
|
91
|
+
|
92
|
+
raise "Error connecting to URL #{uri} #{e}"
|
93
|
+
rescue JSON::ParserError => e
|
94
|
+
raise BadResponseError, "Invalid JSON returned from API:\n#{reply}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module TimesPeople
|
2
|
+
##
|
3
|
+
# The generic Error class from which all other Errors are derived.
|
4
|
+
class Error < ::RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
##
|
8
|
+
# This error is thrown if there are problems authenticating your API key.
|
9
|
+
class AuthenticationError < Error
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# This error is thrown if the request was not parsable by the API server.
|
14
|
+
class BadRequestError < Error
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# This error is thrown if the response from the API server is not parsable.
|
19
|
+
class BadResponseError < Error
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# This error is thrown if there is an error connecting to the API server.
|
24
|
+
class ServerError < Error
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# This error is thrown if there is a timeout connecting to the server (to be implemented).
|
29
|
+
class TimeoutError < Error
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# This error is thrown for general connection errors to the API server.
|
34
|
+
class ConnectionError < Error
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# This error is thrown if the User was not found
|
39
|
+
class UserNotFoundError < Error
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module TimesPeople
|
4
|
+
class User < Base
|
5
|
+
attr_reader :user_id, :display_name, :location, :picture_url, :following_count, :followers_count
|
6
|
+
|
7
|
+
def initialize(hash)
|
8
|
+
@user_id = hash[:user_id]
|
9
|
+
@display_name = hash[:display_name]
|
10
|
+
@location = hash[:location]
|
11
|
+
@picture_url = hash[:picture_url]
|
12
|
+
@following_count = hash[:following_count]
|
13
|
+
@followers_count = hash[:followers_count]
|
14
|
+
end
|
15
|
+
|
16
|
+
# http://api.nytimes.com/svc/timespeople/api/{version}/user/{hash}/id{.response-format}?api-key={your-API-key}
|
17
|
+
def self.find(email_addr)
|
18
|
+
digest = Digest::MD5.hexdigest(email_addr.downcase)
|
19
|
+
|
20
|
+
begin
|
21
|
+
reply = invoke("user/#{digest}/id")
|
22
|
+
if reply.nil?
|
23
|
+
raise UserNotFoundError, "No user with that email address was found in TimesPeople"
|
24
|
+
end
|
25
|
+
|
26
|
+
rescue ServerError
|
27
|
+
raise UserNotFoundError, "No user with that email address was found in TimesPeople"
|
28
|
+
end
|
29
|
+
|
30
|
+
# http://api.nytimes.com/svc/timespeople/api/{version}/user/{user-id}/{data-type}{.response-format}?api-key={your-API-key}
|
31
|
+
unless reply['results'].nil? || reply['results']['user_id'].nil?
|
32
|
+
user_id = reply['results']['user_id']
|
33
|
+
reply = invoke("user/#{user_id}/profile")
|
34
|
+
init_from_api(reply['results'])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.init_from_api(api_hash)
|
39
|
+
new :user_id => api_hash['user_id'],
|
40
|
+
:display_name => api_hash['user_displayname'],
|
41
|
+
:location => api_hash['location'],
|
42
|
+
:picture_url => api_hash['user_pic_url'],
|
43
|
+
:following_count => api_hash['following_count'],
|
44
|
+
:followers_count => api_hash['followers_count']
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/timespeople.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'times_people')
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/timespeople.rb'}"
|
9
|
+
puts "Loading timespeople gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
gem 'chrisk-fakeweb'
|
6
|
+
require 'fake_web'
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
require 'timespeople'
|
10
|
+
|
11
|
+
class Test::Unit::TestCase
|
12
|
+
API_KEY = '13e234323232222'
|
13
|
+
|
14
|
+
def init_test_key
|
15
|
+
TimesPeople::Base.api_key = API_KEY
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module TestTimesPeople
|
20
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper.rb'
|
2
|
+
|
3
|
+
class TestTimesPeople::TestBase < Test::Unit::TestCase
|
4
|
+
include TimesPeople
|
5
|
+
|
6
|
+
context "Base.build_request_url" do
|
7
|
+
end
|
8
|
+
|
9
|
+
context "Base.invoke" do
|
10
|
+
setup do
|
11
|
+
init_test_key
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when the API key has not been set" do
|
15
|
+
setup do
|
16
|
+
Base.api_key = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
should_eventually "raise an AuthenticationError" do
|
20
|
+
assert_raise(AuthenticationError) do
|
21
|
+
Base.invoke
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when the Articles API returns a 403 forbidden error (API key issue)" do
|
27
|
+
setup do
|
28
|
+
FakeWeb.register_uri(api_url_for, :status => ["403", "Forbidden"])
|
29
|
+
end
|
30
|
+
|
31
|
+
should_eventually "raise an AuthenticationError" do
|
32
|
+
assert_raise(AuthenticationError) do
|
33
|
+
Base.invoke
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when the Articles API returns a 400 bad request error" do
|
39
|
+
setup do
|
40
|
+
FakeWeb.register_uri(api_url_for, :status => ["400", "Bad Request"])
|
41
|
+
end
|
42
|
+
|
43
|
+
should_eventually "raise an BadRequestError" do
|
44
|
+
assert_raise(BadRequestError) do
|
45
|
+
Base.invoke
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "When calling the Articles API returns a 500 server error" do
|
51
|
+
setup do
|
52
|
+
FakeWeb.register_uri(api_url_for, :status => ["500", "Server Error"])
|
53
|
+
end
|
54
|
+
|
55
|
+
should_eventually "raise an ServerError" do
|
56
|
+
assert_raise(ServerError) do
|
57
|
+
Base.invoke
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when the Articles API returns any other HTTP error" do
|
63
|
+
setup do
|
64
|
+
FakeWeb.register_uri(api_url_for, :status => ["502", "Random Error"])
|
65
|
+
end
|
66
|
+
|
67
|
+
should_eventually "raise an ConnectionError" do
|
68
|
+
assert_raise(ConnectionError) do
|
69
|
+
Base.invoke
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "When the Articles API returns JSON that can't be parsed" do
|
75
|
+
setup do
|
76
|
+
FakeWeb.register_uri(api_url_for, :text => "e2131421212121221 => 3i109sdfvinp;2 112")
|
77
|
+
end
|
78
|
+
|
79
|
+
should_eventually "raise an BadResponseError" do
|
80
|
+
assert_raise(BadResponseError) do
|
81
|
+
Base.invoke
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timespeople
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacob Harris
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-25 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A simple gem for the TimesPeople API
|
17
|
+
email: jharris@nytimes.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- LICENSE
|
28
|
+
- README
|
29
|
+
- Rakefile
|
30
|
+
- VERSION.yml
|
31
|
+
- features/steps/timespeople_steps.rb
|
32
|
+
- features/support/env.rb
|
33
|
+
- features/timespeople.feature
|
34
|
+
- lib/times_people.rb
|
35
|
+
- lib/times_people/base.rb
|
36
|
+
- lib/times_people/exceptions.rb
|
37
|
+
- lib/times_people/user.rb
|
38
|
+
- lib/timespeople.rb
|
39
|
+
- script/console
|
40
|
+
- test/test_helper.rb
|
41
|
+
- test/times_people/test_base.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/harrisj/timespeople
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.2
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: A simple gem for the TimesPeople API
|
70
|
+
test_files:
|
71
|
+
- test/test_helper.rb
|
72
|
+
- test/times_people/test_base.rb
|