advent 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44488f23b0a385b7060d3a2d64d6538dc1e738bbd8600bcc405e1735ace44ff2
4
- data.tar.gz: aa26c72769ec2cc0eeb05bf4ed67e3619470e7e1e7e77465c0b19c69b65abe2b
3
+ metadata.gz: 360b1871cc09fd664dd8562fd1a29afd9b2e8bbc7a83d9d711c7933a6c62b5ec
4
+ data.tar.gz: 2f9cde07fa43650354a3f11c7cb869efa7c969d4254083ca807bd7a855fcf9d0
5
5
  SHA512:
6
- metadata.gz: 8be6f552d4fd5930143da51281cf204b7e93f680358578927094f97dde8443d1d5c4307641286edf14fa2454fde7058abd2a2422afe107d4a857b1bdfdd5129b
7
- data.tar.gz: 51575d6fd1d772627c00e199c69f07c4069429787345fa239312effbeac090604c35bf9ab3c1dada8a13de0e75c8a41c83afccfe91723507eb06c36ee6692f8d
6
+ metadata.gz: e27b9323e1ad7a07484fe9302dfd49b430bd4fc5433ef0e412e1af14f734bdd1c0a8c719803e108e4b7b3aec144ec942119bc6a07bc93381f4aae9ef634324b1
7
+ data.tar.gz: b8e99258c57406a1cf945e868a1642d8ed8636c983fd960fd3f889bc6760c170bb346e3cdb0cc82e547f880dbd8c6f71898e941defdfd41b97a73974bc4cdec0
data/README.md CHANGED
@@ -16,6 +16,26 @@ If bundler is not being used to manage dependencies, install the gem by executin
16
16
 
17
17
  ## Usage
18
18
 
19
+ Advent expects you to have a working directory resembling something like:
20
+
21
+ $ tree
22
+ .
23
+ ├── 2015
24
+ └── 2016
25
+
26
+ Some commands can be run from within a directory for a specific year, but it's
27
+ better to run from the parent directory where possible.
28
+
29
+ The typical flow for tackling a daily challenge would be:
30
+
31
+ $ advent generate 2015 1 # generate files to work in
32
+ $ advent download 2015 1 # download the input file
33
+
34
+ $ vim 2015/day1.rb 2015/day1_test.rb # do your work
35
+
36
+ $ ruby 2015/day1_test.rb # run any tests you may have
37
+ $ advent solve 2015/day1.rb # get your answers to submit
38
+
19
39
  A list of commands and help is available using `advent`:
20
40
 
21
41
  $ advent help
@@ -3,21 +3,25 @@
3
3
  class Advent::CLI::Solver
4
4
  PARTS = [1, 2]
5
5
 
6
- def initialize(command, year:, day:)
6
+ if RUBY_VERSION >= "3.1"
7
+ module Solutions
8
+ end
9
+ end
10
+
11
+ def initialize(command, path)
7
12
  @command = command
8
- @year = year
9
- @day = day
13
+ @path = path
10
14
  end
11
15
 
12
16
  def solve
13
- if @command.in_year_directory?
14
- require @command.root_path.join(solution_file_name).to_s
17
+ if RUBY_VERSION >= "3.1"
18
+ load @path, Solutions
19
+ solution = Solutions.const_get(solution_class_name).new
15
20
  else
16
- require @command.root_path.join(@year.to_s, solution_file_name)
21
+ require @path
22
+ solution = Object.const_get(solution_class_name).new
17
23
  end
18
24
 
19
- solution = Object.const_get(solution_class_name).new
20
-
21
25
  PARTS.each do |n|
22
26
  method_name = "part#{n}".to_sym
23
27
 
@@ -33,11 +37,15 @@ class Advent::CLI::Solver
33
37
 
34
38
  private
35
39
 
40
+ def day
41
+ @_day ||= @path.basename.to_s.match(/day([0-9]+)\.rb/)[1]
42
+ end
43
+
36
44
  def solution_file_name
37
- "day#{@day}.rb"
45
+ "day#{day}.rb"
38
46
  end
39
47
 
40
48
  def solution_class_name
41
- "Day#{@day}"
49
+ "Day#{day}"
42
50
  end
43
51
  end
data/lib/advent/cli.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "advent"
4
+ require "date"
4
5
  require "pathname"
5
6
  require "thor"
6
7
 
@@ -9,6 +10,7 @@ module Advent
9
10
  include Thor::Actions
10
11
 
11
12
  class_option :root_path, default: Dir.pwd, hide: true, check_default_type: false
13
+ class_option :http_module, default: Net::HTTP, check_default_type: false
12
14
 
13
15
  def initialize(*args)
14
16
  super
@@ -17,45 +19,119 @@ module Advent
17
19
  source_paths << File.expand_path("templates", __dir__)
