mysh 0.1.14 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -2
  3. data/lib/mysh/expression/lineage.rb +24 -0
  4. data/lib/mysh/expression.rb +53 -51
  5. data/lib/mysh/external_ruby.rb +1 -1
  6. data/lib/mysh/{support/frame.rb → internal/action.rb} +4 -4
  7. data/lib/mysh/{support/command_pool.rb → internal/action_pool.rb} +8 -8
  8. data/lib/mysh/internal/actions/actions_path.rb +15 -0
  9. data/lib/mysh/{commands → internal/actions}/cd.rb +2 -2
  10. data/lib/mysh/{commands → internal/actions}/exit.rb +2 -2
  11. data/lib/mysh/{commands → internal/actions}/help.rb +7 -7
  12. data/lib/mysh/{commands → internal/actions}/help.txt +1 -1
  13. data/lib/mysh/{commands → internal/actions}/help_expr.txt +1 -0
  14. data/lib/mysh/{commands → internal/actions}/help_help.txt +1 -1
  15. data/lib/mysh/{commands → internal/actions}/help_math.txt +0 -0
  16. data/lib/mysh/{commands → internal/actions}/history.rb +2 -2
  17. data/lib/mysh/{support → internal}/decorate.rb +2 -2
  18. data/lib/mysh/internal/format.rb +48 -0
  19. data/lib/mysh/internal/manage.rb +34 -0
  20. data/lib/mysh/internal.rb +7 -9
  21. data/lib/mysh/user_input/handlebars.rb +33 -0
  22. data/lib/mysh/user_input/parse.rb +65 -0
  23. data/lib/mysh/{smart_source.rb → user_input/smart_source.rb} +2 -1
  24. data/lib/mysh/user_input.rb +39 -0
  25. data/lib/mysh/version.rb +1 -1
  26. data/lib/mysh.rb +14 -25
  27. data/mysh.reek +112 -0
  28. data/rakefile.rb +1 -1
  29. data/tests/my_shell_tests.rb +13 -13
  30. metadata +22 -19
  31. data/lib/mysh/commands/command_path.rb +0 -15
  32. data/lib/mysh/support/format.rb +0 -50
  33. data/lib/mysh/support/handlebars.rb +0 -30
  34. data/lib/mysh/support/manage.rb +0 -26
  35. data/lib/mysh/support/parse.rb +0 -70
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d1ba8a1e0f44a3d96f072108abe9bba55aefebf
4
- data.tar.gz: f5811e7f8aeba916be0b9dccd4864282314cb0f0
3
+ metadata.gz: 5357ad0b3384cce27b2e53bf392dde640f5b21ec
4
+ data.tar.gz: a84115ea89d9d586dae27bdbcaaffcff3060dbe0
5
5
  SHA512:
6
- metadata.gz: a81089b6f2bf37d034eb8d1860774999e531ac34042d371dc9348090639f232c4d177cbfaee70220e24f68bd7a4e63420d39ba5fa983d14a1d3034a92d0bf432
7
- data.tar.gz: 57a32385f635325c19ea131c79c1e8463ec7f405ff11a4e35934a64bae57baf54b07c8069c7050ce9f4b7f353b492d289f66fc854d97399437ce8c8c1439bef3
6
+ metadata.gz: a9336ae69aaf60132e7af4eaea1e09e3d98e1aded2daa1239508a843069f9729f539b80244bd5d59aa455bd9847f54f199968acb5bbe68b6a5c260e61dab1f0e
7
+ data.tar.gz: 52a3ceecf702aeb6c135f93c99249e2a3c8bd78c2f46a1a6a286bb438ad4e0223e2c0d23b8c920f2011d653d411809a7a04a5c1f60daa6b00da84cc7c5dd1725
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Mysh
2
2
 
3
+ mysh -- A ruby based command line shell program.
4
+
5
+ ## Background
6
+
3
7
  Inspired by the excellent article "Writing a Shell in 25 Lines of Ruby Code"
4
8
  I thought it would be fun to experiment with that concept and see if it could
5
9
  be taken further.
@@ -47,8 +51,8 @@ presented with a command prompt:
47
51
  $ mysh
48
52
  mysh>
49
53
 
50
-
51
- This prompt can be used to execute four sorts of commands:
54
+ Then start entering some commands! This prompt can be used to execute four
55
+ sorts of commands:
52
56
 
53
57
  * Ruby expressions, which are preceded by the equal (=) sign.
54
58
  * Internal commands that are processed directly by mysh
@@ -70,6 +74,7 @@ A few noteworthy methods exist that facilitate use of Ruby expressions:
70
74
  ```
71
75
  reset Reset the execution environment to the default state.
72
76
  result Returns the result of the previous expression.
77
+ x.lineage Get the class lineage of the object x.
73
78
  vls "mask" List modules with version info. The optional mask string value is
74
79
  used to filter for modules containing that string.
75
80
  ```
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+
3
+ #Object monkey patch for the mysh lineage command.
4
+ class Object
5
+
6
+ #Get the lineage of this object.
7
+ def lineage
8
+ klass = self.class
9
+ klass.name + " instance < " + klass.lineage
10
+ end
11
+
12
+ end
13
+
14
+ #Class monkey patch for the mysh lineage command.
15
+ class Class
16
+
17
+ #Get the lineage of this class.
18
+ def lineage
19
+ klass = superclass
20
+ name + (klass ? " < " + klass.lineage : "")
21
+ end
22
+
23
+ end
24
+
@@ -3,6 +3,8 @@
3
3
  require 'pp'
