harrisj-nytimes-articles 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 1
3
- :patch: 1
3
+ :patch: 2
4
4
  :major: 0
@@ -2,6 +2,10 @@ require 'rubygems'
2
2
 
3
3
  module Nytimes
4
4
  module Articles
5
+ ##
6
+ # The Article class represents a single article returned from the New York Times Article Search API. Note that an article can have many attributes
7
+ # but these are not necessarily populated unless you explicitly request them in the reply from the server via the <tt>:fields</tt> parameter to
8
+ # search (or use <tt>:fields => :all</tt>).
5
9
  class Article < Base
6
10
  RAW_FIELDS = %w(url)
7
11
  TEXT_FIELDS = %w(abstract author body byline lead_paragraph nytd_lead_paragraph nytd_title title)
@@ -10,10 +14,13 @@ module Nytimes
10
14
  IMAGE_FIELDS = %w(small_image small_image_url small_image_height small_image_width)
11
15
  MULTIMEDIA_FIELDS = %w(multimedia related_multimedia)
12
16
 
13
- ALL_FIELDS = TEXT_FIELDS + RAW_FIELDS + NUMERIC_FIELDS + BOOLEAN_FIELDS + IMAGE_FIELDS + MULTIMEDIA_FIELDS + Facet::ALL_FACETS
17
+ ALL_FIELDS = TEXT_FIELDS + RAW_FIELDS + NUMERIC_FIELDS + BOOLEAN_FIELDS + MULTIMEDIA_FIELDS + Facet::ALL_FACETS + IMAGE_FIELDS
14
18
 
15
19
  attr_reader *ALL_FIELDS
16
20
 
21
+ # special additional objects
22
+ attr_reader :thumbnail
23
+
17
24
  # Scalar facets
18
25
  attr_reader :page, :column, :pub_month, :pub_year, :pub_day, :day_of_week, :desk, :date, :section_page, :source
19
26
 
@@ -53,7 +60,7 @@ module Nytimes
53
60
  :nytd_title => text_field(params['nytd_title']),
54
61
  :nytd_lead_paragraph => text_field(params['nytd_lead_paragraph']),
55
62
  :related_multimedia => nil, # FIXME
56
- :image => nil, # FIXME
63
+ :thumbnail => Thumbnail.init_from_api(params),
57
64
  :title => text_field(params['title']),
58
65
  :url => params['url'],
59
66
  :word_count => integer_field(params['word_count']),
@@ -151,16 +158,18 @@ module Nytimes
151
158
  # * <tt>Facet::NYTD_SECTION</tt> - The section the article appears in (on NYTimes.com)
152
159
  # * <tt>Facet::NYTD_WORKS_MENTIONED</tt> - Literary works mentioned (titles formatted for use on NYTimes.com)
153
160
  #
161
+ # Note that for your convenience you can also search with symbol versions of the constants (<tt>:geo => ['MANHATTAN']</tt>). Even pluralization is supported. To get the string API version of the facet use Facet#symbol_name
162
+ #
154
163
  # The following two search fields are used for facet searching:
155
- # * <tt>:search_facets</tt> - takes a single value or array of facets to search. Facets can either be specified as array pairs (like <tt>[Facet::GEOGRAPHIC, 'CALIFORNIA']</tt>) or facets returned from a previous search can be passed directly. A single string can be passed as well if you have hand-crafted string.
156
- # * <tt>:exclude_facets</tt> - similar to <tt>:search_facets</tt> but is used to specify a list of facets to exclude.
164
+ # * <tt>:only_facets</tt> - takes a single value or array of facets to search. Facets can either be specified as array pairs (like <tt>[Facet::GEOGRAPHIC, 'CALIFORNIA']</tt>) or facets returned from a previous search can be passed directly. A single string can be passed as well if you have hand-crafted string.
165
+ # * <tt>:except_facets</tt> - similar to <tt>:only_facets</tt> but is used to specify a list of facets to exclude.
157
166
  #
158
167
  # == OTHER SEARCH FIELDS
