ruby-shell 3.4.7 → 3.4.9
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/bin/rsh +108 -73
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2eadccdac83c394da85a64a138d36cad24de2603aeb2867bce52f4aa00d0568
|
|
4
|
+
data.tar.gz: 0b54ea26c9f82db1f1c29fffbc6e69d19c0c4ad3e5d94d490ba33aaf268be3c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8448bde19ed12fb02b3b28a7295f928ce643caa43ce7045f8693a733729bd8dea2b23c01cbae44fcf3d39f412238a5aa5a17281e9d55cf64f5ec2bf5c56fe38d
|
|
7
|
+
data.tar.gz: bd903733344c8aa9b0d42a2dfbcaf63339861bc7db5a289425881fea0a8c10fcbfc705a0840000ef165a99a843ebdd39c155263fe393fbd09ce8885dad6513c0
|
data/bin/rsh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# Web_site: http://isene.com/
|
|
9
9
|
# Github: https://github.com/isene/rsh
|
|
10
10
|
# License: Public domain
|
|
11
|
-
@version = "3.4.
|
|
11
|
+
@version = "3.4.9" # Improved :calc with safer eval, Math sandbox, better error messages
|
|
12
12
|
|
|
13
13
|
# MODULES, CLASSES AND EXTENSIONS
|
|
14
14
|
class String # Add coloring to strings (with escaping for Readline)
|
|
@@ -1077,6 +1077,16 @@ def suggest_command(cmd) # Smart command suggestions for typos
|
|
|
1077
1077
|
candidates.sort_by! { |c| levenshtein_distance(cmd, c) }
|
|
1078
1078
|
candidates.first(3)
|
|
1079
1079
|
end
|
|
1080
|
+
def ensure_type(var_name, type, default) # Helper to ensure instance variable has correct type
|
|
1081
|
+
var = instance_variable_get(var_name)
|
|
1082
|
+
instance_variable_set(var_name, default) unless var.is_a?(type)
|
|
1083
|
+
end
|
|
1084
|
+
def persist_var(conf, var_name, value, condition = true) # Helper to persist variable to config
|
|
1085
|
+
return conf unless condition
|
|
1086
|
+
conf.sub!(/^#{Regexp.escape(var_name)}.*(\n|$)/, "")
|
|
1087
|
+
conf += "#{var_name} = #{value.inspect}\n"
|
|
1088
|
+
conf
|
|
1089
|
+
end
|
|
1080
1090
|
def hist_clean # Clean up @history
|
|
1081
1091
|
@history.compact!
|
|
1082
1092
|
@history.delete("")
|
|
@@ -1378,31 +1388,19 @@ def rshrc # Write user configuration to .rshrc (portable between machines)
|
|
|
1378
1388
|
conf = ""
|
|
1379
1389
|
end
|
|
1380
1390
|
|
|
1381
|
-
#
|
|
1382
|
-
conf
|
|
1383
|
-
conf
|
|
1384
|
-
conf
|
|
1385
|
-
conf
|
|
1386
|
-
conf
|
|
1387
|
-
conf
|
|
1388
|
-
conf
|
|
1389
|
-
conf
|
|
1390
|
-
conf
|
|
1391
|
-
conf
|
|
1392
|
-
conf
|
|
1393
|
-
conf
|
|
1394
|
-
conf.sub!(/^@auto_correct.*(\n|$)/, "")
|
|
1395
|
-
conf += "@auto_correct = #{@auto_correct}\n" if @auto_correct
|
|
1396
|
-
conf.sub!(/^@slow_command_threshold.*(\n|$)/, "")
|
|
1397
|
-
conf += "@slow_command_threshold = #{@slow_command_threshold}\n" if @slow_command_threshold && @slow_command_threshold > 0
|
|
1398
|
-
conf.sub!(/^@completion_learning.*(\n|$)/, "")
|
|
1399
|
-
conf += "@completion_learning = #{@completion_learning}\n" unless @completion_learning
|
|
1400
|
-
conf.sub!(/^@completion_show_metadata.*(\n|$)/, "")
|
|
1401
|
-
conf += "@completion_show_metadata = #{@completion_show_metadata}\n" if @completion_show_metadata
|
|
1402
|
-
conf.sub!(/^@plugin_enabled.*(\n|$)/, "")
|
|
1403
|
-
conf += "@plugin_enabled = #{@plugin_enabled}\n" unless @plugin_enabled.empty?
|
|
1404
|
-
conf.sub!(/^@validation_rules.*(\n|$)/, "")
|
|
1405
|
-
conf += "@validation_rules = #{@validation_rules}\n" unless @validation_rules.empty?
|
|
1391
|
+
# Persist user-editable configuration using helper
|
|
1392
|
+
conf = persist_var(conf, '@nick', @nick)
|
|
1393
|
+
conf = persist_var(conf, '@gnick', @gnick)
|
|
1394
|
+
conf = persist_var(conf, '@bookmarks', @bookmarks, !@bookmarks.empty?)
|
|
1395
|
+
conf = persist_var(conf, '@defuns', @defuns, !@defuns.empty?)
|
|
1396
|
+
conf = persist_var(conf, '@history_dedup', @history_dedup, @history_dedup && @history_dedup != 'smart')
|
|
1397
|
+
conf = persist_var(conf, '@session_autosave', @session_autosave, @session_autosave && @session_autosave > 0)
|
|
1398
|
+
conf = persist_var(conf, '@auto_correct', @auto_correct, @auto_correct)
|
|
1399
|
+
conf = persist_var(conf, '@slow_command_threshold', @slow_command_threshold, @slow_command_threshold && @slow_command_threshold > 0)
|
|
1400
|
+
conf = persist_var(conf, '@completion_learning', @completion_learning, !@completion_learning)
|
|
1401
|
+
conf = persist_var(conf, '@completion_show_metadata', @completion_show_metadata, @completion_show_metadata)
|
|
1402
|
+
conf = persist_var(conf, '@plugin_disabled', @plugin_disabled, !@plugin_disabled.empty?)
|
|
1403
|
+
conf = persist_var(conf, '@validation_rules', @validation_rules, !@validation_rules.empty?)
|
|
1406
1404
|
|
|
1407
1405
|
File.write(Dir.home+'/.rshrc', conf)
|
|
1408
1406
|
rshstate # Also save runtime state
|
|
@@ -1562,6 +1560,8 @@ def help
|
|
|
1562
1560
|
col3 << ":help This help"
|
|
1563
1561
|
col3 << ":info About rsh"
|
|
1564
1562
|
col3 << ":version Version info"
|
|
1563
|
+
col3 << ":history Show history"
|
|
1564
|
+
col3 << ":rehash Rebuild cache"
|
|
1565
1565
|
|
|
1566
1566
|
# Pad columns to same length
|
|
1567
1567
|
max_lines = [col1.length, col2.length, col3.length].max
|
|
@@ -1622,12 +1622,17 @@ def nick(nick_str = nil) # Define a new nick like this: `:nick "ls = ls --color
|
|
|
1622
1622
|
puts
|
|
1623
1623
|
elsif nick_str.match(/^\s*-/)
|
|
1624
1624
|
source = nick_str.sub(/^\s*-/, '')
|
|
1625
|
-
@nick.delete(source)
|
|
1626
|
-
|
|
1625
|
+
if @nick.delete(source)
|
|
1626
|
+
puts "Nick '#{source}' deleted"
|
|
1627
|
+
rshrc
|
|
1628
|
+
else
|
|
1629
|
+
puts "Nick '#{source}' not found"
|
|
1630
|
+
end
|
|
1627
1631
|
else
|
|
1628
1632
|
source = nick_str.sub(/ =.*/, '')
|
|
1629
1633
|
target = nick_str.sub(/.*= /, '')
|
|
1630
1634
|
@nick[source] = target
|
|
1635
|
+
puts "Nick '#{source}' → '#{target}'"
|
|
1631
1636
|
rshrc
|
|
1632
1637
|
end
|
|
1633
1638
|
end
|
|
@@ -1643,12 +1648,17 @@ def gnick(nick_str = nil) # Define a generic/global nick to match not only comma
|
|
|
1643
1648
|
puts
|
|
1644
1649
|
elsif nick_str.match(/^\s*-/)
|
|
1645
1650
|
source = nick_str.sub(/^\s*-/, '')
|
|
1646
|
-
@gnick.delete(source)
|
|
1647
|
-
|
|
1651
|
+
if @gnick.delete(source)
|
|
1652
|
+
puts "Gnick '#{source}' deleted"
|
|
1653
|
+
rshrc
|
|
1654
|
+
else
|
|
1655
|
+
puts "Gnick '#{source}' not found"
|
|
1656
|
+
end
|
|
1648
1657
|
else
|
|
1649
1658
|
source = nick_str.sub(/ =.*/, '')
|
|
1650
1659
|
target = nick_str.sub(/.*= /, '')
|
|
1651
1660
|
@gnick[source] = target
|
|
1661
|
+
puts "Gnick '#{source}' → '#{target}'"
|
|
1652
1662
|
rshrc
|
|
1653
1663
|
end
|
|
1654
1664
|
end
|
|
@@ -2505,24 +2515,46 @@ def validate_command(cmd) # Syntax validation before execution
|
|
|
2505
2515
|
end
|
|
2506
2516
|
def calc(*args) # Inline calculator using Ruby's Math library
|
|
2507
2517
|
if args.empty?
|
|
2508
|
-
puts "Usage: calc <expression>"
|
|
2518
|
+
puts "Usage: :calc <expression>"
|
|
2509
2519
|
puts "Examples:"
|
|
2510
|
-
puts " calc 2 + 2"
|
|
2511
|
-
puts " calc
|
|
2512
|
-
puts " calc
|
|
2520
|
+
puts " :calc 2 + 2"
|
|
2521
|
+
puts " :calc Math.sqrt(16)"
|
|
2522
|
+
puts " :calc Math::PI * 2"
|
|
2523
|
+
puts " :calc sin(PI/4)"
|
|
2524
|
+
puts "Available: +, -, *, /, **, %, sqrt, sin, cos, tan, log, exp, abs, PI, E, etc."
|
|
2513
2525
|
return
|
|
2514
2526
|
end
|
|
2515
2527
|
|
|
2516
2528
|
expression = args.join(' ')
|
|
2517
2529
|
|
|
2530
|
+
# Create sandbox with Math module for safer evaluation
|
|
2531
|
+
sandbox = Object.new
|
|
2532
|
+
sandbox.extend(Math)
|
|
2533
|
+
|
|
2518
2534
|
begin
|
|
2519
|
-
|
|
2520
|
-
result = eval(expression, binding, __FILE__, __LINE__)
|
|
2535
|
+
result = sandbox.instance_eval(expression)
|
|
2521
2536
|
puts result
|
|
2537
|
+
rescue ZeroDivisionError
|
|
2538
|
+
puts "Error: Division by zero"
|
|
2539
|
+
rescue NameError => e
|
|
2540
|
+
# Extract the undefined name
|
|
2541
|
+
if e.message =~ /undefined local variable or method `(\w+)'/
|
|
2542
|
+
undefined = $1
|
|
2543
|
+
puts "Error: Unknown function or variable '#{undefined}'"
|
|
2544
|
+
puts "Available Math functions: sqrt, sin, cos, tan, log, exp, abs, ceil, floor, round"
|
|
2545
|
+
puts "Constants: PI, E"
|
|
2546
|
+
else
|
|
2547
|
+
puts "Error: #{e.message}"
|
|
2548
|
+
end
|
|
2522
2549
|
rescue SyntaxError => e
|
|
2523
|
-
puts "
|
|
2550
|
+
puts "Error: Invalid expression syntax"
|
|
2551
|
+
puts "Check parentheses, operators, and spacing"
|
|
2552
|
+
rescue ArgumentError => e
|
|
2553
|
+
puts "Error: Invalid argument - #{e.message}"
|
|
2554
|
+
rescue TypeError => e
|
|
2555
|
+
puts "Error: Type mismatch - #{e.message}"
|
|
2524
2556
|
rescue => e
|
|
2525
|
-
puts "Error
|
|
2557
|
+
puts "Error: #{e.message}"
|
|
2526
2558
|
end
|
|
2527
2559
|
end
|
|
2528
2560
|
def validate(rule_str = nil) # Custom validation rule management
|
|
@@ -3055,26 +3087,26 @@ def load_rshrc_safe
|
|
|
3055
3087
|
# Try to load the .rshrc file
|
|
3056
3088
|
load(Dir.home+'/.rshrc')
|
|
3057
3089
|
|
|
3058
|
-
# Validate critical variables
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3090
|
+
# Validate critical variables using helper
|
|
3091
|
+
ensure_type(:@history, Array, [])
|
|
3092
|
+
ensure_type(:@nick, Hash, {})
|
|
3093
|
+
ensure_type(:@gnick, Hash, {})
|
|
3094
|
+
ensure_type(:@cmd_frequency, Hash, {})
|
|
3095
|
+
ensure_type(:@cmd_stats, Hash, {})
|
|
3096
|
+
ensure_type(:@bookmarks, Hash, {})
|
|
3097
|
+
ensure_type(:@defuns, Hash, {})
|
|
3098
|
+
ensure_type(:@history_dedup, String, 'smart')
|
|
3099
|
+
ensure_type(:@session_autosave, Integer, 0)
|
|
3100
|
+
ensure_type(:@slow_command_threshold, Integer, 0)
|
|
3101
|
+
ensure_type(:@plugin_disabled, Array, []) # FIXED: was @plugin_enabled
|
|
3102
|
+
ensure_type(:@plugins, Array, [])
|
|
3103
|
+
ensure_type(:@plugin_commands, Hash, {})
|
|
3104
|
+
ensure_type(:@validation_rules, Array, [])
|
|
3105
|
+
ensure_type(:@completion_weights, Hash, {})
|
|
3106
|
+
ensure_type(:@recording, Hash, {active: false, name: nil, commands: []})
|
|
3107
|
+
ensure_type(:@recordings, Hash, {})
|
|
3108
|
+
@auto_correct = false unless [true, false].include?(@auto_correct)
|
|
3075
3109
|
@completion_learning = true if @completion_learning.nil?
|
|
3076
|
-
@recording = {active: false, name: nil, commands: []} unless @recording.is_a?(Hash)
|
|
3077
|
-
@recordings = {} unless @recordings.is_a?(Hash)
|
|
3078
3110
|
|
|
3079
3111
|
# Restore defuns from .rshrc
|
|
3080
3112
|
if @defuns && !@defuns.empty?
|
|
@@ -3205,31 +3237,34 @@ def auto_heal_rshrc
|
|
|
3205
3237
|
end
|
|
3206
3238
|
|
|
3207
3239
|
def load_defaults
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3240
|
+
# Use ensure_type for consistency
|
|
3241
|
+
ensure_type(:@history, Array, [])
|
|
3242
|
+
ensure_type(:@nick, Hash, {"ls" => "ls --color -F"})
|
|
3243
|
+
ensure_type(:@gnick, Hash, {})
|
|
3244
|
+
ensure_type(:@cmd_frequency, Hash, {})
|
|
3245
|
+
ensure_type(:@cmd_stats, Hash, {})
|
|
3246
|
+
ensure_type(:@bookmarks, Hash, {})
|
|
3247
|
+
ensure_type(:@defuns, Hash, {})
|
|
3248
|
+
ensure_type(:@switch_cache, Hash, {})
|
|
3249
|
+
ensure_type(:@switch_cache_time, Hash, {})
|
|
3250
|
+
ensure_type(:@plugin_disabled, Array, []) # FIXED: was @plugin_enabled
|
|
3251
|
+
ensure_type(:@plugins, Array, [])
|
|
3252
|
+
ensure_type(:@plugin_commands, Hash, {})
|
|
3253
|
+
ensure_type(:@validation_rules, Array, [])
|
|
3254
|
+
ensure_type(:@completion_weights, Hash, {})
|
|
3255
|
+
ensure_type(:@recording, Hash, {active: false, name: nil, commands: []})
|
|
3256
|
+
ensure_type(:@recordings, Hash, {})
|
|
3257
|
+
|
|
3211
3258
|
@completion_limit ||= 10
|
|
3212
3259
|
@completion_case_sensitive ||= false
|
|
3213
3260
|
@completion_show_descriptions ||= false
|
|
3214
3261
|
@completion_fuzzy ||= true
|
|
3215
|
-
@cmd_frequency ||= {}
|
|
3216
|
-
@cmd_stats ||= {}
|
|
3217
|
-
@bookmarks ||= {}
|
|
3218
|
-
@defuns ||= {}
|
|
3219
|
-
@switch_cache ||= {}
|
|
3220
|
-
@switch_cache_time ||= {}
|
|
3221
3262
|
@history_dedup ||= 'smart'
|
|
3222
3263
|
@session_autosave ||= 0
|
|
3223
3264
|
@auto_correct ||= false
|
|
3224
3265
|
@slow_command_threshold ||= 0
|
|
3225
|
-
@plugin_enabled ||= []
|
|
3226
|
-
@plugins ||= []
|
|
3227
|
-
@plugin_commands ||= {}
|
|
3228
|
-
@validation_rules ||= []
|
|
3229
|
-
@completion_weights ||= {}
|
|
3230
3266
|
@completion_learning = true if @completion_learning.nil?
|
|
3231
|
-
|
|
3232
|
-
@recordings ||= {}
|
|
3267
|
+
|
|
3233
3268
|
puts "Loaded with default configuration."
|
|
3234
3269
|
end
|
|
3235
3270
|
|
metadata
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-shell
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.4.
|
|
4
|
+
version: 3.4.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'A shell written in Ruby with extensive tab completions, aliases/nicks,
|
|
14
14
|
history, syntax highlighting, theming, auto-cd, auto-opening files and more. UPDATE
|
|
15
|
-
v3.4.
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
v3.4.9: Improved :calc with Math sandbox for safer evaluation, better error messages
|
|
16
|
+
(division by zero, unknown functions, syntax errors). Suggested by havenwood from
|
|
17
|
+
#ruby IRC!'
|
|
18
18
|
email: g@isene.com
|
|
19
19
|
executables:
|
|
20
20
|
- rsh
|