squared 0.0.9 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -6
- data/README.ruby.md +145 -47
- data/lib/squared/app.rb +10 -0
- data/lib/squared/common/base.rb +17 -15
- data/lib/squared/common/class.rb +20 -3
- data/lib/squared/common/format.rb +104 -46
- data/lib/squared/common/prompt.rb +38 -0
- data/lib/squared/common/shell.rb +16 -7
- data/lib/squared/common/system.rb +8 -38
- data/lib/squared/common/task.rb +3 -2
- data/lib/squared/common/utils.rb +69 -0
- data/lib/squared/common.rb +2 -0
- data/lib/squared/config.rb +31 -26
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +285 -137
- data/lib/squared/workspace/project/base.rb +459 -178
- data/lib/squared/workspace/project/git.rb +95 -114
- data/lib/squared/workspace/project/node.rb +306 -160
- data/lib/squared/workspace/project/python.rb +45 -19
- data/lib/squared/workspace/project/ruby.rb +156 -127
- data/lib/squared/workspace/project.rb +0 -3
- data/lib/squared/workspace/repo.rb +100 -93
- data/lib/squared/workspace/series.rb +82 -52
- data/lib/squared/workspace.rb +5 -4
- data/lib/squared.rb +1 -11
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d000551aa00185dae3ea1bc7e3a61c64af96dce7d717339483d8cb572ca12d43
|
4
|
+
data.tar.gz: 229f06052512ee06d16e43688602753a867025e8cf39b6b1c6dfafa69805fdbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5efe60578369457075929b95ba8ea628d39d7324d40f84c397e07606c8c5fe48f07f797b940d7ca34a80321bb3f1435494c36afcd95432cbe8924e4353099f56
|
7
|
+
data.tar.gz: daf832c4fbcf0e80459317be7b91781cfea9b653b3368cdbc090101d67ca59aff8b38940c2803100e322ca14bd25f45ccf37ef27bb348312b724c45babeb3393
|
data/README.md
CHANGED
@@ -16,9 +16,6 @@
|
|
16
16
|
* [E-mc](https://github.com/anpham6/e-mc#readme)
|
17
17
|
* [Pi-r](https://github.com/anpham6/pi-r#readme)
|
18
18
|
|
19
|
-
> [!NOTE]
|
20
|
-
> `squared-express` originally was intended to be used as a HTTP/1.1 insecure development server for Android projects. `E-mc` was created a few years later to produce static outputs for any kind of development accessible through a simple REST API. Gradually any new development that calls the REST API will use [NGINX Unit](https://unit.nginx.org) as the *production* application runtime to efficiently serve static assets. Middleware and application code can be conducted in the same manner with Express and other programming frameworks (e.g. Ruby). Maintanence updates with subsequent `E-mc` releases supporting legacy development environments will be tested only against `Express 4.19` although most existing applications can be upgraded without any problems. Upgrading to `Express 5` is not recommended as it offers no new noticeable features (e.g. HTTP/2) and possibly compatibility issues with template libraries.
|
21
|
-
|
22
19
|
## Installation
|
23
20
|
|
24
21
|
* NodeJS 16 LTS
|
@@ -113,14 +110,13 @@ cd workspaces # REPO_ROOT
|
|
113
110
|
|
114
111
|
wget https://raw.githubusercontent.com/anpham6/squared/master/Rakefile
|
115
112
|
|
116
|
-
# REPO_DOCS=1 (venv)
|
117
113
|
rake -T # List tasks
|
118
114
|
|
119
115
|
# REPO_BUILD={dev,prod}
|
120
116
|
# PIPE_FAIL=1
|
121
117
|
rake repo:init # nightly
|
122
|
-
#
|
123
|
-
rake repo:init[
|
118
|
+
rake repo:init[latest] # squared
|
119
|
+
rake repo:init[0.11.x] # e-mc
|
124
120
|
# OR
|
125
121
|
REPO_ROOT=/tmp/123 NODE_INSTALL=pnpm repo:init
|
126
122
|
```
|
data/README.ruby.md
CHANGED
@@ -4,9 +4,17 @@
|
|
4
4
|
* [manifest](https://github.com/anpham6/squared-repo)
|
5
5
|
* [docs](https://squared.readthedocs.io)
|
6
6
|
|
7
|
-
##
|
7
|
+
## Version Compatibility
|
8
8
|
|
9
|
-
|
9
|
+
| Date | squared | Ruby 2 | Ruby 3 |
|
10
|
+
| :------: | ------: | -----: | -----: |
|
11
|
+
| 12-07-24 | 0.1.0 | 2.4.0 | 3.0.0 |
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
```sh
|
16
|
+
gem install squared
|
17
|
+
```
|
10
18
|
|
11
19
|
### Optional
|
12
20
|
|
@@ -20,42 +28,67 @@ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.bin/repo
|
|
20
28
|
chmod a+rx ~/.bin/repo
|
21
29
|
```
|
22
30
|
|
23
|
-
## Installation
|
24
|
-
|
25
|
-
```sh
|
26
|
-
gem install squared
|
27
|
-
```
|
28
|
-
|
29
31
|
## Example - Rakefile
|
30
32
|
|
31
33
|
Projects from any accessible folder can be added either relative to `REPO_ROOT` or absolutely. The same Rakefile can also manage other similarly cloned repositories remotely by setting the `REPO_ROOT` environment variable to the location. Missing projects will simply be excluded from the task runner.
|
32
34
|
|
33
35
|
```ruby
|
34
36
|
require "squared"
|
35
|
-
|
37
|
+
|
38
|
+
require "squared/workspace"
|
39
|
+
require "squared/workspace/repo" # Optional
|
40
|
+
require "squared/workspace/project/node" #
|
41
|
+
require "squared/workspace/project/python" #
|
42
|
+
require "squared/workspace/project/ruby" #
|
43
|
+
# OR
|
44
|
+
require "squared/app" # All workspace related modules
|
36
45
|
|
37
46
|
# NODE_ENV = production
|
38
47
|
# REPO_ROOT = /workspaces
|
39
|
-
# REPO_HOME = /workspaces/squared
|
48
|
+
# REPO_HOME = /workspaces/squared (Dir.pwd)
|
40
49
|
# rake = /workspaces/squared/Rakefile
|
50
|
+
# pathname = /workspaces/pathname
|
51
|
+
# optparse = /workspaces/optparse
|
52
|
+
# log = /workspaces/logger
|
53
|
+
# emc = /workspaces/e-mc
|
54
|
+
# pir = /workspaces/pi-r
|
55
|
+
# squared = /workspaces/squared
|
56
|
+
# cli = /workspaces/squared/publish/sqd-cli
|
57
|
+
# sqd-serve = /workspaces/squared/publish/sqd-serve
|
58
|
+
# sqd = /workspaces/squared/sqd
|
41
59
|
|
42
60
|
Workspace::Application
|
43
|
-
.new(main: "squared") # Dir.pwd? (main? is implicitly basename)
|
44
|
-
.banner("group", "project", styles: %i[yellow black], border: "bold") # name | project | path | ref | group?
|
45
|
-
.repo("https://github.com/anpham6/squared-repo", "nightly",
|
61
|
+
.new(Dir.pwd, main: "squared") # Dir.pwd? (main? is implicitly basename)
|
62
|
+
.banner("group", "project", styles: %i[yellow black], border: "bold") # name | project | path | ref | group? | parent? | version?
|
63
|
+
.repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "build:prod"], ref: :node) # Repo (optional)
|
46
64
|
.run("rake install", ref: :ruby)
|
47
65
|
.depend(false, group: "default")
|
48
66
|
.clean("rake clean", group: "default")
|
49
67
|
.clean(["build/"], group: "app")
|
50
|
-
.
|
68
|
+
.log({ file: "tmp/%Y-%m-%d.log", level: "debug" }, group: "app")
|
69
|
+
.add("pathname", run: "rake compile", copy: "rake install", test: "rake test", group: "default", env: { # Ruby (with C extensions)
|
70
|
+
"CFLAGS" => "-fPIC -O1"
|
71
|
+
})
|
51
72
|
.add("optparse", doc: "rake rdoc", group: "default") # Uses bundler/gem_tasks (without C extensions)
|
52
|
-
.add("logger", copy: { from: "lib", glob: "**/*.rb",
|
53
|
-
.add("
|
54
|
-
.add("
|
55
|
-
.add("squared",
|
56
|
-
|
73
|
+
.add("logger", copy: { from: "lib", glob: "**/*.rb", into: "~/.rvm/gems/ruby-3.3.5/gems/logger-1.6.1" }, clean: ["tmp/"]) # autodetect: true
|
74
|
+
.add("e-mc", "emc", copy: { from: "publish", scope: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node) # Node
|
75
|
+
.add("pi-r", "pir", copy: { from: "publish", scope: "@pi-r" }, clean: ["publish/**/*.js", "tmp/"]) # Trailing slash required for directories
|
76
|
+
.add("squared", script: ["build:stage1", "build:stage2"], group: "app") do # Copy target (main)
|
77
|
+
add("publish/sqd-cli", "cli", exclude: [:git]) # rake cli:build
|
78
|
+
add("publish/sqd-serve") # rake sqd-serve:build
|
79
|
+
add("publish/sqd-admin", group: "sqd", exclude: [:base])
|
80
|
+
# OR
|
81
|
+
with(exclude: [:base]) { add("publish/*", "packages") } # rake packages:sqd-serve:build
|
82
|
+
# OR
|
83
|
+
add(["publish/sqd-cli", "publish/sqd-serve", "publish/sqd-admin"], true, exclude: [:base]) # rake squared:sqd-serve:build
|
84
|
+
end
|
85
|
+
.add("squared/sqd", exclude: [:git]) do
|
86
|
+
variable_set :script, "build:sqd" # Override detection
|
87
|
+
variable_set :depend, false
|
88
|
+
variable_set :clean, ["build/sqd/"]
|
89
|
+
end
|
57
90
|
.style("banner", 255.255) # 256 colors (fg | fg.bg | -0.bg)
|
58
|
-
.build(default: "status", parallel: ["pull", "fetch", "rebase", "copy", "clean"]) do |workspace|
|
91
|
+
.build(default: "status", parallel: ["pull", "fetch", "rebase", "copy", "clean", "outdated:ruby"]) do |workspace|
|
59
92
|
workspace
|
60
93
|
.enable_aixterm
|
61
94
|
.style({
|
@@ -63,45 +96,40 @@ Workspace::Application
|
|
63
96
|
border: "bright_white"
|
64
97
|
})
|
65
98
|
end
|
66
|
-
# pathname = /workspaces/pathname
|
67
|
-
# optparse = /workspaces/optparse
|
68
|
-
# log = /workspaces/logger
|
69
|
-
# emc = /workspaces/e-mc
|
70
|
-
# pir = /workspaces/pi-r
|
71
|
-
# squared = /workspaces/squared
|
72
99
|
|
73
|
-
Workspace::Application
|
74
|
-
.new(ENV["SQUARED_DIR"], prefix: "rb", common: false) # Local styles
|
75
|
-
.group("default", "ruby/", run: "rake build", copy: "rake install", clean: "rake clean", ref: :ruby, override: {
|
76
|
-
pathname: {
|
77
|
-
run: "rake compile" # rake rb:pathname:build
|
78
|
-
}
|
79
|
-
})
|
80
|
-
.with(:python) do # ref=Symbol | group=String
|
81
|
-
banner("path", command: false, styles: "yellow") #
|
82
|
-
doc("make html") # rake rb:doc:python
|
83
|
-
run(false) # rake rb:build:python (disabled)
|
84
|
-
exclude(%i[base git]) # Project::Git.ref (superclass)
|
85
|
-
add("android", "android-docs") # rake rb:android:doc
|
86
|
-
add("chrome", "chrome-docs") # rake rb:chrome:doc
|
87
|
-
end #
|
88
|
-
.style("inline", "bold")
|
89
|
-
.build
|
90
100
|
# default = /workspaces/ruby/*
|
91
101
|
# pathname = /workspaces/ruby/pathname
|
92
102
|
# optparse = /workspaces/ruby/optparse
|
93
103
|
# logger = /workspaces/ruby/logger
|
94
104
|
# android = /workspaces/android-docs
|
95
105
|
# chrome = /workspaces/chrome-docs
|
106
|
+
|
107
|
+
Workspace::Application
|
108
|
+
.new(ENV["SQUARED_HOME"], prefix: "rb", common: false) # Local styles
|
109
|
+
.group("ruby", "default", run: "rake build", copy: "rake install", clean: "rake clean", ref: :ruby, override: {
|
110
|
+
pathname: {
|
111
|
+
run: "rake compile" # rake rb:pathname:build
|
112
|
+
}
|
113
|
+
})
|
114
|
+
.with(:python) do # ref=Symbol | group=String
|
115
|
+
banner([:name, ': ', :version], "path") # chrome-docs: 0.1.0 | /workspaces/chrome-docs
|
116
|
+
doc("make html") # rake rb:doc:python
|
117
|
+
run(false) # rake rb:build:python (disabled)
|
118
|
+
exclude(%i[base git]) # Project::Git.ref (superclass)
|
119
|
+
add("android-docs", "android") # rake rb:android:doc
|
120
|
+
add("chrome-docs", "chrome") # rake rb:chrome:doc
|
121
|
+
end #
|
122
|
+
.style("inline", "bold")
|
123
|
+
.build
|
96
124
|
```
|
97
125
|
|
98
|
-
**NOTE**: The use of "**ref**" (class name) is only necessary when
|
126
|
+
**NOTE**: The use of "**ref**" (class name) is only necessary when initializing an empty directory (e.g. *rake repo:init*).
|
99
127
|
|
100
128
|
## Usage
|
101
129
|
|
102
130
|
```sh
|
103
|
-
rake -T
|
104
|
-
rake
|
131
|
+
rake -T # List tasks
|
132
|
+
rake # rake status (usually "build")
|
105
133
|
|
106
134
|
# GIT_OPTIONS=rebase
|
107
135
|
rake pull # All except "default" + "app"
|
@@ -123,7 +151,17 @@ rake clean:app # none + skip + ["build/"]
|
|
123
151
|
rake clean:node # none + ["publish/**/*.js", "tmp/"] + ["build/"]
|
124
152
|
```
|
125
153
|
|
126
|
-
|
154
|
+
```sh
|
155
|
+
rake build:app # squared + cli + sqd-serve
|
156
|
+
rake squared:build:workspace # cli + sqd-serve
|
157
|
+
rake pull:sqd # sqd-admin
|
158
|
+
rake squared:pull:workspace # sqd-serve + sqd-admin
|
159
|
+
rake squared:outdated:workspace # cli + sqd-serve + sqd-admin
|
160
|
+
```
|
161
|
+
|
162
|
+
## Methods
|
163
|
+
|
164
|
+
Task:
|
127
165
|
|
128
166
|
* run
|
129
167
|
* depend
|
@@ -131,6 +169,11 @@ rake clean:node # none + ["publish/**/*.js", "tmp/"] + ["build/"]
|
|
131
169
|
* doc
|
132
170
|
* clean
|
133
171
|
|
172
|
+
Non-task:
|
173
|
+
|
174
|
+
* log
|
175
|
+
* exclude
|
176
|
+
|
134
177
|
## Styles
|
135
178
|
|
136
179
|
* banner
|
@@ -139,6 +182,61 @@ rake clean:node # none + ["publish/**/*.js", "tmp/"] + ["build/"]
|
|
139
182
|
* active
|
140
183
|
* inline
|
141
184
|
* major
|
185
|
+
* red
|
186
|
+
* yellow
|
187
|
+
* green
|
188
|
+
|
189
|
+
## Environment
|
190
|
+
|
191
|
+
### Build
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
# :env :run :opts
|
195
|
+
# LD_LIBRARY_PATH="path/to/lib" CFLAGS="-Wall" gcc a.c -o a.o -c
|
196
|
+
BUILD_${NAME} # gcc a.c -o a.o
|
197
|
+
BUILD_OPTS_${NAME} # -c
|
198
|
+
BUILD_ENV_${NAME} # {"LD_LIBRARY_PATH":"path/to/lib","CFLAGS":"-Wall"} (hash/json)
|
199
|
+
|
200
|
+
# :env :opts :script
|
201
|
+
# NODE_ENV="production" NO_COLOR="1" npm run --loglevel=error --workspaces=false build:dev
|
202
|
+
BUILD_${NAME} # build:dev
|
203
|
+
BUILD_OPTS_${NAME} # --loglevel=error --workspaces=false
|
204
|
+
BUILD_ENV_${NAME} # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
|
205
|
+
BUILD_DEV_${NAME} # pattern,0,1 (:dev)
|
206
|
+
BUILD_PROD_${NAME} # pattern,0,1 (:prod)
|
207
|
+
|
208
|
+
BUILD_${NAME}=0 # skip project
|
209
|
+
```
|
210
|
+
|
211
|
+
These options also support the project specific suffix `${NAME}`. (e.g. LOG_FILE_SQUARED)
|
212
|
+
|
213
|
+
### Logger
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
LOG_FILE # %Y-%m-%d.log
|
217
|
+
# OR
|
218
|
+
LOG_AUTO # year,y,month,m,day,d,1
|
219
|
+
# Optional
|
220
|
+
LOG_DIR # exist?
|
221
|
+
LOG_LEVEL # See gem "logger"
|
222
|
+
LOG_COLUMNS # terminal width (default: 80)
|
223
|
+
```
|
224
|
+
|
225
|
+
### Repo
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
REPO_ROOT # parent dir
|
229
|
+
REPO_HOME # project dir (main)
|
230
|
+
REPO_BUILD # run,script
|
231
|
+
REPO_GROUP # string
|
232
|
+
REPO_REF # e.g. ruby,node
|
233
|
+
REPO_DEV # pattern,0,1
|
234
|
+
REPO_PROD # pattern,0,1
|
235
|
+
REPO_WARN # 0,1
|
236
|
+
REPO_SYNC # 0,1
|
237
|
+
REPO_MANIFEST # e.g. latest,nightly,prod
|
238
|
+
REPO_TIMEOUT # confirm dialog (seconds)
|
239
|
+
```
|
142
240
|
|
143
241
|
## LICENSE
|
144
242
|
|
data/lib/squared/app.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'workspace'
|
4
|
+
require_relative 'workspace/repo'
|
5
|
+
require_relative 'workspace/project/node'
|
6
|
+
require_relative 'workspace/project/python'
|
7
|
+
require_relative 'workspace/project/ruby'
|
8
|
+
require_relative 'config'
|
9
|
+
|
10
|
+
Common = Squared::Common
|
data/lib/squared/common/base.rb
CHANGED
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
module Squared
|
4
4
|
module Common
|
5
|
+
ARG = {
|
6
|
+
PIPE: 1,
|
7
|
+
OUT: nil,
|
8
|
+
FAIL: false,
|
9
|
+
COMMON: true,
|
10
|
+
BANNER: true,
|
11
|
+
SPACE: ' => ',
|
12
|
+
COLOR: ENV.fetch('NO_COLOR', '').empty?
|
13
|
+
}
|
5
14
|
VAR = {
|
6
15
|
project: {},
|
7
16
|
colors: {
|
@@ -65,6 +74,7 @@ module Squared
|
|
65
74
|
end
|
66
75
|
|
67
76
|
def __freeze__
|
77
|
+
ARG.freeze
|
68
78
|
VAR.each_value(&:freeze)
|
69
79
|
VAR[:theme].each_value(&:freeze)
|
70
80
|
VAR.freeze
|
@@ -72,25 +82,17 @@ module Squared
|
|
72
82
|
|
73
83
|
module_function
|
74
84
|
|
75
|
-
def env(key, default = nil, equals: nil, ignore: nil, **)
|
76
|
-
ret = ENV.fetch(key, '')
|
77
|
-
return ret == equals.to_s unless equals.nil?
|
78
|
-
|
79
|
-
ret.empty? || (ignore && as_a(ignore).any? { |val| ret == val.to_s }) ? default : ret
|
80
|
-
end
|
81
|
-
|
82
|
-
def message(*args, hint: nil)
|
83
|
-
args.reject(&:empty?).join(' => ') + (hint ? " (#{hint})" : '')
|
84
|
-
end
|
85
|
-
|
86
85
|
def as_a(obj, meth = nil, flat: nil, compact: false)
|
87
86
|
return [] if obj.nil?
|
88
87
|
|
89
|
-
|
90
|
-
obj =
|
91
|
-
|
92
|
-
|
88
|
+
unless obj.is_a?(::Array)
|
89
|
+
obj = if !obj.is_a?(::Hash) && obj.respond_to?(:to_a) && (val = obj.to_a).is_a?(::Array)
|
90
|
+
val
|
91
|
+
else
|
92
|
+
[obj]
|
93
|
+
end
|
93
94
|
end
|
95
|
+
obj = obj.flatten(flat.is_a?(::Numeric) ? flat : nil) if flat
|
94
96
|
obj = obj.map(&meth) if meth
|
95
97
|
compact ? obj.compact : obj
|
96
98
|
end
|
data/lib/squared/common/class.rb
CHANGED
@@ -1,14 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'set'
|
4
|
+
require 'forwardable'
|
4
5
|
|
5
6
|
module Squared
|
6
7
|
module Common
|
7
|
-
class
|
8
|
-
|
9
|
-
|
8
|
+
class SymSet
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegators :@data, :each, :each_with_index, :entries, :to_a, :include?
|
12
|
+
|
13
|
+
def initialize(data = [])
|
14
|
+
@data = Set.new(data)
|
10
15
|
end
|
11
16
|
|
17
|
+
def add(val)
|
18
|
+
@data.add(val.to_sym)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
@data.to_s.sub('Set', SymSet.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
alias inspect to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
class JoinSet < ::Set
|
12
29
|
def initialize(data = [], delim: ' ')
|
13
30
|
super(data.compact)
|
14
31
|
@delim = delim
|
@@ -40,7 +40,7 @@ module Squared
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def sub_style(val, *args, styles: nil, pat: nil, index: 1)
|
43
|
-
return val unless
|
43
|
+
return val unless ARG[:COLOR]
|
44
44
|
|
45
45
|
if pat && index != 0
|
46
46
|
return val unless (data = pat.match(val))
|
@@ -61,7 +61,7 @@ module Squared
|
|
61
61
|
else
|
62
62
|
s = ret
|
63
63
|
end
|
64
|
-
if type.is_a?(Numeric)
|
64
|
+
if type.is_a?(::Numeric)
|
65
65
|
f, b = type.to_s.split('.')
|
66
66
|
s = wrap.(s, ['38', '5', f]) if f[0] != '-' && f.to_i <= 255
|
67
67
|
if b
|
@@ -77,7 +77,7 @@ module Squared
|
|
77
77
|
code << c
|
78
78
|
end
|
79
79
|
else
|
80
|
-
next unless (n = TEXT_STYLE.index
|
80
|
+
next unless (n = TEXT_STYLE.index(t))
|
81
81
|
|
82
82
|
s = "\x1B[#{n + 1}m#{s}\x1B[#{n == 0 ? 22 : n + 21}m"
|
83
83
|
end
|
@@ -106,11 +106,11 @@ module Squared
|
|
106
106
|
out
|
107
107
|
end
|
108
108
|
|
109
|
-
def check_style(
|
109
|
+
def check_style(args, empty: true)
|
110
110
|
ret = []
|
111
111
|
colors = __get__(:colors)
|
112
112
|
as_a(args, flat: true, compact: true).each do |val|
|
113
|
-
if !val.is_a?(Numeric)
|
113
|
+
if !val.is_a?(::Numeric)
|
114
114
|
val = val.to_sym
|
115
115
|
ret << val if colors.key?(val) || TEXT_STYLE.include?(val)
|
116
116
|
elsif val >= 0 && val <= 256
|
@@ -123,39 +123,41 @@ module Squared
|
|
123
123
|
ret if empty || !ret.empty?
|
124
124
|
end
|
125
125
|
|
126
|
-
def apply_style(data, key,
|
127
|
-
data = __get__(:theme)[data]
|
128
|
-
return unless data
|
126
|
+
def apply_style(data, key, args, empty: true)
|
127
|
+
return if data.is_a?(::Symbol) && (data = __get__(:theme)[data]).nil?
|
129
128
|
|
130
|
-
set = ->(k, v) { data[k
|
131
|
-
if key.is_a?(Hash)
|
132
|
-
key.each { |k, v| set.(k, v || args
|
129
|
+
set = ->(k, v) { data[k] = check_style(v, empty: empty) }
|
130
|
+
if key.is_a?(::Hash)
|
131
|
+
key.each { |k, v| set.(k, v || args) }
|
133
132
|
else
|
134
|
-
set.(key, args
|
133
|
+
set.(key.to_sym, args)
|
135
134
|
end
|
136
135
|
end
|
137
136
|
|
138
|
-
def
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
137
|
+
def log_sym(level)
|
138
|
+
if level.is_a?(::Numeric)
|
139
|
+
case level
|
140
|
+
when Logger::DEBUG
|
141
|
+
:debug
|
142
|
+
when Logger::INFO
|
143
|
+
:info
|
144
|
+
when Logger::WARN
|
145
|
+
:warn
|
146
|
+
when Logger::ERROR
|
147
|
+
:error
|
148
|
+
when Logger::FATAL
|
149
|
+
:fatal
|
150
|
+
else
|
151
|
+
:unknown
|
152
|
+
end
|
153
|
+
else
|
154
|
+
level.to_s.downcase.to_sym
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def log_title(level, color: ARG[:COLOR])
|
157
159
|
theme = __get__(:theme)[:logger]
|
158
|
-
styles = theme[level] || theme[level = :unknown]
|
160
|
+
styles = theme[level = log_sym(level)] || theme[level = :unknown]
|
159
161
|
case (ret = +level.to_s.upcase)
|
160
162
|
when 'WARN', 'ERROR', 'FATAL'
|
161
163
|
ret += '!'
|
@@ -163,26 +165,56 @@ module Squared
|
|
163
165
|
color ? sub_style(ret, *styles) : ret
|
164
166
|
end
|
165
167
|
|
166
|
-
def log_message(level, *args, subject: nil, hint: nil, color:
|
167
|
-
|
168
|
-
|
169
|
-
|
168
|
+
def log_message(level, *args, subject: nil, hint: nil, color: ARG[:COLOR])
|
169
|
+
args = args.map(&:to_s)
|
170
|
+
if args.size > 1
|
171
|
+
title = log_title(level, color: false)
|
172
|
+
sub = { pat: /^(#{title})(.+)$/, styles: __get__(:theme)[:logger][log_sym(level)] } if color
|
173
|
+
emphasize(args, title: title + (subject ? " #{subject}" : ''), sub: sub)
|
174
|
+
else
|
175
|
+
msg = [log_title(level, color: color)]
|
176
|
+
if subject
|
177
|
+
msg << (color ? sub_style(subject, :underline) : subject)
|
178
|
+
else
|
179
|
+
msg += args
|
180
|
+
args.clear
|
181
|
+
end
|
182
|
+
message(msg.join(' '), *args, hint: hint)
|
183
|
+
end
|
170
184
|
end
|
171
185
|
|
172
|
-
def
|
173
|
-
|
186
|
+
def puts_oe(*args, pipe: 1)
|
187
|
+
if pipe.is_a?(::Pathname)
|
188
|
+
begin
|
189
|
+
File.open(pipe, 'a') do |f|
|
190
|
+
br = File::SEPARATOR == '\\' ? "\r\n" : "\n"
|
191
|
+
args.flatten.each { |val| f.write("#{strip_style(val.chomp)}#{br}") }
|
192
|
+
end
|
193
|
+
return
|
194
|
+
rescue StandardError
|
195
|
+
pipe = 2
|
196
|
+
end
|
197
|
+
end
|
198
|
+
(pipe == 2 ? $stderr : $stdout).puts(*args)
|
174
199
|
end
|
175
200
|
|
176
201
|
module_function
|
177
202
|
|
178
|
-
def
|
203
|
+
def message(*args, hint: nil, empty: false)
|
204
|
+
(empty ? args.reject { |val| val.nil? || val.empty? } : args).join(ARG[:SPACE]) + (hint ? " (#{hint})" : '')
|
205
|
+
end
|
206
|
+
|
207
|
+
def emphasize(val, title: nil, footer: nil, cols: nil, sub: nil, border: nil, pipe: nil)
|
179
208
|
n = 0
|
180
|
-
|
181
|
-
|
182
|
-
n =
|
209
|
+
set = lambda do |l|
|
210
|
+
ret = as_a(l, :to_s)
|
211
|
+
n = ret.max_by(&:size).size
|
212
|
+
ret
|
183
213
|
end
|
214
|
+
title = set.(title) if title
|
215
|
+
footer = set.(footer) if footer
|
184
216
|
if val.is_a?(::Array)
|
185
|
-
lines = val
|
217
|
+
lines = val.map(&:to_s)
|
186
218
|
else
|
187
219
|
lines = val.to_s.lines.map(&:chomp)
|
188
220
|
lines[0] = "#{val.class}: #{lines.first}" if (err = val.is_a?(::StandardError))
|
@@ -194,24 +226,50 @@ module Squared
|
|
194
226
|
end
|
195
227
|
out = []
|
196
228
|
bord = '-' * (n + 4)
|
229
|
+
bord = sub_style(bord, styles: border) if border
|
197
230
|
sub = [sub] if sub && !sub.is_a?(::Array)
|
198
231
|
pr = lambda do |line|
|
199
232
|
s = line.ljust(n)
|
200
233
|
sub&.each { |h| s = sub_style(s, **h) }
|
201
|
-
"| #{s} |"
|
234
|
+
s = "| #{s} |"
|
235
|
+
if border
|
236
|
+
s = sub_style(s, pat: /^(\|)(.+)$/m, styles: border)
|
237
|
+
s = sub_style(s, pat: /^(.+)(\|)$/m, styles: border, index: 2)
|
238
|
+
end
|
239
|
+
s
|
202
240
|
end
|
203
241
|
out << bord
|
204
|
-
|
242
|
+
if title
|
243
|
+
out += title.map { |t| pr.(t) }
|
244
|
+
out << bord
|
245
|
+
end
|
205
246
|
lines.each { |line| out << pr.(line) }
|
206
247
|
out << bord
|
248
|
+
out += footer if footer
|
207
249
|
if block_given?
|
208
250
|
yield out
|
209
|
-
elsif pipe
|
251
|
+
elsif pipe
|
252
|
+
case pipe
|
253
|
+
when 0
|
254
|
+
pipe = $stdin
|
255
|
+
when 2
|
256
|
+
pipe = $stderr
|
257
|
+
else
|
258
|
+
pipe = $stdout unless pipe.respond_to?(:puts)
|
259
|
+
end
|
210
260
|
pipe.puts out
|
211
261
|
else
|
212
262
|
err ? warn(out) : puts(out)
|
213
263
|
end
|
214
264
|
end
|
265
|
+
|
266
|
+
def strip_style(val)
|
267
|
+
val.gsub(/\x1B\[(\d+;?)+m/, '')
|
268
|
+
end
|
269
|
+
|
270
|
+
def raise_error(*args, hint: nil, kind: ArgumentError)
|
271
|
+
raise kind, message(*args, hint: hint, empty: true)
|
272
|
+
end
|
215
273
|
end
|
216
274
|
end
|
217
275
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'readline'
|
4
|
+
|
5
|
+
module Squared
|
6
|
+
module Common
|
7
|
+
module Prompt
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 5, timeout: 15)
|
11
|
+
require 'timeout'
|
12
|
+
agree = /^#{agree}$/i if agree.is_a?(::String)
|
13
|
+
cancel = /^#{cancel}$/i if cancel.is_a?(::String)
|
14
|
+
Timeout.timeout(timeout) do
|
15
|
+
begin
|
16
|
+
while (ch = Readline.readline(msg, true))
|
17
|
+
ch = ch.chomp
|
18
|
+
ch = default if ch.empty?
|
19
|
+
case ch
|
20
|
+
when agree
|
21
|
+
return true
|
22
|
+
when cancel
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
attempts -= 1
|
26
|
+
exit 1 unless attempts >= 0
|
27
|
+
end
|
28
|
+
rescue Interrupt
|
29
|
+
puts
|
30
|
+
exit 0
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|