159
- # * <tt>:fee</tt> - to be implemented
168
+ # * <tt>:fee</tt> - if set to true, only returns articles that must be purchased. If false, returns only free articles. If not specified, returns all articles
160
169
  # * <tt>:begin_date</tt>, <tt>:end_date</tt> - the parameters are used to specify a start and end date for search results. BOTH of these must be provided or the API will return an error. Accepts either a Time/Date argument or a string of the format YYYYMMDD. For convenience the following alternative methods are provided
161
170
  # * <tt>:before</tt> - an alternative to :end_date. Automatically adds a :before_date of sometime in 1980 if no :since argument is also provided; to be implemented
162
171
  # * <tt>:since</tt> - An alternative to :begin_date. Automatically adds an :end_date of Time.now if no :before argument is provided; to be implemented.
163
- # * <tt>:has_thumbnail</tt> - to be implemented
172
+ # * <tt>:has_thumbnail</tt> - returns only articles that have thumbnail images associated. Note that to see the thumbnails, you must specify either <tt>:thumbnail</tt> or <tt>:all</tt> in the <tt>:fields</tt> argument).
164
173
  # * <tt>:has_multimedia</tt> - to be implemented
165
174
  #
166
175
  # == FACET SUMMARIES
@@ -193,10 +202,10 @@ module Nytimes
193
202
  api_params = {}
194
203
 
195
204
  add_query_params(api_params, params)
196
- add_search_facets_param(api_params, params)
205
+ add_facet_conditions_params(api_params, params)
197
206
  add_boolean_params(api_params, params)
198
- add_fields_param(api_params, params)
199
207
  add_facets_param(api_params, params)
208
+ add_fields_param(api_params, params)
200
209
  add_rank_params(api_params, params)
201
210
  add_date_params(api_params, params)
202
211
  add_offset_params(api_params, params)
@@ -239,7 +248,16 @@ module Nytimes
239
248
 
240
249
  def self.add_facets_param(out_params, in_params)
241
250
  if in_params[:facets]
242
- out_params['facets'] = in_params[:facets].to_a.join(',')
251
+ out_params['facets'] = in_params[:facets].to_a.map {|f| Facet.symbol_name(f)}.join(',')
252
+ end
253
+ end
254
+
255
+ def self.field_param(name)
256
+ case name.to_s
257
+ when 'thumbnail'
258
+ IMAGE_FIELDS.join(',')
259
+ else
260
+ name.to_s
243
261
  end
244
262
  end
245
263
 
@@ -249,10 +267,15 @@ module Nytimes
249
267
  # do nothing
250
268
  when :all
251
269
  out_params['fields'] = ALL_FIELDS.join(',')
270
+ when :none
271
+ out_params['fields'] = ' '
272
+ unless out_params['facets']
273
+ out_params['facets'] = Facet::DEFAULT_RETURN_FACETS.join(',')
274
+ end
252
275
  when String, Symbol
253
- out_params['fields'] = in_params[:fields].to_s
276
+ out_params['fields'] = field_param(in_params[:fields])
254
277
  when Array
255
- out_params['fields'] = in_params[:fields].map {|f| f.to_s}.join(',')
278
+ out_params['fields'] = in_params[:fields].map {|f| field_param(f)}.join(',')
256
279
  else
257
280
  raise ArgumentError, "Fields must either be :all, a single field name, or an array of field names (either strings or symbols)"
258
281
  end
@@ -279,19 +302,23 @@ module Nytimes
279
302
  value = [value]
280
303
  end
281
304
 
305
+ if name.is_a? Symbol
306
+ name = Facet.symbol_name(name)
307
+ end
308
+
282
309
  "#{'-' if exclude}#{name}:[#{value.join(',')}]"
283
310
  end
284
311
 
285
312
  def self.parse_facet_params(facets, exclude = false)
286
- search_facets = []
313
+ facet_args = []
287
314
 
288
315
  case facets
289
316
  when nil
290
317
  # do nothing
291
318
  when String
292
- search_facets = [facets]
319
+ facet_args = [facets]
293
320
  when Facet
294
- search_facets = [facet_argument(facets.facet_type, facets.term, exclude)]
321
+ facet_args = [facet_argument(facets.facet_type, facets.term, exclude)]
295
322
  when Array
