picturehouse_uk 5.0.0 → 6.0.1
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/.github/workflows/ci.yml +34 -13
- data/.github/workflows/release.yml +61 -14
- data/CHANGELOG.md +31 -0
- data/README.md +128 -24
- data/Rakefile +4 -13
- data/lib/picturehouse_uk/cinema.rb +60 -146
- data/lib/picturehouse_uk/internal/api_response.rb +42 -0
- data/lib/picturehouse_uk/internal/guest_token.rb +28 -0
- data/lib/picturehouse_uk/internal/parser/api/showtime.rb +70 -0
- data/lib/picturehouse_uk/internal/title_sanitizer.rb +8 -3
- data/lib/picturehouse_uk/performance.rb +37 -17
- data/lib/picturehouse_uk/version.rb +3 -3
- data/lib/picturehouse_uk.rb +19 -5
- data/picturehouse_uk.gemspec +7 -5
- data/test/fixtures/api/showtimes-019.json +62 -0
- data/test/fixtures/api/sites.json +40 -0
- data/test/lib/picturehouse_uk/cinema_test.rb +50 -188
- data/test/lib/picturehouse_uk/internal/api_response_test.rb +80 -0
- data/test/lib/picturehouse_uk/internal/fixture_shape_test.rb +14 -0
- data/test/lib/picturehouse_uk/internal/guest_token_test.rb +71 -0
- data/test/lib/picturehouse_uk/internal/parser/api/showtime_test.rb +59 -0
- data/test/lib/picturehouse_uk/internal/title_sanitizer_test.rb +192 -344
- data/test/lib/picturehouse_uk/performance_test.rb +85 -171
- data/test/lib/picturehouse_uk/version_test.rb +3 -3
- data/test/live/api_shape_test.rb +19 -0
- data/test/live/integration_test.rb +17 -41
- data/test/live/live_helper.rb +11 -0
- data/test/package_test.rb +56 -0
- data/test/support/api_fixtures_helper.rb +17 -0
- data/test/support/api_shapes.rb +48 -0
- data/test/support/fake_api_response.rb +20 -0
- data/test/test_helper.rb +7 -6
- metadata +71 -55
- data/lib/picturehouse_uk/internal/api.rb +0 -42
- data/lib/picturehouse_uk/internal/parser/address.rb +0 -66
- data/lib/picturehouse_uk/internal/parser/screenings.rb +0 -125
- data/lib/picturehouse_uk/internal/website.rb +0 -44
- data/rake/fixture_creator.rb +0 -62
- data/test/fixtures/cinemas.html +0 -4262
- data/test/fixtures/duke-of-york-s-picturehouse/cinema.html +0 -6187
- data/test/fixtures/duke-of-york-s-picturehouse/get_movies.json +0 -120552
- data/test/fixtures/duke-of-york-s-picturehouse/information.html +0 -3725
- data/test/fixtures/duke-s-at-komedia/cinema.html +0 -6138
- data/test/fixtures/duke-s-at-komedia/get_movies.json +0 -112
- data/test/fixtures/duke-s-at-komedia/information.html +0 -3690
- data/test/fixtures/home.html +0 -6254
- data/test/fixtures/phoenix-picturehouse/cinema.html +0 -6089
- data/test/fixtures/phoenix-picturehouse/get_movies.json +0 -120552
- data/test/fixtures/phoenix-picturehouse/information.html +0 -3630
- data/test/lib/picturehouse_uk/internal/api_test.rb +0 -92
- data/test/lib/picturehouse_uk/internal/parser/address_test.rb +0 -53
- data/test/lib/picturehouse_uk/internal/parser/screenings_test.rb +0 -124
- data/test/lib/picturehouse_uk/internal/website_test.rb +0 -67
- data/test/support/fake_api.rb +0 -16
- data/test/support/fake_website.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dbd3a058670fb5fddd59683dc130ca62792a7e8774f1ead04c64381f7a74eada
|
|
4
|
+
data.tar.gz: 524f5901b6da77584e89e3bdcc79e8350a25c61ececc435b96db0d472060892b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b95c2afbb020c0cacd05491ff598b548b8398b31157bbc47f018aee02ff2ac64f0c4746df751a46dc9c9cb23df11ca2a93e536dd3b40577afabbe6e20be1d57
|
|
7
|
+
data.tar.gz: 782d007b28cc7afa7e9ae296169aa443488400241110fe290a9cad4aa53b6170be1cbab1cbd489655811df48ca9dd71a69d47ade2efa096b4e431d99ec410779
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -9,18 +9,39 @@ on:
|
|
|
9
9
|
jobs:
|
|
10
10
|
test:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
|
-
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
ruby-version: ['3.4', '4.0']
|
|
13
15
|
steps:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
bundler-cache: true
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
- run: bundle exec rake test
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
package:
|
|
24
|
+
strategy:
|
|
25
|
+
matrix:
|
|
26
|
+
include:
|
|
27
|
+
- runner: ubuntu-latest
|
|
28
|
+
platform: x86_64-linux-gnu
|
|
29
|
+
- runner: ubuntu-24.04-arm
|
|
30
|
+
platform: aarch64-linux-gnu
|
|
31
|
+
- runner: macos-15
|
|
32
|
+
platform: arm64-darwin
|
|
33
|
+
- runner: macos-15-intel
|
|
34
|
+
platform: x86_64-darwin
|
|
35
|
+
runs-on: ${{ matrix.runner }}
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
- uses: ruby/setup-ruby@v1
|
|
39
|
+
with:
|
|
40
|
+
ruby-version: '4.0'
|
|
41
|
+
bundler-cache: true
|
|
42
|
+
- run: bundle exec rake 'package:native[${{ matrix.platform }}]'
|
|
43
|
+
- run: PICTUREHOUSE_PACKAGE="$(echo pkg/*.gem)" bundle exec ruby test/package_test.rb
|
|
44
|
+
- uses: actions/upload-artifact@v4
|
|
45
|
+
with:
|
|
46
|
+
name: gem-${{ matrix.platform }}
|
|
47
|
+
path: pkg/*.gem
|
|
@@ -4,23 +4,70 @@ on:
|
|
|
4
4
|
workflow_dispatch:
|
|
5
5
|
|
|
6
6
|
jobs:
|
|
7
|
+
package:
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
include:
|
|
11
|
+
- runner: ubuntu-latest
|
|
12
|
+
platform: x86_64-linux-gnu
|
|
13
|
+
- runner: ubuntu-24.04-arm
|
|
14
|
+
platform: aarch64-linux-gnu
|
|
15
|
+
- runner: macos-15
|
|
16
|
+
platform: arm64-darwin
|
|
17
|
+
- runner: macos-15-intel
|
|
18
|
+
platform: x86_64-darwin
|
|
19
|
+
runs-on: ${{ matrix.runner }}
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: ruby/setup-ruby@v1
|
|
23
|
+
with:
|
|
24
|
+
ruby-version: '4.0'
|
|
25
|
+
bundler-cache: true
|
|
26
|
+
- run: bundle exec rake 'package:native[${{ matrix.platform }}]'
|
|
27
|
+
- run: PICTUREHOUSE_PACKAGE="$(echo pkg/*.gem)" bundle exec ruby test/package_test.rb
|
|
28
|
+
- uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: gem-${{ matrix.platform }}
|
|
31
|
+
path: pkg/*.gem
|
|
32
|
+
|
|
7
33
|
release:
|
|
34
|
+
needs: package
|
|
8
35
|
runs-on: ubuntu-latest
|
|
9
36
|
permissions:
|
|
10
37
|
id-token: write
|
|
11
38
|
contents: write
|
|
12
|
-
|
|
13
39
|
steps:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
uses: rubygems/
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: ruby/setup-ruby@v1
|
|
42
|
+
with:
|
|
43
|
+
ruby-version: '4.0'
|
|
44
|
+
bundler-cache: true
|
|
45
|
+
- run: bundle exec rake test
|
|
46
|
+
- run: bundle exec rake build
|
|
47
|
+
- uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
path: pkg
|
|
50
|
+
pattern: gem-*
|
|
51
|
+
merge-multiple: true
|
|
52
|
+
- uses: rubygems/configure-rubygems-credentials@v1.0.0
|
|
53
|
+
- name: Push the source and platform gems
|
|
54
|
+
run: |
|
|
55
|
+
for gem in pkg/*.gem; do
|
|
56
|
+
if output=$(gem push "$gem" 2>&1); then
|
|
57
|
+
echo "$output"
|
|
58
|
+
elif echo "$output" | grep -q "Repushing of gem versions is not allowed"; then
|
|
59
|
+
echo "Already published: $gem"
|
|
60
|
+
else
|
|
61
|
+
echo "$output"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
done
|
|
65
|
+
- name: Tag the release
|
|
66
|
+
run: |
|
|
67
|
+
tag="v$(ruby -e 'print Gem::Specification.load(Dir["*.gemspec"].first).version')"
|
|
68
|
+
if git ls-remote --exit-code --tags origin "$tag" >/dev/null; then
|
|
69
|
+
echo "$tag already exists"
|
|
70
|
+
else
|
|
71
|
+
git tag "$tag"
|
|
72
|
+
git push origin "$tag"
|
|
73
|
+
fi
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
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
|
+
## [6.0.1] - 2026-10-22
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Reuse one Vista client, and so one kept-alive connection, for all requests until the token changes, instead of opening a TLS connection for every request. Requires cinebase 5.5.
|
|
9
|
+
|
|
10
|
+
## [6.0.0] - 2026-10-22
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Retrieve cinemas and performances from the Vista API behind picturehouses.com, as cineworld_uk and odeon_uk do, instead of scraping HTML pages and the `get-movies-ajax` endpoint.
|
|
14
|
+
- **BREAKING**: Cinema ids are Vista site ids, zero-padded strings such as `'019'` for Duke's at Komedia, instead of page slugs such as `'duke-s-at-komedia'`.
|
|
15
|
+
- **BREAKING**: `Performance.at` accepts inclusive `from:` and `to:` business dates, defaulting to today in London and the following 13 days. One request per day.
|
|
16
|
+
- **BREAKING**: Require cinebase 5.4, and so Ruby 3.3 or newer. Nokogiri is no longer needed.
|
|
17
|
+
- `Cinema#adr` comes from the API's address lines; `region` is no longer given. `Cinema#url` points at the current cinema pages.
|
|
18
|
+
- Film titles also use cinebase's common rules and lose `Dog-Friendly -`, `Relaxed-Screening:`, `LMF Presents:`, `Green Screen:` and `FFQ:` prefixes.
|
|
19
|
+
- Tests use hand-written fixtures in place of recorded pages and API responses.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- `PicturehouseUk.token` and `PicturehouseUk.acquire_token!`, with platform gems that bundle curl-impersonate 2.2.3.
|
|
23
|
+
- `Performance#film_year`, `#subtitled?` and `#festival`.
|
|
24
|
+
- Variants from the current Vista attributes: `audio_described`, `baby`, `kids`, `q&a`, `relaxed`, `senior`, `subtitled` and `arts`.
|
|
25
|
+
- Live API shape tests that check real responses without storing them.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Performance times were London local times labelled as UTC, an hour out during British Summer Time. They now use Vista's explicit offsets.
|
|
29
|
+
- Booking links used the cinema slug and now use Vista's showtime id, as in `https://web.picturehouses.com/order/showtimes/019-41632/seats`.
|
|
30
|
+
- Performances could include other cinemas' films returned by the old endpoint.
|
|
31
|
+
- Unit tests failed under Minitest 6, which moved `stub` into `minitest-mock`.
|
|
32
|
+
|
|
33
|
+
### Removed
|
|
34
|
+
- HTML scraping, the `fixtures` rake task, and live tests in CI.
|
|
35
|
+
|
|
5
36
|
## [5.0.0] - 2025-10-31
|
|
6
37
|
|
|
7
38
|
### Added
|
data/README.md
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# PicturehouseUk
|
|
2
2
|
|
|
3
|
-
A simple gem to
|
|
3
|
+
A simple gem to pull cinemas and performances from the Vista API behind the [Picturehouse website](https://www.picturehouses.com) and spit out useful formatted info.
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/rb/picturehouse_uk)
|
|
6
|
-
[](https://codeclimate.com/github/andycroll/picturehouse_uk)
|
|
7
|
-
[](https://codeclimate.com/github/andycroll/picturehouse_uk/coverage)
|
|
8
|
-
[](https://travis-ci.org/andycroll/picturehouse_uk)
|
|
9
|
-
[](http://inch-ci.org/github/andycroll/picturehouse_uk)
|
|
10
6
|
|
|
11
7
|
## Installation
|
|
12
8
|
|
|
@@ -24,44 +20,152 @@ Or install it yourself as:
|
|
|
24
20
|
|
|
25
21
|
## Usage
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
The gem conforms to the API set down in the `cinebase` gem, [andycroll/cinebase](https://github.com/andycroll/cinebase), which provides a lot of useful base vocabulary and repetitive code for this series of cinema focussed gems.
|
|
24
|
+
|
|
25
|
+
Performance titles are sanitized as much as possible, removing 'screening type' information, years, Q&A add-ons and festival prefixes, and standardising theatre and opera event naming.
|
|
26
|
+
|
|
27
|
+
### Guest token
|
|
28
|
+
|
|
29
|
+
Every API call needs the public guest token the Picturehouse ticketing site
|
|
30
|
+
gives each visitor. Fetch one before anything else:
|
|
31
|
+
|
|
32
|
+
``` ruby
|
|
33
|
+
PicturehouseUk.acquire_token!
|
|
34
|
+
PicturehouseUk::Cinema.all
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This shells out to **`curl-impersonate`**, not ordinary system `curl`, using its
|
|
38
|
+
Chrome 150 TLS/HTTP profile, and reads `gasToken` from the page data of
|
|
39
|
+
`https://web.picturehouses.com/`. No browser or JavaScript execution is needed.
|
|
40
|
+
|
|
41
|
+
Platform-specific gem packages include the pinned `curl-impersonate` 2.2.3
|
|
42
|
+
executable for glibc Linux (x86-64, ARM64) and macOS (Intel, Apple Silicon).
|
|
43
|
+
On other platforms, or when installing from Git, install a
|
|
44
|
+
[curl-impersonate release](https://github.com/lexiforest/curl-impersonate/releases/tag/v2.2.3)
|
|
45
|
+
yourself and put it on your `PATH` or pass its path:
|
|
28
46
|
|
|
29
47
|
``` ruby
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<PicturehouseUK::Cinema brand="Picturehouse" name="Duke's at Komedia" slug="dukes-at-komedia" chain_id="Dukes_At_Komedia" url="...">,
|
|
33
|
-
<PicturehouseUK::Cinema brand="Picturehouse" name="Duke of York's" slug="duke-of-yorks" chain_id="Duke_Of_Yorks" url="...">,
|
|
34
|
-
...
|
|
35
|
-
]
|
|
48
|
+
PicturehouseUk.acquire_token!(curl: '/opt/curl-impersonate/curl-impersonate')
|
|
49
|
+
```
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
Or supply your own current guest JWT, without the `Bearer ` prefix:
|
|
52
|
+
|
|
53
|
+
``` ruby
|
|
54
|
+
PicturehouseUk.token = ENV.fetch('PICTUREHOUSE_TOKEN')
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Tokens currently last 12 hours. The gem checks the declared expiry but does not
|
|
58
|
+
verify the signature. Missing, expired or rejected tokens raise
|
|
59
|
+
`PicturehouseUk::AuthenticationError`; call `PicturehouseUk.acquire_token!`
|
|
60
|
+
again. Normal API calls never acquire or renew a token automatically.
|
|
61
|
+
|
|
62
|
+
### Cinema
|
|
63
|
+
|
|
64
|
+
Cinema ids are Vista site ids. Duke's at Komedia is `'019'`.
|
|
65
|
+
|
|
66
|
+
``` ruby
|
|
67
|
+
PicturehouseUk::Cinema.all
|
|
68
|
+
#=> [<PicturehouseUk::Cinema ...>, <PicturehouseUk::Cinema ...>, ...]
|
|
69
|
+
|
|
70
|
+
cinema = PicturehouseUk::Cinema.new(19)
|
|
71
|
+
#=> <PicturehouseUk::Cinema ...>
|
|
72
|
+
|
|
73
|
+
cinema.adr
|
|
74
|
+
#=> {
|
|
75
|
+
street_address: '44-47 Gardner Street',
|
|
76
|
+
extended_address: nil,
|
|
77
|
+
locality: 'Brighton',
|
|
78
|
+
region: nil,
|
|
79
|
+
postal_code: 'BN1 1UN',
|
|
80
|
+
country_name: 'United Kingdom'
|
|
81
|
+
}
|
|
39
82
|
|
|
40
83
|
cinema.brand
|
|
41
84
|
#=> 'Picturehouse'
|
|
42
85
|
|
|
43
|
-
cinema.
|
|
44
|
-
#=> '
|
|
86
|
+
cinema.name
|
|
87
|
+
#=> "Duke's at Komedia"
|
|
88
|
+
|
|
89
|
+
cinema.id
|
|
90
|
+
#=> '019'
|
|
45
91
|
|
|
46
92
|
cinema.url
|
|
47
|
-
#=>
|
|
93
|
+
#=> 'https://www.picturehouses.com/cinema/duke-s-at-komedia'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Performances
|
|
97
|
+
|
|
98
|
+
`Performance.at` returns performances for an inclusive range of business dates,
|
|
99
|
+
defaulting to today in London and the following 13 days. It makes one request
|
|
100
|
+
per day.
|
|
101
|
+
|
|
102
|
+
``` ruby
|
|
103
|
+
PicturehouseUk::Performance.at(19, from: Date.today, to: Date.today + 2)
|
|
104
|
+
#=> [<PicturehouseUk::Performance ...>, <PicturehouseUk::Performance ...>, ...]
|
|
105
|
+
|
|
106
|
+
performance = PicturehouseUk::Performance.at(19).first
|
|
107
|
+
#=> <PicturehouseUk::Performance ...>
|
|
108
|
+
|
|
109
|
+
performance.film_name
|
|
110
|
+
#=> 'Sense and Sensibility'
|
|
111
|
+
|
|
112
|
+
performance.film_year
|
|
113
|
+
#=> 2026 # from the title, nil when it has no year
|
|
48
114
|
|
|
49
|
-
|
|
50
|
-
#=>
|
|
115
|
+
performance.dimension
|
|
116
|
+
#=> '2d'
|
|
51
117
|
|
|
52
|
-
|
|
53
|
-
#=> [
|
|
118
|
+
performance.variant
|
|
119
|
+
#=> ['audio_described', 'subtitled']
|
|
54
120
|
|
|
121
|
+
performance.subtitled?
|
|
122
|
+
#=> true
|
|
123
|
+
|
|
124
|
+
performance.festival
|
|
125
|
+
#=> #<data Cinebase::Festival name="CFF", year=2026> # nil outside festivals
|
|
126
|
+
|
|
127
|
+
performance.starting_at
|
|
128
|
+
#=> 2026-10-23 15:00:00 UTC
|
|
129
|
+
|
|
130
|
+
performance.booking_url
|
|
131
|
+
#=> 'https://web.picturehouses.com/order/showtimes/019-41632/seats'
|
|
132
|
+
|
|
133
|
+
performance.cinema_name
|
|
134
|
+
#=> "Duke's at Komedia"
|
|
135
|
+
|
|
136
|
+
performance.cinema_id
|
|
137
|
+
#=> '019'
|
|
55
138
|
```
|
|
56
139
|
|
|
57
|
-
|
|
140
|
+
Variants are `arts`, `audio_described`, `baby`, `kids`, `q&a`, `relaxed`,
|
|
141
|
+
`senior` and `subtitled`. Picturehouse doesn't tag audio languages, so
|
|
142
|
+
`language` is always `nil`.
|
|
58
143
|
|
|
59
|
-
|
|
144
|
+
## Tests
|
|
60
145
|
|
|
146
|
+
```sh
|
|
147
|
+
bundle exec rake # unit tests against fixtures
|
|
148
|
+
bundle exec rake live # live API, needs curl-impersonate or PICTUREHOUSE_TOKEN
|
|
61
149
|
```
|
|
62
|
-
|
|
150
|
+
|
|
151
|
+
Fixtures in `test/fixtures/api` are hand-written: invented films and
|
|
152
|
+
addresses in the shape of Vista responses, never recorded API output.
|
|
153
|
+
`test/support/api_shapes.rb` describes the fields the gem reads. The unit suite
|
|
154
|
+
checks the fixtures against it and the live suite checks real responses, held
|
|
155
|
+
only in memory. When the API changes, update the shape first, then the fixtures.
|
|
156
|
+
|
|
157
|
+
## Building release packages
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
bundle exec rake build # Portable source gem; external curl-impersonate required
|
|
161
|
+
bundle exec rake package:all # Four platform gems with bundled executables
|
|
63
162
|
```
|
|
64
163
|
|
|
164
|
+
To release, bump the version, merge, then run the **Release Gem** workflow in
|
|
165
|
+
GitHub Actions. It builds the source and four platform gems, publishes them
|
|
166
|
+
through RubyGems trusted publishing (no MFA code needed) and tags the release.
|
|
167
|
+
Versions already on RubyGems are skipped, so a failed run can be rerun.
|
|
168
|
+
|
|
65
169
|
## Contributing
|
|
66
170
|
|
|
67
171
|
1. Fork it
|
data/Rakefile
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
require 'bundler/gem_tasks'
|
|
3
3
|
|
|
4
4
|
require 'rake/testtask'
|
|
5
|
+
require 'cinebase/vista/package_task'
|
|
6
|
+
|
|
7
|
+
Cinebase::Vista::PackageTask.new('picturehouse_uk.gemspec', root: __dir__)
|
|
5
8
|
|
|
6
9
|
Rake::TestTask.new do |t|
|
|
7
10
|
t.libs << 'lib/picturehouse_uk'
|
|
8
11
|
t.test_files = FileList[
|
|
9
12
|
'test/lib/picturehouse_uk/*_test.rb',
|
|
10
13
|
'test/lib/picturehouse_uk/internal/*_test.rb',
|
|
11
|
-
'test/lib/picturehouse_uk/internal/parser/*_test.rb'
|
|
14
|
+
'test/lib/picturehouse_uk/internal/parser/api/*_test.rb'
|
|
12
15
|
]
|
|
13
16
|
t.verbose = true
|
|
14
17
|
end
|
|
@@ -31,16 +34,4 @@ task :console do
|
|
|
31
34
|
IRB.start
|
|
32
35
|
end
|
|
33
36
|
|
|
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
|
-
FixtureCreator.new.cinemas
|
|
41
|
-
%w(duke-of-york-s-picturehouse duke-s-at-komedia phoenix-picturehouse).each do |cinema_id|
|
|
42
|
-
FixtureCreator.new.cinema(cinema_id)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
37
|
task default: :test
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
module PicturehouseUk
|
|
2
2
|
# The object representing a cinema on the Picturehouse UK website
|
|
3
3
|
class Cinema < Cinebase::Cinema
|
|
4
|
-
# address css
|
|
5
|
-
ADDRESS_CSS = '.cinemaAdrass:not(.openingTime)'.freeze
|
|
6
|
-
# cinema link css on the /cinema page
|
|
7
|
-
CINEMA_LINKS_CSS = 'a[href*="/cinema/"]'.freeze
|
|
8
|
-
|
|
9
4
|
# @!attribute [r] id
|
|
10
|
-
# @return [
|
|
11
|
-
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
# @return [String] the zero-padded Vista id of the cinema, e.g. '019'
|
|
6
|
+
|
|
7
|
+
# Vista site ids whose page slug differs from the cinema name
|
|
8
|
+
URL_SLUGS = {
|
|
9
|
+
'013' => 'picturehouse-at-fact',
|
|
10
|
+
'030' => 'epsom-picturehouse'
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
# UK postcode, the last line of an address
|
|
14
|
+
POSTCODE = /\A[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}\z/i.freeze
|
|
15
|
+
|
|
16
|
+
# Constructor
|
|
17
|
+
# @param [Integer, String] id the cinema id of the format 19/'019'
|
|
18
|
+
# @return [PicturehouseUk::Cinema]
|
|
19
|
+
def initialize(id)
|
|
20
|
+
@id = id.to_s.rjust(3, '0')
|
|
21
|
+
end
|
|
16
22
|
|
|
17
23
|
# Return basic cinema information for all cinemas
|
|
18
24
|
# @return [Array<PicturehouseUk::Cinema>]
|
|
@@ -20,12 +26,7 @@ module PicturehouseUk
|
|
|
20
26
|
# PicturehouseUk::Cinema.all
|
|
21
27
|
# #=> [<PicturehouseUk::Cinema>, <PicturehouseUk::Cinema>, ...]
|
|
22
28
|
def self.all
|
|
23
|
-
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# @api private
|
|
27
|
-
def self.cinema_hash
|
|
28
|
-
@cinema_hash ||= ListParser.new(cinema_links).to_hash
|
|
29
|
+
sites_hash.keys.map { |id| new(id) }
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
# @!method address
|
|
@@ -35,178 +36,91 @@ module PicturehouseUk
|
|
|
35
36
|
|
|
36
37
|
# Address of the cinema
|
|
37
38
|
# @return [Hash] contains :street_address, :extended_address,
|
|
38
|
-
# :locality, :postal_code, :
|
|
39
|
+
# :locality, :region, :postal_code, :country_name
|
|
39
40
|
# @example
|
|
40
|
-
# cinema = PicturehouseUk::Cinema.new(
|
|
41
|
+
# cinema = PicturehouseUk::Cinema.new(19)
|
|
41
42
|
# cinema.adr
|
|
42
43
|
# #=> {
|
|
43
44
|
# street_address: '44-47 Gardner Street',
|
|
44
|
-
# extended_address:
|
|
45
|
+
# extended_address: nil,
|
|
45
46
|
# locality: 'Brighton',
|
|
46
|
-
# region:
|
|
47
|
+
# region: nil,
|
|
47
48
|
# postal_code: 'BN1 1UN',
|
|
48
|
-
# country_name:
|
|
49
|
+
# country_name: 'United Kingdom'
|
|
49
50
|
# }
|
|
50
51
|
# @note Uses method naming as at http://microformats.org/wiki/adr
|
|
51
52
|
def adr
|
|
52
|
-
|
|
53
|
+
lines = address_lines
|
|
54
|
+
postal_code = lines.pop if lines.last&.match?(POSTCODE)
|
|
55
|
+
street_address = lines.shift
|
|
56
|
+
locality = lines.pop
|
|
57
|
+
{
|
|
58
|
+
street_address: street_address,
|
|
59
|
+
extended_address: lines.empty? ? nil : lines.join(', '),
|
|
60
|
+
locality: locality,
|
|
61
|
+
region: nil,
|
|
62
|
+
postal_code: postal_code,
|
|
63
|
+
country_name: 'United Kingdom'.freeze
|
|
64
|
+
}
|
|
53
65
|
end
|
|
54
66
|
|
|
55
67
|
# Brand of the cinema
|
|
56
68
|
# @return [String] which will always be 'Picturehouse'
|
|
57
|
-
# @example
|
|
58
|
-
# cinema = PicturehouseUk::Cinema.new('Dukes_At_Komedia')
|
|
59
|
-
# cinema.brand
|
|
60
|
-
# #=> 'Picturehouse'
|
|
61
69
|
def brand
|
|
62
70
|
'Picturehouse'.freeze
|
|
63
71
|
end
|
|
64
72
|
|
|
65
|
-
#
|
|
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'
|
|
80
|
-
|
|
81
|
-
# The name of the cinema (might include brand)
|
|
73
|
+
# The name of the cinema, which often includes the brand
|
|
82
74
|
# @return [String]
|
|
83
75
|
# @example
|
|
84
|
-
# cinema = PicturehouseUk::Cinema.new(
|
|
76
|
+
# cinema = PicturehouseUk::Cinema.new(19)
|
|
85
77
|
# cinema.full_name
|
|
86
|
-
# #=> "Duke's
|
|
78
|
+
# #=> "Duke's at Komedia"
|
|
87
79
|
def full_name
|
|
88
80
|
name
|
|
89
81
|
end
|
|
90
82
|
|
|
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'
|
|
98
|
-
|
|
99
83
|
# The name of the cinema
|
|
100
84
|
# @return [String]
|
|
101
85
|
# @example
|
|
102
|
-
# cinema = PicturehouseUk::Cinema.new(
|
|
86
|
+
# cinema = PicturehouseUk::Cinema.new(19)
|
|
103
87
|
# cinema.name
|
|
104
|
-
# #=> "Duke's
|
|
88
|
+
# #=> "Duke's at Komedia"
|
|
105
89
|
def name
|
|
106
|
-
|
|
90
|
+
site_hash.dig('name', 'text').to_s
|
|
107
91
|
end
|
|
108
92
|
|
|
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
93
|
# The url of the cinema on the Picturehouse website
|
|
142
94
|
# @return [String]
|
|
143
95
|
def url
|
|
144
|
-
"
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
private
|
|
148
|
-
|
|
149
|
-
def self.cinema_links
|
|
150
|
-
cinemas_doc.css(CINEMA_LINKS_CSS)
|
|
151
|
-
end
|
|
152
|
-
private_class_method :cinema_links
|
|
153
|
-
|
|
154
|
-
def self.cinemas_doc
|
|
155
|
-
@cinemas_doc ||=
|
|
156
|
-
Nokogiri::HTML(PicturehouseUk::Internal::Website.new.cinemas)
|
|
157
|
-
end
|
|
158
|
-
private_class_method :cinemas_doc
|
|
159
|
-
|
|
160
|
-
def address_node
|
|
161
|
-
@address_node ||= info_doc.css(ADDRESS_CSS)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def info_doc
|
|
165
|
-
@info_doc ||=
|
|
166
|
-
Nokogiri::HTML(PicturehouseUk::Internal::Website.new.information(id))
|
|
96
|
+
"https://www.picturehouses.com/cinema/#{url_slug}"
|
|
167
97
|
end
|
|
168
98
|
|
|
169
99
|
# @api private
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
@nodes = nodes
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def to_hash
|
|
177
|
-
@nodes.each_with_object({}) do |node, result|
|
|
178
|
-
cinema_id = id(node)
|
|
179
|
-
cinema_name = name(node)
|
|
180
|
-
# Skip links that don't have valid cinema data
|
|
181
|
-
next unless cinema_id && cinema_name
|
|
182
|
-
# Skip duplicate entries
|
|
183
|
-
next if result.key?(cinema_id)
|
|
184
|
-
|
|
185
|
-
result[cinema_id] = { name: cinema_name, url: url(node) }
|
|
186
|
-
end
|
|
100
|
+
def self.sites_hash
|
|
101
|
+
@sites_hash ||= PicturehouseUk::Internal::ApiResponse.new.sites.to_h do |site|
|
|
102
|
+
[site.fetch('id'), site]
|
|
187
103
|
end
|
|
104
|
+
end
|
|
105
|
+
private_class_method :sites_hash
|
|
188
106
|
|
|
189
|
-
|
|
107
|
+
private
|
|
190
108
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
109
|
+
def address_lines
|
|
110
|
+
address = site_hash.dig('contactDetails', 'addressV2') || {}
|
|
111
|
+
%w(line1 line2 line3 line4).filter_map do |key|
|
|
112
|
+
text = address.dig(key, 'text').to_s.strip
|
|
113
|
+
text unless text.empty?
|
|
196
114
|
end
|
|
115
|
+
end
|
|
197
116
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return nil unless p_tag
|
|
202
|
-
# Get text and remove the location span
|
|
203
|
-
p_tag.children.find { |child| child.text? }&.text&.strip
|
|
204
|
-
end
|
|
117
|
+
def site_hash
|
|
118
|
+
@site_hash ||= self.class.send(:sites_hash).fetch(id, {})
|
|
119
|
+
end
|
|
205
120
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
href&.start_with?('http') ? href : "https://www.picturehouses.com#{href}"
|
|
121
|
+
def url_slug
|
|
122
|
+
URL_SLUGS.fetch(id) do
|
|
123
|
+
name.downcase.gsub(/[^a-z0-9]+/, '-').gsub(/\A-|-\z/, '')
|
|
210
124
|
end
|
|
211
125
|
end
|
|
212
126
|
end
|