4
4
  require 'mathn'
5
5
 
6
+ require_relative 'expression/lineage'
7
+
6
8
  #* expression.rb -- mysh ruby expression processor.
7
9
  #<br>Endemic Code Smells
8
10
  #* :reek:Attribute
@@ -14,82 +16,82 @@ module Mysh
14
16
  include Math
15
17
 
16
18
  #These variables live here so that they are not part of the mysh
17
- #execution environment. This provides a little isolation.
19
+ #execution environment. This provides a little bit of isolation.
18
20
  class << self
21
+ #The result of the last expression evaluated.
19
22
  attr_accessor :result
20
- attr_accessor :exec_fiber
23
+
24
+ #The execution binding used for ruby expressions.
21
25
  attr_accessor :exec_binding
22
- attr_accessor :exec_result
23
26
  end
24
27
 
25
28
  #Set up a new execution environment
26
- #<br>Note
27
- #* The exec_result variable is needed because Fiber.yield messes up the
28
- # return value on an exception, even if that exception is handled.
29
- def initialize
30
- ExecHost.result = nil
31
-
32
- ExecHost.exec_fiber = Fiber.new do |cmd|
33
- ExecHost.exec_binding = binding
34
-
35
- while true
36
- begin
37
- ExecHost.exec_result = ExecHost.exec_binding.eval(cmd)
38
- rescue StandardError, ScriptError => err
39
- ExecHost.exec_result = "#{err.class.to_s}: #{err}"
40
- end
41
-
42
- cmd = Fiber.yield
43
- end
44
- end
29
+ def initialize(owner)
30
+ @owner = owner
31
+ mysh_binding
32
+ end
45
33
 
34
+ #Create a binding for mysh to execute expressions in.
35
+ def mysh_binding
36
+ ExecHost.exec_binding = binding
46
37
  end
47
38
 
48
39
  #Process an expression.
49
40
  def execute(str)
50
41
  if str.start_with?('=')
51
- do_execute(do_build(str))
52
- else
53
- false
42
+ do_execute(str)
43
+ :expression
54
44
  end
55
45
  end
56
46
 
57
- #Reset the state of the execution host.
58
- #<br>Endemic Code Smells
59
- #* :reek:UtilityFunction
60
- def reset
61
- Mysh.reset_host
62
- nil
63
- end
64
-
65
47
  private
66
48
 
67
- #Gather up the full string of the expression to evaluate.
68
- #<br>Endemic Code Smells
69
- #* :reek:TooManyStatements
70
- def do_build(str)
71
- if /\\\s*$/ =~ str
72
- parms = {prompt: 'mysh\\',
73
- auto_source: MiniReadline::QuotedFileFolderSource}
74
-
75
- do_build($PREMATCH + "\n" + Mysh.input.readline(parms))
76
- else
77
- str
78
- end
79
- end
80
-
81
- #Execute the string
49
+ #Do the actual work of executing an expression.
82
50
  def do_execute(str)
83
- ExecHost.exec_fiber.resume("ExecHost.result #{str}")
84
- result = ExecHost.exec_result
51
+ self.result = exec_binding.eval(str[1..-1])
85
52
  send(result ? :pp : :puts, result)
86
- :expression
53
+ rescue Interrupt, StandardError, ScriptError => err
54
+ puts "#{err.class.to_s}: #{err}"
55
+ end
56
+
57
+ #Get the execute binding.
58
+ def exec_binding
59
+ self.class.exec_binding
87
60
  end
88
61
 
89
62
  #Get the previous result
90
63
  def result
91
64
  self.class.result
92
65
  end
66
+
67
+ #Set the current result
68
+ def result=(value)
69
+ self.class.result=value
70
+ end
71
+
72
+ #Reset the state of the execution host.
73
+ def reset
74
+ @owner.reset_host
75
+ nil
76
+ end
77
+
78
+ #A proxy for instance_eval.
79
+ def instance_eval(str)
80
+ exec_binding.eval(str)
81
+ end
82
+ end
83
+
84
+ #Try to execute the string as a ruby expression.
85
+ def self.try_execute_ruby_expression(str)
86
+ if str.start_with?('=')
87
+ @exec_host.execute(str)
88
+ :expression
89
+ end
90
+ end
91
+
92
+ #Reset the state of the execution hosting environment.
93
+ def self.reset_host
94
+ @exec_host = ExecHost.new(self)
93
95
  end
94
96
 
95
97
  end
@@ -4,7 +4,7 @@
4
4
  module Mysh
5
5
 
6
6
  #Try to execute as a Ruby program.
7
- def self.ruby_execute(str)
7
+ def self.try_execute_external_ruby(str)
8
8
  cmd = str.split[0]
9
9
 
10
10
  if cmd && File.extname(cmd) == '.rb'
@@ -1,10 +1,10 @@
1
1
  # coding: utf-8
2
2
 
3
- #* support/frame.rb -- The abstract frame of mysh internal commands.
3
+ #* internal/action.rb -- The framework of mysh internal actions.
4
4
  module Mysh
5
5
 
6
- #The mysh internal command instance data and methods.
7
- class Command
6
+ #The mysh internal action class.
7
+ class Action
8
8
  #The name of the command.