296
323
  unless facets.all? {|f| f.is_a? Facet }
297
324
  raise ArgumentError, "Only Facet instances can be passed in as an array; use Hash for Facet::Name => values input"
@@ -307,22 +334,22 @@ module Nytimes
307
334
  end
308
335
 
309
336
  facet_hash.each_pair do |k,v|
310
- search_facets << facet_argument(k, v, exclude)
337
+ facet_args << facet_argument(k, v, exclude)
311
338
  end
312
339
  when Hash
313
340
  facets.each_pair do |k,v|
314
- search_facets << facet_argument(k, v, exclude)
341
+ facet_args << facet_argument(k, v, exclude)
315
342
  end
316
343
  end
317
344
 
318
- search_facets
345
+ facet_args
319
346
  end
320
347
 
321
- def self.add_search_facets_param(out_params, in_params)
348
+ def self.add_facet_conditions_params(out_params, in_params)
322
349
  query = out_params['query']
323
350
 
324
- search_facets = parse_facet_params(in_params[:search_facets])
325
- exclude_facets = parse_facet_params(in_params[:exclude_facets], true)
351
+ search_facets = parse_facet_params(in_params[:only_facets])
352
+ exclude_facets = parse_facet_params(in_params[:except_facets], true)
326
353
 
327
354
  unless search_facets.empty? && exclude_facets.empty?
328
355
  out_params['query'] = ([query] + search_facets + exclude_facets).compact.join(' ')
@@ -1,23 +1,37 @@
1
1
  module Nytimes
2
2
  module Articles
3
+ ##
4
+ # The generic Error class from which all other Errors are derived.
3
5
  class Error < ::RuntimeError
4
6
  end
5
7
 
8
+ ##
9
+ # This error is thrown if there are problems authenticating your API key.
6
10
  class AuthenticationError < Error
7
11
  end
8
12
 
13
+ ##
14
+ # This error is thrown if the request was not parsable by the API server.
9
15
  class BadRequestError < Error
10
16
  end
11
17
 
18
+ ##
19
+ # This error is thrown if the response from the API server is not parsable.
12
20
  class BadResponseError < Error
13
21
  end
14
22
 
23
+ ##
24
+ # This error is thrown if there is an error connecting to the API server.
15
25
  class ServerError < Error
16
26
  end
17
27
 
28
+ ##
29
+ # This error is thrown if there is a timeout connecting to the server (to be implemented).
18
30
  class TimeoutError < Error
19
31
  end
20
32
 
33
+ ##
34
+ # This error is thrown for general connection errors to the API server.
21
35
  class ConnectionError < Error
22
36
  end
23
37
  end
@@ -1,7 +1,24 @@
1
1
  module Nytimes
2
2
  module Articles
3
+
4
+ ##
5
+ # This class represents a Facet used in the ArticleSearch API. Facets can be used to both search for matching articles (see Article#search) and
6
+ # are also returned as article and search metadata. Facets are made up of 3 parts:
7
+ # * <tt>facet_type</tt> - a string; see Article#search for a list of facet types
8
+ # * <tt>term</tt> - a string as well
9
+ # * <tt>count</tt> - Facets returned as search metadata (via the <tt>:facets</tt> parameter to Article#search) also include a non-nil count of matching articles for that facet
3
10
  class Facet
4
- attr_reader :term, :count, :facet_type
11
+ ##
12
+ # The term for the facet
13
+ attr_reader :term
14
+
15
+ ##
16
+ # The number of times this facet has appeared in the search results (note: this only applies for facets returned in the facets header on an Article#search)
17
+ attr_reader :count
18
+
19
+ ##
20
+ # The facet type
21
+ attr_reader :facet_type
5
22
 
6
23
  # Facet name constants
7
24
  CLASSIFIERS = 'classifiers_facet'
@@ -31,23 +48,64 @@ module Nytimes
31
48
  NYTD_SECTION = 'nytd_section_facet'
32
49
  NYTD_WORKS_MENTIONED = 'nytd_works_mentioned_facet'
33
50
 
