picturehouse_uk 1.0.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.
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