9
9
  attr_reader :name
10
10
 
@@ -25,7 +25,7 @@ module Mysh
25
25
  end
26
26
 
27
27
  #Get information about the command.
28
- def command_info
28
+ def action_info
29
29
  [@name, @description]
30
30
  end
31
31
 
@@ -1,17 +1,17 @@
1
1
  # coding: utf-8
2
2
 
3
- #* support/command_pool.rb -- A managed hash of mysh commands.
3
+ #* internal/action_pool.rb -- A managed hash of mysh commands.
4
4
  module Mysh
5
5
 
6
- #* support/command_pool.rb -- A managed hash of mysh commands.
7
- class CommandPool
6
+ #* A managed hash of mysh commands.
7
+ class ActionPool
8
8
 
9
9
  #Create a new command pool
10
10
  def initialize(&default_action)
11
11
  @pool = {}
12
12
 
13
13
  if default_action
14
- @pool.default = Command.new("", "", &default_action)
14
+ @pool.default = Action.new("", "", &default_action)
15
15
  end
16
16
  end
17
17
 
@@ -24,7 +24,7 @@ module Mysh
24
24
  def add(name, description, &action)
25
25
  split_name = name.split[0] || ""
26
26
 
27
- @pool[split_name] = Command.new(name, description, &action)
27
+ @pool[split_name] = Action.new(name, description, &action)
28
28
  end
29
29
 
30
30
  #Add an alias for an existing command.
@@ -36,14 +36,14 @@ module Mysh
36
36
  split_name = new_name.split[0]
37
37
 
38
38
  @pool[split_name] =
39
- Command.new(new_name, command.description, &command.action)
39
+ Action.new(new_name, command.description, &command.action)
40
40
  end
41
41
 
42
42
  #Get information on all commands.
43
- def command_info
43
+ def actions_info
44
44
  @pool
45
45
  .values
46
- .map {|command| command.command_info }
46
+ .map {|action| action.action_info }
47
47
  .sort {|first, second| first[0] <=> second[0] }
48
48
  end
49
49
 
@@ -0,0 +1,15 @@
1
+ # coding: utf-8
2
+
3
+ #* internal/actions/actions_path.rb -- A convenient hook to the command folder.
4
+ module Mysh
5
+
6
+ #* internal/actions/actions_path.rb -- A convenient hook to the command folder.
7
+ class Action
8
+
9
+ #Capture this folder's name.
10
+ ACTIONS_PATH = File.dirname(__FILE__) + "/"
11
+
12
+ end
13
+
14
+ end
15
+
@@ -1,10 +1,10 @@
1
1
  # coding: utf-8
2
2
 
3
- #* commands/cd.rb -- The mysh internal cd command.
3
+ #* internal/actions/cd.rb -- The mysh internal cd command.
4
4
  module Mysh
5
5
 
6
6
  #* cd.rb -- The mysh internal cd command.
7
- class Command
7
+ class Action
8
8
  #Add the cd command to the library.
9
9
  desc = ['Change directory to the optional <dir> parameter',
10
10
  'and then display the current working directory.']
@@ -1,10 +1,10 @@
1
1
  # coding: utf-8
2
2
 
3
- #* commands/exit.rb -- The mysh internal exit command.
3
+ #* internal/actions/exit.rb -- The mysh internal exit command.
4
4
  module Mysh
5
5
 
6
6
  #* exit.rb -- The mysh internal exit command.
7
- class Command
7
+ class Action
8
8
  #Add the exit command to the library.
9
9
  COMMANDS.add('exit', 'Exit mysh.') do |args|
10
10
  raise MiniReadlineEOI
@@ -1,30 +1,30 @@
1
1
  # coding: utf-8
2
2
 
3
- #* commands/help.rb -- The mysh internal help command.
3
+ #* internal/actions/help.rb -- The mysh internal help command.
4
4
  module Mysh
5
5
 
6
6
  #* help.rb -- The mysh internal help command.
7
- class Command
7
+ class Action
8
8
 
9
9
  # Help topics
10
- HELP = CommandPool.new do |args|
10
+ HELP = ActionPool.new do |args|
11
11
  puts "No help found for #{args[0].inspect}."
12
12
  end
13
13
 
14
14
  HELP.add("", "General help on mysh.") do |args|
15
- show_file('help.txt')
15
+ show_handlebar_file(ACTIONS_PATH + 'help.txt')
16
16
  end
17
17
 
18
18
  HELP.add("math", "Help on mysh math functions.") do |args|
19
- show_file('help_math.txt')
19
+ show_handlebar_file(ACTIONS_PATH + 'help_math.txt')
20
20
  end
21
21
 
22
22
  HELP.add("=", "Help on mysh ruby expressions.") do |args|
23
- show_file('help_expr.txt')
23
+ show_handlebar_file(ACTIONS_PATH + 'help_expr.txt')
24
24
  end
25
25
 
26
26
  HELP.add("help", "Help on mysh help.") do |args|
27
- show_file('help_help.txt')
27
+ show_handlebar_file(ACTIONS_PATH + 'help_help.txt')
28
28
  end
29
29
 
30
30
  HELP.add_alias('?', 'help')
@@ -24,7 +24,7 @@ the name of an internal command is processed internally by mysh.
24
24
 