34
- # The best 5 facets to return
35
- DEFAULT_RETURN_FACETS = [NYTD_DESCRIPTION, NYTD_GEO, NYTD_ORGANIZATION, NYTD_PERSON, NYTD_SECTION]
51
+ # The default 5 facets to return
52
+ DEFAULT_RETURN_FACETS = [DESCRIPTION, GEO, ORGANIZATION, PERSON, DESK]
36
53
 
37
54
  ALL_FACETS = [CLASSIFIERS, COLUMN, DATE, DAY_OF_WEEK, DESCRIPTION, DESK, GEO, MATERIAL_TYPE, ORGANIZATION, PAGE, PERSON, PUB_DAY,
38
55
  PUB_MONTH, PUB_YEAR, SECTION_PAGE, SOURCE, WORKS_MENTIONED, NYTD_BYLINE, NYTD_DESCRIPTION, NYTD_GEO,
39
56
  NYTD_ORGANIZATION, NYTD_PERSON, NYTD_SECTION, NYTD_WORKS_MENTIONED]
40
57
 
58
+ ##
59
+ # Initializes the facet. There is seldom a reason for you to call this.
41
60
  def initialize(facet_type, term, count)
42
61
  @facet_type = facet_type
43
62
  @term = term
44
63
  @count = count
45
64
  end
46
65
 
47
- # def self.init_from_api(type, hash)
48
- # self.new(type, hash['term'], hash['count'].to_i)
49
- # end
66
+ ##
67
+ # Takes a symbol name and subs it to a string constant
68
+ def self.symbol_name(facet)
69
+ case facet
70
+ when String
71
+ return facet
72
+ when Facet
73
+ return facet.facet_type
74
+ when Symbol
75
+ # fall through
76
+ else
77
+ raise ArgumentError, "Unsupported type to Facet#symbol_to_api_name"
78
+ end
79
+
80
+ case facet
81
+ when :geography
82
+ GEO
83
+ when :org, :orgs
84
+ ORGANIZATION
85
+ when :people
86
+ PERSON
87
+ when :nytd_geography
88
+ NYTD_GEO
89
+ when :nytd_org, :nytd_orgs
90
+ NYTD_ORGANIZATION
91
+ when :nytd_people
92
+ NYTD_PERSON
93
+ else
94
+ name = facet.to_s.upcase
95
+
96
+ if const_defined?(name)
97
+ const_get(name)
98
+ elsif name =~ /S$/ && const_defined?(name.gsub(/S$/, ''))
99
+ const_get(name.gsub(/S$/, ''))
100
+ else
101
+ raise ArgumentError, "Unable to find a matching facet key for symbol :#{facet}"
102
+ end
103
+ end
104
+ end
50
105
 
106
+ ##
107
+ # Initializes a selection of Facet objects returned from the API. Used for marshaling Facets in articles and metadata from search results
108
+ # (Note: some facets are returned as scalar values)
51
109
  def self.init_from_api(api_hash)
52
110
  return nil if api_hash.nil?
53
111
 
@@ -3,9 +3,29 @@ require 'forwardable'
3
3
 
4
4
  module Nytimes
5
5
  module Articles
6
+ ##
7
+ # The ResultSet is returned by Article#search and contains an array of up to 10 results out of the total matches. For your convenience, this
8
+ # object provides a selection of array methods on the underlying collection of articles.
6
9
  class ResultSet < Base
7
10
  extend Forwardable
8
- attr_reader :offset, :total_results, :results, :facets
11
+
12
+ ##
13
+ # The offset of the result_set. Note that this is essentially the ordinal position of the batch among all results. First 10 results are offset
14
+ # 0, the next 10 are offset 1, etc.
15
+ attr_reader :offset
16
+
17
+ ##
18
+ # The total results that matched the query.
19
+ attr_reader :total_results
20
+
21
+ ##
22
+ # The results array of articles returned. Note that if you call Articles#find with :fields => :none, this will return nil even if
23
+ # there are matching results.
24
+ attr_reader :results
25
+
26
+ ##
27
+ # If you have specified a list of <tt>:facets</tt> for Article#search, they will be returned in a hash keyed by the facet name here.
28
+ attr_reader :facets
9
29
 
