rotten 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +1 -1
- data/README.md +19 -5
- data/lib/rotten.rb +8 -7
- data/lib/rotten/api.rb +26 -2
- data/lib/rotten/cache.rb +4 -1
- data/lib/rotten/movie.rb +14 -9
- data/lib/rotten/review.rb +1 -0
- data/lib/rotten/search_result.rb +59 -0
- data/lib/rotten/version.rb +1 -1
- data/spec/cache_spec.rb +2 -2
- data/spec/movie_spec.rb +9 -9
- data/spec/spec_helper.rb +7 -0
- metadata +3 -2
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
Rotten.api_key = 'your_key'
|
7
7
|
|
8
8
|
# Info about a specific film
|
9
|
-
movies = Rotten::Movies.search("There will be blood")
|
9
|
+
movies = Rotten::Movies.search("There will be blood")[0]
|
10
10
|
movie.reviews
|
11
11
|
movie.cast
|
12
12
|
|
@@ -32,7 +32,25 @@
|
|
32
32
|
Rotten::Movie.search "Blue Velvet" # Hits API
|
33
33
|
Rotten::Movie.search "Blue Velvet" # Hits MemoryStore
|
34
34
|
|
35
|
+
Most API calls return multiple results. They are encapsulated in a SearchResult class. They are instances of Set with some Array sugar added. Example:
|
35
36
|
|
37
|
+
Rotten::Movie.upcoming
|
38
|
+
=> <Rotten::SearchResult total=14 more='false'>
|
39
|
+
Rotten::Movie.upcoming[0]
|
40
|
+
=> <Rotten::Movie title='Water for Elephants' id='771204250'>
|
41
|
+
Rotten::Movie.upcoming.to_a
|
42
|
+
=> <Rotten::Movie title='Water for Elephants' id='771204250'><Rotten::Movie title (you get the idea)...
|
43
|
+
|
44
|
+
results = Rotten::Movie.search "bob"
|
45
|
+
=> <Rotten::SearchResult total=242 more='true'>
|
46
|
+
results.size
|
47
|
+
=> 50
|
48
|
+
results.next #grab the next 50
|
49
|
+
results.size
|
50
|
+
=> 100
|
51
|
+
|
52
|
+
SearchResult works with caching, albeit a little wonky as the URLs are not grouped together.
|
53
|
+
|
36
54
|
### Features
|
37
55
|
- Movie search
|
38
56
|
- Movies opening this week
|
@@ -40,10 +58,6 @@
|
|
40
58
|
- Movie reviews
|
41
59
|
- Caching
|
42
60
|
|
43
|
-
#### TODO
|
44
|
-
- Implement all APIs
|
45
|
-
- More tests
|
46
|
-
|
47
61
|
#### Copyright
|
48
62
|
|
49
63
|
Rotten is licensed under the MIT license.
|
data/lib/rotten.rb
CHANGED
@@ -2,13 +2,14 @@ lib = File.expand_path('../', __FILE__)
|
|
2
2
|
$:.unshift lib unless $:.include?(lib)
|
3
3
|
|
4
4
|
module Rotten
|
5
|
-
autoload :Entity,
|
6
|
-
autoload :Api,
|
7
|
-
autoload :Actor,
|
8
|
-
autoload :Cast,
|
9
|
-
autoload :Review,
|
10
|
-
autoload :Movie,
|
11
|
-
autoload :Cache,
|
5
|
+
autoload :Entity, "rotten/entity"
|
6
|
+
autoload :Api, "rotten/api"
|
7
|
+
autoload :Actor, "rotten/actor"
|
8
|
+
autoload :Cast, "rotten/cast"
|
9
|
+
autoload :Review, "rotten/review"
|
10
|
+
autoload :Movie, "rotten/movie"
|
11
|
+
autoload :Cache, "rotten/cache"
|
12
|
+
autoload :SearchResult, "rotten/search_result"
|
12
13
|
|
13
14
|
def api_key=(val)
|
14
15
|
@api_key = val
|
data/lib/rotten/api.rb
CHANGED
@@ -11,6 +11,10 @@ module Rotten
|
|
11
11
|
|
12
12
|
def self.included(base)
|
13
13
|
base.send :extend, ClassMethods
|
14
|
+
base.send :include, InstanceMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
14
18
|
end
|
15
19
|
|
16
20
|
module ClassMethods
|
@@ -23,6 +27,10 @@ module Rotten
|
|
23
27
|
'1.0'
|
24
28
|
end
|
25
29
|
|
30
|
+
def maximum_per_page
|
31
|
+
'50'
|
32
|
+
end
|
33
|
+
|
26
34
|
def use_cache?
|
27
35
|
@use_cache == true
|
28
36
|
end
|
@@ -64,7 +72,6 @@ module Rotten
|
|
64
72
|
url = url_for(path, options)
|
65
73
|
cached = get_from_cache(url)
|
66
74
|
if cached
|
67
|
-
puts "Using cache.."
|
68
75
|
if block_given?
|
69
76
|
return yield(cached)
|
70
77
|
else
|
@@ -88,12 +95,29 @@ module Rotten
|
|
88
95
|
|
89
96
|
params = ''
|
90
97
|
if options.keys.any?
|
91
|
-
options.each_pair{|k,v| params << "#{k}=#{URI.escape(v)}&" }
|
98
|
+
options.each_pair{|k,v| params << "#{k}=#{URI.escape(v.to_s)}&" }
|
92
99
|
end
|
93
100
|
params.chomp! "&" if params
|
94
101
|
|
95
102
|
"#{endpoint}/#{path}.json?apikey=#{Rotten.api_key}&#{params}"
|
96
103
|
end
|
104
|
+
|
105
|
+
def extract_info klass, json={}
|
106
|
+
if json.is_a?(Array)
|
107
|
+
json.map{|m| extract_info(klass, m) }
|
108
|
+
else
|
109
|
+
klass.new(json)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
def warn_about_unknown_options hash
|
115
|
+
known = [:page_limit, :page, :q, :country, :review_type, :apikey]
|
116
|
+
unknown = hash.keys.map{|k| k.to_sym} - known
|
117
|
+
if unknown.any?
|
118
|
+
puts "[rotten] Unknown API options: #{unknown.join(',')}."
|
119
|
+
end
|
120
|
+
end
|
97
121
|
end
|
98
122
|
end
|
99
123
|
end
|
data/lib/rotten/cache.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# Capture JSON responses and re-use them
|
2
2
|
# Works with any cache store that accepts the "read" and "write" methods
|
3
|
-
# TODO method_missing should hit the @store
|
4
3
|
|
5
4
|
module Rotten
|
6
5
|
autoload :SimpleCache, "rotten/simple_cache.rb"
|
@@ -21,5 +20,9 @@ module Rotten
|
|
21
20
|
@store.write key, value
|
22
21
|
end
|
23
22
|
alias_method :set, :write
|
23
|
+
|
24
|
+
def method_missing sym, *args, &block
|
25
|
+
@store.send sym, *args, &block
|
26
|
+
end
|
24
27
|
end
|
25
28
|
end
|
data/lib/rotten/movie.rb
CHANGED
@@ -28,18 +28,22 @@ module Rotten
|
|
28
28
|
fetch "movies/search", options
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
json.map{|m| extract_movie_info(m) }
|
34
|
-
else
|
35
|
-
Movie.new(json)
|
36
|
-
end
|
31
|
+
def find_first phrase, options={}
|
32
|
+
(search phrase, options.merge(:page_limit => 1))[0]
|
37
33
|
end
|
38
34
|
|
39
35
|
protected
|
40
36
|
def fetch path, options, json_start="movies"
|
37
|
+
warn_about_unknown_options(options)
|
38
|
+
options.merge!(:page_limit => maximum_per_page) unless options.key?(:page_limit)
|
39
|
+
|
40
|
+
if options.key?(:page_limit) && options[:page_limit].to_i > 50
|
41
|
+
puts "[rotten] Page limits higher than 50 may not work."
|
42
|
+
end
|
43
|
+
|
41
44
|
result = get(path, options) do |json|
|
42
|
-
|
45
|
+
#extract_info(Movie, json[json_start])
|
46
|
+
SearchResult.new( :path => path, :api_options => options, :class => Movie, :json => json )
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
@@ -62,8 +66,9 @@ module Rotten
|
|
62
66
|
# Moview reviews
|
63
67
|
# @return [Array]
|
64
68
|
def reviews options={}
|
65
|
-
|
66
|
-
|
69
|
+
path = "movies/#{id}/reviews"
|
70
|
+
Movie.get path, options.merge(:page_limit => Movie.maximum_per_page) do |json|
|
71
|
+
SearchResult.new( :path => path, :api_options => options, :class => Review, :json => json, :start => "reviews" )
|
67
72
|
end
|
68
73
|
end
|
69
74
|
|
data/lib/rotten/review.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Encapsulates APIs that return a 'total'
|
2
|
+
module Rotten
|
3
|
+
require "set"
|
4
|
+
class SearchResult < Set
|
5
|
+
include Api
|
6
|
+
|
7
|
+
#def initialize url, json={}, start='movies'
|
8
|
+
def initialize options={}
|
9
|
+
@path = options[:path]
|
10
|
+
@api_options = options[:api_options] || {}
|
11
|
+
@start = options[:start] || 'movies'
|
12
|
+
@json = options[:json]
|
13
|
+
@klass = options[:class]
|
14
|
+
@proper = @klass.extract_info(@klass, @json[@start])
|
15
|
+
|
16
|
+
super([@proper].flatten)
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"<Rotten::SearchResult total=#{total} more='#{more?}'>"
|
21
|
+
end
|
22
|
+
|
23
|
+
def total
|
24
|
+
@total ||= @json['total'].nil? ? size : @json['total'].to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def more?
|
28
|
+
total > size
|
29
|
+
end
|
30
|
+
|
31
|
+
# Make the Set more array-like and allow index-based access
|
32
|
+
def [](index)
|
33
|
+
to_a[index]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Cache Set#to_a and return it. Calling #next resets it.
|
37
|
+
def to_a
|
38
|
+
@to_a ||= super.to_a
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_page
|
42
|
+
@api_options.delete(:page) || 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Fetch additional results, while more exist.
|
46
|
+
def next
|
47
|
+
if more?
|
48
|
+
@api_options.merge! :page => current_page + 1
|
49
|
+
@api_options.merge! :page_limit => Movie.maximum_per_page
|
50
|
+
@klass.get @path, @api_options do |json|
|
51
|
+
[@klass.extract_info(@klass, json[@start])].flatten.each do |x|
|
52
|
+
add x
|
53
|
+
end
|
54
|
+
@to_a = nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/rotten/version.rb
CHANGED
data/spec/cache_spec.rb
CHANGED
@@ -46,9 +46,9 @@ describe Rotten::Cache do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it "should fetch from the cache" do
|
49
|
-
Rotten::Movie.search("There Will Be Blood").should be_an_instance_of(
|
49
|
+
Rotten::Movie.search("There Will Be Blood").should be_an_instance_of(Rotten::SearchResult)
|
50
50
|
json = JSON.parse(File.read( File.join(fixture_path, "search.json") ) )
|
51
|
-
YAML.load(Rotten::Movie.cache.cache_file)["http://api.rottentomatoes.com/api/public/v1.0/movies/search.json?apikey=1234567890&q=There%20Will%20Be%20Blood"]["movies"].should == json["movies"]
|
51
|
+
YAML.load(Rotten::Movie.cache.cache_file)["http://api.rottentomatoes.com/api/public/v1.0/movies/search.json?apikey=1234567890&q=There%20Will%20Be%20Blood&page_limit=50"]["movies"].should == json["movies"]
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/spec/movie_spec.rb
CHANGED
@@ -4,8 +4,8 @@ describe Rotten::Movie do
|
|
4
4
|
context "#cast" do
|
5
5
|
before :each do
|
6
6
|
simulate "movies/search", "search.json", :q => "There Will Be Blood"
|
7
|
-
@movie = Rotten::Movie.search("There Will Be Blood")
|
8
|
-
simulate "movies/#{@movie.id}/cast", "cast.json"
|
7
|
+
@movie = Rotten::Movie.search("There Will Be Blood")[-1]
|
8
|
+
simulate "movies/#{@movie.id}/cast", "cast.json", :page_limit => nil
|
9
9
|
end
|
10
10
|
|
11
11
|
context "sent :full" do
|
@@ -19,13 +19,13 @@ describe Rotten::Movie do
|
|
19
19
|
context "#reviews" do
|
20
20
|
before :each do
|
21
21
|
simulate "movies/search", "search.json", :q => "There Will Be Blood"
|
22
|
-
@movie = Rotten::Movie.search("There Will Be Blood")
|
22
|
+
@movie = Rotten::Movie.search("There Will Be Blood")[-1]
|
23
23
|
simulate "movies/#{@movie.id}/reviews", "reviews.json"
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should return an array of Review" do
|
27
|
-
@movie.reviews.should be_an_instance_of(
|
28
|
-
@movie.reviews.
|
27
|
+
@movie.reviews.should be_an_instance_of(Rotten::SearchResult)
|
28
|
+
@movie.reviews[0].should be_an_instance_of(Rotten::Review)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -35,7 +35,7 @@ describe Rotten::Movie do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
it "should return an array" do
|
38
|
-
Rotten::Movie.dvd_releases.should be_an_instance_of(
|
38
|
+
Rotten::Movie.dvd_releases.should be_an_instance_of(Rotten::SearchResult)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -45,7 +45,7 @@ describe Rotten::Movie do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should return an array" do
|
48
|
-
Rotten::Movie.in_theatres.should be_an_instance_of(
|
48
|
+
Rotten::Movie.in_theatres.should be_an_instance_of(Rotten::SearchResult)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -55,7 +55,7 @@ describe Rotten::Movie do
|
|
55
55
|
end
|
56
56
|
|
57
57
|
it "should return an array" do
|
58
|
-
Rotten::Movie.opening.should be_an_instance_of(
|
58
|
+
Rotten::Movie.opening.should be_an_instance_of(Rotten::SearchResult)
|
59
59
|
end
|
60
60
|
|
61
61
|
it "should contain a movie" do
|
@@ -77,7 +77,7 @@ describe Rotten::Movie do
|
|
77
77
|
end
|
78
78
|
|
79
79
|
it "should return an array" do
|
80
|
-
Rotten::Movie.search("There Will Be Blood").should be_an_instance_of(
|
80
|
+
Rotten::Movie.search("There Will Be Blood").should be_an_instance_of(Rotten::SearchResult)
|
81
81
|
end
|
82
82
|
|
83
83
|
it "should contain a movie" do
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,13 @@ RSpec.configure do |config|
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def simulate(path, fixture, options={})
|
13
|
+
if options.member?(:page_limit)
|
14
|
+
if options[:page_limit].nil?
|
15
|
+
options.delete :page_limit
|
16
|
+
end
|
17
|
+
else
|
18
|
+
options.merge!(:page_limit => 50)
|
19
|
+
end
|
13
20
|
FakeWeb.register_uri(:get, Rotten::Movie.url_for(path, options),
|
14
21
|
:body => File.read( File.join(fixture_path, fixture)) )
|
15
22
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 5
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.5.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- James Cook
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- lib/rotten/entity.rb
|
67
67
|
- lib/rotten/movie.rb
|
68
68
|
- lib/rotten/review.rb
|
69
|
+
- lib/rotten/search_result.rb
|
69
70
|
- lib/rotten/simple_cache.rb
|
70
71
|
- lib/rotten/version.rb
|
71
72
|
- rotten.gemspec
|