picturehouse_uk 3.0.14 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -3
  3. data/CHANGELOG.md +19 -0
  4. data/{LICENSE.txt → COMM-LICENSE} +0 -0
  5. data/LICENSE +617 -0
  6. data/README.md +5 -4
  7. data/Rakefile +12 -0
  8. data/lib/picturehouse_uk.rb +2 -4
  9. data/lib/picturehouse_uk/cinema.rb +133 -124
  10. data/lib/picturehouse_uk/internal/parser/address.rb +20 -8
  11. data/lib/picturehouse_uk/internal/parser/screenings.rb +70 -28
  12. data/lib/picturehouse_uk/internal/title_sanitizer.rb +61 -65
  13. data/lib/picturehouse_uk/performance.rb +60 -0
  14. data/lib/picturehouse_uk/version.rb +2 -2
  15. data/picturehouse_uk.gemspec +5 -6
  16. data/rake/fixture_creator.rb +42 -0
  17. data/test/fixtures/Duke_Of_Yorks/cinema.html +3408 -0
  18. data/test/fixtures/{info/Duke_Of_Yorks.html → Duke_Of_Yorks/info.html} +192 -185
  19. data/test/fixtures/Duke_Of_Yorks/whats_on.html +3159 -0
  20. data/test/fixtures/Dukes_At_Komedia/cinema.html +4764 -0
  21. data/test/fixtures/{info/Dukes_At_Komedia.html → Dukes_At_Komedia/info.html} +161 -172
  22. data/test/fixtures/Dukes_At_Komedia/whats_on.html +4429 -0
  23. data/test/fixtures/National_Media_Museum/cinema.html +9200 -0
  24. data/test/fixtures/National_Media_Museum/info.html +606 -0
  25. data/test/fixtures/National_Media_Museum/whats_on.html +8850 -0
  26. data/test/fixtures/Phoenix_Picturehouse/cinema.html +8274 -0
  27. data/test/fixtures/{info/Phoenix_Picturehouse.html → Phoenix_Picturehouse/info.html} +165 -176
  28. data/test/fixtures/Phoenix_Picturehouse/whats_on.html +7986 -0
  29. data/test/fixtures/home.html +148 -157
  30. data/test/lib/picturehouse_uk/cinema_test.rb +107 -136
  31. data/test/lib/picturehouse_uk/internal/parser/{address_parser_test.rb → address_test.rb} +3 -3
  32. data/test/lib/picturehouse_uk/internal/parser/screenings_test.rb +8 -10
  33. data/test/lib/picturehouse_uk/internal/website_test.rb +6 -3
  34. data/test/lib/picturehouse_uk/{screening_test.rb → performance_test.rb} +20 -44
  35. data/test/live/integration_test.rb +8 -25
  36. data/test/support/fake_website.rb +24 -0
  37. data/test/test_helper.rb +12 -2
  38. metadata +50 -49
  39. data/.rdoc_options +0 -16
  40. data/lib/picturehouse_uk/film.rb +0 -59
  41. data/lib/picturehouse_uk/screening.rb +0 -70
  42. data/test/fixture_updater.rb +0 -73
  43. data/test/fixtures/cinema/Duke_Of_Yorks.html +0 -2984
  44. data/test/fixtures/cinema/Dukes_At_Komedia.html +0 -5518
  45. data/test/fixtures/cinema/National_Media_Museum.html +0 -10266
  46. data/test/fixtures/cinema/Phoenix_Picturehouse.html +0 -5202
  47. data/test/fixtures/whats_on/Duke_Of_Yorks.html +0 -2737
  48. data/test/fixtures/whats_on/Dukes_At_Komedia.html +0 -5132
  49. data/test/fixtures/whats_on/National_Media_Museum.html +0 -9690
  50. data/test/fixtures/whats_on/Phoenix_Picturehouse.html +0 -4916
  51. data/test/lib/picturehouse_uk/film_test.rb +0 -141
data/README.md CHANGED
@@ -2,10 +2,11 @@
2
2
 
