rotten 0.3.0 → 0.4.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.2.0)
4
+ rotten (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -10,17 +10,35 @@
10
10
  movie.reviews
11
11
  movie.cast
12
12
 
13
- # list upcoming movies
13
+ # List upcoming movies
14
14
  Rotten::Movie.upcoming
15
15
 
16
- # list movies opening this week
16
+ # List movies in theatres now
17
+ Rotten::Movie.in_theatres
18
+
19
+ # List movies opening this week
17
20
  Rotten::Movie.opening
18
21
 
22
+ # List movies coming to dvd this week
23
+ Rotten::Movie.dvd_release
24
+
25
+ # Use a file cache (baked in)
26
+ Rotten::Movie.enable_cache!
27
+ Rotten::Movie.search "What about bob?" # Hits API
28
+ Rotten::Movie.search "What about bob?" # Hits on-disk cache
29
+
30
+ # Custom cache. Should respond to #read & #write. Recommended over included cache.
31
+ Rotten::Movie.cache= ActiveSupport::Cache::MemoryStore.new
32
+ Rotten::Movie.search "Blue Velvet" # Hits API
33
+ Rotten::Movie.search "Blue Velvet" # Hits MemoryStore
34
+
35
+
19
36
  ### Features
20
37
  - Movie search
21
38
  - Movies opening this week
22
39
  - Movies upcoming
23
40
  - Movie reviews
41
+ - Caching
24
42
 
25
43
  #### TODO
26
44
  - Implement all APIs
@@ -8,6 +8,7 @@ module Rotten
8
8
  autoload :Cast, "rotten/cast"
9
9
  autoload :Review, "rotten/review"
10
10
  autoload :Movie, "rotten/movie"
11
+ autoload :Cache, "rotten/cache"
11
12
 
12
13
  def api_key=(val)
13
14
  @api_key = val
@@ -23,16 +23,60 @@ module Rotten
23
23
  '1.0'
24
24
  end
25
25
 
26
+ def use_cache?
27
+ @use_cache == true
28
+ end
29
+
30
+ def enable_cache!
31
+ @use_cache = true
32
+ end
33
+
34
+ def disable_cache!
35
+ @use_cache = false
36
+ end
37
+
38
+ def cache
39
+ @cache ||= Cache.new
40
+ @cache.store
41
+ end
42
+
43
+ # Use your own cache, such as ActiveSupport::Cache::MemoryStore
44
+ def cache=(_cache)
45
+ enable_cache!
46
+ @cache = Cache.new :store => _cache
47
+ end
48
+
49
+ def get_from_cache url
50
+ if use_cache?
51
+ cache.read(url)
52
+ end
53
+ end
54
+
55
+ def write_to_cache url, data
56
+ cache.write(url, data) if use_cache?
57
+ end
58
+
26
59
  def get path, options={}
27
60
  if Rotten.api_key.nil?
28
61
  raise UndefinedApiKeyError, "Please define your API key with Rotten.api_key=(your_key)"
29
62
  end
30
63
 
31
- url = url_for(path, options)
64
+ url = url_for(path, options)
65
+ cached = get_from_cache(url)
66
+ if cached
67
+ puts "Using cache.."
68
+ if block_given?
69
+ return yield(cached)
70
+ else
71
+ return cached
72
+ end
73
+ end
74
+
32
75
  open( url ) do |response|
33
76
  data = JSON.parse(response.read)
77
+ write_to_cache url, data
34
78
  if block_given?
35
- yield data
79
+ yield(data)
36
80
  else
37
81
  data
38
82
  end
@@ -0,0 +1,25 @@
1
+ # Capture JSON responses and re-use them
2
+ # Works with any cache store that accepts the "read" and "write" methods
3
+ # TODO method_missing should hit the @store
4
+
5
+ module Rotten
6
+ autoload :SimpleCache, "rotten/simple_cache.rb"
7
+
8
+ class Cache
9
+
10
+ attr_reader :store
11
+ def initialize options={}
12
+ @store = options.delete(:store) || SimpleCache.new(options)
13
+ end
14
+
15
+ def read key
16
+ @store.read key
17
+ end
18
+ alias_method :get, :read
19
+
20
+ def write key, value
21
+ @store.write key, value
22
+ end
23
+ alias_method :set, :write
24
+ end
25
+ end
@@ -17,8 +17,6 @@ module Rotten
17
17
  @characters[index]
18
18
  end
19
19
 
20
- def first; at[0]; end
21
-
22
20
  def process array
23
21
  array.each do |hash|
24
22
  actor = @actors.detect{|a| a.name == hash["name"] } || a=Actor.new; a.attributes=hash
@@ -0,0 +1,18 @@
1
+ module Rotten
2
+ require "ostruct"
3
+ class Entity < OpenStruct
4
+ class << self
5
+ def from_json(json)
6
+ e = new
7
+ e.attributes = json
8
+ e
9
+ end
10
+ end
11
+
12
+ def attributes= hash={}
13
+ hash.each_pair do |k,v|
14
+ instance_variable_get("@table")[k.to_sym] = v
15
+ end
16
+ end
17
+ end
18
+ end
@@ -12,6 +12,15 @@ module Rotten
12
12
  fetch "lists/movies/upcoming", options
13
13
  end
14
14
 
15
+ def dvd_releases options={}
16
+ fetch "lists/dvds/new_releases", options
17
+ end
18
+
19
+ def in_theaters options={}
20
+ fetch "lists/movies/in_theaters", options
21
+ end
22
+ alias_method :in_theatres, :in_theaters
23
+
15
24
  def search phrase, options={}
16
25
  options.delete :q
17
26
  options[:q] = phrase
@@ -23,7 +32,7 @@ module Rotten
23
32
  if json.is_a?(Array)
24
33
  json.map{|m| extract_movie_info(m) }
25
34
  else
26
- return Movie.new(json)
35
+ Movie.new(json)
27
36
  end
28
37
  end
29
38
 
@@ -0,0 +1,4 @@
1
+ module Rotten
2
+ class Review < Entity
3
+ end
4
+ end
@@ -0,0 +1,30 @@
1
+ module Rotten
2
+ class SimpleCache
3
+ attr_reader :cache_file
4
+ def initialize options={}
5
+ require "yaml"
6
+ if options[:cache_path] && File.exists?(options[:cache_path])
7
+ @cache_file = options[:cache_path]
8
+ @data = YAML.load_file @cache_file || {}
9
+ else
10
+ require "tempfile"
11
+ @cache_file = Tempfile.new "__rotten"
12
+ @data = YAML.load_file(@cache_file) || {}
13
+ end
14
+ end
15
+
16
+ def read key
17
+ @data[key.to_s]
18
+ end
19
+
20
+ def write key, value
21
+ @data[key.to_s] = value
22
+ save_to_disk
23
+ end
24
+
25
+ private
26
+ def save_to_disk
27
+ File.open(@cache_file, "w"){|f| f.puts(YAML.dump(@data)) }
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module Rotten
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ describe Rotten::Cache do
4
+ context ".new" do
5
+ context "when :store is not given" do
6
+ it "should set the store to SimpleCache" do
7
+ Rotten::Cache.new.store.class.should == Rotten::SimpleCache
8
+ end
9
+ end
10
+ end
11
+
12
+ context "#read" do
13
+ before :each do
14
+ @cache = Rotten::SimpleCache.new
15
+ @cache.write :name, "bob"
16
+ end
17
+
18
+ context "given a key" do
19
+ it "should read the key from the cache" do
20
+ @cache.read(:name).should == "bob"
21
+ YAML.load(@cache.cache_file).should == {"name" => "bob"}
22
+ end
23
+ end
24
+ end
25
+
26
+ context "#write" do
27
+ before :each do
28
+ @cache = Rotten::SimpleCache.new
29
+ @cache.write :name, "hank"
30
+ end
31
+ context "given a key/value" do
32
+ it "should write the data to the cache" do
33
+ YAML.load(@cache.cache_file).should == {"name" => "hank"}
34
+ end
35
+ end
36
+ end
37
+
38
+ context "hooked into Rotten::Movie" do
39
+ before :each do
40
+ Rotten::Movie.enable_cache!
41
+ simulate "movies/search", "search.json", :q => "There Will Be Blood"
42
+ #TODO figure out how open uri uses Net::HTTP for better tests
43
+ Net::HTTP.stub(:open){ raise }
44
+ Net::HTTP.stub(:start){ raise }
45
+ Net::HTTP.stub(:get){ raise }
46
+ end
47
+
48
+ it "should fetch from the cache" do
49
+ Rotten::Movie.search("There Will Be Blood").should be_an_instance_of(Array)
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"]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1 @@
1
+ {"total":41,"reviews":[{"critic":"Bob Mondello","date":"2008-10-18","publication":"NPR.org","quote":"This sometimes magnificent, decidedly strange film is a portrait of a terrible, rapacious man.","links":{"review":"http://www.npr.org/templates/story/story.php?storyId=17628099"}},{"critic":"Richard Schickel","date":"2008-10-18","publication":"TIME Magazine","quote":"One of the most wholly original American movies ever made.","links":{"review":"http://www.time.com/time/arts/article/0,8599,1698168,00.html"}},{"critic":"Christopher Orr","date":"2008-09-18","publication":"New Republic","quote":"There Will Be Blood establishes itself as a film of Darwinian ferocity, a stark and pitiless parable of American capitalism.","links":{"review":"http://www.tnr.com/booksarts/story.html?id=ba49de78-d44a-4963-9d96-65ef4fef9e21"}},{"critic":"Jonathan F. Richards","date":"2008-02-05","publication":"Film.com","quote":"Daniel Day-Lewis bestrides the narrow world like a colossus as Daniel Plainview, a turn-of-the-last-century prospector for gold and silver who stumbles upon oil in rural California and goes after it with the ferocity, focus, and ethical sensitivity of a f","links":{"review":"http://www.filmfreak.be/index.php?module=filmfreak&func=viewpub&tid=9&pid=420&title=There.Will.Be.Blood"}},{"critic":"Christy Lemire","date":"2008-01-18","publication":"Associated Press","quote":"Someday, we're probably going to look back at There Will Be Blood, Paul Thomas Anderson's epic about greed, lies, manipulation and insanity, and call it his masterpiece.","links":{}},{"critic":"Roger Moore","date":"2008-01-18","original_score":"4/5","publication":"Orlando Sentinel","quote":"It's oil, and a West that was just discovering it, that makes There Will Be Blood intriguing. But it's Day-Lewis who draws us in.","links":{"review":"http://www.orlandosentinel.com/entertainment/movies/orl-db-moviereviews-searchresults,0,3279701,results.formprofile?turbine_cdb_lib__cdb_01_txt=There%20Will%20Be%20Blood&Find+it%21=Submit+Query"}},{"critic":"Amy Biancolli","date":"2008-01-11","original_score":"3.5/4","publication":"Houston Chronicle","quote":"As an incurable romantic, I hold out hope that Anderson's flawed but phenomenal feat here marks the start of a new, mature stage in a long career. He has genius in him. So does the movie -- before its ending.","links":{"review":"http://www.chron.com/disp/story.mpl/ent/movies/reviews/5320054.html"}},{"critic":"Adam Graham","date":"2008-01-11","original_score":"B-","publication":"Detroit News","quote":"A maddening epic with a towering, bravura performance from star Daniel Day-Lewis, There Will Be Blood will rattle your brain for weeks, even months after you see it.","links":{"review":"http://www.detnews.com/apps/pbcs.dll/article?AID=/20080111/ENT02/801110391/1034/ENT02"}},{"critic":"Peter Travers","date":"2008-01-08","original_score":"4/4","publication":"Rolling Stone","quote":"There Will Be Blood hits with hurricane force. Lovers of formula and sugarcoating will hate it. Screw them. In terms of excitement, imagination and rule-busting experimentation, it's a gusher.","links":{}},{"critic":"Richard Roeper","date":"2008-01-07","publication":"Ebert & Roeper","quote":"Filmmaking at the highest level.","links":{}},{"critic":"Michael Phillips","date":"2008-01-04","original_score":"4/4","publication":"Chicago Tribune","quote":"There Will Be Blood reminds us that the greatest screen performances don't settle for capturing one trait, a dominant emotion or an easy way in.","links":{"review":"http://chicago.metromix.com/movies/movie_review/movie-review-there-will/276085/content"}},{"critic":"Peter Howell","date":"2008-01-04","original_score":"3.5/4","publication":"Toronto Star","quote":"There Will Be Blood, the astounding new film by Paul Thomas Anderson, is Horatio Alger by way of Faust.","links":{"review":"http://www.thestar.com/entertainment/Movies/article/290905"}},{"critic":"Mick LaSalle","date":"2008-01-04","original_score":"2.5/4","publication":"San Francisco Chronicle","quote":"Individual scenes and sequences are too strange, haunting and emotionally right for the film to be dismissed. There should be no attempt or temptation to dismiss it.","links":{"review":"http://www.sfgate.com/cgi-bin/article.cgi?file=/c/a/2008/01/04/DDVCU8QKP.DTL&type=movies"}},{"critic":"Liam Lacey","date":"2008-01-04","original_score":"3.5/4","publication":"Globe and Mail","quote":"Conjure up the maddest despot scene you can remember and you might get a sense of the seismic register of Day-Lewis's extravagant performance. Watch and marvel, though you may have to suspend your disbelief from the top of an oil derrick.","links":{"review":"http://www.theglobeandmail.com/servlet/story/RTGAM.20080103.blood04nat/BNStory/Entertainment/home"}},{"critic":"Lisa Kennedy","date":"2008-01-04","original_score":"4/4","publication":"Denver Post","quote":"If [Day-Lewis] does not win the Academy Award for this protean portrayal, it will be because he's won before. Or because his gift, his discipline, is so daunting it can be confounding.","links":{"review":"http://www.denverpost.com/movies/ci_7866021"}},{"critic":"Chris Vognar","date":"2008-01-04","original_score":"A-","publication":"Dallas Morning News","quote":"A haunting enigma that refuses to conform to any recognizable pattern, period or otherwise. I suppose you could see it as a descendent of Citizen Kane. But such comparisons aren't really fair to either film.","links":{"review":"http://www.guidelive.com/portal/page?_pageid=33,97283&_dad=portal&_schema=PORTAL&item_id=62157"}},{"critic":"Wesley Morris","date":"2008-01-04","original_score":"4/4","publication":"Boston Globe","quote":"There Will Be Blood is anti-state of the art. It's the work of an analog filmmaker railing against an increasingly digitized world. In that sense, the movie is idiosyncratic, too: vintage visionary stuff. It's physical and tactile.","links":{"review":"http://www.boston.com/movies/display?display=movie&id=10610"}},{"critic":"Roger Ebert","date":"2008-01-04","original_score":"3.5/4","publication":"Chicago Sun-Times","quote":"A force beyond categories.","links":{"review":"http://rogerebert.suntimes.com/apps/pbcs.dll/article?AID=/20080103/REVIEWS/801030301"}},{"critic":"Ann Hornaday","date":"2008-01-04","publication":"Washington Post","quote":"Paul Thomas Anderson becomes California's certified cinematic poet laureate with There Will Be Blood.","links":{"review":"http://www.washingtonpost.com/wp-dyn/content/article/2008/01/03/AR2008010304142.html"}},{"critic":"Colin Covert","date":"2008-01-04","original_score":"4/4","publication":"Minneapolis Star Tribune","quote":"A work of stunning intelligence and dramatic sweep, a portrait of a young nation struggling to find itself, torn between religious and business values.","links":{"review":"http://www.startribune.http://www.startribune.com/entertainment/movies/13018431.htmlcom/movies/"}}],"links":{"self":"http://api.rottentomatoes.com/api/public/v1.0/movies/770671487/reviews.json?review_type=topCritic&page_limit=20&country=us&page=1","next":"http://api.rottentomatoes.com/api/public/v1.0/movies/770671487/reviews.json?review_type=topCritic&page_limit=20&country=us&page=2","alternate":"http://www.rottentomatoes.com/m/there_will_be_blood/#reviews","rel":"http://api.rottentomatoes.com/api/public/v1.0/movies/770671487.json"},"link_template":"http://api.rottentomatoes.com/api/public/v1.0/movies/{movie-id}/reviews.json?review_type={top_critic|all|dvd}&page_limit={results-per-page}&page={page-number}&country={country-code}"}
@@ -3,9 +3,9 @@ require "spec_helper"
3
3
  describe Rotten::Movie do
4
4
  context "#cast" do
5
5
  before :each do
6
- simulate_movie_search
6
+ simulate "movies/search", "search.json", :q => "There Will Be Blood"
7
7
  @movie = Rotten::Movie.search("There Will Be Blood").pop
8
- simulate_full_cast(@movie)
8
+ simulate "movies/#{@movie.id}/cast", "cast.json"
9
9
  end
10
10
 
11
11
  context "sent :full" do
@@ -18,9 +18,9 @@ describe Rotten::Movie do
18
18
 
19
19
  context "#reviews" do
20
20
  before :each do
21
- simulate_movie_search
21
+ simulate "movies/search", "search.json", :q => "There Will Be Blood"
22
22
  @movie = Rotten::Movie.search("There Will Be Blood").pop
23
- simulate_movie_reviews(@movie)
23
+ simulate "movies/#{@movie.id}/reviews", "reviews.json"
24
24
  end
25
25
 
26
26
  it "should return an array of Review" do
@@ -28,10 +28,30 @@ describe Rotten::Movie do
28
28
  @movie.reviews.shift.should be_an_instance_of(Rotten::Review)
29
29
  end
30
30
  end
31
+
32
+ context ".dvd_releases" do
33
+ before :each do
34
+ simulate "lists/dvds/new_releases", "movie_openings.json"
35
+ end
36
+
37
+ it "should return an array" do
38
+ Rotten::Movie.dvd_releases.should be_an_instance_of(Array)
39
+ end
40
+ end
41
+
42
+ context ".in_theaters" do
43
+ before :each do
44
+ simulate "lists/movies/in_theaters", "movie_openings.json"
45
+ end
46
+
47
+ it "should return an array" do
48
+ Rotten::Movie.in_theatres.should be_an_instance_of(Array)
49
+ end
50
+ end
31
51
 
32
52
  context ".opening" do
33
53
  before :each do
34
- simulate_movie_openings
54
+ simulate "lists/movies/opening", "movie_openings.json"
35
55
  end
36
56
 
37
57
  it "should return an array" do
@@ -53,7 +73,7 @@ describe Rotten::Movie do
53
73
 
54
74
  context ".search" do
55
75
  before :each do
56
- simulate_movie_search
76
+ simulate "movies/search", "search.json", :q => "There Will Be Blood"
57
77
  end
58
78
 
59
79
  it "should return an array" do
@@ -67,7 +87,7 @@ describe Rotten::Movie do
67
87
 
68
88
  context "when api_key is undefined" do
69
89
  it "should raise error" do
70
- simulate_movie_openings
90
+ simulate "lists/movies/opening", "movie_openings.json"
71
91
  Rotten.api_key = nil
72
92
  lambda{ Rotten::Movie.opening }.should raise_error(Rotten::Api::UndefinedApiKeyError)
73
93
  end
@@ -9,27 +9,11 @@ RSpec.configure do |config|
9
9
  File.join( File.dirname(__FILE__), "fixtures" )
10
10
  end
11
11
 
12
- def simulate_movie_openings
13
- FakeWeb.register_uri(:get, Rotten::Movie.url_for("lists/movies/opening"),
14
- :body => File.read( File.join(fixture_path, "movie_openings.json") ))
12
+ def simulate(path, fixture, options={})
13
+ FakeWeb.register_uri(:get, Rotten::Movie.url_for(path, options),
14
+ :body => File.read( File.join(fixture_path, fixture)) )
15
15
  end
16
16
 
17
- def simulate_movie_search
18
- FakeWeb.register_uri(:get, Rotten::Movie.url_for("movies/search", :q => "There Will Be Blood"),
19
- :body => File.read( File.join(fixture_path, "search.json") ))
20
- end
21
-
22
- def simulate_movie_reviews(movie)
23
- FakeWeb.register_uri(:get, Rotten::Movie.url_for("movies/#{movie.id}/reviews"),
24
- :body => File.read( File.join(fixture_path, "reviews.json") ))
25
- end
26
-
27
- def simulate_full_cast(movie)
28
- FakeWeb.register_uri(:get, Rotten::Movie.url_for("movies/#{movie.id}/cast"),
29
- :body => File.read( File.join(fixture_path, "cast.json") ))
30
- end
31
-
32
-
33
17
  config.before(:each) do
34
18
  Rotten.api_key = "1234567890"
35
19
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
7
+ - 4
8
8
  - 0
9
- version: 0.3.0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - James Cook
@@ -61,12 +61,18 @@ files:
61
61
  - lib/rotten.rb
62
62
  - lib/rotten/actor.rb
63
63
  - lib/rotten/api.rb
64
+ - lib/rotten/cache.rb
64
65
  - lib/rotten/cast.rb
66
+ - lib/rotten/entity.rb
65
67
  - lib/rotten/movie.rb
68
+ - lib/rotten/review.rb
69
+ - lib/rotten/simple_cache.rb
66
70
  - lib/rotten/version.rb
67
71
  - rotten.gemspec
72
+ - spec/cache_spec.rb
68
73
  - spec/fixtures/cast.json
69
74
  - spec/fixtures/movie_openings.json
75
+ - spec/fixtures/reviews.json
70
76
  - spec/fixtures/search.json
71
77
  - spec/movie_spec.rb
72
78
  - spec/spec_helper.rb
@@ -105,8 +111,10 @@ signing_key:
105
111
  specification_version: 3
106
112
  summary: Wrapper for Rotten Tomatoes API
107
113
  test_files:
114
+ - spec/cache_spec.rb
108
115
  - spec/fixtures/cast.json
109
116
  - spec/fixtures/movie_openings.json
117
+ - spec/fixtures/reviews.json
110
118
  - spec/fixtures/search.json
111
119
  - spec/movie_spec.rb
112
120
  - spec/spec_helper.rb