18
20
  end
19
21
 
22
+ # @return [Boolean] defines whether an exit status is set if a command fails
20
23
  def self.exit_on_failure?
21
24
  true
22
25
  end
23
26
 
24
27
  no_commands do
28
+ # @return [Boolean] whether the current root_path option is in a
29
+ # directory that looks like a year (eg. 2015)
25
30
  def in_year_directory?
26
31
  dir = root_path.basename.to_s
27
32
  dir =~ /^20[0-9]{2}/
28
33
  end
34
+ end
35
+
36
+ desc "download YEAR DAY", "Download the input for YEAR and DAY"
37
+ def download(year_or_day, day = nil)
38
+ year, day = determine_year_and_day(year_or_day, day)
39
+
40
+ if (error_message = validate(year, day))
41
+ say_error error_message, :red
42
+ return
43
+ end
44
+
45
+ subpath = if in_year_directory?
46
+ ""
47
+ else
48
+ "#{year}/"
49
+ end
50
+
51
+ unless Advent.session.exist?
52
+ session = ask "What is your Advent of Code session cookie value?", echo: false
53
+ Advent.session.value = session
29
54
 
30
- def root_path
31
- @_root_path ||= if options.root_path.is_a?(Pathname)
32
- options.root_path
33
- else
34
- Pathname.new(options.root_path)
35
- end
55
+ say "\n\nThanks. Psst, we're going to save this for next time. It's in .advent_session if you need to update or delete it.\n\n"
56
+ end
57
+
58
+ input = Advent::Input.new(root_path.join(subpath), day: day.to_i)
59
+
60
+ if input.download(Advent.session.value, options.http_module)
61
+ say "Input downloaded to #{input.file_path}.", :green
62
+ say "\nUsing #load_input in your daily solution will load the input file for you."
63
+ else
64
+ say_error "Something went wrong, maybe an old session cookie?", :red
36
65
  end
37
66
  end
38
67
 
39
- desc "generate YEAR DAY or generate DAY in a year directory", "Generate a new solution for YEAR and DAY"
68
+ desc "generate YEAR DAY", "Generate a new solution for YEAR and DAY"
69
+ # Generates a new solution file. If within a year directory, only the day
70
+ # is used, otherwise both the year and day will be required to generate the
71
+ # output.
40
72
  def generate(year_or_day, day = nil)
41
- destination = if in_year_directory?
42
- "day#{year_or_day}.rb"
73
+ year, day = determine_year_and_day(year_or_day, day)
74
+
75
+ if (error_message = validate(year, day))
76
+ say_error error_message, :red
77
+ return
78
+ end
79
+
80
+ subpath = if in_year_directory?
81
+ ""
43
82
  else
44
- "#{year_or_day}/day#{day}.rb"
83
+ "#{year}/"
45
84
  end
46
85
 
47
- template "solution.rb.tt", destination, context: binding
86
+ template "solution.rb.tt", "#{subpath}day#{day}.rb", context: binding
87
+ template "solution_test.rb.tt", "#{subpath}test/day#{day}_test.rb", context: binding
48
88
  end
49
89
 
50
- desc "solve YEAR DAY", "Solve your solution for YEAR and DAY"
51
- def solve(year, day)
90
+ desc "solve FILE", "Solve your solution"
91
+ # Runs a solution file, outputting both :part1 and :part2 method return values.
92
+ def solve(path)
52
93
  require "advent/cli/solver"
53
- Solver.new(self, year: year, day: day).solve
94
+ Solver.new(self, root_path.join(path)).solve
54
95
  end
55
96
 
56
- desc "version", "Prints the current version of Advent"
97
+ desc "version", "Prints the current version of the gem"
98
+ # Prints the current version of the gem
57
99
  def version
58
100
  say Advent::VERSION
59
101
  end
102
+
103
+ private
104
+
105
+ def determine_year_and_day(year_or_day, day)
106
+ if in_year_directory?
107
+ [root_path.basename.to_s, parse_number(year_or_day)]
108
+ else
109
+ [year_or_day, parse_number(day)]
110
+ end
111
+ end
112
+
113
+ def root_path
114
+ @_root_path ||= if options.root_path.is_a?(Pathname)
115
+ options.root_path
116
+ else
117
+ Pathname.new(options.root_path)
118
+ end
119
+ end
120
+
121
+ def parse_number(str)
122
+ if (m = str.match(/[0-9]+/))
123
+ m[0]
124
+ end
125
+ end
126
+
127
+ def validate(year, day)
128
+ if year.to_i < 2014
129
+ "Advent of Code only started in 2014!"
130
+ elsif year.to_i > Date.today.year
131
+ "Future years are not supported."
132
+ elsif !(1..25).cover? day.to_i
133
+ "Day must be between 1 and 25 (inclusive)."
134
+ end
135
+ end
60
136
  end
