run_kit 0.1.3 → 0.1.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: 1876301102a9221f5207f024bcd108a95d3547cf96d270f475700959d8209cb6
4
- data.tar.gz: b21d96ce661cbe5e45f2de8b6bf9cb1718d2bb7305bce4e15773aa7a9c4882e2
3
+ metadata.gz: 957080dc55365e15529d2077974586f3239ac02d04d8b2ef6f568ca126ed22a5
4
+ data.tar.gz: 92beadf93985edeac07cc957b66fea237a55f29e4c7db97e43eecfd214eda91e
5
5
  SHA512:
6
- metadata.gz: a82974e7beaa687457ff4b8555e8d1b38a284c89d4ef756755bbccc92c16ff857cdfe9d5083620bb53265d6b04de43d7fdca168559c4bef25ef99c052e98fbae
7
- data.tar.gz: 43946b37bb5f6a155b5eb2d27220afa96df7705827db9e0a7ddb4aa5b86f6234629908436cdd288ed20cc8b751c48c413f3441d265cf4c8925de373629f7e99f
6
+ metadata.gz: 5ff6d38e709d6412237e499a8df5f3388d133fa56dcd57c66bc89355da4c419273203ae3264acb54dcf569a232d0d8321753821c1b20e599e9480d497d9d0228
7
+ data.tar.gz: 9999afdfe9bda8cf5903cb695a59b26c387401e5d21732e107977100b0873a35c9c2b6bfdb38fa309fcfa3642922050f5ebd809229d57db22f7d5422b75601a5
data/README.md CHANGED
@@ -6,10 +6,10 @@ RunKit is a small toolkit for cli. It provides option parsing, shell and file he
6
6
 
7
7
  ```ruby
8
8
  # install gem
9
- $ gem install run_key
9
+ $ gem install run_kit
10
10
 
11
11
  # or add to your Gemfile
12
- gem "run_key"
12
+ gem "run_kit"
13
13
  ```
14
14
 
15
15
  ## RunKit::Options
@@ -37,7 +37,28 @@ o.bool "--force", env: true # ENV["FORCE"]
37
37
  o.str "--token", env: "API_TOKEN" # ENV["API_TOKEN"]
38
38
  ```
39
39
 
40
- Configured variables appear in `--help`. Command-line values override ENV, which overrides defaults. Boolean ENV values accept `true/1/yes/on` and `false/0/no/off/empty`, ignoring case.
40
+ Custom validation:
41
+
42
+ ```ruby
43
+ o.validate = lambda do |options|
44
+ raise "--count must be positive" if options.count <= 0
45
+ end
46
+ ```
47
+
48
+ Also supports subcommands, `git`-style:
49
+
50
+ ```ruby
51
+ o.bool "-n", "--dry-run"
52
+ o.cmd "build", "Build the project" do |c|
53
+ c.str "--target <target>", default: "release"
54
+ end
55
+ o.cmd "test", "Run tests" do |c|
56
+ c.bool "--verbose"
57
+ end
58
+
59
+ # myapp build --target debug
60
+ # => #<data command="build", dry_run=false, target="debug", _args=[]>
61
+ ```
41
62
 
42
63
  ## RunKit::Shell
43
64
 
@@ -48,7 +69,7 @@ Configured variables appear in `--help`. Command-line values override ENV, which
48
69
  | `csv_read` / `csv_write` | Read/write CSV (add .gz for gzip) |
49
70
  | `file_read` / `file_write` | Atomic read/write files (add .gz for gzip) |
50
71
  | `json_read` / `json_write` | Atomic read/write json (add .gz for gzip) |
51
- | `jsonl_read / `jsonl_write` | Atomic read/write jsonl (add .gz for gzip) |
72
+ | `jsonl_read` / `jsonl_write` | Atomic read/write jsonl (add .gz for gzip) |
52
73
  | |
53
74
  | `csv_write_stdout` | Write CSV to stdout |
54
75
  | `gunzip` / `gzip` | (De)compress a string |
@@ -66,7 +87,7 @@ Configured variables appear in `--help`. Command-line values override ENV, which
66
87
  | |
67
88
  | `banner` / `warning` / `fatal` | Pretty banner in green, orange or red (fatal exits) |
68
89
  | `program_name` | Return executable name |
69
- | `prompt?` | Ask use for confirmation |
90
+ | `prompt?` | Ask user for confirmation |
70
91
  | `md5` / `sha256` | Hash strings |