10
30
  BATCH_SIZE = 10
11
31
 
@@ -18,16 +38,22 @@ module Nytimes
18
38
  @facets = params[:facets]
19
39
  end
20
40
 
41
+ ##
42
+ # For your convenience, the page_number method is an alternate version of #offset that counts up from 1.
21
43
  def page_number
22
44
  return 0 if @total_results == 0
23
45
  @offset + 1
24
46
  end
25
47
 
48
+ ##
49
+ # Calculates the total number of pages in the results based on the standard batch size and total results.
26
50
  def total_pages
27
51
  return 0 if @total_results == 0
28
52
  (@total_results.to_f / BATCH_SIZE).ceil
29
53
  end
30
54
 
55
+ ##
56
+ # Used to initialize a new result_set from Article#search.
31
57
  def self.init_from_api(api_hash)
32
58
  self.new(:offset => integer_field(api_hash['offset']),
33
59
  :total_results => integer_field(api_hash['total']),
@@ -0,0 +1,30 @@
1
+ module Nytimes
2
+ module Articles
3
+ ##
4
+ # If requested in <tt>:fields</tt> for an article search, some articles are returned with a matching thumbnail image. The several thumbnail
5
+ # fields are collected together into a single Thumbnail instance for your convenience.
6
+ class Thumbnail
7
+ attr_reader :url, :width, :height
8
+
9
+ def initialize(url, width, height)
10
+ @url = url
11
+ @width = width
12
+ @height = height
13
+ end
14
+
15
+ def self.init_from_api(api_hash)
16
+ return nil unless !api_hash.nil? && api_hash['small_image_url']
17
+
18
+ unless api_hash['small_image_width'].nil?
19
+ width = api_hash['small_image_width'].to_i
20
+ end
21
+
22
+ unless api_hash['small_image_height'].nil?
23
+ height = api_hash['small_image_height'].to_i
24
+ end
25
+
26
+ new(api_hash['small_image_url'], width, height)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,5 +4,6 @@ $KCODE = 'UTF8'
4
4
  require File.join(File.dirname(__FILE__), 'nytimes_articles', 'exceptions')
5
5
  require File.join(File.dirname(__FILE__), 'nytimes_articles', 'base')
6
6
  require File.join(File.dirname(__FILE__), 'nytimes_articles', 'facet')
7
+ require File.join(File.dirname(__FILE__), 'nytimes_articles', 'thumbnail')
7
8
  require File.join(File.dirname(__FILE__), 'nytimes_articles', 'article')
8
9
  require File.join(File.dirname(__FILE__), 'nytimes_articles', 'result_set')
@@ -81,26 +81,26 @@ class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
81
81
  end
82
82
  end
83
83
 
84
- context "search_facets" do
84
+ context "only_facets" do
85
85
  should "accept a String" do
86
86
  Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA]"))
87
- Article.search :search_facets => "#{Facet::GEO}:[CALIFORNIA]"
87
+ Article.search :only_facets => "#{Facet::GEO}:[CALIFORNIA]"
88
88
  end
89
89
 
90
90
  should "accept a single hash value Facet string to a term" do
91
91
  Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA]"))
92
- Article.search :search_facets => {Facet::GEO => 'CALIFORNIA'}
92
+ Article.search :only_facets => {Facet::GEO => 'CALIFORNIA'}
93
93
  end
94
94
 
95
95
  should "accept an Facet string hashed to an array terms" do
96
96
  Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA,GREAT BRITAIN]"))
97
- Article.search :search_facets => {Facet::GEO => ['CALIFORNIA', 'GREAT BRITAIN']}
97
+ Article.search :only_facets => {Facet::GEO => ['CALIFORNIA', 'GREAT BRITAIN']}
98
98
  end
99
99
 
100
100
  should "accept a single Facet object" do
101
101
  f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
102
102
  Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA]"))
103
- Article.search :search_facets => f
103
+ Article.search :only_facets => f
104
104
  end
105
105
 
106
106
  should "accept an array of Facet objects" do
@@ -108,7 +108,7 @@ class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
108
108
  f2 = Facet.new(Facet::NYTD_ORGANIZATION, 'University Of California', 12)
