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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/rsh +108 -73
  3. metadata +5 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70cf831b842cb78920de4f541751dc71b1ec87d4d2619eb813d726aa9abe844d
4
- data.tar.gz: 3a1baee44aa63c583f62485d6df477b5aeca1f04801ce2dd6c9b92a35795e27e
3
+ metadata.gz: c2eadccdac83c394da85a64a138d36cad24de2603aeb2867bce52f4aa00d0568
4
+ data.tar.gz: 0b54ea26c9f82db1f1c29fffbc6e69d19c0c4ad3e5d94d490ba33aaf268be3c5
5
5
  SHA512:
6
- metadata.gz: 96e26e3cb1788d7aa04eea7d8aabf034ac59f904fcc808151c3297d6a896794330be4e92c372c71f96376605e9943cd734c8871373fe6c4bf07e5fb3a0bbd891
7
- data.tar.gz: ef2907de7d2758f996beb911677430b1e1e674b388ebfa726d2dbead4e8b9670dc03baa7009b534ca268064ae4feaf8c1c6dc474552c1f6a6e60a86068cbd147
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.7" # Added :rehash command to manually rebuild executable cache
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
- # Only update user-editable items in .rshrc
1382
- conf.sub!(/^@nick.*(\n|$)/, "")
1383
- conf += "@nick = #{@nick}\n"
1384
- conf.sub!(/^@gnick.*(\n|$)/, "")
1385
- conf += "@gnick = #{@gnick}\n"
1386
- conf.sub!(/^@bookmarks.*(\n|$)/, "")
1387
- conf += "@bookmarks = #{@bookmarks}\n" unless @bookmarks.empty?
1388
- conf.sub!(/^@defuns.*(\n|$)/, "")
1389
- conf += "@defuns = #{@defuns}\n" unless @defuns.empty?
1390
- conf.sub!(/^@history_dedup.*(\n|$)/, "")
1391
- conf += "@history_dedup = '#{@history_dedup}'\n" if @history_dedup && @history_dedup != 'smart'
1392
- conf.sub!(/^@session_autosave.*(\n|$)/, "")
1393
- conf += "@session_autosave = #{@session_autosave}\n" if @session_autosave && @session_autosave > 0
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
- rshrc
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
- rshrc
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 \"Math.sqrt(16)\""
2512
- puts " calc \"Math::PI * 2\""
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
- # Safe evaluation with Math library
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 "Syntax error in expression: #{e.message}"
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 evaluating expression: #{e.message}"
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
- @history = [] unless @history.is_a?(Array)
3060
- @nick = {} unless @nick.is_a?(Hash)
3061
- @gnick = {} unless @gnick.is_a?(Hash)
3062
- @cmd_frequency = {} unless @cmd_frequency.is_a?(Hash)
3063
- @cmd_stats = {} unless @cmd_stats.is_a?(Hash)
3064
- @bookmarks = {} unless @bookmarks.is_a?(Hash)
3065
- @defuns = {} unless @defuns.is_a?(Hash)
3066
- @history_dedup = 'smart' unless @history_dedup.is_a?(String)
3067
- @session_autosave = 0 unless @session_autosave.is_a?(Integer)
3068
- @auto_correct = false unless [@auto_correct].any? { |v| v == true || v == false }
3069
- @slow_command_threshold = 0 unless @slow_command_threshold.is_a?(Integer)
3070
- @plugin_enabled = [] unless @plugin_enabled.is_a?(Array)
3071
- @plugins = [] unless @plugins.is_a?(Array)
3072
- @plugin_commands = {} unless @plugin_commands.is_a?(Hash)
3073
- @validation_rules = [] unless @validation_rules.is_a?(Array)
3074
- @completion_weights = {} unless @completion_weights.is_a?(Hash)
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
- @history ||= []
3209
- @nick ||= {"ls" => "ls --color -F"}
3210
- @gnick ||= {}
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
- @recording ||= {active: false, name: nil, commands: []}
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.7
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-29 00:00:00.000000000 Z
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.7: Added :rehash command to manually rebuild executable cache (like zsh''s
16
- rehash builtin). Plus v3.4.6: Plugin help system, 4 new plugins, plugins disabled
17
- by default!'
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