61
137
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi/cookie"
4
+ require "fileutils"
5
+ require "net/http"
6
+ require "uri"
7
+
8
+ module Advent
9
+ class Input
10
+ def initialize(dir, day:)
11
+ @dir = dir
12
+ @day = day
13
+ end
14
+
15
+ def file_path
16
+ @dir.join(".day#{@day}.input.txt")
17
+ end
18
+
19
+ def exist?
20
+ File.exist? file_path
21
+ end
22
+
23
+ def download(session, http = Net::HTTP)
24
+ session_cookie = CGI::Cookie.new("session", session)
25
+ response = http.get_response(input_url, {"Cookie" => session_cookie.to_s})
26
+
27
+ if success?(response)
28
+ FileUtils.mkdir_p file_path.dirname
29
+ File.write file_path, response.body
30
+ true
31
+ else
32
+ false
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def input_url
39
+ URI("https://adventofcode.com/#{year}/day/#{@day}/input")
40
+ end
41
+
42
+ def year
43
+ @_year ||= @dir.basename
44
+ end
45
+
46
+ def success?(response)
47
+ response.code >= "200" && response.code < "300"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,28 @@
1
+ module Advent
2
+ # Simple class for handling the session cookie file
3
+ class Session
4
+ FILE_NAME = ".advent_session"
5
+
6
+ attr_reader :file_name
7
+
8
+ def initialize(file_name = FILE_NAME)
9
+ @file_name = file_name
10
+ end
11
+
12
+ def clear
13
+ File.delete file_name if exist?
14
+ end
15
+
16
+ def exist?
17
+ File.exist? file_name
18
+ end
19
+
20
+ def value=(val)
21
+ File.write file_name, val
22
+ end
23
+
24
+ def value
25
+ File.read file_name if exist?
26
+ end
27
+ end
28
+ end
@@ -3,19 +3,25 @@
3
3
  require "pathname"
4
4
 
5
5
  module Advent
6
+ # Baes class for an Advent of Code solution attempt. If subclass is in a
7
+ # directory matching a year (eg. 2015) and the filename is for a particular
8
+ # day (eg. day1.rb) then the @year and @day instance variables are
9
+ # automatically inferred.
6
10
  class Solution
7
- attr_reader :year, :day
11
+ attr_reader :year # @return [Numeric] the year this solution is from
12
+ attr_reader :day # @return [Numeric] the day this solution is for
8
13
 
9
14
  def initialize
10
15
  year, day = infer_year_and_day_from_file_system
11
16
 
12
17
  @year = year
13
18
  @day = day
19
+
20
+ @input = Input.new(source_location.dirname, day: day)
14
21
  end
15
22
 
16
23
  def load_input
17
- dir = source_location.dirname
18
- File.read(dir.join(".day#{@day}.input.txt"))
24
+ File.read(@input.file_path)
19
25
  end
20
26
 
21
27
  private
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "advent"
4
+ require "minitest/autorun"
5
+
6
+ require_relative "../day<%= day %>"
7
+
8
+ class Day<%= day %>Test < Advent::TestCase
9
+ def setup
10
+ @solution = Day<%= day %>.new
11
+ end
12
+
13
+ # def test_part1
14
+ # assert_equal 123, @solution.part1
15
+ # end
16
+
17
+ # def test_part2
18
+ # assert_equal 123, @solution.part2
19
+ # end
20
+ end
@@ -0,0 +1,6 @@
1
+ require "minitest"
2
+
3
+ module Advent
4
+ class TestCase < Minitest::Test
5
+ end
6
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Advent
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.4"
5
5
  end
data/lib/advent.rb CHANGED
@@ -1,8 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "advent/input"
4
+ require_relative "advent/session"
3
5
  require_relative "advent/solution"
6
+ require_relative "advent/test_case"
4
7
  require_relative "advent/version"
5
8
 
6
9
  module Advent
7
10
  class Error < StandardError; end
11
+
12
+ class << self
13
+ def session
14
+ @_session = Session.new
15
+ end
16
+ end
8
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: advent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Grieve
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-11-21 00:00:00.000000000 Z
11
+ date: 2022-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -39,8 +39,12 @@ files:
39
39
  - lib/advent.rb
40
40
  - lib/advent/cli.rb
41
41
  - lib/advent/cli/solver.rb
42
+ - lib/advent/input.rb
43
+ - lib/advent/session.rb
42
44
  - lib/advent/solution.rb
43
45
  - lib/advent/templates/solution.rb.tt
46
+ - lib/advent/templates/solution_test.rb.tt
47
+ - lib/advent/test_case.rb
44
48
  - lib/advent/version.rb
45
49
  homepage: https://github.com/dnlgrv/advent-rb
46
50
  licenses: