ukiryu 0.1.0 → 0.1.1

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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +63 -0
  3. data/.github/workflows/links.yml +99 -0
  4. data/.github/workflows/rake.yml +19 -0
  5. data/.github/workflows/release.yml +27 -0
  6. data/.gitignore +18 -4
  7. data/.rubocop.yml +1 -0
  8. data/.rubocop_todo.yml +213 -0
  9. data/Gemfile +12 -8
  10. data/README.adoc +613 -0
  11. data/Rakefile +2 -2
  12. data/docs/assets/logo.svg +1 -0
  13. data/exe/ukiryu +11 -0
  14. data/lib/ukiryu/action/base.rb +77 -0
  15. data/lib/ukiryu/cache.rb +199 -0
  16. data/lib/ukiryu/cli.rb +133 -307
  17. data/lib/ukiryu/cli_commands/base_command.rb +155 -0
  18. data/lib/ukiryu/cli_commands/commands_command.rb +120 -0
  19. data/lib/ukiryu/cli_commands/commands_command.rb.fixed +40 -0
  20. data/lib/ukiryu/cli_commands/config_command.rb +249 -0
  21. data/lib/ukiryu/cli_commands/describe_command.rb +326 -0
  22. data/lib/ukiryu/cli_commands/describe_command.rb.fixed +254 -0
  23. data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +180 -0
  24. data/lib/ukiryu/cli_commands/extract_command.rb +84 -0
  25. data/lib/ukiryu/cli_commands/info_command.rb +156 -0
  26. data/lib/ukiryu/cli_commands/list_command.rb +70 -0
  27. data/lib/ukiryu/cli_commands/opts_command.rb +106 -0
  28. data/lib/ukiryu/cli_commands/opts_command.rb.fixed +105 -0
  29. data/lib/ukiryu/cli_commands/response_formatter.rb +240 -0
  30. data/lib/ukiryu/cli_commands/run_command.rb +375 -0
  31. data/lib/ukiryu/cli_commands/run_file_command.rb +215 -0
  32. data/lib/ukiryu/cli_commands/system_command.rb +90 -0
  33. data/lib/ukiryu/cli_commands/validate_command.rb +87 -0
  34. data/lib/ukiryu/cli_commands/version_command.rb +16 -0
  35. data/lib/ukiryu/cli_commands/which_command.rb +166 -0
  36. data/lib/ukiryu/command_builder.rb +205 -0
  37. data/lib/ukiryu/config/env_provider.rb +64 -0
  38. data/lib/ukiryu/config/env_schema.rb +63 -0
  39. data/lib/ukiryu/config/override_resolver.rb +68 -0
  40. data/lib/ukiryu/config/type_converter.rb +59 -0
  41. data/lib/ukiryu/config.rb +249 -0
  42. data/lib/ukiryu/errors.rb +3 -0
  43. data/lib/ukiryu/executable_locator.rb +114 -0
  44. data/lib/ukiryu/execution/command_info.rb +64 -0
  45. data/lib/ukiryu/execution/metadata.rb +97 -0
  46. data/lib/ukiryu/execution/output.rb +144 -0
  47. data/lib/ukiryu/execution/result.rb +194 -0
  48. data/lib/ukiryu/execution.rb +15 -0
  49. data/lib/ukiryu/execution_context.rb +251 -0
  50. data/lib/ukiryu/executor.rb +76 -493
  51. data/lib/ukiryu/extractors/base_extractor.rb +63 -0
  52. data/lib/ukiryu/extractors/extractor.rb +150 -0
  53. data/lib/ukiryu/extractors/help_parser.rb +188 -0
  54. data/lib/ukiryu/extractors/native_extractor.rb +47 -0
  55. data/lib/ukiryu/io.rb +196 -0
  56. data/lib/ukiryu/logger.rb +544 -0
  57. data/lib/ukiryu/models/argument.rb +28 -0
  58. data/lib/ukiryu/models/argument_definition.rb +119 -0
  59. data/lib/ukiryu/models/arguments.rb +113 -0
  60. data/lib/ukiryu/models/command_definition.rb +176 -0
  61. data/lib/ukiryu/models/command_info.rb +37 -0
  62. data/lib/ukiryu/models/components.rb +107 -0
  63. data/lib/ukiryu/models/env_var_definition.rb +30 -0
  64. data/lib/ukiryu/models/error_response.rb +41 -0
  65. data/lib/ukiryu/models/execution_metadata.rb +31 -0
  66. data/lib/ukiryu/models/execution_report.rb +236 -0
  67. data/lib/ukiryu/models/exit_codes.rb +74 -0
  68. data/lib/ukiryu/models/flag_definition.rb +67 -0
  69. data/lib/ukiryu/models/option_definition.rb +102 -0
  70. data/lib/ukiryu/models/output_info.rb +25 -0
  71. data/lib/ukiryu/models/platform_profile.rb +153 -0
  72. data/lib/ukiryu/models/routing.rb +211 -0
  73. data/lib/ukiryu/models/search_paths.rb +39 -0
  74. data/lib/ukiryu/models/success_response.rb +85 -0
  75. data/lib/ukiryu/models/tool_definition.rb +145 -0
  76. data/lib/ukiryu/models/tool_metadata.rb +82 -0
  77. data/lib/ukiryu/models/validation_result.rb +80 -0
  78. data/lib/ukiryu/models/version_compatibility.rb +152 -0
  79. data/lib/ukiryu/models/version_detection.rb +39 -0
  80. data/lib/ukiryu/models.rb +23 -0
  81. data/lib/ukiryu/options/base.rb +95 -0
  82. data/lib/ukiryu/options_builder/formatter.rb +87 -0
  83. data/lib/ukiryu/options_builder/validator.rb +43 -0
  84. data/lib/ukiryu/options_builder.rb +311 -0
  85. data/lib/ukiryu/platform.rb +6 -6
  86. data/lib/ukiryu/registry.rb +143 -183
  87. data/lib/ukiryu/response/base.rb +217 -0
  88. data/lib/ukiryu/runtime.rb +179 -0
  89. data/lib/ukiryu/schema_validator.rb +8 -10
  90. data/lib/ukiryu/shell/bash.rb +3 -3
  91. data/lib/ukiryu/shell/cmd.rb +4 -4
  92. data/lib/ukiryu/shell/fish.rb +1 -1
  93. data/lib/ukiryu/shell/powershell.rb +3 -3
  94. data/lib/ukiryu/shell/sh.rb +1 -1
  95. data/lib/ukiryu/shell/zsh.rb +1 -1
  96. data/lib/ukiryu/shell.rb +146 -39
  97. data/lib/ukiryu/thor_ext.rb +208 -0
  98. data/lib/ukiryu/tool.rb +649 -258
  99. data/lib/ukiryu/tool_index.rb +224 -0
  100. data/lib/ukiryu/tools/base.rb +381 -0
  101. data/lib/ukiryu/tools/class_generator.rb +132 -0
  102. data/lib/ukiryu/tools/executable_finder.rb +29 -0
  103. data/lib/ukiryu/tools/generator.rb +154 -0
  104. data/lib/ukiryu/tools.rb +109 -0
  105. data/lib/ukiryu/type.rb +28 -43
  106. data/lib/ukiryu/validation/constraints.rb +281 -0
  107. data/lib/ukiryu/validation/validator.rb +188 -0
  108. data/lib/ukiryu/validation.rb +21 -0
  109. data/lib/ukiryu/version.rb +1 -1
  110. data/lib/ukiryu/version_detector.rb +51 -0
  111. data/lib/ukiryu.rb +31 -15
  112. data/ukiryu-proposal.md +2952 -0
  113. data/ukiryu.gemspec +18 -14
  114. metadata +137 -5
  115. data/.github/workflows/test.yml +0 -143
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_extractor'
4
+ require_relative 'native_extractor'
5
+ require_relative 'help_parser'
6
+
7
+ module Ukiryu
8
+ module Extractors
9
+ # Main extractor class that orchestrates extraction strategies
10
+ #
11
+ # Tries multiple extraction strategies in order:
12
+ # 1. Native flag extraction (--ukiryu-definition)
13
+ # 2. Help parsing (--help output)
14
+ #
15
+ # @example Extract definition from a tool
16
+ # result = Ukiryu::Extractor.extract(:git)
17
+ # if result[:success]
18
+ # puts result[:yaml]
19
+ # else
20
+ # puts "Failed: #{result[:error]}"
21
+ # end
22
+ class Extractor
23
+ # Extract definition from a tool
24
+ #
25
+ # Tries multiple extraction strategies in order until one succeeds.
26
+ #
27
+ # @param tool_name [String, Symbol] the tool name
28
+ # @param options [Hash] extraction options
29
+ # @option options [Symbol] :method specific method to use (:native, :help, :auto)
30
+ # @option options [Boolean] :verbose enable verbose output
31
+ # @return [Hash] result with :success, :yaml, :method, :error keys
32
+ def self.extract(tool_name, options = {})
33
+ new(tool_name, options).extract
34
+ end
35
+
36
+ # Initialize the extractor
37
+ #
38
+ # @param tool_name [String, Symbol] the tool name
39
+ # @param options [Hash] extraction options
40
+ def initialize(tool_name, options = {})
41
+ @tool_name = tool_name
42
+ @options = options
43
+ end
44
+
45
+ # Extract definition using available strategies
46
+ #
47
+ # @return [Hash] result with :success, :yaml, :method, :error keys
48
+ def extract
49
+ method = @options[:method] || :auto
50
+
51
+ if method == :auto
52
+ extract_auto
53
+ elsif method == :native
54
+ extract_with_native
55
+ elsif method == :help
56
+ extract_with_help
57
+ else
58
+ {
59
+ success: false,
60
+ error: "Unknown extraction method: #{method}",
61
+ method: nil,
62
+ yaml: nil
63
+ }
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ # Try all extraction strategies in order
70
+ #
71
+ # @return [Hash] result hash
72
+ def extract_auto
73
+ # Try native flag first
74
+ result = extract_with_native
75
+ return result if result[:success]
76
+
77
+ # Fall back to help parsing
78
+ extract_with_help
79
+ end
80
+
81
+ # Extract using native flag
82
+ #
83
+ # @return [Hash] result hash
84
+ def extract_with_native
85
+ extractor = NativeExtractor.new(@tool_name, @options)
86
+
87
+ unless extractor.available?
88
+ return {
89
+ success: false,
90
+ error: "Tool '#{@tool_name}' does not support native definition extraction",
91
+ method: :native,
92
+ yaml: nil
93
+ }
94
+ end
95
+
96
+ yaml = extractor.extract
97
+
98
+ if yaml
99
+ {
100
+ success: true,
101
+ yaml: yaml,
102
+ method: :native,
103
+ error: nil
104
+ }
105
+ else
106
+ {
107
+ success: false,
108
+ error: "Native extraction failed",
109
+ method: :native,
110
+ yaml: nil
111
+ }
112
+ end
113
+ end
114
+
115
+ # Extract using help parser
116
+ #
117
+ # @return [Hash] result hash
118
+ def extract_with_help
119
+ extractor = HelpParser.new(@tool_name, @options)
120
+
121
+ unless extractor.available?
122
+ return {
123
+ success: false,
124
+ error: "Tool '#{@tool_name}' does not have help output",
125
+ method: :help,
126
+ yaml: nil
127
+ }
128
+ end
129
+
130
+ yaml = extractor.extract
131
+
132
+ if yaml
133
+ {
134
+ success: true,
135
+ yaml: yaml,
136
+ method: :help,
137
+ error: nil
138
+ }
139
+ else
140
+ {
141
+ success: false,
142
+ error: "Help parsing failed",
143
+ method: :help,
144
+ yaml: nil
145
+ }
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_extractor'
4
+
5
+ module Ukiryu
6
+ module Extractors
7
+ # Help parser extraction strategy
8
+ #
9
+ # Reverse-engineers a tool definition by parsing the output
10
+ # of the tool's `--help` command.
11
+ class HelpParser < BaseExtractor
12
+ # Extract definition by parsing help output
13
+ #
14
+ # @return [String, nil] the YAML definition or nil if extraction failed
15
+ def extract
16
+ return nil unless available?
17
+
18
+ help_result = execute_command([@tool_name.to_s, '--help'])
19
+ return nil unless help_result[:exit_status].zero?
20
+
21
+ help_text = help_result[:stdout] + help_result[:stderr]
22
+ return nil if help_text.strip.empty?
23
+
24
+ # Parse help output and generate YAML
25
+ parse_help_to_yaml(help_text)
26
+ end
27
+
28
+ # Check if the tool has help output
29
+ #
30
+ # @return [Boolean] true if --help produces output
31
+ def available?
32
+ help_result = execute_command([@tool_name.to_s, '--help'])
33
+ help_result[:exit_status].zero? && !(help_result[:stdout] + help_result[:stderr]).strip.empty?
34
+ end
35
+
36
+ private
37
+
38
+ # Parse help text and convert to YAML format
39
+ #
40
+ # @param help_text [String] the help output
41
+ # @return [String] YAML definition
42
+ def parse_help_to_yaml(help_text)
43
+ require 'yaml'
44
+
45
+ # Extract tool name from help text (usually first word)
46
+ name = extract_name(help_text)
47
+
48
+ # Try to detect version
49
+ version = extract_version
50
+
51
+ # Build basic YAML structure
52
+ definition = {
53
+ 'ukiryu_schema' => '1.1',
54
+ '$self' => "https://www.ukiryu.com/register/1.1/#{name}/#{version || '1.0'}",
55
+ 'name' => name,
56
+ 'version' => version || '1.0',
57
+ 'display_name' => name.capitalize,
58
+ 'profiles' => [
59
+ {
60
+ 'name' => 'default',
61
+ 'platforms' => %w[macos linux windows],
62
+ 'shells' => %w[bash zsh fish powershell cmd],
63
+ 'commands' => []
64
+ }
65
+ ]
66
+ }
67
+
68
+ # Parse commands/options/flags from help text
69
+ parse_help_elements(help_text, definition)
70
+
71
+ definition.to_yaml
72
+ end
73
+
74
+ # Extract tool name from help text
75
+ #
76
+ # @param help_text [String] the help output
77
+ # @return [String] the tool name
78
+ def extract_name(help_text)
79
+ # Use the tool name passed to the extractor
80
+ @tool_name.to_s
81
+ end
82
+
83
+ # Try to detect tool version
84
+ #
85
+ # @return [String, nil] the detected version
86
+ def extract_version
87
+ version_result = execute_command([@tool_name.to_s, '--version'])
88
+ if version_result[:exit_status].zero?
89
+ version_text = version_result[:stdout]
90
+ # Try to extract version number
91
+ if version_text =~ /(\d+\.\d+(?:\.\d+)?)/
92
+ return Regexp.last_match(1)
93
+ end
94
+ end
95
+ nil
96
+ end
97
+
98
+ # Parse help elements (commands, options, flags)
99
+ #
100
+ # @param help_text [String] the help output
101
+ # @param definition [Hash] the definition hash to modify
102
+ def parse_help_elements(help_text, definition)
103
+ # This is a basic implementation - real-world parsing would be more sophisticated
104
+ # For now, we create a basic structure
105
+
106
+ lines = help_text.split("\n")
107
+
108
+ # Look for option patterns like:
109
+ # -v, --verbose
110
+ # --output=FILE
111
+ # -h, --help
112
+
113
+ options = []
114
+
115
+ lines.each do |line|
116
+ # Match short and long options
117
+ if line =~ /^\s*(-[a-z]),?\s*\[--([a-z-]+)(?:[=\s]+([A-Z_]+))?\]/i
118
+ short_opt = Regexp.last_match(1)
119
+ long_opt = Regexp.last_match(2)
120
+ param = Regexp.last_match(3)
121
+
122
+ option = {
123
+ 'name' => long_opt.gsub(/-/, '_'),
124
+ 'description' => line.strip
125
+ }
126
+
127
+ if short_opt
128
+ option['cli'] = short_opt
129
+ else
130
+ option['cli'] = "--#{long_opt}"
131
+ end
132
+
133
+ if param
134
+ option['type'] = infer_type(param)
135
+ else
136
+ option['type'] = 'boolean'
137
+ end
138
+
139
+ options << option
140
+ elsif line =~ /^\s*\[--([a-z-]+)(?:[=\s]+([A-Z_]+))?\]/i
141
+ long_opt = Regexp.last_match(1)
142
+ param = Regexp.last_match(2)
143
+
144
+ option = {
145
+ 'name' => long_opt.gsub(/-/, '_'),
146
+ 'cli' => "--#{long_opt}",
147
+ 'description' => line.strip
148
+ }
149
+
150
+ if param
151
+ option['type'] = infer_type(param)
152
+ else
153
+ option['type'] = 'boolean'
154
+ end
155
+
156
+ options << option
157
+ end
158
+ end
159
+
160
+ # Add default command with parsed options
161
+ definition['profiles'][0]['commands'] = [
162
+ {
163
+ 'name' => 'default',
164
+ 'description' => "Default #{@tool_name} command",
165
+ 'options' => options.uniq { |opt| opt['name'] }
166
+ }
167
+ ]
168
+ end
169
+
170
+ # Infer type from parameter name
171
+ #
172
+ # @param param_name [String] the parameter name
173
+ # @return [String] the inferred type
174
+ def infer_type(param_name)
175
+ case param_name.upcase
176
+ when /FILE|PATH/
177
+ 'file'
178
+ when /NUM|COUNT|LEVEL/
179
+ 'integer'
180
+ when /DIR|FOLDER/
181
+ 'directory'
182
+ else
183
+ 'string'
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_extractor'
4
+
5
+ module Ukiryu
6
+ module Extractors
7
+ # Native flag extraction strategy
8
+ #
9
+ # Attempts to extract definition using the tool's native
10
+ # `--ukiryu-definition` flag if supported.
11
+ class NativeExtractor < BaseExtractor
12
+ # Native flag to try
13
+ NATIVE_FLAG = '--ukiryu-definition'
14
+
15
+ # Extract definition using native flag
16
+ #
17
+ # @return [String, nil] the YAML definition or nil if extraction failed
18
+ def extract
19
+ return nil unless available?
20
+
21
+ result = execute_command([@tool_name.to_s, NATIVE_FLAG])
22
+
23
+ return nil unless result[:exit_status].zero?
24
+ return nil if result[:stdout].strip.empty?
25
+
26
+ result[:stdout]
27
+ end
28
+
29
+ # Check if the tool supports native definition extraction
30
+ #
31
+ # @return [Boolean] true if the tool exists and the flag is supported
32
+ def available?
33
+ # First check if tool exists
34
+ which_result = execute_command(['which', @tool_name.to_s])
35
+ return false unless which_result[:exit_status].zero?
36
+
37
+ # Then check if it supports the flag
38
+ help_result = execute_command([@tool_name.to_s, '--help'])
39
+ return false unless help_result[:exit_status].zero?
40
+
41
+ # Check if help mentions ukiryu
42
+ help_output = help_result[:stdout] + help_result[:stderr]
43
+ help_output.downcase.include?('ukiryu') || help_output.include?(NATIVE_FLAG)
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/ukiryu/io.rb ADDED
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ukiryu
4
+ # I/O stream primitives for CLI tools
5
+ #
6
+ # This module defines standard I/O primitives that are common across
7
+ # command-line tools, providing a consistent interface for:
8
+ # - Standard input (stdin)
9
+ # - Standard output (stdout)
10
+ # - Standard error (stderr)
11
+ # - Pipes (inter-process communication)
12
+ # - File redirections
13
+ #
14
+ # These are GENERAL PRIMITIVES that apply to ALL CLI tools, not tool-specific.
15
+
16
+ # Standard stream marker for stdin
17
+ STDIN = '-'
18
+
19
+ # Standard stream marker for stdout
20
+ STDOUT = '-'
21
+ # Also available as "%stdout%" in some tools (Ghostscript)
22
+
23
+ # Special value for reading from stdin (double dash)
24
+ STDIN_MARKER = '--'
25
+
26
+ # Standard stream constants
27
+ module Stream
28
+ # Standard input stream descriptor
29
+ #
30
+ # Used for commands that can read from stdin instead of a file
31
+ # @example
32
+ # # Read from stdin
33
+ # tool.options_for(:process).tap do |opts|
34
+ # opts.inputs = [Ukiryu::IO::Stream::STDIN]
35
+ # opts.output = "output.pdf"
36
+ # end
37
+ STDIN = :stdin
38
+
39
+ # Standard output stream descriptor
40
+ #
41
+ # Used for commands that can write to stdout instead of a file
42
+ # @example
43
+ # tool.options_for(:export).tap do |opts|
44
+ # opts.inputs = ["input.svg"]
45
+ # opts.output = Ukiryu::IO::Stream::STDOUT
46
+ # end
47
+ STDOUT = :stdout
48
+
49
+ # Standard error stream descriptor
50
+ #
51
+ # Used for separating stderr from stdout
52
+ STDERR = :stderr
53
+ end
54
+
55
+ # Pipe redirection for inter-process communication
56
+ #
57
+ # Pipes allow the output of one command to become the input of another.
58
+ # This is represented by special file path markers.
59
+ #
60
+ # @example
61
+ # # Pipe output of command1 to command2
62
+ # result1 = command1.execute(output: Pipe.to("command2"))
63
+ #
64
+ # @example Using special syntax in tools
65
+ # # Ghostscript: -sOutputFile=%pipe%lpr
66
+ # # Tar: --to-command=COMMAND
67
+ class Pipe
68
+ # Special marker for pipe output
69
+ MARKER = '%pipe%'
70
+
71
+ # Create a pipe to a command
72
+ #
73
+ # @param command [String] the command to pipe to
74
+ # @return [String] the pipe marker for use in CLI options
75
+ #
76
+ # @example
77
+ # Pipe.to("lpr") # => "%pipe%lpr"
78
+ def self.to(command)
79
+ "#{MARKER}#{command}"
80
+ end
81
+
82
+ # Parse a pipe marker
83
+ #
84
+ # @param value [String] the pipe marker string
85
+ # @return [String, nil] the command if it's a pipe marker
86
+ #
87
+ # @example
88
+ # Pipe.parse("%pipe%lpr") # => "lpr"
89
+ def self.parse(value)
90
+ return nil unless value.is_a?(String)
91
+ return nil unless value.start_with?(MARKER)
92
+
93
+ value.sub(MARKER, '')
94
+ end
95
+
96
+ # Check if a value is a pipe marker
97
+ #
98
+ # @param value [String] the value to check
99
+ # @return [Boolean] true if it's a pipe marker
100
+ def self.pipe?(value)
101
+ return false unless value.is_a?(String)
102
+
103
+ value.start_with?(MARKER)
104
+ end
105
+ end
106
+
107
+ # File redirection primitives
108
+ #
109
+ # Provides utilities for file redirection operations
110
+ module Redirection
111
+ # Redirect output to a file
112
+ #
113
+ # @param output [Symbol, String] :stdout or :stderr
114
+ # @param path [String] the file path to redirect to
115
+ # @return [Hash] redirection specification
116
+ #
117
+ # @example
118
+ # Redirection.to(:stdout, "/tmp/output.txt")
119
+ def self.to(stream, path)
120
+ { stream => path }
121
+ end
122
+
123
+ # Redirect stderr to stdout (2>&1 in shell)
124
+ #
125
+ # @return [Hash] redirection specification
126
+ #
127
+ # @example
128
+ # Redirection.stderr_to_stdout
129
+ def self.stderr_to_stdout
130
+ { stderr: :stdout }
131
+ end
132
+ end
133
+
134
+ # Standard input/output file handles
135
+ #
136
+ # This class represents special file handles for stdin/stdout/stderr
137
+ # that are recognized by CLI tools.
138
+ #
139
+ # @example
140
+ # # Reading from stdin
141
+ # input = Ukiryu::IO::StandardInput.new
142
+ # input.read
143
+ #
144
+ # # Writing to stdout
145
+ # output = Ukiryu::IO::StandardOutput.new
146
+ # output.write("data")
147
+ class StandardInput
148
+ # The stdin file descriptor
149
+ FILENO = 0
150
+
151
+ # Check if a path represents stdin
152
+ #
153
+ # @param path [String, Symbol] the path to check
154
+ # @return [Boolean] true if the path represents stdin
155
+ #
156
+ # @example
157
+ # Ukiryu::IO::StandardInput.stdin?("-") # => true
158
+ # Ukiryu::IO::StandardInput.stdin?(:stdin) # => true
159
+ def self.stdin?(path)
160
+ path = path.to_s if path.is_a?(Symbol)
161
+ [$stdin, '-', '/dev/stdin'].include?(path)
162
+ end
163
+ end
164
+
165
+ class StandardOutput
166
+ # The stdout file descriptor
167
+ FILENO = 1
168
+
169
+ # Check if a path represents stdout
170
+ #
171
+ # @param path [String, Symbol] the path to check
172
+ # @return [Boolean] true if the path represents stdout
173
+ #
174
+ # @example
175
+ # Ukiryu::IO::StandardOutput.stdout?("-") # => true
176
+ # Ukiryu::IO::StandardOutput.stdout?(:stdout) # => true
177
+ def self.stdout?(path)
178
+ path = path.to_s if path.is_a?(Symbol)
179
+ [$stdout, '-', '/dev/stdout', '%stdout%'].include?(path)
180
+ end
181
+ end
182
+
183
+ class StandardError
184
+ # The stderr file descriptor
185
+ FILENO = 2
186
+
187
+ # Check if a path represents stderr
188
+ #
189
+ # @param path [String, Symbol] the path to check
190
+ # @return [Boolean] true if the path represents stderr
191
+ def self.stderr?(path)
192
+ path = path.to_s if path.is_a?(Symbol)
193
+ [:stderr, '/dev/stderr'].include?(path)
194
+ end
195
+ end
196
+ end