3
3
  A simple gem to parse the [Picturehouse Cinemas UK website](http://picturehouses.com) and spit out useful formatted info.
4
4
 
5
- [![Gem Version](https://badge.fury.io/rb/picturehouse_uk.png)](http://badge.fury.io/rb/picturehouse_uk)
6
- [![Code Climate](https://codeclimate.com/github/andycroll/picturehouse_uk.png)](https://codeclimate.com/github/andycroll/picturehouse_uk)
7
- [![Build Status](https://travis-ci.org/andycroll/picturehouse_uk.png?branch=master)](https://travis-ci.org/andycroll/picturehouse_uk)
8
- [![Inline docs](http://inch-ci.org/github/andycroll/picturehouse_uk.png)](http://inch-ci.org/github/andycroll/picturehouse_uk)
5
+ [![Gem Version](https://badge.fury.io/rb/picturehouse_uk.svg)](https://badge.fury.io/rb/picturehouse_uk)
6
+ [![Code Climate](https://codeclimate.com/github/andycroll/picturehouse_uk/badges/gpa.svg)](https://codeclimate.com/github/andycroll/picturehouse_uk)
7
+ [![Test Coverage](https://codeclimate.com/github/andycroll/picturehouse_uk/badges/coverage.svg)](https://codeclimate.com/github/andycroll/picturehouse_uk/coverage)
8
+ [![Build Status](https://travis-ci.org/andycroll/picturehouse_uk.svg?branch=master)](https://travis-ci.org/andycroll/picturehouse_uk)
9
+ [![Inline docs](http://inch-ci.org/github/andycroll/picturehouse_uk.svg?branch=master)](http://inch-ci.org/github/andycroll/picturehouse_uk)
9
10
 
10
11
  ## Installation
11
12
 
data/Rakefile CHANGED
@@ -31,4 +31,16 @@ task :console do
31
31
  IRB.start
32
32
  end
33
33
 
34
+ desc 'recreate test fixtures'
35
+ task :fixtures do
36
+ require 'picturehouse_uk'
37
+ require_relative 'rake/fixture_creator'
38
+
39
+ FixtureCreator.new.home
40
+ %w(Duke_Of_Yorks Dukes_At_Komedia Phoenix_Picturehouse
41
+ National_Media_Museum).each do |cinema_id|
42
+ FixtureCreator.new.cinema(cinema_id)
43
+ end
44
+ end
45
+
34
46
  task default: :test
@@ -1,6 +1,5 @@
1
+ require 'cinebase'
1
2
  require 'nokogiri'
2
- require 'tzinfo'
3
- require 'tzinfo/data'
4
3
 
5
4
  require_relative './picturehouse_uk/version'
6
5
 
@@ -10,8 +9,7 @@ require_relative './picturehouse_uk/internal/title_sanitizer'
10
9
  require_relative './picturehouse_uk/internal/website'
11
10
 
12
11
  require_relative './picturehouse_uk/cinema'
13
- require_relative './picturehouse_uk/film'
14
- require_relative './picturehouse_uk/screening'
12
+ require_relative './picturehouse_uk/performance'
15
13
 
16
14
  module PicturehouseUk
17
15
  end
@@ -1,59 +1,43 @@
1
1
  module PicturehouseUk
2
2
  # The object representing a cinema on the Picturehouse UK website
3
- class Cinema
3
+ class Cinema < Cinebase::Cinema
4
4
  # address css
5
- ADDRESS_CSS = '.static-content #contact-us + p:first'
5
+ ADDRESS_CSS = '.static-content #contact-us + p:first'.freeze
6
6
  # cinema link css
7
- CINEMA_LINKS_CSS = '.footer .col-sm-3 option + option'
8
-
9
- # @return [String] the brand of the cinema
10
- attr_reader :brand
11
- # @return [String] the id of the cinema on the Picturehouse website
12
- attr_reader :id
13
- # @return [String] the name of the cinema
14
- attr_reader :name
15
- # @return [String] the slug of the cinema
16
- attr_reader :slug
17
- # @return [String] the url of the cinema on the Picturehouse website
18
- attr_reader :url
19
-
20
- # @param [Hash] options id, name and url of the cinemas
21
- # @return [PicturehouseUk::Cinema]
22
- def initialize(options)
23
- @brand = 'Picturehouse'
24
- @id = options[:id]
25
- @name = options[:name]
26
- @slug = @name.downcase.gsub(/[^0-9a-z ]/, '').gsub(/\s+/, '-')
27
- @url = if options[:url][0] == '/'
28
- "http://www.picturehouses.com#{options[:url]}"
29
- else
30
- options[:url]
31
- end
32
- end
7
+ CINEMA_LINKS_CSS = '.footer .col-sm-3 option + option'.freeze
8
+
9
+ # @!attribute [r] id
10
+ # @return [Integer] the numeric id of the cinema on the Cineworld website
11
+
12
+ # @!method initialize(options)
13
+ # Constructor
14
+ # @param [String] id the cinema id of the cinema in capitalized snake case
15
+ # @return [PicturehouseUk::Cinema]
33
16
 
34
17
  # Return basic cinema information for all cinemas
35
18
  # @return [Array<PicturehouseUk::Cinema>]
36
19
  # @example
37
20
  # PicturehouseUk::Cinema.all
38
- # # => [<PicturehouseUK::Cinema ...>, <PicturehouseUK::Cinema ...>, ...]
21
+ # #=> [<PicturehouseUk::Cinema>, <PicturehouseUk::Cinema>, ...]
39
22
  def self.all
40
- cinema_links.map { |link| new_from_link(link) }
23
+ cinema_hash.keys.map { |id| new(id) }
41
24
  end
42
25
 
43
- # Find a single cinema
44
- # @param [String] id the cinema id as used on the picturehouses.co.uk website
45
- # @return [PicturehouseUk::Cinema, nil]
46
- # @example
47
- # PicturehouseUk::Cinema.find('Dukes_At_Komedia')
48
- # # => <PicturehouseUK::Cinema ...>
49
- def self.find(id)
50
- all.find { |cinema| cinema.id == id }
26
+ # @api private
27
+ def self.cinema_hash
28
+ @cinema_hash ||= ListParser.new(cinema_links).to_hash
51
29
  end
52
30
 
31
+ # @!method address
32
+ # Address of the cinema
33
+ # @return [Hash] of different address parts
34
+ # @see #adr
35
+
53
36
  # Address of the cinema
54
- # @return [Hash] of different address parts
37
+ # @return [Hash] contains :street_address, :extended_address,
38
+ # :locality, :postal_code, :country
55
39
  # @example
56
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
40
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
57
41
  # cinema.adr
58
42
  # #=> {
59
43
  # street_address: '44-47 Gardner Street',
@@ -67,91 +51,97 @@ module PicturehouseUk
67
51
  def adr
68
52
  PicturehouseUk::Internal::Parser::Address.new(address_node.to_s).address
69
53
  end
70
- alias_method :address, :adr
71
-
72
- # The second address line of of the cinema
73
- # @return [String, nil]
74
- # @example
75
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
76
- # cinema.extended_address
77
- # #=> 'North Laine'
78
- # @note Uses method naming as at http://microformats.org/wiki/adr
79
- def extended_address
80
- address[:extended_address]
81
- end
82
54
 
83
- # Films with showings scheduled at this cinema
84
- # @return [Array<PicturehouseUk::Film>]
55
+ # Brand of the cinema
56
+ # @return [String] which will always be 'Picturehouse'
85
57
  # @example
86
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
87
- # cinema.films
88
- # # => [<PicturehouseUk::Film ...>, <PicturehouseUk::Film ...>, ...]
89
- def films
90
- PicturehouseUk::Film.at(@id)
91
- end
58
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
59
+ # cinema.brand
60
+ # #=> 'Picturehouse'
61
+ def brand
62
+ 'Picturehouse'.freeze
63
+ end
64
+
65
+ # @!method country_name
66
+ # Country of the cinema
67
+ # @return [String] which will always be 'United Kingdom'
68
+ # @example
69
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
70
+ # cinema.country_name
71
+ # #=> 'United Kingdom'
72
+
73
+ # @!method extended_address
74
+ # The second address line of the cinema
75
+ # @return [String]
76
+ # @example
77
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
78
+ # cinema.extended_address
79
+ # #=> 'North Laine'
92
80
 
93
81
  # The name of the cinema (might include brand)
94
82
  # @return [String]
95
83
  # @example
96
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
84
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
97
85
  # cinema.full_name
98
86
  # #=> "Duke's At Komedia"
99
87
  def full_name
100
88
  name
101
89
  end
102
90
 
103
- # The locality (town) of the cinema
104
- # @return [String]
105
- # @example
106
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
107
- # cinema.locality
108
- # #=> 'Brighton'
109
- # @note Uses the standard method naming as at http://microformats.org/wiki/adr
110
- def locality
111
- address[:locality]
112
- end
91
+ # @!method locality
92
+ # The locality (town) of the cinema
93
+ # @return [String]
94
+ # @example
95
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
96
+ # cinema.locality
97
+ # #=> 'Brighton'
113
98
 
114
- # Post code of the cinema
99
+ # The name of the cinema
115
100
  # @return [String]
116
101
  # @example
117
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
118
- # cinema.postal_code
119
- # #=> 'BN1 1UN'
120
- # @note Uses the standard method naming as at http://microformats.org/wiki/adr
121
- def postal_code
122
- address[:postal_code]
123
- end
124
-
125
- # The region (county) of the cinema
102
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
103
+ # cinema.name
104
+ # #=> "Duke's At Komedia"
105
+ def name
106
+ self.class.cinema_hash.fetch(id, {})[:name]
107
+ end
108
+
109
+ # @!method postal_code
110
+ # Post code of the cinema
111
+ # @return [String]
112
+ # @example
113
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
114
+ # cinema.postal_code
115
+ # #=> 'BN1 1UN'
116
+
117
+ # @!method region
118
+ # The region (county) of the cinema if provided
119
+ # @return [String]
120
+ # @example
121
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
122
+ # cinema.region
123
+ # #=> 'East Sussex'
124
+
125
+ # @!method slug
126
+ # The URL-able slug of the cinema
127
+ # @return [String]
128
+ # @example
129
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
130
+ # cinema.slug
131
+ # #=> 'dukes-at-komedia'
132
+
133
+ # @!method street_address
134
+ # The street address of the cinema
135
+ # @return [String]
136
+ # @example
137
+ # cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
138
+ # cinema.street_address
139
+ # #=> '44-47 Gardner Street'
140
+
141
+ # The url of the cinema on the Picturehouse website
126
142
  # @return [String]
127
- # @example
128
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
129
- # cinema.region
130
- # #=> 'East Sussex'
131
- # @note Uses the standard method naming as at http://microformats.org/wiki/adr
132
- def region
133
- address[:region]
134
- end
135
-
136
- # All planned screenings
137
- # @return [Array<PicturehouseUk::Screening>]
138
- # @example
139
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
140
- # cinema.screenings
141
- # # => [<PicturehouseUk::Screening ...>, <PicturehouseUk::Screening ...>]
142
- def screenings
143
- PicturehouseUk::Screening.at(@id)
144
- end
145
-
146
- # The street adress of the cinema
147
- # @return a String
148
- # @example
149
- # cinema = PicturehouseUk::Cinema.find('Dukes_At_Komedia')
150
- # cinema.street_address
151
- # #=> '44-47 Gardner Street'
152
- # @note Uses the standard method naming as at http://microformats.org/wiki/adr
153
- def street_address
154
- address[:street_address]
143
+ def url
144
+ "http://www.picturehouses.com#/cinema/#{id}"
155
145
  end
156
146
 
157
147
  private
@@ -159,30 +149,49 @@ module PicturehouseUk
159
149
  def self.cinema_links
160
150
  home_doc.css(CINEMA_LINKS_CSS)
161
151
  end
152
+ private_class_method :cinema_links
162
153
 
163
154
  def self.home_doc
164
- @home_doc ||= Nokogiri::HTML(website.home)
165
- end
166
-
167
- def self.website
168
- @website ||= PicturehouseUk::Internal::Website.new
169
- end
170
-
171
- def self.new_from_link(link)
172
- url = link.get_attribute('data-href')
173
- name = link.children.first.to_s.split(' — ')[1]
174
-
175
- new(id: url.match(%r{/cinema/(.+)$})[1],
176
- name: name,
177
- url: url)
155
+ @home_doc ||=
156
+ Nokogiri::HTML(PicturehouseUk::Internal::Website.new.home)
178
157
  end
158
+ private_class_method :home_doc
179
159
 
180
160
  def address_node
181
161
  @address_node ||= info_doc.css(ADDRESS_CSS)
182
162
  end
183
163
 
184
164
  def info_doc
185
- @info_doc ||= Nokogiri::HTML(self.class.website.info(id))
165
+ @info_doc ||=
166
+ Nokogiri::HTML(PicturehouseUk::Internal::Website.new.info(id))
167
+ end
168
+
169
+ # @api private
170
+ # Utility class to parse the links spat out from the options
171
+ class ListParser
172
+ def initialize(nodes)
173
+ @nodes = nodes
174
+ end
175
+
176
+ def to_hash
177
+ @nodes.each_with_object({}) do |node, result|
178
+ result[id(node)] = { name: name(node), url: url(node) }
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ def id(node)
185
+ url(node).match(%r{/cinema/(.+)$})[1]
186
+ end
187
+
188
+ def name(node)
189
+ node.children.first.to_s.split(' — ')[1]
190
+ end
191
+
192
+ def url(node)
193
+ node.get_attribute('data-href')
194
+ end
186
195
  end
187
196
  end
188
197
  end
@@ -16,23 +16,35 @@ module PicturehouseUk
16
16
  def address
17
17
  {
18
18
  street_address: array[1],
19
- extended_address: array.length > 5 ? array[2] : nil,
19
+ extended_address: extended_address,
20
20
  locality: town,
21
- region: array[-2] == town ? nil : array[-2],
22
- postal_code: array[-1],
23
- country: 'United Kingdom'
21
+ region: region,
22
+ postal_code: postal_code,
23
+ country_name: 'United Kingdom'.freeze
24
24
  }
25
25
  end
26
26
 
27
27
  private
28
28
 
29
- def town
30
- @town ||= array[0].to_s.split(', ')[-1]
31
- end
32
-
33
29
  def array
34
30
  @array ||= Array(@html.gsub(/\<.?p.?\>/, '').split('<br>'))
35
31
  end
32
+
33
+ def extended_address
34
+ array.length > 5 ? array[2] : nil
35
+ end
36
+
37
+ def postal_code
38
+ array[-1]
39
+ end
40
+
41
+ def region
42
+ array[-2] == town ? nil : array[-2]
43
+ end
44
+
45
+ def town
46
+ @town ||= array[0].to_s.split(', ')[-1]
47
+ end
36
48
  end
37
49
  end
38
50
  end
@@ -4,23 +4,28 @@ module PicturehouseUk
4
4
  # @api private
5
5
  module Parser
6
6
  # Parses screenings page into an array of hashes for an individual cinema
7
- Screenings = Struct.new(:cinema_id) do
7
+ class Screenings
8
8
  # css for a day of films & screenings
9
- LISTINGS = '.listings li:not(.dark)'
10
- DATE = '.nav-collapse.collapse'
9
+ LISTINGS = '.listings li:not(.dark)'.freeze
10
+ DATE = '.nav-collapse.collapse'.freeze
11
+
12
+ def initialize(cinema_id)
13
+ @cinema_id = cinema_id
14
+ end
11
15
 
12
16
  # parse the cinema page into an array of screenings attributes
13
17
  # @return [Array<Hash>]
14
18
  def to_a
15
19
  doc.css(LISTINGS).flat_map do |node|
16
- FilmWithShowtimes.new(node, date_from_html(node.css(DATE).to_s)).to_a
20
+ FilmWithShowtimes.new(node,
21
+ date_from_html(node.css(DATE).to_s)).to_a
17
22
  end
18
23
  end
19
24
 
20
25
  private
21
26
 
22
27
  def date_from_html(html)
23
- if !!html.match(/listings-further-ahead-today/)
28
+ if html =~ /listings-further-ahead-today/
24
29
  Date.now
25
30
  else
26
31
  html.match(/listings-further-ahead-(\d{4})(\d{2})(\d{2})/) do |m|
@@ -34,16 +39,23 @@ module PicturehouseUk
34
39
  end
35
40
 
36
41
  def page
37
- @page ||= PicturehouseUk::Internal::Website.new.whats_on(cinema_id)
42
+ @page ||= PicturehouseUk::Internal::Website.new.whats_on(@cinema_id)
38
43
  end
39
44
  end
40
45
  end
41
46
 
42
- FilmWithShowtimes = Struct.new(:node, :date) do
47
+ # @api private
48
+ # collection of timings for a specific film
49
+ class FilmWithShowtimes
43
50
  # film name css
44
- NAME = '.top-mg-sm a'
51
+ NAME = '.top-mg-sm a'.freeze
45
52
  # variants css
46
- VARIANTS = '.film-times .col-xs-10'
53
+ VARIANTS = '.film-times .col-xs-10'.freeze
54
+
55
+ def initialize(node, date)
56
+ @node = node
57
+ @date = date
58
+ end
47
59
 
48
60
  # The film name
49
61
  # @return [String]
@@ -54,8 +66,8 @@ module PicturehouseUk
54
66
  # Showings hashes
55
67
  # @return [Array<Hash>]
56
68
  def to_a
57
- Array(node.css(VARIANTS)).flat_map do |variant|
58
- Variant.new(variant, date).to_a.map do |hash|
69
+ Array(@node.css(VARIANTS)).flat_map do |variant|
70
+ Variant.new(variant, @date).to_a.map do |hash|
59
71
  {
60
72
  film_name: name,
61
73
  dimension: dimension
@@ -67,18 +79,19 @@ module PicturehouseUk
67
79
  private
68
80
 
69
81
  def dimension
70
- raw_name.match(/3d/i) ? '3d' : '2d'
82
+ raw_name =~ /3d/i ? '3d' : '2d'
71
83
  end
72
84
 
73
85
  def raw_name
74
- @raw_name ||= node.css(NAME).children.first.to_s
86
+ @raw_name ||= @node.css(NAME).children.first.to_s
75
87
  end
76
88
  end
77
89
 
90
+ # @api private
78
91
  # variants can have multiple screenings
79
- Variant = Struct.new(:node, :date) do
80
- SHOWTIMES = '.btn'
81
- VARIANT = '.film-type-desc'
92
+ class Variant
93
+ SHOWTIMES = '.btn'.freeze
94
+ VARIANT = '.film-type-desc'.freeze
82
95
  TRANSLATOR = {
83
96
  'Big Scream' => 'baby',
84
97
  'IMAX' => 'imax',
@@ -86,13 +99,18 @@ module PicturehouseUk
86
99
  'NT Live' => 'arts',
87
100
  'Screen Arts' => 'arts',
88
101
  'Silver Screen' => 'senior'
89
- }
102
+ }.freeze
103
+
104
+ def initialize(node, date)
105
+ @node = node
106
+ @date = date
107
+ end
90
108
 
91
109
  # Variant arrays
92
110
  # @return [Array<Hash>]
93
111
  def to_a
94
- node.css(SHOWTIMES).map do |node|
95
- { variant: variant }.merge(Showtime.new(node, date).to_hash)
112
+ @node.css(SHOWTIMES).map do |node|
113
+ { variant: variant }.merge(Showtime.new(@node, @date).to_hash)
96
114
  end
97
115
  end
98
116
 
@@ -105,26 +123,50 @@ module PicturehouseUk
105
123
  end
106
124
 
107
125
  def variant_text
108
- @variant_text ||= node.css(VARIANT).to_s
126
+ @variant_text ||= @node.css(VARIANT).to_s
109
127
  end
110
128
  end
111
129
 
130
+ # @api private
112
131
  # parse an individual screening node
113
- Showtime = Struct.new(:node, :date) do
132
+ class Showtime
133
+ def initialize(node, date)
134
+ @node = node
135
+ @date = date
136
+ end
137
+
114
138
  def to_hash
115
139
  {
116
- booking_url: node['href'],
117
- time: time
140
+ booking_url: booking_url,
141
+ starting_at: starting_at
118
142
  }
119
143
  end
120
144
 
121
145
  private
122
146
 
123
- def time
124
- @time ||= begin
125
- hour, min = node.text.split('.').map(&:to_i)
126
- date.to_time + (hour * 60 + min) * 60
127
- end
147
+ def booking_url
148
+ return if href.nil? || href.empty?
149
+ "https://picturehouses.com#{href}"
150
+ end
151
+
152
+ def hour
153
+ split[0]
154
+ end
155
+
156
+ def href
157
+ @href ||= @node['href']
158
+ end
159
+
160
+ def min
161
+ split[1]
162
+ end
163
+
164
+ def split
165
+ @split ||= @node.text.split('.').map(&:to_i)
166
+ end
167
+
168
+ def starting_at
169
+ @starting_at ||= @date.to_time + (hour * 60 + min) * 60
128
170
  end
129
171
  end
130
172
  end