markdown_exec 1.3.0 → 1.3.2

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.
@@ -30,6 +30,7 @@ _mde() {
30
30
  if [[ ${prev} == -* ]] ; then
31
31
  case $prev in
32
32
  <% svhs.each do |svh|
33
+
33
34
  svn = svh[:long_name]
34
35
  if svn && svh[:arg_name]
35
36
  svn = '--' + svh[:long_name]
@@ -43,6 +44,21 @@ _mde() {
43
44
  <%= svn + ') COMPREPLY="' + svh[:compreply] + '"; return 0 ;;' %>
44
45
  <% end
45
46
  end
47
+
48
+ svn = svh[:short_name]
49
+ if svn && svh[:arg_name]
50
+ svn = '-' + svh[:short_name]
51
+ if svh[:compreply] == false
52
+ # nothing
53
+ elsif svh[:compreply].nil? %>
54
+ <%= svn + ') __filedirs_all; return 0 ;;' %>
55
+ <% elsif svh[:compreply].empty?
56
+ # nothing
57
+ else %>
58
+ <%= svn + ') COMPREPLY="' + svh[:compreply] + '"; return 0 ;;' %>
59
+ <% end
60
+ end
61
+
46
62
  end %>
47
63
  esac
48
64
  fi
@@ -65,11 +81,18 @@ _mde() {
65
81
  if [[ -z ${cur} ]] ; then
66
82
  case $prev in
67
83
  <% svhs.each do |svh|
84
+
68
85
  svn = svh[:long_name]
69
86
  if svn && svh[:arg_name]
70
87
  svn = '--' + svh[:long_name] %>
71
88
  <%= svn + ') COMPREPLY=".' + svh[:arg_name] + '."; return 0 ;;' %>
72
89
  <% end
90
+
91
+ svn = svh[:short_name]
92
+ if svn && svh[:arg_name]
93
+ svn = '-' + svh[:short_name] %>
94
+ <%= svn + ') COMPREPLY=".' + svh[:arg_name] + '."; return 0 ;;' %>
95
+ <% end
73
96
  end %>
74
97
  esac
75
98
  fi
data/lib/cli.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # encoding=utf-8
4
+
5
+ # utility functions to provide CLI
6
+ #
7
+ module CLI
8
+ # skip :reek:UtilityFunction
9
+ def value_for_cli(value)
10
+ case value.class.to_s
11
+ when 'String'
12
+ Shellwords.escape value
13
+ when 'FalseClass', 'TrueClass'
14
+ value ? '1' : '0'
15
+ else
16
+ Shellwords.escape value.to_s
17
+ end
18
+ end
19
+ end
data/lib/env.rb CHANGED
@@ -10,17 +10,21 @@ module Env
10
10
  # :reek:NilCheck
11
11
  # :reek:UtilityFunction
12
12
  def env_bool(name, default: false)
13
- return default if name.nil? || (val = ENV[name]).nil?
13
+ return default if name.nil? || (val = ENV.fetch(name, nil)).nil?
14
14
  return false if val.empty? || val == '0'
15
15
 
16
16
  true
17
17
  end
18
18
 
19
+ def env_bool_false(name)
20
+ !(val = (name && ENV.fetch(name, nil))).nil? && !(val.empty? || val == '0')
21
+ end
22
+
19
23
  # skip :reek:DataClump
20
24
  # skip :reek:NilCheck
21
25
  # skip :reek:UtilityFunction
22
26
  def env_int(name, default: 0)
23
- return default if name.nil? || (val = ENV[name]).nil?
27
+ return default if name.nil? || (val = ENV.fetch(name, nil)).nil?
24
28
  return default if val.empty?
25
29
 
26
30
  val.to_i
@@ -30,7 +34,7 @@ module Env
30
34
  # skip :reek:NilCheck
31
35
  # skip :reek:UtilityFunction
32
36
  def env_str(name, default: '')
33
- return default if name.nil? || (val = ENV[name]).nil?
37
+ return default if name.nil? || (val = ENV.fetch(name, nil)).nil?
34
38
 
35
39
  val || default
36
40
  end
data/lib/env_opts.rb ADDED
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ # encoding=utf-8
4
+
5
+ require_relative 'tap'
6
+
7
+ include Tap #; tap_config
8
+
9
+ # define options with initial values
10
+ # option to read value from environmnt variables
11
+ # option to cast input values
12
+ # value priority: default < environment < argument
13
+ #
14
+ class EnvOpts
15
+ attr_reader :opts, :values
16
+
17
+ def initialize(opts_raw = {}, argv = ARGV)
18
+ @opts = {}
19
+ @values = {}
20
+ add_options(opts_raw)
21
+ # parse(argv, &block) if block_given?
22
+ block_given? ? parse(argv, &block) : parse(argv)
23
+
24
+ self # rubocop:disable Lint/Void
25
+ end
26
+
27
+ # add options to menu
28
+ # calculate help text
29
+ #
30
+ def add_options(opts_raw)
31
+ return self if opts_raw.nil?
32
+
33
+ rows = opts_raw.map do |key, opt_raw|
34
+ key2 = key_name_to_option_name(key)
35
+
36
+ # set_per_options(key2, opt_raw)
37
+ @opts[key2] = (opt_raw ||= {})
38
+ set_key_value_as_cast key2, EnvOpts.optdefault(opt_raw)
39
+
40
+ set_key_value_per_environment_as_cast(key2, opt_raw)
41
+
42
+ [
43
+ [20, '-', "--#{key}"],
44
+ [16, '-', @opts[key2][:env].present? ? option_name_to_environment_name(key2, @opts[key2]) : ''],
45
+ # [24, '-', get_environment_value_from_option(key2, @opts[key2])],
46
+ [24, '-', @opts[key2][:default]],
47
+ [6, '-', if (fixed = opt_raw.fetch(:fixed, nil)).nil?
48
+ ":#{option_cast(@opts[key2])}"
49
+ else
50
+ fixed.to_s
51
+ end]
52
+ ]
53
+ end.tap_yaml 'rows'
54
+
55
+ max_widths = rows.reduce([0, 0, 0, 0]) do |memo, (c0, c1, c2, c3)|
56
+ [
57
+ [memo[0], c0[2].to_s.length].max,
58
+ [memo[1], c1[2].to_s.length].max,
59
+ [memo[2], c2[2].to_s.length].max,
60
+ [memo[3], c3[2].to_s.length].max
61
+ ]
62
+ end.tap_inspect 'max_widths'
63
+
64
+ @values['help'] = rows.map do |(c0, c1, c2, c3)|
65
+ [format("%#{c0[1]}#{max_widths[0]}s", c0[2]),
66
+ format("%#{c1[1]}#{max_widths[1]}s", c1[2]),
67
+ format("%#{c2[1]}#{max_widths[2]}s", c2[2]),
68
+ format("%#{c3[1]}#{max_widths[3]}s", c3[2])]
69
+ end.map do |row|
70
+ row.join(' ')
71
+ end.join("\n")
72
+ @opts.tap_inspect '@opts'
73
+ @values.tap_inspect '@values'
74
+
75
+ self
76
+ end
77
+
78
+ # accept :d or :default option
79
+ #
80
+ def self.optdefault(opt_raw)
81
+ return opt_raw[:d] unless opt_raw[:d].nil?
82
+
83
+ opt_raw[:default]
84
+ end
85
+
86
+ def output_help
87
+ puts @values['help']
88
+ end
89
+
90
+ # process arguments as mostly pairs of option name and value
91
+ #
92
+ def parse(argv = ARGV)
93
+ return self if argv.nil? || !(argv&.count || 0).positive?
94
+
95
+ args_ind = 0
96
+ while args_ind < argv.count
97
+ args_consumed = 0
98
+ arg = argv.fetch(args_ind, '') #.tap_inspect 'argument', source: 'EnvOpts'
99
+ if arg.start_with? '--'
100
+ opt_name = arg[2..-1] #.tap_inspect 'opt_name', source: 'EnvOpts'
101
+ args_consumed = consume_arguments(opt_name, argv.fetch(args_ind + 1, nil))
102
+ end
103
+
104
+ if args_consumed.zero?
105
+ if arg == '--help'
106
+ output_help
107
+ exit
108
+ elsif block_given?
109
+ yield 'NAO', [arg]
110
+ args_consumed = 1
111
+ else
112
+ warn "Invalid argument: #{arg.inspect} in #{argv.inspect}"
113
+ exit 1
114
+ end
115
+ end
116
+
117
+ args_ind += args_consumed
118
+ end
119
+ @opts.tap_inspect '@opts'
120
+ @values.tap_inspect '@values'
121
+
122
+ self
123
+ end
124
+
125
+ # set option current values per environment values
126
+ #
127
+ def set_keys_value_per_environment_as_cast(opts_raw)
128
+ return self if opts_raw.nil?
129
+
130
+ opts_raw.each do |key, opt_raw|
131
+ set_key_value_per_environment_as_cast(key_name_to_option_name(key), opt_raw)
132
+ end
133
+ @opts.tap_inspect '@opts'
134
+
135
+ self
136
+ end
137
+
138
+ private
139
+
140
+ # convert key name or symbol to an option name
141
+ #
142
+ def key_name_to_option_name(key)
143
+ (key.is_a?(Symbol) ? symbol_name_to_option_name(key) : key) #.tap_inspect
144
+ end
145
+
146
+ # get cast of environment variable
147
+ #
148
+ def option_cast(opt_raw)
149
+ (opt_raw[:cast].present? ? opt_raw[:cast].to_s : 'to_s').tap_inspect
150
+ end
151
+
152
+ # update value for named option
153
+ # return number of arguments used
154
+ #
155
+ def consume_arguments(opt_name, value)
156
+ return 0 if (opt_raw = @opts.fetch(opt_name, nil)).nil?
157
+
158
+ return 0 unless opt_raw.fetch(:option, true)
159
+
160
+ if !(fixed = opt_raw.fetch(:fixed, nil)).nil?
161
+ set_key_value_as_cast(opt_name, fixed)
162
+ 1
163
+ elsif value.nil?
164
+ 0
165
+ else
166
+ set_key_value_as_cast(opt_name, value)
167
+ 2
168
+ end
169
+ end
170
+
171
+ # option names use hyphens
172
+ #
173
+ def method_name_to_option_name(name)
174
+ name.to_s.gsub('_', '-') #.tap_inspect
175
+ end
176
+
177
+ # read and write options using the option name as a method
178
+ #
179
+ def method_missing(method_name, *args)
180
+ method_name.tap_inspect 'method_name'
181
+ if method_name.to_s.end_with?('=')
182
+ value = args.first
183
+ name = method_name_to_option_name(method_name.to_s[0..-2])
184
+ set_key_value_as_cast(name, value)
185
+ else
186
+ @values[method_name_to_option_name(method_name)]
187
+ end.tap_inspect "ref #{method_name}", source: 'EnvOpts'
188
+ end
189
+
190
+ # option name to environment name
191
+ # if true or empty, compute from option name
192
+ #
193
+ def option_name_to_environment_name(opt_name, opt_raw)
194
+ case env_name = opt_raw.fetch(:env, '')
195
+ when true, ''
196
+ "#{@values['env-prefix']}#{opt_name.upcase.gsub('-', '_')}"
197
+ else
198
+ env_name
199
+ end.tap_inspect
200
+ end
201
+
202
+ # get environment value from option
203
+ #
204
+ def get_environment_value_from_option(opt_name, opt_raw)
205
+ ENV.fetch(option_name_to_environment_name(opt_name, opt_raw), nil).tap_inspect
206
+ end
207
+
208
+ # option names are available as methods
209
+ #
210
+ def respond_to_missing?(method_name, include_private = false)
211
+ (@opts.keys.include?(method_name_to_option_name(method_name)) || super)
212
+ end
213
+
214
+ def set_key_value_as_cast(key, value)
215
+ [key, value].tap_inspect 'key, value'
216
+ opt = @opts[key]
217
+ set_key_value_raw(key, (opt[:cast] ? value.send(opt[:cast]) : value))
218
+ end
219
+
220
+ # set key value_per environment as cast
221
+ #
222
+ def set_key_value_per_environment_as_cast(key, opt_raw)
223
+ key.tap_inspect 'key'
224
+ opt_raw.tap_inspect 'opt_raw'
225
+ return if opt_raw[:env].nil?
226
+
227
+ value = get_environment_value_from_option(key, opt_raw).tap_inspect 'value'
228
+ set_key_value_as_cast(key, opt_raw[:cast] ? value.send(opt_raw[:cast]) : value) unless value.nil?
229
+ end
230
+
231
+ # set key value (raw)
232
+ #
233
+ def set_key_value_raw(key, value)
234
+ [key, value].tap_inspect 'key, value'
235
+ @values[key] = value
236
+ end
237
+
238
+ # symbol name to option name
239
+ # option names use hyphens
240
+ #
241
+ def symbol_name_to_option_name(name)
242
+ name.to_s.gsub('_', '-') #.tap_inspect
243
+ end
244
+ end
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ # encoding=utf-8
4
+
5
+ require 'optparse'
6
+ require 'yaml'
7
+
8
+ require_relative 'object_present'
9
+
10
+ # add Hash.sym_keys
11
+ #
12
+ class Hash
13
+ unless defined?(sym_keys)
14
+ def sym_keys
15
+ transform_keys(&:to_sym)
16
+ # map do |key, value|
17
+ # [key.to_sym, value]
18
+ # end.to_h
19
+ end
20
+ end
21
+ end
22
+
23
+ # parse application configuration from command-line options and environment variables
24
+ #
25
+ class EnvironmentOptParse
26
+ attr_reader :options, :remainder
27
+
28
+ # utility functions to create menu
29
+ #
30
+ module Menu
31
+ def menu_all(menu_data, lambdas, config)
32
+ config.tap_yaml 'config'
33
+ input_option_values, remainder, = menu_parse(add_proc(menu_data, lambdas))
34
+ # options = menu_default_option_values(menu_data).merge input_option_values
35
+ # options = (menu_default_option_values(menu_data)).merge(input_option_values).tap_yaml 'options1'
36
+ options = menu_default_option_values(menu_data).merge(config).merge(input_option_values) #.tap_yaml 'options2'
37
+ # options = menu_data.map do |menu_item|
38
+ # menu_item.tap_inspect 'menu_item'
39
+ # mion = menu_item[:opt_name]&.to_sym.tap_inspect 'mion'
40
+ # omion = config[mion].tap_inspect 'omion'
41
+ # unless omion.nil?
42
+ # menu_item[:default] = omion
43
+ # end
44
+ # menu_item
45
+ # end,
46
+
47
+ [options, remainder]
48
+ end
49
+
50
+ def add_proc(menu_data, lambdas)
51
+ menu_data.each do |menu_item|
52
+ menu_item.tap_yaml 'menu_item'
53
+ procname = menu_item[:procname]
54
+ next if procname.nil?
55
+
56
+ menu_item[:proccode] = lambdas.fetch(procname.to_sym, menu_item[:procname])
57
+ end.tap_yaml
58
+ end
59
+
60
+ def menu_default_option_values(menu_data)
61
+ menu_data.map do |item|
62
+ item_default = item[:default]
63
+ next if item_default.nil?
64
+ next unless item[:opt_name].present?
65
+
66
+ [item[:opt_name].to_sym, item_default]
67
+ end.compact.to_h
68
+ end
69
+
70
+ def menu_help(menu_data)
71
+ options = {}
72
+ option_parser = OptionParser.new do |opts|
73
+ opts.banner = [
74
+ "#{APP_NAME} - #{APP_DESC} (#{VERSION})",
75
+ "Usage: #{File.basename($PROGRAM_NAME)} [options]"
76
+ ].join("\n")
77
+
78
+ menu_data.each do |item|
79
+ menu_option_append opts, options, item
80
+ end
81
+ end
82
+
83
+ option_parser.help
84
+ end
85
+
86
+ def menu_option_append(opts, options, item)
87
+ return unless item[:long_name].present? || item[:short_name].present?
88
+
89
+ mmoo = [
90
+ # long name
91
+ if item[:long_name].present?
92
+ "--#{item[:long_name]}#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}"
93
+ end,
94
+
95
+ # short name
96
+ item[:short_name].present? ? "-#{item[:short_name]}" : nil,
97
+
98
+ # description and default
99
+ [item[:description],
100
+ item[:default].present? ? "[#{value_for_menu item[:default]}]" : nil].compact.join(' '),
101
+
102
+ # apply proccode, if present, to value
103
+ # save value to options hash if option is named
104
+ #
105
+ lambda { |value|
106
+ (item[:proccode] ? item[:proccode].call(value) : value).tap do |converted|
107
+ opt_name = item[:opt_name]
108
+ next if opt_name.nil?
109
+
110
+ options[opt_name.to_sym] = converted if item[:opt_name]
111
+ end
112
+ }
113
+ ].compact
114
+ opts.on(*mmoo)
115
+ end
116
+
117
+ def menu_parse(menu_options)
118
+ options = {}
119
+ option_parser = OptionParser.new do |opts|
120
+ menu_options.each do |item|
121
+ item[:opt_name] = item[:opt_name]&.to_sym
122
+ menu_option_append opts, options, item
123
+ end
124
+ end
125
+ option_parser.load # filename defaults to basename of the program without suffix in a directory ~/.options
126
+ option_parser.environment # env defaults to the basename of the program.
127
+ remainder = option_parser.parse!
128
+
129
+ [options, remainder, option_parser.help]
130
+ end
131
+
132
+ # skip :reek:UtilityFunction
133
+ def value_for_menu(value)
134
+ case value.class.to_s
135
+ when 'String'
136
+ value
137
+ when 'FalseClass', 'TrueClass'
138
+ value ? '1' : '0'
139
+ else
140
+ value.to_s
141
+ end
142
+ end
143
+ end
144
+
145
+ include Menu
146
+
147
+ def initialize(menu: {}, lambdas: nil, options: nil, version: nil)
148
+ @menu = if menu.class.to_s == 'String'
149
+ filetext = File.read(menu).tap_yaml 'filetext'
150
+ fileyaml = YAML.load(filetext)
151
+ fileyaml.map(&:sym_keys)
152
+ else
153
+ menu
154
+ end.tap_yaml '@menu'
155
+ @lambdas = lambdas
156
+ @version = version || '0.1'
157
+ # @options = {}
158
+ @options = if options.class.to_s == 'String'
159
+ YAML.safe_load(File.read(options)).sym_keys.tap_yaml '@options'
160
+ else
161
+ {}
162
+ end #.tap_yaml '@options'
163
+
164
+ parse!
165
+ end
166
+
167
+ def parse!
168
+ @options, @remainder = menu_all(
169
+ @menu,
170
+ # @menu.map do |menu_item|
171
+ # menu_item.tap_inspect 'menu_item'
172
+ # mion = menu_item[:opt_name]&.to_sym.tap_inspect 'mion'
173
+ # omion = @options[mion].tap_inspect 'omion'
174
+ # unless omion.nil?
175
+ # @options[menu_item[:default]] = omion
176
+ # end
177
+ # menu_item
178
+ # end,
179
+ {
180
+ debug: ->(value) { tap_config value: value },
181
+
182
+ # stdout_configuration: lambda { |_| self.options.tap_puts 'eop' },
183
+ # stdout_configuration: (lambda { |options|
184
+ # lambda { |v| options.tap_puts 'eop_l' }
185
+ # }).call(@options),
186
+
187
+ stdout_defaults: ->(_) { menu_default_option_values(@menu).to_yaml.tap_puts },
188
+ stdout_help: lambda { |_|
189
+ menu_help(@menu).tap_puts
190
+ exit
191
+ },
192
+ val_as_bool: ->(value) { value.class.to_s == 'String' ? (value.chomp != '0') : value },
193
+ val_as_int: ->(value) { value.to_i },
194
+ val_as_str: ->(value) { value.to_s },
195
+ version: lambda { |_|
196
+ @version.tap_puts
197
+ exit
198
+ }
199
+ }.merge(@lambdas || {}),
200
+ @options
201
+ )
202
+ @options #.tap_yaml '@options'
203
+ end
204
+ end
@@ -7,5 +7,5 @@ module MarkdownExec
7
7
  BIN_NAME = 'mde'
8
8
  GEM_NAME = 'markdown_exec'
9
9
  TAP_DEBUG = 'MDE_DEBUG'
10
- VERSION = '1.3.0'
10
+ VERSION = '1.3.2'
11
11
  end