71
92
 
72
93
  ### RunKit CoreExt
@@ -111,6 +132,11 @@ Note: There has been some effort to get the Pathname helpers into Ruby itself, w
111
132
 
112
133
  ### Changelog
113
134
 
135
+ #### 0.1.4 (Sep 2026)
136
+
137
+ - add subcommand support (`o.cmd`)
138
+ - add custom validation (`o.validate`)
139
+
114
140
  #### 0.1.3 (Sep 2026)
115
141
 
116
142
  - move PROGBAR constant into RunKit::
data/demo.rb CHANGED
@@ -3,9 +3,21 @@
3
3
  require_relative "lib/run_kit"
4
4
 
5
5
  options = RunKit.parse do |o|
6
- o.int "-n", "--count <n>", "How many times to run", default: 1
7
- o.str "--mode <mode>", "Run quickly, or not", choices: %w[fast slow]
8
- o.positional "<url>", "url to fetch"
6
+ o.version = "1.0"
7
+ o.bool "--dry-run", "Preview without making changes"
8
+ o.desc = "this is made up"
9
+
10
+ o.cmd "fetch", "Fetch a URL" do |c|
11
+ c.int "-n", "--count <n>", "How many times to run", default: 1
12
+ c.str "--mode <mode>", "Run quickly, or not", choices: %w[fast slow]
13
+ c.positional "<url>", "URL to fetch"
14
+ end
15
+
16
+ o.cmd "build", "Build the project" do |c|
17
+ c.naked = false
18
+ c.str "--target <target>", "Build target", choices: %w[debug release], default: "release"
19
+ c.bool "--force", "Force a rebuild", env: "DEMO_FORCE"
20
+ end
9
21
  end
10
22
 
11
23
  p options
@@ -6,15 +6,17 @@
6
6
  module RunKit
7
7
  module Options
8
8
  class Config
9
- attr_accessor :app_name, :banner, :color, :exit, :help, :naked, :version
10
- attr_reader :flags, :help_flag, :lookup, :positionals, :separators, :version_flag
9
+ attr_accessor :banner, :color, :desc, :exit, :help, :naked, :root, :validate, :version
10
+ attr_reader :help_flag, :name, :version_flag
11
11
  alias_method :naked?, :naked
12
12
 
13
- def initialize
14
- @app_name = File.basename($PROGRAM_NAME)
13
+ def initialize(name: nil)
15
14
  @naked = true
16
- @lookup = {}
17
- @flags, @positionals, @separators = [], [], []
15
+ self.name = name || Shell.program_name
16
+ end
17
+
18
+ def name=(name)
19
+ @name = name.to_s
18
20
  end
19
21
 
20
22
  # Add a positional param declared as `<url>`.
@@ -26,6 +28,18 @@ module RunKit
26
28
  end
27
29
  end
28
30
 
31
+ # Add a subcommand with its own nested Config, eg `myapp build`.
32
+ def cmd(name, desc = nil)
33
+ name = name.to_s
34
+ raise ArgumentError, "duplicate command #{name}" if commands.key?(name)
35
+ Config.new(name:).tap do
36
+ _1.desc, _1.root = desc, self
37
+ yield _1 if block_given?
38
+ raise ArgumentError, "nested commands are not supported" if _1.commands.any?
39
+ commands[name] = _1
40
+ end
41
+ end
42
+
29
43
  # Add separator text at the current point in generated help.
30
44
  def sep(text = "")
31
45
  [flags.length, text].tap do
@@ -62,7 +76,9 @@ module RunKit
62
76
  end
63
77
 
64
78
  # long-form aliases
79
+ alias_method :app_name=, :name=
65
80
  alias_method :boolean, :bool
81
+ alias_method :command, :cmd
66
82
  alias_method :integer, :int
67
83
  alias_method :pathname, :path
68
84
  alias_method :positional, :pos
@@ -81,27 +97,38 @@ module RunKit
81
97
  # one-liners
82
98
  def flag(switch) = lookup[switch]
83
99
  def flag?(switch) = lookup.key?(switch)
100
+ def full_name = root ? "#{root.full_name} #{name}" : name
84
101
  def key?(key) = lookup.key?(key)
85
102
  def required = flags.select(&:required?)
86
103
 