25
25
  The following set of commands are supported:
26
26
 
27
- {{ format_items(COMMANDS.command_info).join("\n") }}
27
+ {{ format_items(Mysh::COMMANDS.actions_info).join("\n") }}
28
28
 
29
29
  3) External commands:
30
30
 
@@ -15,6 +15,7 @@ A few noteworthy special methods exist:
15
15
 
16
16
  =reset Reset the execution environment to the default state.
17
17
  =result Returns the result of the previous expression.
18
+ =x.lineage Get the class lineage of the object x.
18
19
  =vls "mask" List modules with version info. The optional mask string value is
19
20
  used to filter for modules containing that string.
20
21
 
@@ -5,5 +5,5 @@ or an optional specified topic.
5
5
 
6
6
  The available help topics are:
7
7
 
8
- {{ format_items(HELP.command_info).join("\n") }}
8
+ {{ format_items(HELP.actions_info).join("\n") }}
9
9
 
@@ -1,10 +1,10 @@
1
1
  # coding: utf-8
2
2
 
3
- #* commands/history.rb -- The mysh internal history command.
3
+ #* internal/actions/history.rb -- The mysh internal history command.
4
4
  module Mysh
5
5
 
6
6
  #* history.rb -- The mysh internal history command.
7
- class Command
7
+ class Action
8
8
  #Add the exit command to the library.
9
9
  COMMANDS.add('history', 'Display the mysh command history.') do |args|
10
10
  history = Mysh.input.history
@@ -1,10 +1,10 @@
1
1
  # coding: utf-8
2
2
 
3
- #* support/decorate.rb -- mysh internal file name beauty treatments.
3
+ #* internal/decorate.rb -- mysh internal file name beauty treatments.
4
4
  module Mysh
5
5
 
6
6
  #The mysh internal file name beauty treatments.
7
- class Command
7
+ class Action
8
8
 
9
9
  #Make the file name fit the local system.
10
10
  def decorate(name)
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+
3
+ #The mysh internal command class level report formatting.
4
+ class Object
5
+
6
+ private
7
+
8
+ #Display an array of items.
9
+ def display_items(items)
10
+ puts format_items(items)
11
+ puts
12
+ end
13
+
14
+ #Format an array of items.
15
+ #<br>Endemic Code Smells
16
+ #* :reek:FeatureEnvy
17
+ def format_items(items, buffer=[])
18
+ #Determine the width of the tag area.
19
+ tag_width = items.max_by {|item| item[0].length}[0].length + 1
20
+
21
+ #Display the information.
22
+ items.each {|item| format_item(item, buffer, tag_width) }
23
+
24
+ buffer
25
+ end
26
+
27
+ #Display one item.
28
+ def display_item(item, tag_width=nil)
29
+ puts format_item(item, [], tag_width)
30
+ puts
31
+ end
32
+
33
+ #Format one item.
34
+ #<br>Endemic Code Smells
35
+ #* :reek:UtilityFunction
36
+ def format_item(item, buffer=[], tag_width)
37
+ tag = item[0]
38
+
39
+ item[1].each do |detail|
40
+ buffer << "#{tag.ljust(tag_width)} #{detail}"
41
+ tag = ""
42
+ end
43
+
44
+ buffer
45
+ end
46
+
47
+ end
48
+
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+
3
+ #* internal/manage.rb -- Manage mysh internal commands.
4
+ module Mysh
5
+
6
+ #Set up the command library hash.
7
+ COMMANDS = ActionPool.new
8
+
9
+ #Parse a command string for use by commands.
10
+ def self.parse_command(str)
11
+ result = Mysh.parse_args(str.chomp)
12
+
13
+ [COMMANDS[result.shift], result]
14
+ end
15
+
16
+ #Execute an internal command
17
+ def self.execute(str)
18
+ unless str[0] == ' '
19
+ command, args = parse_command(str.chomp)
20
+
21
+ if (command)
22
+ command.execute(args)
23
+ :internal
24
+ end
25
+ end
26
+ end
27
+
28
+ #Try to execute the string as an internal command.
29
+ def self.try_execute_internal_command(str)
30
+ execute(str)
31
+ end
32
+
33
+ end
34
+
data/lib/mysh/internal.rb CHANGED
@@ -1,13 +1,11 @@
1
1
  # coding: utf-8
2
2
 
3
- require_relative 'commands/command_path'
4
- require_relative 'support/frame'
5
- require_relative 'support/command_pool'
6
- require_relative 'support/manage'
7
- require_relative 'support/parse'
8
- require_relative 'support/format'
9
- require_relative 'support/decorate'
10
- require_relative 'support/handlebars'
3
+ require_relative 'internal/actions/actions_path'
4
+ require_relative 'internal/action'
5
+ require_relative 'internal/action_pool'
6
+ require_relative 'internal/manage'
7
+ require_relative 'internal/format'
8
+ require_relative 'internal/decorate'
11
9
 
12
10
  #Load up the internal commands!