109
109
 
110
110
  Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA] #{Facet::NYTD_ORGANIZATION}:[University Of California]"))
111
- Article.search :search_facets => [f, f2]
111
+ Article.search :only_facets => [f, f2]
112
112
  end
113
113
 
114
114
  should "merge multiple Facets objects in the array of the same type into one array" do
@@ -116,35 +116,35 @@ class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
116
116
  f2 = Facet.new(Facet::GEO, 'IOWA', 12)
117
117
 
118
118
  Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA,IOWA]"))
119
- Article.search :search_facets => [f, f2]
119
+ Article.search :only_facets => [f, f2]
120
120
  end
121
121
 
122
122
  should "not stomp on an existing query string" do
123
123
  Article.expects(:invoke).with(has_entry("query", "ice cream #{Facet::GEO}:[CALIFORNIA]"))
124
- Article.search "ice cream", :search_facets => {Facet::GEO => "CALIFORNIA"}
124
+ Article.search "ice cream", :only_facets => {Facet::GEO => "CALIFORNIA"}
125
125
  end
126
126
  end
127
127
 
128
- context "exclude_facets" do
128
+ context "except_facets" do
129
129
  should "accept a String" do
130
130
  Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA]"))
131
- Article.search :exclude_facets => "-#{Facet::GEO}:[CALIFORNIA]"
131
+ Article.search :except_facets => "-#{Facet::GEO}:[CALIFORNIA]"
132
132
  end
133
133
 
134
134
  should "accept a single hash value Facet string to a term" do
135
135
  Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA]"))
136
- Article.search :exclude_facets => {Facet::GEO => 'CALIFORNIA'}
136
+ Article.search :except_facets => {Facet::GEO => 'CALIFORNIA'}
137
137
  end
138
138
 
139
139
  should "accept an Facet string hashed to an array terms" do
140
140
  Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA,GREAT BRITAIN]"))
141
- Article.search :exclude_facets => {Facet::GEO => ['CALIFORNIA', 'GREAT BRITAIN']}
141
+ Article.search :except_facets => {Facet::GEO => ['CALIFORNIA', 'GREAT BRITAIN']}
142
142
  end
143
143
 
144
144
  should "accept a single Facet object" do
145
145
  f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
146
146
  Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA]"))
147
- Article.search :exclude_facets => f
147
+ Article.search :except_facets => f
148
148
  end
149
149
 
150
150
  should "accept an array of Facet objects" do
@@ -152,7 +152,7 @@ class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
152
152
  f2 = Facet.new(Facet::NYTD_ORGANIZATION, 'University Of California', 12)
153
153
 
154
154
  Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA] -#{Facet::NYTD_ORGANIZATION}:[University Of California]"))
155
- Article.search :exclude_facets => [f, f2]
155
+ Article.search :except_facets => [f, f2]
156
156
  end
157
157
 
158
158
  should "merge multiple Facets objects in the array of the same type into one array" do
@@ -160,12 +160,12 @@ class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
160
160
  f2 = Facet.new(Facet::GEO, 'IOWA', 12)
161
161
 
162
162
  Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA,IOWA]"))
163
- Article.search :exclude_facets => [f, f2]
163
+ Article.search :except_facets => [f, f2]
164
164
  end
165
165
 
166
166
  should "not stomp on an existing query string" do
167
167
  Article.expects(:invoke).with(has_entry("query", "ice cream -#{Facet::GEO}:[CALIFORNIA]"))
168
- Article.search "ice cream", :exclude_facets => {Facet::GEO => "CALIFORNIA"}
168
+ Article.search "ice cream", :except_facets => {Facet::GEO => "CALIFORNIA"}
169
169
  end
170
170
  end
171
171
 
@@ -190,15 +190,32 @@ class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
190
190
  end
191
191
 
192
192
  context "for the :none argument" do
