nytimes-articles 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/HISTORY +9 -0
- data/LICENSE +24 -0
- data/README +32 -0
- data/Rakefile +54 -0
- data/VERSION.yml +4 -0
- data/features/nytimes_articles.feature +9 -0
- data/features/steps/nytimes_articles_steps.rb +0 -0
- data/features/support/env.rb +13 -0
- data/lib/nytimes_articles.rb +6 -0
- data/lib/nytimes_articles/article.rb +466 -0
- data/lib/nytimes_articles/base.rb +124 -0
- data/lib/nytimes_articles/exceptions.rb +38 -0
- data/lib/nytimes_articles/facet.rb +128 -0
- data/lib/nytimes_articles/facet_hash.rb +26 -0
- data/lib/nytimes_articles/query.rb +28 -0
- data/lib/nytimes_articles/result_set.rb +66 -0
- data/lib/nytimes_articles/thumbnail.rb +30 -0
- data/nytimes-articles.gemspec +73 -0
- data/script/console +10 -0
- data/test/nytimes/articles/test_article.rb +584 -0
- data/test/nytimes/articles/test_base.rb +120 -0
- data/test/nytimes/articles/test_facet.rb +109 -0
- data/test/nytimes/articles/test_query.rb +89 -0
- data/test/nytimes/articles/test_result_set.rb +62 -0
- data/test/nytimes/articles/test_thumbnail.rb +47 -0
- data/test/test_helper.rb +31 -0
- metadata +98 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{nytimes-articles}
|
5
|
+
s.version = "0.4.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Jacob Harris"]
|
9
|
+
s.date = %q{2009-07-23}
|
10
|
+
s.description = %q{A gem for accessing the New York Times Article Search API}
|
11
|
+
s.email = %q{jharris@nytimes.com}
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"LICENSE",
|
14
|
+
"README"
|
15
|
+
]
|
16
|
+
s.files = [
|
17
|
+
".gitignore",
|
18
|
+
"HISTORY",
|
19
|
+
"LICENSE",
|
20
|
+
"README",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION.yml",
|
23
|
+
"features/nytimes_articles.feature",
|
24
|
+
"features/steps/nytimes_articles_steps.rb",
|
25
|
+
"features/support/env.rb",
|
26
|
+
"lib/nytimes_articles.rb",
|
27
|
+
"lib/nytimes_articles/article.rb",
|
28
|
+
"lib/nytimes_articles/base.rb",
|
29
|
+
"lib/nytimes_articles/exceptions.rb",
|
30
|
+
"lib/nytimes_articles/facet.rb",
|
31
|
+
"lib/nytimes_articles/facet_hash.rb",
|
32
|
+
"lib/nytimes_articles/query.rb",
|
33
|
+
"lib/nytimes_articles/result_set.rb",
|
34
|
+
"lib/nytimes_articles/thumbnail.rb",
|
35
|
+
"nytimes-articles.gemspec",
|
36
|
+
"script/console",
|
37
|
+
"test/nytimes/articles/test_article.rb",
|
38
|
+
"test/nytimes/articles/test_base.rb",
|
39
|
+
"test/nytimes/articles/test_facet.rb",
|
40
|
+
"test/nytimes/articles/test_query.rb",
|
41
|
+
"test/nytimes/articles/test_result_set.rb",
|
42
|
+
"test/nytimes/articles/test_thumbnail.rb",
|
43
|
+
"test/test_helper.rb"
|
44
|
+
]
|
45
|
+
s.homepage = %q{http://github.com/harrisj/nytimes-articles}
|
46
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
47
|
+
s.require_paths = ["lib"]
|
48
|
+
s.requirements = ["Unicode", "The htmlentities gem"]
|
49
|
+
s.rubygems_version = %q{1.3.5}
|
50
|
+
s.summary = %q{A gem for accessing the NYTimes Article Search API}
|
51
|
+
s.test_files = [
|
52
|
+
"test/nytimes/articles/test_article.rb",
|
53
|
+
"test/nytimes/articles/test_base.rb",
|
54
|
+
"test/nytimes/articles/test_facet.rb",
|
55
|
+
"test/nytimes/articles/test_query.rb",
|
56
|
+
"test/nytimes/articles/test_result_set.rb",
|
57
|
+
"test/nytimes/articles/test_thumbnail.rb",
|
58
|
+
"test/test_helper.rb"
|
59
|
+
]
|
60
|
+
|
61
|
+
if s.respond_to? :specification_version then
|
62
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
63
|
+
s.specification_version = 3
|
64
|
+
|
65
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
66
|
+
s.add_runtime_dependency(%q<htmlentities>, [">= 0"])
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<htmlentities>, [">= 0"])
|
69
|
+
end
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<htmlentities>, [">= 0"])
|
72
|
+
end
|
73
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/nytimes_articles.rb'}"
|
9
|
+
puts "Loading nytimes_articles gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
@@ -0,0 +1,584 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper.rb'
|
2
|
+
|
3
|
+
ARTICLE_API_HASH = {"page_facet"=>"8", "lead_paragraph"=>"", "classifiers_facet"=>["Top/News/Business", "Top/Classifieds/Job Market/Job Categories/Banking, Finance and Insurance", "Top/News/Business/Markets"], "title"=>"Wall St. Treads Water as It Waits on Washington", "nytd_title"=>"Wall St. Treads Water as It Waits on Washington", "byline"=>"By JACK HEALY", "body"=>"Wall Street held its breath on Monday as it awaited details on a banking bailout from Washington. Investors had expected to start the week with an announcement from the Treasury Department outlining its latest plans to stabilize the financial system. But the Obama administration delayed releasing the details until at least Tuesday to keep the focus", "material_type_facet"=>["News"], "url"=>"http://www.nytimes.com/2009/02/10/business/10markets.html", "publication_month"=>"02", "date"=>"20090210", "publication_year"=>"2009", "nytd_section_facet"=>["Business"], "source_facet"=>"The New York Times", "desk_facet"=>"Business", "publication_day"=>"10", "des_facet"=>["STOCKS AND BONDS"], "day_of_week_facet"=>"Tuesday"}
|
4
|
+
|
5
|
+
ARTICLE_API_HASH2 = {"page_facet"=>"29", "lead_paragraph"=>"", "geo_facet"=>["WALL STREET (NYC)"], "small_image_width"=>"75", "classifiers_facet"=>["Top/News/New York and Region", "Top/Classifieds/Job Market/Job Categories/Education", "Top/Features/Travel/Guides/Destinations/North America", "Top/Classifieds/Job Market/Job Categories/Banking, Finance and Insurance", "Top/Features/Travel/Guides/Destinations/North America/United States/New York", "Top/Features/Travel/Guides/Destinations/North America/United States", "Top/News/Education"], "title"=>"OUR TOWNS; As Pipeline to Wall Street Narrows, Princeton Students Adjust Sights", "nytd_title"=>"As Pipeline to Wall Street Narrows, Princeton Students Adjust Sights", "byline"=>"By PETER APPLEBOME", "body"=>"Princeton, N.J. There must be a screenplay in the fabulous Schoppe twins, Christine and Jennifer, Princeton University juniors from Houston. They had the same G.P.A. and SATs in high school, where they became Gold Award Girl Scouts , sort of the female version of Eagle Scouts. They live together and take all the same courses, wear identical necklac", "material_type_facet"=>["News"], "url"=>"http://www.nytimes.com/2009/02/08/nyregion/08towns.html", "publication_month"=>"02", "small_image_height"=>"75", "date"=>"20090208", "column_facet"=>"Our Towns", "small_image"=>"Y", "publication_year"=>"2009", "nytd_section_facet"=>["New York and Region", "Education"], "source_facet"=>"The New York Times", "org_facet"=>["PRINCETON UNIVERSITY"], "desk_facet"=>"New York Region", "publication_day"=>"08", "small_image_url"=>"http://graphics8.nytimes.com/images/2009/02/08/nyregion/08towns.751.jpg", "des_facet"=>["EDUCATION AND SCHOOLS", "BANKS AND BANKING"], "day_of_week_facet"=>"Sunday"}
|
6
|
+
|
7
|
+
class TestNytimes::TestArticles::TestArticle < Test::Unit::TestCase
|
8
|
+
include Nytimes::Articles
|
9
|
+
|
10
|
+
def setup
|
11
|
+
init_test_key
|
12
|
+
Article.stubs(:parse_reply)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "Article.search" do
|
16
|
+
should "accept a String for the first argument that is passed through to the query in the API" do
|
17
|
+
Article.expects(:invoke).with(has_entry("query", "FOO BAR"))
|
18
|
+
Article.search "FOO BAR"
|
19
|
+
end
|
20
|
+
|
21
|
+
should "accept a Hash for the first argument" do
|
22
|
+
Article.expects(:invoke).with(has_entry("query", "FOO BAR"))
|
23
|
+
Article.search :query => 'FOO BAR', :page => 2
|
24
|
+
end
|
25
|
+
|
26
|
+
context "date ranges" do
|
27
|
+
should "pass a string argument to begin_date straight through" do
|
28
|
+
date = "20081212"
|
29
|
+
Article.expects(:invoke).with(has_entry("begin_date", date))
|
30
|
+
Article.search :begin_date => date
|
31
|
+
end
|
32
|
+
|
33
|
+
should "convert begin_date from a Date or Time to YYYYMMDD format" do
|
34
|
+
time = Time.now
|
35
|
+
Article.expects(:invoke).with(has_entry("begin_date", time.strftime("%Y%m%d")))
|
36
|
+
Article.search :begin_date => time
|
37
|
+
end
|
38
|
+
|
39
|
+
should "pass a string argument to end_date straight through" do
|
40
|
+
date = "20081212"
|
41
|
+
Article.expects(:invoke).with(has_entry("end_date", date))
|
42
|
+
Article.search :end_date => date
|
43
|
+
end
|
44
|
+
|
45
|
+
should "convert end_date from a Date or Time to YYYYMMDD format" do
|
46
|
+
time = Time.now
|
47
|
+
Article.expects(:invoke).with(has_entry("end_date", time.strftime("%Y%m%d")))
|
48
|
+
Article.search :end_date => time
|
49
|
+
end
|
50
|
+
|
51
|
+
should "raise an ArgumentError if the begin_date is NOT a string and does not respond_to strftime" do
|
52
|
+
assert_raise(ArgumentError) { Article.search :begin_date => 23 }
|
53
|
+
end
|
54
|
+
|
55
|
+
should "raise an ArgumentError if the end_date is NOT a string and does not respond_to strftime" do
|
56
|
+
assert_raise(ArgumentError) { Article.search :end_date => 23 }
|
57
|
+
end
|
58
|
+
|
59
|
+
context ":before" do
|
60
|
+
should "send the :before value through as a end_date" do
|
61
|
+
t = Time.now
|
62
|
+
Article.expects(:invoke).with(has_entry('end_date', t.strftime("%Y%m%d")))
|
63
|
+
Article.search :before => t
|
64
|
+
end
|
65
|
+
|
66
|
+
should "not send through :before as an argument to the API" do
|
67
|
+
t = Time.now
|
68
|
+
Article.expects(:invoke).with(Not(has_key('before')))
|
69
|
+
Article.search :before => t
|
70
|
+
end
|
71
|
+
|
72
|
+
should "raise an ArgumentError if the before_date is NOT a string and does not respond_to strftime" do
|
73
|
+
assert_raise(ArgumentError) { Article.search :before => 23 }
|
74
|
+
end
|
75
|
+
|
76
|
+
should "add a begin_date in 1980 if no :since or :begin_date argument is provided" do
|
77
|
+
Article.expects(:invoke).with(has_entry('begin_date', Article::EARLIEST_BEGIN_DATE))
|
78
|
+
Article.search :before => Time.now
|
79
|
+
end
|
80
|
+
|
81
|
+
should "not automatically add a begin_date is there is a :since argument" do
|
82
|
+
since = Time.now - 12000
|
83
|
+
Article.expects(:invoke).with(has_entry('begin_date', since.strftime("%Y%m%d")))
|
84
|
+
Article.search :before => Time.now, :since => since
|
85
|
+
end
|
86
|
+
|
87
|
+
should "not automatically add a begin_date if there is a :begin_date argument already" do
|
88
|
+
since = Time.now - 12000
|
89
|
+
Article.expects(:invoke).with(has_entry('begin_date', since.strftime("%Y%m%d")))
|
90
|
+
Article.search :before => Time.now, :begin_date => since
|
91
|
+
end
|
92
|
+
|
93
|
+
should "raise an ArgumentError if there is also an :end_date argument" do
|
94
|
+
assert_raise(ArgumentError) { Article.search :before => Time.now, :end_date => Time.now }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context ":since" do
|
99
|
+
should "send the :since value through as a begin_date" do
|
100
|
+
t = Time.now - 1200
|
101
|
+
Article.expects(:invoke).with(has_entry('begin_date', t.strftime("%Y%m%d")))
|
102
|
+
Article.search :since => t
|
103
|
+
end
|
104
|
+
|
105
|
+
should "not send through :since as an argument to the API" do
|
106
|
+
t = Time.now
|
107
|
+
Article.expects(:invoke).with(Not(has_key('since')))
|
108
|
+
Article.search :since => t
|
109
|
+
end
|
110
|
+
|
111
|
+
should "raise an ArgumentError if the before_date is NOT a string and does not respond_to strftime" do
|
112
|
+
assert_raise(ArgumentError) { Article.search :since => 23 }
|
113
|
+
end
|
114
|
+
|
115
|
+
# This is to fix an error where the begin and end date are the same
|
116
|
+
should "add a end_date of tomorrow if no :before or :end_date argument is provided" do
|
117
|
+
Article.expects(:invoke).with(has_entry('end_date', (Date.today + 1).strftime("%Y%m%d")))
|
118
|
+
Article.search :since => Date.today
|
119
|
+
end
|
120
|
+
|
121
|
+
should "not automatically add a end_date is there is a :before argument" do
|
122
|
+
since = '19990101'
|
123
|
+
Article.expects(:invoke).with(has_entry('end_date', '20030101'))
|
124
|
+
Article.search :before => '20030101', :since => since
|
125
|
+
end
|
126
|
+
|
127
|
+
should "not automatically add a end_date if there is a :end_date argument already" do
|
128
|
+
since = '19990101'
|
129
|
+
Article.expects(:invoke).with(has_entry('end_date', '20030101'))
|
130
|
+
Article.search :end_date => '20030101', :since => since
|
131
|
+
end
|
132
|
+
|
133
|
+
should "raise an ArgumentError if there is also an :begin_date argument" do
|
134
|
+
assert_raise(ArgumentError) { Article.search :since => Time.now, :begin_date => Time.now }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "facets" do
|
140
|
+
should "accept a single string" do
|
141
|
+
Article.expects(:invoke).with(has_entry("facets", Facet::DATE))
|
142
|
+
Article.search "FOO BAR", :facets => Facet::DATE
|
143
|
+
end
|
144
|
+
|
145
|
+
should "accept an array of strings" do
|
146
|
+
Article.expects(:invoke).with(has_entry("facets", [Facet::DATE, Facet::GEO].join(',')))
|
147
|
+
Article.search "FOO BAR", :facets => [Facet::DATE, Facet::GEO]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "only_facets" do
|
152
|
+
should "accept a String" do
|
153
|
+
Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA]"))
|
154
|
+
Article.search :only_facets => "#{Facet::GEO}:[CALIFORNIA]"
|
155
|
+
end
|
156
|
+
|
157
|
+
should "accept a single hash value Facet string to a term" do
|
158
|
+
Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA]"))
|
159
|
+
Article.search :only_facets => {Facet::GEO => 'CALIFORNIA'}
|
160
|
+
end
|
161
|
+
|
162
|
+
should "accept an Facet string hashed to an array terms" do
|
163
|
+
Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA] #{Facet::GEO}:[GREAT BRITAIN]"))
|
164
|
+
Article.search :only_facets => {Facet::GEO => ['CALIFORNIA', 'GREAT BRITAIN']}
|
165
|
+
end
|
166
|
+
|
167
|
+
should "accept a single Facet object" do
|
168
|
+
f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
|
169
|
+
Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA]"))
|
170
|
+
Article.search :only_facets => f
|
171
|
+
end
|
172
|
+
|
173
|
+
should "accept an array of Facet objects" do
|
174
|
+
f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
|
175
|
+
f2 = Facet.new(Facet::NYTD_ORGANIZATION, 'University Of California', 12)
|
176
|
+
|
177
|
+
Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA] #{Facet::NYTD_ORGANIZATION}:[University Of California]"))
|
178
|
+
Article.search :only_facets => [f, f2]
|
179
|
+
end
|
180
|
+
|
181
|
+
should "merge multiple Facets objects in the array of the same type into one array" do
|
182
|
+
f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
|
183
|
+
f2 = Facet.new(Facet::GEO, 'IOWA', 12)
|
184
|
+
|
185
|
+
Article.expects(:invoke).with(has_entry("query", "#{Facet::GEO}:[CALIFORNIA] #{Facet::GEO}:[IOWA]"))
|
186
|
+
Article.search :only_facets => [f, f2]
|
187
|
+
end
|
188
|
+
|
189
|
+
should "not stomp on an existing query string" do
|
190
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream #{Facet::GEO}:[CALIFORNIA]"))
|
191
|
+
Article.search "ice cream", :only_facets => {Facet::GEO => "CALIFORNIA"}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "except_facets" do
|
196
|
+
should "accept a String" do
|
197
|
+
Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA]"))
|
198
|
+
Article.search :except_facets => "-#{Facet::GEO}:[CALIFORNIA]"
|
199
|
+
end
|
200
|
+
|
201
|
+
should "accept a single hash value Facet string to a term" do
|
202
|
+
Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA]"))
|
203
|
+
Article.search :except_facets => {Facet::GEO => 'CALIFORNIA'}
|
204
|
+
end
|
205
|
+
|
206
|
+
should "accept an Facet string hashed to an array terms" do
|
207
|
+
Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA] -#{Facet::GEO}:[GREAT BRITAIN]"))
|
208
|
+
Article.search :except_facets => {Facet::GEO => ['CALIFORNIA', 'GREAT BRITAIN']}
|
209
|
+
end
|
210
|
+
|
211
|
+
should "accept a single Facet object" do
|
212
|
+
f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
|
213
|
+
Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA]"))
|
214
|
+
Article.search :except_facets => f
|
215
|
+
end
|
216
|
+
|
217
|
+
should "accept an array of Facet objects" do
|
218
|
+
f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
|
219
|
+
f2 = Facet.new(Facet::NYTD_ORGANIZATION, 'University Of California', 12)
|
220
|
+
|
221
|
+
Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA] -#{Facet::NYTD_ORGANIZATION}:[University Of California]"))
|
222
|
+
Article.search :except_facets => [f, f2]
|
223
|
+
end
|
224
|
+
|
225
|
+
should "merge multiple Facets objects in the array of the same type into one array" do
|
226
|
+
f = Facet.new(Facet::GEO, 'CALIFORNIA', 2394)
|
227
|
+
f2 = Facet.new(Facet::GEO, 'IOWA', 12)
|
228
|
+
|
229
|
+
Article.expects(:invoke).with(has_entry("query", "-#{Facet::GEO}:[CALIFORNIA] -#{Facet::GEO}:[IOWA]"))
|
230
|
+
Article.search :except_facets => [f, f2]
|
231
|
+
end
|
232
|
+
|
233
|
+
should "not stomp on an existing query string" do
|
234
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream -#{Facet::GEO}:[CALIFORNIA]"))
|
235
|
+
Article.search "ice cream", :except_facets => {Facet::GEO => "CALIFORNIA"}
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context ":fee" do
|
240
|
+
should "send through as fee:Y if set to true" do
|
241
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream fee:Y"))
|
242
|
+
Article.search "ice cream", :fee => true
|
243
|
+
end
|
244
|
+
|
245
|
+
should "send through as -fee:Y if set to false" do
|
246
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream -fee:Y"))
|
247
|
+
Article.search "ice cream", :fee => false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context ":fields" do
|
252
|
+
context "when not specified at all" do
|
253
|
+
should "pass all fields in a comma-delimited list" do
|
254
|
+
Article.expects(:invoke).with(has_entry('fields', Article::ALL_FIELDS.join(',')))
|
255
|
+
Article.search "FOO BAR", :fields => :all
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context "for the :all argument" do
|
260
|
+
should "pass all fields in a comma-delimited list" do
|
261
|
+
Article.expects(:invoke).with(has_entry('fields', Article::ALL_FIELDS.join(',')))
|
262
|
+
Article.search "FOO BAR", :fields => :all
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context "for the :basic argument" do
|
267
|
+
should "not send a fields argument to the api" do
|
268
|
+
Article.expects(:invoke).with(Not(has_key('fields')))
|
269
|
+
Article.search "FOO BAR", :fields => :basic
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context "for the :none argument" do
|
274
|
+
should "request a blank space for the fields argument" do
|
275
|
+
Article.expects(:invoke).with(has_entry('fields', ' '))
|
276
|
+
Article.search "FOO BAR", :fields => :none
|
277
|
+
end
|
278
|
+
|
279
|
+
should "request the standard :facets if no :facets have been explicitly provided" do
|
280
|
+
Article.expects(:invoke).with(has_entry('facets', Facet::DEFAULT_RETURN_FACETS.join(',')))
|
281
|
+
Article.search "FOO BAR", :fields => :none
|
282
|
+
end
|
283
|
+
|
284
|
+
should "request the given :facets field if provided" do
|
285
|
+
Article.expects(:invoke).with(has_entry('facets', "#{Facet::GEO}"))
|
286
|
+
Article.search "FOO BAR", :fields => :none, :facets => Facet::GEO
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
context ":thumbnail" do
|
291
|
+
should "accept the symbol version of the argument" do
|
292
|
+
Article.expects(:invoke).with(has_entry('fields', Article::IMAGE_FIELDS.join(',')))
|
293
|
+
Article.search "FOO BAR", :fields => :thumbnail
|
294
|
+
end
|
295
|
+
|
296
|
+
should "accept the string version of the argument" do
|
297
|
+
Article.expects(:invoke).with(has_entry('fields', Article::IMAGE_FIELDS.join(',')))
|
298
|
+
Article.search "FOO BAR", :fields => 'thumbnail'
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context ":multimedia" do
|
303
|
+
should "be implemented"
|
304
|
+
end
|
305
|
+
|
306
|
+
should "accept a single string as an argument" do
|
307
|
+
Article.expects(:invoke).with(has_entry('fields', 'body'))
|
308
|
+
Article.search "FOO BAR", :fields => 'body'
|
309
|
+
end
|
310
|
+
|
311
|
+
should "accept a single symbol as an argument" do
|
312
|
+
Article.expects(:invoke).with(has_entry('fields', 'body'))
|
313
|
+
Article.search "FOO BAR", :fields => :body
|
314
|
+
end
|
315
|
+
|
316
|
+
should "accept an array of strings and symbols" do
|
317
|
+
Article.expects(:invoke).with(has_entry('fields', 'abstract,body'))
|
318
|
+
Article.search "FOO BAR", :fields => [:abstract, 'body']
|
319
|
+
end
|
320
|
+
|
321
|
+
should "raise an ArgumentError otherwise" do
|
322
|
+
assert_raise(ArgumentError) { Article.search :fields => 12 }
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context ":has_multimedia" do
|
327
|
+
should "send through as related_multimedia:Y if set to true" do
|
328
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream related_multimedia:Y"))
|
329
|
+
Article.search "ice cream", :has_multimedia => true
|
330
|
+
end
|
331
|
+
|
332
|
+
should "send through as -related_multimedia:Y if set to false" do
|
333
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream -related_multimedia:Y"))
|
334
|
+
Article.search "ice cream", :has_multimedia => false
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context ":has_thumbnail" do
|
339
|
+
should "send through as small_image:Y if set to true" do
|
340
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream small_image:Y"))
|
341
|
+
Article.search "ice cream", :has_thumbnail => true
|
342
|
+
end
|
343
|
+
|
344
|
+
should "send through as -small_image:Y if set to false" do
|
345
|
+
Article.expects(:invoke).with(has_entry("query", "ice cream -small_image:Y"))
|
346
|
+
Article.search "ice cream", :has_thumbnail => false
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
context ":offset" do
|
351
|
+
should "pass through an explicit offset parameter if specified" do
|
352
|
+
Article.expects(:invoke).with(has_entry("offset", 10))
|
353
|
+
Article.search :offset => 10
|
354
|
+
end
|
355
|
+
|
356
|
+
should "raise an ArgumentError if the offset is not an Integer" do
|
357
|
+
assert_raise(ArgumentError) { Article.search :offset => 'apple' }
|
358
|
+
end
|
359
|
+
|
360
|
+
should "pass through an offset of page - 1 if :page is used instead" do
|
361
|
+
Article.expects(:invoke).with(has_entry("offset", 2))
|
362
|
+
Article.search :page => 3
|
363
|
+
end
|
364
|
+
|
365
|
+
should "not pass through a page parameter to the API" do
|
366
|
+
Article.expects(:invoke).with(Not(has_key("page")))
|
367
|
+
Article.search :page => 3
|
368
|
+
end
|
369
|
+
|
370
|
+
should "raise an ArgumentError if the page is not an Integer" do
|
371
|
+
assert_raise(ArgumentError) { Article.search :page => 'orange' }
|
372
|
+
end
|
373
|
+
|
374
|
+
should "raise an ArgumentError if the page is less than 1" do
|
375
|
+
assert_raise(ArgumentError) { Article.search :page => 0 }
|
376
|
+
end
|
377
|
+
|
378
|
+
should "use the :offset argument if both an :offset and :page are provided" do
|
379
|
+
Article.expects(:invoke).with(has_entry("offset", 2))
|
380
|
+
Article.search :offset => 2, :page => 203
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
context "rank" do
|
385
|
+
%w(newest oldest closest).each do |rank|
|
386
|
+
should "accept #{rank} as the argument to rank" do
|
387
|
+
Article.expects(:invoke).with(has_entry("rank", rank))
|
388
|
+
Article.search :rank => rank.to_sym
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
should "raise an ArgumentError if rank is something else" do
|
393
|
+
assert_raise(ArgumentError) { Article.search :rank => :clockwise }
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
Article::TEXT_FIELDS.each do |tf|
|
398
|
+
context ":#{tf} parameter" do
|
399
|
+
should "prefix each non-quoted term with the #{tf}: field identifier in the query to the API" do
|
400
|
+
Article.expects(:invoke).with(has_entry("query", "#{tf}:ice #{tf}:cream"))
|
401
|
+
Article.search tf.to_sym => 'ice cream'
|
402
|
+
end
|
403
|
+
|
404
|
+
should "prefix -terms (excluded terms) with -#{tf}:" do
|
405
|
+
Article.expects(:invoke).with(has_entry("query", "#{tf}:ice -#{tf}:cream"))
|
406
|
+
Article.search tf.to_sym => 'ice -cream'
|
407
|
+
end
|
408
|
+
|
409
|
+
should "put quoted terms behind the field spec" do
|
410
|
+
Article.expects(:invoke).with(has_entry("query", "#{tf}:\"ice cream\" #{tf}:cone"))
|
411
|
+
Article.search tf.to_sym => '"ice cream" cone'
|
412
|
+
end
|
413
|
+
|
414
|
+
should "handle complicated combinations of expressions" do
|
415
|
+
Article.expects(:invoke).with(has_entry("query", "#{tf}:\"ice cream\" -#{tf}:cone #{tf}:\"waffle\""))
|
416
|
+
Article.search tf.to_sym => '"ice cream" -cone "waffle"'
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
# context "query parameters" do
|
422
|
+
# context "abstract" do
|
423
|
+
# should "be prefixed with the abstract: field identifier in the query"
|
424
|
+
# should "cast the argument to a string (will figure out processing later)"
|
425
|
+
# end
|
426
|
+
#
|
427
|
+
# context "author" do
|
428
|
+
# should "be prefixed with the author: field identifier in the query"
|
429
|
+
# should "cast the argument to a string (will figure out processing later)"
|
430
|
+
# end
|
431
|
+
#
|
432
|
+
# context "body" do
|
433
|
+
# should "be prefixed with the body: field identifier in the query"
|
434
|
+
# should "cast the argument to a string (will figure out processing later)"
|
435
|
+
# end
|
436
|
+
#
|
437
|
+
# context "byline" do
|
438
|
+
# should "be prefixed with the body: field identifier in the query"
|
439
|
+
# should "cast the argument to a string (will figure out processing later)"
|
440
|
+
# end
|
441
|
+
# end
|
442
|
+
end
|
443
|
+
|
444
|
+
context "Article.init_from_api" do
|
445
|
+
setup do
|
446
|
+
@article = Article.init_from_api(ARTICLE_API_HASH2)
|
447
|
+
end
|
448
|
+
|
449
|
+
Article::TEXT_FIELDS.each do |tf|
|
450
|
+
context "@#{tf}" do
|
451
|
+
should "read the value from the hash input" do
|
452
|
+
hash = {}
|
453
|
+
hash[tf] = "TEST TEXT"
|
454
|
+
article = Article.init_from_api(hash)
|
455
|
+
assert_equal "TEST TEXT", article.send(tf)
|
456
|
+
end
|
457
|
+
|
458
|
+
should "properly translate HTML entities back into characters" do
|
459
|
+
article = Article.init_from_api(tf => '“Money for Nothing”')
|
460
|
+
assert_equal "“Money for Nothing”", article.send(tf), article.inspect
|
461
|
+
end
|
462
|
+
|
463
|
+
should "only provide read-only access to the field" do
|
464
|
+
article = Article.init_from_api(tf => "TEST TEXT")
|
465
|
+
assert !article.respond_to?("#{tf}=")
|
466
|
+
end
|
467
|
+
|
468
|
+
should "return nil if the value is not provided in the hash" do
|
469
|
+
article = Article.init_from_api({"foo" => "bar"})
|
470
|
+
assert_nil article.send(tf)
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
Article::NUMERIC_FIELDS.each do |tf|
|
476
|
+
context "@#{tf}" do
|
477
|
+
should "read and coerce the string value from the hash input" do
|
478
|
+
article = Article.init_from_api(tf => "23")
|
479
|
+
assert_equal 23, article.send(tf)
|
480
|
+
end
|
481
|
+
|
482
|
+
should "only provide read-only access to the field" do
|
483
|
+
article = Article.init_from_api(tf => "23")
|
484
|
+
assert !article.respond_to?("#{tf}=")
|
485
|
+
end
|
486
|
+
|
487
|
+
should "return nil if the value is not provided in the hash" do
|
488
|
+
article = Article.init_from_api({"foo" => "bar"})
|
489
|
+
assert_nil article.send(tf)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
# all the rest
|
495
|
+
context "@fee" do
|
496
|
+
setup do
|
497
|
+
@article = Article.init_from_api(ARTICLE_API_HASH)
|
498
|
+
end
|
499
|
+
|
500
|
+
should "be true if returned as true from the API" do
|
501
|
+
article = Article.init_from_api('fee' => true)
|
502
|
+
assert_equal true, article.fee?
|
503
|
+
assert_equal false, article.free?
|
504
|
+
end
|
505
|
+
|
506
|
+
should "be true if returned as Y from the API" do
|
507
|
+
article = Article.init_from_api('fee' => 'Y')
|
508
|
+
assert_equal true, article.fee?
|
509
|
+
assert_equal false, article.free?
|
510
|
+
end
|
511
|
+
|
512
|
+
should "default to false if not specified in the hash" do
|
513
|
+
assert_equal false, @article.fee?
|
514
|
+
assert_equal true, @article.free?
|
515
|
+
end
|
516
|
+
|
517
|
+
should "default to false if returned as N from the API" do
|
518
|
+
article = Article.init_from_api('fee' => 'N')
|
519
|
+
assert_equal false, article.fee?
|
520
|
+
assert_equal true, article.free?
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
context "@url" do
|
525
|
+
setup do
|
526
|
+
@article = Article.init_from_api(ARTICLE_API_HASH)
|
527
|
+
end
|
528
|
+
|
529
|
+
should "read the value from the hash" do
|
530
|
+
assert_equal ARTICLE_API_HASH['url'], @article.url
|
531
|
+
end
|
532
|
+
|
533
|
+
should "return a String" do
|
534
|
+
assert_kind_of(String, @article.url)
|
535
|
+
end
|
536
|
+
|
537
|
+
should "only provide read-only access to the field" do
|
538
|
+
assert !@article.respond_to?("url=")
|
539
|
+
end
|
540
|
+
|
541
|
+
should "return nil if the value is not provided in the hash" do
|
542
|
+
article = Article.init_from_api({"foo" => "bar"})
|
543
|
+
assert_nil article.url
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
context "@page" do
|
548
|
+
should "read the value from the page_facet field" do
|
549
|
+
assert_equal ARTICLE_API_HASH2['page_facet'].to_i, @article.page
|
550
|
+
end
|
551
|
+
|
552
|
+
should "only provide read-only access to the field" do
|
553
|
+
article = Article.new
|
554
|
+
assert !article.respond_to?("page=")
|
555
|
+
end
|
556
|
+
|
557
|
+
should "return nil if the value is not provided in the hash" do
|
558
|
+
article = Article.init_from_api({"foo" => "bar"})
|
559
|
+
assert_nil article.page
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context "@thumbnail" do
|
564
|
+
should "assign nil to thumbnail otherwise" do
|
565
|
+
article = Article.init_from_api({"foo" => "bar"})
|
566
|
+
assert_nil article.thumbnail
|
567
|
+
end
|
568
|
+
|
569
|
+
should "create a thumbnail object if a small_image_url is part of the return hash" do
|
570
|
+
article = Article.init_from_api(ARTICLE_API_HASH2)
|
571
|
+
thumbnail = article.thumbnail
|
572
|
+
assert_not_nil thumbnail
|
573
|
+
assert_kind_of Thumbnail, thumbnail
|
574
|
+
assert_equal ARTICLE_API_HASH2['small_image_url'], thumbnail.url
|
575
|
+
assert_equal ARTICLE_API_HASH2['small_image_width'].to_i, thumbnail.width
|
576
|
+
assert_equal ARTICLE_API_HASH2['small_image_height'].to_i, thumbnail.height
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
context "array facets" do
|
581
|
+
should "have some tests for array facets"
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|