trees 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc7886d327dd2a8f6d2a3833a08ba2b555b198d441c0348e276670514fde2b5f
4
- data.tar.gz: a7a6a657bb62fa25936a50b4c991e260e0d1daf6b421c063b4184a088f89b57a
3
+ metadata.gz: a1dc77a5635f0f3a070cad2a2de939adfd6d9e3550d4b4f3a25c34b567da0dca
4
+ data.tar.gz: 04abe90dbcabe052c78a56581bf906596f8b6eb2c057985df0c4f364c9c5c8a9
5
5
  SHA512:
6
- metadata.gz: ddd1461e661cfc499e54d516f801ececa8fafbfd3613f9bbb119d2ee14b62701d6b3e05784f3539619cbcd75adbb3476209416e2e3fa64b2a09a42342f23e7c4
7
- data.tar.gz: af7bb3374418c5b39151076c07fceb1c38d58bf44deb890bfffb4d88c5a0a2218477e40e830e29418fecb374bc6804c3c52e722ed575260be99e31e2a0928674
6
+ metadata.gz: 4743bc99029f3fc8f7a3c21c58c2757b3916855d1f02a66db14a70fbfa73a44176406e75569bfb22a69001f6f2ba08b39f843051ba2f21f8578ff72837a6eb74
7
+ data.tar.gz: 4dc45169e415cb0e705ae3fecdfd4f83b5652cdda893778ab05402f53339e638bda08f7d8fb86b142a1546a4231629d8446d71483701d739a72a5868f1eb3f34
data/lib/line.rb ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'low_type'
4
+
5
+ module Trees
6
+ class Line
7
+ include LowType
8
+
9
+ attr_reader :path, :params
10
+ attr_accessor :summary, :execute
11
+
12
+ def initialize(path: String, params: {})
13
+ @path = path
14
+ @params = params
15
+
16
+ @summary = nil
17
+ @execute = nil
18
+ end
19
+ end
20
+ end
data/lib/trees.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'line'
4
+ require_relative 'trie'
5
+
6
+ module Trees
7
+ attr_reader :lines, :trie
8
+
9
+ def line(path, &block)
10
+ current_path << path
11
+ @current_line = Line.new(path: current_path.join, params: block.parameters.to_h)
12
+
13
+ lines[current_path.join] = @current_line
14
+ trie.merge(line: @current_line)
15
+
16
+ block.call if block_given?
17
+
18
+ current_path.pop
19
+ end
20
+
21
+ def summary(&block)
22
+ @current_line.summary = block
23
+ end
24
+
25
+ def execute(&block)
26
+ @current_line.execute = block
27
+ end
28
+
29
+ def run(args)
30
+ results = trie.match(path: args.join(' '))
31
+
32
+ execute_block(results:)
33
+ end
34
+
35
+ private
36
+
37
+ def execute_block(results:)
38
+ while results.count > 0
39
+ result = results.pop
40
+
41
+ if result.line.execute
42
+ result.line.params.values.each do |param|
43
+ result.line.execute.binding.local_variable_set(param, result.params[param])
44
+ end
45
+
46
+ result.line.execute.call
47
+
48
+ return true
49
+ end
50
+ end
51
+
52
+ false
53
+ end
54
+
55
+ def current_path
56
+ @current_path ||= []
57
+ end
58
+
59
+ def lines
60
+ @lines ||= {}
61
+ end
62
+
63
+ def trie
64
+ @trie ||= Trie.new
65
+ end
66
+ end
data/lib/trie.rb ADDED
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'line'
4
+ require_relative 'trie_node'
5
+
6
+ module Trees
7
+ class Trie
8
+ include LowType
9
+
10
+ PARAM_DELIMITERS = [' ', ':'].freeze
11
+ ARG_DELIMITERS = [' '].freeze
12
+
13
+ attr_reader :root_node
14
+
15
+ Result = Data.define(:line, :params)
16
+
17
+ def initialize
18
+ @root_node = TrieNode.new
19
+ end
20
+
21
+ def merge(line:, current_node: @root_node)
22
+ @current_index = 0
23
+ path = line.path
24
+
25
+ while @current_index < path.length
26
+ key = path[@current_index]
27
+
28
+ @current_index += 1
29
+
30
+ # Sometimes the key is an entire variable name.
31
+ key = capture_param(path:) if key == ':'
32
+
33
+ current_node = current_node.upsert_child(key:)
34
+ end
35
+
36
+ current_node.line = line
37
+ end
38
+
39
+ def match(path:, current_node: @root_node, current_index: 0, params: {})
40
+ return [] if (key = path[current_index]).nil?
41
+
42
+ results = []
43
+
44
+ # Static path segment.
45
+ if (child_node = current_node.child(key:))
46
+ results << Result.new(line: child_node.line, params:) if child_node.line
47
+ results = [*results, *match(path:, current_node: child_node, current_index: current_index + 1, params:)]
48
+ end
49
+
50
+ # Dynamic path segment.
51
+ current_node.params.each do |param|
52
+ child_node = current_node.child(key: param)
53
+
54
+ arg, next_index = capture_arg(start_index: current_index, path:)
55
+ params[param.delete_prefix(':').to_sym] = arg
56
+
57
+ results << Result.new(line: child_node.line, params:) if child_node.line
58
+ results = [*results, *match(path:, current_node: child_node, current_index: next_index, params:)]
59
+ end
60
+
61
+ results
62
+ end
63
+
64
+ private
65
+
66
+ def capture_param(path:)
67
+ param = [':']
68
+
69
+ path[@current_index...path.length].chars.each do |char|
70
+ break if PARAM_DELIMITERS.include?(char)
71
+
72
+ @current_index += 1
73
+ param << char
74
+ end
75
+
76
+ param.join
77
+ end
78
+
79
+ def capture_arg(start_index:, path:)
80
+ next_index = start_index
81
+ arg = []
82
+
83
+ path[start_index...path.length].chars.each do |char|
84
+ arg << char
85
+ next_index += 1
86
+ break if path[next_index].nil? || ARG_DELIMITERS.include?(path[next_index])
87
+ end
88
+
89
+ [arg.join, next_index]
90
+ end
91
+ end
92
+ end
data/lib/trie_node.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'line'
4
+
5
+ module Trees
6
+ class TrieNode
7
+ include LowType
8
+ using LowType::Syntax
9
+
10
+ # TODO: Represent empty hash/array return value as {}/[] rather than Hash/Array (https://github.com/low-rb/low_type/issues/16)
11
+ type_reader nodes: Hash[String => TrieNode] | Hash
12
+ type_reader params: Array[String] | Array
13
+ type_accessor line: Line | nil
14
+
15
+ def initialize
16
+ @nodes = {}
17
+ @params = []
18
+ @line = nil
19
+ end
20
+
21
+ def child(key: String)
22
+ @nodes[key]
23
+ end
24
+
25
+ def upsert_child(key: String)
26
+ @params << key if key.start_with?(':')
27
+ @nodes[key] || @nodes[key] = TrieNode.new
28
+ end
29
+ end
30
+ end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Trees
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trees
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: low_type
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  description: "A CLI framework where you write the literal terminal commands and \nTrees
13
27
  breaks them apart into a tree of options.\n"
14
28
  email:
@@ -17,7 +31,10 @@ executables: []
17
31
  extensions: []
18
32
  extra_rdoc_files: []
19
33
  files:
20
- - lib/plugs.rb
34
+ - lib/line.rb
35
+ - lib/trees.rb
36
+ - lib/trie.rb
37
+ - lib/trie_node.rb
21
38
  - lib/version.rb
22
39
  homepage: https://github.com/raindeer-rb/trees
23
40
  licenses: []
data/lib/plugs.rb DELETED
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Trees
4
-
5
- end