193
- should "request a blank space for the fields argument"
194
- should "request the standard :facets if no :facets have been explicitly provided"
195
- should "request the given :facets field if provided"
193
+ should "request a blank space for the fields argument" do
194
+ Article.expects(:invoke).with(has_entry('fields', ' '))
195
+ Article.search "FOO BAR", :fields => :none
196
+ end
197
+
198
+ should "request the standard :facets if no :facets have been explicitly provided" do
199
+ Article.expects(:invoke).with(has_entry('facets', Facet::DEFAULT_RETURN_FACETS.join(',')))
200
+ Article.search "FOO BAR", :fields => :none
201
+ end
202
+
203
+ should "request the given :facets field if provided" do
204
+ Article.expects(:invoke).with(has_entry('facets', "#{Facet::GEO}"))
205
+ Article.search "FOO BAR", :fields => :none, :facets => Facet::GEO
206
+ end
196
207
  end
197
208
 
198
209
  context ":thumbnail" do
199
- should "accept the symbol version of the argument"
200
- should "accept the string version of the argument"
201
- should "request all the thumbnail image fields from the API"
210
+ should "accept the symbol version of the argument" do
211
+ Article.expects(:invoke).with(has_entry('fields', Article::IMAGE_FIELDS.join(',')))
212
+ Article.search "FOO BAR", :fields => :thumbnail
213
+ end
214
+
215
+ should "accept the string version of the argument" do
216
+ Article.expects(:invoke).with(has_entry('fields', Article::IMAGE_FIELDS.join(',')))
217
+ Article.search "FOO BAR", :fields => 'thumbnail'
218
+ end
202
219
  end
203
220
 
204
221
  context ":multimedia" do
@@ -449,6 +466,23 @@ class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
449
466
  assert_nil article.page
450
467
  end
451
468
  end
469
+
470
+ context "@thumbnail" do
471
+ should "assign nil to thumbnail otherwise" do
472
+ article = Article.init_from_api({"foo" => "bar"})
473
+ assert_nil article.thumbnail
474
+ end
475
+
476
+ should "create a thumbnail object if a small_image_url is part of the return hash" do
477
+ article = Article.init_from_api(ARTICLE_API_HASH2)
478
+ thumbnail = article.thumbnail
479
+ assert_not_nil thumbnail
480
+ assert_kind_of Thumbnail, thumbnail
481
+ assert_equal ARTICLE_API_HASH2['small_image_url'], thumbnail.url
482
+ assert_equal ARTICLE_API_HASH2['small_image_width'].to_i, thumbnail.width
483
+ assert_equal ARTICLE_API_HASH2['small_image_height'].to_i, thumbnail.height
484
+ end
485
+ end
452
486
  end
453
487
  end
454
488
 
@@ -43,4 +43,67 @@ class TestNytimes::TestArticles::TestFacet < Test::Unit::TestCase
43
43
  assert @facets[first_key].all? {|f| f.facet_type == first_key }
44
44
  end
45
45
  end
