squared 0.0.9 → 0.0.11
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/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
|