rubynpr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +58 -0
- data/README.txt +53 -0
- data/Rakefile +16 -0
- data/bin/rubynpr +0 -0
- data/lib/rubynpr/audio.rb +43 -0
- data/lib/rubynpr/byline.rb +20 -0
- data/lib/rubynpr/client.rb +192 -0
- data/lib/rubynpr/content.rb +17 -0
- data/lib/rubynpr/exception.rb +34 -0
- data/lib/rubynpr/image.rb +34 -0
- data/lib/rubynpr/item.rb +25 -0
- data/lib/rubynpr/list.rb +47 -0
- data/lib/rubynpr/message.rb +15 -0
- data/lib/rubynpr/organization.rb +22 -0
- data/lib/rubynpr/parent.rb +24 -0
- data/lib/rubynpr/product.rb +32 -0
- data/lib/rubynpr/pull_quote.rb +12 -0
- data/lib/rubynpr/related_link.rb +26 -0
- data/lib/rubynpr/result_parser.rb +18 -0
- data/lib/rubynpr/show.rb +32 -0
- data/lib/rubynpr/story.rb +91 -0
- data/lib/rubynpr/subcategory.rb +17 -0
- data/lib/rubynpr.rb +28 -0
- data/test/audio_test.rb +24 -0
- data/test/byline_test.rb +10 -0
- data/test/client_test.rb +44 -0
- data/test/fixtures/audio.yml +32 -0
- data/test/fixtures/byline.yml +6 -0
- data/test/fixtures/client.yml +190 -0
- data/test/fixtures/image.yml +10 -0
- data/test/fixtures/item.yml +15 -0
- data/test/fixtures/list.yml +78 -0
- data/test/fixtures/list_text.xml +3 -0
- data/test/fixtures/organization.yml +11 -0
- data/test/fixtures/parent.yml +6 -0
- data/test/fixtures/product.yml +9 -0
- data/test/fixtures/pullquote.yml +6 -0
- data/test/fixtures/relatedlink.yml +6 -0
- data/test/fixtures/show.yml +6 -0
- data/test/fixtures/stories.yml +522 -0
- data/test/fixtures/story.yml +124 -0
- data/test/fixtures/subcategory.yml +59 -0
- data/test/fixtures.rb +86 -0
- data/test/image_test.rb +16 -0
- data/test/item_test.rb +18 -0
- data/test/list_test.rb +17 -0
- data/test/organization_test.rb +16 -0
- data/test/parent_test.rb +13 -0
- data/test/product_test.rb +16 -0
- data/test/pullquote_test.rb +11 -0
- data/test/related_link_test.rb +9 -0
- data/test/show_test.rb +12 -0
- data/test/story_list_test.rb +21 -0
- data/test/story_test.rb +50 -0
- data/test/subcategory_test.rb +15 -0
- data/test/test_helper.rb +12 -0
- data/test/test_rubynpr.rb +1 -0
- metadata +150 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/rubynpr
|
6
|
+
lib/rubynpr/audio.rb
|
7
|
+
lib/rubynpr/byline.rb
|
8
|
+
lib/rubynpr/client.rb
|
9
|
+
lib/rubynpr/content.rb
|
10
|
+
lib/rubynpr/exception.rb
|
11
|
+
lib/rubynpr/image.rb
|
12
|
+
lib/rubynpr/item.rb
|
13
|
+
lib/rubynpr/list.rb
|
14
|
+
lib/rubynpr/message.rb
|
15
|
+
lib/rubynpr/organization.rb
|
16
|
+
lib/rubynpr/parent.rb
|
17
|
+
lib/rubynpr/product.rb
|
18
|
+
lib/rubynpr/pull_quote.rb
|
19
|
+
lib/rubynpr/related_link.rb
|
20
|
+
lib/rubynpr/result_parser.rb
|
21
|
+
lib/rubynpr/show.rb
|
22
|
+
lib/rubynpr/story.rb
|
23
|
+
lib/rubynpr/subcategory.rb
|
24
|
+
lib/rubynpr.rb
|
25
|
+
test/audio_test.rb
|
26
|
+
test/byline_test.rb
|
27
|
+
test/client_test.rb
|
28
|
+
test/fixtures.rb
|
29
|
+
test/fixtures/audio.yml
|
30
|
+
test/fixtures/byline.yml
|
31
|
+
test/fixtures/client.yml
|
32
|
+
test/fixtures/image.yml
|
33
|
+
test/fixtures/item.yml
|
34
|
+
test/fixtures/list.yml
|
35
|
+
test/fixtures/list_text.xml
|
36
|
+
test/fixtures/organization.yml
|
37
|
+
test/fixtures/parent.yml
|
38
|
+
test/fixtures/product.yml
|
39
|
+
test/fixtures/pullquote.yml
|
40
|
+
test/fixtures/relatedlink.yml
|
41
|
+
test/fixtures/show.yml
|
42
|
+
test/fixtures/stories.yml
|
43
|
+
test/fixtures/story.yml
|
44
|
+
test/fixtures/subcategory.yml
|
45
|
+
test/image_test.rb
|
46
|
+
test/item_test.rb
|
47
|
+
test/list_test.rb
|
48
|
+
test/organization_test.rb
|
49
|
+
test/parent_test.rb
|
50
|
+
test/product_test.rb
|
51
|
+
test/pullquote_test.rb
|
52
|
+
test/related_link_test.rb
|
53
|
+
test/show_test.rb
|
54
|
+
test/story_list_test.rb
|
55
|
+
test/story_test.rb
|
56
|
+
test/subcategory_test.rb
|
57
|
+
test/test_helper.rb
|
58
|
+
test/test_rubynpr.rb
|
data/README.txt
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
Ruby-NPR
|
2
|
+
by Tiffani Bell (tiffani@thinkpositif.com)
|
3
|
+
http://the.commentari.at
|
4
|
+
|
5
|
+
== How do I install?
|
6
|
+
|
7
|
+
<tt>sudo gem i ruby-npr</tt>
|
8
|
+
|
9
|
+
== How do I get started?
|
10
|
+
|
11
|
+
<tt>@client = NPR::Client.new(:api_key => 'your NPR API key')</tt>
|
12
|
+
|
13
|
+
Pass a new Client instance your NPR API key which you can get here[http://www.npr.org/api/index].
|
14
|
+
|
15
|
+
== Accessing Stories
|
16
|
+
|
17
|
+
Use the Client#query method to find content by accessing stories directly or via lists of
|
18
|
+
stories from specific NPR topics.
|
19
|
+
|
20
|
+
<tt>@client.query(:id => 95965641)</tt>
|
21
|
+
|
22
|
+
Client#query always requires the <tt>:id</tt> option and in this particular case, this accesses
|
23
|
+
a story directly. The Story is accessible from <tt>@client.results.list[0]</tt>.
|
24
|
+
|
25
|
+
Client#query accepts a single ID value (for either a story or a topic), as above, or a list of IDs in the form of an array.
|
26
|
+
|
27
|
+
See Client#query for more details.
|
28
|
+
|
29
|
+
|
30
|
+
== License
|
31
|
+
|
32
|
+
(The MIT License)
|
33
|
+
|
34
|
+
Copyright (c) 2008 Tiffani Bell <tiffani@thinkpositif.com>
|
35
|
+
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
37
|
+
a copy of this software and associated documentation files (the
|
38
|
+
'Software'), to deal in the Software without restriction, including
|
39
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
40
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
41
|
+
permit persons to whom the Software is furnished to do so, subject to
|
42
|
+
the following conditions:
|
43
|
+
|
44
|
+
The above copyright notice and this permission notice shall be
|
45
|
+
included in all copies or substantial portions of the Software.
|
46
|
+
|
47
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
48
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
49
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
50
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
51
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
52
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
53
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/rubynpr.rb'
|
6
|
+
|
7
|
+
Hoe.new('rubynpr', RubyNPR::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'rubynpr'
|
9
|
+
p.summary = "A Ruby wrapper for the NPR API"
|
10
|
+
p.developer('Tiffani Ashley Bell', 'tiffani@thinkpositif.com')
|
11
|
+
p.url = "http://rubynpr.rubyforge.org"
|
12
|
+
p.extra_deps = %w( hpricot )
|
13
|
+
p.test_globs = 'test/*.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
# vim: syntax=Ruby
|
data/bin/rubynpr
ADDED
File without changes
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module NPR
|
2
|
+
#
|
3
|
+
# Audio represents all the available audio that is packaged with a particular story. Audio
|
4
|
+
# features the following attributes:
|
5
|
+
# * <tt>id</tt>: the unique ID for the audio
|
6
|
+
# * <tt>duration</tt>: the duration in seconds of the audio
|
7
|
+
# * <tt>title</tt>: the title of the audio piece
|
8
|
+
# * <tt>formats</tt>: audio pieces come in three different formats: MP3, Windows Media, and Real Audio. Audio
|
9
|
+
# of the different formats can be accessed by a hash like so: <tt>audio.formats[:mp3]</tt>. Hash options are
|
10
|
+
# <tt>:mp3</tt>, <tt>:wm</tt>, <tt>:rm</tt>, and <tt>:ram</tt>. <tt>:ram</tt> is more likely to show up with older stories.
|
11
|
+
#
|
12
|
+
class Audio < Content
|
13
|
+
FORMATS = [:mp3, :wm, :rm, :ram]
|
14
|
+
|
15
|
+
attr_accessor :id, :duration, :type, :title, :formats, :rights_holder, :mp3_rights
|
16
|
+
|
17
|
+
private
|
18
|
+
def new_from_nprml(audio_nprml)
|
19
|
+
return nil if audio_nprml.nil?
|
20
|
+
|
21
|
+
@id, @type = audio_nprml[:id], audio_nprml[:type]
|
22
|
+
@title, @duration = audio_nprml.at('title').html, audio_nprml.at('duration').html
|
23
|
+
@rights_holder = audio_nprml.at('rightsHolder').html
|
24
|
+
|
25
|
+
process_audio_formats(audio_nprml.at('format'))
|
26
|
+
|
27
|
+
if audio_nprml.at('mp3')
|
28
|
+
@mp3_rights = audio_nprml.at('mp3')[:rights]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_audio_formats(format_nprml)
|
33
|
+
return nil if format_nprml.empty?
|
34
|
+
|
35
|
+
@formats = {}
|
36
|
+
FORMATS.each do |format|
|
37
|
+
unless format_nprml.at(format.to_s).nil?
|
38
|
+
@formats[format] = format_nprml.at(format.to_s).html
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module NPR
|
2
|
+
# Byline represents a listing of the author(s) of an NPR story. From a byline, you have
|
3
|
+
# access to:
|
4
|
+
#
|
5
|
+
# * <tt>id</tt> - unique ID of the byline
|
6
|
+
# * <tt>name</tt> - the author's name
|
7
|
+
# * <tt>person</tt> - the NPR ID of the author
|
8
|
+
# * <tt>links</tt> - links to all content from a particular author
|
9
|
+
#
|
10
|
+
class Byline < Content
|
11
|
+
attr_accessor :id, :name, :person, :links
|
12
|
+
|
13
|
+
private
|
14
|
+
def new_from_nprml(byline_nprml)
|
15
|
+
return nil if byline_nprml.empty?
|
16
|
+
@id = byline_nprml[:id]
|
17
|
+
@name, @person = byline_nprml.at('name').html, byline_nprml.at('name')[:personId]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
|
2
|
+
module NPR
|
3
|
+
# Client handles the connection to NPR. Create an instance of Client and use Client#query or Client#list to pull
|
4
|
+
# content from NPR.
|
5
|
+
#
|
6
|
+
class Client
|
7
|
+
API_HOST = 'api.npr.org'
|
8
|
+
API_OUTPUT_FORMATS = [:nprml, :rss, :media_rss, :json, :html]
|
9
|
+
API_INPUT_QUERY_OPTIONS = [:id, :date, :start_date, :end_date, :org_id, :search_term,
|
10
|
+
:search_type, :num_results, :fields]
|
11
|
+
|
12
|
+
attr_accessor :results, :api_key, :warnings
|
13
|
+
|
14
|
+
# Creates an NPR API client session. Set the <tt>:api_key</tt> value to include the NPR API key. This
|
15
|
+
# can also be set later using the <tt>api_key method</tt>. To get started, make sure you have an API key from NPR. You
|
16
|
+
# can get an API key here: http://www.npr.org/api/index. Then:
|
17
|
+
#
|
18
|
+
# <tt>client = NPR::Client.new(:api_key => 'your NPR api key')</tt>
|
19
|
+
#
|
20
|
+
# If a query is attempted using a client whose <tt>api_key</tt> hasn't been set, an InvalidAPIKey exception will be raised.
|
21
|
+
#
|
22
|
+
def initialize(options = {})
|
23
|
+
@api_key = options[:api_key]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sends queries to NPR. <tt>query</tt> has a bunch of options for building a query. Options include:
|
27
|
+
# * <tt>:id</tt> - returns stories that belong to the given IDs or stories belonging to a given topic ID. Accepts either a single ID value,
|
28
|
+
# an array of IDs, or an object representing a topic.
|
29
|
+
# * <tt>:date</tt> - returns stories with the exact date requested. If <tt>:date</tt> to <tt>:current</tt>, one of two things can happen. If
|
30
|
+
# one of the parameters is a program, setting <tt>:date</tt> to <tt>:current</tt> will return stories from the last complete episode of the
|
31
|
+
# given program. If none of the <tt>:id</tt> parameters is a program, setting <tt>:date</tt> to <tt>:current</tt> will just return stories from today.
|
32
|
+
# <em>Note:</em> Any dates used will be converted to follow the big-endian <tt>YYYY-MM-DD</tt> format as part of the URI as required by NPR
|
33
|
+
# * <tt>:start_date</tt> - returns stories published on or after the given date. Takes time as either a UTC or local Time object.
|
34
|
+
# * <tt>:end_date</tt> - returns stories published on or before the given date. Same as <tt>:start_date</tt> - takes time as either a
|
35
|
+
# UTC or local Time object.
|
36
|
+
# * <tt>:org_id</tt> - returns stories that are provided by the given organization.
|
37
|
+
# * <tt>:search_term</tt> - returns stories that are considered to be matches on the given search term.
|
38
|
+
# * <tt>:search_type</tt> - used with <tt>:search_term</tt> to help NPR determine which fields it should search. If <tt>:search_type</tt> isn't
|
39
|
+
# specified, NPR will search the full content of the story. Otherwise, use <tt>:main</tt> to search only the <tt>title</tt> and
|
40
|
+
# <tt>teaser</tt> fields on stories.
|
41
|
+
# * <tt>:num_results</tt> - determines the number of stories that will be returned. The NPR API caps the number of stories that can
|
42
|
+
# be returned at 20. If the <tt>:num_results</tt> is greater than the maximum, the API will return the maximum.
|
43
|
+
# * <tt>:start</tt> - used to paginate through a large result set. Use with <tt>:num_results</tt> to return something lesser than the maximum
|
44
|
+
# number of remaining results (20).
|
45
|
+
# * <tt>:fields</tt> - used to specify which attributes to return on stories. Use a single value or an array of any of the following values:
|
46
|
+
# * <tt>:title</tt>
|
47
|
+
# * <tt>:teaser</tt>
|
48
|
+
# * <tt>:storyDate</tt>
|
49
|
+
# * <tt>:show</tt>
|
50
|
+
# * <tt>:byline</tt>
|
51
|
+
# * <tt>:text</tt>
|
52
|
+
# * <tt>:audio</tt>
|
53
|
+
# * <tt>:image</tt>
|
54
|
+
# * <tt>:textWithHtml</tt>
|
55
|
+
# * <tt>:pullquote</tt>
|
56
|
+
# * <tt>:relatedLink</tt>
|
57
|
+
# * <tt>:album</tt>
|
58
|
+
# * <tt>:product</tt>
|
59
|
+
# * <tt>:parent</tt>
|
60
|
+
# * <tt>:artist</tt> - if the story is about an album, this will return all associated artists
|
61
|
+
# * <tt>:summary</tt> - a way of accessing certain fields including <tt>title</tt>, <tt>subtitle</tt>, <tt>shortTitle</tt>, <tt>teaser</tt>,
|
62
|
+
# <tt>mini_teaser</tt>, <tt>slug</tt>, <tt>thumbnail</tt>, <tt>toenail</tt>, <tt>story_date</tt>, <tt>publication_date</tt>,
|
63
|
+
# <tt>last_modified</tt>, <tt>keywords</tt>, and <tt>priority_keywords</tt>.
|
64
|
+
# * <tt>:titles</tt> - returns all titles associated with a story, e.g., <tt>title</tt>, <tt>subtitle</tt>, <tt>shortTitle</tt>, and <tt>slug</tt>.
|
65
|
+
# * <tt>:teasers</tt> - returns all teasers associated with a story, e.g., <tt>teaser</tt> and <tt>mini_teaser</tt>.
|
66
|
+
# * <tt>:dates</tt> - returns all dates associated with a story, e.g., <tt>story_date</tt>, <tt>publication_date</tt>, and <tt>last_modifed</tt>.
|
67
|
+
#
|
68
|
+
# ==== Examples
|
69
|
+
# <tt>client.query(:id => 95937183)</tt><br />
|
70
|
+
# <tt>client.query(:id => [95937183, 95691841, 95683592])</tt><br />
|
71
|
+
# <tt>client.query(:id => 1031, :search_term => 'polio')</tt><br />
|
72
|
+
# <tt>client.query(:id => 1017, :search_term => 'Alan Greenspan', :search_type => :main)</tt><br />
|
73
|
+
# <tt>client.query(:id => some_topic, :num_results => 3, :start_date => Time.utc(2008, 10, 3))</tt> where <tt>some_topic</tt> is an object
|
74
|
+
# representing a topic as pulled from the results of a Client#list query.
|
75
|
+
#
|
76
|
+
def query(options = {})
|
77
|
+
if @api_key.nil?
|
78
|
+
raise InvalidAPIKey.new(NPRException::INVALID_API_KEY, 'An API key has not been specified for this NPR client.')
|
79
|
+
elsif !options.include?(:id) && !options.include?(:search_term)
|
80
|
+
raise InvalidQueryOptions.new(nil, 'An :id must be specified with every request.')
|
81
|
+
end
|
82
|
+
|
83
|
+
options.merge!(:type => :query) unless options[:type] == :list
|
84
|
+
path = assemble_path(options)
|
85
|
+
request(path)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Accesses the different kinds of lists available via the NPR API. NPR provides access to topics, music genres, programs, bios,
|
89
|
+
# music artists, columns, and series via the API. This method takes the lone <tt>:id</tt> hash to specify which list to access.
|
90
|
+
# The <tt>:id</tt> parameter only accepts one value. The available list values to pass with <tt>:id</tt> are
|
91
|
+
# available at http://www.npr.org/api/mappingCodes.php.
|
92
|
+
#
|
93
|
+
# The results of these queries are returned as a List where List#list holds Item elements that represent the topics returned. A query like
|
94
|
+
# <tt>all_topics = client.list(:id => '3002')</tt> will return all the topics NPR has. <tt>some_topic = all_topics.list[3]</tt> is an Item
|
95
|
+
# representing a topic. Later, this <tt>some_topic</tt> object can be used to generate queries with Client#query:
|
96
|
+
# <tt>story_results = client.query(:id => some_topic, :num_results => 3)</tt>.
|
97
|
+
#
|
98
|
+
# ==== Example
|
99
|
+
# <tt>client.list(:id => 3002)</tt>
|
100
|
+
#
|
101
|
+
def list(options = {})
|
102
|
+
query(options.merge(:type => :list))
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
def assemble_path(options = {})
|
107
|
+
return unless [:list, :query].include?(options[:type])
|
108
|
+
path = "/#{ options[:type].to_s }?#{ process_query_options(options) }"
|
109
|
+
path += "&format=#{ options[:format] }" if API_OUTPUT_FORMATS.include?(options[:format])
|
110
|
+
path += "&apiKey=#{ @api_key }"
|
111
|
+
end
|
112
|
+
|
113
|
+
def npr_date(date)
|
114
|
+
date.strftime("%Y-%m-%d")
|
115
|
+
end
|
116
|
+
|
117
|
+
def process_query_options(options = {})
|
118
|
+
return if options.empty?
|
119
|
+
query_string = ""
|
120
|
+
options.each do |key, value|
|
121
|
+
next unless API_INPUT_QUERY_OPTIONS.include?(key)
|
122
|
+
query_string += '&' unless query_string.empty?
|
123
|
+
query_string += case key
|
124
|
+
when :id
|
125
|
+
id_s = "id="
|
126
|
+
id_s += if value.kind_of?(Array): options[:id].join(',')
|
127
|
+
elsif value.kind_of?(Item): value.id
|
128
|
+
else value.to_s
|
129
|
+
end
|
130
|
+
when :date && options[:date] == :current
|
131
|
+
'date=current'
|
132
|
+
when :start_date
|
133
|
+
"startDate=#{ npr_date(value) }"
|
134
|
+
when :end_date
|
135
|
+
"endDate=#{ npr_date(value) }"
|
136
|
+
when :org_id
|
137
|
+
"orgId=#{ options[:org_id] }"
|
138
|
+
when :search_term
|
139
|
+
"searchTerm=#{ options[:search_term] }"
|
140
|
+
when :search_type
|
141
|
+
"searchType=#{ options[:search_type].to_s }"
|
142
|
+
when :num_results
|
143
|
+
"numResults=#{ value }"
|
144
|
+
when :fields
|
145
|
+
fields = "fields="
|
146
|
+
fields += if value.kind_of?(Array): options[:fields].join(',')
|
147
|
+
else value.to_s
|
148
|
+
end
|
149
|
+
else "#{ key }=#{ value }"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
query_string
|
153
|
+
end
|
154
|
+
|
155
|
+
def process_client_errors(response)
|
156
|
+
error_result = Hpricot.XML(response).at("message[@level='error']") || Hpricot.XML(response).at("errorResponse[@error='true']")
|
157
|
+
return if error_result.nil?
|
158
|
+
|
159
|
+
error_msg = if error_result.at('text')
|
160
|
+
error_result.at('text').html
|
161
|
+
elsif error_result.at('message')
|
162
|
+
error_result.at('message').html
|
163
|
+
end
|
164
|
+
|
165
|
+
case error_result[:id]
|
166
|
+
when NPRException::SYSTEM_ISSUES
|
167
|
+
raise NPRSystemIssues.new(error_result[:id], error_msg)
|
168
|
+
when NPRException::INVALID_API_KEY
|
169
|
+
raise InvalidAPIKey.new(error_result[:id], error_msg)
|
170
|
+
when NPRException::DEACTIVATED_API_KEY
|
171
|
+
raise DeactivatedAPIKey.new(error_result[:id], error_msg)
|
172
|
+
when nil && error_msg == 'The requested list could not be found.'
|
173
|
+
raise InvalidList.new(nil, error_msg)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def process_result_warnings(warning_results)
|
178
|
+
return if warning_results.empty?
|
179
|
+
@warnings = warning_results.collect { |warning| Warning.new(warning[:id], warning.at('text').html, warning) }
|
180
|
+
end
|
181
|
+
|
182
|
+
def request(path)
|
183
|
+
connection = Net::HTTP.new(API_HOST)
|
184
|
+
headers, response = connection.get(path)
|
185
|
+
|
186
|
+
process_client_errors(response)
|
187
|
+
process_result_warnings(Hpricot.XML(response).search("message[@level='warning']"))
|
188
|
+
|
189
|
+
@results = NPR::ResultParser.new.parse(response)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module NPR
|
2
|
+
# Content is the parent of all the content returned from an NPR query. Mainly in place
|
3
|
+
# to assist later in dealing with content returned using different formats. The intention
|
4
|
+
# is to have it become the starting point (obviously with the presence of initialize) for
|
5
|
+
# dealing with NPR results in such a way that if somebody was previously dealing with raw
|
6
|
+
# JSON for instance, they could throw it over to this and it would return Ruby objects to work
|
7
|
+
# with. 'tis forthcoming.
|
8
|
+
#
|
9
|
+
class Content
|
10
|
+
def initialize(markup, format = :nprml)
|
11
|
+
case format
|
12
|
+
when :nprml
|
13
|
+
new_from_nprml(markup)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module NPR
|
2
|
+
class NPRException < StandardError
|
3
|
+
SYSTEM_ISSUES = '101'
|
4
|
+
INVALID_API_KEY = '310'
|
5
|
+
DEACTIVATED_API_KEY = '311'
|
6
|
+
|
7
|
+
attr_accessor :code, :message
|
8
|
+
|
9
|
+
def initialize(code, message)
|
10
|
+
@code, @message = code, message
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"#{ @code }: #{ @message }"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Raised when NPR is experiencing issues
|
19
|
+
class NPRSystemIssues < NPRException; end
|
20
|
+
|
21
|
+
# Raised when the API key passed in with the request is invalid
|
22
|
+
class InvalidAPIKey < NPRException; end
|
23
|
+
|
24
|
+
# Raised when the API key passed in with the request has been deactivated
|
25
|
+
class DeactivatedAPIKey < NPRException; end
|
26
|
+
|
27
|
+
# Raised when a list is requested that doesn't exist
|
28
|
+
class InvalidList < NPRException; end
|
29
|
+
|
30
|
+
# Raised when query is not formed with the proper options, i.e. a missing <tt>:id</tt>, etc.
|
31
|
+
class InvalidQueryOptions < NPRException; end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module NPR
|
2
|
+
# Some NPR stories include images. Images have as attributes:
|
3
|
+
#
|
4
|
+
# * <tt>id<tt> - a unique NPR identifier
|
5
|
+
# * <tt>type</tt> - TBD
|
6
|
+
# * <tt>caption</tt>
|
7
|
+
# * <tt>width</tt> - the width of the image in pixels
|
8
|
+
# * <tt>src</tt> - the source URL for the image
|
9
|
+
# * <tt>border</tt> - indicates if an image has a border
|
10
|
+
# * <tt>title</tt>
|
11
|
+
# * <tt>link</tt> - the URL to which the image links
|
12
|
+
# * <tt>producer</tt> - source who will receive credit for the image
|
13
|
+
# * <tt>provider</tt> - the owner or provider of the image, which may be independent of the producer
|
14
|
+
# * <tt>copyright</tt> - copyright year
|
15
|
+
#
|
16
|
+
class Image < Content
|
17
|
+
attr_accessor :id, :type, :width, :src, :border, :title, :link, :producer,
|
18
|
+
:copyright, :caption, :provider
|
19
|
+
|
20
|
+
alias_method :source, :src
|
21
|
+
|
22
|
+
private
|
23
|
+
def new_from_nprml(image_nprml)
|
24
|
+
@id, @type = image_nprml[:id], image_nprml[:type]
|
25
|
+
@title = image_nprml.at('title').html
|
26
|
+
@src = image_nprml[:src]
|
27
|
+
@caption = image_nprml.at('caption').html
|
28
|
+
@producer = image_nprml.at('producer').html
|
29
|
+
@copyright = image_nprml.at('copyright').html
|
30
|
+
@border = image_nprml[:hasBorder]
|
31
|
+
@link = image_nprml.at('link')[:url]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/rubynpr/item.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module NPR
|
2
|
+
# Topics accessed via the API are packaged as items with the following attributes:
|
3
|
+
#
|
4
|
+
# * <tt>id</tt> - the topic ID
|
5
|
+
# * <tt>number</tt> - item number
|
6
|
+
# * <tt>type</tt> - what kind of content the topic represents, i.e. biography, a column, etc.
|
7
|
+
# * <tt>slug</tt> - short item descriptor
|
8
|
+
# * <tt>additional_info</tt> - extra info/summary about the target
|
9
|
+
# * <tt>title</tt> - the name of the topic
|
10
|
+
#
|
11
|
+
class Item < Content
|
12
|
+
attr_accessor :id, :number, :type, :slug, :additional_info, :title, :slug
|
13
|
+
|
14
|
+
alias_method :info, :additional_info
|
15
|
+
|
16
|
+
private
|
17
|
+
def new_from_nprml(item_nprml)
|
18
|
+
@id, @number = item_nprml[:id], item_nprml[:num]
|
19
|
+
@type = item_nprml[:type]
|
20
|
+
@slug = item_nprml.at('slug').html
|
21
|
+
@title = item_nprml.at('title').html
|
22
|
+
@additional_info = item_nprml.at('additionalInfo').html.strip
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/rubynpr/list.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module NPR
|
2
|
+
class TopicList < Content
|
3
|
+
attr_accessor :id, :type_id, :title, :list, :has_subcategories, :subcategories
|
4
|
+
|
5
|
+
private
|
6
|
+
def new_from_nprml(list_nprml)
|
7
|
+
@title = list_nprml.at('title').html
|
8
|
+
@id, @type_id = list_nprml[:id], list_nprml[:typeid]
|
9
|
+
|
10
|
+
if list_nprml.search('subcategory').empty?
|
11
|
+
@has_subcategories = false
|
12
|
+
@list = NPR.process_elements('item', list_nprml)
|
13
|
+
else
|
14
|
+
@has_subcategories = true
|
15
|
+
@subcategories = NPR.process_elements('subcategory', list_nprml)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# StoryList contains a listing of all the stories returned for a particular query performed through either
|
21
|
+
# NPR::Client#query or NPR::Client#list.
|
22
|
+
class StoryList < Content
|
23
|
+
attr_accessor :id, :title, :list, :teaser, :mini_teaser
|
24
|
+
|
25
|
+
def stories
|
26
|
+
@list
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def new_from_nprml(story_list_nprml)
|
31
|
+
@title = story_list_nprml.at('title').html
|
32
|
+
@teaser = Hpricot.XML(story_list_nprml.at('teaser').html).innerText
|
33
|
+
@mini_teaser = Hpricot.XML(story_list_nprml.at('miniTeaser').html).innerText
|
34
|
+
@list = NPR.process_elements('story', story_list_nprml)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# I REALLY want to move this into Content.
|
39
|
+
def self.process_elements(element, nprml)
|
40
|
+
return nil if !['story', 'item', 'subcategory'].include?(element)
|
41
|
+
list = []
|
42
|
+
nprml.search(element).each do |e|
|
43
|
+
list << NPR.const_get(element.capitalize).new(e)
|
44
|
+
end
|
45
|
+
list
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module NPR
|
2
|
+
class Message
|
3
|
+
attr_accessor :body, :message_id, :raw_message
|
4
|
+
|
5
|
+
def initialize(message_id, body, raw_message)
|
6
|
+
@body, @message_id, @raw_message = body, message_id, raw_message
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
"#{ self.class } (#{ message_id }): #{ @body }"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Warning < Message; end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NPR
|
2
|
+
# Organization represents an owner organization of a story. Includes:
|
3
|
+
#
|
4
|
+
# * <tt>id</tt> - unique ID of the organization to which a story belongs
|
5
|
+
# * <tt>name</tt> - the name of the organization
|
6
|
+
# * <tt>website</tt> - URL for the organization's website
|
7
|
+
# * <tt>abbr</tt> - an organization's NPR abbreviation
|
8
|
+
#
|
9
|
+
# ==== Example
|
10
|
+
# <tt>some_story.organization.name</tt>
|
11
|
+
#
|
12
|
+
class Organization < Content
|
13
|
+
attr_accessor :id, :name, :website, :abbr
|
14
|
+
|
15
|
+
private
|
16
|
+
def new_from_nprml(org_nprml)
|
17
|
+
return nil if org_nprml.nil?
|
18
|
+
@id, @name = org_nprml[:orgId], org_nprml.at('name').html
|
19
|
+
@website, @abbr = org_nprml.at('website').html, org_nprml[:orgAbbr]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module NPR
|
3
|
+
# Parent represents the parent topics to which a story belongs. Parents have:
|
4
|
+
#
|
5
|
+
# * <tt>id</tt> - a unique NPR ID for the Parent
|
6
|
+
# * <tt>title</tt> - the title of the parent topic
|
7
|
+
# * <tt>link</tt> - a hash containing the API URI for a parent and the normal web URI
|
8
|
+
# * <tt>type</tt> - usually a topic, series, or column
|
9
|
+
#
|
10
|
+
class Parent < Content
|
11
|
+
attr_accessor :id, :type, :title, :link
|
12
|
+
|
13
|
+
private
|
14
|
+
def new_from_nprml(parent_nprml)
|
15
|
+
return nil if parent_nprml.empty?
|
16
|
+
@id, @type = parent_nprml[:id], parent_nprml[:type]
|
17
|
+
@title = parent_nprml.at('title').html
|
18
|
+
|
19
|
+
link_nprml = parent_nprml.search('link')
|
20
|
+
@link = { :html => link_nprml[0].html,
|
21
|
+
:api => link_nprml[1].html }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module NPR
|
2
|
+
# Product represents any products associated with a Story. A lot of stories have
|
3
|
+
# books attached as products. Product isn't officially documented in the NPR API
|
4
|
+
# documentation, thus the comments here are based on observations. Products have the
|
5
|
+
# following attributes:
|
6
|
+
#
|
7
|
+
# * <tt>id</tt> - the unique ID for the Product within NPR
|
8
|
+
# * <tt>type</tt> - what kind of Product this is, i.e., book, etc
|
9
|
+
# * <tt>author</tt> - a book's author
|
10
|
+
# * <tt>upc</tt> - the UPC code for a Product
|
11
|
+
# * <tt>publisher</tt> - the publisher of a book
|
12
|
+
# * <tt>year</tt> - the year the book or Product was introduced/published
|
13
|
+
# * <tt>link</tt> - a hash linking to the product for sale on a vendor's website. Use <tt>link[:uri]</tt>
|
14
|
+
# to access the URI for the product and <tt>link[:vendor]</tt> to access the vendor.
|
15
|
+
#
|
16
|
+
class Product < Content
|
17
|
+
attr_accessor :id, :type, :title, :author, :upc, :publisher, :year, :link
|
18
|
+
|
19
|
+
private
|
20
|
+
def new_from_nprml(product_nprml)
|
21
|
+
@id = product_nprml[:id]
|
22
|
+
@type = product_nprml[:type]
|
23
|
+
@title = product_nprml.at('title').html
|
24
|
+
@author = product_nprml.at('author').html
|
25
|
+
@publisher = product_nprml.at('publisher').html
|
26
|
+
@year = product_nprml.at('publishYear').html
|
27
|
+
@upc = product_nprml.at('upc').html
|
28
|
+
@link = { :vendor => product_nprml.at('purchaseLink')[:vendor],
|
29
|
+
:uri => product_nprml.at('purchaseLink').html }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module NPR
|
2
|
+
class PullQuote < Content
|
3
|
+
attr_accessor :id, :text, :person, :date
|
4
|
+
|
5
|
+
private
|
6
|
+
def new_from_nprml(pullquote_nprml)
|
7
|
+
return nil if pullquote_nprml.empty?
|
8
|
+
@id, @person = pullquote_nprml[:id], pullquote_nprml.at('person').html
|
9
|
+
@text = Hpricot.XML(pullquote_nprml.at('text').html).innerText
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|