cineworld_uk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +59 -0
  8. data/Rakefile +15 -0
  9. data/cineworld_uk.gemspec +29 -0
  10. data/lib/cineworld_uk.rb +15 -0
  11. data/lib/cineworld_uk/cinema.rb +218 -0
  12. data/lib/cineworld_uk/film.rb +47 -0
  13. data/lib/cineworld_uk/internal/film_with_screenings_parser.rb +128 -0
  14. data/lib/cineworld_uk/screening.rb +33 -0
  15. data/lib/cineworld_uk/version.rb +6 -0
  16. data/test/fixtures/cinemas.html +420 -0
  17. data/test/fixtures/cinemas/brighton.html +8 -0
  18. data/test/fixtures/cinemas/bristol.html +1206 -0
  19. data/test/fixtures/cinemas/bury-st-edmunds.html +1211 -0
  20. data/test/fixtures/cinemas/chelsea.html +1030 -0
  21. data/test/fixtures/cinemas/the-o2-grenwich.html +1191 -0
  22. data/test/fixtures/whatson/brighton.html +7906 -0
  23. data/test/fixtures/whatson/brighton/autism-friendly-cloudy-2.html +79 -0
  24. data/test/fixtures/whatson/brighton/geethanjali-malayalam.html +71 -0
  25. data/test/fixtures/whatson/brighton/gravity.html +2129 -0
  26. data/test/fixtures/whatson/brighton/take-2-thursday-about-time.html +89 -0
  27. data/test/fixtures/whatson/glasgow-imax-at-gsc-cinema.html +3160 -0
  28. data/test/fixtures/whatson/glasgow-imax-at-gsc/the-hunger-games-catching-fire.html +498 -0
  29. data/test/fixtures/whatson/the-o2-greenwich.html +6854 -0
  30. data/test/fixtures/whatson/the-o2-greenwich/gravity.html +784 -0
  31. data/test/fixtures/whatson/wandsworth.html +13729 -0
  32. data/test/fixtures/whatson/wandsworth/arrambam-tamil.html +126 -0
  33. data/test/fixtures/whatson/wandsworth/bolshoi-ballet-live-lost-illusions.html +80 -0
  34. data/test/fixtures/whatson/wandsworth/frankenstein-nt-50th.html +82 -0
  35. data/test/fixtures/whatson/wandsworth/met-opera-falstaff.html +74 -0
  36. data/test/fixtures/whatson/wandsworth/nt-live-war-horse.html +80 -0
  37. data/test/fixtures/whatson/wandsworth/royal-ballet-live-the-sleeping-beauty.html +79 -0
  38. data/test/fixtures/whatson/wandsworth/royal-opera-live-parsifal-weird-date.html +79 -0
  39. data/test/fixtures/whatson/wandsworth/rsc-live-richard-ii-encore.html +80 -0
  40. data/test/fixtures/whatson/wandsworth/west-end-theatre-series-private-lives.html +80 -0
  41. data/test/lib/cineworld_uk/cinema_test.rb +466 -0
  42. data/test/lib/cineworld_uk/film_test.rb +95 -0
  43. data/test/lib/cineworld_uk/internal/film_with_screenings_parser_test.rb +200 -0
  44. data/test/lib/cineworld_uk/screening_test.rb +31 -0
  45. data/test/lib/cineworld_uk/version_test.rb +9 -0
  46. data/test/test_helper.rb +6 -0
  47. metadata +219 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Yjc5NWQ4NDdkYzUwN2NmMzRkMzBhZGZhNjM4NWRiNDQ1ZmQ0N2JmZQ==
