cineworld_uk 4.3.2-x86_64-linux-gnu → 4.4.0-x86_64-linux-gnu

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ddd265d5bfbbedaec6d9004a093f70b02873a978ce710f60f0e811b5bc86e7c
4
- data.tar.gz: 39da4663b71cdf540a370f69d152287f851ef285cd497b95955cdf5859d7e1e4
3
+ metadata.gz: bda5e3524fc495d9cd2299b5a8563d1a0a06c1268565282dbc9f861b5902c173
4
+ data.tar.gz: f63b13ab73cd926c9954181a3270b24312bcf92635d283ac8f4517495f1bfec7
5
5
  SHA512:
6
- metadata.gz: a604de2a95540a40e026a4f005a84e6739f2b7d165f6c3e473df5fb97fe53a2fbb2bfd0c3d240154eed13f57cb592387460a8d98e3c6710379397ef10bd8873e
7
- data.tar.gz: ea2ca7e6dd557850ed0726910c95de48aed582d10f60dc33e7c9b5866fb128932504f4d13f7b4d6638ff9368b74b5af98901c3c6b2b6f13f1d42c79959824818
6
+ metadata.gz: 0242c5dd32fc1240940a8311b1772e4994288bb7ca4179b6ad1216b48dec12cb9b1e6f99347df0d35c73ccefa88725cd55a71a081ff72b6f7b3df076024ceb32
7
+ data.tar.gz: dd2f8ca6902de3884be7b8688738d264092fff5148060e4cad33e7f4c15c52e080d0def03d93c4ce749251738d105d209d538fe04ae42b0646683a3999c3fd41
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+ ## 4.4.0 - 2026-10-31
6
+
7
+ ### Added
8
+ - `Cinema#latitude` and `Cinema#longitude` as Floats in decimal degrees, from the Vista sites response. They are `nil` when a site has no location or sits at 0, 0.
9
+ - `CineworldUk.ensure_token!` fetches a guest token only when there is none or it expires within an hour, and `CineworldUk.token_expires_at` reads its expiry. Both come from cinebase's `Vista::TokenHolder`.
10
+
11
+ ### Changed
12
+ - A token the API rejects is cleared from `CineworldUk.token`, so the next `ensure_token!` fetches a fresh one.
13
+ - Requires cinebase 5.6.
14
+
5
15
  ## 4.3.2 - 2026-10-22
6
16
 
7
17
  ### Changed
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in cineworld_uk.gemspec
4
4
  gemspec
5
+
data/README.md CHANGED
@@ -36,6 +36,16 @@ CineworldUk.acquire_token!
36
36
  CineworldUk::Cinema.all
37
37
  ```
38
38
 
39
+ `acquire_token!` always fetches a new token. `ensure_token!` fetches one only
40
+ when there is no token, or the token expires within an hour, and otherwise
41
+ keeps the current one. Call it before each batch of requests:
42
+
43
+ ```ruby
44
+ CineworldUk.ensure_token!
45
+ CineworldUk.token_expires_at
46
+ #=> 2026-10-31 03:00:00 UTC
47
+ ```
48
+
39
49
  This shells out to **`curl-impersonate`**, not ordinary system `curl`, using its
40
50
  Chrome 150 TLS/HTTP profile. No Python, Chrome, JavaScript execution or browser
41
51
  cookies are needed. Platform-specific gem packages include the pinned
@@ -89,8 +99,9 @@ The gem sends `Authorization: Bearer ...` and `Connect-Region-Code: GB` to
89
99
  automatically.
90
100
 
91
101
  Missing tokens and HTTP 401 responses raise `CineworldUk::AuthenticationError`.
92
- Call `CineworldUk.acquire_token!` again, or set `CineworldUk.token` with a fresh
93
- token, before retrying. Tokens are never persisted by the gem. Other HTTP
102
+ A token the API rejects is cleared from `CineworldUk.token`, so call
103
+ `CineworldUk.ensure_token!`, or set `CineworldUk.token` with a fresh token,
104
+ before retrying. Tokens are never persisted by the gem. Other HTTP
94
105
  failures raise an error rather than returning an empty schedule. Redirects are
95
106
  not followed, so credentials are not forwarded to another host.
96
107
 
@@ -134,6 +145,12 @@ cinema.full_name
134
145
  cinema.id
135
146
  #=> 14
136
147
 
148
+ cinema.latitude
149
+ #=> 50.8125991821289
150
+
151
+ cinema.longitude
152
+ #=> -0.104699999094009
153
+
137
154
  cinema.postal_code
138
155
  #=> 'BN2 5UF'
139
156
  ```
data/cineworld_uk.gemspec CHANGED
@@ -25,6 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'simplecov', '< 1'
26
26
  spec.add_development_dependency 'webmock'
27
27
 