13
- Dir[Mysh::Command::COMMAND_PATH + '*.rb'].each {|file| require file }
11
+ Dir[Mysh::Action::ACTIONS_PATH + '*.rb'].each {|file| require file }
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+
3
+ #* user_input/handlebars.rb -- Handlebar embedded ruby support.
4
+ class Object
5
+
6
+ #Process a string with embedded Ruby code.
7
+ def eval_handlebars(str)
8
+ loop do
9
+ pre_match, match, post_match = str.partition(/{{.*?}}/m)
10
+
11
+ return pre_match if match.empty?
12
+
13
+ result = instance_eval(code = match[2...-2])
14
+
15
+ str = pre_match + (result unless code.end_with?("#")).to_s + post_match
16
+ end
17
+ end
18
+
19
+ #Expand a file with embedded ruby handlebars.
20
+ def eval_handlebar_file(name)
21
+ eval_handlebars(IO.read(name))
22
+ end
23
+
24
+ #Show a file with embedded ruby handlebars.
25
+ def show_handlebar_file(name)
26
+ puts eval_handlebar_file(name)
27
+ rescue Interrupt, StandardError, ScriptError => err
28
+ puts "Error in file: #{name}\n#{err.class}: #{err}"
29
+ end
30
+
31
+ end
32
+
33
+
@@ -0,0 +1,65 @@
1
+ # coding: utf-8
2
+
3
+ #* user_input/parse.rb -- mysh general parser.
4
+ module Mysh
5
+
6
+ #Parse a string into components.
7
+ #<br>Endemic Code Smells
8
+ #* :reek:TooManyStatements
9
+ def self.parse_args(input)
10
+ result, read_point = [], input.chars.each
11
+
12
+ loop do
13
+ next_parse_char = read_point.next
14
+
15
+ if next_parse_char == '"'
16
+ result.concat(get_string(read_point))
17
+ elsif next_parse_char != ' '
18
+ result.concat(get_parameter(next_parse_char, read_point))
19
+ end
20
+ end
21
+
22
+ result
23
+ end
24
+
25
+ private
26
+
27
+ #Get a string parameter.
28
+ #<br>Endemic Code Smells
29
+ #* :reek:TooManyStatements
30
+ def self.get_string(read_point)
31
+ result = ""
32
+
33
+ loop do
34
+ next_str_char = read_point.next
35
+
36
+ break if next_str_char == '"'
37
+
38
+ result << next_str_char
39
+ end
40
+
41
+ [result]
42
+ end
43
+
44
+ #Get a parameter.
45
+ #<br>Endemic Code Smells
46
+ #* :reek:TooManyStatements
47
+ def self.get_parameter(first_char, read_point)
48
+ result = first_char
49
+
50
+ loop do
51
+ next_parm_char = read_point.next
52
+
53
+ if next_parm_char == '"'
54
+ return [result].concat(get_string(read_point))
55
+ elsif next_parm_char == ' '
56
+ break
57
+ else
58
+ result << next_parm_char
59
+ end
60
+ end
61
+
62
+ [result]
63
+ end
64
+
65
+ end
@@ -8,13 +8,14 @@ module Mysh
8
8
 
9
9
  #Create a new file/folder auto-data source. NOP
10
10
  def initialize(options)
11
+ @prefix = options[:prefix]
11
12
  @auto_source = MiniReadline::AutoFileSource.new(options)
12
13
  @quote_source = MiniReadline::QuotedFileFolderSource.new(options)
13
14
  end
14
15
 
15
16
  #Construct a new data list for auto-complete
16
17
  def rebuild(str)
17
- @active_source = str[0] == '=' ? @quote_source : @auto_source
18
+ @active_source = (@prefix || str[0]) == '=' ? @quote_source : @auto_source
18
19
  @active_source.rebuild(str)
19
20
  end
20
21
 
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'user_input/smart_source'
4
+ require_relative 'user_input/parse'
5
+ require_relative 'user_input/handlebars'
6
+
7
+ #* user_input.rb -- Get a command from the user.
8
+ module Mysh
9
+
10
+ #Get one command from the user.
11
+ def self.get_command
12
+ input = @input.readline(prompt: 'mysh>')
13
+ get_command_extra(input)
14
+ end
15
+
16
+ private
17
+
18
+ #Get any continuations of the inputs
19
+ def self.get_command_extra(str)
20
+ if /\\\s*$/ =~ str
21
+ parms = {prompt: 'mysh\\', prefix: str[0] }
22
+ get_command_extra($PREMATCH + "\n" + @input.readline(parms))
23
+ else
24
+ str
25
+ end
26
+
27
+ end
28
+
29
+ #Get the user input ready.
30
+ def self.init_input
31
+ @input = MiniReadline::Readline.new(history: true,
32
+ eoi_detect: true,
33
+ auto_complete: true,
34
+ auto_source: SmartSource)
35
+ end
36
+
37
+ end
38
+
39
+
data/lib/mysh/version.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Mysh
4
4
  #The version string of MY SHell.
5
- VERSION = "0.1.14"
5
+ VERSION = "0.1.15"
6
6
 
7
7
  #A brief summary of this gem.
8
8
  SUMMARY = "mysh -- a Ruby inspired command shell."
data/lib/mysh.rb CHANGED
@@ -7,7 +7,7 @@ require 'mini_readline'
7
7
  require 'vls'
8
8
  require 'in_array'
9
9
 
10
- require_relative 'mysh/smart_source'
10
+ require_relative 'mysh/user_input'
11
11
  require_relative 'mysh/expression'
12
12
  require_relative 'mysh/internal'
13
13
  require_relative 'mysh/external_ruby'
@@ -25,36 +25,25 @@ module Mysh
25
25
  #<br>Endemic Code Smells
