advent 0.1.3 → 0.1.5

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: d20f95e7e3d0a52a78b93287b93c1864643d94abb70155af037434e254ef18b4
4
- data.tar.gz: 1c4452e0c6b36b2cda5687398c18f819038b06e7cb71056293b8f427246542d9
3
+ metadata.gz: ffa503d42078237632f9e87e404a67ba1e1a09b4a9e4cf2fee9ce9b647e89cb7
4
+ data.tar.gz: d147b5bbc31d6759fdf9441c69b2921d43037cff3258f7ef42d0c01d77ecce0d
5
5
  SHA512:
6
- metadata.gz: 9e39e7bc77607f5cb2d056377b45a763f6564f3205314e9000a128a53faa6cd758e5c427f9e6255d57a495cf971793703ba45a5eb2dfbb85bf6e15d5e21b1143
7
- data.tar.gz: 626cb6bb873ac8c5927a302b155c1f2239207e054b8eb186764a0e91929a8afe009af3cf5fb1abd5dd282de36043b9878d7d0d9266dcb4bb6be29b05fa0d8fee
6
+ metadata.gz: cc70be78e2d488a49fd0bdce22ac3b53016757516fc6aa6fc307e26aacd1899f2026d0b6c7f78d81dc1269f0dc568f571b645deb819dd23f7f25f78b0df7313e
7
+ data.tar.gz: 23900afcbe9288005d5ec925106c9a0ae20e3ffcc99fb3b04f17b4434af2d6b2437bc492287d28fd5d51d3c7560b73ab4eea94261c8521040be4d47b6be4afd7
data/README.md CHANGED
@@ -16,10 +16,67 @@ If bundler is not being used to manage dependencies, install the gem by executin
16
16
 
17
17
  ## Usage
18
18
 