28
- spec.add_runtime_dependency 'cinebase', '~> 5.5'
28
+ spec.add_runtime_dependency 'cinebase', '~> 5.6'
29
29
  spec.add_runtime_dependency 'nokogiri', '>= 1.11'
30
30
  end
@@ -101,6 +101,26 @@ module CineworldUk
101
101
  # cinema.locality
102
102
  # #=> 'Brighton'
103
103
 
104
+ # Latitude of the cinema
105
+ # @return [Float, nil] decimal degrees, or nil when unknown
106
+ # @example
107
+ # cinema = CineworldUk::Cinema.new(14)
108
+ # cinema.latitude
109
+ # #=> 50.8125991821289
110
+ def latitude
111
+ coordinates&.first
112
+ end
113
+
114
+ # Longitude of the cinema
115
+ # @return [Float, nil] decimal degrees, or nil when unknown
116
+ # @example
117
+ # cinema = CineworldUk::Cinema.new(14)
118
+ # cinema.longitude
119
+ # #=> -0.104699999094009
120
+ def longitude
121
+ coordinates&.last
122
+ end
123
+
104
124
  # The name of the cinema
105
125
  # @return [String]
106
126
  # @example
@@ -164,5 +184,19 @@ module CineworldUk
164
184
  JSON.parse(api.cinema_list)['body']['cinemas']
165
185
  end
166
186
  private_class_method :cinema_list_json
187
+
188
+ private
189
+
190
+ def cinema_hash
191
+ formatted_id = @id.to_s.rjust(3, '0')
192
+ self.class.send(:cinema_list_json).find { |cinema| cinema['id'] == formatted_id } || {}
193
+ end
194
+
195
+ # 0, 0 marks a site whose position was never set
196
+ def coordinates
197
+ location = cinema_hash['location'] || {}
198
+ pair = [location['latitude'], location['longitude']]
199
+ pair.map(&:to_f) if pair.all?(Numeric) && pair.any?(&:nonzero?)
200
+ end
167
201
  end
168
202
  end
