rotten 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rotten (0.3.0)
4
+ rotten (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
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").pop
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.
@@ -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, "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"
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
@@ -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
@@ -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
@@ -28,18 +28,22 @@ module Rotten
28
28
  fetch "movies/search", options
29
29
  end
30
30
 
31
- def extract_movie_info json={}
32
- if json.is_a?(Array)
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
- extract_movie_info(json[json_start])
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
- Movie.get "movies/#{id}/reviews", options do |json|
66
- json["reviews"].map{|review| Review.from_json(review) }
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
 
@@ -1,4 +1,5 @@
1
1
  module Rotten
2
2
  class Review < Entity
3
+ include Api
3
4
  end
4
5
  end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Rotten
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -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(Array)
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
@@ -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").pop
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").pop
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(Array)
28
- @movie.reviews.shift.should be_an_instance_of(Rotten::Review)
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(Array)
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(Array)
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(Array)
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(Array)
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
@@ -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
- - 4
7
+ - 5
8
8
  - 0
9
- version: 0.4.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