squared 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17d98e04832d29621058a1dafa1f9440f0734849aa2b68aecffed2b0c50c0e26
4
- data.tar.gz: 31391a2561c19025ddd25a20c980f10cf605de2593a24e793eee87ee3d1ccc67
3
+ metadata.gz: a9e8d3d41e090465c85e45088c984711f2a26f6ab2f38d56ed8b58658735f678
4
+ data.tar.gz: 2ff93124747a950b6ac94424052268e4ab6b107e934a2fd16478ca03d74a5409
5
5
  SHA512:
6
- metadata.gz: 246be3dde753890551351e232e8f804b7cf3e32ea610113983ad465dd863365f800142d18c91e732df7152e3cfdc6c8c727c980d85b1079dc7acc39ecde6a0aa
7
- data.tar.gz: 3a9f07b19f697678bacf6d15914df6c33f74e2aa9a0d9b5f10b033b10c29e3460b34e2da3890ae62ed655858d499fb34dae49098096c73b9a2279bb8f9745bd1
6
+ metadata.gz: 60b75a0d2d6c361ccf9341496b0b4e9e3a0599030ea6099f50a2819246111ff671a8f52cf583e91294593179949542f1e18814059c953007bbee528c1dbaa244
7
+ data.tar.gz: c52d560aacea0d6279948db05d29c1a5b5c55a5d7fb2218f03d418122417a8ca3377a1b4af54238538d4d911c4827b6a2921b76001b161a6bfdd460e55dd13e5
data/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## [0.7.1] - 2025-01-20
3
+ ## [0.7.2] - 2026-02-09
4
+
5
+ ### Added
6
+
7
+ - OptionPartition can parse flags with a custom switch or separator.
8
+ - Gem command uninstall and pristine are interactive.
9
+ - Docker command network action create was implemented.
10
+ - Python command serve using http.server module was implemented.
11
+ - Ruby command serve using webrick was implemented.
12
+ - Node command serve using filename or string was implemented.
13
+
14
+ ### Fixed
15
+
16
+ - Docker task clean does not run in parallel without ENV override.
17
+
18
+ ## [0.7.1] - 2026-01-20
4
19
 
5
20
  ### Added
6
21
 
@@ -14,7 +29,7 @@
14
29
 
15
30
  - Git task show was completely non-functional due to excessive formatting.
16
31
 
17
- ## [0.7.0] - 2025-01-07
32
+ ## [0.7.0] - 2026-01-07
18
33
 
19
34
  ### Added
20
35
 
@@ -87,7 +102,7 @@
87
102
  - Project base method command_args did not append prompted value.
88
103
  - Bundler command exec did not use correct delegate for delete.
89
104
 
90
- ## [0.6.9] - 2025-01-07
105
+ ## [0.6.9] - 2026-01-07
91
106
 
92
107
  ### Added
93
108
 
@@ -1680,6 +1695,7 @@
1680
1695
 
1681
1696
  - Changelog was created.
1682
1697
 