104
+ # memoized accessors
105
+ def commands = @commands ||= {}
106
+ def flags = @flags ||= []
107
+ def lookup = @lookup ||= {}
108
+ def positionals = @positionals ||= []
109
+ def separators = @separators ||= []
110
+
87
111
  # Complete one-time setup after the caller has declared overrides.
88
112
  def prepare!
89
113
  return if @prepared
90
114
  @prepared = true
115
+
116
+ # Children inherit shared settings before adding builtins.
117
+ if root
118
+ self.color, self.exit, self.version = root.color, root.exit, root.version
119
+ end
120
+
121
+ # now defaults
91
122
  @exit ||= lambda { |status| Kernel.exit(status) }
92
- @help_flag = add_builtin(["-h", "--help"], "Show this message")
93
- @version_flag = add_builtin(["-v", "--version"], "Show version") if version
123
+ @help_flag = bool("-h", "--help", "Show this message")
124
+ @version_flag = bool("-v", "--version", "Show version") if version
125
+
126
+ # setup subcommands
127
+ commands.each_value(&:prepare!)
94
128
  end
95
129
 
96
130
  protected
97
131
 
98
- # Add help/version flags, but only for switches the user did not override.
99
- def add_builtin(switches, help_text)
100
- unused = switches.select { !flag?(_1) }
101
- return if unused.empty?
102
- add_flag(Flag.new(:bool, unused + [help_text]))
103
- end
104
-
105
132
  def add_flag(flag)
106
133
  # dup check
107
134
  raise ArgumentError, "reserved flag key: _args" if flag.key == :_args
@@ -18,65 +18,88 @@ module RunKit
18
18
  # Render generated help, unless the caller supplied complete help text.
19
19
  def to_s
20
20
  return config.help if config.help
21
+ help = [].tap do
22
+ _1 << desc if config.desc
23
+ _1 << banner
24
+ _1 << commands_text if config.commands.any?
25
+ _1 << flags_text(config, "Options:", builtins: !config.root)
26
+ _1 << flags_text(config.root, "Other options:") if config.root
27
+ end.compact.join("\n\n")
28
+ "#{help}\n"
29
+ end
21
30
 
22
- # usage: xyz (banner)
23
- buf = StringIO.new
24
- buf << banner
25
- buf << "\n"
26
- buf << "\n" if config.separators.any? { _1.first.zero? }
31
+ # Render a heading and aligned flags, preserving explicit separator lines.
32
+ def flags_text(source, title, builtins: true)
33
+ # gather flags
34
+ flags = source.flags
35
+ flags -= [source.help_flag, source.version_flag] unless builtins
36
+ return if flags.empty? && source.separators.empty?
27
37
 
28
- # Render each flag with aligned switch labels and wrapped help text.
29
- label_width = widest_label
30
- config.flags.each.with_index do |flag, idx|
31
- buf << separator_text(idx) # sep
38
+ [].tap do |lines|
39
+ lines << color.blue(title)
40
+ label_width = flags.map { Term.width(flag_label(_1)) }.max
41
+ flags.each.with_index do |flag, idx|
42
+ lines.concat(separator_lines(source, idx))
43
+ buf = StringIO.new
32
44
 
33
- # left
34
- label = flag_label(flag)
35
- buf << " " * INDENT
36
- buf << label
45
+ # left
46
+ label = flag_label(flag)
47
+ buf << " " * INDENT
48
+ buf << label
37
49
 
38
- # right
39
- help = flag.help
40
- if flag.env
41
- env_help = "[env: #{flag.env}]"
42
- help = help ? "#{help} #{env_help}" : env_help
43
- end
44
- if help
45
- buf << " " * (label_width - Term.width(label) + 2)
46
- indent = INDENT + label_width + 2
47
- buf << Term.wrap(help, width - indent).gsub("\n", "\n#{" " * indent}")
50
+ # right
51
+ help = flag.help
52
+ if flag.env
53
+ env_help = "[env: #{flag.env}]"
54
+ help = help ? "#{help} #{env_help}" : env_help
55
+ end
56
+ if help
57
+ buf << " " * (label_width - Term.width(label) + 2)
58
+ indent = INDENT + label_width + 2
59
+ buf << Term.wrap(help, width - indent).gsub("\n", "\n#{" " * indent}")
60
+ end
61
+ lines << buf.string
48
62
  end
49
- buf << "\n"
50
- end
51
- buf << separator_text(config.flags.length)
52
-
53
- buf.string
63
+ lines.concat(separator_lines(source, flags.length))
64
+ end.join("\n")
54
65
  end
