cineworld_uk 2.1.6 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -3
  3. data/README.md +56 -14
  4. data/Rakefile +57 -1
  5. data/cineworld_uk.gemspec +1 -2
  6. data/lib/cineworld_uk/cinema.rb +113 -182
  7. data/lib/cineworld_uk/internal/api_response.rb +74 -0
  8. data/lib/cineworld_uk/internal/parser/api/cinema_address.rb +81 -0
  9. data/lib/cineworld_uk/internal/parser/api/film.rb +46 -0
  10. data/lib/cineworld_uk/internal/parser/api/film_lookup.rb +38 -0
  11. data/lib/cineworld_uk/internal/parser/api/performance.rb +46 -0
  12. data/lib/cineworld_uk/internal/parser/api/performances_by_day.rb +50 -0
  13. data/lib/cineworld_uk/internal/title_sanitizer.rb +56 -52
  14. data/lib/cineworld_uk/performance.rb +78 -0
  15. data/lib/cineworld_uk/version.rb +2 -2
  16. data/lib/cineworld_uk.rb +11 -8
  17. data/test/fixtures/api/cinema-detail-10.json +1 -0
  18. data/test/fixtures/api/cinema-detail-21.json +1 -0
  19. data/test/fixtures/api/cinema-detail-3.json +1 -0
  20. data/test/fixtures/api/cinema-detail-96.json +1 -0
  21. data/test/fixtures/api/cinema-list.json +1 -0
  22. data/test/fixtures/api/dates-3.json +1 -0
  23. data/test/fixtures/api/film-list-comingsoon.json +1 -0
  24. data/test/fixtures/api/film-list.json +1 -0
  25. data/test/fixtures/api/performances-tomorrow-3.json +1 -0
  26. data/test/lib/cineworld_uk/cinema_test.rb +96 -197
  27. data/test/lib/cineworld_uk/internal/api_response_test.rb +85 -0
  28. data/test/lib/cineworld_uk/internal/parser/api/cinema_address_test.rb +93 -0
  29. data/test/lib/cineworld_uk/internal/parser/api/film_lookup_test.rb +30 -0
  30. data/test/lib/cineworld_uk/internal/parser/api/film_test.rb +169 -0
  31. data/test/lib/cineworld_uk/internal/parser/api/performance_test.rb +62 -0
  32. data/test/lib/cineworld_uk/internal/title_sanitizer_test.rb +1 -1
  33. data/test/lib/cineworld_uk/{screening_test.rb → performance_test.rb} +36 -48
  34. data/test/support/fixture_reader.rb +44 -0
  35. metadata +48 -59
  36. data/lib/cineworld_uk/film.rb +0 -65
  37. data/lib/cineworld_uk/internal/film_with_screenings_parser.rb +0 -69
  38. data/lib/cineworld_uk/internal/screening_parser.rb +0 -132
  39. data/lib/cineworld_uk/internal/website.rb +0 -35
  40. data/lib/cineworld_uk/internal/whatson_parser.rb +0 -36
  41. data/lib/cineworld_uk/screening.rb +0 -87
  42. data/test/fixture_updater.rb +0 -64
  43. data/test/fixtures/cinemas.html +0 -513
  44. data/test/fixtures/information/brighton.html +0 -1268
  45. data/test/fixtures/information/bristol.html +0 -1265
  46. data/test/fixtures/whatson/brighton/film_first.html +0 -207
  47. data/test/fixtures/whatson/brighton/film_last.html +0 -62
  48. data/test/fixtures/whatson/brighton/film_second.html +0 -347
  49. data/test/fixtures/whatson/brighton.html +0 -7508
  50. data/test/fixtures/whatson/glasgow-imax-at-gsc/film_first.html +0 -182
  51. data/test/fixtures/whatson/the-o2-greenwich/film_first.html +0 -167
  52. data/test/lib/cineworld_uk/film_test.rb +0 -142
  53. data/test/lib/cineworld_uk/internal/film_with_screenings_parser_test.rb +0 -101
  54. data/test/lib/cineworld_uk/internal/website_test.rb +0 -57
  55. data/test/lib/cineworld_uk/internal/whatson_parser_test.rb +0 -58
