cinnabar 0.0.0 → 0.0.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/.yardopts +4 -0
- data/docs/Readme-zh.md +3 -35
- data/docs/Readme.md +92 -2
- data/lib/cinnabar/00_pre.rb +1 -1
- data/lib/cinnabar/cmd_runner.rb +437 -88
- data/lib/cinnabar/net.rb +49 -19
- data/lib/cinnabar/pipe.rb +5 -5
- data/lib/cinnabar/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ba73ee68ae237057d44f55990de599fae1290bdb0ad22ed9f4a3cc6688e8a9c
|
|
4
|
+
data.tar.gz: 0ca77910329aa2c50e2ee0f6562ae1873ab31db0926dedc856aafeb9e1b98c03
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9123046f853c2533f6adbab221418066af5c351b283c346e31937ecd7a8010b897860cb3fb1365386d3d6abfdf7e822e405aa79ab63420fd1553cb5605f08b9f
|
|
7
|
+
data.tar.gz: 54b6ac22db5f6b5bb7287329d9b74876f03a8e76738968276cdd45b6d2ce22281b084c203ae93e66615bead81a2ce94cebbfdfd518c84956017a6f3a3e51949b
|
data/.rubocop.yml
CHANGED
data/.yardopts
ADDED
data/docs/Readme-zh.md
CHANGED
|
@@ -24,41 +24,9 @@
|
|
|
24
24
|
2. 朱砂有毒。这个项目是为了 **猛、糙、快** (a.k.a. *Dirty and Quick*) 的目的而开发的,可能会产生意料之外的副作用(它并非完全无害)。
|
|
25
25
|
3. 朱砂是一种硫化汞 (HgS) 的矿物,呈深红色,而 Ruby 也是一种深红色的宝石。给一个 Ruby 项目取名为 “Cinnabar(朱砂)” 非常贴切。
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## API DOC
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+