26
26
  #* :reek:TooManyStatements
27
27
  def self.run
28
- init_run
28
+ reset_host
29
+ init_input
29
30
 
30
31
  loop do
31
- input = @input.readline(prompt: 'mysh>')
32
-
33
32
  begin
34
- @exec_host.execute(input) ||
35
- Command.execute(input) ||
36
- ruby_execute(input) ||
37
- system(input)
38
- rescue Interrupt => err
39
- puts err
40
- end
41
- end
33
+ input = @exec_host.eval_handlebars(get_command)
42
34
 
43
- rescue Interrupt, MiniReadlineEOI
44
- end
35
+ try_execute_ruby_expression(input) ||
36
+ try_execute_internal_command(input) ||
37
+ try_execute_external_ruby(input) ||
38
+ system(input)
45
39
 
46
- #Set up for the run command.
47
- def self.init_run
48
- reset_host
49
- @input = MiniReadline::Readline.new(history: true,
50
- eoi_detect: true,
51
- auto_complete: true,
52
- auto_source: SmartSource)
53
- end
40
+ rescue MiniReadlineEOI
41
+ break
54
42
 
55
- #Reset the state of the execution hosting environment.
56
- def self.reset_host
57
- @exec_host = ExecHost.new
43
+ rescue Interrupt, StandardError, ScriptError => err
44
+ puts err, err.backtrace
45
+ end
46
+ end
58
47
  end
59
48
 
60
49
  end
data/mysh.reek ADDED
@@ -0,0 +1,112 @@
1
+ ---
2
+ Attribute:
3
+ enabled: false
4
+ exclude: []
5
+ BooleanParameter:
6
+ enabled: true
7
+ exclude: []
8
+ ClassVariable:
9
+ enabled: true
10
+ exclude: []
11
+ ControlParameter:
12
+ enabled: true
13
+ exclude: []
14
+ DataClump:
15
+ enabled: true
16
+ exclude: []
17
+ max_copies: 2
18
+ min_clump_size: 2
19
+ DuplicateMethodCall:
20
+ enabled: true
21
+ exclude: []
22
+ max_calls: 1
23
+ allow_calls: []
24
+ FeatureEnvy:
25
+ enabled: true
26
+ exclude: []
27
+ IrresponsibleModule:
28
+ enabled: true
29
+ exclude: []
30
+ LongParameterList:
31
+ enabled: true
32
+ exclude: []
33
+ max_params: 3
34
+ overrides:
35
+ initialize:
36
+ max_params: 5
37
+ LongYieldList:
38
+ enabled: true
39
+ exclude: []
40
+ max_params: 3
41
+ NestedIterators:
42
+ enabled: true
43
+ exclude: []
44
+ max_allowed_nesting: 1
45
+ ignore_iterators: []
46
+ NilCheck:
47
+ enabled: true
48
+ exclude: []
49
+ PrimaDonnaMethod:
50
+ enabled: true
51
+ exclude: []
52
+ RepeatedConditional:
53
+ enabled: true
54
+ exclude: []
55
+ max_ifs: 2
56
+ TooManyInstanceVariables:
57
+ enabled: true
58
+ exclude: []
59
+ max_instance_variables: 9
60
+ TooManyMethods:
61
+ enabled: true
62
+ exclude: []
63
+ max_methods: 25
64
+ TooManyStatements:
65
+ enabled: true
66
+ exclude:
67
+ - initialize
68
+ max_statements: 5
69
+ UncommunicativeMethodName:
70
+ enabled: true
71
+ exclude: []
72
+ reject:
73
+ - !ruby/regexp /^[a-z]$/
74
+ - !ruby/regexp /[0-9]$/
75
+ - !ruby/regexp /[A-Z]/
76
+ accept: []
77
+ UncommunicativeModuleName:
78
+ enabled: true
79
+ exclude: []
80
+ reject:
81
+ - !ruby/regexp /^.$/
82
+ - !ruby/regexp /[0-9]$/
83
+ accept:
84
+ - Inline::C
85
+ UncommunicativeParameterName:
86
+ enabled: true
87
+ exclude: []
88
+ reject:
89
+ - !ruby/regexp /^.$/
90
+ - !ruby/regexp /[0-9]$/
91
+ - !ruby/regexp /[A-Z]/
92
+ - !ruby/regexp /^_/
93
+ accept: []
94
+ UncommunicativeVariableName:
95
+ enabled: true
96
+ exclude: []
97
+ reject:
98
+ - !ruby/regexp /^.$/
99
+ - !ruby/regexp /[0-9]$/
100
+ - !ruby/regexp /[A-Z]/
101
+ accept:
102
+ - _
103
+ UnusedParameters:
104
+ enabled: true
105
+ exclude: []
106
+ UtilityFunction:
107
+ enabled: true
108
+ exclude: []
109
+ max_helper_calls: 1
110
+ UnusedPrivateMethod:
111
+ enabled: false
112
+ exclude: []
data/rakefile.rb CHANGED
@@ -10,7 +10,7 @@ RDoc::Task.new do |rdoc|
10
10
  rdoc.rdoc_dir = "rdoc"
11
11
 
12
12
  #List out all the files to be documented.
13
- rdoc.rdoc_files.include("lib/**/*.rb", "license.txt", "README.md")
13
+ rdoc.rdoc_files.include("lib/**/*.rb", "license.txt")
14
14
 