@@ -25,6 +25,7 @@ module CineworldUk
25
25
  {
26
26
  "id" => site.fetch("id"),
27
27
  "displayName" => site.fetch("name").fetch("text"),
28
+ "location" => site["location"],
28
29
  "addressInfo" => {
29
30
  "address1" => address.dig("line1", "text"),
30
31
  "city" => address.dig("line2", "text"),
@@ -87,6 +88,9 @@ module CineworldUk
87
88
 
88
89
  def fetch(path, query = {})
89
90
  client.get(path, query)
91
+ rescue Cinebase::Vista::AuthenticationError
92
+ CineworldUk.reject_token!(@client_token)
93
+ raise
90
94
  end
91
95
 
92
96
  # One client keeps one connection open, so reuse it until the token changes.
@@ -1,6 +1,6 @@
1
1
  # Ruby interface for http://www.cineworld.co.uk
2
- # @version 4.3.2
2
+ # @version 4.4.0
3
3
  module CineworldUk
4
4
  # Gem version
5
- VERSION = "4.3.2".freeze
5
+ VERSION = "4.4.0".freeze
6
6
  end
data/lib/cineworld_uk.rb CHANGED
@@ -20,13 +20,10 @@ require_relative './cineworld_uk/performance'
20
20
  module CineworldUk
21
21
  AuthenticationError = Cinebase::Vista::AuthenticationError
22
22
 
23
- # A raw guest JWT, without the "Bearer " prefix. Never persisted by the gem.
24
- class << self
25
- attr_accessor :token
26
-
27
- # Explicit acquisition/renewal; ordinary API calls never launch a subprocess.
28
- def acquire_token!(curl: nil)
29
- self.token = Internal::GuestToken.new(curl: curl).fetch
30
- end
23
+ extend Cinebase::Vista::TokenHolder
24
+
25
+ # @api private
26
+ def self.guest_token_class
27
+ Internal::GuestToken
31
28
  end
32
29
  end
@@ -3,6 +3,7 @@
3
3
  {
4
4
  "id": "014",
5
5
  "name": { "text": "Brighton" },
6
+ "location": { "latitude": 50.8125991821289, "longitude": -0.104699999094009 },
6
7
  "contactDetails": {
7
8
  "addressV2": {
8
9
  "line1": { "text": "1 Example Marina" },
@@ -15,6 +16,7 @@
15
16
  {
16
17
  "id": "088",
17
18
  "name": { "text": "Glasgow Silverburn" },
19
+ "location": { "latitude": 0, "longitude": 0 },
18
20
  "contactDetails": {
19
21
  "addressV2": {
20
22
  "line1": { "text": "2 Example Centre" },
@@ -27,6 +29,7 @@
27
29
  {
28
30
  "id": "103",
29
31
  "name": { "text": "Leicester Square" },
32
+ "location": null,
30
33
  "contactDetails": {
31
34
  "addressV2": {
32
35
  "line1": { "text": "3 Example Square" },
@@ -61,6 +61,25 @@ class CineworldUk::CinemaTest < Minitest::Test
61
61
  assert_equal 'Example: Riverside', CineworldUk::Cinema.new(999).name
62
62
  end
63
63
 
64
+ def test_latitude_and_longitude
65
+ cinema = CineworldUk::Cinema.new(14)
66
+
67
+ assert_in_delta 50.8126, cinema.latitude, 0.0001
68
+ assert_in_delta(-0.1047, cinema.longitude, 0.0001)
69
+ end
70
+
71
+ def test_latitude_and_longitude_are_nil_without_a_location
72
+ [103, 999].each do |id|
73
+ assert_nil CineworldUk::Cinema.new(id).latitude
74
+ assert_nil CineworldUk::Cinema.new(id).longitude
75
+ end
76
+ end
77
+
78
+ def test_latitude_and_longitude_are_nil_at_zero_zero
79
+ assert_nil CineworldUk::Cinema.new(88).latitude
80
+ assert_nil CineworldUk::Cinema.new(88).longitude
81
+ end
82
+
64
83
  def test_locality
65
84
  assert_equal 'Brighton', CineworldUk::Cinema.new(14).locality
66
85
  assert_equal 'London', CineworldUk::Cinema.new(103).locality
@@ -68,6 +68,15 @@ class CineworldUk::Internal::ApiResponseTest < Minitest::Test
68
68
  refute_includes error.message, "expired-secret"
69
69
  end
70
70
 
71
+ def test_rejected_token_is_cleared
72
+ CineworldUk.token = "rejected-token"
73
+ stub_request(:get, "#{BASE_URL}sites").to_return(status: 401)
74
+
75
+ assert_raises(CineworldUk::AuthenticationError) { CineworldUk::Cinema.all }
76
+
77
+ assert_nil CineworldUk.token
78
+ end
79
+
71
80
  def test_uses_replacement_token
72
81
  CineworldUk.token = "replacement"
73
82
  stub_request(:get, "#{BASE_URL}sites")
@@ -27,6 +27,16 @@ class CineworldUk::Internal::GuestTokenTest < Minitest::Test
27
27
  end
28
28
  end
29
29
 
30
+ def test_ensure_token_fetches_only_without_a_current_token
31
+ CineworldUk.token = nil
32
+ html, token = token_page(Time.now.to_i + 7200)
33
+ serve_token_page(html)
34
+
35
+ assert_equal token, CineworldUk.ensure_token!(curl: @curl)
36
+ serve_token_page("", exit_code: 7)
37
+ assert_equal token, CineworldUk.ensure_token!(curl: @curl)
38
+ end
39
+
30
40
  def test_rejects_expired_tokens_including_boundary_and_keeps_current_token
31
41
  now = Time.at(1_800_000_000)
32
42
  Time.stub(:now, now) do
@@ -8,6 +8,16 @@ class LiveCinemaTest < Minitest::Test
8
8
  assert_operator cinemas.size, :>, 18
9
9
  assert_equal 'Brighton', cinemas.detect { |cinema| cinema.id == 14 }.name
10
10
  end
11
+
12
+ def test_cinemas_have_uk_coordinates
13
+ located = CineworldUk::Cinema.all.select(&:latitude)
14
+
15
+ refute_empty located
16
+ located.each do |cinema|
17
+ assert_includes 49.0..61.0, cinema.latitude, "#{cinema.id} #{cinema.name}"
18
+ assert_includes(-9.0..2.0, cinema.longitude, "#{cinema.id} #{cinema.name}")
19
+ end
20
+ end
11
21
  end
12
22
 
13
23
  class LivePerformanceTest < Minitest::Test
@@ -16,6 +16,11 @@ module ApiShapes
16
16
  line = address.fetch(key)
17
17
  assert_kind_of String, line.fetch('text'), "site #{site['id']} #{key}" unless line.nil?
18
18
  end
19
+ location = site['location'] || {}
20
+ %w(latitude longitude).each do |key|
21
+ value = location[key]
22
+ assert_kind_of Numeric, value, "site #{site['id']} #{key}" unless value.nil?
23
+ end
19
24
  end
20
25
  end
21
26
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cineworld_uk
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.2
4
+ version: 4.4.0
5
5
  platform: x86_64-linux-gnu
6
6
  authors:
7
7
  - Andy Croll
@@ -113,14 +113,14 @@ dependencies:
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '5.5'
116
+ version: '5.6'
117
117
  type: :runtime
118
118
  prerelease: false
119
119
  version_requirements: !ruby/object:Gem::Requirement
120
120
  requirements:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
- version: '5.5'
123
+ version: '5.6'
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: nokogiri
126
126
  requirement: !ruby/object:Gem::Requirement