odeon_uk 5.1.1-aarch64-linux-gnu → 5.2.0-aarch64-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: b5a94d85e78d82fcdf946a593e322d2298aa56dc5306d007890b8464e4ab4b07
4
- data.tar.gz: ed5fe47b76aea7d19069d494b3ebf37ff5c3eac94f7059ff2c0bfe103769aeb5
3
+ metadata.gz: a06b3f9817ba8b98f06ae0f5b1eb2f8e97cbe7e788f43ae2c01287e98556d478
4
+ data.tar.gz: df991bb647072be922477d6eff931c3025275576be0b275249c392879b3c14bd
5
5
  SHA512:
6
- metadata.gz: 0164e6e55c6b9775df18c9e5c97be95eb928aadd1fd203e814d666629b44514fddf80ddc04ea61a6a54f5d91ad19db1646c226ebe1d90cedc6a949438b156d97
7
- data.tar.gz: 2528a86fbce3b73674c62bb679da7da3d5480239a0f36704bdcb1fddd841fddec7bce839b44677700878aeac8b74467626e532d2a6673fce54a8b8b1c69d04e9
6
+ metadata.gz: e88c2b7186c13bb8e1ee24c1002e038026fb9b6293200a2e331753fe5d767e9f7671245e5f7d34e699ebd94ad16a79f5e30d9c058292d61a7e06827c6a428fdc
7
+ data.tar.gz: 38411c85e123976e642d988ab341b18a69d407caa79893f542d1ac9bf83a9829cb021ebbfd3154c9acdf64d3d57dd0a769d3c342a11ec3f07b35b68c5f5f7945
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
+ ## 5.2.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
+ - `OdeonUk.ensure_token!` fetches a guest token only when there is none or it expires within an hour, and `OdeonUk.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 `OdeonUk.token`, so the next `ensure_token!` fetches a fresh one.
13
+ - Requires cinebase 5.6.
14
+
5
15
  ## 5.1.1 - 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 odeon_uk.gemspec
4
4
  gemspec
5
+
data/README.md CHANGED
@@ -57,9 +57,21 @@ OdeonUk.token = ENV.fetch('ODEON_TOKEN')
57
57
  ```
58
58
 
59
59
  Tokens currently last 12 hours. The gem checks the declared expiry but does not
60
- verify the signature. Missing, expired or rejected tokens raise
61
- `OdeonUk::AuthenticationError`; call `OdeonUk.acquire_token!` again. Normal API
62
- calls never acquire or renew a token automatically.
60
+ verify the signature. Normal API calls never acquire or renew a token
61
+ automatically. `OdeonUk.ensure_token!` fetches one only when there is no token,
62
+ or the token expires within an hour, and otherwise keeps the current one. Call
63
+ it before each batch of requests:
64
+
65
+ ``` ruby
66
+ OdeonUk.ensure_token!
67
+ OdeonUk.token_expires_at
68
+ #=> 2026-10-31 03:00:00 UTC
69
+ OdeonUk::Cinema.all
70
+ ```
71
+
72
+ Missing, expired or rejected tokens raise `OdeonUk::AuthenticationError`. A
73
+ token the API rejects is cleared from `OdeonUk.token`, so the next
74
+ `ensure_token!` fetches a fresh one.
63
75
 
64
76
  ### Cinema
65
77
 
@@ -93,6 +105,12 @@ cinema.full_name
93
105
  cinema.id
94
106
  #=> '824'
95
107
 
108
+ cinema.latitude
109
+ #=> 50.821 # Float, nil when the API has no location
110
+
111
+ cinema.longitude
112
+ #=> -0.1452
113
+
96
114
  cinema.url
97
115
  #=> 'https://www.odeon.co.uk/cinemas/brighton/'