|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
env:
|
|
33
|
-
# Speeds up script startup by disabling RubyGems
|
|
34
|
-
RUBYOPT: "--disable=gems"
|
|
35
|
-
default_ci_shell: ruby cinnabar/ci.rb {0}
|
|
31
|
+
- Github Pages: <https://2moe.github.io/cinnabar>
|
|
36
32
|
|
|
37
|
-
jobs:
|
|
38
|
-
build:
|
|
39
|
-
runs-on: ubuntu-latest
|
|
40
|
-
defaults:
|
|
41
|
-
run:
|
|
42
|
-
shell: ${{env.default_ci_shell}}
|
|
43
|
-
steps:
|
|
44
|
-
- uses: actions/checkout@v6
|
|
45
|
-
|
|
46
|
-
- name: clone cinnabar
|
|
47
|
-
uses: actions/checkout@v6
|
|
48
|
-
with:
|
|
49
|
-
repository: 2moe/cinnabar
|
|
50
|
-
path: cinnabar
|
|
51
|
-
ref: v0.0.0
|
|
52
|
-
|
|
53
|
-
- name: (example) run cargo command
|
|
54
|
-
run: |
|
|
55
|
-
{
|
|
56
|
-
cargo: (),
|
|
57
|
-
build: (),
|
|
58
|
-
profile: 'release',
|
|
59
|
-
verbose: true,
|
|
60
|
-
target: 'x86_64-unknown-linux-musl'
|
|
61
|
-
}
|
|
62
|
-
.to_argv
|
|
63
|
-
.run
|
|
64
|
-
```
|
data/docs/Readme.md
CHANGED
|
@@ -24,6 +24,12 @@ A:
|
|
|
24
24
|
2. Cinnabar is toxic. This project was developed for *Dirty and Quick* purposes and may produce unexpected side effects—in a sense, it is not entirely harmless.
|
|
25
25
|
3. Cinnabar, a mineral form of mercury sulfide (HgS), is a deep red-colored stone. And ruby is also a deep red stone. Naming a Ruby project "Cinnabar" is particularly fitting.
|
|
26
26
|
|
|
27
|
+
## API DOC
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
- Github Pages: <https://2moe.github.io/cinnabar>
|
|
32
|
+
|
|
27
33
|
## Quick Start
|
|
28
34
|
|
|
29
35
|
Github Actions for cinnabar
|
|
@@ -33,6 +39,8 @@ env:
|
|
|
33
39
|
# Speeds up script startup by disabling RubyGems
|
|
34
40
|
RUBYOPT: "--disable=gems"
|
|
35
41
|
default_ci_shell: ruby cinnabar/ci.rb {0}
|
|
42
|
+
# optional values: debug, info, warn, error, fatal, unknown
|
|
43
|
+
RUBY_LOG: "debug"
|
|
36
44
|
|
|
37
45
|
jobs:
|
|
38
46
|
build:
|
|
@@ -48,7 +56,7 @@ jobs:
|
|
|
48
56
|
with:
|
|
49
57
|
repository: 2moe/cinnabar
|
|
50
58
|
path: cinnabar
|
|
51
|
-
ref: v0.0.
|
|
59
|
+
ref: v0.0.1
|
|
52
60
|
|
|
53
61
|
- name: (example) run cargo command
|
|
54
62
|
run: |
|
|
@@ -60,5 +68,87 @@ jobs:
|
|
|
60
68
|
target: 'x86_64-unknown-linux-musl'
|
|
61
69
|
}
|
|
62
70
|
.to_argv
|
|
63
|
-
.
|
|
71
|
+
.run_cmd
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Examples
|
|
75
|
+
|
|
76
|
+
### Command Runner
|
|
77
|
+
|
|
78
|
+
#### `.run` + pass stdin data
|
|
79
|
+
|
|
80
|
+
```ruby,yaml
|
|
81
|
+
- run: |
|
|
82
|
+
opts = { stdin_data: "Hello", allow_failure: true }
|
|
83
|
+
stdout = %w[wc -m].run(opts:)
|
|
84
|
+
|
|
85
|
+
stdout.to_i == 5 #=> true
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### `.async_run`
|
|
89
|
+
|
|
90
|
+
```ruby,yaml
|
|
91
|
+
- run: |
|
|
92
|
+
'building wasi file...'.log_dbg
|
|
93
|
+
task = {
|
|
94
|
+
cargo: (),
|
|
95
|
+
b: (),
|
|
96
|
+
r: true,
|
|
97
|
+
target: 'wasm32-wasip2'
|
|
98
|
+
} .to_argv
|
|
99
|
+
.async_run
|
|
100
|
+
|
|
101
|
+
stdout, status = task.wait_with_output
|
|
102
|
+
stdout.log_info
|
|
103
|
+
raise "wasi" unless status.success?
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `.async_run` + pass stdin data
|
|
107
|
+
|
|
108
|
+
```ruby,yaml
|
|
109
|
+
- run: |
|
|
110
|
+
stdin_data = <<~'QMP_JSON'
|
|
111
|
+
{ "execute":"qmp_capabilities" }
|
|
112
|
+
{ "execute":"query-cpu-model-expansion",
|
|
113
|
+
"arguments":{"type":"full","model":{"name":"host"}} }
|
|
114
|
+
{ "execute":"quit" }
|
|
115
|
+
QMP_JSON
|
|
116
|
+
|
|
117
|
+
# opts = { stdin_data:, stdin_binmode: false }
|
|
118
|
+
opts = { stdin_data: }
|
|
119
|
+
|
|
120
|
+
accel = %w[kvm hvf tcg].join ':'
|
|
121
|
+
task = {
|
|
122
|
+
'qemu-system-x86_64': (),
|
|
123
|
+
machine: "accel=#{accel}",
|
|
124
|
+
cpu: 'host',
|
|
125
|
+
display: 'none',
|
|
126
|
+
nodefaults: true,
|
|
127
|
+
no_user_config: true,
|
|
128
|
+
qmp: 'stdio',
|
|
129
|
+
} .to_argv_bsd
|
|
130
|
+
.async_run(opts:)
|
|
131
|
+
|
|
132
|
+
stdout, status = task.wait_with_output
|
|
133
|
+
stdout.log_info if status.success?
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Downloader
|
|
137
|
+
|
|
138
|
+
```ruby,yaml
|
|
139
|
+
- run: |
|
|
140
|
+
url = 'https://docs.ruby-lang.org/en/master'
|
|
141
|
+
url.download
|
|
142
|
+
# OR: url.download({out_dir: "/tmp", file_name: "index.html"})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Function Pipe
|
|
146
|
+
|
|
147
|
+
```ruby,yaml
|
|
148
|
+
- run: |
|
|
149
|
+
upper = ->s { s.upcase }
|
|
150
|
+
|
|
151
|
+
'Foo'
|
|
152
|
+
.▷(upper)
|
|
153
|
+
.▷ :puts #=> "FOO"
|
|
64
154
|
```
|
data/lib/cinnabar/00_pre.rb
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# Define Cinnabar first to prevent errors when creating Cinnabar::SubMod
|
|
4
4
|
# (compact ClassAndModuleChildren)
|
|
5
|
-
#
|
|
6
5
|
# ------------------
|
|
7
6
|
|
|
7
|
+
# @see https://github.com/2moe/cinnabar
|
|
8
8
|
module Cinnabar; end
|
|
9
9
|
|
|
10
10
|
# To ensure compatibility with "--disable=gems" (allowing users to pre-require),
|
data/lib/cinnabar/cmd_runner.rb
CHANGED
|
@@ -1,162 +1,511 @@
|
|
|
1
|
+
# typed: false
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module Cinnabar::Command
|
|
4
5
|
module_function
|
|
5
6
|
|
|
7
|
+
require 'open3'
|
|
6
8
|
using Sinlog::Refin
|
|
7
9
|
|
|
8
|
-
#
|
|
10
|
+
# Executes the command synchronously (blocking) and returns its standard output.
|
|
9
11
|
#
|
|
10
|
-
#
|
|
12
|
+
# @raise [RuntimeError] when `allow_failure: false` and the process exits with non-zero status
|
|
11
13
|
#
|
|
12
|
-
#
|
|
14
|
+
# @example pass env
|
|
13
15
|
#
|
|
14
16
|
# Cmd = Cinnabar::Command
|
|
15
|
-
# %w[
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
# cmd_arr = %w[sh -c] << 'printf $WW'
|
|
18
|
+
# env_hash = {WW: 2}
|
|
19
|
+
# opts = {allow_failure: true}
|
|
20
|
+
# output = Cmd.run(cmd_arr, env_hash, opts:)
|
|
21
|
+
# output.to_i == 2 #=> true
|
|
22
|
+
#
|
|
23
|
+
# @example pass stdin data
|
|
24
|
+
#
|
|
25
|
+
# opts = {stdin_data: "Hello\nWorld\n"}
|
|
26
|
+
# output = Cinnabar::Command.run(%w[wc -l], opts:)
|
|
27
|
+
# output.to_i == 2 #=> true
|
|
28
|
+
#
|
|
29
|
+
# @param cmd_arr [Array<String>] The command and its arguments (e.g., `%w[printf hello]`).
|
|
30
|
+
# @param env_hash [#to_h] Environment variables to pass to the command.
|
|
31
|
+
# @param opts [Hash]
|
|
32
|
+
#
|
|
33
|
+
# - Only the `:allow_failure` is extracted and handled explicitly;
|
|
34
|
+
# - all other keys are passed through to **Open3.capture2** unchanged.
|
|
35
|
+
#
|
|
36
|
+
# @option opts [Boolean] :allow_failure
|
|
37
|
+
# Indicates whether the command is allowed to fail.
|
|
38
|
+
#
|
|
39
|
+
# @return [String, nil] the standard output of the command.
|
|
40
|
+
#
|
|
41
|
+
# @see async_run
|
|
42
|
+
def run(cmd_arr, env_hash = nil, opts: {}) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
43
|
+
'Running and capturing the output of a system command.'.log_dbg
|
|
44
|
+
cmd_arr.log_info
|
|
45
|
+
"opts: #{opts}".log_dbg
|
|
46
|
+
|
|
47
|
+
allow_failure = opts.delete(:allow_failure) || false
|
|
48
|
+
|
|
49
|
+
final_env = normalize_env(env_hash)
|
|
50
|
+
|
|
51
|
+
begin
|
|
52
|
+
stdout, status =
|
|
53
|
+
if final_env.nil?
|
|
54
|
+
Open3.capture2(*cmd_arr, opts)
|
|
55
|
+
else
|
|
56
|
+
Open3.capture2(final_env, *cmd_arr, opts)
|
|
57
|
+
end
|
|
58
|
+
rescue StandardError => e
|
|
59
|
+
Kernel.raise e unless allow_failure
|
|
60
|
+
e.log_err
|
|
61
|
+
return stdout
|
|
22
62
|
end
|
|
63
|
+
|
|
64
|
+
return stdout if status.success?
|
|
65
|
+
|
|
66
|
+
err_msg = "Command failed: #{cmd_arr.join(' ')}"
|
|
67
|
+
Kernel.raise err_msg unless allow_failure
|
|
68
|
+
|
|
69
|
+
err_msg.log_err
|
|
70
|
+
stdout
|
|
23
71
|
end
|
|
24
72
|
|
|
25
|
-
#
|
|
73
|
+
# Executes a system command using Ruby's `Kernel.system`.
|
|
26
74
|
#
|
|
27
|
-
#
|
|
75
|
+
# It runs the command synchronously, blocks until completion, and does not capture stdout or stderr.
|
|
28
76
|
#
|
|
29
|
-
#
|
|
77
|
+
# @param cmd_arr [Array<String>] The command and its arguments as an array,
|
|
78
|
+
# e.g., `%w[ls -lh]`.
|
|
79
|
+
#
|
|
80
|
+
# @param env_hash [#to_h] Environment variables to pass to the command.
|
|
81
|
+
# @param opts [Hash]
|
|
82
|
+
#
|
|
83
|
+
# - Only the `:allow_failure` is extracted and handled explicitly;
|
|
84
|
+
# - all other keys are passed through to `Kernel.system` unchanged.
|
|
85
|
+
#
|
|
86
|
+
# @option opts [Boolean] :allow_failure
|
|
87
|
+
# Indicates whether the command is allowed to fail.
|
|
88
|
+
# If true, the method will return false instead of raising an exception when the
|
|
89
|
+
# command exits with a non-zero status.
|
|
90
|
+
#
|
|
91
|
+
# @see https://docs.ruby-lang.org/en/3.4/Process.html#module-Process-label-Execution+Options
|
|
92
|
+
#
|
|
93
|
+
# @example pwd
|
|
30
94
|
#
|
|
31
95
|
# Cmd = Cinnabar::Command
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
96
|
+
# opts = {chdir: '/tmp', allow_failure: true}
|
|
97
|
+
# Cmd.run_cmd(%w[pwd], opts:)
|
|
98
|
+
#
|
|
99
|
+
# @example pass env
|
|
100
|
+
#
|
|
101
|
+
# Cmd = Cinnabar::Command
|
|
102
|
+
# cmd_arr = %w[sh -c] << 'printf $WW'
|
|
103
|
+
# env_hash = {WW: 2}
|
|
104
|
+
# Cmd.run_cmd(cmd_arr, env_hash)
|
|
105
|
+
#
|
|
106
|
+
# @return [Boolean] Returns true if the command succeeds (exit status 0),
|
|
107
|
+
# or false if it fails and `allow_failure` is true.
|
|
108
|
+
# @raise [RuntimeError] Raises an error if the command fails and `allow_failure` is false.
|
|
109
|
+
def run_cmd(cmd_arr, env_hash = nil, opts: {})
|
|
110
|
+
'Running system command'.log_dbg
|
|
111
|
+
cmd_arr.log_info
|
|
112
|
+
"opts: #{opts}".log_dbg
|
|
113
|
+
|
|
114
|
+
allow_failure = opts.delete(:allow_failure) || false
|
|
115
|
+
exception = !allow_failure
|
|
116
|
+
"exception: #{exception}".log_dbg
|
|
117
|
+
options = opts.merge({ exception: })
|
|
118
|
+
|
|
119
|
+
final_env = normalize_env(env_hash)
|
|
120
|
+
if final_env.nil?
|
|
121
|
+
Kernel.system(*cmd_arr, options)
|
|
122
|
+
else
|
|
123
|
+
Kernel.system(final_env, *cmd_arr, options)
|
|
38
124
|
end
|
|
39
125
|
end
|
|
40
126
|
|
|
41
|
-
#
|
|
127
|
+
# @param hash [#to_h]
|
|
128
|
+
# @return [Hash{String => String}, nil] a hash where both keys and values are strings
|
|
129
|
+
def normalize_env(hash)
|
|
130
|
+
return nil if hash.nil?
|
|
131
|
+
return nil if hash.respond_to?(:empty?) && hash.empty?
|
|
132
|
+
|
|
133
|
+
hash.to_h { |k, v| [k.to_s, v.to_s] }
|
|
134
|
+
.tap { "normalized_env:#{_1}".log_dbg }
|
|
135
|
+
end
|
|
42
136
|
|
|
43
|
-
#
|
|
137
|
+
# Launch a command asynchronously (non-blocking) and return its stdout stream and process waiter.
|
|
44
138
|
#
|
|
45
|
-
# This
|
|
46
|
-
#
|
|
139
|
+
# This is a sugar over **Open3.popen2**, intended to start a subprocess
|
|
140
|
+
# and **immediately** hand back:
|
|
47
141
|
#
|
|
48
|
-
#
|
|
142
|
+
# 1. an `IO` for reading the command's stdout, and
|
|
143
|
+
# 2. a `Process::Waiter` (a thread-like object) that can be awaited later.
|
|
49
144
|
#
|
|
50
|
-
#
|
|
145
|
+
# - If `:stdin_data` is provided, the data will be written to
|
|
146
|
+
# the child's stdin and the stdin will be closed.
|
|
147
|
+
# - When `:stdin_data` is absent, stdin is simply closed and
|
|
148
|
+
# the method returns immediately without blocking on output.
|
|
149
|
+
#
|
|
150
|
+
# @param cmd_arr [Array<String>] The command and its arguments (e.g., `%w[printf hello]`).
|
|
151
|
+
# @param env_hash [#to_h] Optional environment variables;
|
|
152
|
+
# keys/values will be normalized by {#normalize_env} before being passed to the child.
|
|
153
|
+
#
|
|
154
|
+
# @param opts [Hash] Additional options.
|
|
155
|
+
#
|
|
156
|
+
# - Only the following keys are extracted and handled explicitly;
|
|
157
|
+
# - :stdin_data
|
|
158
|
+
# - :binmode
|
|
159
|
+
# - :stdin_binmode
|
|
160
|
+
# - :stdout_binmode
|
|
161
|
+
#
|
|
162
|
+
# all other keys are passed through to **Open3.popen2** unchanged.
|
|
163
|
+
#
|
|
164
|
+
# @option opts [String, #readpartial] :stdin_data
|
|
165
|
+
# Data to write to the child's stdin. If it responds to `#readpartial`,
|
|
166
|
+
# it will be streamed via **IO.copy_stream**;
|
|
167
|
+
#
|
|
168
|
+
# @option opts [Boolean] :binmode
|
|
169
|
+
# When `true`, set both stdin and stdout to binary mode (useful for binary data).
|
|
170
|
+
#
|
|
171
|
+
# @option opts [Boolean] :stdin_binmode
|
|
172
|
+
# Sets only stdin to binary mode.
|
|
173
|
+
#
|
|
174
|
+
# @option opts [Boolean] :stdout_binmode
|
|
175
|
+
# Sets only stdout to binary mode.
|
|
176
|
+
#
|
|
177
|
+
# @return [Array(IO, Process::Waiter)] A pair `[stdout_io, waiter]`:
|
|
178
|
+
#
|
|
179
|
+
# - `stdout_io` is an `IO` for reading **stdout**
|
|
180
|
+
# - `waiter` is a `Process::Waiter`;
|
|
181
|
+
# - call `waiter.value` to get `Process::Status`;
|
|
182
|
+
# - or `waiter.join` to block until the process exits.
|
|
183
|
+
#
|
|
184
|
+
# @raise [StandardError] Reraises any non-`Errno::EPIPE` exception encountered while writing to stdin.
|
|
185
|
+
# `Errno::EPIPE` is logged and swallowed.
|
|
186
|
+
#
|
|
187
|
+
# @example start a process and later wait for it
|
|
188
|
+
#
|
|
189
|
+
# Cmd = Cinnabar::Command
|
|
190
|
+
# stdout_fd, waiter = Cmd.async_run(['sh', '-c', 'echo hello; sleep 1; echo done'])
|
|
191
|
+
#
|
|
192
|
+
# output, status = Cmd.wait_with_output(stdout_fd, waiter)
|
|
193
|
+
#
|
|
194
|
+
# @example pass stdin data
|
|
51
195
|
#
|
|
52
196
|
# Cmd = Cinnabar::Command
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
197
|
+
# opts = {stdin_data: "Run in the background" }
|
|
198
|
+
# io_and_waiter = Cmd.async_run(%w[wc -m], opts:)
|
|
199
|
+
#
|
|
200
|
+
# output, status = Cmd.wait_with_output(*io_and_waiter)
|
|
201
|
+
# status.success? #=> true
|
|
202
|
+
# output.to_i == 21 #=> true
|
|
203
|
+
#
|
|
204
|
+
# @see wait_with_output
|
|
205
|
+
# @see run
|
|
206
|
+
def async_run(cmd_arr, env_hash = nil, opts: {}) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
|
|
207
|
+
"Asynchronously executing system command: #{cmd_arr}".log_dbg
|
|
208
|
+
"opts: #{opts}".log_dbg
|
|
209
|
+
|
|
210
|
+
stdin_data = opts.delete(:stdin_data)
|
|
211
|
+
binmode = opts.delete(:binmode)
|
|
212
|
+
stdin_binmode = opts.delete(:stdin_binmode)
|
|
213
|
+
stdout_binmode = opts.delete(:stdout_binmode)
|
|
214
|
+
|
|
215
|
+
'async_run() does not support the :allow_failure option.'.log_warn if opts.delete(:allow_failure)
|
|
216
|
+
|
|
217
|
+
final_env = normalize_env(env_hash)
|
|
218
|
+
|
|
219
|
+
stdin, stdout, waiter =
|
|
220
|
+
if final_env.nil?
|
|
221
|
+
Open3.popen2(*cmd_arr, opts)
|
|
222
|
+
else
|
|
223
|
+
Open3.popen2(final_env, *cmd_arr, opts)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
if binmode
|
|
227
|
+
stdin.binmode
|
|
228
|
+
stdout.binmode
|
|
229
|
+
else
|
|
230
|
+
stdin.binmode if stdin_binmode
|
|
231
|
+
stdout.binmode if stdout_binmode
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Non-blocking: no stdin to write; return immediately
|
|
235
|
+
unless stdin_data
|
|
236
|
+
stdin.close
|
|
237
|
+
return [stdout, waiter]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
begin
|
|
241
|
+
if stdin_data.respond_to? :readpartial
|
|
242
|
+
IO.copy_stream(stdin_data, stdin)
|
|
243
|
+
else
|
|
244
|
+
stdin.write stdin_data
|
|
245
|
+
end
|
|
246
|
+
rescue Errno::EPIPE => e
|
|
247
|
+
e.log_err
|
|
248
|
+
rescue StandardError => e
|
|
249
|
+
"Failed to write stdin data: #{e}".log_err
|
|
250
|
+
Kernel.raise e
|
|
251
|
+
ensure
|
|
252
|
+
stdin.close
|
|
64
253
|
end
|
|
254
|
+
[stdout, waiter]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Waits for a process to finish and reads all remaining output from its stdout.
|
|
258
|
+
#
|
|
259
|
+
# @param io_fd [IO] The IO object connected to the process's stdout (or combined stdout & stderr).
|
|
260
|
+
# @param waiter [Process::Waiter] The waiter thread returned by **Open3.popen2** or **Open3.popen2e**.
|
|
261
|
+
#
|
|
262
|
+
# @return [Array(String, Process::Status)] A two-element array:
|
|
263
|
+
#
|
|
264
|
+
# - The full output read from `io_fd`.
|
|
265
|
+
# - The **Process::Status** object representing the process's exit status.
|
|
266
|
+
#
|
|
267
|
+
# @example Wait for process and capture output
|
|
268
|
+
#
|
|
269
|
+
# require 'sinlog'
|
|
270
|
+
# using Sinlog::Refin
|
|
271
|
+
#
|
|
272
|
+
# Cmd = Cinnabar::Command
|
|
273
|
+
#
|
|
274
|
+
# fd, waiter = %w[ruby -e].push('sleep 2; puts "OK"')
|
|
275
|
+
# .then { Cmd.async_run(_1) }
|
|
276
|
+
#
|
|
277
|
+
# "You can now do other things without waiting for the process to complete.".log_dbg
|
|
278
|
+
#
|
|
279
|
+
# "blocking wait".log_info
|
|
280
|
+
# output, status = Cmd.wait_with_output(fd, waiter)
|
|
281
|
+
#
|
|
282
|
+
# "Exit code: #{status.exitstatus}".log_warn unless status.success?
|
|
283
|
+
# "Output:\n#{output}".log_info
|
|
284
|
+
#
|
|
285
|
+
# @note This method blocks until the process exits and all output is read.
|
|
286
|
+
# @see async_run
|
|
287
|
+
def wait_with_output(io_fd, waiter)
|
|
288
|
+
status = waiter.value
|
|
289
|
+
output = io_fd.read
|
|
290
|
+
io_fd.close
|
|
291
|
+
[output, status]
|
|
65
292
|
end
|
|
66
|
-
# ---------------
|
|
67
293
|
end
|
|
68
294
|
|
|
69
295
|
module Cinnabar::Command
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
296
|
+
# The foundation of {ArrRefin} and {ArrMixin}
|
|
297
|
+
# @see Cinnabar::Command.run
|
|
298
|
+
# @see Cinnabar::Command.async_run
|
|
299
|
+
# @see Cinnabar::Command.run_cmd
|
|
300
|
+
module ArrExt
|
|
301
|
+
# Executes the command synchronously (blocking) and returns its standard output.
|
|
302
|
+
#
|
|
303
|
+
# @note self [`Array<String>`]: The command and its arguments (e.g., `%w[printf hello]`).
|
|
304
|
+
#
|
|
305
|
+
# @param env_hash [#to_h] Environment variables to pass to the command.
|
|
306
|
+
# @param opts [Hash]
|
|
307
|
+
#
|
|
308
|
+
# - Only the `:allow_failure` is extracted and handled explicitly;
|
|
309
|
+
# - all other keys are passed through to **Open3.capture2** unchanged.
|
|
310
|
+
#
|
|
311
|
+
# @raise [StandardError] when `allow_failure: false` and the process exits with non-zero status
|
|
312
|
+
#
|
|
313
|
+
# @return [String, nil] the standard output of the command.
|
|
314
|
+
# @see Cinnabar::Command.run
|
|
315
|
+
#
|
|
316
|
+
# @example pass stdin data
|
|
317
|
+
#
|
|
318
|
+
# using Cinnabar::Command::ArrRefin
|
|
319
|
+
# # OR: include Cinnabar::Command::ArrMixin
|
|
320
|
+
#
|
|
321
|
+
# opts = {allow_failure: true, stdin_data: "Hello\nWorld\n"}
|
|
322
|
+
# output = %w[wc -l].run(opts:)
|
|
323
|
+
# output.to_i == 2 unless output.nil? #=> true
|
|
324
|
+
#
|
|
325
|
+
# @note This method blocks until the process completes.
|
|
326
|
+
def run(env_hash = nil, opts: {})
|
|
327
|
+
Cinnabar::Command.run(self, env_hash, opts:)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Starts a command asynchronously using this `Array<String>`.
|
|
331
|
+
#
|
|
332
|
+
# @note self [`Array<String>`]: The command and its arguments (e.g., `%w[printf hello]`).
|
|
333
|
+
#
|
|
334
|
+
# @param env_hash [#to_h] Optional environment variables.
|
|
335
|
+
# @param opts [Hash]
|
|
336
|
+
#
|
|
337
|
+
# @see Cinnabar::Command.async_run
|
|
338
|
+
#
|
|
339
|
+
# @return [Array(IO, Process::Waiter)] a pair `[stdout_io, waiter]`
|
|
340
|
+
#
|
|
341
|
+
# @example pass stdin data
|
|
342
|
+
#
|
|
343
|
+
# using Cinnabar::Command::ArrRefin
|
|
344
|
+
# # OR: include Cinnabar::Command::ArrMixin
|
|
345
|
+
#
|
|
346
|
+
# opts = {stdin_data: "Hello\nWorld\n"}
|
|
347
|
+
# io_and_waiter = %w[wc -l].async_run(opts:)
|
|
348
|
+
# output, status = Cinnabar::Command.wait_with_output *io_and_waiter
|
|
349
|
+
# output.to_i == 2 #=> true
|
|
350
|
+
def async_run(env_hash = nil, opts: {})
|
|
351
|
+
Cinnabar::Command.async_run(self, env_hash, opts:)
|
|
73
352
|
end
|
|
74
353
|
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
354
|
+
# @note self [`Array<String>`]: The command and its arguments (e.g., `%w[printf hello]`).
|
|
355
|
+
#
|
|
356
|
+
# @example pwd
|
|
357
|
+
#
|
|
358
|
+
# using Cinnabar::Command::ArrRefin
|
|
359
|
+
# # OR: include Cinnabar::Command::ArrMixin
|
|
360
|
+
#
|
|
361
|
+
# opts = {chdir: '/tmp', allow_failure: true}
|
|
362
|
+
# status = %w[pwd].run_cmd(opts:)
|
|
363
|
+
#
|
|
364
|
+
# @example pass env
|
|
365
|
+
#
|
|
366
|
+
# using Cinnabar::Command::ArrRefin
|
|
367
|
+
#
|
|
368
|
+
# env_hash = {WW: 2}
|
|
369
|
+
# status =
|
|
370
|
+
# %w[sh -c]
|
|
371
|
+
# .push('printf $WW')
|
|
372
|
+
# .run_cmd(env_hash)
|
|
373
|
+
#
|
|
374
|
+
# status == true
|
|
375
|
+
#
|
|
376
|
+
# @return [Boolean]
|
|
377
|
+
# @see Cinnabar::Command.run_cmd
|
|
378
|
+
def run_cmd(env_hash = nil, opts: {}) # rubocop:disable Style/OptionalBooleanParameter
|
|
379
|
+
Cinnabar::Command.run_cmd(self, env_hash, opts:)
|
|
78
380
|
end
|
|
79
381
|
end
|
|
80
382
|
|
|
81
383
|
# ---------------------
|
|
82
384
|
|
|
83
|
-
#
|
|
385
|
+
# Monkey patching: Array#run, Array#async_run, Array#run_cmd
|
|
386
|
+
#
|
|
387
|
+
# @example run
|
|
388
|
+
#
|
|
389
|
+
# include Cinnabar::Command::ArrMixin
|
|
390
|
+
#
|
|
391
|
+
# stdout = %w[printf World].run
|
|
392
|
+
# stdout == "World" #=> true
|
|
393
|
+
#
|
|
394
|
+
# @example async_run
|
|
395
|
+
#
|
|
396
|
+
# include Cinnabar::Command::ArrMixin
|
|
397
|
+
#
|
|
398
|
+
# fd, waiter = %w[ruby -e].push('sleep 2; puts "OK"').async_run
|
|
399
|
+
#
|
|
400
|
+
# status = waiter.value
|
|
401
|
+
# status.success? #=> true
|
|
402
|
+
#
|
|
403
|
+
# output = fd.read.chomp
|
|
404
|
+
# fd.close
|
|
405
|
+
# output == 'OK' #=> true
|
|
84
406
|
#
|
|
85
|
-
#
|
|
407
|
+
# @example async_run + wait_with_output
|
|
86
408
|
#
|
|
87
409
|
# include Cinnabar::Command::ArrMixin
|
|
410
|
+
# include Cinnabar::Command::TaskArrMixin
|
|
88
411
|
#
|
|
89
|
-
# %w[
|
|
412
|
+
# task = %w[ruby -e].push('sleep 2; puts "OK"').async_run
|
|
90
413
|
#
|
|
91
|
-
#
|
|
414
|
+
# output, status = task.wait_with_output
|
|
415
|
+
#
|
|
416
|
+
# status.success? #=> true
|
|
417
|
+
# output.chomp == 'OK' #=> true
|
|
418
|
+
#
|
|
419
|
+
# @see ArrExt
|
|
92
420
|
module ArrMixin
|
|
93
|
-
def self.included(_host) = ::Array.include
|
|
421
|
+
def self.included(_host) = ::Array.include ArrExt
|
|
94
422
|
end
|
|
95
423
|
|
|
96
|
-
# Refinements: Array#run, Array#
|
|
424
|
+
# Refinements: Array#run, Array#async_run, Array#run_cmd
|
|
97
425
|
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
# == Simple
|
|
426
|
+
# @example run
|
|
101
427
|
#
|
|
102
428
|
# using Cinnabar::Command::ArrRefin
|
|
103
429
|
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
430
|
+
# stdout =
|
|
431
|
+
# %w[ruby -e]
|
|
432
|
+
# .push('print 2')
|
|
433
|
+
# .run
|
|
434
|
+
#
|
|
435
|
+
# stdout.to_i == 2 #=> true
|
|
106
436
|
#
|
|
107
|
-
#
|
|
437
|
+
# @example run(opts:)
|
|
108
438
|
#
|
|
109
439
|
# using Cinnabar::Command::ArrRefin
|
|
440
|
+
#
|
|
441
|
+
# opts = { allow_failure: true, stdin_data: "Hello" }
|
|
442
|
+
#
|
|
443
|
+
# stdout = %w[wc -m].run(opts:)
|
|
444
|
+
#
|
|
445
|
+
# stdout.to_i == 5 #=> true
|
|
446
|
+
#
|
|
447
|
+
# @example Argvise + run_async
|
|
448
|
+
#
|
|
449
|
+
# require 'argvise'
|
|
450
|
+
# require 'cinnabar'
|
|
451
|
+
#
|
|
110
452
|
# using Argvise::HashRefin
|
|
453
|
+
# using Cinnabar::Command::ArrRefin
|
|
454
|
+
# using Cinnabar::Command::TaskArrRefin
|
|
111
455
|
#
|
|
112
|
-
#
|
|
456
|
+
# task = {
|
|
113
457
|
# cargo: (),
|
|
114
|
-
#
|
|
458
|
+
# b: (),
|
|
459
|
+
# r: true,
|
|
115
460
|
# target: "wasm32-wasip2"
|
|
116
|
-
# }
|
|
117
|
-
#
|
|
461
|
+
# }
|
|
462
|
+
# .to_argv
|
|
463
|
+
# .run_async
|
|
464
|
+
#
|
|
465
|
+
# stdout, status = task.wait_with_output
|
|
466
|
+
# status.success? #=> true
|
|
118
467
|
#
|
|
468
|
+
# @see ArrExt
|
|
119
469
|
module ArrRefin
|
|
120
470
|
refine ::Array do
|
|
121
|
-
import_methods
|
|
471
|
+
import_methods ArrExt
|
|
122
472
|
end
|
|
123
473
|
end
|
|
124
474
|
end
|
|
125
475
|
|
|
126
476
|
module Cinnabar::Command
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
Cinnabar::Command.wait_task.call(self)
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# ---------------------
|
|
134
|
-
|
|
135
|
-
# Monkey Patching: Integer#wait_task
|
|
477
|
+
# The foundation of {TaskArrRefin} and {TaskArrMixin}
|
|
478
|
+
# @see Cinnabar::Command.wait_with_output
|
|
136
479
|
#
|
|
137
|
-
#
|
|
480
|
+
# @example simple
|
|
138
481
|
#
|
|
139
|
-
#
|
|
482
|
+
# using Cinnabar::Command::ArrRefin
|
|
483
|
+
# using Cinnabar::Command::TaskArrRefin
|
|
484
|
+
# # OR: include Cinnabar::Command::TaskArrMixin
|
|
485
|
+
#
|
|
486
|
+
# task = %w[ruby -e]
|
|
487
|
+
# .push('sleep 2; puts "OK"')
|
|
488
|
+
# .async_run
|
|
140
489
|
#
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
490
|
+
# stdout, status = task.wait_with_output
|
|
491
|
+
# status.success? #=> true
|
|
492
|
+
module TaskArrExt
|
|
493
|
+
def wait_with_output
|
|
494
|
+
Cinnabar::Command.wait_with_output(*self)
|
|
495
|
+
end
|
|
146
496
|
end
|
|
147
497
|
|
|
148
|
-
# Refinement:
|
|
149
|
-
#
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
#
|
|
154
|
-
# pid = %w[sleep 3].run_in_bg
|
|
155
|
-
# p "wait 3s"
|
|
156
|
-
# pid.wait_task
|
|
157
|
-
module IntRefin
|
|
158
|
-
refine ::Integer do
|
|
159
|
-
import_methods IntegerExt
|
|
498
|
+
# Refinement: Array#wait_with_output
|
|
499
|
+
# @see TaskArrExt
|
|
500
|
+
module TaskArrRefin
|
|
501
|
+
refine ::Array do
|
|
502
|
+
import_methods TaskArrExt
|
|
160
503
|
end
|
|
161
504
|
end
|
|
505
|
+
|
|
506
|
+
# Monkey Patching: Array#wait_with_output
|
|
507
|
+
# @see TaskArrExt
|
|
508
|
+
module TaskArrMixin
|
|
509
|
+
def self.included(_host) = ::Array.include TaskArrExt
|
|
510
|
+
end
|
|
162
511
|
end
|
data/lib/cinnabar/net.rb
CHANGED
|
@@ -5,19 +5,33 @@ module Cinnabar::Downloader
|
|
|
5
5
|
|
|
6
6
|
require 'open-uri'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
DEFAULT_DL_OPTS = {
|
|
9
|
+
out_dir: 'tmp', file_name: nil,
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
# @example
|
|
9
13
|
#
|
|
10
14
|
# url = 'https://docs.ruby-lang.org/en/3.4/OpenURI.html'
|
|
11
15
|
# opts = { out_dir: "tmp", file_name: "doc.html" }
|
|
12
16
|
# DL = Cinnabar::Downloader
|
|
13
17
|
# DL.download(url, opts)
|
|
14
18
|
#
|
|
15
|
-
#
|
|
19
|
+
# @param url [String] e.g., **https://url.local**
|
|
20
|
+
#
|
|
21
|
+
# @param opts [Hash] Options for customizing the download behavior.
|
|
22
|
+
# e.g., `{out_dir: 'download', file_name: nil, headers: {'User-Agent' => "aria2/1.37.0"}}`
|
|
16
23
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
|
|
24
|
+
# @option opts [String] :out_dir
|
|
25
|
+
# Directory where the downloaded file will be saved.
|
|
26
|
+
# @option opts [String, nil] :file_name
|
|
27
|
+
# Name of the output file. If nil, it will be inferred from the URL.
|
|
28
|
+
# @option opts [Hash{String => String}] :headers
|
|
29
|
+
# Optional HTTP headers to include in the request.
|
|
30
|
+
# @return [Integer]
|
|
31
|
+
def download(url, opts = {})
|
|
32
|
+
opts = DEFAULT_DL_OPTS.merge(opts)
|
|
20
33
|
out_dir, file_name, headers = opts.values_at(:out_dir, :file_name, :headers)
|
|
34
|
+
out_dir = '.' if out_dir.nil?
|
|
21
35
|
|
|
22
36
|
headers = build_headers(headers)
|
|
23
37
|
parsed_url = Kernel.URI(url)
|
|
@@ -30,7 +44,7 @@ module Cinnabar::Downloader
|
|
|
30
44
|
.then { IO.copy_stream(_1, file_path.to_s) }
|
|
31
45
|
end
|
|
32
46
|
|
|
33
|
-
#
|
|
47
|
+
# @return [Hash]
|
|
34
48
|
def build_headers(headers)
|
|
35
49
|
base_headers = {
|
|
36
50
|
'User-Agent' => 'Mozilla/5.0 (Linux; aarch64 Wayland; rv:138.0) Gecko/20100101 Firefox/138.0',
|
|
@@ -38,7 +52,7 @@ module Cinnabar::Downloader
|
|
|
38
52
|
base_headers.merge(headers || {}).transform_keys(&:to_s)
|
|
39
53
|
end
|
|
40
54
|
|
|
41
|
-
#
|
|
55
|
+
# @return [String]
|
|
42
56
|
def determine_filename(file_name, parsed_url)
|
|
43
57
|
filename = file_name || File.basename(parsed_url.path || '')
|
|
44
58
|
case filename.strip
|
|
@@ -47,7 +61,7 @@ module Cinnabar::Downloader
|
|
|
47
61
|
end
|
|
48
62
|
end
|
|
49
63
|
|
|
50
|
-
#
|
|
64
|
+
# @return [Pathname]
|
|
51
65
|
def setup_file_path(out_dir, file_name)
|
|
52
66
|
Kernel.Pathname(out_dir)
|
|
53
67
|
.tap(&:mkpath)
|
|
@@ -56,37 +70,53 @@ module Cinnabar::Downloader
|
|
|
56
70
|
end
|
|
57
71
|
|
|
58
72
|
module Cinnabar::Downloader
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
# The foundation of {StrRefin} and {StrMixin}
|
|
74
|
+
module StrExt
|
|
75
|
+
# @see Cinnabar::Downloader.download
|
|
76
|
+
#
|
|
77
|
+
# @param opts [Hash] Options for customizing the download behavior.
|
|
78
|
+
# e.g., `{out_dir: 'download', file_name: nil, headers: {'User-Agent' => "aria2/1.37.0"}}`
|
|
79
|
+
#
|
|
80
|
+
# @option opts [String] :out_dir
|
|
81
|
+
# @option opts [String, nil] :file_name
|
|
82
|
+
# @option opts [Hash{String => String}] :headers
|
|
83
|
+
# @return [Integer]
|
|
84
|
+
def download(opts = {})
|
|
85
|
+
Cinnabar::Downloader.download(self, opts)
|
|
62
86
|
end
|
|
63
87
|
end
|
|
64
88
|
|
|
65
89
|
# -------------
|
|
66
90
|
|
|
67
|
-
#
|
|
91
|
+
# @example
|
|
68
92
|
#
|
|
69
93
|
# include Cinnabar::Downloader::StrMixin
|
|
70
94
|
#
|
|
71
|
-
# url = 'https://docs.ruby-lang.org'
|
|
95
|
+
# url = 'https://docs.ruby-lang.org/en/master'
|
|
72
96
|
#
|
|
73
97
|
# url.download
|
|
74
|
-
# # OR: url.download(out_dir: "tmp", file_name: "custom.html")
|
|
98
|
+
# # OR: url.download({out_dir: "tmp", file_name: "custom.html"})
|
|
99
|
+
#
|
|
100
|
+
# @see Cinnabar::Downloader.download
|
|
101
|
+
# @see StrExt
|
|
75
102
|
module StrMixin
|
|
76
|
-
def self.included(_host) = ::String.include
|
|
103
|
+
def self.included(_host) = ::String.include StrExt
|
|
77
104
|
end
|
|
78
105
|
|
|
79
|
-
#
|
|
106
|
+
# @example
|
|
80
107
|
#
|
|
81
108
|
# using Cinnabar::Downloader::StrRefin
|
|
82
109
|
#
|
|
83
|
-
# url = 'https://docs.ruby-lang.org/en/master
|
|
110
|
+
# url = 'https://docs.ruby-lang.org/en/master'
|
|
84
111
|
#
|
|
85
112
|
# url.download
|
|
86
|
-
# # OR: url.download(out_dir: "/tmp", file_name: "index.html")
|
|
113
|
+
# # OR: url.download({out_dir: "/tmp", file_name: "index.html"})
|
|
114
|
+
#
|
|
115
|
+
# @see Cinnabar::Downloader.download
|
|
116
|
+
# @see StrExt
|
|
87
117
|
module StrRefin
|
|
88
118
|
refine ::String do
|
|
89
|
-
import_methods
|
|
119
|
+
import_methods StrExt
|
|
90
120
|
end
|
|
91
121
|
end
|
|
92
122
|
end
|
data/lib/cinnabar/pipe.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Cinnabar::FnPipe
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
module Cinnabar::FnPipe
|
|
20
|
-
module
|
|
20
|
+
module Ext
|
|
21
21
|
def ▷(other) # rubocop:disable Naming/MethodName
|
|
22
22
|
Cinnabar::FnPipe.▷(self, other)
|
|
23
23
|
end
|
|
@@ -29,7 +29,7 @@ module Cinnabar::FnPipe
|
|
|
29
29
|
#
|
|
30
30
|
# Monkey Patching: Object#▷
|
|
31
31
|
#
|
|
32
|
-
#
|
|
32
|
+
# @example
|
|
33
33
|
#
|
|
34
34
|
# include Cinnabar::FnPipe::Mixin
|
|
35
35
|
#
|
|
@@ -40,14 +40,14 @@ module Cinnabar::FnPipe
|
|
|
40
40
|
# 2.▷ :puts
|
|
41
41
|
# #=> 2
|
|
42
42
|
module Mixin
|
|
43
|
-
def self.included(_host) = ::Object.include
|
|
43
|
+
def self.included(_host) = ::Object.include Ext
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Function Pipe
|
|
47
47
|
#
|
|
48
48
|
# Refinement: Object#▷
|
|
49
49
|
#
|
|
50
|
-
#
|
|
50
|
+
# @example
|
|
51
51
|
#
|
|
52
52
|
# using Cinnabar::FnPipe::Refin
|
|
53
53
|
#
|
|
@@ -63,7 +63,7 @@ module Cinnabar::FnPipe
|
|
|
63
63
|
#
|
|
64
64
|
module Refin
|
|
65
65
|
refine ::Object do
|
|
66
|
-
import_methods
|
|
66
|
+
import_methods Ext
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
data/lib/cinnabar/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cinnabar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 2moe
|
|
@@ -29,6 +29,7 @@ extensions: []
|
|
|
29
29
|
extra_rdoc_files: []
|
|
30
30
|
files:
|
|
31
31
|
- ".rubocop.yml"
|
|
32
|
+
- ".yardopts"
|
|
32
33
|
- License
|
|
33
34
|
- docs/Readme-zh.md
|
|
34
35
|
- docs/Readme.md
|