odeon_uk 2.0.4 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/CHANGELOG.md +6 -0
- data/Rakefile +29 -0
- data/lib/odeon_uk/api/cinema.rb +62 -0
- data/lib/odeon_uk/api/response.rb +55 -0
- data/lib/odeon_uk/api/screenings.rb +89 -0
- data/lib/odeon_uk/cinema.rb +53 -114
- data/lib/odeon_uk/configuration.rb +21 -0
- data/lib/odeon_uk/html/cinema.rb +101 -0
- data/lib/odeon_uk/html/parser/film_with_screenings.rb +160 -0
- data/lib/odeon_uk/html/screenings.rb +38 -0
- data/lib/odeon_uk/{internal → html}/website.rb +1 -1
- data/lib/odeon_uk/internal/title_sanitizer.rb +1 -0
- data/lib/odeon_uk/screening.rb +7 -15
- data/lib/odeon_uk/version.rb +2 -2
- data/lib/odeon_uk.rb +11 -4
- data/odeon_uk.gemspec +2 -0
- data/rake/fixture_creator.rb +98 -0
- data/test/fixtures/api/all_cinemas.plist +0 -0
- data/test/fixtures/api/app_init.plist +0 -0
- data/test/fixtures/api/film_times/71-100747.plist +0 -0
- data/test/fixtures/api/film_times/71-100750.plist +0 -0
- data/test/fixtures/api/film_times/71-100790.plist +0 -0
- data/test/fixtures/api/film_times/71-14646.plist +0 -0
- data/test/fixtures/api/film_times/71-14725.plist +0 -0
- data/test/fixtures/api/film_times/71-15086.plist +0 -0
- data/test/fixtures/api/film_times/71-15096.plist +0 -0
- data/test/fixtures/api/film_times/71-15122.plist +0 -0
- data/test/fixtures/api/film_times/71-15130.plist +0 -0
- data/test/fixtures/api/film_times/71-15141.plist +0 -0
- data/test/fixtures/api/film_times/71-15142.plist +0 -0
- data/test/fixtures/api/film_times/71-15143.plist +0 -0
- data/test/fixtures/api/film_times/71-15144.plist +0 -0
- data/test/fixtures/api/film_times/71-15145.plist +0 -0
- data/test/fixtures/api/film_times/71-15170.plist +0 -0
- data/test/fixtures/api/film_times/71-15172.plist +0 -0
- data/test/fixtures/api/film_times/71-15177.plist +0 -0
- data/test/fixtures/api/film_times/71-15179.plist +0 -0
- data/test/fixtures/api/film_times/71-15182.plist +0 -0
- data/test/fixtures/api/film_times/71-15286.plist +0 -0
- data/test/fixtures/api/film_times/71-15360.plist +0 -0
- data/test/fixtures/api/film_times/71-15586.plist +0 -0
- data/test/fixtures/api/film_times/71-15700.plist +0 -0
- data/test/fixtures/api/film_times/71-15718.plist +0 -0
- data/test/fixtures/api/film_times/71-15768.plist +0 -0
- data/test/fixtures/api/film_times/71-15788.plist +0 -0
- data/test/fixtures/api/film_times/71-15793.plist +0 -0
- data/test/fixtures/api/film_times/71-15794.plist +0 -0
- data/test/fixtures/api/film_times/71-15795.plist +0 -0
- data/test/fixtures/api/film_times/71-15796.plist +0 -0
- data/test/fixtures/api/film_times/71-15799.plist +0 -0
- data/test/fixtures/api/film_times/71-15802.plist +0 -0
- data/test/fixtures/api/film_times/71-15814.plist +0 -0
- data/test/fixtures/api/film_times/71-15817.plist +0 -0
- data/test/fixtures/api/film_times/71-15840.plist +0 -0
- data/test/fixtures/{cinema/leicester_square.html → html/cinema/105.html} +384 -313
- data/test/fixtures/{cinema/bfi_imax.html → html/cinema/211.html} +677 -355
- data/test/fixtures/{cinema/brighton.html → html/cinema/71.html} +899 -431
- data/test/fixtures/html/showtimes/11-imax.html +170 -0
- data/test/fixtures/html/showtimes/171-d-box.html +203 -0
- data/test/fixtures/html/showtimes/71-0.html +59 -0
- data/test/fixtures/html/showtimes/71.html +2395 -0
- data/test/fixtures/{sitemap.html → html/sitemap.html} +264 -352
- data/test/lib/odeon_uk/api/cinema_test.rb +149 -0
- data/test/lib/odeon_uk/api/response_test.rb +44 -0
- data/test/lib/odeon_uk/api/screenings_test.rb +43 -0
- data/test/lib/odeon_uk/cinema_test.rb +373 -148
- data/test/lib/odeon_uk/configuration_test.rb +29 -0
- data/test/lib/odeon_uk/html/cinema_test.rb +149 -0
- data/test/lib/odeon_uk/{internal/film_with_screenings_parser_test.rb → html/parser/film_with_screenings_test.rb} +12 -42
- data/test/lib/odeon_uk/html/screenings_test.rb +43 -0
- data/test/lib/odeon_uk/html/website_test.rb +37 -0
- data/test/lib/odeon_uk/screening_test.rb +17 -83
- data/test/support/api_fixtures_helper.rb +34 -0
- data/test/support/website_fixtures_helper.rb +25 -0
- data/test/test_helper.rb +7 -2
- metadata +149 -34
- data/lib/odeon_uk/film.rb +0 -65
- data/lib/odeon_uk/internal/film_with_screenings_parser.rb +0 -159
- data/lib/odeon_uk/internal/showtimes_page.rb +0 -36
- data/test/fixture_updater.rb +0 -72
- data/test/fixtures/showtimes/brighton/film_first.html +0 -92
- data/test/fixtures/showtimes/brighton/film_last.html +0 -100
- data/test/fixtures/showtimes/brighton.html +0 -2071
- data/test/fixtures/showtimes/liverpool_one/film_first_dbox.html +0 -188
- data/test/fixtures/showtimes/manchester/film_first_imax.html +0 -182
- data/test/lib/odeon_uk/film_test.rb +0 -142
- data/test/lib/odeon_uk/internal/showtimes_page_test.rb +0 -51
- data/test/lib/odeon_uk/internal/website_test.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 012176725c0f9d576455a7bbefd6da08dd415f43
|
4
|
+
data.tar.gz: 482cbfad8d2372243a791074fa356293098a3e90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36489dec0d6411c706399f4e64afdac6c12497ff86e2afc91865a6211b117985fddd52f68329a2786f88f11b574f3bf254bfdf928b146ad4bf140e004a591eee
|
7
|
+
data.tar.gz: 9c6e62a13a0201e4d83314a4818debfea7b08ee423f74970cb0f436eb62c40b13b0a3d148faa9a6e62f4cdf7bd8c052111354f9bb60fed5fdd1c63218cf5e53d
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Rakefile
CHANGED
@@ -7,12 +7,16 @@ Rake::TestTask.new do |t|
|
|
7
7
|
t.libs << 'lib/odeon_uk'
|
8
8
|
t.test_files = FileList[
|
9
9
|
'test/lib/odeon_uk/*_test.rb',
|
10
|
+
'test/lib/odeon_uk/api/*_test.rb',
|
11
|
+
'test/lib/odeon_uk/html/*_test.rb',
|
12
|
+
'test/lib/odeon_uk/html/parser/*_test.rb',
|
10
13
|
'test/lib/odeon_uk/internal/*_test.rb'
|
11
14
|
]
|
12
15
|
t.verbose = true
|
13
16
|
end
|
14
17
|
|
15
18
|
# http://erniemiller.org/2014/02/05/7-lines-every-gems-rakefile-should-have/
|
19
|
+
desc 'run gem console'
|
16
20
|
task :console do
|
17
21
|
require 'irb'
|
18
22
|
require 'irb/completion'
|
@@ -21,4 +25,29 @@ task :console do
|
|
21
25
|
IRB.start
|
22
26
|
end
|
23
27
|
|
28
|
+
desc 'recreate test fixtures'
|
29
|
+
namespace :fixtures do
|
30
|
+
require 'odeon_uk'
|
31
|
+
require_relative 'rake/fixture_creator'
|
32
|
+
|
33
|
+
desc 'html'
|
34
|
+
task :html do
|
35
|
+
FixtureCreator::Html.new(nil).sitemap!
|
36
|
+
FixtureCreator::Html.new(71).cinema! # brighton
|
37
|
+
FixtureCreator::Html.new(211).cinema! # bfi imax
|
38
|
+
FixtureCreator::Html.new(105).cinema! # leceister square
|
39
|
+
FixtureCreator::Html.new(71).showtimes!
|
40
|
+
FixtureCreator::Html.new(71).film_node!(0)
|
41
|
+
FixtureCreator::Html.new(11).film_node!('imax') # manchester imax
|
42
|
+
FixtureCreator::Html.new(171).film_node!('d-box') # liverpool dbox
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'api'
|
46
|
+
task :api do
|
47
|
+
FixtureCreator::Api.new.app_init!
|
48
|
+
FixtureCreator::Api.new.all_cinemas!
|
49
|
+
FixtureCreator::Api.new.film_times!(71)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
24
53
|
task default: :test
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module OdeonUk
|
2
|
+
# Internal utility classes: Do not use
|
3
|
+
# @api private
|
4
|
+
module Api
|
5
|
+
# The object representing a cinema on the Odeon UK website
|
6
|
+
class Cinema
|
7
|
+
# @return [Integer] the numeric id of the cinema via the API
|
8
|
+
attr_reader :id
|
9
|
+
|
10
|
+
# @param [Integer, String] id cinema id
|
11
|
+
# @return [OdeonUk::Cinema]
|
12
|
+
def initialize(id)
|
13
|
+
@id = id.to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return basic cinema information for all cinemas
|
17
|
+
# @return [Array<OdeonUk::Cinema>]
|
18
|
+
def self.ids
|
19
|
+
cinemas_hash.keys.map(&:to_i)
|
20
|
+
end
|
21
|
+
|
22
|
+
# The locality (town) of the cinema
|
23
|
+
# @return [String]
|
24
|
+
def locality
|
25
|
+
cinema_hash['siteAddress2']
|
26
|
+
end
|
27
|
+
|
28
|
+
# The name of the cinema
|
29
|
+
# @return [String]
|
30
|
+
def name
|
31
|
+
cinema_hash['siteName']
|
32
|
+
end
|
33
|
+
|
34
|
+
# The url of the cinema
|
35
|
+
# @return [Nil]
|
36
|
+
def url
|
37
|
+
end
|
38
|
+
|
39
|
+
# Post code of the cinema
|
40
|
+
# @return [String]
|
41
|
+
def postal_code
|
42
|
+
cinema_hash['sitePostcode']
|
43
|
+
end
|
44
|
+
|
45
|
+
# The street adress of the cinema
|
46
|
+
# @return [String]
|
47
|
+
def street_address
|
48
|
+
cinema_hash['siteAddress1']
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def self.cinemas_hash
|
54
|
+
@@cinemas_hash ||= Api::Response.new.all_cinemas['data']['sites']
|
55
|
+
end
|
56
|
+
|
57
|
+
def cinema_hash
|
58
|
+
@cinema_hash ||= self.class.cinemas_hash[id.to_s]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'cfpropertylist'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module OdeonUk
|
5
|
+
# Internal utility classes: Do not use
|
6
|
+
# @api private
|
7
|
+
module Api
|
8
|
+
# Utility class to make calls to the odeon website
|
9
|
+
class Response
|
10
|
+
VERSION = '2.1'
|
11
|
+
|
12
|
+
# cinemas information
|
13
|
+
# @return [Hash] decoded response of api containing cinema details
|
14
|
+
def all_cinemas
|
15
|
+
parse(all_cinemas_raw)
|
16
|
+
end
|
17
|
+
|
18
|
+
# application initialize
|
19
|
+
# @return [Hash] decoded response of api, mostly films
|
20
|
+
def app_init
|
21
|
+
parse(app_init_raw)
|
22
|
+
end
|
23
|
+
|
24
|
+
# showings for a film at a cinema
|
25
|
+
# @return [Hash] decoded response of api, day split times
|
26
|
+
def film_times(cinema_id, film_id)
|
27
|
+
parse(film_times_raw(cinema_id, film_id))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def all_cinemas_raw
|
33
|
+
post('all-cinemas').body
|
34
|
+
end
|
35
|
+
|
36
|
+
def app_init_raw
|
37
|
+
post('app-init').body
|
38
|
+
end
|
39
|
+
|
40
|
+
def film_times_raw(cinema_id, film_id)
|
41
|
+
post('film-times', { s: cinema_id, m: film_id }).body
|
42
|
+
end
|
43
|
+
|
44
|
+
def post(path, request_body={})
|
45
|
+
uri = URI("https://api.odeon.co.uk/#{VERSION}/api/#{path}")
|
46
|
+
Net::HTTP.post_form(uri, request_body)
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse(content)
|
50
|
+
plist = CFPropertyList::List.new(data: content)
|
51
|
+
CFPropertyList.native_types(plist.value).fetch('data', {})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module OdeonUk
|
2
|
+
# Internal utility classes: Do not use
|
3
|
+
# @api private
|
4
|
+
module Api
|
5
|
+
# The object representing a single screening of a film via the Odeon UK api
|
6
|
+
class Screenings
|
7
|
+
# All currently listed screenings at a cinema
|
8
|
+
# @param [Integer] cinema_id id of the cinema
|
9
|
+
# @return [Array<Hash>]
|
10
|
+
def self.at(cinema_id)
|
11
|
+
film_ids(cinema_id).flat_map do |film_id|
|
12
|
+
times_response = response.film_times(cinema_id, film_id)
|
13
|
+
|
14
|
+
times_response.flat_map do |day|
|
15
|
+
date = day['date']
|
16
|
+
screenings = day['attributes']
|
17
|
+
|
18
|
+
screenings.flat_map do |screening|
|
19
|
+
performances = screening['showtimes']
|
20
|
+
|
21
|
+
performances.flat_map do |p|
|
22
|
+
{
|
23
|
+
dimension: screening['attribute'].include?('3D') ? '3d' : '2d',
|
24
|
+
film_name: films_map(cinema_id)[film_id],
|
25
|
+
time: TimeParser.new(date, p[0]['performanceTime']).to_utc,
|
26
|
+
variant: '',
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def self.app_init
|
37
|
+
@@app_init ||= Response.new.app_init
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.film_ids(cinema_id)
|
41
|
+
films_map(cinema_id).keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.films_map(cinema_id)
|
45
|
+
films_at(cinema_id).each_with_object({}) do |f, result|
|
46
|
+
result[f['filmMasterId']] = f['title']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.films_at(cinema_id)
|
51
|
+
app_init['films'].select { |f| f['sites'].include?(cinema_id) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.response
|
55
|
+
@@response ||= Response.new
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
TimeParser = Struct.new(:date, :time) do
|
60
|
+
def to_utc
|
61
|
+
tz.local_to_utc(parsed_time)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def early_morning_screening?
|
67
|
+
!!time.match(/\A0/)
|
68
|
+
end
|
69
|
+
|
70
|
+
def parsed_date
|
71
|
+
case date
|
72
|
+
when 'Today' then Time.now.strftime '%Y-%m-%d'
|
73
|
+
when 'Tomorrow' then (Time.now + 60 * 60 * 24).strftime '%Y-%m-%d'
|
74
|
+
else date.gsub('.', '').match(/\d+\s\w+\z/)[0]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def parsed_time
|
79
|
+
parsed_time = Time.parse("#{parsed_date} #{time} UTC")
|
80
|
+
# Odeon deliberately mislabel their 00:01 screenings as the day before
|
81
|
+
early_morning_screening? ? parsed_time + 60 * 60 * 24 : parsed_time
|
82
|
+
end
|
83
|
+
|
84
|
+
def tz
|
85
|
+
@tz ||= TZInfo::Timezone.get('Europe/London')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/odeon_uk/cinema.rb
CHANGED
@@ -1,49 +1,49 @@
|
|
1
1
|
module OdeonUk
|
2
2
|
# The object representing a cinema on the Odeon UK website
|
3
3
|
class Cinema
|
4
|
-
|
5
|
-
|
4
|
+
extend Forwardable
|
5
|
+
|
6
6
|
# @return [Integer] the numeric id of the cinema on the Odeon website
|
7
7
|
attr_reader :id
|
8
|
-
# @return [String] the name of the cinema
|
9
|
-
attr_reader :name
|
10
|
-
# @return [String] the slug of the cinema
|
11
|
-
attr_reader :slug
|
12
|
-
# @return [String] the url of the cinema on the Odeon website
|
13
|
-
attr_reader :url
|
14
8
|
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
9
|
+
# @!method locality
|
10
|
+
# The locality (town) of the cinema
|
11
|
+
# @return [String]
|
12
|
+
# @example
|
13
|
+
# cinema = OdeonUk::Cinema.find('71')
|
14
|
+
# cinema.locality
|
15
|
+
# #=> 'Brighton'
|
16
|
+
# @!method postal_code
|
17
|
+
# Post code of the cinema
|
18
|
+
# @return [String]
|
19
|
+
# @example
|
20
|
+
# cinema = OdeonUk::Cinema.find('71')
|
21
|
+
# cinema.postal_code
|
22
|
+
# #=> 'BN1 2RE'
|
23
|
+
# @!method street_address
|
24
|
+
# The street adress of the cinema
|
25
|
+
# @return [String]
|
26
|
+
# @example
|
27
|
+
# cinema = OdeonUk::Cinema.find('71')
|
28
|
+
# cinema.street_address
|
29
|
+
# #=> 'Kingswest'
|
30
|
+
# @!method url
|
31
|
+
# Website URI for the cinema
|
32
|
+
# @return [String] the url of the cinema on the Odeon website
|
33
|
+
def_delegators :cinema_parser, :locality, :postal_code, :street_address,
|
34
|
+
:url
|
35
|
+
|
36
|
+
# @param [Integer, String] id the cinema id of the format 71/'71' as used on
|
37
|
+
# the odeon.co.uk website
|
18
38
|
# @return [OdeonUk::Cinema]
|
19
|
-
def initialize(id
|
20
|
-
@
|
21
|
-
@id = id.to_i
|
22
|
-
@name = name.gsub('London - ', '').gsub(' - ', ': ')
|
23
|
-
@slug = @name.downcase.gsub(/[^0-9a-z ]/, '').gsub(/\s+/, '-')
|
24
|
-
@url = (url[0] == '/') ? "http://www.odeon.co.uk#{url}" : url
|
39
|
+
def initialize(id)
|
40
|
+
@id = id
|
25
41
|
end
|
26
42
|
|
27
43
|
# Return basic cinema information for all cinemas
|
28
44
|
# @return [Array<OdeonUk::Cinema>]
|
29
45
|
def self.all
|
30
|
-
|
31
|
-
new_from_link link
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Find a single cinema
|
36
|
-
# @param [Integer, String] id the cinema id of the format 71/'71' as used on
|
37
|
-
# the odeon.co.uk website
|
38
|
-
# @return [OdeonUk::Cinema, nil]
|
39
|
-
# @example
|
40
|
-
# OdeonUk::Cinema.find('71')
|
41
|
-
# #=> <OdeonUk::Cinema brand="Odeon" name="Brighton" slug="brighton" ...>
|
42
|
-
def self.find(id)
|
43
|
-
id = id.to_i
|
44
|
-
return nil unless id > 0
|
45
|
-
|
46
|
-
all.select { |cinema| cinema.id == id }[0]
|
46
|
+
cinema_parser_class.ids.map { |cinema_id| new(cinema_id) }
|
47
47
|
end
|
48
48
|
|
49
49
|
# Address of the cinema
|
@@ -58,17 +58,16 @@ module OdeonUk
|
|
58
58
|
def adr
|
59
59
|
{
|
60
60
|
street_address: street_address,
|
61
|
-
locality:
|
62
|
-
postal_code:
|
63
|
-
|
61
|
+
locality: locality,
|
62
|
+
postal_code: postal_code,
|
63
|
+
country_name: 'United Kingdom'
|
64
64
|
}
|
65
65
|
end
|
66
66
|
alias_method :address, :adr
|
67
67
|
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
Film.at(id)
|
68
|
+
# @return [String] 'Odeon'
|
69
|
+
def brand
|
70
|
+
'Odeon'
|
72
71
|
end
|
73
72
|
|
74
73
|
# The name of the cinema including the brand
|
@@ -78,29 +77,13 @@ module OdeonUk
|
|
78
77
|
# cinema.full_name
|
79
78
|
# #=> 'Odeon Brighton'
|
80
79
|
def full_name
|
81
|
-
"#{brand} #{name}"
|
82
|
-
end
|
83
|
-
|
84
|
-
# The locality (town) of the cinema
|
85
|
-
# @return [String]
|
86
|
-
# @example
|
87
|
-
# cinema = OdeonUk::Cinema.find('71')
|
88
|
-
# cinema.locality
|
89
|
-
# #=> 'Brighton'
|
90
|
-
def locality
|
91
|
-
return unless address_node
|
92
|
-
address_node.text.match(/\w+(\s\w+){0,}\s+(\w+(\s\w+){0,})/)[2]
|
80
|
+
@full_name ||= "#{brand} #{name}"
|
93
81
|
end
|
94
82
|
|
95
|
-
#
|
96
|
-
# @return [String]
|
97
|
-
|
98
|
-
|
99
|
-
# cinema.postal_code
|
100
|
-
# #=> 'BN1 2RE'
|
101
|
-
def postal_code
|
102
|
-
return unless address_node
|
103
|
-
address_node.text.match(/[A-Z]{1,2}\d{1,2}[A-Z]?\s\d{1,2}[A-Z]{1,2}/)[0]
|
83
|
+
# Cinema name, slightly sanitized
|
84
|
+
# @return [String] the name of the cinema
|
85
|
+
def name
|
86
|
+
@name ||= cinema_parser.name.gsub('London - ', '').gsub(' - ', ': ')
|
104
87
|
end
|
105
88
|
|
106
89
|
# All planned screenings
|
@@ -113,64 +96,20 @@ module OdeonUk
|
|
113
96
|
Screening.at(id)
|
114
97
|
end
|
115
98
|
|
116
|
-
#
|
117
|
-
# @
|
118
|
-
|
119
|
-
|
120
|
-
# cinema = OdeonUk::Cinema.find('71')
|
121
|
-
# cinema.screenings_of('Iron Man 3')
|
122
|
-
# #=> [<OdeonUk::Screening film_name="Iron Man 3" cinema_name="Brighton" when="..." variant="...">, <OdeonUk::Screening ...>]
|
123
|
-
# iron_man_3 = OdeonUk::Film.new "Iron Man 3"
|
124
|
-
# cinema.screenings_of(iron_man_3)
|
125
|
-
# #=> [<OdeonUk::Screening film_name="Iron Man 3" cinema_name="Brighton" when="..." variant="...">, <OdeonUk::Screening ...>]
|
126
|
-
def screenings_of film
|
127
|
-
film_name = (film.is_a?(OdeonUk::Film) ? film.name : film)
|
128
|
-
screenings.select { |s| s.film_name == film_name }
|
129
|
-
end
|
130
|
-
|
131
|
-
# The street adress of the cinema
|
132
|
-
# @return a String
|
133
|
-
# @example
|
134
|
-
# cinema = OdeonUk::Cinema.find('71')
|
135
|
-
# cinema.street_address
|
136
|
-
# #=> 'Kingswest'
|
137
|
-
def street_address
|
138
|
-
return unless address_node
|
139
|
-
address_node.text.match(/\A\s+(\w+(\s\w+){0,})/)[1]
|
99
|
+
# slug from the hotel name
|
100
|
+
# @return [String] the slug of the cinema
|
101
|
+
def slug
|
102
|
+
@slug ||= full_name.downcase.gsub(/[^0-9a-z ]/, '').gsub(/\s+/, '-')
|
140
103
|
end
|
141
104
|
|
142
105
|
private
|
143
106
|
|
144
|
-
def self.
|
145
|
-
|
146
|
-
links.select { |link| link.get_attribute('href').match(/\/\d+\/$/) }
|
147
|
-
end
|
148
|
-
|
149
|
-
def self.new_from_link(link)
|
150
|
-
url = link.get_attribute('href')
|
151
|
-
id = url.match(/\/(\d+)\/$/)[1]
|
152
|
-
name = link.children.first.to_s
|
153
|
-
new id, name, url
|
154
|
-
end
|
155
|
-
|
156
|
-
def self.parsed_sitemap
|
157
|
-
Nokogiri::HTML(sitemap_response)
|
158
|
-
end
|
159
|
-
|
160
|
-
def self.sitemap_response
|
161
|
-
@sitemap_response ||= OdeonUk::Internal::Website.new.sitemap
|
162
|
-
end
|
163
|
-
|
164
|
-
def address_node
|
165
|
-
parsed_cinema.css('.gethere .span4 .description')[0]
|
166
|
-
end
|
167
|
-
|
168
|
-
def cinema_response
|
169
|
-
@cinema_response ||= OdeonUk::Internal::Website.new.cinema(@id)
|
107
|
+
def self.cinema_parser_class
|
108
|
+
OdeonUk.configuration.method == :api ? Api::Cinema : Html::Cinema
|
170
109
|
end
|
171
110
|
|
172
|
-
def
|
173
|
-
|
111
|
+
def cinema_parser
|
112
|
+
@cinema_parser ||= self.class.cinema_parser_class.new(id)
|
174
113
|
end
|
175
114
|
end
|
176
115
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module OdeonUk
|
2
|
+
class << self
|
3
|
+
attr_accessor :configuration
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.configuration
|
7
|
+
@configuration ||= Configuration.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure
|
11
|
+
yield(configuration)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Configuration
|
15
|
+
attr_accessor :method
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@method = :html
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module OdeonUk
|
2
|
+
# Internal utility classes: Do not use
|
3
|
+
# @api private
|
4
|
+
module Html
|
5
|
+
# The object representing a cinema on the Odeon UK website
|
6
|
+
class Cinema
|
7
|
+
ADDRESS_REGEX = '.gethere .span4 .description'
|
8
|
+
ALL_LINKS = '.sitemap a[href*=cinemas]'
|
9
|
+
|
10
|
+
# @return [Integer] the numeric id of the cinema on the Odeon website
|
11
|
+
attr_reader :id
|
12
|
+
|
13
|
+
# @param [Integer, String] id cinema id
|
14
|
+
# @return [OdeonUk::Cinema]
|
15
|
+
def initialize(id)
|
16
|
+
@id = id.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return basic cinema information for all cinemas
|
20
|
+
# @return [Array<OdeonUk::Cinema>]
|
21
|
+
def self.ids
|
22
|
+
cinema_links.map { |link| id_from(link) }
|
23
|
+
end
|
24
|
+
|
25
|
+
# The locality (town) of the cinema
|
26
|
+
# @return [String]
|
27
|
+
def locality
|
28
|
+
return unless address_node
|
29
|
+
address_node.text.match(/\w+(\s\w+){0,}\s+(\w+(\s\w+){0,})/)[2]
|
30
|
+
end
|
31
|
+
|
32
|
+
# The name of the cinema
|
33
|
+
# @return [String]
|
34
|
+
def name
|
35
|
+
cinema_link_doc.children.first.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
# The web url of the cinema
|
39
|
+
# @return [String]
|
40
|
+
def url
|
41
|
+
@url ||= begin
|
42
|
+
link = cinema_link_doc.get_attribute('href')
|
43
|
+
(link[0] == '/') ? "http://www.odeon.co.uk#{link}" : link
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Post code of the cinema
|
48
|
+
# @return [String]
|
49
|
+
def postal_code
|
50
|
+
return unless address_node
|
51
|
+
address_node.text.match(/[A-Z]{1,2}\d{1,2}[A-Z]?\s\d{1,2}[A-Z]{1,2}/)[0]
|
52
|
+
end
|
53
|
+
|
54
|
+
# The street adress of the cinema
|
55
|
+
# @return [String]
|
56
|
+
def street_address
|
57
|
+
return unless address_node
|
58
|
+
address_node.text.match(/\A\s+(\w+(\s\w+){0,})/)[1]
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def self.id_from(link)
|
64
|
+
link.get_attribute('href').match(/\/(\d+)\/$/)[1].to_i
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.cinema_links
|
68
|
+
@@cinema_links ||= begin
|
69
|
+
links = sitemap_doc.css(ALL_LINKS)
|
70
|
+
links.select { |link| link.get_attribute('href').match(/\/\d+\/$/) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.sitemap_doc
|
75
|
+
@@sitemap_doc ||= Nokogiri::HTML(sitemap_response)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.sitemap_response
|
79
|
+
@@sitemap_response ||= Html::Website.new.sitemap
|
80
|
+
end
|
81
|
+
|
82
|
+
def address_node
|
83
|
+
cinema_doc.css(ADDRESS_REGEX)[0]
|
84
|
+
end
|
85
|
+
|
86
|
+
def cinema_response
|
87
|
+
@cinema_response ||= Html::Website.new.cinema(id)
|
88
|
+
end
|
89
|
+
|
90
|
+
def cinema_doc
|
91
|
+
@cinema_doc ||= Nokogiri::HTML(cinema_response)
|
92
|
+
end
|
93
|
+
|
94
|
+
def cinema_link_doc
|
95
|
+
self.class.cinema_links.select do |link|
|
96
|
+
link.get_attribute('href').include?("/#{id}/")
|
97
|
+
end.first
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|