55
66
 
56
- # Build the usage line from the configured app name and positionals.
67
+ # Build the usage line from the command's full name and positionals.
57
68
  def banner
58
69
  text = config.banner
59
- text ||= [color.blue("Usage:"), color.green(config.app_name), "[options]"].tap do
70
+ text ||= [color.blue("Usage:"), color.green(config.full_name), "[options]"].tap do
60
71
  _1.push(*config.positionals.map(&:meta))
72
+ _1.push(color.yellow("<command>")) if config.commands.any?
61
73
  end.join(" ")
62
74
  Term.wrap(text, width)
63
75
  end
64
76
 
65
- # Render separator text at its recorded position between flags.
66
- def separator_text(position)
67
- StringIO.new.tap do |buf|
68
- config.separators.each do |(pos, str)|
69
- if pos == position
70
- buf << color.blue(str)
71
- buf << "\n"
72
- end
77
+ # Render the list of subcommands, aligned like the flag list above.
78
+ def commands_text
79
+ [].tap do |lines|
80
+ lines << color.blue("Commands:")
81
+ label_width = config.commands.keys.map { Term.width(_1) }.max
82
+ config.commands.each do |name, child|
83
+ lines << StringIO.new.tap do |buf|
84
+ buf << " " * INDENT << color.green(name)
85
+ if child.desc
86
+ buf << " " * (label_width - Term.width(name) + 2)
87
+ indent = INDENT + label_width + 2
88
+ buf << Term.wrap(child.desc, width - indent).gsub("\n", "\n#{" " * indent}")
89
+ end
90
+ end.string
73
91
  end
74
- end.string
92
+ end.join("\n")
93
+ end
94
+
95
+ # Keep blank and multiline separators exactly as supplied.
96
+ def separator_lines(source, position)
97
+ source.separators.filter_map { |pos, str| color.blue(str) if pos == position }
75
98
  end
76
99
 
77
100
  # one-liners
78
101
  def color = @color ||= Color.new(config.color)
79
- def widest_label = config.flags.map { Term.width(flag_label(_1)) }.max
102
+ def desc = Term.wrap(config.desc, width)
80
103
 
81
104
  protected
82
105
 
@@ -16,67 +16,125 @@
16
16
  # | meta | placeholder shown for a param | <xxx> from `--port <xxx>` |
17
17
  # | positional | configured required param slot | o.positional "<url>" |
18
18
  # | separator | help section heading | "Network:" |
19
+ # | command | subcommand with its own Config | o.cmd "build" { ... } |
19
20
  #
20
21
 
21
22
  module RunKit
22
23
  module Options
23
24
  class Main
24
- attr_reader :config
25
+ # root is the top-level config; ctx is the active parser or validator's config.
26
+ attr_reader :ctx, :root
25
27
 
26
- def initialize = @config = Config.new
27
- def app_name = config.app_name
28
+ def initialize = @root = Config.new
28
29
 
29
30
  # Parse argv and turn internal parser outcomes into CLI behavior.
30
31
  def parse(argv)
31
- config.prepare!
32
+ @ctx = root
33
+ root.prepare!
34
+
35
+ # handle --help and --version
36
+ return exit_fn(0) if early_exit?(argv)
32
37
 
33
38
  begin
34
- options = Parser.new(config).parse(argv)
39
+ options = root.commands.empty? ? parse_with_ctx(root, argv) : subcommand(argv)
35
40
  klass = Data.define(*options.keys)
36
- klass.new(**options)
37
- rescue Error => ex
38
- warn "#{app_name}: #{ex.message}"
39
- warn "#{app_name}: try '#{app_name} --help' for more information"
40
- exit_fn(1, error: ex.message)
41
- rescue HelpRequested, NakedRequested, VersionRequested => ex
42
- early_exit(ex)
43
- exit_fn(0)
41
+ klass.new(**options).tap { validate(_1) }
42
+ rescue Error, NakedRequested => ex
43
+ handle_error(ex)
44
44
  end
45
45
  end
46
46
 
47
47
  protected
48
48
 
