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 +1 -1
- data/lib/nytimes_articles/article.rb +47 -20
- data/lib/nytimes_articles/exceptions.rb +14 -0
- data/lib/nytimes_articles/facet.rb +64 -6
- data/lib/nytimes_articles/result_set.rb +27 -1
- data/lib/nytimes_articles/thumbnail.rb +30 -0
- data/lib/nytimes_articles.rb +1 -0
- data/test/nytimes/articles/test_article.rb +56 -22
- data/test/nytimes/articles/test_facet.rb +63 -0
- data/test/nytimes/articles/test_thumbnail.rb +47 -0
- metadata +4 -2
data/VERSION.yml
CHANGED
@@ -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 +
|
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
|
-
:
|
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>:
|
156
|
-
# * <tt>:
|
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
|
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
|
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
|
-
|
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]
|
276
|
+
out_params['fields'] = field_param(in_params[:fields])
|
254
277
|
when Array
|
255
|
-
out_params['fields'] = in_params[:fields].map {|f| f
|
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
|
-
|
313
|
+
facet_args = []
|
287
314
|
|
288
315
|
case facets
|
289
316
|
when nil
|
290
317
|
# do nothing
|
291
318
|
when String
|
292
|
-
|
319
|
+
facet_args = [facets]
|
293
320
|
when Facet
|
294
|
-
|
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
|
-
|
337
|
+
facet_args << facet_argument(k, v, exclude)
|
311
338
|
end
|
312
339
|
when Hash
|
313
340
|
facets.each_pair do |k,v|
|
314
|
-
|
341
|
+
facet_args << facet_argument(k, v, exclude)
|
315
342
|
end
|
316
343
|
end
|
317
344
|
|
318
|
-
|
345
|
+
facet_args
|
319
346
|
end
|
320
347
|
|
321
|
-
def self.
|
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[:
|
325
|
-
exclude_facets = parse_facet_params(in_params[:
|
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
|
-
|
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
|
35
|
-
DEFAULT_RETURN_FACETS = [
|
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
|
-
|
48
|
-
#
|
49
|
-
|
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
|
-
|
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
|
data/lib/nytimes_articles.rb
CHANGED
@@ -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 "
|
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 :
|
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 :
|
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 :
|
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 :
|
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 :
|
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 :
|
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", :
|
124
|
+
Article.search "ice cream", :only_facets => {Facet::GEO => "CALIFORNIA"}
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
-
context "
|
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 :
|
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 :
|
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 :
|
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 :
|
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 :
|
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 :
|
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", :
|
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
|
-
|
195
|
-
|
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
|
-
|
201
|
-
|
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.
|
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-
|
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
|