arxiv-dl 0.1.0
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 +7 -0
- data/CHANGELOG.md +38 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.md +21 -0
- data/README.md +141 -0
- data/exe/arxiv-dl +5 -0
- data/lib/arxiv/downloader/abstract_page.rb +20 -0
- data/lib/arxiv/downloader/archive.rb +68 -0
- data/lib/arxiv/downloader/assets_cache.rb +32 -0
- data/lib/arxiv/downloader/bibtex.rb +55 -0
- data/lib/arxiv/downloader/categories.rb +20 -0
- data/lib/arxiv/downloader/categories.yaml +624 -0
- data/lib/arxiv/downloader/cli.rb +85 -0
- data/lib/arxiv/downloader/client.rb +47 -0
- data/lib/arxiv/downloader/error.rb +5 -0
- data/lib/arxiv/downloader/feed_parser.rb +61 -0
- data/lib/arxiv/downloader/html_archive.rb +69 -0
- data/lib/arxiv/downloader/identifier.rb +135 -0
- data/lib/arxiv/downloader/metadata/bibtex.rb +21 -0
- data/lib/arxiv/downloader/metadata/json.rb +32 -0
- data/lib/arxiv/downloader/metadata/markdown.rb +65 -0
- data/lib/arxiv/downloader/metadata/yaml.rb +31 -0
- data/lib/arxiv/downloader/metadata.rb +19 -0
- data/lib/arxiv/downloader/path.rb +31 -0
- data/lib/arxiv/downloader/pdf.rb +20 -0
- data/lib/arxiv/downloader/slug.rb +44 -0
- data/lib/arxiv/downloader/source_archive.rb +43 -0
- data/lib/arxiv/downloader/version.rb +5 -0
- data/lib/arxiv/downloader.rb +26 -0
- data/sig/arxiv/downloader.rbs +5 -0
- metadata +146 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5b80906292c193199cba698da4d7e85ff927e41af088c2452b1b3c8ee3234997
|
|
4
|
+
data.tar.gz: bd732114a37495687cd23d62bbdc67cf645db682c8b3302cc1eb283c64ebd356
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 76153d238bfc2dddf1d9343e130e10beac24f508193c7429c8bbbe42a603a4451996bfb88dc1b592b2d7e34e31055e313d69c2f09235f28c714f03fa25288189
|
|
7
|
+
data.tar.gz: 7c508e83063cbf8d6832d34b9d55a89c7fa8ba55c60f97cbfb1aaab08a5c0d4489a2af359bf55d0a483416ec390ed8402ee7f42114cf15365c9ed647bb53028f
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## [0.1.0]
|
|
2
|
+
|
|
3
|
+
First release. Per-paper offline archive of arxiv.org papers, with PDF, abstract HTML, full HTML version (with relative-path images and a deduplicated cross-paper shared assets cache), TeX source, and four metadata sidecar files.
|
|
4
|
+
|
|
5
|
+
### CLI
|
|
6
|
+
|
|
7
|
+
- `arxiv-dl <ARXIV_ID_OR_URL>...` accepts bare IDs, prefixed IDs, legacy IDs (`cs/0002001`), versioned IDs (`2508.16190v1`), and `/abs`, `/pdf`, `/html` URLs.
|
|
8
|
+
- Flags: `-p/--path PATH`, `--rate-limit SECONDS`, `-v/--verbose`, `-q/--quiet`, `--version`, `-h/--help`. `-v` and `-q` are mutually exclusive.
|
|
9
|
+
- ENV fallbacks: `ARXIV_DOWNLOAD_PATH`, `ARXIV_RATE_LIMIT`. Precedence: CLI flag > ENV > default.
|
|
10
|
+
- Default download path: `$HOME/Downloads/ArXiv_Papers`. Default rate limit: 3 seconds (arxiv etiquette).
|
|
11
|
+
- Verbose mode prints `==> Downloading <id>` step lines and `==> GET <url> (<bytes> bytes)` per HTTP request.
|
|
12
|
+
|
|
13
|
+
### Per-paper output layout
|
|
14
|
+
|
|
15
|
+
```txt
|
|
16
|
+
$ARXIV_DOWNLOAD_PATH/
|
|
17
|
+
_shared/<host>/<path> # deduplicated cross-paper static assets (CSS, JS, fonts)
|
|
18
|
+
YYYY/MM/DD/<primary_category>/<arxiv-id>-<slug>/
|
|
19
|
+
<arxiv-id>.pdf
|
|
20
|
+
<arxiv-id>-abstract.html
|
|
21
|
+
metadata.{md,yaml,json,bib}
|
|
22
|
+
html/<arxiv-id>.html + x1.png, x2.png, ...
|
|
23
|
+
src/*.tex, *.bbl, ...
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Library API
|
|
27
|
+
|
|
28
|
+
- `Arxiv::Downloader::Identifier.new(input).id` / `.version` parses every supported input form.
|
|
29
|
+
- `Arxiv::Downloader::Client.new(rate_limit:, log:).get(url)` is the rate-limited HTTP wrapper.
|
|
30
|
+
- `Arxiv::Downloader::FeedParser.new(xml).metadata` parses the arxiv Atom feed into a `Metadata` value object.
|
|
31
|
+
- `Arxiv::Downloader::Bibtex.new(metadata, client:)` produces synthesized or fetched BibTeX.
|
|
32
|
+
- `Arxiv::Downloader::PDF`, `AbstractPage`, `HTMLArchive`, `SourceArchive`, `AssetsCache` download individual artifacts.
|
|
33
|
+
- `Arxiv::Downloader::Metadata::{Markdown,YAML,JSON,Bibtex}.new(metadata).write(to: dir)` produce the four sidecar files.
|
|
34
|
+
- `Arxiv::Downloader::Archive.new(identifier, root:, client:).run` orchestrates the full per-paper pipeline and returns the paper directory path.
|
|
35
|
+
|
|
36
|
+
### Dependencies
|
|
37
|
+
|
|
38
|
+
Runtime: `feedjira`, `http`, `nokogiri`, `ostruct`, `stringex`. Stdlib only for the rest (`optparse`, `fileutils`, `pathname`, `yaml`, `json`, `rubygems/package`, `zlib`).
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Code of Conduct
|
|
2
|
+
|
|
3
|
+
"arxiv-dl" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
|
|
4
|
+
|
|
5
|
+
- Participants will be tolerant of opposing views.
|
|
6
|
+
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
|
7
|
+
- When interpreting the words and actions of others, participants should always assume good intentions.
|
|
8
|
+
- Behaviour which can be reasonably considered harassment will not be tolerated.
|
|
9
|
+
|
|
10
|
+
If you have any concerns about behaviour within this project, please contact us at ["veganstraightedge@gmail.com"](mailto:"veganstraightedge@gmail.com").
|
data/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shane Becker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# arxiv-dl
|
|
2
|
+
|
|
3
|
+
Ruby gem to download papers and metadata from [arxiv.org](https://arxiv.org) for offline archives.
|
|
4
|
+
|
|
5
|
+
For each paper it produces a self-contained per-paper folder with:
|
|
6
|
+
|
|
7
|
+
- The PDF
|
|
8
|
+
- The single-page abstract HTML
|
|
9
|
+
- The full HTML version (with images and a deduplicated, cross-paper shared assets cache)
|
|
10
|
+
- The TeX/LaTeX source tarball, extracted
|
|
11
|
+
- Four sidecar metadata files: `metadata.md`, `metadata.yaml`, `metadata.json`, `metadata.bib`
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
gem install arxiv-dl
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or in a `Gemfile`:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'arxiv-dl'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## CLI usage
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
arxiv-dl <ARXIV_ID_OR_URL> [<ARXIV_ID_OR_URL>...]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Accepted input forms:
|
|
32
|
+
|
|
33
|
+
| Form | Example |
|
|
34
|
+
| ------------ | -------------------------------------- |
|
|
35
|
+
| Bare ID | `2508.16190` |
|
|
36
|
+
| Prefixed ID | `arXiv:2508.16190` |
|
|
37
|
+
| Legacy ID | `cs/0002001`, `alg-geom/9708001` |
|
|
38
|
+
| Versioned ID | `2508.16190v1` |
|
|
39
|
+
| Abstract URL | `https://arxiv.org/abs/2508.16190` |
|
|
40
|
+
| PDF URL | `https://arxiv.org/pdf/2508.16190.pdf` |
|
|
41
|
+
| HTML URL | `https://arxiv.org/html/2508.16190` |
|
|
42
|
+
|
|
43
|
+
### Flags
|
|
44
|
+
|
|
45
|
+
| Flag | Description |
|
|
46
|
+
| ------------------------ | -------------------------------------------------------- |
|
|
47
|
+
| `-p PATH`, `--path PATH` | Root download directory |
|
|
48
|
+
| `--rate-limit SECONDS` | Seconds between HTTP requests; `0` disables throttling |
|
|
49
|
+
| `-v`, `--verbose` | Print step lines and per-request URL/byte logs to stdout |
|
|
50
|
+
| `-q`, `--quiet` | Print nothing; success/failure conveyed via exit code |
|
|
51
|
+
| `--version` | Print the gem version and exit |
|
|
52
|
+
| `-h`, `--help` | Print help and exit |
|
|
53
|
+
|
|
54
|
+
`-v` and `-q` are mutually exclusive.
|
|
55
|
+
|
|
56
|
+
### Environment variables
|
|
57
|
+
|
|
58
|
+
| Variable | Effect |
|
|
59
|
+
| --------------------- | ------------------------------------------------------------------------------- |
|
|
60
|
+
| `ARXIV_DOWNLOAD_PATH` | Root download directory (default: `$HOME/Downloads/ArXiv_Papers`) |
|
|
61
|
+
| `ARXIV_RATE_LIMIT` | Seconds between HTTP requests (default: `3`, per arxiv etiquette; `0` disables) |
|
|
62
|
+
|
|
63
|
+
Precedence: CLI flag > ENV var > default.
|
|
64
|
+
|
|
65
|
+
### Examples
|
|
66
|
+
|
|
67
|
+
Download a single paper to the default location:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
arxiv-dl 2508.16190
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Download to a custom directory:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
arxiv-dl --path ~/Archives/arxiv 2508.16190
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Download multiple papers, verbose:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
arxiv-dl -v 2508.16190 1207.7214 cs/0002001
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Disable rate limiting (when running against a local mirror, etc):
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
arxiv-dl --rate-limit 0 2508.16190
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Output layout
|
|
92
|
+
|
|
93
|
+
```txt
|
|
94
|
+
$ARXIV_DOWNLOAD_PATH/ # default: $HOME/Downloads/ArXiv_Papers
|
|
95
|
+
_shared/ # cross-paper deduplicated static assets
|
|
96
|
+
arxiv.org/static/...
|
|
97
|
+
cdn.jsdelivr.net/...
|
|
98
|
+
YYYY/MM/DD/<primary_category>/<arxiv-id>-<slug>/
|
|
99
|
+
<arxiv-id>.pdf
|
|
100
|
+
<arxiv-id>-abstract.html
|
|
101
|
+
metadata.md # YAML frontmatter + Markdown body
|
|
102
|
+
metadata.yaml
|
|
103
|
+
metadata.json
|
|
104
|
+
metadata.bib # upstream BibTeX, falls back to synthesized
|
|
105
|
+
html/
|
|
106
|
+
<arxiv-id>.html # path-rewritten to local assets
|
|
107
|
+
x1.png, x2.png, ... # paper-specific images
|
|
108
|
+
src/
|
|
109
|
+
*.tex, *.bbl, ... # extracted from /src/<id> tarball
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`YYYY/MM/DD` is the original submission date. `<primary_category>` is from the paper's metadata (`cs.CL`, `math.NT`, etc). `<slug>` is derived from the paper title (Unicode → ASCII, hyphenated, truncated to 80 chars at a word boundary).
|
|
113
|
+
|
|
114
|
+
For legacy IDs containing `/` (e.g. `cs/0002001`), the slash is replaced with `-` in the directory name (`cs-0002001-...`).
|
|
115
|
+
|
|
116
|
+
## Library usage
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
require 'arxiv/downloader'
|
|
120
|
+
|
|
121
|
+
identifier = Arxiv::Downloader::Identifier.new '2508.16190'
|
|
122
|
+
client = Arxiv::Downloader::Client.new # 3-second rate limit by default
|
|
123
|
+
path = Arxiv::Downloader::Archive.new(identifier, root: '/tmp/papers', client: client).run
|
|
124
|
+
# => "/tmp/papers/2025/08/22/cs.CL/2508.16190-comicscene154-a-scene-dataset-for-comic-analysis"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
script/setup # install dependencies
|
|
131
|
+
script/test # run specs and rubocop
|
|
132
|
+
script/console # interactive prompt
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT — see [LICENSE.md](LICENSE.md).
|
|
138
|
+
|
|
139
|
+
## Code of Conduct
|
|
140
|
+
|
|
141
|
+
See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
data/exe/arxiv-dl
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Arxiv
|
|
2
|
+
module Downloader
|
|
3
|
+
class AbstractPage
|
|
4
|
+
def initialize identifier, client:
|
|
5
|
+
@identifier = identifier
|
|
6
|
+
@client = client
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def download to:
|
|
10
|
+
File.write to, @client.get(url).to_s
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def url
|
|
16
|
+
"https://arxiv.org/abs/#{@identifier.id}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
module Arxiv
|
|
4
|
+
module Downloader
|
|
5
|
+
class Archive
|
|
6
|
+
def initialize identifier, root:, client: Client.new
|
|
7
|
+
@identifier = identifier
|
|
8
|
+
@root = root
|
|
9
|
+
@client = client
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
FileUtils.mkdir_p paper_dir
|
|
14
|
+
|
|
15
|
+
download_pdf
|
|
16
|
+
download_abstract
|
|
17
|
+
download_html_archive
|
|
18
|
+
download_source_archive
|
|
19
|
+
write_sidecars
|
|
20
|
+
|
|
21
|
+
paper_dir
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def metadata
|
|
27
|
+
@metadata ||= FeedParser.new(@client.get(atom_url).to_s).metadata
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def atom_url
|
|
31
|
+
"https://export.arxiv.org/api/query?id_list=#{@identifier.id}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def paper_dir
|
|
35
|
+
@paper_dir ||= File.join @root, Path.new(metadata).to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def download_pdf
|
|
39
|
+
PDF.new(@identifier, client: @client).download to: File.join(paper_dir, "#{@identifier.id}.pdf")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def download_abstract
|
|
43
|
+
AbstractPage.new(@identifier, client: @client)
|
|
44
|
+
.download to: File.join(paper_dir, "#{@identifier.id}-abstract.html")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def download_html_archive
|
|
48
|
+
HTMLArchive.new(@identifier, client: @client, assets_cache: assets_cache)
|
|
49
|
+
.download to: File.join(paper_dir, 'html')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def download_source_archive
|
|
53
|
+
SourceArchive.new(@identifier, client: @client).download to: File.join(paper_dir, 'src')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def assets_cache
|
|
57
|
+
@assets_cache ||= AssetsCache.new root: @root, client: @client
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def write_sidecars
|
|
61
|
+
Metadata::Markdown.new(metadata).write to: paper_dir
|
|
62
|
+
Metadata::YAML.new(metadata).write to: paper_dir
|
|
63
|
+
Metadata::JSON.new(metadata).write to: paper_dir
|
|
64
|
+
Metadata::Bibtex.new(metadata, client: @client).write to: paper_dir
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'uri'
|
|
3
|
+
|
|
4
|
+
module Arxiv
|
|
5
|
+
module Downloader
|
|
6
|
+
class AssetsCache
|
|
7
|
+
SHARED_DIR = '_shared'.freeze
|
|
8
|
+
|
|
9
|
+
def initialize root:, client:
|
|
10
|
+
@root = root
|
|
11
|
+
@client = client
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def fetch url
|
|
15
|
+
path = local_path_for url
|
|
16
|
+
return path if File.exist? path
|
|
17
|
+
|
|
18
|
+
FileUtils.mkdir_p File.dirname(path)
|
|
19
|
+
File.binwrite path, @client.get(url).to_s
|
|
20
|
+
|
|
21
|
+
path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def local_path_for url
|
|
27
|
+
uri = URI.parse url
|
|
28
|
+
File.join @root, SHARED_DIR, uri.host, uri.path.delete_prefix('/')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Arxiv
|
|
2
|
+
module Downloader
|
|
3
|
+
class Bibtex
|
|
4
|
+
def initialize metadata, client: nil
|
|
5
|
+
@metadata = metadata
|
|
6
|
+
@client = client
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def synthesize
|
|
10
|
+
<<~BIBTEX
|
|
11
|
+
@misc{#{key},
|
|
12
|
+
title={#{@metadata.title}},
|
|
13
|
+
author={#{authors}},
|
|
14
|
+
year={#{@metadata.published.year}},
|
|
15
|
+
eprint={#{@metadata.arxiv_id}},
|
|
16
|
+
archivePrefix={arXiv},
|
|
17
|
+
primaryClass={#{@metadata.primary_category[:id]}},
|
|
18
|
+
url={#{@metadata.arxiv_url}},
|
|
19
|
+
}
|
|
20
|
+
BIBTEX
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fetch
|
|
24
|
+
return nil if @client.nil?
|
|
25
|
+
|
|
26
|
+
response = @client.get url
|
|
27
|
+
return nil unless response.status.success?
|
|
28
|
+
|
|
29
|
+
response.to_s
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
fetch || synthesize
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def key
|
|
37
|
+
last_name = @metadata.authors.first.split.last.downcase.gsub(/[^a-z]/, '')
|
|
38
|
+
year = @metadata.published.year
|
|
39
|
+
title_word = Slug.new(@metadata.title).to_s.split('-').first
|
|
40
|
+
|
|
41
|
+
"#{last_name}#{year}#{title_word}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def url
|
|
47
|
+
"https://arxiv.org/bibtex/#{@metadata.arxiv_id}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def authors
|
|
51
|
+
@metadata.authors.join ' and '
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module Arxiv
|
|
4
|
+
module Downloader
|
|
5
|
+
class Categories
|
|
6
|
+
DATA_PATH = File.expand_path('categories.yaml', __dir__).freeze
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@data = YAML.load_file(DATA_PATH)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def lookup id
|
|
13
|
+
entry = @data[id]
|
|
14
|
+
return nil if entry.nil?
|
|
15
|
+
|
|
16
|
+
{ id: id, name: entry['name'], group: entry['group'], description: entry['description'] }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|