foobara-sh-cli-connector 0.0.2 → 0.0.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: 4b1018788f796d0bfc6ae3756d69ad14784d53f5e71a8a9858fe7b6911c79b9b
4
- data.tar.gz: a629294b8364e4ba86fa1031f52e11266c835e2cfb371ecc479082d8f832afd9
3
+ metadata.gz: cf42373692dbe6435042ec12f9b7012eafcf597ca432d96e9f17029b38c29926
4
+ data.tar.gz: c31a1cc16a6beea651e161ebfcd2fb096f2ad32ff7608c78826047097e72fafd
5
5
  SHA512:
6
- metadata.gz: 983515e65dcdbdc13c2a63c64fda3524bfc383275b80a7a3950f94acd84cd84bb52a2e2e073b688fc6410f3e6d5f7f11d7fae843b7503c32e7935872c4cd8ca8
7
- data.tar.gz: 186305f0c99bc81f8093ca8cb987defe85afd879a29ec9eb653804e96498301b02a2e59cc388512f742d7363c103bbd958b6dca02d0ba48ac9f78f67fe59b1c0
6
+ metadata.gz: 8ad89d0a1eba1455cf58c82664a6046fc6c9145733f84296a4561c5ce8f9a551cc05fadc22b7033c023290852265e3f1a1d1de5c957786ece24e05ca475c29b8
7
+ data.tar.gz: e7117b39c7502619545eaa824667ad3adef557606abe1da98a68c6307a97bd8aa805f238e1b9b3c32102ad2a7131e69a8f05eb92f8fd0482a2937922f68a46f9
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
+ ## [0.0.4] - 2024-10-07
2
+
3
+ * Introduce --no- options to negate boolean flags
4
+
5
+ ## [0.0.3]
6
+
7
+ * Make use of single-command mode in Help command
8
+
1
9
  ## [0.0.2]
2
10
 
3
- * Add single command mode
11
+ * Add single-command mode
4
12
 
5
13
  ## [0.0.1]
6
14
 
@@ -12,21 +12,34 @@ module Foobara
12
12
  def execute
13
13
  print_usage
14
14
 
15
- if valid_argument?
16
- if argument_is_command?
17
- print_command_description
18
- print_command_input_options
19
- end
15
+ if single_command_mode?
16
+ print_command_description
17
+ print_command_input_options
20
18
  else
21
- print_available_actions
22
- print_available_commands
23
- end
19
+ if valid_argument?
20
+ if argument_is_command?
21
+ print_command_description
22
+ print_command_input_options
23
+ end
24
+ else
25
+ print_available_actions
26
+ print_available_commands
27
+ end
24
28
 
25
- print_global_options
29
+ print_global_options
30
+ end
26
31
 
27
32
  output_string
28
33
  end
29
34
 
35
+ def single_command_mode?
36
+ command_connector.single_command_mode
37
+ end
38
+
39
+ def command_connector
40
+ request.command_connector
41
+ end
42
+
30
43
  def print_usage
31
44
  if argument
32
45
  if valid_argument?
@@ -48,7 +61,13 @@ module Foobara
48
61
  end
49
62
 
50
63
  def print_usage_without_argument
51
- output.puts "Usage: #{program_name} [GLOBAL_OPTIONS] [ACTION] [COMMAND_OR_TYPE] [COMMAND_INPUTS]"
64
+ usage = if single_command_mode?
65
+ "[INPUTS]"
66
+ else
67
+ "[GLOBAL_OPTIONS] [ACTION] [COMMAND_OR_TYPE] [COMMAND_INPUTS]"
68
+ end
69
+
70
+ output.puts "Usage: #{program_name} #{usage}"
52
71
  end
53
72
 
54
73
  def print_usage_for_argument
@@ -78,10 +97,6 @@ module Foobara
78
97
  command_connector.program_name
79
98
  end
80
99
 
81
- def command_connector
82
- request.command_connector
83
- end
84
-
85
100
  def command_registry
86
101
  command_connector.command_registry
87
102
  end