46
+
47
+ context "Facet.symbol_name" do
48
+ [:geo, :geography].each do |sym|
49
+ should "return Facet::GEO for #{sym}" do
50
+ assert_equal Facet::GEO, Facet.symbol_name(sym)
51
+ end
52
+ end
53
+
54
+ [:org, :orgs, :organization, :organizations].each do |sym|
55
+ should "return Facet::ORGANIZATION for #{sym}" do
56
+ assert_equal Facet::ORGANIZATION, Facet.symbol_name(sym)
57
+ end
58
+ end
59
+
60
+ [:people, :person, :persons].each do |sym|
61
+ should "return Facet::PERSON for #{sym}" do
62
+ assert_equal Facet::PERSON, Facet.symbol_name(sym)
63
+ end
64
+ end
65
+
66
+ [:nytd_geo, :nytd_geography].each do |sym|
67
+ should "return Facet::NYTD_GEO for #{sym}" do
68
+ assert_equal Facet::NYTD_GEO, Facet.symbol_name(sym)
69
+ end
70
+ end
71
+
72
+ [:nytd_org, :nytd_orgs, :nytd_organization, :nytd_organizations].each do |sym|
73
+ should "return Facet::NYTD_ORGANIZATION for #{sym}" do
74
+ assert_equal Facet::NYTD_ORGANIZATION, Facet.symbol_name(sym)
75
+ end
76
+ end
77
+
78
+ [:nytd_people, :nytd_person, :nytd_persons].each do |sym|
79
+ should "return Facet::NYTD_PERSON for #{sym}" do
80
+ assert_equal Facet::NYTD_PERSON, Facet.symbol_name(sym)
81
+ end
82
+ end
83
+
84
+ should "look for a matching constant and use that value" do
85
+ assert_equal Facet::SOURCE, Facet.symbol_name(:source)
86
+ end
87
+
88
+ should "singularize the symbol when looking for a constant if no match for the plural form" do
89
+ assert_equal Facet::PAGE, Facet.symbol_name(:pages)
90
+ end
91
+
92
+ should "return the string passed in if passed a string" do
93
+ assert_equal "FOOBAR", Facet.symbol_name('FOOBAR')
94
+ end
95
+
96
+ should "return the facet's facet_type if passed a Facet object" do
97
+ f = Facet.new(Facet::ORGANIZATION, 'THE NEW YORK TIMES', nil)
98
+ assert_equal Facet::ORGANIZATION, Facet.symbol_name(f)
99
+ end
100
+
101
+ should "raise an ArgumentError if not passed a symbol" do
102
+ assert_raise(ArgumentError) { Facet.symbol_name(23) }
103
+ end
104
+
105
+ should "raise an ArgumentError if unable to find a matching Facet constant" do
106
+ assert_raise(ArgumentError) { Facet.symbol_name(:clown) }
107
+ end
108
+ end
46
109
  end
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper.rb'
2
+
3
+ class TestNytimes::TestArticles::TestThumbnail < Test::Unit::TestCase
4
+ include Nytimes::Articles
5
+
6
+ context "read-only attributes" do
7
+ setup do
8
+ @thumbnail = Thumbnail.new("http://www.foo.com", 400, 600)
9
+ end
10
+
11
+ %w(url width height).each do |field|
12
+ should "have a #{field} attribute" do
13
+ assert @thumbnail.respond_to?(field)
14
+ end
15
+
16
+ should "be read-only for the #{field} attribute" do
17
+ assert_raise(NoMethodError) do
18
+ @thumbnail.send "#{field}=", "new value"
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ context "Facet.init_from_api" do
25
+ setup do
26
+ @hash = {"small_image_url" => "http://foo.com/", 'small_image_width' => '400', 'small_image_height' => '600'}
27
+ end
28
+
29
+ should "return nil if reply from API has no URL" do
30
+ assert_nil Thumbnail.init_from_api(nil)
31
+ assert_nil Thumbnail.init_from_api({})
32
+ end
33
+
34
+ %w(width height).each do |dimension|
35
+ should "set to nil if it is nil in the array" do
36
+ @hash["small_image_#{dimension}"] = nil
37
+ thumbnail = Thumbnail.init_from_api(@hash)
38
+ assert_nil thumbnail.send(dimension)
39
+ end
40
+
41
+ should "cast to an Integer value if passed a string" do
42
+ thumbnail = Thumbnail.init_from_api(@hash)
43
+ assert_equal @hash["small_image_#{dimension}"].to_i, thumbnail.send(dimension)
44
+ end
45
+ end
46
+ end
47
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harrisj-nytimes-articles
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Harris
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-14 00:00:00 -08:00
12
+ date: 2009-02-17 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -38,6 +38,7 @@ files:
38
38
  - lib/nytimes_articles/exceptions.rb
39
39
  - lib/nytimes_articles/facet.rb
40
40
  - lib/nytimes_articles/result_set.rb
41
+ - lib/nytimes_articles/thumbnail.rb
41
42
  - lib/nytimes_articles.rb
42
43
  - test/nytimes
43
44
  - test/nytimes/articles
@@ -45,6 +46,7 @@ files:
45
46
  - test/nytimes/articles/test_base.rb
46
47
  - test/nytimes/articles/test_facet.rb
47
48
  - test/nytimes/articles/test_result_set.rb
49
+ - test/nytimes/articles/test_thumbnail.rb
48
50
  - test/test_helper.rb
49
51
  has_rdoc: true
50
52
  homepage: http://github.com/harrisj/nytimes-articles