picturehouse_uk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/.gitignore +17 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +64 -0
  6. data/Rakefile +20 -0
  7. data/lib/picturehouse_uk.rb +16 -0
  8. data/lib/picturehouse_uk/cinema.rb +129 -0
  9. data/lib/picturehouse_uk/film.rb +52 -0
  10. data/lib/picturehouse_uk/internal/film_with_screenings_parser.rb +48 -0
  11. data/lib/picturehouse_uk/screening.rb +33 -0
  12. data/lib/picturehouse_uk/version.rb +3 -0
  13. data/picturehouse_uk.gemspec +29 -0
  14. data/test/fixtures/dukes-at-komedia-cinema.html +7148 -0
  15. data/test/fixtures/film_node/blue-jasmine-done.html +53 -0
  16. data/test/fixtures/film_node/blue-jasmine-future.html +55 -0
  17. data/test/fixtures/film_node/bolshoi-spartacus.html +26 -0
  18. data/test/fixtures/film_node/captain-phillips-with-silver-screen-and-subtitles.html +103 -0
  19. data/test/fixtures/film_node/fifth-estate-with-big-scream.html +73 -0
  20. data/test/fixtures/film_node/london-film-festival-with-toddler-time.html +46 -0
  21. data/test/fixtures/film_node/met-encore-rusalka-as-live.html +26 -0
  22. data/test/fixtures/film_node/nt-encore-hamlet.html +26 -0
  23. data/test/fixtures/film_node/planes-with-kids-club.html +77 -0
  24. data/test/fixtures/film_node/royal-opera-house-don-quixote.html +26 -0
  25. data/test/fixtures/film_node/rsc-encore-richard-ii.html +28 -0
  26. data/test/fixtures/film_node/rsc-live-richard-ii.html +41 -0
  27. data/test/fixtures/film_node/rsc-live-the-two-gentlemen-of-verona-zero-cert.html +19 -0
  28. data/test/fixtures/picturehouses-homepage.html +1454 -0
  29. data/test/lib/picturehouse_uk/cinema_test.rb +129 -0
  30. data/test/lib/picturehouse_uk/film_test.rb +92 -0
  31. data/test/lib/picturehouse_uk/internal/film_with_screenings_parser_test.rb +185 -0
  32. data/test/lib/picturehouse_uk/screening_test.rb +32 -0
  33. data/test/lib/picturehouse_uk/version_test.rb +7 -0
  34. data/test/test_helper.rb +6 -0
  35. metadata +213 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in picturehouse_uk.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Andy Croll
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # PicturehouseUk
2
+
3
+ A simple gem to parse the [Picturehouse Cinemas UK website](http://picturehouses.co.uk) and spit out useful formatted info.
4
+
5
+ [![Code Climate](https://codeclimate.com/github/andycroll/picturehouse_uk.png)](https://codeclimate.com/github/andycroll/picturehouse_uk)
6
+ [![Build Status](https://travis-ci.org/andycroll/picturehouse_uk.png?branch=master)](https://travis-ci.org/andycroll/picturehouse_uk)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'picturehouse_uk'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install picturehouse_uk
21
+
22
+ ## Usage
23
+
24
+ ### Cinema
25
+
26
+ ``` ruby
27
+ PicturehouseUK::Cinema.all
28
+ #=> [<PicturehouseUK::Cinema brand="Picturehouse" name="Duke's At Komedia" slug="dukes-at-komedia" chain_id="Dukes_At_Komedia" url="...">, #=> <PicturehouseUK::Cinema brand="Picturehouse" name="Duke o York's" slug="duke-of-yorks" chain_id="Duke_Of_Yorks" url="...">, ...]
29
+
30
+ PicturehouseUK::Cinema.find_by_id('Duke_Of_Yorks')
31
+ #=> <PicturehouseUK::Cinema brand="Picturehouse" name="Duke_Of_Yorks" slug="duke-of-yorks" address="..." chain_id="Duke_Of_Yorks" url="...">
32
+
33
+ cinema = PicturehouseUK::Cinema.find_by_slug('duke-of-yorks')
34
+ #=> <PicturehouseUK::Cinema brand="Picturehouse" name="Duke_Of_Yorks" slug="duke-of-yorks" address="..." chain_id="Duke_Of_Yorks" url="...">
35
+
36
+ cinema.brand
37
+ #=> 'Picturehouse'
38
+
39
+ cinema.chain_id
40
+ #=> 'Duke_Of_Yorks'
41
+
42
+ cinema.url
43
+ #=> "http://www.picturehouses.co.uk/cinema/Duke_Of_Yorks/"
44
+
45
+ cinema.films
46
+ #=> [<PicturehouseUK::Film name="Iron Man 3">, <PicturehouseUK::Film name="Star Trek: Into Darkness">]
47
+
48
+ cinema.screenings
49
+ #=> [<PicturehouseUK::Screening film="About Time" when="2013-09-09 11:00 UTC" varient="3d">, <PicturehouseUK::Screening film="Iron Man 3" when="2013-09-09 13:50 UTC" varient="kids">, <PicturehouseUK::Screening ..>, <PicturehouseUK::Screening ...>]
50
+
51
+ cinema.screenings_of 'Iron Man 3'
52
+ #=> [<PicturehouseUK::Screening film="Iron Man 3" when="2013-09-09 11:00 UTC" varient="3d">, <PicturehouseUK::Screening film="Iron Man 3" when="2013-09-09 13:50 UTC" varient="kids">]
53
+
54
+ cinema.screenings_of <PicturehouseUK::Film name="Iron Man 3">
55
+ #=> [<PicturehouseUK::Screening film="Iron Man 3" when="2013-09-09 11:00 UTC" varient="3d">, <PicturehouseUK::Screening film="Iron Man 3" when="2013-09-09 13:50 UTC" varient="kids">]
56
+ ```
57
+
58
+ ## Contributing
59
+
60
+ 1. Fork it
61
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
62
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
63
+ 4. Push to the branch (`git push origin my-new-feature`)
64
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'lib/picturehouse_uk'
8
+ t.test_files = FileList['test/lib/picturehouse_uk/*_test.rb', 'test/lib/picturehouse_uk/internal/*_test.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :build do
13
+ system "gem build picturehouse_uk.gemspec"
14
+ end
15
+
16
+ task :release => :build do
17
+ system "gem push picturehouse_uk-#{PicturehouseUk::VERSION}"
18
+ end
19
+
20
+ task :default => :test
@@ -0,0 +1,16 @@
1
+ require 'httparty'
2
+ require 'nokogiri'
3
+ require 'tzinfo'
4
+ require 'tzinfo/data'
5
+
6
+ require_relative './picturehouse_uk/version'
7
+
8
+ require_relative './picturehouse_uk/internal/film_with_screenings_parser'
9
+
10
+ require_relative './picturehouse_uk/cinema'
11
+ require_relative './picturehouse_uk/film'
12
+ require_relative './picturehouse_uk/screening'
13
+
14
+ module PicturehouseUk
15
+ # Your code goes here...
16
+ end
@@ -0,0 +1,129 @@
1
+ module PicturehouseUk
2
+
3
+ # Public: The object representing a cinema on the Picturehouse UK website
4
+ class Cinema
5
+
6
+ # Public: Returns the String brand of the cinema #=> 'Picturehouse'
7
+ attr_reader :brand
8
+ # Public: Returns the String id of the cinema on the Picturehouse website
9
+ attr_reader :id
10
+ # Public: Returns the String name of the cinema
11
+ attr_reader :name
12
+ # Public: Returns the String slug of the cinema
13
+ attr_reader :slug
14
+ # Public: Returns the String url of the cinema's page on picturehouses.co.uk
15
+ attr_reader :url
16
+
17
+ # Public: Initialize a cinema
18
+ #
19
+ # id - String of the cinema on the picturehouse website
20
+ # name - String of cinema name
21
+ # url - String of cinema url on the picturehouse website
22
+ def initialize(id, name, url)
23
+ @brand = 'Picturehouse'
24
+ @id = id
25
+ @name = name
26
+ @slug = name.downcase.gsub(/[^0-9a-z ]/,'').gsub(/\s+/, '-')
27
+ @url = (url[0] == '/') ? "http://www.picturehouses.co.uk#{url}" : url
28
+ end
29
+
30
+ # Public: Return basic cinema information for all Picturehouse cinemas
31
+ #
32
+ # Examples
33
+ #
34
+ # PicturehouseUk::Cinema.all
35
+ # # => [<PicturehouseUK::Cinema brand="Picturehouse" name="Duke's At Komedia" slug="dukes-at-komedia" id="Dukes_At_Komedia" url="...">, #=> <PicturehouseUK::Cinema brand="Picturehouse" name="Duke o York's" slug="duke-of-yorks" id="Duke_Of_Yorks" url="...">, ...]
36
+ #
37
+ # Returns an array of hashes of cinema information.
38
+ def self.all
39
+ cinema_links.map do |link|
40
+ new_from_link link
41
+ end
42
+ end
43
+
44
+ # Public: Return single cinema information for an Odeon cinema
45
+ #
46
+ # string - a string representing the cinema id
47
+ #
48
+ # Examples
49
+ #
50
+ # PicturehouseUk::Cinema.find('Dukes_At_Komedia')
51
+ # # => <PicturehouseUK::Cinema brand="Picturehouse" name="Duke's At Komedia" slug="dukes-at-komedia" id="Dukes_At_Komedia" url="...">
52
+ #
53
+ # Returns an PicturehouseUk::Cinema or nil if none was found
54
+ def self.find(id)
55
+ all.select { |cinema| cinema.id == id }[0]
56
+ end
57
+
58
+ # Public: Returns films for an Picturehouse cinema
59
+ #
60
+ # Examples
61
+ #
62
+ # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
63
+ # cinema.films
64
+ # # => [<PicturehouseUk::Film name="Iron Man 3">, <PicturehouseUk::Film name="Star Trek Into Darkness">]
65
+ #
66
+ # Returns an array of PicturehouseUk::Film objects
67
+ def films
68
+ film_nodes.map do |node|
69
+ parser = PicturehouseUk::Internal::FilmWithScreeningsParser.new node.to_s
70
+ PicturehouseUk::Film.new parser.film_name
71
+ end.uniq
72
+ end
73
+
74
+ # Public: Returns screenings for a Picturehouse cinema
75
+ #
76
+ # Examples
77
+ #
78
+ # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
79
+ # cinema.screenings
80
+ # # => [<PicturehouseUk::Screening film_name="Iron Man 3" cinema_name="Duke's At Komedia" when="..." varient="...">, <PicturehouseUk::Screening ...>]
81
+ #
82
+ # Returns an array of Odeon::Screening objects
83
+ def screenings
84
+ film_nodes.map do |node|
85
+ parser = PicturehouseUk::Internal::FilmWithScreeningsParser.new node.to_s
86
+ parser.showings.map do |screening_type, times|
87
+ times.map do |time|
88
+ varient = screening_type == '2d' ? nil : screening_type
89
+ PicturehouseUk::Screening.new parser.film_name, self.name, time, varient
90
+ end
91
+ end
92
+ end.flatten
93
+ end
94
+
95
+ private
96
+
97
+ def self.cinema_links
98
+ parsed_homepage.css('#cinemalisthome .cinemas a')
99
+ end
100
+
101
+ def self.homepage_response
102
+ @homepage_response ||= HTTParty.get('http://www.picturehouses.co.uk/')
103
+ end
104
+
105
+ def self.new_from_link(link)
106
+ url = link.get_attribute('href')
107
+ id = url.match(/\/cinema\/(.+)\/$/)[1]
108
+ name = link.css('span:nth-child(2)').text
109
+ new id, name, url
110
+ end
111
+
112
+ def self.parsed_homepage
113
+ Nokogiri::HTML(homepage_response)
114
+ end
115
+
116
+ def cinema_response
117
+ @cinema_response ||= HTTParty.get(@url)
118
+ end
119
+
120
+ def film_nodes
121
+ parsed_cinema.css('.box8_content .largelist .item')
122
+ end
123
+
124
+ def parsed_cinema
125
+ Nokogiri::HTML(cinema_response)
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,52 @@
1
+ module PicturehouseUk
2
+
3
+ # Public: The object representing a film on the Odeon UK website
4
+ class Film
5
+ include Comparable
6
+
7
+ # Public: Returns the String name of the film
8
+ attr_reader :name
9
+ # Public: Returns the String slug of the film
10
+ attr_reader :slug
11
+
12
+ # Public: Allows sort on objects
13
+ def <=> other
14
+ self.slug <=> other.slug
15
+ end
16
+
17
+ # Public: Check an object is the same as another object.
18
+ #
19
+ # True if both objects are the same exact object, or if they are of the same
20
+ # type and share an equal slug
21
+ #
22
+ # Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
23
+ #
24
+ # object - object to be compared
25
+ #
26
+ # Returns Boolean
27
+ def eql? other
28
+ self.class == other.class && self == other
29
+ end
30
+
31
+ # Public: generates hash of slug in order to allow two records of the same
32
+ # type and id to work with something like:
33
+ #
34
+ # [ Film.new('ABC'), Film.new('DEF') ] & [ Film.new('DEF'), Film.new('GHI') ]
35
+ # #=> [ Film.new('DEF') ]
36
+ #
37
+ # Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
38
+ #
39
+ # Returns an Integer hash of the slug
40
+ def hash
41
+ self.slug.hash
42
+ end
43
+
44
+ # Public: Initialize a screening
45
+ #
46
+ # name - String of the film name
47
+ def initialize(name)
48
+ @name = name
49
+ @slug = name.downcase.gsub(/[^0-9a-z ]/,'').gsub(/\s+/, '-')
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,48 @@
1
+ module PicturehouseUk
2
+ module Internal
3
+ # Private: An object to parse a film HTML snippet
4
+ class FilmWithScreeningsParser
5
+
6
+ def initialize(film_html)
7
+ @nokogiri_html = Nokogiri::HTML(film_html)
8
+ end
9
+
10
+ def film_name
11
+ name = @nokogiri_html.css('.movielink').children.first.to_s
12
+
13
+ # screening types
14
+ name = name.gsub /\s\[(AS LIVE: )?[ACPGU1258]+\]/, '' # remove certificate
15
+ name = name.gsub /\s\[NO CERT\]/, '' # remove no certificate
16
+ name = name.gsub /\s\[\]/, '' # remove no certificate
17
+ name = name.gsub /\s+[23][dD]/, '' # remove 2d or 3d from title
18
+
19
+ # special screenings
20
+ name = name.gsub 'ROH. Live:', 'Royal Opera House:' # fill out Royal Opera House
21
+ name = name.gsub 'Met. Encore:', 'Met Opera:' # fill out Met Opera
22
+ name = name.gsub 'NT Encore:', 'National Theatre:' # National theatre
23
+ name = name.gsub 'RSC Live:', 'Royal Shakespeare Company:' # RSC
24
+ name = name.gsub 'RSC Encore:', 'Royal Shakespeare Company:' # RSC
25
+
26
+ name = name.squeeze(' ') # spaces compressed
27
+ name = name.gsub /\A\s+/, '' # remove leading spaces
28
+ name = name.gsub /\s+\z/, '' # remove trailing spaces
29
+ end
30
+
31
+ def showings
32
+ @nokogiri_html.css('a[epoch]').inject({}) do |result, link|
33
+ key = case link['class']
34
+ when /big_scream/ then 'baby'
35
+ when /kids_club|toddler_time/ then 'kids'
36
+ when /silver_screen/ then 'silver'
37
+ when /subtitled_cinema/ then 'subtitled'
38
+ else '2d'
39
+ end
40
+ # this is a hack because Time.at() only uses local time
41
+ time = Time.utc(1970)+link['epoch'].to_i
42
+
43
+ result.merge(key => (result[key] || []) << time)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,33 @@
1
+ module PicturehouseUk
2
+
3
+ # Public: The object representing a screening of a film on the Picturehouse UK website
4
+ class Screening
5
+
6
+ # Public: Returns the String name of the cinema
7
+ attr_reader :cinema_name
8
+ # Public: Returns the String name of the film
9
+ attr_reader :film_name
10
+ # Public: Returns the Time of the screening
11
+ attr_reader :when
12
+ # Public: Returns the Type of screening (3d, baby, kids, live)
13
+ attr_reader :varient
14
+
15
+ # Public: Initialize a screening
16
+ #
17
+ # film_name - String of the film name
18
+ # cinema_name - String of the cinema name on the Picturehouse website
19
+ # time - DateTime representing the time of the screening, UTC preferred
20
+ # varient - String representing the type of showing (e.g. 3d/baby/live)
21
+ def initialize(film_name, cinema_name, time, varient=nil)
22
+ @cinema_name, @film_name, @varient = cinema_name, film_name, varient
23
+ @when = time.utc? ? time : TZInfo::Timezone.get('Europe/London').local_to_utc(time)
24
+ end
25
+
26
+ # Public: The Date of the screening
27
+ #
28
+ # Returns a Date
29
+ def date
30
+ @when.to_date
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module PicturehouseUk
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'picturehouse_uk/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "picturehouse_uk"
8
+ spec.version = PicturehouseUk::VERSION
9
+ spec.authors = ["Andy Croll"]
10
+ spec.email = ["andy@goodscary.com"]
11
+ spec.description = %q{An API to pull movie information from the picturehouse.co.uk website}
12
+ spec.summary = %q{It's a scraper, but a nice one}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency 'webmock'
24
+
25
+ spec.add_runtime_dependency 'httparty'
26
+ spec.add_runtime_dependency 'nokogiri'
27
+ spec.add_runtime_dependency 'tzinfo'
28
+ spec.add_runtime_dependency 'tzinfo-data'
29
+ end