aoc_generator 0.1.2 → 0.1.4
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/CHANGELOG.md +11 -1
- data/Gemfile.lock +1 -1
- data/README.md +3 -4
- data/lib/advent_of_code_generator/cli.rb +14 -3
- data/lib/advent_of_code_generator/generator.rb +22 -16
- data/lib/advent_of_code_generator/html_parser.rb +71 -0
- data/lib/advent_of_code_generator/scraper.rb +17 -9
- data/lib/advent_of_code_generator.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c2e50281fb4c3f191cbe11ca0417cb2ca969178f87a124572a5b34e07e01b17
|
4
|
+
data.tar.gz: b00f142cd08f4ca55e651974c7d923f2c62515a49a4f07c4d3443f5584f24e4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce6d572095bf2acc47c429e5ece1fee0c335ed0403c94c47f33063a996193d0848deacf49a981fa18546b418d05c2425a553317f1677c5fb714d3de9163caf18
|
7
|
+
data.tar.gz: 93ccfd82a61cb9ea1eb8b096cfa1c0a922a002abb923c93f06c25796a5af6d4356be28e06a88d9b62d4e489f04328e15d37569c24a7480cec366c57560bdd0ab
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [0.1.
|
3
|
+
## [0.1.4] - 2024-11-28
|
4
|
+
|
5
|
+
- Add test data to spec file
|
6
|
+
|
7
|
+
## [0.1.3]
|
8
|
+
|
9
|
+
- Allow to read a AOC_SESSION environment variable
|
10
|
+
- Change file names
|
11
|
+
- Convert HTML to Markdown
|
12
|
+
|
13
|
+
## [0.1.2]
|
4
14
|
|
5
15
|
- [Fix] Add the correct FileUtils gem
|
6
16
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -16,12 +16,11 @@ A Ruby gem that generates daily puzzle templates for Advent of Code.
|
|
16
16
|
|
17
17
|
```sh
|
18
18
|
-y, [--year=N] # Defaults to the current year.
|
19
|
-
# Default: 2024
|
20
19
|
-d, [--day=N] # Defaults to the current day.
|
21
|
-
# Default: 28
|
22
20
|
-u, [--username=USERNAME] # Files will be generated in a directory with this name. Useful for multi-user repos.
|
23
21
|
# Default: advent_of_code
|
24
22
|
-s, [--session=SESSION] # Your adventofcode.com session key. Necessary for scraping data files and specs for part two.
|
23
|
+
# Default: reads AOC_SESSION environment variable if it exists.
|
25
24
|
```
|
26
25
|
|
27
26
|
## Generated file structure
|
@@ -32,8 +31,8 @@ adventofcode/
|
|
32
31
|
└── year_2024/
|
33
32
|
|
|
34
33
|
└── day_01/
|
35
|
-
├──
|
36
|
-
├──
|
34
|
+
├── day_01.rb
|
35
|
+
├── day_01_spec.rb
|
37
36
|
├── data.txt
|
38
37
|
└── README.md
|
39
38
|
```
|
@@ -32,11 +32,13 @@ module AdventOfCodeGenerator
|
|
32
32
|
method_option :session,
|
33
33
|
required: false,
|
34
34
|
aliases: "-s",
|
35
|
-
desc: "Your adventofcode.com session key.
|
35
|
+
desc: "Your adventofcode.com session key. " \
|
36
|
+
"Necessary for scraping data files and specs for part two. " \
|
37
|
+
"Defaults to a AOC_SESSION environment variable if it exists.",
|
38
|
+
default: ENV.fetch("AOC_SESSION", nil)
|
36
39
|
|
37
40
|
def generate
|
38
|
-
|
39
|
-
generator = AdventOfCodeGenerator::Generator.new(options, scraper)
|
41
|
+
generator = AdventOfCodeGenerator::Generator.new(options, content)
|
40
42
|
|
41
43
|
generator.call
|
42
44
|
end
|
@@ -44,5 +46,14 @@ module AdventOfCodeGenerator
|
|
44
46
|
def self.exit_on_failure?
|
45
47
|
true
|
46
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def content
|
53
|
+
scraped_data = AdventOfCodeGenerator::Scraper.new(options).call
|
54
|
+
parsed_data = AdventOfCodeGenerator::HTMLParser.new(scraped_data[:puzzle_description]).call
|
55
|
+
|
56
|
+
scraped_data.merge(parsed_data)
|
57
|
+
end
|
47
58
|
end
|
48
59
|
end
|
@@ -12,19 +12,19 @@ module AdventOfCodeGenerator
|
|
12
12
|
# └── year_2024/
|
13
13
|
# |
|
14
14
|
# └── day_01/
|
15
|
-
# ├──
|
16
|
-
# ├──
|
15
|
+
# ├── day_01.rb
|
16
|
+
# ├── day_01_spec.rb
|
17
17
|
# ├── data.txt
|
18
18
|
# └── README.md
|
19
19
|
#
|
20
20
|
class Generator
|
21
21
|
FileData = Struct.new(:path, :content)
|
22
22
|
|
23
|
-
def initialize(options,
|
23
|
+
def initialize(options, scraped_data)
|
24
24
|
@year = options[:year].to_s
|
25
25
|
@day = options[:day].to_s.rjust(2, "0") # Ensures day is two digits (e.g., "01" instead of "1")
|
26
26
|
@username = options[:username].gsub(/[_\-\.\s]/, "")
|
27
|
-
@
|
27
|
+
@scraped_data = scraped_data
|
28
28
|
end
|
29
29
|
|
30
30
|
def call
|
@@ -45,20 +45,20 @@ module AdventOfCodeGenerator
|
|
45
45
|
|
46
46
|
def readme
|
47
47
|
path = "#{daily_directory}/README.md"
|
48
|
-
content = @
|
48
|
+
content = @scraped_data[:puzzle_description]
|
49
49
|
|
50
50
|
FileData.new(path, content)
|
51
51
|
end
|
52
52
|
|
53
53
|
def data_file
|
54
54
|
path = "#{daily_directory}/data.txt"
|
55
|
-
content = @
|
55
|
+
content = @scraped_data[:input_data]
|
56
56
|
|
57
57
|
FileData.new(path, content)
|
58
58
|
end
|
59
59
|
|
60
60
|
def main_file
|
61
|
-
path = "#{daily_directory}/
|
61
|
+
path = "#{daily_directory}/day_#{@day}.rb"
|
62
62
|
content = <<~RUBY
|
63
63
|
# frozen_string_literal: true
|
64
64
|
|
@@ -85,23 +85,29 @@ module AdventOfCodeGenerator
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def spec_file
|
88
|
-
path = "#{daily_directory}/
|
88
|
+
path = "#{daily_directory}/day_#{@day}_spec.rb"
|
89
|
+
input, expectations = @scraped_data.values_at(:test_input, :test_expectations)
|
90
|
+
input ||= ["", ""]
|
89
91
|
content = <<~RUBY
|
90
92
|
# frozen_string_literal: true
|
91
93
|
|
92
|
-
require_relative "
|
94
|
+
require_relative "day_#{@day}"
|
93
95
|
|
94
96
|
RSpec.describe #{@username.capitalize}::Year#{@year}::Day#{@day} do
|
95
|
-
subject(:puzzle) { described_class.new(input) }
|
96
|
-
|
97
|
-
let(:input) { File.read("\#{__dir__}/data.txt") }
|
98
|
-
|
99
97
|
it "solves Part One" do
|
100
|
-
|
98
|
+
input = <<~INPUT
|
99
|
+
#{input[0]&.gsub("\n", "\n ")}
|
100
|
+
INPUT
|
101
|
+
|
102
|
+
expect(described_class.part_one(input)).to eq(#{expectations[0]})
|
101
103
|
end
|
102
104
|
|
103
|
-
|
104
|
-
|
105
|
+
it "solves Part Two", skip: "not implemented yet" do
|
106
|
+
input = <<~INPUT
|
107
|
+
#{input[1]&.gsub("\n", "\n ")}
|
108
|
+
INPUT
|
109
|
+
|
110
|
+
expect(described_class.part_two(input)).to eq(#{expectations[1]})
|
105
111
|
end
|
106
112
|
end
|
107
113
|
RUBY
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
|
5
|
+
module AdventOfCodeGenerator
|
6
|
+
# Converts HTML puzzle descriptions from adventofcode.com into markdown format.
|
7
|
+
class HTMLParser
|
8
|
+
def initialize(html_content)
|
9
|
+
@html_content = html_content
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
{
|
14
|
+
puzzle_description: part_descriptions.join("\n"),
|
15
|
+
test_input:,
|
16
|
+
test_expectations:
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def part_descriptions
|
23
|
+
@part_descriptions ||= articles.map do |node|
|
24
|
+
process_article(node).join("\n")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_input
|
29
|
+
part_descriptions.map { |desc| desc.scan(/```sh\n(.*?)\n```/m) }.flatten
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_expectations
|
33
|
+
part_descriptions
|
34
|
+
.flat_map { |desc| desc.scan(/\*\*`(.*?)`\*\*/) }
|
35
|
+
.map do |matches|
|
36
|
+
match = matches.first
|
37
|
+
match&.match?(/\A\d+\z/) ? match.to_i : match
|
38
|
+
end.compact
|
39
|
+
end
|
40
|
+
|
41
|
+
def articles
|
42
|
+
doc = Nokogiri::HTML(@html_content)
|
43
|
+
doc.css("article")
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_article(article)
|
47
|
+
article.children.map { |node| process_node(node) }.compact
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_node(node)
|
51
|
+
case node.name
|
52
|
+
when "h2"
|
53
|
+
"## #{node.text}\n"
|
54
|
+
when "p"
|
55
|
+
"#{process_paragraph(node)}\n"
|
56
|
+
when "pre"
|
57
|
+
"```sh\n#{node.text.strip}\n```\n"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def process_paragraph(para)
|
62
|
+
para.inner_html
|
63
|
+
.gsub(%r{<a[^>]*href="([^"]*)"[^>]*>(.*?)</a>}, '[\2](\1)')
|
64
|
+
.gsub(%r{<code><em>(.*?)</em></code>}, '**`\1`**')
|
65
|
+
.gsub(%r{<em><code>(.*?)</code></em>}, '**`\1`**')
|
66
|
+
.gsub(%r{<em.*?>(.*?)</em>}, '**\1**')
|
67
|
+
.gsub(%r{<code>(.*?)</code>}, '`\1`')
|
68
|
+
.gsub(/<[^>]*>/, "")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "net/http"
|
4
|
+
require_relative "html_parser"
|
4
5
|
|
5
6
|
module AdventOfCodeGenerator
|
6
7
|
# Fetches puzzle descriptions and input data from adventofcode.com.
|
@@ -11,6 +12,21 @@ module AdventOfCodeGenerator
|
|
11
12
|
@session_key = options[:session]
|
12
13
|
end
|
13
14
|
|
15
|
+
def call
|
16
|
+
{
|
17
|
+
puzzle_description:,
|
18
|
+
input_data:
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def headers
|
25
|
+
return {} unless @session_key
|
26
|
+
|
27
|
+
@headers ||= { cookie: "session=#{@session_key}" }
|
28
|
+
end
|
29
|
+
|
14
30
|
def puzzle_description
|
15
31
|
warn "You'll need to provide a session key to scrape Part Two." unless @session_key
|
16
32
|
|
@@ -21,20 +37,12 @@ module AdventOfCodeGenerator
|
|
21
37
|
end
|
22
38
|
|
23
39
|
def input_data
|
24
|
-
return @input_data if @input_data
|
25
40
|
return warn "No session key provided; unable to download data file." unless @session_key
|
26
41
|
|
27
42
|
uri = URI("https://adventofcode.com/#{@year}/day/#{@day}/input")
|
28
43
|
response = Net::HTTP.get_response(uri, headers)
|
29
|
-
@input_data ||= response.body
|
30
|
-
end
|
31
44
|
|
32
|
-
|
33
|
-
|
34
|
-
def headers
|
35
|
-
return {} unless @session_key
|
36
|
-
|
37
|
-
@headers ||= { cookie: "session=#{@session_key}" }
|
45
|
+
response.body
|
38
46
|
end
|
39
47
|
end
|
40
48
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aoc_generator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fileutils
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- lib/advent_of_code_generator.rb
|
75
75
|
- lib/advent_of_code_generator/cli.rb
|
76
76
|
- lib/advent_of_code_generator/generator.rb
|
77
|
+
- lib/advent_of_code_generator/html_parser.rb
|
77
78
|
- lib/advent_of_code_generator/scraper.rb
|
78
79
|
homepage: https://github.com/cheddachedda/advent_of_code_generator
|
79
80
|
licenses: []
|