nytimes-movies 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/README.rdoc +55 -0
- data/Rakefile +54 -0
- data/VERSION.yml +4 -0
- data/lib/nytimes-movies.rb +4 -0
- data/lib/nytimes/movies.rb +6 -0
- data/lib/nytimes/movies/base.rb +81 -0
- data/lib/nytimes/movies/critic.rb +73 -0
- data/lib/nytimes/movies/link.rb +24 -0
- data/lib/nytimes/movies/multimedia_link.rb +30 -0
- data/lib/nytimes/movies/result_set.rb +46 -0
- data/lib/nytimes/movies/review.rb +171 -0
- data/test/nytimes/movies/test_critic.rb +105 -0
- data/test/nytimes/movies/test_link.rb +29 -0
- data/test/nytimes/movies/test_multimedia_link.rb +36 -0
- data/test/nytimes/movies/test_result_set.rb +48 -0
- data/test/nytimes/movies/test_review.rb +198 -0
- data/test/test_helper.rb +46 -0
- metadata +82 -0
data/History.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
= nytimes-movies
|
2
|
+
|
3
|
+
== DESCRIPTION:
|
4
|
+
|
5
|
+
A gem for accessing the NY Times' Movie Reviews API (http://developer.nytimes.com/)
|
6
|
+
|
7
|
+
== FEATURES/PROBLEMS:
|
8
|
+
|
9
|
+
* Still under development. Please email jharris@nytimes.com with bugs. Some methods and classes may
|
10
|
+
change until we reach a stable 1.0 version.
|
11
|
+
|
12
|
+
== SYNOPSIS:
|
13
|
+
|
14
|
+
include Nytimes::Movies
|
15
|
+
Base.api_key = 'YOUR API KEY'
|
16
|
+
reviews = Review.find(:dvd => true, :reviewer => 'A. O. Scott')
|
17
|
+
ao = Critic.find_by_name('A. O. Scott')
|
18
|
+
|
19
|
+
== REQUIREMENTS:
|
20
|
+
|
21
|
+
* The JSON gem
|
22
|
+
* An API key for the NY Times' Movies API
|
23
|
+
|
24
|
+
== INSTALL:
|
25
|
+
|
26
|
+
* sudo gem install harrisj-nytimes-movies --source=http://gems.github.com/
|
27
|
+
|
28
|
+
== LICENSE:
|
29
|
+
|
30
|
+
(The MIT License)
|
31
|
+
|
32
|
+
Copyright (c) 2008 Jacob Harris
|
33
|
+
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
35
|
+
a copy of this software and associated documentation files (the
|
36
|
+
'Software'), to deal in the Software without restriction, including
|
37
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
38
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
39
|
+
permit persons to whom the Software is furnished to do so, subject to
|
40
|
+
the following conditions:
|
41
|
+
|
42
|
+
The above copyright notice and this permission notice shall be
|
43
|
+
included in all copies or substantial portions of the Software.
|
44
|
+
|
45
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
46
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
47
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
48
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
49
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
50
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
51
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
52
|
+
|
53
|
+
THIS LICENSE APPLIES ONLY TO THE NYTIMES-MOVIES GEM AND DOES NOT ALTER
|
54
|
+
OR ABROGATE THE TERMS OF SERVICE FOR THE MOVIE REVIEWS API PROVIDED BY
|
55
|
+
THE NEW YORK TIMES.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "nytimes-movies"
|
7
|
+
s.summary = "A gem for talking to the New York Times Movie Reviews API"
|
8
|
+
s.email = "jharris@nytimes.com"
|
9
|
+
s.homepage = "http://github.com/harrisj/nytimes-moves"
|
10
|
+
s.description = "A gem for talking to the New York Times Movie Reviews API"
|
11
|
+
s.authors = ["Jacob Harris"]
|
12
|
+
s.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*"]
|
13
|
+
s.add_dependency 'json'
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
require 'rake/rdoctask'
|
21
|
+
Rake::RDocTask.new do |rdoc|
|
22
|
+
rdoc.rdoc_dir = 'rdoc'
|
23
|
+
rdoc.title = 'nytimes-articles'
|
24
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
25
|
+
rdoc.rdoc_files.include('README*')
|
26
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib' << 'test'
|
32
|
+
t.pattern = 'test/**/test_*.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
require 'rcov/rcovtask'
|
38
|
+
Rcov::RcovTask.new do |t|
|
39
|
+
t.libs << 'test'
|
40
|
+
t.test_files = FileList['test/**/test_*.rb']
|
41
|
+
t.verbose = true
|
42
|
+
end
|
43
|
+
rescue LoadError
|
44
|
+
puts "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
require 'cucumber/rake/task'
|
49
|
+
Cucumber::Rake::Task.new(:features)
|
50
|
+
rescue LoadError
|
51
|
+
puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
52
|
+
end
|
53
|
+
|
54
|
+
task :default => :test
|
data/VERSION.yml
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Nytimes
|
5
|
+
module Movies
|
6
|
+
class Base
|
7
|
+
API_SERVER = 'api.nytimes.com'
|
8
|
+
API_VERSION = 'v2'
|
9
|
+
API_NAME = 'movies'
|
10
|
+
API_BASE = "/svc/#{API_NAME}/#{API_VERSION}"
|
11
|
+
|
12
|
+
@@api_key = nil
|
13
|
+
@@copyright = nil
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
##
|
18
|
+
# The copyright footer to be placed at the bottom of any data from the New York Times. Note this is only set after an API call.
|
19
|
+
def copyright
|
20
|
+
@@copyright
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Set the API key used for operations. This needs to be called before any requests against the API. To obtain an API key, go to http://developer.nytimes.com/
|
25
|
+
def api_key=(key)
|
26
|
+
@@api_key = key
|
27
|
+
end
|
28
|
+
|
29
|
+
def api_key
|
30
|
+
@@api_key
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Builds a request URI to call the API server
|
35
|
+
def build_request_url(path, params)
|
36
|
+
URI::HTTP.build :host => API_SERVER,
|
37
|
+
:path => "#{API_BASE}/#{path}",
|
38
|
+
:query => params.map {|k,v| "#{k}=#{v}"}.join('&')
|
39
|
+
end
|
40
|
+
|
41
|
+
def invoke(path, params={})
|
42
|
+
begin
|
43
|
+
if @@api_key.nil?
|
44
|
+
raise "You must initialize the API key before you run any API queries"
|
45
|
+
end
|
46
|
+
|
47
|
+
full_params = params.merge 'api-key' => @@api_key
|
48
|
+
|
49
|
+
uri = build_request_url(path, full_params)
|
50
|
+
|
51
|
+
# puts "Request [#{uri}]"
|
52
|
+
|
53
|
+
reply = uri.read
|
54
|
+
parsed_reply = JSON.parse reply
|
55
|
+
|
56
|
+
if parsed_reply.nil?
|
57
|
+
# FIXME
|
58
|
+
raise "Empty reply returned from API"
|
59
|
+
end
|
60
|
+
|
61
|
+
#case parsed_reply['status']
|
62
|
+
# FIXME
|
63
|
+
#end
|
64
|
+
|
65
|
+
@@copyright = parsed_reply['copyright']
|
66
|
+
|
67
|
+
parsed_reply
|
68
|
+
rescue OpenURI::HTTPError => e
|
69
|
+
if e.message =~ /^404/
|
70
|
+
return nil
|
71
|
+
end
|
72
|
+
|
73
|
+
raise "Error connecting to URL #{uri} #{e}"
|
74
|
+
#rescue JSON::ParserError => e
|
75
|
+
# raise RuntimeError, "Invalid JSON returned from CRNR:\n#{reply}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Nytimes
|
2
|
+
module Movies
|
3
|
+
class Critic < Base
|
4
|
+
attr_reader :display_name, :sort_name, :status, :bio, :seo_name, :photo
|
5
|
+
|
6
|
+
def initialize(params={})
|
7
|
+
params.each_pair do |k,v|
|
8
|
+
instance_variable_set("@#{k}", v)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Returns reviews made by the critic. Please refer to Review#find for other optional flags that can be applied.
|
14
|
+
def reviews(params={})
|
15
|
+
Review.find(params.merge :reviewer => seo_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Create a Critic object from a hash snippet returned from the API. You should never need to call this.
|
20
|
+
def self.create_from_api(params={})
|
21
|
+
self.new :display_name => params['display_name'],
|
22
|
+
:sort_name => params['sort_name'],
|
23
|
+
:status => params['status'],
|
24
|
+
:bio => params['bio'],
|
25
|
+
:seo_name => params['seo_name'],
|
26
|
+
:photo => MultimediaLink.create_from_api(params['multimedia'])
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Returns a list of critics that match a given type. The following types are supported:
|
31
|
+
# * <tt>:full_time</tt> - fulltime critics at the New York Times
|
32
|
+
# * <tt>:part_time</tt> - part-time critics at the New York Times
|
33
|
+
# * <tt>:all</tt> - all critics
|
34
|
+
def self.find_by_type(type)
|
35
|
+
key = case type
|
36
|
+
when :full_time
|
37
|
+
'full-time'
|
38
|
+
when :part_time
|
39
|
+
'part-time'
|
40
|
+
when :all
|
41
|
+
'all'
|
42
|
+
else
|
43
|
+
raise ArgumentError, "Type can be :full_time, :part_time, or :all"
|
44
|
+
end
|
45
|
+
|
46
|
+
reply = invoke("critics/#{key}")
|
47
|
+
results = reply['results']
|
48
|
+
results.map {|r| self.create_from_api(r)}
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Escapes a critic's name to the SEO variation used for name searches. This is automatically used by Critic#find_by_name, so you don't need to call it.
|
53
|
+
def self.escape_critic_name(name)
|
54
|
+
return name if name =~ /^[a-z\-]+$/ # might be escaped already
|
55
|
+
name.downcase.gsub(/[^a-z\s]/, ' ').gsub(/\s+/, '-')
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Find a critic record matching a given name. Both the English name (eg, 'A. O. Scott') and the SEO name ('a-o-scott') are valid keys, although the SEO name is
|
60
|
+
# suggested if you want to avoid ambiguity. Only exact matches are used (no last name searches). If a single matching record is found, returns a single Critic instance.
|
61
|
+
# In the admittedly rare chance of 2 distinct critics having the same name, returns an array. Returns nil if no matches are found.
|
62
|
+
def self.find_by_name(name)
|
63
|
+
name = escape_critic_name(name)
|
64
|
+
reply = invoke("critics/#{name}")
|
65
|
+
return nil if reply.nil? || reply['results'].nil?
|
66
|
+
results = reply['results']
|
67
|
+
critics = results.map {|r| self.create_from_api(r)}
|
68
|
+
return critics.first if critics.length == 1
|
69
|
+
critics
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Nytimes
|
2
|
+
module Movies
|
3
|
+
|
4
|
+
##
|
5
|
+
# Represents a link returned from the Reviews API. Each link has a URL, suggested text, and a link_type which gives you some idea of what the remote
|
6
|
+
# target of the link is. Possible link types include:
|
7
|
+
# FIXME
|
8
|
+
class Link
|
9
|
+
attr_reader :link_type, :url, :suggested_text
|
10
|
+
|
11
|
+
def initialize(hash={})
|
12
|
+
@url = hash[:url]
|
13
|
+
@link_type = hash[:link_type]
|
14
|
+
@suggested_text = hash[:suggested_text]
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Create a Link object from a hash snippet returned from the API. You should never need to call this.
|
19
|
+
def self.create_from_api(hash={})
|
20
|
+
Link.new :url => hash['url'], :link_type => hash['type'], :suggested_text => hash['suggested_link_text']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Nytimes
|
2
|
+
module Movies
|
3
|
+
|
4
|
+
##
|
5
|
+
# Represents a link to a multimedia asset from the New York Times. Invariably these are images or thumbnails, but it could also be expanded to
|
6
|
+
# include movies or sound. The following attributes are part of the link. FIXME
|
7
|
+
class MultimediaLink
|
8
|
+
attr_reader :media_type, :url, :height, :width, :credit
|
9
|
+
private_class_method :new
|
10
|
+
|
11
|
+
def initialize(hash={})
|
12
|
+
hash.each_pair do |k,v|
|
13
|
+
instance_variable_set("@#{k}", v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Create a MultimediaLink object from a hash snippet returned from the API. You should never need to call this.
|
19
|
+
def self.create_from_api(hash={})
|
20
|
+
return nil if hash.nil? || hash.empty?
|
21
|
+
|
22
|
+
if hash.key? 'resource'
|
23
|
+
hash = hash['resource']
|
24
|
+
end
|
25
|
+
|
26
|
+
new(:media_type => hash['type'], :url => hash['src'], :height => hash['height'], :width => hash['width'], :credit => hash['credit'])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Nytimes
|
2
|
+
module Movies
|
3
|
+
class ResultSet
|
4
|
+
attr_reader :num_results, :offset, :results
|
5
|
+
|
6
|
+
def initialize(params, json_reply, coerce_type)
|
7
|
+
@num_results = json_reply['num_results']
|
8
|
+
@params = params.dup
|
9
|
+
@offset = @params['offset'] || 0
|
10
|
+
@results = json_reply['results'].map {|r| coerce_type.create_from_api(r)}
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# The first display index of the result set. Note this is a human index, not a programmer index; it starts from 1
|
15
|
+
def first_index
|
16
|
+
offset + 1
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# The last display index of the result set.
|
21
|
+
def last_index
|
22
|
+
first_index + batch_size - 1
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# The page of this result set
|
27
|
+
def page_number
|
28
|
+
offset / batch_size + 1
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# The calculated number of pages returned from the movies API.
|
33
|
+
def num_pages
|
34
|
+
(num_results.to_f / batch_size).ceil
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# The number of results returned in a result set
|
39
|
+
def batch_size
|
40
|
+
Review::BATCH_SIZE
|
41
|
+
end
|
42
|
+
|
43
|
+
alias per_page batch_size
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module Nytimes
|
2
|
+
module Movies
|
3
|
+
class Review < Base
|
4
|
+
attr_reader :nyt_movie_id, :display_title, :sort_name, :mpaa_rating, :byline, :headline, :summary_short, :publication_date, :opening_date, :dvd_release_date, \
|
5
|
+
:date_updated, :seo_name, :critics_pick, :thousand_best, :thumbnail, :link, :related_links, :critic
|
6
|
+
|
7
|
+
alias :updated_on :date_updated
|
8
|
+
alias :critics_pick? :critics_pick
|
9
|
+
alias :thousand_best? :thousand_best
|
10
|
+
|
11
|
+
alias :critic_name :byline
|
12
|
+
alias :movie_title :display_title
|
13
|
+
alias :review_title :headline
|
14
|
+
|
15
|
+
BATCH_SIZE = 20
|
16
|
+
|
17
|
+
def initialize(params={})
|
18
|
+
params.each_pair do |k,v|
|
19
|
+
instance_variable_set("@#{k}", v)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class <<self
|
24
|
+
def parse_date(value)
|
25
|
+
return nil if value.nil?
|
26
|
+
Date.parse(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def rename_hash_key(hash, key, new_key)
|
30
|
+
hash[new_key] = hash.delete(key)
|
31
|
+
end
|
32
|
+
|
33
|
+
def coerce_key_boolean(hash, key)
|
34
|
+
hash[key] = hash[key] == 'Y'
|
35
|
+
end
|
36
|
+
|
37
|
+
def format_date_arg(arg)
|
38
|
+
return arg if arg.is_a? String
|
39
|
+
|
40
|
+
unless arg.respond_to? :strftime
|
41
|
+
raise "Date argument must respond to strftime"
|
42
|
+
end
|
43
|
+
|
44
|
+
arg.strftime "%Y-%m-%d"
|
45
|
+
end
|
46
|
+
|
47
|
+
def date_to_query_arg(date_or_range)
|
48
|
+
return nil if date_or_range.nil?
|
49
|
+
if date_or_range.respond_to?(:first) && date_or_range.respond_to?(:last)
|
50
|
+
"#{format_date_arg(date_or_range.first)};#{format_date_arg(date_or_range.last)}"
|
51
|
+
else
|
52
|
+
format_date_arg(date_or_range)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def boolean_to_query_arg(arg)
|
57
|
+
return nil if arg.nil?
|
58
|
+
arg ? 'Y' : 'N'
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_from_api(hash)
|
62
|
+
hash = hash.dup
|
63
|
+
|
64
|
+
hash['dvd_release_date'] = parse_date(hash['dvd_release_date'])
|
65
|
+
hash['opening_date'] = parse_date(hash['opening_date'])
|
66
|
+
hash['publication_date'] = parse_date(hash['publication_date'])
|
67
|
+
hash['date_updated'] = parse_date(hash['date_updated'])
|
68
|
+
|
69
|
+
coerce_key_boolean hash, 'critics_pick'
|
70
|
+
coerce_key_boolean hash, 'thousand_best'
|
71
|
+
|
72
|
+
multimedia = hash.delete('multimedia')
|
73
|
+
unless multimedia.nil?
|
74
|
+
hash['thumbnail'] = MultimediaLink.create_from_api(multimedia)
|
75
|
+
end
|
76
|
+
|
77
|
+
if hash.has_key? 'link'
|
78
|
+
hash['link'] = Link.create_from_api(hash['link'])
|
79
|
+
end
|
80
|
+
|
81
|
+
related = hash.delete('related_urls')
|
82
|
+
unless related.nil?
|
83
|
+
hash['related_links'] = related.map {|l| Link.create_from_api(l) }
|
84
|
+
end
|
85
|
+
|
86
|
+
rename_hash_key hash, 'seo-name', 'seo_name'
|
87
|
+
new hash
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Find Movie Reviews that match your parameters. Up to 3 of the following query parameters can be used in a request:
|
92
|
+
#
|
93
|
+
# * <tt>:text</tt> - search movie title and indexed terms for the movie. To limit to exact matches, enclose parts of the query in single quotes. Otherwise, it will include include partial matches ("head words") as well as exact matches (e.g., president will match president, presidents and presidential). Multiple terms will be ORed together
|
94
|
+
# * <tt>:critics_pick</tt> - set to true to only search movies that are designated critics picks. Should your tastes be more lowbrow, set to false to return movies that are specifically NOT critic's picks. To get all movies, don't send this parameter at all.
|
95
|
+
# * <tt>:thousand_best</tt> - search only movies on the Times List of "Thousand Best Movies Ever Made". Set to false if you want to explicitly search movies not on the list. Omit if you want all movies.
|
96
|
+
# * <tt>:dvd</tt> - if true, search only movies out on DVD. If false, movies not on DVD. Don't specify if you want all movies.
|
97
|
+
# * <tt>:reviewer</tt> - search reviews made by a specific critic. The full-name or the SEO name can be used to specify the critic.
|
98
|
+
# * <tt>:publication_date</tt> - when the review was published. This can be either a single Date or Time object or a range of Times (anything that responds to .first and .last). If you'd prefer, you can also pass in a "YYYY-MM-DD" string
|
99
|
+
# * <tt>:opening_date</tt> - when the movie opened in theaters in the NY Metro area. See +:publication_date+ for argument times.
|
100
|
+
#
|
101
|
+
# In addition, you can also specify the following order and pagination values:
|
102
|
+
# * <tt>:offset</tt> - a maximum of 20 result are returned for queries. To retrieve further pages, an offset from the first result can be specified. This must be a multiple of 20. So +20+ means results +21 - 40+
|
103
|
+
# * <tt>:page</tt> - as a convenience, page will calculate the offset for here. This is not an API parameter and is translated into an offset.
|
104
|
+
# * <tt>:order</tt> - ordering for the results. The following four sorting options are available: <tt>:title</tt> (<em>ascending</em>), <tt>:publication_date</tt>, <tt>:opening_date</tt>, <tt>:dvd_release_date</tt> (<em>most recent first</em>). If you do not specify a sort order, the results will be ordered by closest match.
|
105
|
+
# * <tt>:load_critics</tt> - if true, automatically load the Critic object for each record's reviewer. Note that this requires N additional API calls (where N is the unique number of critics in the result set)
|
106
|
+
def find(in_params={})
|
107
|
+
params = {}
|
108
|
+
|
109
|
+
if in_params[:text]
|
110
|
+
params['query'] = in_params[:text]
|
111
|
+
end
|
112
|
+
|
113
|
+
params['thousand-best'] = boolean_to_query_arg(in_params[:thousand_best])
|
114
|
+
params['critics-pick'] = boolean_to_query_arg(in_params[:critics_pick])
|
115
|
+
params['dvd'] = boolean_to_query_arg(in_params[:dvd])
|
116
|
+
|
117
|
+
params['publication-date'] = date_to_query_arg(in_params[:publication_date])
|
118
|
+
params['opening-date'] = date_to_query_arg(in_params[:opening_date])
|
119
|
+
|
120
|
+
if in_params.has_key? :reviewer
|
121
|
+
params['reviewer'] = Critic.escape_critic_name(in_params[:reviewer])
|
122
|
+
end
|
123
|
+
|
124
|
+
params['offset'] = in_params[:offset]
|
125
|
+
|
126
|
+
if in_params.has_key? :page
|
127
|
+
params['offset'] = BATCH_SIZE * (in_params[:page] - 1)
|
128
|
+
end
|
129
|
+
|
130
|
+
if in_params.has_key? :order
|
131
|
+
params['order'] = case in_params[:order]
|
132
|
+
when :title, :publication_date, :opening_date, :dvd_release_date
|
133
|
+
"by-#{in_params[:order].to_s.gsub('_', '-')}"
|
134
|
+
else
|
135
|
+
raise ArgumentError, "Order can only be :title, :publication_date, :opening_date, or :dvd_release_date"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
params.delete_if {|k, v| v.nil? }
|
140
|
+
|
141
|
+
reply = invoke 'reviews/search', params
|
142
|
+
out = ResultSet.new(params, reply, Review)
|
143
|
+
|
144
|
+
if in_params[:load_critics]
|
145
|
+
critics_hash = {}
|
146
|
+
out.results.each do |r|
|
147
|
+
name = r.byline
|
148
|
+
|
149
|
+
if critics_hash.has_key? name
|
150
|
+
critic = critics_hash[name]
|
151
|
+
else
|
152
|
+
critic = Critic.find_by_name(name)
|
153
|
+
critics_hash[name] = critic
|
154
|
+
end
|
155
|
+
|
156
|
+
r.instance_variable_set '@critic', critic
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
out
|
161
|
+
end
|
162
|
+
|
163
|
+
def find_by_type(params={})
|
164
|
+
end
|
165
|
+
|
166
|
+
def find_by_reviewer(params={})
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper.rb'
|
2
|
+
|
3
|
+
CRITIC_HASH = {"display_name" => "A. O. Scott",
|
4
|
+
"sort_name" => "A. O. Scott",
|
5
|
+
"status" => "full-time",
|
6
|
+
"bio" => "A. O. Scott joined The New York Times as a film critic in January 2000. Previously, Mr. Scott was a Sunday book reviewer for Newsday and a frequent contributor to Slate, The New York Review of Books, and many other publications. He has served on the editorial staffs of Lingua Franca and The New York Review of Books. He also edited \"A Bolt from the Blue and Other Essays,\" a collection by Mary McCarthy, which was published by The New York Review of Books in 2002. In addition to his film-reviewing duties, Mr. Scott often writes for the Times Magazine and the Book Review. He was born on July 10, 1966, in Northampton, Mass., and now lives in Brooklyn, N.Y. with his wife and two children.",
|
7
|
+
"seo_name" => "A-O-Scott",
|
8
|
+
"multimedia" => {"resource" => {"type" => "image","src" => "http:\/\/graphics8.nytimes.com\/images\/2007\/03\/02\/movies\/scott.163.jpg", "height" => nil,"width" => nil,"credit" => "Tony Cenicola\/<br>The New York Times"}}}
|
9
|
+
|
10
|
+
FIND_BY_NAME_REPLY = <<-EOF
|
11
|
+
{"status":"OK","copyright":"Copyright (c) 2008 The New York Times Company. All Rights Reserved.","results":[{"display_name":"A. O. Scott","sort_name":"A. O. Scott","status":"full-time","bio":"A. O. Scott joined The New York Times as a film critic in January 2000. Previously, Mr. Scott was a Sunday book reviewer for Newsday and a frequent contributor to Slate, The New York Review of Books, and many other publications. He has served on the editorial staffs of Lingua Franca and The New York Review of Books. He also edited \\\"A Bolt from the Blue and Other Essays,\\\" a collection by Mary McCarthy, which was published by The New York Review of Books in 2002. In addition to his film-reviewing duties, Mr. Scott often writes for the Times Magazine and the Book Review. He was born on July 10, 1966, in Northampton, Mass., and now lives in Brooklyn, N.Y. with his wife and two children.","seo_name":"A-O-Scott","multimedia":{"resource":{"type":"image","src":"http://graphics8.nytimes.com/images/2007/03/02/movies/scott.163.jpg","height":null,"width":null,"credit":"Tony Cenicola/<br>The New York Times"}}}]}
|
12
|
+
EOF
|
13
|
+
|
14
|
+
class TestNytimes::TestMovies::TestCritic < Test::Unit::TestCase
|
15
|
+
include Nytimes::Movies
|
16
|
+
|
17
|
+
# global setup
|
18
|
+
def setup
|
19
|
+
FakeWeb.clean_registry
|
20
|
+
FakeWeb.block_uri_pattern(Base::API_SERVER)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "Critic.create_from_api" do
|
24
|
+
setup do
|
25
|
+
@critic = Critic.create_from_api(CRITIC_HASH)
|
26
|
+
end
|
27
|
+
|
28
|
+
should "return an object of the Critic type" do
|
29
|
+
assert_kind_of(Critic, @critic)
|
30
|
+
end
|
31
|
+
|
32
|
+
%w(display_name sort_name status bio).each do |attr|
|
33
|
+
should "assign the value of the @#{attr} attribute from the '#{attr}' key in the hash" do
|
34
|
+
assert_equal(CRITIC_HASH[attr], @critic.send(attr))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "for the multimedia hash value" do
|
39
|
+
should "assign it to the @photo attribute" do
|
40
|
+
assert_not_nil @critic.photo
|
41
|
+
end
|
42
|
+
|
43
|
+
should "return a Nytimes::Movies::MultimediaLink instance" do
|
44
|
+
assert_kind_of(MultimediaLink, @critic.photo)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Critic.escape_critic_name" do
|
50
|
+
should "not escape a name that looks like it's escaped" do
|
51
|
+
assert_equal 'a-o-scott', Critic.escape_critic_name('a-o-scott')
|
52
|
+
end
|
53
|
+
|
54
|
+
should "downcase a name and replace spaces with hyphens" do
|
55
|
+
assert_equal 'mahnola-dargis', Critic.escape_critic_name('Mahnola Dargis')
|
56
|
+
end
|
57
|
+
|
58
|
+
should "not include punctuation characters in the escaped name" do
|
59
|
+
assert_equal 'a-o-scott', Critic.escape_critic_name('A.O. Scott')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "Critic.find_by_name" do
|
64
|
+
context "for a valid critic" do
|
65
|
+
setup do
|
66
|
+
FakeWeb.register_uri(api_url_for('critics/a-o-scott'), :string => FIND_BY_NAME_REPLY)
|
67
|
+
@critic = Critic.find_by_name('a-o-scott')
|
68
|
+
end
|
69
|
+
|
70
|
+
should "return a single Nytimes::Movies::Critic instance" do
|
71
|
+
assert_kind_of(Critic, @critic)
|
72
|
+
assert_equal 'A. O. Scott', @critic.display_name
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "for a nonexistant critic" do
|
77
|
+
setup do
|
78
|
+
FakeWeb.register_uri(api_url_for('critics/unknown-person'), :string => FIND_BY_NAME_REPLY, :status => [ 404, "Not Found" ])
|
79
|
+
@critic = Critic.find_by_name('Unknown Person')
|
80
|
+
end
|
81
|
+
|
82
|
+
should "return nil" do
|
83
|
+
assert_nil @critic
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "Critic.find_by_type" do
|
89
|
+
setup do
|
90
|
+
FakeWeb.register_uri(api_url_for('critics/full-time'), :string => FIND_BY_NAME_REPLY)
|
91
|
+
end
|
92
|
+
|
93
|
+
should "raise an ArgumentError if passed anything besides :full_time, :part_time, or :all" do
|
94
|
+
assert_raise(ArgumentError) { Critic.find_by_type(:foo) }
|
95
|
+
end
|
96
|
+
|
97
|
+
should "return an array of Nytimes::Movies::Critic instances" do
|
98
|
+
@critics = Critic.find_by_type(:full_time)
|
99
|
+
|
100
|
+
assert_kind_of(Array, @critics)
|
101
|
+
assert @critics.all? {|c| c.is_a? Critic}
|
102
|
+
assert_equal 'A. O. Scott', @critics.first.display_name
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper.rb'
|
2
|
+
|
3
|
+
class TestNytimes::TestMovies::TestLink < Test::Unit::TestCase
|
4
|
+
context "Link.create" do
|
5
|
+
setup do
|
6
|
+
@url = 'http://foo.com'
|
7
|
+
@type = 'article'
|
8
|
+
@suggest_text = 'Suggested Text'
|
9
|
+
|
10
|
+
@link = Nytimes::Movies::Link.create_from_api('url' => @url, 'type' => @type, 'suggested_link_text' => @suggest_text)
|
11
|
+
end
|
12
|
+
|
13
|
+
should "return an object of type Nytimes::Movies::Link" do
|
14
|
+
assert_kind_of(Nytimes::Movies::Link, @link)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "assign the @url attribute from the 'url' key in the hash" do
|
18
|
+
assert_equal(@url, @link.url)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "assign the @link_type attribute from the 'type' key in the hash" do
|
22
|
+
assert_equal(@type, @link.link_type)
|
23
|
+
end
|
24
|
+
|
25
|
+
should "assign the @suggested_text attribute from the 'suggested_link_text' key in the hash" do
|
26
|
+
assert_equal(@suggest_text, @link.suggested_text)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper.rb'
|
2
|
+
|
3
|
+
LINK_HASH = {"resource" => {"type" => "image","src" => "http:\/\/graphics8.nytimes.com\/images\/2007\/03\/02\/movies\/scott.163.jpg", "height" => 234, "width" => 345,"credit" => "Tony Cenicola\/<br>The New York Times"}}.freeze
|
4
|
+
|
5
|
+
|
6
|
+
class TestNytimes::TestMovies::TestMultimediaLink < Test::Unit::TestCase
|
7
|
+
context "MultimediaLink.create_from_api" do
|
8
|
+
setup do
|
9
|
+
@link = Nytimes::Movies::MultimediaLink.create_from_api(LINK_HASH)
|
10
|
+
end
|
11
|
+
|
12
|
+
should "return an instance of MultimediaLink" do
|
13
|
+
assert_kind_of(Nytimes::Movies::MultimediaLink, @link)
|
14
|
+
end
|
15
|
+
|
16
|
+
should "assign the @media_type attribute from the 'type' key in the hash" do
|
17
|
+
assert_equal(LINK_HASH['resource']['type'], @link.media_type)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "assign the @url attribute from the 'src' key in the hash" do
|
21
|
+
assert_equal(LINK_HASH['resource']['src'], @link.url)
|
22
|
+
end
|
23
|
+
|
24
|
+
should "assign the @height attribute from the 'height' key in the hash" do
|
25
|
+
assert_equal(LINK_HASH['resource']['height'], @link.height)
|
26
|
+
end
|
27
|
+
|
28
|
+
should "assign the @width attribute from the 'width' key in the hash" do
|
29
|
+
assert_equal(LINK_HASH['resource']['width'], @link.width)
|
30
|
+
end
|
31
|
+
|
32
|
+
should "assign the @credit attribute from the 'credit' key in the hash" do
|
33
|
+
assert_equal(LINK_HASH['resource']['credit'], @link.credit)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper.rb'
|
2
|
+
|
3
|
+
class TestNytimes::TestMovies::TestResultSet < Test::Unit::TestCase
|
4
|
+
include Nytimes::Movies
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@reply = MOVIE_RESULT_HASH
|
8
|
+
@params = {'offset' => 40}
|
9
|
+
@result_set = ResultSet.new(@params, @reply, Review)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "first_index" do
|
13
|
+
should "equal the offset + 1" do
|
14
|
+
assert_equal 41, @result_set.first_index
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "last_index" do
|
19
|
+
should "equal the first_index + batch_size - 1" do
|
20
|
+
assert_equal 60, @result_set.last_index
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "num_pages" do
|
25
|
+
should "equal the ceiling of num_results / batch_size" do
|
26
|
+
@result_set.instance_variable_set '@num_results', 123
|
27
|
+
|
28
|
+
assert_equal 7, @result_set.num_pages
|
29
|
+
end
|
30
|
+
|
31
|
+
should "equal 1 if the number of results < batch_size" do
|
32
|
+
@result_set.instance_variable_set '@num_results', 1
|
33
|
+
assert_equal 1, @result_set.num_pages
|
34
|
+
end
|
35
|
+
|
36
|
+
should "not erroneously round up if the num_results % batch_size = 0" do
|
37
|
+
@result_set.instance_variable_set '@num_results', 120
|
38
|
+
assert_equal 6, @result_set.num_pages
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "page_number" do
|
43
|
+
should "equal the offset / batch_size + 1" do
|
44
|
+
assert_equal 3, @result_set.page_number
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper.rb'
|
2
|
+
|
3
|
+
class TestNytimes::TestMovies::TestReview < Test::Unit::TestCase
|
4
|
+
include Nytimes::Movies
|
5
|
+
|
6
|
+
# global setup
|
7
|
+
def setup
|
8
|
+
FakeWeb.clean_registry
|
9
|
+
FakeWeb.block_uri_pattern(Base::API_SERVER)
|
10
|
+
end
|
11
|
+
|
12
|
+
def expects_invoke_arg(key, value)
|
13
|
+
Review.expects(:invoke).with(anything, has_entry(key, value)).returns(MOVIE_RESULT_HASH)
|
14
|
+
end
|
15
|
+
|
16
|
+
context "Review.create_from_api" do
|
17
|
+
setup do
|
18
|
+
@review = Review.create_from_api(MOVIE_REVIEW_HASH)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "rename seo-name to seo_name" do
|
22
|
+
assert_equal MOVIE_REVIEW_HASH['seo-name'], @review.seo_name
|
23
|
+
end
|
24
|
+
|
25
|
+
should "return a MultimediaLink object for thumbnail" do
|
26
|
+
assert_kind_of MultimediaLink, @review.thumbnail
|
27
|
+
end
|
28
|
+
|
29
|
+
context "link" do
|
30
|
+
should "return a Link object" do
|
31
|
+
assert_kind_of Link, @review.link
|
32
|
+
end
|
33
|
+
|
34
|
+
should "map the URL" do
|
35
|
+
assert_equal MOVIE_REVIEW_HASH['link']['url'], @review.link.url
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "related links" do
|
40
|
+
should "return an array of Link objects" do
|
41
|
+
assert_kind_of Array, @review.related_links
|
42
|
+
@review.related_links.all? {|l| assert_kind_of(Link, l)}
|
43
|
+
end
|
44
|
+
|
45
|
+
should "be one for each link object" do
|
46
|
+
assert_equal MOVIE_REVIEW_HASH["related_urls"].length, @review.related_links.length
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
should "map the critics_pick to a boolean" do
|
51
|
+
assert_equal true, @review.critics_pick?
|
52
|
+
end
|
53
|
+
|
54
|
+
should "map the thousand_best to a boolean" do
|
55
|
+
assert_equal false, @review.thousand_best?
|
56
|
+
end
|
57
|
+
|
58
|
+
%w(nyt_movie_id display_title sort_name mpaa_rating byline headline summary_short).each do |field|
|
59
|
+
should "copy the #{field} from the hash into a class attribute" do
|
60
|
+
assert_equal MOVIE_REVIEW_HASH[field], @review.send(field)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
%w(dvd_release_date opening_date date_updated publication_date).each do |date|
|
65
|
+
should "parse the #{date} from the hash into a Ruby Date" do
|
66
|
+
assert_kind_of Date, @review.send(date)
|
67
|
+
assert_equal Date.parse(MOVIE_REVIEW_HASH[date]), @review.send(date)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "Review.find" do
|
73
|
+
setup do
|
74
|
+
FakeWeb.register_uri(api_url_for('reviews/search', 'query' => 'constantine'), :string => MOVIE_RESULT_REPLY)
|
75
|
+
end
|
76
|
+
|
77
|
+
should "call the reviews/search endpoint"
|
78
|
+
|
79
|
+
should "return an instance of ResultSet" do
|
80
|
+
result_set = Review.find(:text => 'constantine')
|
81
|
+
assert_kind_of(ResultSet, result_set)
|
82
|
+
end
|
83
|
+
|
84
|
+
context "input parameters" do
|
85
|
+
%w(critics_pick dvd thousand_best).each do |flag|
|
86
|
+
context flag do
|
87
|
+
should "send Y if the #{flag} is true" do
|
88
|
+
expects_invoke_arg(flag.gsub('_','-'), 'Y')
|
89
|
+
Review.find(flag.to_sym => true)
|
90
|
+
end
|
91
|
+
|
92
|
+
should "send N if the #{flag} is false" do
|
93
|
+
expects_invoke_arg(flag.gsub('_','-'), 'N')
|
94
|
+
Review.find(flag.to_sym => false)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
%w(opening_date publication_date).each do |field|
|
100
|
+
context field do
|
101
|
+
should "accept a single Date object" do
|
102
|
+
d = Date.parse('1/1/2008')
|
103
|
+
expects_invoke_arg(field.gsub('_','-'), d.strftime('%Y-%m-%d'))
|
104
|
+
Review.find(field.to_sym => d)
|
105
|
+
end
|
106
|
+
|
107
|
+
should "accept a single Time object" do
|
108
|
+
t = Time.parse('1/1/2008')
|
109
|
+
expects_invoke_arg(field.gsub('_','-'), t.strftime('%Y-%m-%d'))
|
110
|
+
Review.find(field.to_sym => t)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "accept a range of Date objects" do
|
114
|
+
d1 = Date.parse('1/1/2008')
|
115
|
+
d2 = Date.parse('1/30/2008')
|
116
|
+
expects_invoke_arg(field.gsub('_','-'), "#{d1.strftime('%Y-%m-%d')};#{d2.strftime('%Y-%m-%d')}")
|
117
|
+
Review.find(field.to_sym => d1..d2)
|
118
|
+
end
|
119
|
+
|
120
|
+
should "accept a range of Time objects" do
|
121
|
+
t1 = Time.parse('1/1/2008')
|
122
|
+
t2 = Time.parse('1/30/2008')
|
123
|
+
expects_invoke_arg(field.gsub('_','-'), "#{t1.strftime('%Y-%m-%d')};#{t2.strftime('%Y-%m-%d')}")
|
124
|
+
Review.find(field.to_sym => t1..t2)
|
125
|
+
end
|
126
|
+
|
127
|
+
should "accept an array of Date objects" do
|
128
|
+
d1 = Date.parse('1/1/2008')
|
129
|
+
d2 = Date.parse('1/30/2008')
|
130
|
+
expects_invoke_arg(field.gsub('_','-'), "#{d1.strftime('%Y-%m-%d')};#{d2.strftime('%Y-%m-%d')}")
|
131
|
+
Review.find(field.to_sym => [d1,d2])
|
132
|
+
end
|
133
|
+
|
134
|
+
should "accept an array of Time objects" do
|
135
|
+
t1 = Time.parse('1/1/2008')
|
136
|
+
t2 = Time.parse('1/30/2008')
|
137
|
+
expects_invoke_arg(field.gsub('_','-'), "#{t1.strftime('%Y-%m-%d')};#{t2.strftime('%Y-%m-%d')}")
|
138
|
+
Review.find(field.to_sym => [t1,t2])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "offset" do
|
144
|
+
should "send the offset argument through" do
|
145
|
+
expects_invoke_arg('offset', 60)
|
146
|
+
Review.find(:offset => 60)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "page" do
|
151
|
+
should "set the offset to be batchsize * page-1" do
|
152
|
+
expects_invoke_arg('offset', 40)
|
153
|
+
Review.find(:page => 3)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "ordering parameters" do
|
158
|
+
should "send a by-title to the API for :title order" do
|
159
|
+
expects_invoke_arg('order', 'by-title')
|
160
|
+
Review.find(:order => :title)
|
161
|
+
end
|
162
|
+
|
163
|
+
should "send a by-publication-date to the API for :publication_date order" do
|
164
|
+
expects_invoke_arg('order', 'by-publication-date')
|
165
|
+
Review.find(:order => :publication_date)
|
166
|
+
end
|
167
|
+
|
168
|
+
should "send a by-opening-date to the API for :opening_date order" do
|
169
|
+
expects_invoke_arg('order', 'by-opening-date')
|
170
|
+
Review.find(:order => :opening_date)
|
171
|
+
end
|
172
|
+
|
173
|
+
should "send a by-dvd-release-date to the API for :dvd_release_date order" do
|
174
|
+
expects_invoke_arg('order', 'by-dvd-release-date')
|
175
|
+
Review.find(:order => :dvd_release_date)
|
176
|
+
end
|
177
|
+
|
178
|
+
should "raise an ArgumentError if the ordering is not one of those allowed" do
|
179
|
+
assert_raise(ArgumentError) do
|
180
|
+
Review.find(:order => :random)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "reviewer" do
|
186
|
+
should "escape the Critic Name to the SEO form" do
|
187
|
+
expects_invoke_arg('reviewer', 'a-o-scott')
|
188
|
+
Review.find(:reviewer => 'A. O. Scott')
|
189
|
+
end
|
190
|
+
|
191
|
+
should "not escape again if already in SEO form" do
|
192
|
+
expects_invoke_arg('reviewer', 'a-o-scott')
|
193
|
+
Review.find(:reviewer => 'A. O. Scott')
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'thoughtbot-shoulda'
|
4
|
+
require 'shoulda'
|
5
|
+
gem 'FakeWeb'
|
6
|
+
require 'fake_web'
|
7
|
+
require 'mocha'
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/../lib/nytimes/movies'
|
11
|
+
|
12
|
+
API_KEY = '13e234323232222'
|
13
|
+
Nytimes::Movies::Base.api_key = API_KEY
|
14
|
+
|
15
|
+
def api_url_for(path, params = {})
|
16
|
+
full_params = params.merge 'api-key' => API_KEY
|
17
|
+
Nytimes::Movies::Base.build_request_url(path, full_params).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
module TestNytimes
|
21
|
+
module TestMovies
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
MOVIE_REVIEW_HASH = {"dvd_release_date"=>"2008-09-16", "opening_date"=>"2008-04-18",
|
26
|
+
"multimedia"=>{"resource"=>{"src"=>"http://graphics8.nytimes.com/images/2008/04/18/arts/18sword-75.jpg", "type"=>"thumbnail", "height"=>75, "width"=>75}},
|
27
|
+
"byline"=>"Stephen Holden",
|
28
|
+
"nyt_movie_id"=>405736,
|
29
|
+
"publication_date"=>"2008-04-18", "critics_pick"=>"Y", "sort_name"=>"Constantine's Sword", "headline"=>"",
|
30
|
+
"summary_short"=>"u201cConstantineu2019s Swordu201d asks: When your core beliefs conflict with church doctrine, how far should your loyalty to the church extend?",
|
31
|
+
"thousand_best"=>"N",
|
32
|
+
"mpaa_rating"=>"NR",
|
33
|
+
"related_urls"=>[{"suggested_link_text"=>"Overview of Constantine's Sword", "url"=>"http://movies.nytimes.com/movie/405736/Constantine-s-Sword/overview", "type"=>"overview"}, {"suggested_link_text"=>"Tickets & Showtimes for Constantine's Sword", "url"=>"http://movies.nytimes.com/movie/405736/Constantine-s-Sword/showtimes", "type"=>"showtimes"}, {"suggested_link_text"=>"Cast, Credits & Awards for Constantine's Sword", "url"=>"http://movies.nytimes.com/movie/405736/Constantine-s-Sword/details", "type"=>"awards"}, {"suggested_link_text"=>"Readers' Reviews of Constantine's Sword", "url"=>"http://movies.nytimes.com/movie/405736/Constantine-s-Sword/rnr", "type"=>"community"}, {"suggested_link_text"=>"Trailers & Clips for Constantine's Sword", "url"=>"http://movies.nytimes.com/movie/405736/Constantine-s-Sword/trailers", "type"=>"trailers"}],
|
34
|
+
"seo-name"=>"Constantine-s-Sword",
|
35
|
+
"display_title"=>"Constantine's Sword",
|
36
|
+
"link"=>{"suggested_link_text"=>"Read the New York Times Review of Constantine's Sword", "url"=>"http://movies.nytimes.com/", "type"=>"article"},
|
37
|
+
"date_updated"=>"2008-08-21 12:10:38"}
|
38
|
+
|
39
|
+
MOVIE_RESULT_HASH = {
|
40
|
+
"status" => "OK",
|
41
|
+
"copyright" => "Copyright (c) 2008 The New York Times Company. All Rights Reserved.",
|
42
|
+
"num_results" => 1,
|
43
|
+
"results" => [MOVIE_REVIEW_HASH]
|
44
|
+
}
|
45
|
+
|
46
|
+
MOVIE_RESULT_REPLY = MOVIE_RESULT_HASH.to_json
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nytimes-movies
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacob Harris
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-12 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: A gem for talking to the New York Times Movie Reviews API
|
26
|
+
email: jharris@nytimes.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- History.txt
|
35
|
+
- Rakefile
|
36
|
+
- README.rdoc
|
37
|
+
- VERSION.yml
|
38
|
+
- lib/nytimes/movies/base.rb
|
39
|
+
- lib/nytimes/movies/critic.rb
|
40
|
+
- lib/nytimes/movies/link.rb
|
41
|
+
- lib/nytimes/movies/multimedia_link.rb
|
42
|
+
- lib/nytimes/movies/result_set.rb
|
43
|
+
- lib/nytimes/movies/review.rb
|
44
|
+
- lib/nytimes/movies.rb
|
45
|
+
- lib/nytimes-movies.rb
|
46
|
+
- test/nytimes/movies/test_critic.rb
|
47
|
+
- test/nytimes/movies/test_link.rb
|
48
|
+
- test/nytimes/movies/test_multimedia_link.rb
|
49
|
+
- test/nytimes/movies/test_result_set.rb
|
50
|
+
- test/nytimes/movies/test_review.rb
|
51
|
+
- test/test_helper.rb
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://github.com/harrisj/nytimes-moves
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --inline-source
|
59
|
+
- --charset=UTF-8
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.3.2
|
78
|
+
signing_key:
|
79
|
+
specification_version: 2
|
80
|
+
summary: A gem for talking to the New York Times Movie Reviews API
|
81
|
+
test_files: []
|
82
|
+
|