trees 0.3.4 → 0.4.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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/lib/flags.rb +25 -0
  3. data/lib/line.rb +17 -2
  4. data/lib/trees.rb +34 -22
  5. data/lib/version.rb +1 -1
  6. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b21e86099694d3b6b36152f0bfa445d7baa921c4fc1b085c5d2da22fa1d2131e
4
- data.tar.gz: c1320e651491ffca5053b1b04a7e797b7633c2253baad83c22fd9b798d38399a
3
+ metadata.gz: b8afa25fe3a28ca015098f7a3417d41ce8d4a02e80bda91889b9e656fa0df35a
4
+ data.tar.gz: b52883d15bcd9ebc738c269d4461b035d3bf3d527ec92267b1bbf5eabd0c4fcc
5
5
  SHA512:
6
- metadata.gz: a0d86359f6a0497932b3a628544b744aeecd8ef9b5f376480e056330ea2c8c3694bfbd8e7c8a4e55634e25a3759e4ee2aec7738288fd5d68eb7c21ad7d66cfb5
7
- data.tar.gz: c75011d6796a2c0a8e60061edd75e10b61aaba8649c7755453ae9cba11a2cd2d9267d8e57b494368b32db4d5ac20bba77fcd376098fe9e664e81869d59b26e7a
6
+ metadata.gz: 03dd4e5487d5d6d79ea979a80ac589240021bda0e71baed1a3789482b605bfbe748aef05cd1b168324e07896248bc86e24da0c956ab59edbd521cf18417819b6
7
+ data.tar.gz: e1cca79dfaf2810852075f1ce75f1f2bae7296cf154daaf1d85ecd791cc6dc8ddbc46e97bac122d101a655ffcbb443eb0b83442224cb2f15f26299eb71f0f2fe
data/lib/flags.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trees
4
+ module Flags
5
+ module_function
6
+
7
+ def split(tokens)
8
+ args = tokens.take_while { !flag?(it) }
9
+ flags = tokens.drop(args.length).slice_before { flag?(it) }.map { flag(*it) }
10
+ return if flags.include?(nil)
11
+
12
+ [args, flags.to_h]
13
+ end
14
+
15
+ # A dash alone, like for stdin, is an arg.
16
+ def flag?(token) = token.start_with?('-') && token != '-'
17
+
18
+ def flag(token, *values)
19
+ return if values.size > 1
20
+
21
+ name, given = token.sub(/\A--?/, '').split('=', 2)
22
+ [name.tr('-', '_').to_sym, given || values.first || true]
23
+ end
24
+ end
25
+ end
data/lib/line.rb CHANGED
@@ -2,16 +2,31 @@
2
2
 
3
3
  module Trees
4
4
  class Line
5
- attr_reader :path, :params
5
+ attr_reader :path, :params, :keywords
6
6
  attr_accessor :summary, :example, :execute
7
7
 
8
- def initialize(path:, params: {})
8
+ def initialize(path:, params: [], keywords: [])
9
9
  @path = path
10
10
  @params = params
11
+ @keywords = keywords
11
12
 
12
13
  @summary = nil
13
14
  @example = nil
14
15
  @execute = nil
16
+ @defaults = {}
17
+ end
18
+
19
+ # The keyword arguments' values once the line's defined, which are their defaults, kept so that a flag that isn't
20
+ # given on a run is its default again, rather than its value from the last run.
21
+ def keep_defaults
22
+ @defaults = keywords.to_h { [it, execute.binding.local_variable_get(it)] } if execute
23
+ end
24
+
25
+ # Sets the execute block's params to the values, or else the keyword arguments' defaults, then runs it.
26
+ def run(values)
27
+ values = @defaults.merge(values)
28
+ params.each { |param| execute.binding.local_variable_set(param, values[param]) if values.key?(param) }
29
+ execute.call
15
30
  end
16
31
  end
17
32
  end
data/lib/trees.rb CHANGED
@@ -1,24 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'flags'
3
4
  require_relative 'line'
4
5
  require_relative 'trie'
5
6
 
6
7
  module Trees
7
- attr_reader :lines, :trie
8
-
9
- TOKEN_SEPARATOR = ' '.freeze
8
+ TOKEN_SEPARATOR = ' '
10
9
 
11
10
  def line(path, &block)
12
11
  current_path << path
13
- current_path_joined = current_path.join(TOKEN_SEPARATOR)
14
-
15
- @current_line = Line.new(path: current_path_joined, params: block.parameters.to_h)
16
-
17
- lines[current_path_joined] = @current_line
18
- trie.merge(line: @current_line)
12
+ defined = define_line(block:)
19
13
 
20
- block.call if block_given?
14
+ block.call(**stub_required_keywords(block:)) if block_given?
21
15
 
16
+ defined.keep_defaults
22
17
  current_path.pop
23
18
  end
24
19
 
@@ -37,30 +32,47 @@ module Trees
37
32
  end
38
33
  alias x execute
39
34
 
35
+ # The tokens match a line as they are, or else its args do, and its flags, in any order, are its keyword arguments.
40
36
  def run(tokens)
41
- results = trie.match(tokens:)
42
- execute_block(results:)
37
+ return true if execute_block(results: trie.match(tokens:))
38
+
39
+ args, flags = Flags.split(tokens)
40
+ return false if flags.nil? || flags.empty?
41
+
42
+ execute_block(results: trie.match(tokens: args), flags:)
43
43
  end
44
44
 
45
45
  private
46
46
 
47
- def execute_block(results:)
47
+ # The deepest line that runs, and has a keyword argument for each flag.
48
+ def execute_block(results:, flags: {})
48
49
  while results.count > 0
49
50
  result = results.pop
50
51
 
51
- if result.line.execute
52
- result.line.params.values.each do |param|
53
- result.line.execute.binding.local_variable_set(param, result.params[param])
54
- end
55
-
56
- result.line.execute.call
52
+ next unless result.line.execute
53
+ next unless (flags.keys - result.line.keywords).empty?
57
54
 
58
- return true
59
- end
55
+ result.line.run(result.params.merge(flags))
56
+ return true
60
57
  end
61
58
 
62
59
  false
63
- end
60
+ end
61
+
62
+ def define_line(block:)
63
+ path = current_path.join(TOKEN_SEPARATOR)
64
+ keywords = block.parameters.filter_map { |type, name| name if %i[key keyreq].include?(type) }
65
+ @current_line = Line.new(path:, params: block.parameters.map(&:last), keywords:)
66
+
67
+ lines[path] = @current_line
68
+ trie.merge(line: @current_line)
69
+ @current_line
70
+ end
71
+
72
+ # The "line" block defines the params that the "execute" child block will use later but doesn't itself execute user input.
73
+ def stub_required_keywords(block:)
74
+ block.parameters.filter_map { |type, name| [name, nil] if type == :keyreq }.to_h
75
+ end
64
76
 
65
77
  def current_path
66
78
  @current_path ||= []
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Trees
4
- VERSION = '0.3.4'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trees
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -17,6 +17,7 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - lib/flags.rb
20
21
  - lib/line.rb
21
22
  - lib/trees.rb
22
23
  - lib/trie.rb
@@ -41,7 +42,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
41
42
  - !ruby/object:Gem::Version
42
43
  version: '0'
43
44
  requirements: []
44
- rubygems_version: 3.7.2
45
+ rubygems_version: 4.0.6
45
46
  specification_version: 4
46
47
  summary: A CLI framework where you write the literal terminal commands.
47
48
  test_files: []