19
+ Initialise a new project somewhere:
20
+
21
+ ```bash
22
+ mkdir advent_of_code && cd advent_of_code
23
+
24
+ # create a blank advent.yml config file
25
+ advent init
26
+ ```
27
+
28
+ Configuration values and format are explained in the [Config](#config) section.
29
+
30
+ Advent expects you to have a working directory resembling something like:
31
+
32
+ $ tree
33
+ .
34
+ ├── 2015
35
+ ├── 2016
36
+ └── advent.yml
37
+
38
+ You can run commands from anywhere under this directory.
39
+
40
+ The typical flow for tackling a daily challenge would be:
41
+
42
+ $ advent generate 2015 1 # generate files to work in
43
+ $ advent download 2015 1 # download the input file
44
+
45
+ $ vim 2015/day1.rb 2015/day1_test.rb # do your work
46
+
47
+ $ ruby 2015/day1_test.rb # run any tests you may have
48
+ $ advent solve 2015/day1.rb # get your answers to submit
49
+
19
50
  A list of commands and help is available using `advent`:
20
51
 
21
52
  $ advent help
22
53
 
54
+ ## Config
55
+
56
+ The config file should be at the root of your working directory called
57
+ `advent.yml`. The default values if you don't provide an override are:
58
+
59
+ ```yaml
60
+ download_when_generating: true
61
+ remember_session: true
62
+ ```
63
+
64
+ ### Config explained
65
+
66
+ <dl>
67
+ <dt>download_when_generating</dt>
68
+ <dd>
69
+ When you run `advent generate` it will automatically download the input file to
70
+ go with it
71
+ </dd>
72
+
73
+ <dt>remember_session</dt>
74
+ <dd>
75
+ Save your session cookie in `.advent_session` when prompted so you don't need to
76
+ find it again
77
+ </dd>
78
+ </dl>
79
+
23
80
  ## Development
24
81
 
25
82
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Advent::CLI::Downloader
4
+ def initialize(command, year, day)
5
+ @command = command
6
+ @year = year
7
+ @day = day
8
+ end
9
+
10
+ def download
11
+ ask_for_session_cookie_if_needed
12
+ input = Advent::Input.new(Advent.root.join(@year), day: @day.to_i)
13
+
14
+ if input.download(Advent.session.value, @command.options.http_module)
15
+ @command.say "Input downloaded to #{input.file_path}.", :green
16
+ @command.say "\nUsing #load_input in your daily solution will load the input file for you."
17
+ else
18
+ @command.say_error "Something went wrong, maybe an old session cookie?", :red
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def ask_for_session_cookie_if_needed
25
+ return if Advent.session.exist?
26
+
27
+ session = @command.ask "What is your Advent of Code session cookie value?", echo: false
28
+ Advent.session.value = session
29
+
30
+ @command.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"
31
+ end
32
+ end
@@ -18,7 +18,7 @@ class Advent::CLI::Solver
18
18
  load @path, Solutions
19
19
  solution = Solutions.const_get(solution_class_name).new
20
20
  else
21
- require @path
21
+ require @path.expand_path
22
22
  solution = Object.const_get(solution_class_name).new
23
23
  end
24
24
 
@@ -37,15 +37,11 @@ class Advent::CLI::Solver
37
37
 
38
38
  private
39
39
 
40
- def day
41
- @_day ||= @path.basename.to_s.match(/day([0-9]+)\.rb/)[1]
42
- end
43
-
44
- def solution_file_name
45
- "day#{day}.rb"
46
- end
47
-
48
40
  def solution_class_name
49
41
  "Day#{day}"
50
42
  end
43
+
44
+ def day
45
+ @_day ||= @path.basename.to_s.match(/day([0-9]+)\.rb/)[1]
46
+ end
51
47
  end
data/lib/advent/cli.rb CHANGED
@@ -9,13 +9,17 @@ module Advent
9
9
  class CLI < Thor
10
10
  include Thor::Actions
11
11
 
12
- class_option :root_path, default: Dir.pwd, hide: true, check_default_type: false
12
+ class_option :http_module, default: Net::HTTP, check_default_type: false
13
13
 
14
14
  def initialize(*args)
15
15
  super
16
16
 
17
- self.destination_root = root_path
18
17
  source_paths << File.expand_path("templates", __dir__)
18
+
19
+ # Don't try to load Advent.root if we're running init
20
+ unless args.last[:current_command]&.name == "init"
21
+ self.destination_root = Advent.root
22
+ end
19
23
  end
20
24
 
21
25
  # @return [Boolean] defines whether an exit status is set if a command fails
@@ -23,20 +27,12 @@ module Advent
23
27
  true
24
28
  end
25
29
 
26
- no_commands do
27
- # @return [Boolean] whether the current root_path option is in a
28
- # directory that looks like a year (eg. 2015)
29
- def in_year_directory?
30
- dir = root_path.basename.to_s
31
- dir =~ /^20[0-9]{2}/
32
- end
30
+ desc "download YEAR DAY", "Download the input for YEAR and DAY"
31
+ def download(year, day)
32
+ require "advent/cli/downloader"
33
33
 
34
- def root_path
35
- @_root_path ||= if options.root_path.is_a?(Pathname)
36
- options.root_path
37
- else
38
- Pathname.new(options.root_path)
39
- end
34
+ Dir.chdir Advent.root do
35
+ Downloader.new(self, year, day).download
40
36
  end
41
37
  end
42
38
 
@@ -44,33 +40,37 @@ module Advent
44
40
  # Generates a new solution file. If within a year directory, only the day
45
41
  # is used, otherwise both the year and day will be required to generate the
46
42
  # output.
47
- def generate(year_or_day, day = nil)
48
- year, day = if in_year_directory?
49
- [root_path.basename.to_s, parse_number(year_or_day)]
50
- else
51
- [year_or_day, parse_number(day)]
52
- end
43
+ def generate(year, day)
44
+ year = parse_number year
45
+ day = parse_number day
53
46
 
54
- if (error_message = validate(year, day))
55
- say_error error_message, :red
47
+ if (message = validate(year, day))
48
+ say_error message, :red
56
49
  return
57
50
  end
58
51
 
59
- subpath = if in_year_directory?
52
+ template "solution.rb.tt", "#{year}/day#{day}.rb", context: binding
53
+ template "solution_test.rb.tt", "#{year}/test/day#{day}_test.rb", context: binding
54
+
55
+ download year, day if Advent.config.download_when_generating
56
+ end
57
+
58
+ desc "init DIR", "Initialise a new advent project in DIR"
59
+ def init(dir = ".")
60
+ create_file Pathname.getwd.join(dir).join(Advent::Configuration::FILE_NAME) do
60
61
  ""
61
- else
62
- "#{year}/"
63
62
  end
64
-
65
- template "solution.rb.tt", "#{subpath}day#{day}.rb", context: binding
66
- template "solution_test.rb.tt", "#{subpath}test/day#{day}_test.rb", context: binding
67
63
  end
68
64
 
69
65
  desc "solve FILE", "Solve your solution"
70
66
  # Runs a solution file, outputting both :part1 and :part2 method return values.
71
67
  def solve(path)
72
68
  require "advent/cli/solver"
73
- Solver.new(self, root_path.join(path)).solve
69
+ file_path = Pathname.getwd.join(path)
70
+
71
+ Dir.chdir Advent.root do
72
+ Solver.new(self, file_path.relative_path_from(Advent.root)).solve
73
+ end
74
74
  end
75
75
 
76
76
  desc "version", "Prints the current version of the gem"
@@ -0,0 +1,30 @@
1
+ require "psych"
2
+
3
+ module Advent
4
+ class Configuration
5
+ DEFAULTS = {
6
+ "download_when_generating" => true,
7
+ "remember_session" => true
8
+ }
9
+ FILE_NAME = "advent.yml"
10
+
11
+ attr_reader :download_when_generating, :remember_session
12
+
13
+ class << self
14
+ def from_file(file = FILE_NAME)
15
+ if RUBY_VERSION >= "3.1"
16
+ new Psych.safe_load_file(file)
17
+ else
18
+ new Psych.safe_load(File.read(file))
19
+ end
20
+ end
21
+ end
22
+
23
+ def initialize(conf)
24
+ config = DEFAULTS.merge(conf || {})
25
+
26
+ @download_when_generating = config.dig("download_when_generating")
27
+ @remember_session = config.dig("remember_session")
28
+ end
29
+ end
30
+ 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,39 @@
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
+ if save_to_disk?
22
+ File.write file_name, val
23
+ else
24
+ @_value = val
25
+ end
26
+ end
27
+
28
+ def value
29
+ return @_value unless save_to_disk?
30
+ return File.read(file_name) if exist?
31
+ end
32
+
33
+ private
34
+
35
+ def save_to_disk?
36
+ Advent.config.remember_session
37
+ end
38
+ end
39
+ end
@@ -16,11 +16,12 @@ module Advent
16
16
 
17
17
  @year = year
18
18
  @day = day
19
+
20
+ @input = Input.new(source_location.dirname, day: day)
19
21
  end
20
22
 
21
23
  def load_input
22
- dir = source_location.dirname
23
- File.read(dir.join(".day#{@day}.input.txt"))
24
+ File.read(@input.file_path)
24
25
  end
25
26
 
26
27
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Advent
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.5"
5
5
  end
data/lib/advent.rb CHANGED
@@ -1,9 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "advent/configuration"
4
+ require_relative "advent/input"
5
+ require_relative "advent/session"
3
6
  require_relative "advent/solution"
4
7
  require_relative "advent/test_case"
5
8
  require_relative "advent/version"
6
9
 
7
10
  module Advent
8
11
  class Error < StandardError; end
12
+
13
+ class << self
14
+ def config
15
+ @_config ||= Configuration.from_file(root.join(Configuration::FILE_NAME))
16
+ end
17
+
18
+ def root
19
+ if (location = find_config_location)
20
+ location
21
+ else
22
+ raise Error, "Cannot find advent.yml config file in current or parent directories."
23
+ end
24
+ end
25
+
26
+ def session
27
+ @_session ||= Session.new
28
+ end
29
+
30
+ private
31
+
32
+ def find_config_location
33
+ Pathname.new(Dir.pwd).ascend do |path|
34
+ return path if File.exist? path.join(Configuration::FILE_NAME)
35
+ return nil if path.to_s == "/"
36
+ end
37
+ end
38
+ end
9
39
  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.3
4
+ version: 0.1.5
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-22 00:00:00.000000000 Z
11
+ date: 2022-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -34,11 +34,14 @@ extra_rdoc_files: []
34
34
  files:
35
35
  - LICENSE.txt
36
36
  - README.md
37
- - Rakefile
38
37
  - exe/advent
39
38
  - lib/advent.rb
40
39
  - lib/advent/cli.rb
40
+ - lib/advent/cli/downloader.rb
41
41
  - lib/advent/cli/solver.rb
42
+ - lib/advent/configuration.rb
43
+ - lib/advent/input.rb
44
+ - lib/advent/session.rb
42
45
  - lib/advent/solution.rb
43
46
  - lib/advent/templates/solution.rb.tt
44
47
  - lib/advent/templates/solution_test.rb.tt
@@ -48,7 +51,13 @@ homepage: https://github.com/dnlgrv/advent-rb
48
51
  licenses:
49
52
  - MIT
50
53
  metadata: {}
51
- post_install_message:
54
+ post_install_message: |2+
55
+
56
+ advent v0.1.5 requires a config file in your working directory.
57
+
58
+ See https://github.com/dnlgrv/advent-rb/blob/main/README.md#usage or if you're
59
+ brave run `advent init` in your current directory.
60
+
52
61
  rdoc_options: []
53
62
  require_paths:
54
63
  - lib
@@ -68,3 +77,4 @@ signing_key:
68
77
  specification_version: 4
69
78
  summary: Have fun with the Advent of Code using Ruby.
70
79
  test_files: []
80
+ ...
data/Rakefile DELETED
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rake/testtask"
5
- require "standard/rake"
6
-
7
- Rake::TestTask.new(:test) do |t|
8
- t.libs << "test"
9
- t.libs << "lib"
10
- t.options = "-p"
11
- t.test_files = FileList["test/**/*_test.rb"]
12
- end
13
-
14
- task default: %i[test]