15
15
  #Set a title.
16
16
  rdoc.options << '--title' << 'My Shell Gem Internals'
@@ -13,32 +13,32 @@ class MyShellTester < Minitest::Test
13
13
 
14
14
  def test_that_module_entities_exists
15
15
  assert_equal(Module, Mysh.class)
16
- assert_equal(Class, Mysh::Command.class)
16
+ assert_equal(Class, Mysh::Action.class)
17
17
  assert_equal(Class, Mysh::ExecHost.class)
18
18
  end
19
19
 
20
20
  def test_for_internal_commands
21
- assert(Mysh::Command::COMMANDS['exit'], "The exit command is missing.")
22
- assert(Mysh::Command::COMMANDS['quit'], "The quit command is missing.")
21
+ assert(Mysh::COMMANDS['exit'], "The exit command is missing.")
22
+ assert(Mysh::COMMANDS['quit'], "The quit command is missing.")
23
23
 
24
- assert(Mysh::Command::COMMANDS['history'], "The history command is missing.")
25
- assert(Mysh::Command::COMMANDS['!'], "The ! command is missing.")
24
+ assert(Mysh::COMMANDS['history'], "The history command is missing.")
25
+ assert(Mysh::COMMANDS['!'], "The ! command is missing.")
26
26
 
27
- assert(Mysh::Command::COMMANDS['help'], "The help command is missing.")
28
- assert(Mysh::Command::COMMANDS['?'], "The ? command is missing.")
27
+ assert(Mysh::COMMANDS['help'], "The help command is missing.")
28
+ assert(Mysh::COMMANDS['?'], "The ? command is missing.")
29
29
 
30
- assert(Mysh::Command::COMMANDS['cd'], "The cd command is missing.")
31
- assert(Mysh::Command::COMMANDS['pwd'], "The pwd command is missing.")
30
+ assert(Mysh::COMMANDS['cd'], "The cd command is missing.")
31
+ assert(Mysh::COMMANDS['pwd'], "The pwd command is missing.")
32
32
 
33
- assert_raises { Mysh::Command::COMMANDS.add_alias('blam', 'shazzam') }
33
+ assert_raises { Mysh::COMMANDS.add_alias('blam', 'shazzam') }
34
34
  end
35
35
 
36
36
  def test_handlebars
37
- cmd = Mysh::Command::COMMANDS['help']
38
-
39
37
  assert_equal("ABC 123 DEF",
40
- cmd.expand_handlebars("ABC {{ (1..3).to_a.join }} DEF"))
38
+ eval_handlebars("ABC {{ (1..3).to_a.join }} DEF"))
41
39
 
40
+ assert_equal("ABC", eval_handlebars("{{ 'ABC' }}"))
41
+ assert_equal("", eval_handlebars("{{ 'ABC' #}}"))
42
42
  end
43
43
 
44
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mysh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.1.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Camilleri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-30 00:00:00.000000000 Z
11
+ date: 2016-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -151,28 +151,31 @@ files:
151
151
  - README.md
152
152
  - bin/mysh
153
153
  - lib/mysh.rb
154
- - lib/mysh/commands/cd.rb
155
- - lib/mysh/commands/command_path.rb
156
- - lib/mysh/commands/exit.rb
157
- - lib/mysh/commands/help.rb
158
- - lib/mysh/commands/help.txt
159
- - lib/mysh/commands/help_expr.txt
160
- - lib/mysh/commands/help_help.txt
161
- - lib/mysh/commands/help_math.txt
162
- - lib/mysh/commands/history.rb
163
154
  - lib/mysh/expression.rb
155
+ - lib/mysh/expression/lineage.rb
164
156
  - lib/mysh/external_ruby.rb
165
157
  - lib/mysh/internal.rb
166
- - lib/mysh/smart_source.rb
167
- - lib/mysh/support/command_pool.rb
168
- - lib/mysh/support/decorate.rb
169
- - lib/mysh/support/format.rb
170
- - lib/mysh/support/frame.rb
171
- - lib/mysh/support/handlebars.rb
172
- - lib/mysh/support/manage.rb
173
- - lib/mysh/support/parse.rb
158
+ - lib/mysh/internal/action.rb
159
+ - lib/mysh/internal/action_pool.rb
160
+ - lib/mysh/internal/actions/actions_path.rb
161
+ - lib/mysh/internal/actions/cd.rb
162
+ - lib/mysh/internal/actions/exit.rb
163
+ - lib/mysh/internal/actions/help.rb
164
+ - lib/mysh/internal/actions/help.txt
165
+ - lib/mysh/internal/actions/help_expr.txt
166
+ - lib/mysh/internal/actions/help_help.txt
167
+ - lib/mysh/internal/actions/help_math.txt
168
+ - lib/mysh/internal/actions/history.rb
169
+ - lib/mysh/internal/decorate.rb
170
+ - lib/mysh/internal/format.rb
171
+ - lib/mysh/internal/manage.rb
172
+ - lib/mysh/user_input.rb
173
+ - lib/mysh/user_input/handlebars.rb
174
+ - lib/mysh/user_input/parse.rb
175
+ - lib/mysh/user_input/smart_source.rb
174
176
  - lib/mysh/version.rb
175
177
  - mysh.gemspec
178
+ - mysh.reek
176
179
  - rakefile.rb
