advent 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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]