foobara-sh-cli-connector 0.0.2 → 0.0.4

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: 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