177
180
  - reek.txt
178
181
  - test.rb
@@ -1,15 +0,0 @@
1
- # coding: utf-8
2
-
3
- #* commands/command_path.rb -- A convenient hook to the command folder.
4
- module Mysh
5
-
6
- #* commands/command_path.rb -- A convenient hook to the command folder.
7
- class Command
8
-
9
- #Capture this folder's name.
10
- COMMAND_PATH = File.dirname(__FILE__) + "/"
11
-
12
- end
13
-
14
- end
15
-
@@ -1,50 +0,0 @@
1
- # coding: utf-8
2
-
3
- #* support/format.rb -- Format mysh internal reports.
4
- module Mysh
5
-
6
- #The mysh internal command class level report formatting.
7
- class Command
8
-
9
- #Display an array of items.
10
- def display_items(items)
11
- puts format_items(items)
12
- puts
13
- end
14
-
15
- #Format an array of items.
16
- #<br>Endemic Code Smells
17
- #* :reek:FeatureEnvy
18
- def format_items(items, buffer=[])
19
- #Determine the width of the tag area.
20
- tag_width = items.max_by {|item| item[0].length}[0].length + 1
21
-
22
- #Display the information.
23
- items.each {|item| format_item(item, buffer, tag_width) }
24
-
25
- buffer
26
- end
27
-
28
- #Display one item.
29
- def display_item(item, tag_width=nil)
30
- puts format_item(item, [], tag_width)
31
- puts
32
- end
33
-
34
- #Format one item.
35
- #<br>Endemic Code Smells
36
- #* :reek:UtilityFunction
37
- def format_item(item, buffer=[], tag_width)
38
- tag = item[0]
39
-
40
- item[1].each do |detail|
41
- buffer << "#{tag.ljust(tag_width)} #{detail}"
42
- tag = ""
43
- end
44
-
45
- buffer
46
- end
47
-
48
- end
49
- end
50
-
@@ -1,30 +0,0 @@
1
- # coding: utf-8
2
-
3
- #* support/handlebars.rb -- Allow text files to embed ruby code.
4
- module Mysh
5
-
6
- #The mysh embedded ruby text formatting.
7
- class Command
8
-
9
- #Process a string with embedded Ruby code.
10
- def expand_handlebars(str)
11
- loop do
12
- pre_match, match, post_match = str.partition(/{{.*?}}/m)
13
-
14
- return pre_match if match.empty?
15
-
16
- str = pre_match + eval(match[2...-2]) + post_match
17
- end
18
- end
19
-
20
- #Show a file with embedded ruby handlebars.
21
- def show_file(name)
22
- puts expand_handlebars(IO.read(full_name = COMMAND_PATH + name))
23
- rescue StandardError, ScriptError => err
24
- puts "Error in file: #{full_name}\n#{err.class}: #{err}"
25
- end
26
-
27
- end
28
-
29
- end
30
-
@@ -1,26 +0,0 @@
1
- # coding: utf-8
2
-
3
- #* support/manage.rb -- Manage mysh internal commands.
4
- module Mysh
5
-
6
- #The mysh internal command class level data and methods.
7
- class Command
8
-
9
- #Set up the command library hash.
10
- COMMANDS = CommandPool.new
11
-
12
- #Execute an internal command
13
- def self.execute(str)
14
- unless str[0] == ' '
15
- command, args = parse(str.chomp)
16
-
17
- if (command)
18
- command.execute(args)
19
- :internal
20
- end
21
- end
22
- end
23
-
24
- end
25
- end
26
-
@@ -1,70 +0,0 @@
1
- # coding: utf-8
2
-
3
- #* support/parse.rb -- mysh internal command parser.
4
- module Mysh
5
-
6
- #The mysh internal command instance data and methods.
7
- class Command
8
-
9
- #Parse a command string for use by commands.
10
- #<br>Endemic Code Smells
11
- #* :reek:TooManyStatements
12
- def self.parse(input)
13
- result, read_point = [], input.chars.each
14
-
15
- loop do
16
- next_parse_char = read_point.next
17
-
18
- if next_parse_char == '"'
19
- result.concat(get_string(read_point))
20
- elsif next_parse_char != ' '
21
- result.concat(get_parameter(next_parse_char, read_point))
22
- end
23
- end
24
-
25
- [COMMANDS[result.shift], result]
26
- end
27
-
28
- private
29
-
30
- #Get a string parameter.
31
- #<br>Endemic Code Smells
32
- #* :reek:TooManyStatements
33
- def self.get_string(read_point)
34
- result = ""
35
-
36
- loop do
37
- next_str_char = read_point.next
38
-
39
- break if next_str_char == '"'
40
-
41
- result << next_str_char
42
- end
43
-
44
- [result]
45
- end
46
-
47
- #Get a parameter.
48
- #<br>Endemic Code Smells
49
- #* :reek:TooManyStatements
50
- def self.get_parameter(first_char, read_point)
51
- result = first_char
52
-
53
- loop do
54
- next_parm_char = read_point.next
55
-
56
- if next_parm_char == '"'
57
- return [result].concat(get_string(read_point))
58
- elsif next_parm_char == ' '
59
- break
60
- else
61
- result << next_parm_char
62
- end
63
- end
64
-
65
- [result]
66
- end
67
-
68
- end
69
-
70
- end