movie_searcher 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg/*
6
+ *.gem
7
+ .bundle
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ -fs
3
+ -Ilib
4
+ -Ispec
5
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in undertexter.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ MovieSearcher (0.0.1)
5
+ httparty
6
+ levenshtein
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ crack (0.1.8)
12
+ diff-lcs (1.1.2)
13
+ httparty (0.7.3)
14
+ crack (= 0.1.8)
15
+ levenshtein (0.2.0)
16
+ rspec (2.4.0)
17
+ rspec-core (~> 2.4.0)
18
+ rspec-expectations (~> 2.4.0)
19
+ rspec-mocks (~> 2.4.0)
20
+ rspec-core (2.4.0)
21
+ rspec-expectations (2.4.0)
22
+ diff-lcs (~> 1.1.2)
23
+ rspec-mocks (2.4.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ MovieSearcher!
30
+ httparty
31
+ levenshtein
32
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jon Maddox
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # ImdbParty!
2
+
3
+ ## How To Use
4
+
5
+ ### Create an instance
6
+
7
+ imdb = ImdbParty::Imdb.new
8
+ ### Search for a movie by title
9
+
10
+ imdb.find_by_title("The Dark Knight") => [{:title => "The Dark Knight", :year => "2008", :imdb_id => "tt0468569"}, {:title => "Batman Unmasked", ...}]
11
+
12
+ ### Get a movie by its imdb_id
13
+
14
+ movie = imdb.find_movie_by_id("tt0468569")
15
+
16
+ movie.title => "The Dark Knight"
17
+ movie.rating => 8.1
18
+ movie.certification => "PG-13"
19
+
20
+ ### Find the top 250 movies of all time
21
+
22
+ imdb.top_250 => [{:title => "Shawshank Redemption", :year => "1994", :imdb_id => "tt0111161"}, {:title => "The Godfather", ...}]
23
+
24
+ ### Get the currently popular tv shows
25
+
26
+ imdb.popular_shows => [{:title => "Glee", :year => "2009", :imdb_id => "tt1327801"}, {:title => "Dexter", ...}]
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "imdb_party"
8
+ gem.summary = %Q{IMDB client using the IMDB API that their iPhone app uses}
9
+ gem.description = %Q{IMDB client using the IMDB API that their iPhone app uses}
10
+ gem.email = "jon@mustacheinc.com"
11
+ gem.homepage = "http://github.com/maddox/imdb_party"
12
+ gem.authors = ["Jon Maddox"]
13
+ gem.add_development_dependency "shoulda"
14
+ gem.add_dependency "httparty"
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/*_test.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION')
47
+ version = File.read('VERSION')
48
+ else
49
+ version = ""
50
+ end
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "imdb_party #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/lib/imdb_party.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'httparty'
2
+
3
+ directory = File.expand_path(File.dirname(__FILE__))
4
+ require File.join(directory, 'imdb_party', 'httparty_icebox')
5
+ require File.join(directory, 'imdb_party', 'imdb')
6
+ require File.join(directory, 'imdb_party', 'movie')
7
+ require File.join(directory, 'imdb_party', 'person')
8
+
@@ -0,0 +1,259 @@
1
+ # = Icebox : Caching for HTTParty
2
+ #
3
+ # Cache responses in HTTParty models [http://github.com/jnunemaker/httparty]
4
+ #
5
+ # === Usage
6
+ #
7
+ # class Foo
8
+ # include HTTParty
9
+ # include HTTParty::Icebox
10
+ # cache :store => 'file', :timeout => 600, :location => MY_APP_ROOT.join('tmp', 'cache')
11
+ # end
12
+ #
13
+ # Modeled after Martyn Loughran's APICache [http://github.com/newbamboo/api_cache]
14
+ # and Ruby On Rails's caching [http://api.rubyonrails.org/classes/ActiveSupport/Cache.html]
15
+ #
16
+ # Author: Karel Minarik [www.karmi.cz]
17
+ #
18
+ # === Notes
19
+ #
20
+ # Thanks to Amit Chakradeo to point out objects have to be stored marhalled on FS
21
+ # Thanks to Marlin Forbes to point out query parameters have to be include in the cache key
22
+ #
23
+ #
24
+
25
+ require 'logger'
26
+ require 'fileutils'
27
+ require 'tmpdir'
28
+ require 'pathname'
29
+ require 'digest/md5'
30
+
31
+ module HTTParty #:nodoc:
32
+ module Icebox
33
+
34
+ module ClassMethods
35
+
36
+ # Enable caching and set cache options
37
+ # Returns memoized cache object
38
+ #
39
+ # Following options are available, default values are in []:
40
+ #
41
+ # +store+:: Storage mechanism for cached data (memory, filesystem, your own) [memory]
42
+ # +timeout+:: Cache expiration in seconds [60]
43
+ # +logger+:: Path to logfile or logger instance [STDOUT]
44
+ #
45
+ # Any additional options are passed to the Cache constructor
46
+ #
47
+ # Usage:
48
+ #
49
+ # # Enable caching in HTTParty, in memory, for 1 minute
50
+ # cache # Use default values
51
+ #
52
+ # # Enable caching in HTTParty, on filesystem (/tmp), for 10 minutes
53
+ # cache :store => 'file', :timeout => 600, :location => '/tmp/'
54
+ #
55
+ # # Use your own cache store (see AbstractStore class below)
56
+ # cache :store => 'memcached', :timeout => 600, :server => '192.168.1.1:1001'
57
+ #
58
+ def cache(options={})
59
+ options[:store] ||= 'memory'
60
+ options[:timeout] ||= 60
61
+ logger = options[:logger]
62
+ @cache ||= Cache.new( options.delete(:store), options )
63
+ end
64
+
65
+ end
66
+
67
+ # When included, extend class with +cache+ method
68
+ # and redefine +get+ method to use cache
69
+ #
70
+ def self.included(receiver) #:nodoc:
71
+ receiver.extend ClassMethods
72
+ receiver.class_eval do
73
+
74
+ # Get reponse from network
75
+ # TODO: Why alias :new :old is not working here? Returns NoMethodError
76
+ #
77
+ def self.get_without_caching(path, options={})
78
+ perform_request Net::HTTP::Get, path, options
79
+ end
80
+
81
+ # Get response from cache, if available
82
+ #
83
+ def self.get_with_caching(path, options={})
84
+ key = path.clone
85
+ key << options[:query].to_s if defined? options[:query]
86
+
87
+ if cache.exists?(key) and not cache.stale?(key)
88
+ Cache.logger.debug "CACHE -- GET #{path}#{options[:query]}"
89
+ return cache.get(key)
90
+ else
91
+ Cache.logger.debug "/!\\ NETWORK -- GET #{path}#{options[:query]}"
92
+ response = get_without_caching(path, options)
93
+ cache.set(key, response) if response.code == 200
94
+ return response
95
+ end
96
+ end
97
+
98
+ # Redefine original HTTParty +get+ method to use cache
99
+ #
100
+ def self.get(path, options={})
101
+ self.get_with_caching(path, options)
102
+ end
103
+
104
+ end
105
+ end
106
+
107
+ # === Cache container
108
+ #
109
+ # Pass a store name ('memory', etc) to initializer
110
+ #
111
+ class Cache
112
+ attr_accessor :store
113
+
114
+ def initialize(store, options={})
115
+ self.class.logger = options[:logger]
116
+ @store = self.class.lookup_store(store).new(options)
117
+ end
118
+
119
+ def get(key); @store.get encode(key) unless stale?(key); end
120
+ def set(key, value); @store.set encode(key), value; end
121
+ def exists?(key); @store.exists? encode(key); end
122
+ def stale?(key); @store.stale? encode(key); end
123
+
124
+ def self.logger; @logger || default_logger; end
125
+ def self.default_logger; logger = ::Logger.new(STDERR); end
126
+
127
+ # Pass a filename (String), IO object, Logger instance or +nil+ to silence the logger
128
+ def self.logger=(device); @logger = device.kind_of?(::Logger) ? device : ::Logger.new(device); end
129
+
130
+ private
131
+
132
+ # Return store class based on passed name
133
+ def self.lookup_store(name)
134
+ store_name = "#{name.capitalize}Store"
135
+ return Store::const_get(store_name)
136
+ rescue NameError => e
137
+ raise Store::StoreNotFound, "The cache store '#{store_name}' was not found. Did you loaded any such class?"
138
+ end
139
+
140
+ def encode(key); Digest::MD5.hexdigest(key); end
141
+ end
142
+
143
+
144
+ # === Cache stores
145
+ #
146
+ module Store
147
+
148
+ class StoreNotFound < StandardError; end #:nodoc:
149
+
150
+ # ==== Abstract Store
151
+ # Inherit your store from this class
152
+ # *IMPORTANT*: Do not forget to call +super+ in your +initialize+ method!
153
+ #
154
+ class AbstractStore
155
+ def initialize(options={})
156
+ raise ArgumentError, "You need to set the :timeout parameter" unless options[:timeout]
157
+ @timeout = options[:timeout]
158
+ message = "Cache: Using #{self.class.to_s.split('::').last}"
159
+ message << " in location: #{options[:location]}" if options[:location]
160
+ message << " with timeout #{options[:timeout]} sec"
161
+ Cache.logger.info message unless options[:logger].nil?
162
+ return self
163
+ end
164
+ %w{set get exists? stale?}.each do |method_name|
165
+ define_method(method_name) { raise NoMethodError, "Please implement method set in your store class" }
166
+ end
167
+ end
168
+
169
+ # ===== Store objects in memory
170
+ #
171
+ Struct.new("TvdbResponse", :code, :body, :headers) { def to_s; self.body; end }
172
+ class MemoryStore < AbstractStore
173
+ def initialize(options={})
174
+ super; @store = {}; self
175
+ end
176
+ def set(key, value)
177
+ Cache.logger.info("Cache: set (#{key})")
178
+ @store[key] = [Time.now, value]; true
179
+ end
180
+ def get(key)
181
+ data = @store[key][1]
182
+ Cache.logger.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
183
+ data
184
+ end
185
+ def exists?(key)
186
+ !@store[key].nil?
187
+ end
188
+ def stale?(key)
189
+ return true unless exists?(key)
190
+ Time.now - created(key) > @timeout
191
+ end
192
+ private
193
+ def created(key)
194
+ @store[key][0]
195
+ end
196
+ end
197
+
198
+ # ===== Store objects on the filesystem
199
+ #
200
+ class FileStore < AbstractStore
201
+ def initialize(options={})
202
+ super
203
+ options[:location] ||= Dir::tmpdir
204
+ @path = Pathname.new( options[:location] )
205
+ FileUtils.mkdir_p( @path )
206
+ self
207
+ end
208
+ def set(key, value)
209
+ Cache.logger.info("Cache: set (#{key})")
210
+ File.open( @path.join(key), 'w' ) { |file| file << Marshal.dump(value) }
211
+ true
212
+ end
213
+ def get(key)
214
+ data = Marshal.load(File.read( @path.join(key)))
215
+ Cache.logger.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
216
+ data
217
+ end
218
+ def exists?(key)
219
+ File.exists?( @path.join(key) )
220
+ end
221
+ def stale?(key)
222
+ return true unless exists?(key)
223
+ Time.now - created(key) > @timeout
224
+ end
225
+ private
226
+ def created(key)
227
+ File.mtime( @path.join(key) )
228
+ end
229
+ end
230
+ end
231
+
232
+ end
233
+ end
234
+
235
+
236
+ # Major parts of this code are based on architecture of ApiCache.
237
+ # Copyright (c) 2008 Martyn Loughran
238
+ #
239
+ # Other parts are inspired by the ActiveSupport::Cache in Ruby On Rails.
240
+ # Copyright (c) 2005-2009 David Heinemeier Hansson
241
+ #
242
+ # Permission is hereby granted, free of charge, to any person obtaining
243
+ # a copy of this software and associated documentation files (the
244
+ # "Software"), to deal in the Software without restriction, including
245
+ # without limitation the rights to use, copy, modify, merge, publish,
246
+ # distribute, sublicense, and/or sell copies of the Software, and to
247
+ # permit persons to whom the Software is furnished to do so, subject to
248
+ # the following conditions:
249
+ #
250
+ # The above copyright notice and this permission notice shall be
251
+ # included in all copies or substantial portions of the Software.
252
+ #
253
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
254
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
255
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
256
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
257
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
258
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
259
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ module ImdbParty
2
+ class Imdb
3
+ include HTTParty
4
+ include HTTParty::Icebox
5
+ cache :store => 'file', :timeout => 120, :location => Dir.tmpdir
6
+
7
+ base_uri 'app.imdb.com'
8
+ default_params = {"api" => "v1", "appid" => "iphone1", "locale" => "en_US", "timestamp" => Time.now.to_i, "sig" => "heres my signature"}
9
+
10
+
11
+ def find_by_title(title)
12
+ movie_results = []
13
+ results = self.class.get('/find', :query => {:q => title}).parsed_response
14
+
15
+ if results["data"] && results["data"]["results"]
16
+ results["data"]["results"].each do |result_section|
17
+ result_section["list"].each do |r|
18
+ next unless r["tconst"] && r["title"]
19
+ h = {:title => r["title"], :year => r["year"], :imdb_id => r["tconst"]}
20
+ h.merge!(:poster_url => r["image"]["url"]) if r["image"] && r["image"]["url"]
21
+ movie_results << h
22
+ end
23
+ end
24
+ end
25
+
26
+ movie_results
27
+ end
28
+
29
+ def find_movie_by_id(imdb_id)
30
+ result = self.class.get('/title/maindetails', :query => {:tconst => imdb_id}).parsed_response
31
+ Movie.new(result["data"]) unless result["data"].nil?
32
+ end
33
+
34
+ def top_250
35
+ results = self.class.get('/chart/top').parsed_response
36
+ results["data"]["list"]["list"].map { |r| {:title => r["title"], :imdb_id => r["tconst"], :year => r["year"], :poster_url => (r["image"] ? r["image"]["url"] : nil)} }
37
+ end
38
+
39
+ def popular_shows
40
+ results = self.class.get('/chart/tv').parsed_response
41
+ results["data"]["list"].map { |r| {:title => r["title"], :imdb_id => r["tconst"], :year => r["year"], :poster_url => (r["image"] ? r["image"]["url"] : nil)} }
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,51 @@
1
+ module ImdbParty
2
+ class Movie
3
+ attr_accessor :imdb_id, :title, :directors, :writers, :tagline, :company, :plot, :runtime, :rating, :poster_url, :release_date, :certification, :genres, :actors, :trailers
4
+
5
+ def initialize(options={})
6
+ if not options.nil? and options.keys.first.class == Symbol
7
+ options.keys.each { |name| instance_variable_set "@" + name.to_s, options[name] }; return
8
+ end
9
+
10
+ @imdb_id = options["tconst"]
11
+ @title = options["title"]
12
+ @tagline = options["tagline"]
13
+ @plot = options["plot"]["outline"] if options["plot"]
14
+ @runtime = "#{(options["runtime"]["time"]/60).to_i} min" if options["runtime"]
15
+ @rating = options["rating"]
16
+ @poster_url = options["image"]["url"] if options["image"]
17
+ @release_date = options["release_date"]["normal"] if options["release_date"] && options["release_date"]["normal"]
18
+ @certification = options["certificate"]["certificate"] if options["certificate"] && options["certificate"]["certificate"]
19
+ @genres = options["genres"] || []
20
+
21
+ # Sometimes the @release_date object is a string instead of a date object
22
+ if @release_date.class == String
23
+ [{:regex => /^\d{4}-\d{2}$/, :concat => "-01"}, {:regex => /^\d{4}$/, :concat => "-01-01"}].each do |value|
24
+ if @release_date.match(value[:regex])
25
+ @release_date = Date.parse(@release_date << value[:concat]); break
26
+ end
27
+ end
28
+ end
29
+
30
+ # parse directors
31
+ @directors = options["directors_summary"] ? options["directors_summary"].map { |d| Person.new(d) } : []
32
+
33
+ # parse directors
34
+ @writers = []
35
+ @writers = options["writers_summary"] ? options["writers_summary"].map { |w| Person.new(w) } : []
36
+
37
+ # parse actors
38
+ @actors = []
39
+ @actors = options["cast_summary"] ? options["cast_summary"].map { |a| Person.new(a) } : []
40
+
41
+ #parse trailers
42
+ @trailers = {}
43
+ if options["trailer"] && options["trailer"]["encodings"]
44
+ options["trailer"]["encodings"].each_pair do |k,v|
45
+ @trailers[v["format"]] = v["url"]
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,13 @@
1
+ module ImdbParty
2
+ class Person
3
+ attr_accessor :name, :role, :imdb_id
4
+
5
+ def initialize(options={})
6
+ @name = options["name"]["name"]
7
+ @imdb_id = options["name"]["nconst"]
8
+
9
+ @role = options["char"] if options["char"]
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,63 @@
1
+ require "#{File.dirname(__FILE__)}/../imdb_party.rb"
2
+ require 'levenshtein'
3
+
4
+ class MovieSearcher
5
+ def initialize(args)
6
+ args.keys.each { |name| instance_variable_set "@" + name.to_s, args[name] unless name == :options }
7
+
8
+ @options = {
9
+ :long => 15,
10
+ :split => /\s+|\./,
11
+ :imdb => ImdbParty::Imdb.new,
12
+ :limit => 0.4
13
+ }
14
+
15
+ @options.merge!(args[:options]) unless args[:options].nil?
16
+ end
17
+
18
+ def self.find_by_release_name(search_value, options = {})
19
+ this = MovieSearcher.new(options.merge(:search_value => search_value.to_s))
20
+ return if this.to_long?
21
+
22
+ return this.find_the_movie!
23
+ end
24
+
25
+ def to_long?
26
+ @split = @search_value.split(@options[:split])
27
+ @split.length > @options[:long]
28
+ end
29
+
30
+ def find_the_movie!
31
+ current = @split.length
32
+
33
+ until current <= 0 do
34
+ title = @split.take(current).join(' ')
35
+ movies = @options[:imdb].find_by_title(title)
36
+ break if movies.any?
37
+ current -= 1
38
+ end
39
+
40
+ return if movies.nil? or not movies.any?
41
+
42
+ # Cleaning up the title, no years allowed
43
+ title = title.gsub(/[^a-z0-9]/i, '').gsub(/(19|20)\d{2}/, '')
44
+
45
+ movie = movies.map do |movie|
46
+ [movie, Levenshtein.normalized_distance(movie[:title].gsub(/[^a-z0-9]/i, ''), title, @options[:limit])]
47
+ end.reject do |value|
48
+ value.last.nil?
49
+ end.sort_by do |_,value|
50
+ value
51
+ end.first
52
+
53
+ return if movie.nil?
54
+
55
+ return ImdbParty::Movie.new(movie.first)
56
+ end
57
+
58
+ def self.method_missing(m, *args, &block)
59
+ result = ImdbParty::Imdb.new.send(m, *args)
60
+ return if result.nil?
61
+ result.class == Array ? result.map{|r| ImdbParty::Movie.new(r)} : result
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "movie_searcher"
6
+ s.version = "0.0.1"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Linus Oleander", "Jon Maddox"]
9
+ s.email = ["linus@oleander.nu", "jon@mustacheinc.com"]
10
+ s.homepage = "https://github.com/oleander/Undertexter"
11
+ s.summary = %q{IMDB client using the IMDB API that their iPhone app uses}
12
+ s.description = %q{IMDB client using the IMDB API that their iPhone app uses. It can also figure out what movie you are looking for just by looking at the release name of the movie}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_dependency('httparty')
20
+ s.add_dependency('levenshtein')
21
+ s.add_development_dependency('rspec')
22
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ describe MovieSearcher do
4
+ it "should only contain should instances of {ImdbParty::Movie}" do
5
+ MovieSearcher.find_by_title("The Dark Knight").each{|movie| movie.should be_instance_of(ImdbParty::Movie)}
6
+ end
7
+
8
+ it "should only contain one instance of {ImdbParty::Movie}" do
9
+ MovieSearcher.find_movie_by_id("tt0468569").should be_instance_of(ImdbParty::Movie)
10
+ end
11
+
12
+ it "should return nil if no movie is found" do
13
+ MovieSearcher.find_movie_by_id("tt23223423423").should be_nil
14
+ end
15
+
16
+ it "should return nil if the movie title is to long" do
17
+ MovieSearcher.find_by_release_name("asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd").should be_nil
18
+ end
19
+
20
+ it "should return nil when setting the limit to low" do
21
+ MovieSearcher.find_by_release_name('Paranormal Activity 2 2010 UNRATED DVDRip XviD-Larceny', :options => {:limit => 0}).should be_nil
22
+ end
23
+
24
+ it "should return the right movie" do
25
+ [{
26
+ :title => "Live Free Or Die Hard 2007 DVDRIP XviD-CRNTV", :iid => "tt0337978"
27
+ }, {
28
+ :title => "The Chronicles of Narnia - The Voyage of the Dawn Treader TS XViD - FLAWL3SS", :iid => "tt0980970"
29
+ }, {
30
+ :title => "Heartbreaker 2010 LIMITED DVDRip XviD-SUBMERGE", :iid => "tt1465487"
31
+ },{
32
+ :title => "Paranormal Activity 2 2010 UNRATED DVDRip XviD-Larceny", :iid => "tt1536044"
33
+ }].each do |movie|
34
+ MovieSearcher.find_by_release_name(movie[:title]).imdb_id.should eq(movie[:iid])
35
+ end
36
+ end
37
+
38
+ it "should return nil if no value is being passed to it" do
39
+ MovieSearcher.find_by_release_name("").should be_nil
40
+ end
41
+
42
+ it "should return nil if nil is being passed to it" do
43
+ MovieSearcher.find_by_release_name(nil).should be_nil
44
+ end
45
+ end
46
+
47
+ describe MovieSearcher, "should work as before" do
48
+ before(:all) do
49
+ @movie = MovieSearcher.find_movie_by_id("tt0382932")
50
+ end
51
+
52
+ it "have a title" do
53
+ @movie.title.should eq("Ratatouille")
54
+ end
55
+
56
+ it "have an imdb_id" do
57
+ @movie.imdb_id.should eq("tt0382932")
58
+ end
59
+
60
+ it "have a tagline" do
61
+ @movie.tagline.should eq("Dinner is served... Summer 2007")
62
+ end
63
+
64
+ it "have a plot" do
65
+ @movie.plot.should eq("Remy is a young rat in the French countryside who arrives in Paris, only to find out that his cooking idol is dead. When he makes an unusual alliance with a restaurant's new garbage boy, the culinary and personal adventures begin despite Remy's family's skepticism and the rat-hating world of humans.")
66
+ end
67
+
68
+ it "have a runtime" do
69
+ @movie.runtime.should eq("111 min")
70
+ end
71
+
72
+ it "have a rating" do
73
+ @movie.rating.should eq(8.1)
74
+ end
75
+
76
+ it "have a poster_url" do
77
+ @movie.poster_url.should match(/http:\/\/ia.media-imdb.com\/images\/.*/)
78
+ end
79
+
80
+ it "have a release date" do
81
+ @movie.release_date.should eq(DateTime.parse("2007-06-29"))
82
+ end
83
+
84
+ it "have a certification" do
85
+ @movie.certification.should eq("G")
86
+ end
87
+
88
+ it "have trailers" do
89
+ @movie.trailers.should be_instance_of(Hash)
90
+ @movie.should have(4).trailers
91
+ @movie.trailers[@movie.trailers.keys.first].should eq("http://www.totaleclips.com/Player/Bounce.aspx?eclipid=e27826&bitrateid=461&vendorid=102&type=.mp4")
92
+ end
93
+
94
+ it "have genres" do
95
+ @movie.genres.should be_instance_of(Array)
96
+ @movie.should have(4).genres
97
+ end
98
+
99
+ it "have actors" do
100
+ @movie.actors.should be_instance_of(Array)
101
+ @movie.should have(4).actors
102
+ end
103
+
104
+ it "have directors" do
105
+ @movie.directors.should be_instance_of(Array)
106
+ @movie.should have(2).directors
107
+ end
108
+
109
+ it "have writers" do
110
+ @movie.writers.should be_instance_of(Array)
111
+ @movie.should have(2).writers
112
+ end
113
+
114
+ it "always return a date object when requesting the release date" do
115
+ [{:imdb => "tt0066026", :date => "1970-03-01"}, {:imdb => "tt1446072", :date => "2010-11-27"}].each do |value|
116
+ movie = MovieSearcher.find_movie_by_id(value[:imdb])
117
+ movie.release_date.strftime('%Y %m %d').should eq(DateTime.parse(value[:date]).strftime('%Y %m %d'))
118
+ movie.release_date.should be_instance_of(Date)
119
+ end
120
+ end
121
+ end
122
+
123
+ describe MovieSearcher, "should also work as before" do
124
+ it "should have at least 15 results" do
125
+ MovieSearcher.find_by_title("ratatouille").count.should >= 15
126
+ end
127
+
128
+ it "search for title with spaces in the name" do
129
+ MovieSearcher.find_by_title("the truman show").count.should >= 1
130
+ end
131
+
132
+ it "search for bad title with no results" do
133
+ MovieSearcher.find_by_title("sdkljlkkl123j4lk23kl3").count.should eq(0)
134
+ end
135
+
136
+ it "find movie by id" do
137
+ MovieSearcher.find_movie_by_id("tt0382932").should be_instance_of(ImdbParty::Movie)
138
+ end
139
+
140
+ it "be an Array of {ImdbParty::Movie}" do
141
+ MovieSearcher.top_250.should be_instance_of(Array)
142
+ MovieSearcher.top_250.first.should be_instance_of(ImdbParty::Movie)
143
+ end
144
+
145
+ it "find popular shows" do
146
+ MovieSearcher.popular_shows.should be_instance_of(Array)
147
+ MovieSearcher.popular_shows.first.should be_instance_of(ImdbParty::Movie)
148
+ end
149
+ end
150
+
151
+ describe MovieSearcher, "should still have the same people" do
152
+ before(:all) do
153
+ @movie = MovieSearcher.find_movie_by_id("tt0382932")
154
+ end
155
+
156
+ it "have a name" do
157
+ @movie.actors.map(&:name).should include('Patton Oswalt')
158
+ end
159
+
160
+ it "have a role" do
161
+ @movie.actors.map(&:role).should include('Remy')
162
+ end
163
+
164
+ it "have a name" do
165
+ @movie.directors.map(&:name).should include('Brad Bird')
166
+ end
167
+
168
+ it "not have a role" do
169
+ @movie.directors.first.role.should be_nil
170
+ end
171
+
172
+ it "have a name" do
173
+ @movie.writers.map(&:name).should include('Brad Bird')
174
+ end
175
+
176
+ it "not have a role" do
177
+ @movie.writers.first.role.should be_nil
178
+ end
179
+ end
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+ require "#{File.dirname(__FILE__)}/../lib/movie_searcher/movie_searcher.rb"
3
+
4
+ RSpec.configure do |config|
5
+ config.mock_with :rspec
6
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: movie_searcher
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Linus Oleander
14
+ - Jon Maddox
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-01-25 00:00:00 +01:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: httparty
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: levenshtein
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :development
63
+ version_requirements: *id003
64
+ description: IMDB client using the IMDB API that their iPhone app uses. It can also figure out what movie you are looking for just by looking at the release name of the movie
65
+ email:
66
+ - linus@oleander.nu
67
+ - jon@mustacheinc.com
68
+ executables: []
69
+
70
+ extensions: []
71
+
72
+ extra_rdoc_files: []
73
+
74
+ files:
75
+ - .bundle/config
76
+ - .document
77
+ - .gitignore
78
+ - .rspec
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - LICENSE
82
+ - README.md
83
+ - Rakefile
84
+ - lib/imdb_party.rb
85
+ - lib/imdb_party/httparty_icebox.rb
86
+ - lib/imdb_party/imdb.rb
87
+ - lib/imdb_party/movie.rb
88
+ - lib/imdb_party/person.rb
89
+ - lib/movie_searcher/movie_searcher.rb
90
+ - movie_searcher.gemspec
91
+ - spec/movie_searcher_spec.rb
92
+ - spec/spec_helper.rb
93
+ has_rdoc: true
94
+ homepage: https://github.com/oleander/Undertexter
95
+ licenses: []
96
+
97
+ post_install_message:
98
+ rdoc_options: []
99
+
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ hash: 3
117
+ segments:
118
+ - 0
119
+ version: "0"
120
+ requirements: []
121
+
122
+ rubyforge_project:
123
+ rubygems_version: 1.3.7
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: IMDB client using the IMDB API that their iPhone app uses
127
+ test_files:
128
+ - spec/movie_searcher_spec.rb
129
+ - spec/spec_helper.rb