@@ -101,7 +116,11 @@ module Foobara
101
116
  def command_class
102
117
  return @command_class if defined?(@command_class)
103
118
 
104
- @command_class = argument && command_registry.transformed_command_from_name(argument)
119
+ @command_class = if single_command_mode?
120
+ command_registry.all_transformed_command_classes.first
121
+ else
122
+ argument && command_registry.transformed_command_from_name(argument)
123
+ end
105
124
  end
106
125
 
107
126
  def argument_is_action?
@@ -156,7 +175,7 @@ module Foobara
156
175
 
157
176
  def print_command_input_options
158
177
  output.puts
159
- output.puts "Command inputs:"
178
+ output.puts single_command_mode? ? "Inputs:" : "Command inputs:"
160
179
  output.puts
161
180
  output.puts request.inputs_parser_for(command_class).parser.summarize
162
181
  end
@@ -0,0 +1,35 @@
1
+ module Foobara
2
+ module CommandConnectors
3
+ class ShCliConnector < CommandConnector
4
+ class InputsParser
5
+ class Option
6
+ class Attributes < Option
7
+ class << self
8
+ def applicable?(attribute_type)
9
+ attribute_type.extends?(BuiltinTypes[:attributes])
10
+ end
11
+
12
+ def attribute_to_options(attribute_name, attribute_type:, prefix:, is_required:, default:)
13
+ sub_required_attributes = if is_required
14
+ attribute_type.declaration_data[:required] || []
15
+ end || []
16
+
17
+ defaults = attribute_type.declaration_data[:defaults] || {}
18
+
19
+ attribute_type.element_types.map do |sub_attribute_name, sub_attribute_type|
20
+ Option.attribute_to_options(
21
+ sub_attribute_name,
22
+ attribute_type: sub_attribute_type,
23
+ prefix: [*prefix, *attribute_name],
24
+ is_required: is_required && sub_required_attributes.include?(sub_attribute_name),
25
+ default: defaults[sub_attribute_name]
26
+ )
27
+ end.flatten
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,52 @@
1
+ module Foobara
2
+ module CommandConnectors
3
+ class ShCliConnector < CommandConnector
4
+ class InputsParser
5
+ class Option
6
+ class Flag < Option
7
+ class << self
8
+ def applicable?(attribute_type)
9
+ attribute_type.extends?(BuiltinTypes[:boolean])
10
+ end
11
+
12
+ def attribute_to_options(attribute_name, attribute_type:, prefix:, is_required:, default:)
13
+ klasses = []
14
+
15
+ klasses << OnFlag if default != true
16
+ klasses << OffFlag if default != false
17
+
18
+ if klasses.size == 1
19
+ klasses.first.new(
20
+ attribute_name:,
21
+ attribute_type:,
22
+ is_required:,
23
+ default:,
24
+ prefix:
25
+ )
26
+ else
27
+ klasses.map do |klass|
28
+ klass.new(
29
+ attribute_name:,
30
+ attribute_type:,
31
+ is_required:,
32
+ default:,
33
+ prefix:
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def _argument_text(_prefixed_name)
41
+ nil
42
+ end
43
+
44
+ def array?
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,27 @@
1
+ module Foobara
2
+ module CommandConnectors
3
+ class ShCliConnector < CommandConnector
4
+ class InputsParser
5
+ class Option
6
+ class Model < Option
7
+ class << self
8
+ def applicable?(attribute_type)
9
+ attribute_type.extends?(BuiltinTypes[:model]) && !attribute_type.extends?(BuiltinTypes[:entity])
10
+ end
11
+
12
+ def attribute_to_options(attribute_name, attribute_type:, prefix:, is_required:, default:)
13
+ Option.attribute_to_options(
14
+ attribute_name,
15
+ attribute_type: attribute_type.target_class.attributes_type,
16
+ prefix:,
17
+ is_required:,
18
+ default:
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "flag"
2
+
3
+ module Foobara
4
+ module CommandConnectors
5
+ class ShCliConnector < CommandConnector
6
+ class InputsParser
7
+ class Option
8
+ class OffFlag < Flag
9
+ def supports_short_option?
10
+ false
11
+ end
12
+
13
+ def _long_option_name(prefixed_name)
14
+ "no-#{Util.kebab_case(prefixed_name)}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module Foobara
2
+ module CommandConnectors
3
+ class ShCliConnector < CommandConnector
4
+ class InputsParser
5
+ class Option
6
+ class OnFlag < Flag
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -3,6 +3,24 @@ module Foobara
3
3
  class ShCliConnector < CommandConnector
4
4
  class InputsParser
5
5
  class Option
6
+ class << self
7
+ def attribute_to_options(attribute_name, attribute_type:, prefix:, is_required:, default:)
8
+ [Model, Attributes, Flag].each do |klass|
9
+ if klass.applicable?(attribute_type)
10
+ return klass.attribute_to_options(
11
+ attribute_name,
12
+ attribute_type:,
13
+ prefix:,
14
+ is_required:,
15
+ default:
16
+ )
17
+ end
18
+ end
19
+
20
+ new(attribute_name:, attribute_type:, prefix:, is_required:, default:)
21
+ end
22
+ end
23
+
6
24
  attr_accessor :attribute_type, :attribute_name, :is_required, :prefix, :default
7
25
 
8
26
  def initialize(attribute_name:, attribute_type:, prefix:, is_required:, default:)
@@ -44,20 +62,28 @@ module Foobara
44
62
  end
45
63
 
46
64
  def to_args(short_options_used, full_paths)
47
- path = _non_colliding_path(full_paths)
48
- prefixed_name = path.join("_")
49
- long_option_name = Util.kebab_case(prefixed_name)
50
- short_option = attribute_name[0]
51
-
52
- argument_text = Util.constantify(prefixed_name)
53
- argument_text = "[#{argument_text}]" if boolean?
54
-
55
- args = ["--#{long_option_name} #{argument_text}"]
56
-
57
- # TODO: we should prioritize required options to get the short name in case of collision?
58
- unless short_options_used.include?(short_option)
59
- short_options_used << short_option
60
- args << "-#{short_option} #{argument_text}"
65
+ prefixed_name = _prefixed_name(full_paths)
66
+ long_option_name = _long_option_name(prefixed_name)
67
+
68
+ argument_text = _argument_text(prefixed_name)
69
+
70
+ args = if argument_text
71
+ ["--#{long_option_name} #{argument_text}"]
72
+ else
73
+ ["--#{long_option_name}"]
74
+ end
75
+
76
+ if supports_short_option?
77
+ short_option = attribute_name[0]
78
+ # TODO: we should prioritize required options to get the short name in case of collision?
79
+ unless short_options_used.include?(short_option)
80
+ short_options_used << short_option
81
+ args << if argument_text
82
+ "-#{short_option} #{argument_text}"
83
+ else
84
+ "-#{short_option}"
85
+ end
86
+ end
61
87
  end
62
88
 
63
89
  args << description if description
@@ -65,6 +91,22 @@ module Foobara
65
91
  args
66
92
  end
67
93
 
94
+ def supports_short_option?
95
+ true
96
+ end
97
+
98
+ def _prefixed_name(full_paths)
99
+ _non_colliding_path(full_paths).join("_")
100
+ end
101
+
102
+ def _long_option_name(prefixed_name)
103
+ Util.kebab_case(prefixed_name)
104
+ end
105
+
106
+ def _argument_text(prefixed_name)
107
+ Util.constantify(prefixed_name)
108
+ end
109
+
68
110
  def full_path
69
111
  [*prefix, attribute_name]
70
112
  end
@@ -73,14 +115,14 @@ module Foobara
73
115
  is_required
74
116
  end
75
117
 
76
- def boolean?
77
- attribute_type.extends?(BuiltinTypes[:boolean])
78
- end
79
-
80
118
  def array?
81
119
  attribute_type.extends?(BuiltinTypes[:array])
82
120
  end
83
121
 
122
+ def has_default?
123
+ !default.nil?
124
+ end
125
+
84
126
  def description
85
127
  desc = []
86
128
  attributes_description = attribute_type.description
@@ -99,7 +141,7 @@ module Foobara
99
141
  desc << "One of: #{one_of.join(", ")}"
100
142
  end
101
143
 
102
- if default
144
+ if has_default?
103
145
  desc << "Default: #{default.inspect}"
104
146
  end
105
147
 
@@ -28,10 +28,6 @@ module Foobara
28
28
  end
29
29
 
30
30
  parser.on(*args) do |value|
31
- if value.nil? && option.boolean?
32
- value = true
33
- end
34
-
35
31
  h = result_source.result.parsed
36
32
 
37
33
  option.prefix.each do |key|
@@ -70,41 +70,23 @@ module Foobara
70
70
  default: nil,
71
71
  prefix: []
72
72
  )
73
- if attribute_type.extends?(BuiltinTypes[:model]) && !attribute_type.extends?(BuiltinTypes[:entity])
74
- attribute_to_option(
75
- attribute_name,
76
- attribute_type: attribute_type.target_class.attributes_type,
77
- prefix:,
78
- is_required:,
79
- default:
80
- )
81
- elsif attribute_type.extends?(BuiltinTypes[:attributes])
82
- sub_required_attributes = if is_required
83
- attribute_type.declaration_data[:required] || []
84
- end || []
85
-
86
- defaults = attribute_type.declaration_data[:defaults] || {}
87
-
88
- attribute_type.element_types.each_pair do |sub_attribute_name, sub_attribute_type|
89
- attribute_to_option(
90
- sub_attribute_name,
91
- attribute_type: sub_attribute_type,
92
- prefix: [*prefix, *attribute_name],
93
- is_required: is_required && sub_required_attributes.include?(sub_attribute_name),
94
- default: defaults[sub_attribute_name]
95
- )
73
+ options = InputsParser::Option.attribute_to_options(
74
+ attribute_name,
75
+ attribute_type:,
76
+ prefix:,
77
+ is_required:,
78
+ default:
79
+ )
80
+
81
+ if options.is_a?(::Array)
82
+ options.each do |option|
83
+ option_set << option
96
84
  end
97
85
  else
98
- option = InputsParser::Option.new(
99
- attribute_name:,
100
- attribute_type:,
101
- prefix:,
102
- is_required:,
103
- default:
104
- )
105
-
106
- option_set << option
107
-
86
+ # Unreachable but would be reachable if we didn't require inputs to be attributes
87
+ # :nocov:
88
+ option_set << options
89
+ # :nocov:
108
90
  end
109
91
  end
110
92
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara-sh-cli-connector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-12 00:00:00.000000000 Z
11
+ date: 2024-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: foobara
@@ -45,6 +45,11 @@ files:
45
45
  - src/sh_cli_connector/globalish_parser.rb
46
46
  - src/sh_cli_connector/inputs_parser.rb
47
47
  - src/sh_cli_connector/inputs_parser/option.rb
48
+ - src/sh_cli_connector/inputs_parser/option/attributes.rb
49
+ - src/sh_cli_connector/inputs_parser/option/flag.rb
50
+ - src/sh_cli_connector/inputs_parser/option/model.rb
51
+ - src/sh_cli_connector/inputs_parser/option/off_flag.rb
52
+ - src/sh_cli_connector/inputs_parser/option/on_flag.rb
48
53
  - src/sh_cli_connector/inputs_parser/option_set.rb
49
54
  - src/sh_cli_connector/request.rb
50
55
  - src/sh_cli_connector/serializers/cli_errors_serializer.rb
@@ -74,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
79
  - !ruby/object:Gem::Version
75
80
  version: '0'
76
81
  requirements: []
77
- rubygems_version: 3.5.18
82
+ rubygems_version: 3.5.21
78
83
  signing_key:
79
84
  specification_version: 4
80
85
  summary: Command-line connector for Foobara