odeon_uk 2.0.4 → 3.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.
- 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
|