@@ -1,65 +0,0 @@
1
- module CineworldUk
2
- # The object representing a film on the Cineworld UK website
3
- class Film
4
- include Comparable
5
-
6
- # @return [String] the name of the film
7
- attr_reader :name
8
- # @return [String] the normalized slug derived from the film name
9
- attr_reader :slug
10
-
11
- # @param [String] name the film name
12
- # @return [CineworldUk::Film]
13
- def initialize(name)
14
- @name = name
15
- @slug = name.downcase.gsub(/[^0-9a-z ]/, '').gsub(/\s+/, '-')
16
- end
17
-
18
- # All currently listed films showing at a cinema
19
- # @param [Integer] cinema_id id of the cinema on the website
20
- # @return [Array<CineworldUk::Film>]
21
- def self.at(cinema_id)
22
- whatson_parser(cinema_id).films_with_screenings.map do |html|
23
- new(screenings_parser(html).film_name)
24
- end.uniq
25
- end
26
-
27
- # Allows sort on objects
28
- # @param [CineworldUk::Film] other another film object
29
- # @return [Integer] -1, 0 or 1
30
- def <=>(other)
31
- slug <=> other.slug
32
- end
33
-
34
- # Check an object is the same as another object.
35
- # @param [CineworldUk::Film] other another film
36
- # @return [Boolean] True if both objects are the same exact object, or if
37
- # they are of the same type and share an equal slug
38
- # @note Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
39
- def eql?(other)
40
- self.class == other.class && self == other
41
- end
42
-
43
- # Generates hash of slug in order to allow two records of the same type and
44
- # id to work with something like:
45
- #
46
- # [Film.new('ABC'), Film.new('DEF')] & [Film.new('DEF'), Film.new('GHI')]
47
- # #=> [ Film.new('DEF') ]
48
- #
49
- # @return [Integer] hash of slug
50
- # @note Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
51
- def hash
52
- slug.hash
53
- end
54
-
55
- private
56
-
57
- def self.screenings_parser(html)
58
- CineworldUk::Internal::FilmWithScreeningsParser.new(html)
59
- end
60
-
61
- def self.whatson_parser(id)
62
- CineworldUk::Internal::WhatsonParser.new(id)
63
- end
64
- end
65
- end
@@ -1,69 +0,0 @@
1
- module CineworldUk
2
- # Internal utility classes: Do not use
3
- # @api private
4
- module Internal
5
- # Parses a chunk of HTML to derive movie showing data
6
- class FilmWithScreeningsParser
7
- # css selector for film name link
8
- FILM_NAME_CSS = 'h3.h1'
9
- # css selector for performances
10
- PERFORMANCES_CSS = '.schedule .performances > li'
11
-
12
- # @param [String] film_html a chunk of html
13
- def initialize(film_html)
14
- @film_html = film_html.to_s
15
- end
16
-
17
- # The film name
18
- # @return [String]
19
- def film_name
20
- title_sanitizer(film_name_text.children[0].to_s)
21
- end
22
-
23
- # attributes of all the screenings
24
- # @return [Array<Hash>]
25
- def to_a
26
- return [] unless performances_doc
27
-
28
- performances_doc.map do |node|
29
- next unless screening_parser_hash(node)
30
- screening_parser_hash(node).merge(film_hash)
31
- end.compact
32
- end
33
-
34
- private
35
-
36
- def doc
37
- @doc ||= Nokogiri::HTML(@film_html)
38
- end
39
-
40
- def film_hash
41
- @film_hash ||= { film_name: film_name }
42
- end
43
-
44
- def film_link
45
- @film_link ||= film_name_doc.css('a[href*=whatson]')
46
- end
47
-
48
- def film_name_text
49
- film_link.empty? ? film_name_doc : film_link
50
- end
51
-
52
- def film_name_doc
53
- @film_name_doc ||= doc.css(FILM_NAME_CSS)
54
- end
55
-
56
- def performances_doc
57
- @performances_doc ||= doc.css(PERFORMANCES_CSS)
58
- end
59
-
60
- def screening_parser_hash(node)
61
- ScreeningParser.new(node).to_hash
62
- end
63
-
64
- def title_sanitizer(title)
65
- TitleSanitizer.new(title).sanitized
66
- end
67
- end
68
- end
69
- end
@@ -1,132 +0,0 @@
1
- module CineworldUk
2
- # Internal utility classes: Do not use
3
- # @api private
4
- module Internal
5
- # parse a single performance
6
- class ScreeningParser
7
- # regex for date in url
8
- DATE_REGEX = /date\=(\d{4})(\d{2})(\d{2})/
9
- # regex for time in url
10
- TIME_REGEX = /time\=(\d{2})\:(\d{2})/
11
-
12
- # css selector for dimension
13
- DIMENSION_CSS = '.icon-service-2d, .icon-service-3d'
14
- # css selector for baby screening
15
- BABY_CSS = '.icon-service-cb'
16
- # css selector for d-box screening
17
- DBOX_CSS = '.icon-service-dx'
18
- # css selector for hfr screening
19
- HFR_CSS = '.icon-service-hfr'
20
- # css selector for imax screening
21
- IMAX_CSS = '.icon-service-ix'
22
- # css selector for kids screening
23
- KIDS_CSS = '.icon-service-m4j'
24
-
25
- # @param [String] html a chunk of html representing one screening
26
- def initialize(html)
27
- @html = html.to_s
28
- end
29
-
30
- # url to book this screening
31
- # @return [String]
32
- def booking_url
33
- return unless bookable?
34
- 'http://www.cineworld.co.uk' + doc.css('a').attribute('href')
35
- end
36
-
37
- # the dimension of the screening
38
- # @return [String]
39
- def dimension
40
- return unless bookable?
41
- doc.css(DIMENSION_CSS).text.downcase
42
- end
43
-
44
- # utc time of the screening
45
- # @return [Time]
46
- def time
47
- return unless bookable?
48
- timezone.local_to_utc(Time.utc(*date_array + time_array))
49
- end
50
-
51
- # anything special about the screening
52
- # @return [Array<String>]
53
- # @example
54
- # Cineworld::Internal::ScreeningParser.new(html).tags
55
- # => ["imax"]
56
- def variant
57
- return unless bookable?
58
- [baby, dbox, hfr, imax, kids].compact
59
- end
60
-
61
- # a attributes of a single screening
62
- # @return [Hash]
63
- # @example
64
- # Cineworld::Internal::ScreeningParser.new(html).to_hash
65
- # => {
66
- # booking_url: 'http://...',
67
- # dimension: '2d',
68
- # time: <Time>,
69
- # variant: ['imax']
70
- # }
71
- def to_hash
72
- return unless bookable?
73
- {
74
- booking_url: booking_url,
75
- dimension: dimension,
76
- time: time,
77
- variant: variant
78
- }
79
- end
80
-
81
- private
82
-
83
- def baby
84
- 'baby' if doc.css(BABY_CSS).length > 0
85
- end
86
-
87
- def bookable?
88
- @bookable ||= doc.css('a.performance').size > 0
89
- end
90
-
91
- def date_array
92
- @date_array ||= performance_link_html.match(DATE_REGEX) do |match|
93
- [match[1], match[2], match[3]]
94
- end
95
- end
96
-
97
- def dbox
98
- 'dbox' if doc.css(DBOX_CSS).length > 0
99
- end
100
-
101
- def doc
102
- @doc ||= Nokogiri::HTML(@html)
103
- end
104
-
105
- def hfr
106
- 'hfr' if doc.css(HFR_CSS).length > 0
107
- end
108
-
109
- def imax
110
- 'imax' if doc.css(IMAX_CSS).length > 0
111
- end
112
-
113
- def kids
114
- 'kids' if doc.css(KIDS_CSS).length > 0
115
- end
116
-
117
- def performance_link_html
118
- @performance_link_html ||= doc.css('a.performance').to_s
119
- end
120
-
121
- def time_array
122
- @time_array ||= performance_link_html.match(TIME_REGEX) do |match|
123
- [match[1], match[2]]
124
- end
125
- end
126
-
127
- def timezone
128
- @timezone ||= TZInfo::Timezone.get('Europe/London')
129
- end
130
- end
131
- end
132
- end
@@ -1,35 +0,0 @@
1
- # encoding: UTF-8
2
- require 'open-uri'
3
-
4
- module CineworldUk
5
- # Internal utility classes: Do not use
6
- # @api private
7
- module Internal
8
- # fetches pages from the cineworld.co.uk website
9
- class Website
10
- # get the cinema information page for passed id
11
- # @return [String]
12
- def cinema_information(id)
13
- get("cinemas/#{id}/information")
14
- end
15
-
16
- # get the cinemas page
17
- # @return [String]
18
- def cinemas
19
- get('cinemas')
20
- end
21
-
22
- # get the cinema page containing all upcoming films and screenings
23
- # @return [String]
24
- def whatson(id)
25
- get("whatson?cinema=#{id}")
26
- end
27
-
28
- private
29
-
30
- def get(path)
31
- open("http://www.cineworld.co.uk/#{path}").read
32
- end
33
- end
34
- end
35
- end
@@ -1,36 +0,0 @@
1
- module CineworldUk
2
- # Internal utility classes: Do not use
3
- # @api private
4
- module Internal
5
- # Parses a chunk of HTML to derive movie showing data
6
- class WhatsonParser
7
- # css selector for film html chunks
8
- FILM_CSS = '#filter-reload > .span9 > .film'
9
-
10
- # @param [Integer] cinema_id cineworld cinema id
11
- def initialize(cinema_id)
12
- @cinema_id = cinema_id
13
- end
14
-
15
- # break up the whats on page into individual chunks for each film
16
- # @return [Array<String>] html chunks for a film and it's screenings
17
- def films_with_screenings
18
- films_html
19
- end
20
-
21
- private
22
-
23
- def films_html
24
- whatson_doc.css(FILM_CSS).map { |n| n.to_s.gsub(/^\s+/, '') }
25
- end
26
-
27
- def whatson
28
- @whatson ||= CineworldUk::Internal::Website.new.whatson(@cinema_id)
29
- end
30
-
31
- def whatson_doc
32
- @whatson_doc ||= Nokogiri::HTML(whatson)
33
- end
34
- end
35
- end
36
- end
@@ -1,87 +0,0 @@
1
- module CineworldUk
2
- # The object representing a single screening on the Cineworld UK website
3
- class Screening
4
- # @return [String] the booking URL on the cinema website
5
- attr_reader :booking_url
6
- # @return [String] 2d or 3d
7
- attr_reader :dimension
8
- # @return [String] the cinema name
9
- attr_reader :cinema_name
10
- # @return [String] the film name
11
- attr_reader :film_name
12
-
13
- # @param [Hash] options options hash
14
- # @option options [String] :booking_url (nil) booking url for the screening
15
- # @option options [String] :cinema_name name of the cinema
16
- # @option options [String] :cinema_id website id of the cinema
17
- # @option options [String] :dimension ('2d') dimension of the screening
18
- # @option options [String] :film_name name of the film
19
- # @option options [Time] :time utc time of the screening
20
- # @option options [Array<String>] :variant ([]) type of screening
21
- def initialize(options)
22
- @booking_url = options.fetch(:booking_url, nil)
23
- @cinema_name = options.fetch(:cinema_name)
24
- @cinema_id = options.fetch(:cinema_id)
25
- @dimension = options.fetch(:dimension, '2d')
26
- @film_name = options.fetch(:film_name)
27
- @time = options.fetch(:time)
28
- @variant = options.fetch(:variant, [])
29
- end
30
-
31
- # All currently listed films showing at a cinema
32
- # @param [Integer] cinema_id id of the cinema on the website
33
- # @return [Array<CineworldUk::Screening>]
34
- def self.at(cinema_id)
35
- whatson_parser(cinema_id).films_with_screenings.flat_map do |html|
36
- create_for_single_film(html, cinema_id)
37
- end
38
- end
39
-
40
- # The date of the screening
41
- # @return [Date]
42
- def showing_on
43
- showing_at.to_date
44
- end
45
-
46
- # The UTC time of the screening
47
- # @return [Time]
48
- def showing_at
49
- @when ||= begin
50
- if @time.utc?
51
- @time
52
- else
53
- TZInfo::Timezone.get('Europe/London').local_to_utc(@time)
54
- end
55
- end
56
- end
57
-
58
- # The kinds of screening
59
- # @return <Array[String]>
60
- def variant
61
- @variant.map(&:downcase).sort
62
- end
63
-
64
- private
65
-
66
- def self.cinema_hash(cinema_id)
67
- {
68
- cinema_id: cinema_id,
69
- cinema_name: CineworldUk::Cinema.find(cinema_id).name
70
- }
71
- end
72
-
73
- def self.create_for_single_film(html, cinema_id)
74
- screenings_parser(html).to_a.map do |attributes|
75
- new cinema_hash(cinema_id).merge(attributes)
76
- end
77
- end
78
-
79
- def self.screenings_parser(html)
80
- CineworldUk::Internal::FilmWithScreeningsParser.new(html)
81
- end
82
-
83
- def self.whatson_parser(id)
84
- CineworldUk::Internal::WhatsonParser.new(id)
85
- end
86
- end
87
- end
@@ -1,64 +0,0 @@
1
- require File.expand_path('../../lib/cineworld_uk.rb', __FILE__)
2
-
3
- def fixture(name)
4
- File.expand_path("../fixtures/#{name}.html", __FILE__)
5
- end
6
-
7
- File.open(fixture('cinemas'), 'w') do |file|
8
- puts '* Cinemas'
9
- file.write CineworldUk::Internal::Website.new.cinemas
10
- end
11
-
12
- # BRIGHTON
13
-
14
- File.open(fixture('information/brighton'), 'w') do |file|
15
- puts '* Brighton Information'
16
- file.write CineworldUk::Internal::Website.new.cinema_information(3)
17
- end
18
-
19
- File.open(fixture('whatson/brighton'), 'w') do |file|
20
- puts '* Brighton Whats On'
21
- file.write CineworldUk::Internal::Website.new.whatson(3)
22
- end
23
-
24
- parser = CineworldUk::Internal::WhatsonParser.new(3)
25
-
26
- File.open(fixture('whatson/brighton/film_first'), 'w') do |file|
27
- puts '* Brighton Main Film'
28
- file.write parser.films_with_screenings[0]
29
- end
30
-
31
- File.open(fixture('whatson/brighton/film_second'), 'w') do |file|
32
- puts '* Brighton Second Film'
33
- file.write parser.films_with_screenings[1]
34
- end
35
-
36
- File.open(fixture('whatson/brighton/film_last'), 'w') do |file|
37
- puts '* Brighton Last Film'
38
- file.write parser.films_with_screenings[-1]
39
- end
40
-
41
- # BRISTOL
42
-
43
- File.open(fixture('information/bristol'), 'w') do |file|
44
- puts '* Bristol Information'
45
- file.write CineworldUk::Internal::Website.new.cinema_information(4)
46
- end
47
-
48
- # O2
49
-
50
- parser = CineworldUk::Internal::WhatsonParser.new(79)
51
-
52
- File.open(fixture('whatson/the-o2-greenwich/film_first'), 'w') do |file|
53
- puts '* The O2 Greenwich Main Film'
54
- file.write parser.films_with_screenings[0]
55
- end
56
-
57
- # GLASGOW IMAX
58
-
59
- parser = CineworldUk::Internal::WhatsonParser.new(88)
60
-
61
- File.open(fixture('whatson/glasgow-imax-at-gsc/film_first'), 'w') do |file|
62
- puts '* Glasgow IMAX Main Film'
63
- file.write parser.films_with_screenings[0]
64
- end