49
- def early_exit(ex)
50
- case ex
51
- when HelpRequested
52
- puts Help.new(config)
53
- when NakedRequested
54
- puts "#{app_name}: try '#{app_name} --help' for more information"
55
- when VersionRequested
56
- puts "#{app_name} #{config.version}"
49
+ # Handle --help or --version
50
+ def early_exit?(argv)
51
+ if argv.include?("--help") || argv.include?("-h")
52
+ selected = root.commands[argv.first] || root
53
+ puts Help.new(selected)
54
+ return true
55
+ end
56
+ if root.version && (argv.include?("--version") || argv.include?("-v"))
57
+ puts "#{root.name} #{root.version}"
58
+ return true
59
+ end
60
+ end
61
+
62
+ # Peek at the first bare argument to pick a subcommand, then parse the
63
+ # rest with its own Config and merge the two option hashes together.
64
+ def subcommand(argv)
65
+ # Parse root options before the subcommand.
66
+ root_options = parse_with_ctx(root, argv, passthru: true)
67
+ name, *rest = root_options[:_args]
68
+ raise NakedRequested if !name
69
+
70
+ # Find and parse the child.
71
+ child = root.commands[name]
72
+ raise Error, "unknown command '#{name}'" if !child
73
+ child_options = parse_with_ctx(child, rest)
74
+
75
+ # merge
76
+ root_options.merge(child_options).merge(command: name)
77
+ end
78
+
79
+ # Validate the final options, root first.
80
+ def validate(options)
81
+ [ctx.root, ctx].compact.each do
82
+ validate_with_ctx(_1, options)
83
+ rescue RuntimeError => ex
84
+ raise Error, ex.message
57
85
  end
58
86
  end
59
87
 
88
+ # Render the outcome using the active context.
89
+ def handle_error(ex)
90
+ if ex.is_a?(Error)
91
+ warn "#{ctx.full_name}: #{ex.message}"
92
+ warn "#{ctx.full_name}: try '#{ctx.full_name} --help' for more information"
93
+ return exit_fn(1, error: ex.message)
94
+ end
95
+
96
+ puts Help.new(ctx)
97
+ exit_fn(0)
98
+ end
99
+
60
100
  def exit_fn(status, error: nil)
61
101
  args = [].tap do
62
102
  _1 << status
63
- _1 << error if config.exit.arity == 2
103
+ _1 << error if root.exit.arity == 2
64
104
  end
65
- config.exit.call(*args)
105
+ root.exit.call(*args)
66
106
  nil
67
107
  end
108
+
109
+ #
110
+ # Keep the active config after a failure so the outer rescue can use it.
111
+ # This is error context, not a push/pop command stack.
112
+ #
113
+
114
+ def with_ctx(ctx)
115
+ @ctx = ctx
116
+ yield
117
+ end
118
+
119
+ def parse_with_ctx(ctx, argv, passthru: false)
120
+ with_ctx(ctx) do
121
+ Parser.new(ctx).parse(argv, passthru:)
122
+ end
123
+ end
124
+
125
+ def validate_with_ctx(ctx, options)
126
+ with_ctx(ctx) do
127
+ ctx.validate&.call(options)
128
+ end
129
+ end
68
130
  end
69
131
 
70
132
  class Error < StandardError; end
71
-
72
- # early exits
73
- class HelpRequested < Exception; end # rubocop:disable Lint/InheritException
74
133
  class NakedRequested < Exception; end # rubocop:disable Lint/InheritException
75
- class VersionRequested < Exception; end # rubocop:disable Lint/InheritException
76
134
 
77
135
  # main entry point
78
136
  def self.parse(argv = ARGV)
79
- Main.new.tap { yield _1.config if block_given? }.parse(argv)
137
+ Main.new.tap { yield _1.root if block_given? }.parse(argv)
80
138
  end
81
139
  end
82
140
  end
@@ -12,14 +12,14 @@ module RunKit
12
12
  @config = config
13
13
  end
14
14
 
15
- # Reset transient state, parse argv, and assemble the result.
16
- def parse(argv)
15
+ # Reset transient state, parse argv, and assemble the result. When
16
+ # passthru is set, scanning halts at the first bare arg (for subcommands).
17
+ def parse(argv, passthru: false)
17
18
  # 1. naked?
18
19
  raise NakedRequested if config.naked? && argv.empty?
19
20
 
20
- # 2. Parse argv. We do this first, because other things can raise and
21
- # --help should trump other issues.
22
- argv_options = parse_argv(argv)
21
+ # 2. Parse argv.
22
+ argv_options = parse_argv(argv, passthru:)
23
23
 
24
24
  # 3. defaults => ENV => ARGV
