rubynpr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +58 -0
  3. data/README.txt +53 -0
  4. data/Rakefile +16 -0
  5. data/bin/rubynpr +0 -0
  6. data/lib/rubynpr/audio.rb +43 -0
  7. data/lib/rubynpr/byline.rb +20 -0
  8. data/lib/rubynpr/client.rb +192 -0
  9. data/lib/rubynpr/content.rb +17 -0
  10. data/lib/rubynpr/exception.rb +34 -0
  11. data/lib/rubynpr/image.rb +34 -0
  12. data/lib/rubynpr/item.rb +25 -0
  13. data/lib/rubynpr/list.rb +47 -0
  14. data/lib/rubynpr/message.rb +15 -0
  15. data/lib/rubynpr/organization.rb +22 -0
  16. data/lib/rubynpr/parent.rb +24 -0
  17. data/lib/rubynpr/product.rb +32 -0
  18. data/lib/rubynpr/pull_quote.rb +12 -0
  19. data/lib/rubynpr/related_link.rb +26 -0
  20. data/lib/rubynpr/result_parser.rb +18 -0
  21. data/lib/rubynpr/show.rb +32 -0
  22. data/lib/rubynpr/story.rb +91 -0
  23. data/lib/rubynpr/subcategory.rb +17 -0
  24. data/lib/rubynpr.rb +28 -0
  25. data/test/audio_test.rb +24 -0
  26. data/test/byline_test.rb +10 -0
  27. data/test/client_test.rb +44 -0
  28. data/test/fixtures/audio.yml +32 -0
  29. data/test/fixtures/byline.yml +6 -0
  30. data/test/fixtures/client.yml +190 -0
  31. data/test/fixtures/image.yml +10 -0
  32. data/test/fixtures/item.yml +15 -0
  33. data/test/fixtures/list.yml +78 -0
  34. data/test/fixtures/list_text.xml +3 -0
  35. data/test/fixtures/organization.yml +11 -0
  36. data/test/fixtures/parent.yml +6 -0
  37. data/test/fixtures/product.yml +9 -0
  38. data/test/fixtures/pullquote.yml +6 -0
  39. data/test/fixtures/relatedlink.yml +6 -0
  40. data/test/fixtures/show.yml +6 -0
  41. data/test/fixtures/stories.yml +522 -0
  42. data/test/fixtures/story.yml +124 -0
  43. data/test/fixtures/subcategory.yml +59 -0
  44. data/test/fixtures.rb +86 -0
  45. data/test/image_test.rb +16 -0
  46. data/test/item_test.rb +18 -0
  47. data/test/list_test.rb +17 -0
  48. data/test/organization_test.rb +16 -0
  49. data/test/parent_test.rb +13 -0
  50. data/test/product_test.rb +16 -0
  51. data/test/pullquote_test.rb +11 -0
  52. data/test/related_link_test.rb +9 -0
  53. data/test/show_test.rb +12 -0
  54. data/test/story_list_test.rb +21 -0
  55. data/test/story_test.rb +50 -0
  56. data/test/subcategory_test.rb +15 -0
  57. data/test/test_helper.rb +12 -0
  58. data/test/test_rubynpr.rb +1 -0
  59. metadata +150 -0
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 / 2008-10-26
2
+
3
+ * Initial release of ruby-npr...!
4
+
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
@@ -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
@@ -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