5
+ data.tar.gz: !binary |-
6
+ ZjkxNGU0ODcwNTQyYTcxOWQxZjQ1OGE4MGUxYTc2YTQ2NzNjYzc1NQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MDQyNDYwMTcxYWNiZGU2ZjEzYzZkMzU1YjJkYjQzZDEzNmYyZmIxNjE0ZTFj
10
+ MGRhMzRmZjVkNDY4ZTVlODczMjQ3MWE0NjczZGZlZTgzOWFjOGE4M2FiZjEz
11
+ Y2YzOWNlMzJhMDY5NmM4YzZlMzlkYmZjNmJjNTUzMWI5MGY5Y2U=
12
+ data.tar.gz: !binary |-
13
+ YTU3ZjQ1YjM2N2RkZTI3YTRkYzlhY2UxMDYwOWE1Njg3MjBkNWEyYjU0ZjRj
14
+ ZGY0MWMxZDViOTUyY2NkN2NiYTBhNTVlYWM5MjE1YjcxYjkyOTlhMTQ2OWM3
15
+ NzljMzkyZDVjMDUyZmQ5MmUyZDNiN2MzODc3NDI5ODFhN2YwY2M=
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/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ ## 1.0.0, _6th Dec 2013_
2
+
3
+ First ready-for-public release
4
+
5
+ - Added changelog
6
+ - added booking url to screenings
7
+ - screenings created with UTC Time objects not strings
8
+ - added cinema addresses
9
+ - Get films and screenings out of a cinema
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cineworld_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,59 @@
1
+ # CineworldUk
2
+
3
+ A simple gem to parse the [Cineworld UK website](http://cineworld.co.uk) and spit out useful formatted info.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/cineword_uk.png)](http://badge.fury.io/rb/cineworld_uk)
6
+ [![Code Climate](https://codeclimate.com/github/andycroll/cineworld_uk.png)](https://codeclimate.com/github/andycroll/cineworld_uk)
7
+ [![Build Status](https://travis-ci.org/andycroll/cineworld_uk.png?branch=master)](https://travis-ci.org/andycroll/cineworld_uk)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'cineworld_uk'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install cineworld_uk
22
+
23
+ ## Usage
24
+
25
+ ### Cinema
26
+
27
+ ``` ruby
28
+ CineworldUK::Cinema.all
29
+ #=> [<CineworldUK::Cinema brand="Cineworld" name="Duke's At Komedia" slug="dukes-at-komedia" chain_id="Dukes_At_Komedia" url="...">, #=> <CineworldUK::Cinema brand="Cineworld" name="Duke o York's" slug="duke-of-yorks" chain_id="Brighton" url="...">, ...]
30
+
31
+ CineworldUK::Cinema.find('3')
32
+ #=> <CineworldUK::Cinema brand="Cineworld" name="Brighton" slug="duke-of-yorks" address="..." chain_id="Brighton" url="...">
33
+
34
+ cinema.brand
35
+ #=> 'Cineworld'
36
+
37
+ cinema.id
38
+ #=> '3'
39
+
40
+ cinema.films
41
+ #=> [<CineworldUK::Film name="Iron Man 3">, <CineworldUK::Film name="Star Trek: Into Darkness">]
42
+
43
+ cinema.screenings
44
+ #=> [<CineworldUK::Screening film="About Time" when="2013-09-09 11:00 UTC" varient="3d">, <CineworldUK::Screening film="Iron Man 3" when="2013-09-09 13:50 UTC" varient="kids">, <CineworldUK::Screening ..>, <CineworldUK::Screening ...>]
45
+
46
+ cinema.screenings_of 'Iron Man 3'
47
+ #=> [<CineworldUK::Screening film="Iron Man 3" when="2013-09-09 11:00 UTC" varient="3d">, <CineworldUK::Screening film="Iron Man 3" when="2013-09-09 13:50 UTC" varient="kids">]
48
+
49
+ cinema.screenings_of <CineworldUK::Film name="Iron Man 3">
50
+ #=> [<CineworldUK::Screening film="Iron Man 3" when="2013-09-09 11:00 UTC" varient="3d">, <CineworldUK::Screening film="Iron Man 3" when="2013-09-09 13:50 UTC" varient="kids">]
51
+ ```
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,15 @@
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/cineworld_uk'
8
+ t.test_files = FileList[
9
+ 'test/lib/cineworld_uk/*_test.rb',
10
+ 'test/lib/cineworld_uk/internal/*_test.rb'
11
+ ]
12
+ t.verbose = true
13
+ end
14
+
15
+ task :default => :test
@@ -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 'cineworld_uk/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cineworld_uk"
8
+ spec.version = CineworldUk::VERSION
9
+ spec.authors = ["Andy Croll"]
10
+ spec.email = ["andy@goodscary.com"]
11
+ spec.description = %q{Cineworld cinema data}
12
+ spec.summary = %q{Parses cinema and showing data from the cineworld.co.uk website}
13
+ spec.homepage = "https://github.com/andycroll/cineworld_uk"
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
@@ -0,0 +1,15 @@
1
+ require 'httparty'
2
+ require 'nokogiri'
3
+ require 'tzinfo'
4
+ require 'tzinfo/data'
5
+
6
+ require_relative './cineworld_uk/version'
7
+
8
+ require_relative './cineworld_uk/internal/film_with_screenings_parser'
9
+
10
+ require_relative './cineworld_uk/cinema'
11
+ require_relative './cineworld_uk/film'
12
+ require_relative './cineworld_uk/screening'
13
+
14
+ module CineworldUk
15
+ end
@@ -0,0 +1,218 @@
1
+ module CineworldUk
2
+
3
+ # The object representing a cinema on the Cineworld UK website
4
+ class Cinema
5
+
6
+ # @return [String] the brand of the cinema
7
+ attr_reader :brand
8
+ # @return [Integer] the numeric id of the cinema on the Odeon website
9
+ attr_reader :id
10
+ # @return [String] the name of the cinema
11
+ attr_reader :name
12
+ # @return [String] the slug of the cinema
13
+ attr_reader :slug
14
+ # @return [String] the url of the cinema on the Cineworld website
15
+ attr_reader :url
16
+
17
+ # @param [Integer, String] id cinema id
18
+ # @param [String] name cinema name
19
+ # @return [CineworldUk::Cinema]
20
+ def initialize(id, name)
21
+ @brand = 'Cineworld'
22
+ @id = id.to_i
23
+ @name = name.gsub('London - ','')
24
+ @slug = @name.downcase.gsub(/[^0-9a-z ]/,'').gsub(/\s+/, '-')
25
+ @url = "http://www.cineworld.co.uk/cinemas/#{@id}/information"
26
+ end
27
+
28
+ # Return basic cinema information for all cinemas
29
+ # @return [Array<CineworldUk::Cinema>]
30
+ # @example
31
+ # CineworldUk::Cinema.all
32
+ # #=> [<CineworldUk::Cinema brand="Cineworld" name="Brighton" slug="brighton" id=3 url="...">, <CineworldUk::Cinema brand="Cineworld" name="The O2, Greenwich" slug="the-o2-greenwich" url="...">, ...]
33
+ def self.all
34
+ parsed_cinemas.css('#cinemaId option[value]').map do |option|
35
+ new option['value'], option.text
36
+ end[1..-1]
37
+ end
38
+
39
+ # Find a single cinema
40
+ # @param [Integer, String] id the cinema id of the format 3/'3' as used on the cineworld.co.uk website
41
+ # @return [CineworldUk::Cinema, nil]
42
+ # @example
43
+ # CineworldUk::Cinema.find('3')
44
+ # #=> <CineworldUk::Cinema brand="Cineworld" name="Brighton" slug="brighton" id=3 url="...">
45
+ def self.find(id)
46
+ id = id.to_i
47
+ return nil unless id > 0
48
+
49
+ all.select { |cinema| cinema.id == id }[0]
50
+ end
51
+
52
+ # Address of the cinema
53
+ # @return [Hash] of different address parts
54
+ # @example
55
+ # cinema = CineworldUk::Cinema.find('Dukes_At_Komedia')
56
+ # cinema.adr
57
+ # #=> { street_address: '44-47 Gardner Street', extended_address: 'North Laine', locality: 'Brighton', postal_code: 'BN1 1UN', country_name: 'United Kingdom' }
58
+ # @note Uses the standard method naming as at http://microformats.org/wiki/adr
59
+ def adr
60
+ {
61
+ street_address: street_address,
62
+ extended_address: extended_address,
63
+ locality: locality,
64
+ region: region,
65
+ postal_code: postal_code,
66
+ country: 'United Kingdom'
67
+ }
68
+ end
69
+ alias_method :address, :adr
70
+
71
+ # The second address line of the cinema
72
+ # @return [String, nil]
73
+ # @example
74
+ # cinema = CineworldUk::Cinema.find(10)
75
+ # cinema.extended_address
76
+ # #=> 'Chelsea'
77
+ #
78
+ # cinema = CineworldUk::Cinema.find(3)
79
+ # cinema.extended_address
80
+ # #=> nil
81
+ # @note Uses the standard method naming as at http://microformats.org/wiki/adr
82
+ def extended_address
83
+ remaining_address * ', ' unless remaining_address.empty?
84
+ end
85
+
86
+ # Films with showings scheduled at this cinema
87
+ # @return [Array<OdeonUk::Film>]
88
+ # @example
89
+ # cinema = OdeonUk::Cinema.find('71')
90
+ # cinema.films
91
+ # #=> [<OdeonUk::Film name="Iron Man 3">, <OdeonUk::Film name="Star Trek Into Darkness">]
92
+ def films
93
+ film_nodes.map do |node|
94
+ parser = CineworldUk::Internal::FilmWithScreeningsParser.new node.to_s
95
+ parser.film_name.length > 0 ? CineworldUk::Film.new(parser.film_name) : nil
96
+ end.compact.uniq
97
+ end
98
+
99
+ # The locality (town) of the cinema
100
+ # @return [String]
101
+ # @example
102
+ # cinema = CineworldUk::Cinema.find(3)
103
+ # cinema.locality
104
+ # #=> 'Brighton'
105
+ # @note Uses the standard method naming as at http://microformats.org/wiki/adr
106
+ def locality
107
+ adr_has_region? && !adr_in_london? ? final_address_array[-2] : final_address_array[-1]
108
+ end
109
+
110
+ # Post code of the cinema
111
+ # @return [String]
112
+ # @example
113
+ # cinema = CineworldUk::Cinema.find(3)
114
+ # cinema.postal_code
115
+ # #=> 'BN2 5UF'
116
+ # @note Uses the standard method naming as at http://microformats.org/wiki/adr
117
+ def postal_code
118
+ final_line_array[-2..-1]*' '
119
+ end
120
+
121
+ # The region (county) of the cinema if provided
122
+ # @return [String, nil]
123
+ # @example
124
+ # cinema = CineworldUk::Cinema.find(3)
125
+ # cinema.region
126
+ # #=> 'East Sussex'
127
+ # @note Uses the standard method naming as at http://microformats.org/wiki/adr
128
+ def region
129
+ final_line_non_postal_code if adr_has_region? && !adr_in_london?
130
+ end
131
+
132
+ # All planned screenings
133
+ # @return [Array<CineworldUk::Screening>]
134
+ # @example
135
+ # cinema = CineworldUk::Cinema.find(3)
136
+ # cinema.screenings
137
+ # # => [<CineworldUk::Screening film_name="Iron Man 3" cinema_name="Brighton" when="..." varient="...">, <CineworldUk::Screening ...>]
138
+ def screenings
139
+ film_nodes.map do |node|
140
+ parser = CineworldUk::Internal::FilmWithScreeningsParser.new node.to_s
141
+ parser.showings.map do |screening_type, times|
142
+ times.map do |array|
143
+ CineworldUk::Screening.new parser.film_name, self.name, array[0], array[1], screening_type
144
+ end
145
+ end
146
+ end.flatten
147
+ end
148
+
149
+ # The street adress of the cinema
150
+ # @return a String
151
+ # @example
152
+ # cinema = CineworldUk::Cinema.find(3)
153
+ # cinema.street_address
154
+ # #=> 'Brighton Marina'
155
+ # @note Uses the standard method naming as at http://microformats.org/wiki/adr
156
+ def street_address
157
+ address_parts[0]
158
+ end
159
+
160
+ private
161
+
162
+ def self.parsed_cinemas
163
+ @parsed_cinemas ||= Nokogiri::HTML(cinemas_response)
164
+ end
165
+
166
+ def self.cinemas_response
167
+ @cinemas_response ||= HTTParty.get('http://www.cineworld.co.uk/cinemas')
168
+ end
169
+
170
+ def address_parts
171
+ @address_parts ||= parsed_information.css('address.marker').to_s.split('<br>')[-2].split(',').map(&:strip)
172
+ end
173
+
174
+ def adr_in_london?
175
+ final_line_non_postal_code == 'London'
176
+ end
177
+
178
+ def adr_has_region?
179
+ final_line_non_postal_code != name
180
+ end
181
+
182
+ def film_nodes
183
+ @film_nodes ||= parsed_whatson.css('.section.light #filter-reload > .span9').to_s.split('<hr>')
184
+ end
185
+
186
+ def final_address_array
187
+ address_parts[0..-2] << final_line_non_postal_code
188
+ end
189
+
190
+ def final_line_array
191
+ address_parts[-1].split(' ')
192
+ end
193
+
194
+ def final_line_non_postal_code
195
+ final_line_array[0..-3]*' '
196
+ end
197
+
198
+ def information_response
199
+ @information_response ||= HTTParty.get("http://www.cineworld.co.uk/cinemas/#{@id}/information")
200
+ end
201
+
202
+ def parsed_information
203
+ @parsed_information ||= Nokogiri::HTML(information_response)
204
+ end
205
+
206
+ def parsed_whatson
207
+ @parsed_whatson ||= Nokogiri::HTML(whatson_response)
208
+ end
209
+
210
+ def remaining_address
211
+ final_address_array.delete_if { |e| e == street_address || e == locality || e == region }
212
+ end
213
+
214
+ def whatson_response
215
+ @whatson_response ||= HTTParty.get("http://www.cineworld.co.uk/whatson?cinema=#{@id}")
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,47 @@
1
+ module CineworldUk
2
+
3
+ # The object representing a film on the Cineworld UK website
4
+ class Film
5
+ include Comparable
6
+
7
+ # @return [String] the name of the film
8
+ attr_reader :name
9
+ # @return [String] the normalized slug derived from the film name
10
+ attr_reader :slug
11
+
12
+ # @param [String] name the film name
13
+ # @return [CineworldUk::Film]
14
+ def initialize(name)
15
+ @name = name
16
+ @slug = name.downcase.gsub(/[^0-9a-z ]/,'').gsub(/\s+/, '-')
17
+ end
18
+
19
+ # Allows sort on objects
20
+ # @param [CineworldUk::Film] other another film object
21
+ # @return [Integer] -1, 0 or 1
22
+ def <=> other
23
+ self.slug <=> other.slug
24
+ end
25
+
26
+ # Check an object is the same as another object.
27
+ # @param [CineworldUk::Film] other another film
28
+ # @return [Boolean] True if both objects are the same exact object, or if
29
+ # they are of the same type and share an equal slug
30
+ # @note Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
31
+ def eql? other
32
+ self.class == other.class && self == other
33
+ end
34
+
35
+ # Generates hash of slug in order to allow two records of the same type and
36
+ # id to work with something like:
37
+ #
38
+ # [ Film.new('ABC'), Film.new('DEF') ] & [ Film.new('DEF'), Film.new('GHI') ]
39
+ # #=> [ Film.new('DEF') ]
40
+ #
41
+ # @return [Integer] hash of slug
42
+ # @note Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
43
+ def hash
44
+ self.slug.hash
45
+ end
46
+ end
47
+ end