98
116
  ```
@@ -105,6 +105,26 @@ module OdeonUk
105
105
  # cinema.locality
106
106
  # #=> 'Brighton'
107
107
 
108
+ # Latitude of the cinema
109
+ # @return [Float, nil] decimal degrees, or nil when unknown
110
+ # @example
111
+ # cinema = OdeonUk::Cinema.new(824)
112
+ # cinema.latitude
113
+ # #=> 50.821
114
+ def latitude
115
+ coordinates&.first
116
+ end
117
+
118
+ # Longitude of the cinema
119
+ # @return [Float, nil] decimal degrees, or nil when unknown
120
+ # @example
121
+ # cinema = OdeonUk::Cinema.new(824)
122
+ # cinema.longitude
123
+ # #=> -0.1452
124
+ def longitude
125
+ coordinates&.last
126
+ end
127
+
108
128
  # The name of the cinema
109
129
  # @return [String]
110
130
  # @example
@@ -164,6 +184,13 @@ module OdeonUk
164
184
 
165
185
  private
166
186
 
187
+ # 0, 0 marks a site whose position was never set
188
+ def coordinates
189
+ location = site_hash['location'] || {}
190
+ pair = [location['latitude'], location['longitude']]
191
+ pair.map(&:to_f) if pair.all?(Numeric) && pair.any?(&:nonzero?)
192
+ end
193
+
167
194
  def site_hash
168
195
  @site_hash ||= self.class.send(:sites_hash).fetch(id, {})
169
196
  end
@@ -14,19 +14,26 @@ module OdeonUk
14
14
 
15
15
  # @return [Array<Hash>] UK cinemas
16
16
  def sites
17
- client.get("sites").fetch("sites")
17
+ get("sites").fetch("sites")
18
18
  end
19
19
 
20
20
  # @param [String] cinema_id zero-padded site id, e.g. '017'
21
21
  # @param [Date] date business date
22
22
  # @return [Hash] showtimes plus their related films and attributes
23
23
  def showtimes(cinema_id, date)
24
- client.get("showtimes/by-business-date/#{date.iso8601}",
24
+ get("showtimes/by-business-date/#{date.iso8601}",
25
25
  "siteIds" => cinema_id)
26
26
  end
27
27
 
28
28
  private
29
29
 
30
+ def get(path, query = {})
31
+ client.get(path, query)
32
+ rescue Cinebase::Vista::AuthenticationError
33
+ OdeonUk.reject_token!(@client_token)
34
+ raise
35
+ end
36
+
30
37
  # One client keeps one connection open, so reuse it until the token changes.
31
38
  def client
32
39
  token = OdeonUk.token
@@ -1,6 +1,6 @@
1
1
  # Ruby interface for https://www.odeon.co.uk
2
- # @version 5.1.1
2
+ # @version 5.2.0
3
3
  module OdeonUk
4
4
  # Gem version
5
- VERSION = '5.1.1'.freeze
5
+ VERSION = '5.2.0'.freeze
6
6
  end
data/lib/odeon_uk.rb CHANGED
@@ -18,13 +18,10 @@ require_relative './odeon_uk/performance'
18
18
  module OdeonUk
19
19
  AuthenticationError = Cinebase::Vista::AuthenticationError
20
20
 
21
- # A raw guest JWT, without the "Bearer " prefix. Never persisted by the gem.
22
- class << self
23
- attr_accessor :token
24
-
25
- # Explicit acquisition/renewal; ordinary API calls never launch a subprocess.
26
- def acquire_token!(curl: nil)
27
- self.token = Internal::GuestToken.new(curl: curl).fetch
28
- end
21
+ extend Cinebase::Vista::TokenHolder
22
+
23
+ # @api private
24
+ def self.guest_token_class
25
+ Internal::GuestToken
29
26
  end
30
27
  end
data/odeon_uk.gemspec CHANGED
@@ -27,5 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency 'simplecov', '< 1'
28
28
  spec.add_development_dependency 'webmock'
29
29
 
30
- spec.add_runtime_dependency 'cinebase', '~> 5.5'
30
+ spec.add_runtime_dependency 'cinebase', '~> 5.6'
31
31
  end
@@ -3,6 +3,7 @@
3
3
  {
4
4
  "id": "017",
5
5
  "name": { "text": "Birmingham New Street" },
6
+ "location": { "latitude": 0, "longitude": 0 },
6
7
  "contactDetails": {
7
8
  "address": { "line1": "1 Example Arcade", "line2": "New Street", "city": "Birmingham" }
8
9
  }
@@ -10,6 +11,7 @@
10
11
  {
11
12
  "id": "153",
12
13
  "name": { "text": "London Leicester Square" },
14
+ "location": null,
13
15
  "contactDetails": {
14
16
  "address": { "line1": "2 Example Square", "line2": "", "city": "London" }
15
17
  }
@@ -17,6 +19,7 @@
17
19
  {
18
20
  "id": "824",
19
21
  "name": { "text": "Brighton" },
22
+ "location": { "latitude": 50.8215, "longitude": -0.146 },
20
23
  "contactDetails": {
21
24
  "address": { "line1": "3 Example Parade", "line2": "Seafront", "city": "Brighton" }
22
25
  }
@@ -66,6 +66,31 @@ class OdeonUk::CinemaTest < Minitest::Test
66
66
  end
67
67
  end
68
68
 
69
+ def test_latitude_and_longitude
70
+ with_fake_api do
71
+ cinema = OdeonUk::Cinema.new(824)
72
+
73
+ assert_in_delta 50.8215, cinema.latitude, 0.0001
74
+ assert_in_delta(-0.146, cinema.longitude, 0.0001)
75
+ end
76
+ end
77
+
78
+ def test_latitude_and_longitude_are_nil_without_a_location
79
+ with_fake_api do
80
+ [153, 955].each do |id|
81
+ assert_nil OdeonUk::Cinema.new(id).latitude
82
+ assert_nil OdeonUk::Cinema.new(id).longitude
83
+ end
84
+ end
85
+ end
86
+
87
+ def test_latitude_and_longitude_are_nil_at_zero_zero
88
+ with_fake_api do
89
+ assert_nil OdeonUk::Cinema.new(17).latitude
90
+ assert_nil OdeonUk::Cinema.new(17).longitude
91
+ end
92
+ end
93
+
69
94
  def test_name_removes_london_prefix
70
95
  with_fake_api do
71
96
  assert_equal 'Leicester Square', OdeonUk::Cinema.new(153).name
@@ -50,6 +50,14 @@ class OdeonUk::Internal::ApiResponseTest < Minitest::Test
50
50
  assert_raises(OdeonUk::AuthenticationError) { api.sites }
51
51
  end
52
52
 
53
+ def test_rejected_token_is_cleared
54
+ stub_request(:get, "#{BASE_URL}/sites").to_return(status: 401)
55
+
56
+ assert_raises(OdeonUk::AuthenticationError) { api.sites }
57
+
58
+ assert_nil OdeonUk.token
59
+ end
60
+
53
61
  def test_server_error_omits_body
54
62
  stub_request(:get, "#{BASE_URL}/sites").to_return(status: 500, body: 'secret')
55
63
 
@@ -51,6 +51,13 @@ class OdeonUk::Internal::GuestTokenTest < Minitest::Test
51
51
  assert_match(/curl-impersonate/, error.message)
52
52
  end
53
53
 
54
+ def test_ensure_token_fetches_only_without_a_current_token
55
+ token = token_expiring_at(Time.now + 7200)
56
+
57
+ assert_equal token, OdeonUk.ensure_token!(curl: fake_curl("#{page_with(token)}\n200"))
58
+ assert_equal token, OdeonUk.ensure_token!(curl: File.join(@directory, 'missing'))
59
+ end
60
+
54
61
  def test_acquire_token_sets_token
55
62
  token = token_expiring_at(Time.now + 3600)
56
63
 
@@ -8,6 +8,16 @@ class LiveCinemaTest < Minitest::Test
8
8
  assert_operator cinemas.size, :>, 90
9
9
  assert_equal 'Brighton', cinemas.detect { |cinema| cinema.id == '824' }.name
10
10
  end
11
+
12
+ def test_cinemas_have_uk_coordinates
13
+ located = OdeonUk::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
@@ -15,6 +15,11 @@ module ApiShapes
15
15
  %w(line1 line2 city).each do |key|
16
16
  assert_kind_of String, address.fetch(key), "site #{site['id']} #{key}"
17
17
  end
18
+ location = site['location'] || {}
19
+ %w(latitude longitude).each do |key|
20
+ value = location[key]
21
+ assert_kind_of Numeric, value, "site #{site['id']} #{key}" unless value.nil?
22
+ end
18
23
  end
19
24
  end
20
25
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: odeon_uk
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.1
4
+ version: 5.2.0
5
5
  platform: aarch64-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
  description: Pull movie & cinema information from the ODEON Vista API
125
125
  email:
126
126
  - andy@goodscary.com