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.
@@ -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