25
25
  options = {}.merge(config.defaults, parse_env, argv_options)
@@ -41,7 +41,7 @@ module RunKit
41
41
  # main parser
42
42
  #
43
43
 
44
- def parse_argv(argv)
44
+ def parse_argv(argv, passthru: false)
45
45
  {}.tap do |result|
46
46
  # any non-flags we find below
47
47
  operands = []
@@ -52,7 +52,12 @@ module RunKit
52
52
  case item
53
53
  when Flag::SWITCH_RE, Flag::INLINE_RE then result.merge!(parse_switch(item, Regexp.last_match, queue))
54
54
  when /\A-[^-]/ then result.merge!(parse_smashed(item, queue))
55
- when "", /\A[^-]/ then operands << item
55
+ when "", /\A[^-]/
56
+ operands << item
57
+ if passthru
58
+ operands.concat(queue)
59
+ break
60
+ end
56
61
  when "--" then break operands.concat(queue)
57
62
  else; raise Error, "unexpected argument '#{item}' found"
58
63
  end
@@ -77,7 +82,6 @@ module RunKit
77
82
 
78
83
  # -x or --xyz?
79
84
  if (flag = config.flag(switch))
80
- builtin!(flag)
81
85
  param = queue.shift if flag.takes_param? && separator.empty?
82
86
  return {flag.key => flag.parse(switch, param)}
83
87
  end
@@ -103,7 +107,6 @@ module RunKit
103
107
  switch = "-#{group[idx]}"
104
108
  flag = config.flag(switch)
105
109
  raise Error, "unexpected argument '#{group}' found" unless flag
106
- builtin!(flag)
107
110
 
108
111
  # For `-qnLee`, `Lee` belongs to `-n`; for `-qn Lee`, shift the queue.
109
112
  if flag.takes_param?
@@ -136,11 +139,6 @@ module RunKit
136
139
  # helpers
137
140
  #
138
141
 
139
- def builtin!(flag)
140
- raise HelpRequested if flag == config.help_flag
141
- raise VersionRequested if flag == config.version_flag
142
- end
143
-
144
142
  def find_negated_flag(switch)
145
143
  if (m = Flag::NEGATE_RE.match(switch))
146
144
  flag = config.flag("--#{m[1]}")
data/lib/run_kit/shell.rb CHANGED
@@ -29,9 +29,9 @@ module RunKit
29
29
  # json file read/write, including gz
30
30
  #
31
31
 
32
- def json_read(path, symbolize_names: true) = JSON.parse(file_read(path), symbolize_names:)
32
+ def json_read(path, symbolize_names: true) = JSON.parse(file_read(path), allow_comments: true, symbolize_names:)
33
33
  def json_write(path, json) = file_write(path, JSON.pretty_generate(json))
34
- def jsonl_read(path, symbolize_names: true) = file_read(path).split("\n").map { JSON.parse(_1, symbolize_names:) }
34
+ def jsonl_read(path, symbolize_names: true) = file_read(path).split("\n").map { JSON.parse(_1, allow_comments: true, symbolize_names:) }
35
35
  def jsonl_write(path, json) = file_write(path, json.map { JSON.generate(_1) }.join("\n"))
36
36
 
37
37
  #
@@ -262,8 +262,8 @@ module RunKit
262
262
  data = gunzip(data) if compress
263
263
  case format
264
264
  when :bin then data.force_encoding("ascii-8bit")
265
- when :json then JSON.parse(data)
266
- when :jsonl then data.split("\n").map { JSON.parse(_1) }
265
+ when :json then JSON.parse(data, allow_comments: true)
266
+ when :jsonl then data.split("\n").map { JSON.parse(_1, allow_comments: true) }
267
267
  when :marshal then Marshal.load(data)
268
268
  when :str, :string then data.force_encoding("utf-8")
269
269
  else; raise "unknown format #{format.inspect}"
data/run_kit.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "run_kit"
3
- s.version = "0.1.3"
3
+ s.version = "0.1.4"
4
4
  s.authors = ["Adam Doppelt"]
5
5
  s.email = "amd@gurge.com"
6
6
  s.summary = "Run kit."
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: run_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Doppelt
@@ -87,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
89
  requirements: []
90
- rubygems_version: 3.6.9
90
+ rubygems_version: 4.0.6
91
91
  specification_version: 4
92
92
  summary: Run kit.
93
93
  test_files: []