1698
+ [0.7.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.2
1683
1699
  [0.7.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.1
1684
1700
  [0.7.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.0
1685
1701
  [0.6.9]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.9
data/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  | 2025-01-07 | 0.2.0 | | 3.4.0 | |
12
12
  | 2025-02-07 | 0.3.0 | | 3.4.1 | |
13
13
  | 2025-03-06 | 0.4.0 | | 3.4.2 | * |
14
- | 2025-06-16 | 0.5.0 | 2.5.0 | 3.4.3 | * |
14
+ | 2025-06-16 | 0.5.0 | 2.5.0 | 3.4.4 | * |
15
15
  | ---------- | ----- | ----- | ----- | --- |
16
16
  | 2025-08-23 | 0.5.5 | | 3.4.5 | |
17
17
  | 2025-10-31 | 0.6.0 | | 3.4.7 | * |
@@ -300,10 +300,10 @@ Workspace::Application
300
300
  end
301
301
  .with(:python) do
302
302
  doc("make html")
303
- add("android-docs") do
303
+ add("android-docs", serve: "./build/html") do # Uses built-in http.server module (ruby: webrick)
304
304
  chain "all", "doc", with: "squared", after: "squared" # step: 4
305
305
  end
306
- add("chrome-docs") do
306
+ add("chrome-docs", serve: { root: "./build/html", port: 8000 }) do # root | bind | port | opts[]
307
307
  chain "all", "doc", with: "squared", before: "squared:revbuild" # Same
308
308
  end
309
309
  end
@@ -73,7 +73,7 @@ module Squared
73
73
  end
74
74
 
75
75
  def shell_option(flag, val = nil, sep: '=', escape: true, quote: true, force: true, double: false, merge: false,
76
- override: false)
76
+ switch: nil, override: false)
77
77
  flag = flag.to_s
78
78
  if flag =~ QUOTE_VALUE
79
79
  double = $1 == '"'
@@ -88,6 +88,9 @@ module Squared
88
88
  else
89
89
  merge ? '' : ' '
90
90
  end
91
+ elsif switch
92
+ pre = switch unless flag.start_with?(switch)
93
+ merge ? '' : sep
91
94
  elsif flag.size == 1
92
95
  pre = '-'
93
96
  merge ? '' : ' '
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.7.1'
4
+ VERSION = '0.7.2'
5
5
  end
@@ -23,7 +23,7 @@ module Squared
23
23
 
24
24
  OPTIONS = Workspace::Support.hashobj
25
25
  VAR_SET = %i[parent global script index envname desc dependfile dependname dependindex theme archive env graph
26
- dev prod timeout pass only exclude asdf].freeze
26
+ dev prod timeout pass only exclude serve asdf].freeze
27
27
  BLK_SET = %i[run depend doc lint test copy clean].freeze
28
28
  SEM_VER = /\b(\d+)(?:(\.)(\d+))?(?:(\.)(\d+))?[-.]?(\S+)?\b/.freeze
29
29
  URI_SCHEME = %r{\A([a-z][a-z\d+-.]*)://[^@:\[\]\\^<>|\s]}i.freeze
@@ -117,7 +117,8 @@ module Squared
117
117
  subtasks({
118
118
  'graph' => %i[run print].freeze,
119
119
  'unpack' => %i[zip gz tar ext],
120
- 'asdf' => %i[set exec env current update latest where reshim]
120
+ 'asdf' => %i[set exec env current update latest where reshim],
121
+ 'serve' => nil
121
122
  })
122
123
 
123
124
  attr_reader :name, :workspace, :path, :theme, :group, :parent, :children, :dependfile,
@@ -276,80 +277,114 @@ module Squared
276
277
  Base.subtasks do |action, flags|
277
278
  next if task_pass?(action)
278
279
 
279
- namespace action do
280
- flags.each do |flag|
281
- case action
282
- when 'graph'
283
- break unless graph?
284
-
285
- format_desc action, flag, '(-)project*'
286
- task flag do |_, args|
287
- args = args.to_a.reject { |val| val == name }
288
- if flag == :run
289
- graph args
290
- else
291
- out = graph(args, out: [], order: {})
292
- emphasize(out, title: path, right: true, border: borderstyle, sub: [
293
- opt_style(theme[:header], /\A(#{Regexp.escape(path.to_s)})(.*)\z/),
294
- opt_style(theme[:active], /\A(#{Regexp.escape(name)})(.*)\z/),
295
- opt_style(theme[:inline], /\A((?~ \() \()(\d+)(\).*)\z/, 2)
296
- ])
280
+ if flags.nil?
281
+ case action
282
+ when 'serve'
283
+ next unless serve?
284
+
285
+ format_desc action, nil, 'root,port?,bind?,opts*'
286
+ task action, [:root] do |_, args|
287
+ if !args.root && @serve
288
+ kwargs = @serve.dup
289
+ root = kwargs.delete(:root)
290
+ opts = kwargs.delete(:opts) || []
291
+ else
292
+ root = param_guard(action, 'path', args: args, key: :root)
293
+ opts = args.extras
294
+ unless opts.empty?
295
+ port = opts.shift
296
+ if port.match?(/^\d+$/)
297
+ bind = opts.shift
298
+ else
299
+ bind = port
300
+ port = nil
301
+ end
302
+ if bind&.start_with?('-')
303
+ opts << bind
304
+ bind = nil
305
+ end
297
306
  end
307
+ kwargs = { port: port, bind: bind }.compact
298
308
  end
299
- when 'unpack'
300
- format_desc(action, flag, 'tag/url,dir,digest?,f/orce?', before: ('ext' if flag == :ext))
301
- params = %i[tag dir digest force]
302
- params.unshift(:ext) if flag == :ext
303
- task flag, params do |_, args|
304
- ext = flag == :ext ? param_guard(action, flag, args: args, key: :ext) : flag.to_s
305
- tag = param_guard(action, flag, args: args, key: :tag)
306
- dir = param_guard(action, flag, args: args, key: :dir)
307
- unless tag.match?(URI_SCHEME)
308
- tag = unpack_get tag, ext
309
- tag ||= if @release
310
- "%s.#{ext}" % [@release.include?('??') ? @release.sub('??', tag) : @release + tag]
309
+ serve(basepath(root).to_s, opts, **kwargs)
310
+ end
311
+ end
312
+ else
313
+ namespace action do
314
+ flags.each do |flag|
315
+ case action
316
+ when 'graph'
317
+ break unless graph?
318
+
319
+ format_desc action, flag, '(-)project*'
320
+ task flag do |_, args|
321
+ args = args.to_a.reject { |val| val == name }
322
+ if flag == :run
323
+ graph args
324
+ else
325
+ out = graph(args, out: [], order: {})
326
+ emphasize(out, title: path, right: true, border: borderstyle, sub: [
327
+ opt_style(theme[:header], /\A(#{Regexp.escape(path.to_s)})(.*)\z/),
328
+ opt_style(theme[:active], /\A(#{Regexp.escape(name)})(.*)\z/),
329
+ opt_style(theme[:inline], /\A((?~ \() \()(\d+)(\).*)\z/, 2)
330
+ ])
331
+ end
332
+ end
333
+ when 'unpack'
334
+ format_desc(action, flag, 'tag/url,dir,digest?,f/orce?', before: ('ext' if flag == :ext))
335
+ params = %i[tag dir digest force]
336
+ params.unshift(:ext) if flag == :ext
337
+ task flag, params do |_, args|
338
+ ext = flag == :ext ? param_guard(action, flag, args: args, key: :ext) : flag.to_s
339
+ tag = param_guard(action, flag, args: args, key: :tag)
340
+ dir = param_guard(action, flag, args: args, key: :dir)
341
+ unless tag.match?(URI_SCHEME)
342
+ tag = unpack_get tag, ext
343
+ tag ||= if @release
344
+ "%s.#{ext}" % [@release.include?('??') ? @release.sub('??', tag) : @release + tag]
345
+ else
346
+ raise_error ArgumentError, "no base uri: #{tag}", hint: ext
347
+ end
348
+ end
349
+ force = case (digest = args.digest)
350
+ when 'f', 'force'
351
+ digest = nil
352
+ true
311
353
  else
312
- raise_error ArgumentError, "no base uri: #{tag}", hint: ext
354
+ args.fetch(:force, false)
313
355
  end
356
+ unpack(basepath(dir), uri: tag, digest: digest, ext: ext, force: force)
314
357
  end
315
- force = case (digest = args.digest)
316
- when 'f', 'force'
317
- digest = nil
318
- true
319
- else
320
- args.fetch(:force, false)
321
- end
322
- unpack(basepath(dir), uri: tag, digest: digest, ext: ext, force: force)
323
- end
324
- when 'asdf'
325
- break unless @asdf
326
-
327
- case flag
328
- when :set
329
- format_desc action, flag, 'version,dir?=u/home|p/arent'
330
- task flag, [:version] do |_, args|
331
- args = if (version = args.version)
332
- args.extras
333
- else
334
- version, opts = choice_index('Select a version',
335
- @asdf[1].children
336
- .map(&:basename)
337
- .sort { |a, b| b <=> a }
338
- .push('latest', 'system'),
339
- accept: [accept_y('Confirm?')],
340
- values: 'Options')
341
- OptionPartition.strip(opts)
342
- end
343
- asdf(flag, args, version: version)
344
- end
345
- else
346
- format_desc(action, flag, case flag when :exec, :env then 'command' end)
347
- task flag do |_, args|
348
- args = args.to_a
349
- if args.empty? && (flag == :exec || flag == :env)
350
- args << readline('Enter command', force: flag == :exec)
358
+ when 'asdf'
359
+ break unless @asdf
360
+
361
+ case flag
362
+ when :set
363
+ format_desc action, flag, 'version,dir?=u/home|p/arent'
364
+ task flag, [:version] do |_, args|
365
+ args = if (version = args.version)
366
+ args.extras
367
+ else
368
+ version, opts = choice_index('Select a version',
369
+ @asdf[1].children
370
+ .map(&:basename)
371
+ .sort { |a, b| b <=> a }
372
+ .push('latest', 'system'),
373
+ accept: [accept_y('Confirm?')],
374
+ values: 'Options')
375
+ OptionPartition.strip(opts)
376
+ end
377
+ asdf(flag, args, version: version)
378
+ end
379
+ else
380
+ format_desc(action, flag, case flag when :exec, :env then 'command' end)
381
+ task flag do |_, args|
382
+ args = args.to_a
383
+ if args.empty? && (flag == :exec || flag == :env)
384
+ args << readline('Enter command', force: flag == :exec)
385
+ end
386
+ asdf flag, args
351
387
  end
352
- asdf flag, args
353
388
  end
354
389
  end
355
390
  end
@@ -959,6 +994,8 @@ module Squared
959
994
  parent_set val
960
995
  when :archive
961
996
  archive_set val
997
+ when :serve
998
+ serve_set val
962
999
  when :asdf
963
1000
  asdf_set val
964
1001
  when :run
@@ -2499,6 +2536,17 @@ module Squared
2499
2536
  end
2500
2537
  end
2501
2538
 
2539
+ def serve_set(val)
2540
+ @serve = case val
2541
+ when false
2542
+ false
2543
+ when String
2544
+ { root: val }
2545
+ when Hash
2546
+ val if val.key?(:root)
2547
+ end
2548
+ end
2549
+
2502
2550
  def asdf_set(val)
2503
2551
  @asdf = if @@asdf && val
2504
2552
  dir = @@asdf.path.join('installs', val)
@@ -2660,6 +2708,10 @@ module Squared
2660
2708
  level.empty? ? ex != false && ex != Logger::INFO : ex.is_a?(Numeric) && level.include?(ex)
2661
2709
  end
2662
2710
 
2711
+ def serve?
2712
+ false
2713
+ end
2714
+
2663
2715
  def variables
2664
2716
  VAR_SET
2665
2717
  end
@@ -72,6 +72,8 @@ module Squared
72
72
  }.freeze,
73
73
  network: {
74
74
  connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=q link=b link-local-ip=q].freeze,
75
+ create: %w[attachable config-only gateway ingress internal ipv4 ipv6 aux-address=q config-from=b d|driver=b
76
+ ip-range=q ipam-driver=b ipam-opt=q label=q o|opt=q scope=b subnet=q].freeze,
75
77
  disconnect: %w[f|force].freeze
76
78
  }.freeze
77
79
  }.freeze
@@ -114,7 +116,7 @@ module Squared
114
116
  'image' => %i[ls rm pull push tag save].freeze,
115
117
  'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
116
118
  rm].freeze,
117
- 'network' => %i[connect disconnect].freeze,
119
+ 'network' => %i[connect disconnect create].freeze,
118
120
  'ls' => nil
119
121
  })
120
122
 
@@ -312,8 +314,9 @@ module Squared
312
314
  when 'network'
313
315
  format_desc action, flag, 'target,opts*'
314
316
  task flag, [:target] do |_, args|
315
- if args.target
316
- network(flag, args.extras, target: args.target)
317
+ target = flag == :create ? param_guard(action, flag, args: args, key: :target) : args.target
318
+ if target
319
+ network(flag, args.extras, target: target)
317
320
  else
318
321
  choice_command flag
319
322
  end
@@ -329,8 +332,8 @@ module Squared
329
332
  def clean(*, sync: invoked_sync?('clean'), **)
330
333
  if runnable?(@clean)
331
334
  super
332
- else
333
- image(:rm, sync: sync)
335
+ elsif sync || option('y', prefix: 'docker')
336
+ image :rm
334
337
  end
335
338
  end
336
339
 
@@ -635,10 +638,17 @@ module Squared
635
638
 
636
639
  def network(flag, opts = [], target: nil)
637
640
  cmd, opts = docker_session('network', flag, opts: opts)
638
- OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
639
- .clear
641
+ op = OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
642
+ .clear
640
643
  from = symjoin 'network', flag
641
- list_image(flag, docker_output('ps -a'), from: from) { |id| success?(run(cmd.temp(target, id), from: from)) }
644
+ if flag == :create
645
+ op.add_quote(target)
646
+ run(from: from)
647
+ else
648
+ list_image(flag, docker_output('ps -a'), from: from) do |id|
649
+ success?(run(cmd.temp(target, id), from: from))
650
+ end
651
+ end
642
652
  end
643
653
 
644
654
  def build?
@@ -1108,15 +1108,16 @@ module Squared
1108
1108
  pull(:autostash, sync: sync)
1109
1109
  end
1110
1110
 
1111
- def fetch(flag = nil, opts = [], sync: invoked_sync?('fetch', flag), remote: nil)
1111
+ def fetch(flag = nil, opts = [], sync: invoked_sync?('fetch', flag), remote: nil,
1112
+ banner: !sync || verbose? || task_invoked?('fetch'))
1112
1113
  opts = git_session('fetch', opts: opts).last
1113
1114
  opts << 'all' if flag == :all || option('all')
1114
1115
  append_pull(opts, collect_hash(OPT_GIT[:fetch]), flag: flag, from: :fetch, remote: remote,
1115
1116
  no: collect_hash(OPT_GIT[:no][:fetch]))
1116
1117
  if sync
1117
- source
1118
+ source(banner: banner)
1118
1119
  else
1119
- source(sync: false, **threadargs)
1120
+ source(sync: false, banner: banner, **threadargs)
1120
1121
  end
1121
1122
  end
1122
1123
 
@@ -106,7 +106,21 @@ module Squared
106
106
  tsc: %w[excludeDirectories excludeFiles customConditions lib moduleSuffixes plugins rootDirs typeRoots
107
107
  types].freeze
108
108
  }.freeze
109
- private_constant :OPT_NPM, :OPT_PNPM, :OPT_YARN, :OPT_BERRY, :OPT_TSC, :PASS_NODE
109
+ APP_SERVE = 'const t=require("http"),e=require("fs"),a=require("path"),i="$ROOT",o={avif:"image/avif",css:"te' \
110
+ 'xt/css",gif:"image/gif",htm:"text/html",html:"text/html",ico:"image/x-icon",jpeg:"image/jpeg",jp' \
111
+ 'g:"image/jpeg",js:"text/javascript",mjs:"application/javascript",json:"application/json",md:"tex' \
112
+ 't/markdown",m4a:"audio/x-m4a",mp3:"audio/mpeg",mp4:"video/mp4",otf:"font/otf",pdf:"application/p' \
113
+ 'df",png:"image/png",rss:"application/rss+xml",svg:"image/svg+xml",ttc:"font/collection",ttf:"fon' \
114
+ 't/ttf",txt:"text/plain",wasm:"application/wasm",webp:"image/webp",woff:"font/woff",woff2:"font/w' \
115
+ 'off2",xml:"application/xml",webmanifest:"application/manifest+json",yaml:"text/yaml",yml:"text/y' \
116
+ 'aml"};function n(t){return o[a.extname(t).slice(1)]||"application/octet-stream"}t.createServer((' \
117
+ 't,o)=>{let l=a.join(i,t.url.split("?")[0]);try{e.statSync(l).isDirectory()&&(l=a.join(l,"index.h' \
118
+ 'tml"))}catch{}e.readFile(l,(t,e)=>{console.error(`[${(new Date).toLocaleTimeString().padStart(11' \
119
+ ', 0)}] ${t?"\x1B[31m404\x1B[39m":"\x1B[1m200\x1B[22m"} ${l.slice(i.length)}`);if(t)return o.writ' \
120
+ 'eHead(404,{"Content-Type":"text/plain"}),void o.end("404 Not Found");o.writeHead(200,{"Content-T' \
121
+ 'ype":n(l)}),o.end(e)})}).listen({host:"$HOST",port:+"$PORT"},()=>{console.log("HTTP server liste' \
122
+ 'ning on http://$HOST:$PORT")});'
123
+ private_constant :OPT_NPM, :OPT_PNPM, :OPT_YARN, :OPT_BERRY, :OPT_TSC, :PASS_NODE, :APP_SERVE
110
124
 
111
125
  class << self
112
126
  def tasks
@@ -160,6 +174,7 @@ module Squared
160
174
  end
161
175
  @dependname = 'package.json'
162
176
  dependfile_set [@dependname]
177
+ serve_set kwargs[:serve]
163
178
  @tsfile = basepath! ts
164
179
  @pm = { __: kwargs[:init] }
165
180
  end
@@ -1148,6 +1163,30 @@ module Squared
1148
1163
  end
1149
1164
  end
1150
1165
 
1166
+ def serve(root, opts = [], bind: nil, port: 3000, **)
1167
+ var = {}
1168
+ if File.extname(root) =~ /[cm]?js/
1169
+ var['HOST'] = bind if bind
1170
+ var['PORT'] = port.to_s if port
1171
+ target = root
1172
+ root = File.dirname(target)
1173
+ else
1174
+ require 'tempfile'
1175
+ app = APP_SERVE.dup
1176
+ app.gsub!('$ROOT', root)
1177
+ app.gsub!('$HOST', bind || 'localhost')
1178
+ app.gsub!('$PORT', port.to_s)
1179
+ file = Tempfile.new([name, '.cjs'])
1180
+ file.write(app)
1181
+ file.close
1182
+ trap 'INT' do
1183
+ file.unlink
1184
+ end
1185
+ target = file.path
1186
+ end
1187
+ shell(var, session('node', shell_quote(target), *opts.map { |s| fill_option(s) }).done, chdir: root)
1188
+ end
1189
+
1151
1190
  def depend?
1152
1191
  @depend != false && (!@depend.nil? || outdated?)
1153
1192
  end
@@ -1438,6 +1477,10 @@ module Squared
1438
1477
  ret
1439
1478
  end
1440
1479
 
1480
+ def serve?
1481
+ @serve != false
1482
+ end
1483
+
1441
1484
  def nolockfile?(prefix = dependbin)
1442
1485
  option('package-lock', 'lockfile', prefix: prefix, equals: '0') || !option('no-lockfile', prefix: prefix).nil?
1443
1486
  end
@@ -129,6 +129,7 @@ module Squared
129
129
  initialize_env(**kwargs)
130
130
  end
131
131
  dependfile_set DEP_PYTHON
132
+ serve_set kwargs[:serve]
132
133
  editable_set editable
133
134
  venv_set kwargs[:venv]
134
135
  end
@@ -272,7 +273,7 @@ module Squared
272
273
  if args.empty?
273
274
  args = readline('Enter command', force: true).split(' ', 2)
274
275
  elsif args.size == 1 && option('interactive', notequals: '0', prefix: ref)
275
- args << readline('Enter arguments', force: false)
276
+ args << readline('Enter arguments', force: false) unless args.first.include?(' ')
276
277
  end
277
278
  venv_init
278
279
  run args.join(' ')
@@ -804,6 +805,10 @@ module Squared
804
805
  .yield_self { |val| ret || val }
805
806
  end
806
807
 
808
+ def serve(root, opts = [], bind: 'localhost', port: nil, **)
809
+ shell(python_session('-m http.server', port, basic_option('b', bind), opts: opts).first.done, chdir: root)
810
+ end
811
+
807
812
  def project
808
813
  return @project unless @project.frozen?
809
814
 
@@ -855,6 +860,10 @@ module Squared
855
860
  !venv.nil?
856
861
  end
857
862
 
863
+ def serve?
864
+ @serve != false
865
+ end
866
+
858
867
  private
859
868
 
860
869
  def pip_session(*cmd)
@@ -213,6 +213,7 @@ module Squared
213
213
  initialize_env(**kwargs)
214
214
  end
215
215
  dependfile_set GEMFILE
216
+ serve_set kwargs[:serve]
216
217
  gemfile_set kwargs[:gemspec]
217
218
  @autodetect = autodetect
218
219
  @steepfile = basepath! steep if steep
@@ -458,9 +459,10 @@ module Squared
458
459
  bundle(flag, opts: args.to_a, banner: flag == :exec ? verbose? : true)
459
460
  end
460
461
  when :reinstall
461
- format_desc action, flag, 'f/orce?,opts*'
462
+ format_desc action, flag, 'f/orce?,l/ocal?,opts*'
462
463
  task flag do |_, args|
463
464
  opts = args.to_a
465
+ opts << 'local' if has_value!(opts, 'l')
464
466
  opts << 'redownload' if has_value!(opts, 'f', 'force')
465
467
  if (lock = basepath!('Gemfile.lock'))
466
468
  config = basepath '.bundle', 'config'
@@ -1167,6 +1169,7 @@ module Squared
1167
1169
  when :dependency, :environment, :list, :search, :specification, :which
1168
1170
  op.concat(args)
1169
1171
  end
1172
+ ia = op.remove(':')
1170
1173
  op.each do |opt|
1171
1174
  if gems && !opt.start_with?('-') && !opt.match?(GEMNAME)
1172
1175
  op.errors << opt
@@ -1228,7 +1231,7 @@ module Squared
1228
1231
  end
1229
1232
  when :install, :uninstall, :pristine
1230
1233
  if flag == :install
1231
- post = if op.remove(':')
1234
+ post = if ia
1232
1235
  op.concat(args)
1233
1236
  readline('Enter command [args]', force: true)
1234
1237
  elsif op.empty?
@@ -1237,6 +1240,25 @@ module Squared
1237
1240
  elsif !args.empty?
1238
1241
  args.join(' ')
1239
1242
  end
1243
+ elsif ia
1244
+ name = op.shift || args.shift || (s = readline('Enter gem name', force: true))
1245
+ list = []
1246
+ pwd_set do
1247
+ pat = /^#{name}\s+\((.+)\)$/
1248
+ IO.popen(gem_output("list -a #{shell_quote(name)}").to_s).each do |val|
1249
+ next unless val =~ pat
1250
+
1251
+ split_escape($1).each { |val| list << val unless val.start_with?('default:') }
1252
+ break
1253
+ end
1254
+ end
1255
+ ver = choice_index('Select version', list, force: s.nil?) unless list.empty?
1256
+ if ver
1257
+ op << '--force'
1258
+ op.unshift("#{name}@#{ver}")
1259
+ else
1260
+ op.unshift(name)
1261
+ end
1240
1262
  end
1241
1263
  raise_error ArgumentError, 'missing gem name', hint: flag if op.empty?
1242
1264
  if op.arg?('all')
@@ -1246,7 +1268,7 @@ module Squared
1246
1268
  else
1247
1269
  op.clear
1248
1270
  end
1249
- elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
1271
+ elsif (n = op.index { |val| val.match?(/(\A|[\w.-])@\d/) })
1250
1272
  name = op.remove_at(n)
1251
1273
  n = name.index('@')
1252
1274
  pre, ver = if n == 0
@@ -1254,7 +1276,7 @@ module Squared
1254
1276
  else
1255
1277
  [name[0, n], name[n.succ..-1]]
1256
1278
  end
1257
- op.adjoin(pre, basic_option('version', ver))
1279
+ op.adjoin(pre, quote_option('version', ver))
1258
1280
  .clear
1259
1281
  end
1260
1282
  if flag == :install
@@ -1540,6 +1562,18 @@ module Squared
1540
1562
  run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: :rubocop)
1541
1563
  end
1542
1564
 
1565
+ def serve(root, *, bind: nil, port: nil, **kwargs)
1566
+ require 'webrick'
1567
+ config = kwargs.merge({ DocumentRoot: root })
1568
+ config[:BindAddress] = bind if bind
1569
+ config[:Port] = port if port
1570
+ server = WEBrick::HTTPServer.new(config)
1571
+ trap 'INT' do
1572
+ server.shutdown
1573
+ end
1574
+ server.start
1575
+ end
1576
+
1543
1577
  def gemspec
1544
1578
  @gemspec = !gemfile.nil? && Gem::Specification.load(gemfile.to_s) rescue false if @gemspec.nil?
1545
1579
  @gemspec || nil
@@ -1873,6 +1907,13 @@ module Squared
1873
1907
  semgte?(ver.to_s, min.to_s) && (max == Float::INFINITY || !semgte?(ver.to_s, max.to_s))
1874
1908
  end
1875
1909
  end
1910
+
1911
+ def serve?
1912
+ !Gem::Specification.find_by_name('webrick').nil?
1913
+ rescue Gem::MissingSpecError => e
1914
+ log.warn e if @serve
1915
+ false
1916
+ end
1876
1917
  end
1877
1918
 
1878
1919
  Application.implement Ruby
@@ -221,6 +221,8 @@ module Squared
221
221
  si = []
222
222
  bl = []
223
223
  ml = []
224
+ sw = {}
225
+ se = {}
224
226
  list.flat_map do |val|
225
227
  x, y = val.split('|', 2)
226
228
  if y
@@ -233,6 +235,11 @@ module Squared
233
235
  end
234
236
  end
235
237
  .each do |val|
238
+ if val.size > 1 && val =~ /^\{([^},]*)(?:,([^}]+))?\}(.+)$/
239
+ op1 = $1 unless $1.empty?
240
+ op2 = $2
241
+ val = $3
242
+ end
236
243
  if (n = val.index('='))
237
244
  flag = val[0, n]
238
245
  case val[n.succ]
@@ -265,9 +272,12 @@ module Squared
265
272
  end
266
273
  m << flag if val[n + 2] == 'm'
267
274
  bare << flag if val.end_with?('?')
275
+ val = flag
268
276
  else
269
277
  bare << val
270
278
  end
279
+ sw[val] = op1 if op1
280
+ se[val] = op2 if op2
271
281
  end
272
282
  no = (no || []).map { |val| (n = val.index('=')) ? val[0, n] : val }
273
283
  bare.concat(no)
@@ -316,30 +326,34 @@ module Squared
316
326
  if single&.match?(opt)
317
327
  add "-#{opt}"
318
328
  elsif bare.include?(opt)
319
- add(opt.size == 1 ? "-#{opt}" : "--#{opt}")
320
- elsif opt.start_with?(/no[-_]/) && no.include?(name = opt[3..-1])
321
- add "--no-#{name}"
329
+ if sw[opt]
330
+ add "#{sw[opt]}#{opt}"
331
+ else
332
+ add(opt.size == 1 ? "-#{opt}" : "--#{opt}")
333
+ end
334
+ elsif opt.start_with?(/no[-_]/) && no.include?(s = opt[3..-1])
335
+ add "--no-#{s}"
322
336
  else
323
337
  if opt =~ OPT_VALUE
324
338
  key = $1
325
339
  val = $2
326
340
  merge = m.include?(key)
327
- if e.include?(key)
328
- add shell_option(key, val, merge: merge, sep: sep)
341
+ switch = sw[key]
342
+ kwargs = { sep: se[key] || sep, merge: merge, switch: switch }
343
+ if e.include?(key) || (bl.include?(key) && %w[true false].include?(val))
344
+ add shell_option(key, val, **kwargs)
329
345
  elsif q.include?(key)
330
- add quote_option(key, val, double: qq.include?(key), merge: merge, sep: sep)
346
+ add quote_option(key, val, double: qq.include?(key), **kwargs)
331
347
  elsif p.include?(key)
332
348
  if val.match?(/\A(["']).+\1\z/)
333
- add shell_option(key, val, escape: false, merge: merge, sep: sep)
349
+ add shell_option(key, val, escape: false, **kwargs)
334
350
  elsif path
335
- add quote_option(key, path + val, merge: merge, sep: sep)
351
+ add quote_option(key, path + val, **kwargs)
336
352
  else
337
353
  push opt
338
354
  end
339
- elsif b.include?(key) || (bl.include?(key) && %w[true false].include?(val)) || numcheck.call(key, val)
340
- add basic_option(key, val, merge: merge, sep: sep)
341
- elsif merge
342
- add basic_option(key, val, merge: true, sep: sep)
355
+ elsif b.include?(key) || numcheck.call(key, val) || se[key] || merge || switch
356
+ add basic_option(key, val, **kwargs)
343
357
  else
344
358
  push opt
345
359
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham