mysh 0.1.16 → 0.1.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -12
- data/lib/mysh.rb +1 -1
- data/lib/mysh/expression.rb +5 -13
- data/lib/mysh/expression/lineage.rb +2 -2
- data/lib/mysh/internal.rb +1 -1
- data/lib/mysh/internal/action.rb +19 -6
- data/lib/mysh/internal/action_pool.rb +14 -10
- data/lib/mysh/internal/actions/actions_path.rb +2 -2
- data/lib/mysh/internal/actions/gls.rb +19 -0
- data/lib/mysh/internal/actions/help.rb +8 -5
- data/lib/mysh/internal/actions/{help.txt → help/help.txt} +2 -2
- data/lib/mysh/internal/actions/{help_expr.txt → help/help_expr.txt} +4 -5
- data/lib/mysh/internal/actions/{help_help.txt → help/help_help.txt} +1 -1
- data/lib/mysh/internal/actions/{help_math.txt → help/help_math.txt} +0 -0
- data/lib/mysh/internal/actions/show.rb +27 -0
- data/lib/mysh/internal/actions/vls.rb +22 -0
- data/lib/mysh/internal/format.rb +12 -17
- data/lib/mysh/internal/format/array.rb +56 -0
- data/lib/mysh/internal/format/bullets.rb +15 -71
- data/lib/mysh/internal/format/columns.rb +122 -0
- data/lib/mysh/internal/format/object.rb +13 -0
- data/lib/mysh/internal/format/string.rb +46 -0
- data/lib/mysh/internal/manage.rb +7 -7
- data/lib/mysh/user_input/handlebars.rb +1 -1
- data/lib/mysh/version.rb +2 -2
- data/samples/show.txt +11 -0
- data/{test.rb → samples/test.rb} +0 -0
- data/tests/my_shell_tests.rb +66 -0
- metadata +16 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3c002087bb9289b3a21c696dbec4cf5eb0c2aad
|
4
|
+
data.tar.gz: 89e63a29c1c26f4f84372cfa34995e820c3cf726
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eab8e85ede1899c6d87a457141a7664421fb9171e5175b5b3c82ac9181d4ce6838db03fe3c9f4334bbf61c9ca190524cfeddf859edefdd70c8171d65d296822a
|
7
|
+
data.tar.gz: 0a8e2f49879875648bf8435e9011c4188f4f05802b95a20a6045e95b6f5583b33b9cf1f526579f91c90d33871c387ff6796eccf154635710d3a3ad26a04b5130
|
data/README.md
CHANGED
@@ -19,7 +19,7 @@ Since that name was available, it would seem that no one had yet written a
|
|
19
19
|
shell program at this level of narcissism.
|
20
20
|
|
21
21
|
The mysh is available as both a stand-alone CLI program and for use as a
|
22
|
-
command shell within Ruby applications and Rails web sites.
|
22
|
+
command shell within Ruby applications and (eventually) Rails web sites.
|
23
23
|
|
24
24
|
See the original article at:
|
25
25
|
(http://www.blackbytes.info/2016/07/writing-a-shell-in-ruby/)
|
@@ -75,8 +75,6 @@ A few noteworthy methods exist that facilitate use of Ruby expressions:
|
|
75
75
|
reset Reset the execution environment to the default state.
|
76
76
|
result Returns the result of the previous expression.
|
77
77
|
x.lineage Get the class lineage of the object x.
|
78
|
-
vls "mask" List modules with version info. The optional mask string value is
|
79
|
-
used to filter for modules containing that string.
|
80
78
|
```
|
81
79
|
|
82
80
|
The Ruby expression execution environment has direct access to many advanced
|
@@ -127,15 +125,18 @@ Internal commands are recognized by name and are executed by mysh directly.
|
|
127
125
|
The following set of commands are supported:
|
128
126
|
|
129
127
|
```
|
130
|
-
!
|
131
|
-
?
|
132
|
-
cd <dir>
|
133
|
-
|
134
|
-
exit
|
135
|
-
help
|
136
|
-
history
|
137
|
-
pwd
|
138
|
-
quit
|
128
|
+
! Display the mysh command history.
|
129
|
+
? <topic> Display help information for mysh with an optional topic.
|
130
|
+
cd <dir> Change directory to the optional <dir> parameter and then display
|
131
|
+
the current working directory.
|
132
|
+
exit Exit mysh.
|
133
|
+
help <topic> Display help information for mysh with an optional topic.
|
134
|
+
history Display the mysh command history.
|
135
|
+
pwd Display the current working directory.
|
136
|
+
quit Exit mysh.
|
137
|
+
show <file> Display a text file with optional embedded handlebars.
|
138
|
+
vls <mask> Display the loaded modules, matching the optional mask, that have
|
139
|
+
version info.
|
139
140
|
```
|
140
141
|
Of note is the command "help help" which provides a list of available topics.
|
141
142
|
|
@@ -238,7 +239,13 @@ module Mysh
|
|
238
239
|
end
|
239
240
|
end
|
240
241
|
```
|
242
|
+
#### Add Method Return Values
|
243
|
+
Both the add and add_alias methods return the newly created action instance.
|
244
|
+
This may be useful, for example, if it is desired to add singleton methods to
|
245
|
+
the action in order to extend functionality.
|
241
246
|
|
247
|
+
Note that when an action is aliased, none of the singleton methods are copied
|
248
|
+
across and these will have to be regenerated in the new action object.
|
242
249
|
|
243
250
|
## Contributing
|
244
251
|
|
data/lib/mysh.rb
CHANGED
data/lib/mysh/expression.rb
CHANGED
@@ -34,24 +34,16 @@ module Mysh
|
|
34
34
|
ExecHost.exec_binding = binding
|
35
35
|
end
|
36
36
|
|
37
|
-
#Process an expression.
|
38
|
-
def execute(str)
|
39
|
-
if str.start_with?('=')
|
40
|
-
do_execute(str)
|
41
|
-
:expression
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
37
|
#Do the actual work of executing an expression.
|
48
|
-
def
|
38
|
+
def execute(str)
|
49
39
|
self.result = exec_binding.eval(str[1..-1])
|
50
40
|
send(result ? :pp : :puts, result)
|
51
41
|
rescue Interrupt, StandardError, ScriptError => err
|
52
42
|
puts "#{err.class.to_s}: #{err}"
|
53
43
|
end
|
54
44
|
|
45
|
+
private
|
46
|
+
|
55
47
|
#Get the execute binding.
|
56
48
|
def exec_binding
|
57
49
|
self.class.exec_binding
|
@@ -73,8 +65,8 @@ module Mysh
|
|
73
65
|
nil
|
74
66
|
end
|
75
67
|
|
76
|
-
#
|
77
|
-
def
|
68
|
+
#Evaluate the string in the my shell context.
|
69
|
+
def mysh_eval(str)
|
78
70
|
exec_binding.eval(str)
|
79
71
|
end
|
80
72
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
#Object monkey patch for the mysh lineage
|
3
|
+
#Object monkey patch for the mysh lineage method.
|
4
4
|
class Object
|
5
5
|
|
6
6
|
#Get the lineage of this object.
|
@@ -11,7 +11,7 @@ class Object
|
|
11
11
|
|
12
12
|
end
|
13
13
|
|
14
|
-
#Class monkey patch for the mysh lineage
|
14
|
+
#Class monkey patch for the mysh lineage method.
|
15
15
|
class Class
|
16
16
|
|
17
17
|
#Get the lineage of this class.
|
data/lib/mysh/internal.rb
CHANGED
data/lib/mysh/internal/action.rb
CHANGED
@@ -5,30 +5,43 @@ module Mysh
|
|
5
5
|
|
6
6
|
#The mysh internal action class.
|
7
7
|
class Action
|
8
|
-
#The name of the
|
8
|
+
#The name of the action.
|
9
9
|
attr_reader :name
|
10
10
|
|
11
|
-
#The description of the
|
11
|
+
#The description of the action.
|
12
12
|
attr_reader :description
|
13
13
|
|
14
|
-
#The action of the
|
14
|
+
#The action of the action.
|
15
15
|
attr_reader :action
|
16
16
|
|
17
|
-
#Setup an internal
|
17
|
+
#Setup an internal action.
|
18
18
|
def initialize(name, description, &action)
|
19
19
|
@name, @description, @action = name, description.in_array, action
|
20
|
+
@exec_binding = mysh_binding
|
20
21
|
end
|
21
22
|
|
22
|
-
#Execute the
|
23
|
+
#Execute the action.
|
23
24
|
def execute(args)
|
24
25
|
instance_exec(args, &@action)
|
25
26
|
end
|
26
27
|
|
27
|
-
#Get information about the
|
28
|
+
#Get information about the action.
|
28
29
|
def action_info
|
29
30
|
[@name].concat(@description)
|
30
31
|
end
|
31
32
|
|
33
|
+
private
|
34
|
+
|
35
|
+
#Create a binding for mysh to execute expressions in.
|
36
|
+
def mysh_binding
|
37
|
+
binding
|
38
|
+
end
|
39
|
+
|
40
|
+
#Evaluate the string in the my shell context.
|
41
|
+
def mysh_eval(str)
|
42
|
+
@exec_binding.eval(str)
|
43
|
+
end
|
44
|
+
|
32
45
|
end
|
33
46
|
|
34
47
|
end
|
@@ -6,9 +6,12 @@ module Mysh
|
|
6
6
|
#* A managed hash of mysh actions.
|
7
7
|
class ActionPool
|
8
8
|
|
9
|
+
#The name of this action pool.
|
10
|
+
attr_reader :pool_name
|
11
|
+
|
9
12
|
#Create a new action pool
|
10
|
-
def initialize(&default_action)
|
11
|
-
@pool = {}
|
13
|
+
def initialize(pool_name, &default_action)
|
14
|
+
@pool_name, @pool = pool_name, {}
|
12
15
|
|
13
16
|
if default_action
|
14
17
|
@pool.default = Action.new("", "", &default_action)
|
@@ -24,27 +27,28 @@ module Mysh
|
|
24
27
|
def add(name, description, &action)
|
25
28
|
split_name = name.split[0] || ""
|
26
29
|
|
30
|
+
if @pool.has_key?(split_name)
|
31
|
+
fail "Add error: Action #{split_name.inspect} already exists in #{pool_name}."
|
32
|
+
end
|
33
|
+
|
27
34
|
@pool[split_name] = Action.new(name, description, &action)
|
28
35
|
end
|
29
36
|
|
30
37
|
#Add an alias for an existing action.
|
31
38
|
def add_alias(new_name, old_name)
|
32
|
-
unless (action = @pool[old_name])
|
33
|
-
fail "
|
39
|
+
unless (action = @pool[old_name.split[0]])
|
40
|
+
fail "Alias error: Action #{old_name.inspect} not found in #{pool_name}"
|
34
41
|
end
|
35
42
|
|
36
|
-
|
37
|
-
|
38
|
-
@pool[split_name] =
|
39
|
-
Action.new(new_name, action.description, &action.action)
|
43
|
+
add(new_name, action.description, &action.action)
|
40
44
|
end
|
41
45
|
|
42
46
|
#Get information on all actions.
|
43
47
|
def actions_info
|
44
48
|
@pool
|
45
49
|
.values
|
46
|
-
.map {|action| action.action_info
|
47
|
-
.sort {|first, second| first[0] <=> second[0]
|
50
|
+
.map {|action| action.action_info}
|
51
|
+
.sort {|first, second| first[0] <=> second[0]}
|
48
52
|
end
|
49
53
|
|
50
54
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
#* internal/actions/actions_path.rb -- A convenient hook to the
|
3
|
+
#* internal/actions/actions_path.rb -- A convenient hook to the actions folder.
|
4
4
|
module Mysh
|
5
5
|
|
6
|
-
#* internal/actions/actions_path.rb -- A convenient hook to the
|
6
|
+
#* internal/actions/actions_path.rb -- A convenient hook to the actions folder.
|
7
7
|
class Action
|
8
8
|
|
9
9
|
#Capture this folder's name.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#* internal/actions/gls.rb -- The mysh gem ls command.
|
4
|
+
module Mysh
|
5
|
+
|
6
|
+
#* internal/actions/gls.rb -- The mysh gem ls command.
|
7
|
+
class Action
|
8
|
+
|
9
|
+
desc = 'Display the loaded ruby gems.'
|
10
|
+
|
11
|
+
COMMANDS.add('gls <mask>', desc) do |args|
|
12
|
+
puts Gem.loaded_specs.keys.sort.join(" ")
|
13
|
+
puts
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
@@ -7,24 +7,24 @@ module Mysh
|
|
7
7
|
class Action
|
8
8
|
|
9
9
|
# Help topics
|
10
|
-
HELP = ActionPool.new do |args|
|
10
|
+
HELP = ActionPool.new("HELP") 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_handlebar_file(ACTIONS_PATH + 'help.txt')
|
15
|
+
show_handlebar_file(ACTIONS_PATH + 'help/help.txt')
|
16
16
|
end
|
17
17
|
|
18
18
|
HELP.add("math", "Help on mysh math functions.") do |args|
|
19
|
-
show_handlebar_file(ACTIONS_PATH + 'help_math.txt')
|
19
|
+
show_handlebar_file(ACTIONS_PATH + 'help/help_math.txt')
|
20
20
|
end
|
21
21
|
|
22
22
|
HELP.add("=", "Help on mysh ruby expressions.") do |args|
|
23
|
-
show_handlebar_file(ACTIONS_PATH + 'help_expr.txt')
|
23
|
+
show_handlebar_file(ACTIONS_PATH + 'help/help_expr.txt')
|
24
24
|
end
|
25
25
|
|
26
26
|
HELP.add("help", "Help on mysh help.") do |args|
|
27
|
-
show_handlebar_file(ACTIONS_PATH + 'help_help.txt')
|
27
|
+
show_handlebar_file(ACTIONS_PATH + 'help/help_help.txt')
|
28
28
|
end
|
29
29
|
|
30
30
|
HELP.add_alias('?', 'help')
|
@@ -42,3 +42,6 @@ module Mysh
|
|
42
42
|
|
43
43
|
end
|
44
44
|
|
45
|
+
#Load up the extra help actions!
|
46
|
+
Dir[Mysh::Action::ACTIONS_PATH + 'help/*.rb'].each {|file| require file }
|
47
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
mysh
|
1
|
+
mysh version: {{ Mysh::VERSION }}
|
2
2
|
|
3
3
|
The mysh is a shell program inspired by the Ruby programming language in the
|
4
4
|
same way the csh was inspired by the "C" programming language.
|
@@ -11,7 +11,7 @@ In mysh, commands fall into one of three broad categories. There are:
|
|
11
11
|
2) Internal mysh commands are processed within mysh itself. The following set
|
12
12
|
of commands are supported:
|
13
13
|
|
14
|
-
{{
|
14
|
+
{{ Mysh::COMMANDS.actions_info.mysh_bulletize.join("\n") }}
|
15
15
|
|
16
16
|
3) All other commands are executed by the system using the standard shell or
|
17
17
|
the appropriate ruby interpreter. If the command has a '.rb' extension, it
|
@@ -16,11 +16,10 @@ string parameters.
|
|
16
16
|
|
17
17
|
A few special methods exist:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
used to filter for modules containing that string.
|
19
|
+
reset Reset the execution environment to the default state.
|
20
|
+
result Returns the result of the previous expression.
|
21
|
+
x.lineage Get the class lineage of the object or class x.
|
22
|
+
|
24
23
|
|
25
24
|
Also included are a number of math methods. See '? math' for more on these.
|
26
25
|
|
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#* internal/actions/show.rb -- The mysh show command.
|
4
|
+
module Mysh
|
5
|
+
|
6
|
+
#* internal/actions/show.rb -- The mysh show command.
|
7
|
+
class Action
|
8
|
+
|
9
|
+
# The base help command.
|
10
|
+
desc = 'Display a text file with optional embedded handlebars.'
|
11
|
+
|
12
|
+
COMMANDS.add('show <file>', desc) do |args|
|
13
|
+
file_name = args.shift
|
14
|
+
|
15
|
+
@exec_binding = binding
|
16
|
+
|
17
|
+
if file_name
|
18
|
+
show_handlebar_file(file_name)
|
19
|
+
else
|
20
|
+
puts "Error: A text file must be specified."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#* internal/actions/vls.rb -- The mysh module version ls command.
|
4
|
+
module Mysh
|
5
|
+
|
6
|
+
#* internal/actions/vls.rb -- The mysh module version ls command.
|
7
|
+
class Action
|
8
|
+
|
9
|
+
desc = 'Display the loaded modules, matching the optional mask, ' +
|
10
|
+
'that have version info.'
|
11
|
+
|
12
|
+
COMMANDS.add('vls <mask>', desc) do |args|
|
13
|
+
mask = args.shift || /./
|
14
|
+
|
15
|
+
puts VersionLS.vls(mask).mysh_bulletize.join("\n")
|
16
|
+
puts
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
data/lib/mysh/internal/format.rb
CHANGED
@@ -1,24 +1,19 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
#The mysh internal command class level report formatting.
|
6
|
-
class Object
|
7
|
-
|
8
|
-
private
|
3
|
+
#* internal/format.rb - Some formatting facilities for mysh.
|
4
|
+
module Mysh
|
9
5
|
|
10
|
-
#
|
11
|
-
|
12
|
-
items.puts_mysh_bullets
|
13
|
-
puts
|
14
|
-
end
|
6
|
+
#Assume an 80 column working area for formatting.
|
7
|
+
PAGE_WIDTH = 80
|
15
8
|
|
16
|
-
#
|
17
|
-
|
18
|
-
#* :reek:FeatureEnvy :reek:UtilityFunction
|
19
|
-
def format_items(items)
|
20
|
-
items.mysh_bulletize
|
21
|
-
end
|
9
|
+
#Assume an in infinite page length for formatting.
|
10
|
+
PAGE_LENGTH = false
|
22
11
|
|
23
12
|
end
|
24
13
|
|
14
|
+
require_relative 'format/bullets'
|
15
|
+
require_relative 'format/columns'
|
16
|
+
require_relative 'format/array'
|
17
|
+
require_relative 'format/string'
|
18
|
+
require_relative 'format/object'
|
19
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#* internal/format/array.rb - Support for displaying an array formatted neatly.
|
4
|
+
class Array
|
5
|
+
|
6
|
+
# Columns ========================================================
|
7
|
+
|
8
|
+
#Print out the array with efficient columns.
|
9
|
+
def puts_mysh_columns(page_width = Mysh::PAGE_WIDTH)
|
10
|
+
|
11
|
+
puts format_mysh_columns(page_width)
|
12
|
+
end
|
13
|
+
|
14
|
+
#Convert the array to strings with efficient columns.
|
15
|
+
#<br>Returns
|
16
|
+
#* An array of pages, that is, arrays of strings.
|
17
|
+
def format_mysh_columns(page_width = Mysh::PAGE_WIDTH)
|
18
|
+
builder = Mysh::ColumnizedPage.new(Mysh::PAGE_LENGTH, page_width)
|
19
|
+
|
20
|
+
self.each {|item| builder.add(item)}
|
21
|
+
|
22
|
+
builder.render
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :format_description :format_mysh_columns
|
26
|
+
|
27
|
+
#Get the widest element of an array.
|
28
|
+
#<br>Returns
|
29
|
+
#* The width of the widest string in the array.
|
30
|
+
def mysh_column_width
|
31
|
+
(self.max_by {|item| item.length}).length
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# Bullets ========================================================
|
36
|
+
|
37
|
+
#Print out the array as bullet points.
|
38
|
+
def puts_mysh_bullets
|
39
|
+
puts mysh_bulletize
|
40
|
+
end
|
41
|
+
|
42
|
+
#Convert the array to strings with bullet points.
|
43
|
+
#<br>
|
44
|
+
#* An array of strings
|
45
|
+
def mysh_bulletize(page_width = Mysh::PAGE_WIDTH)
|
46
|
+
builder = Mysh::BulletPoints.new(page_width)
|
47
|
+
|
48
|
+
self.each do |pair|
|
49
|
+
builder.add(*pair)
|
50
|
+
end
|
51
|
+
|
52
|
+
builder.render
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -6,33 +6,21 @@ module Mysh
|
|
6
6
|
#A class to display data in bullet points.
|
7
7
|
class BulletPoints
|
8
8
|
|
9
|
-
#Assume an 80 column working area.
|
10
|
-
PAGE_WIDTH = 80
|
11
|
-
|
12
9
|
#Prepare a blank slate.
|
13
|
-
def initialize
|
10
|
+
def initialize(page_width)
|
11
|
+
@page_width = page_width
|
14
12
|
@bullet_data = []
|
15
13
|
end
|
16
14
|
|
17
|
-
#Add
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
if raw_item.empty?
|
23
|
-
bullet = ["*"]
|
24
|
-
items = raw_bullet.in_array
|
25
|
-
else
|
26
|
-
bullet = [raw_bullet]
|
27
|
-
items = raw_item.in_array
|
28
|
-
end
|
29
|
-
|
30
|
-
items.each_index do |index|
|
31
|
-
@bullet_data << [(bullet[index] || "").to_s, items[index].to_s]
|
15
|
+
#Add items to these bullet points.
|
16
|
+
def add(bullet, *items)
|
17
|
+
items.each do |item|
|
18
|
+
@bullet_data << [bullet.to_s, item]
|
19
|
+
bullet = ""
|
32
20
|
end
|
33
21
|
end
|
34
22
|
|
35
|
-
#Render the
|
23
|
+
#Render the bullet points as an array of strings.
|
36
24
|
def render
|
37
25
|
@key_length, results = get_key_length, []
|
38
26
|
|
@@ -46,68 +34,24 @@ module Mysh
|
|
46
34
|
|
47
35
|
private
|
48
36
|
|
49
|
-
#
|
37
|
+
#Allowing for a trailing space, how large is the largest bullet?
|
50
38
|
def get_key_length
|
51
|
-
(@bullet_data.max_by {|line| line[0].length})[0].length
|
39
|
+
(@bullet_data.max_by {|line| line[0].length})[0].length + 1
|
52
40
|
end
|
53
41
|
|
54
42
|
#Render one bullet point.
|
55
|
-
#<br>Endemic Code Smells
|
56
|
-
#* :reek:DuplicateMethodCall :reek:TooManyStatements
|
57
43
|
def render_bullet(key, item)
|
58
|
-
result
|
59
|
-
input = item.split(' ').each
|
60
|
-
temp = key.ljust(len = @key_length)
|
44
|
+
result = []
|
61
45
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
while len >= PAGE_WIDTH
|
66
|
-
result << temp.slice!(0, PAGE_WIDTH - 1)
|
67
|
-
temp = blank_key + ' ' + temp
|
68
|
-
len = temp.length
|
69
|
-
end
|
70
|
-
|
71
|
-
if ((len += word.length) >= PAGE_WIDTH) && !pass_one
|
72
|
-
result << temp
|
73
|
-
temp = blank_key + word
|
74
|
-
len = temp.length
|
75
|
-
else
|
76
|
-
temp << word
|
77
|
-
pass_one = false
|
78
|
-
end
|
46
|
+
item.format_description(@page_width - @key_length - 1).each do |desc_line|
|
47
|
+
result << key.ljust(@key_length) + desc_line
|
48
|
+
key = ""
|
79
49
|
end
|
80
50
|
|
81
|
-
result
|
82
|
-
end
|
83
|
-
|
84
|
-
#Generate a blank bullet key
|
85
|
-
def blank_key
|
86
|
-
' ' * @key_length
|
51
|
+
result
|
87
52
|
end
|
88
53
|
|
89
54
|
end
|
90
55
|
|
91
56
|
end
|
92
57
|
|
93
|
-
#Bullet point support in the Array class.
|
94
|
-
class Array
|
95
|
-
#Print out the array as bullet points.
|
96
|
-
def puts_mysh_bullets
|
97
|
-
puts mysh_bulletize
|
98
|
-
end
|
99
|
-
|
100
|
-
#Convert the array to strings with bullet points.
|
101
|
-
#<br>
|
102
|
-
#* An array of arrays of strings
|
103
|
-
def mysh_bulletize
|
104
|
-
builder = Mysh::BulletPoints.new
|
105
|
-
|
106
|
-
self.each do |pair|
|
107
|
-
builder.add(*pair)
|
108
|
-
end
|
109
|
-
|
110
|
-
builder.render
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#* internal/format/bullets.rb - Print out data in neat columns.
|
4
|
+
module Mysh
|
5
|
+
|
6
|
+
#A class to display data in columns.
|
7
|
+
class ColumnizedPage
|
8
|
+
#Prepare a blank page.
|
9
|
+
def initialize(page_length, page_width)
|
10
|
+
@page_length, @page_width = page_length, page_width
|
11
|
+
@page_data = []
|
12
|
+
end
|
13
|
+
|
14
|
+
#Add an item to this page.
|
15
|
+
#<br>Returns
|
16
|
+
#* The number if items that did not fit in the page.
|
17
|
+
def add(raw_item)
|
18
|
+
item = raw_item.to_s
|
19
|
+
fail "Item too large to fit." unless item.length < @page_width
|
20
|
+
|
21
|
+
if (column = find_next_column)
|
22
|
+
@page_data[column] << item
|
23
|
+
else
|
24
|
+
@page_data << [item]
|
25
|
+
end
|
26
|
+
|
27
|
+
adjust_configuration
|
28
|
+
end
|
29
|
+
|
30
|
+
#Render the page as an array of strings.
|
31
|
+
def render
|
32
|
+
results, column_widths = [], get_column_widths
|
33
|
+
|
34
|
+
rows.times { |row_index| results << render_row(row_index, column_widths)}
|
35
|
+
|
36
|
+
@page_data.clear
|
37
|
+
results
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
#Get the widths of all columns
|
43
|
+
def get_column_widths
|
44
|
+
@page_data.map {|column| column.mysh_column_width}
|
45
|
+
end
|
46
|
+
|
47
|
+
#Render a single row of data.
|
48
|
+
def render_row(row_index, widths)
|
49
|
+
@page_data.each_with_index.map do |column, index|
|
50
|
+
column[row_index].to_s.ljust(widths[index])
|
51
|
+
end.join(" ")
|
52
|
+
end
|
53
|
+
|
54
|
+
#Make sure the page fits within its boundaries.
|
55
|
+
#<br>Returns
|
56
|
+
#* The number if items that did not fit in the page.
|
57
|
+
def adjust_configuration
|
58
|
+
while total_width >= @page_width
|
59
|
+
return @page_data.pop.length if rows == @page_length
|
60
|
+
add_a_row
|
61
|
+
end
|
62
|
+
|
63
|
+
0
|
64
|
+
end
|
65
|
+
|
66
|
+
#Add a row to the page, moving items as needed.
|
67
|
+
def add_a_row
|
68
|
+
target = rows + 1
|
69
|
+
pool, @page_data = @page_data.flatten, []
|
70
|
+
|
71
|
+
until pool.empty?
|
72
|
+
@page_data << pool.shift(target)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#Compute the total width of all of the columns.
|
77
|
+
#<br>Returns
|
78
|
+
#* The sum of the widths of the widest items of each column plus a space
|
79
|
+
# between each of those columns.
|
80
|
+
def total_width
|
81
|
+
if empty?
|
82
|
+
0
|
83
|
+
else
|
84
|
+
#The starting point, @page_data.length-1, represents the spaces needed
|
85
|
+
#between the columns. So N columns means N-1 spaces.
|
86
|
+
@page_data.inject(@page_data.length-1) do |sum, column|
|
87
|
+
sum + column.mysh_column_width
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#Does the data fit on the page?
|
93
|
+
def fits?
|
94
|
+
total_width < @page_width
|
95
|
+
end
|
96
|
+
|
97
|
+
#How many rows are currently in this page?
|
98
|
+
def rows
|
99
|
+
empty? ? 0 : @page_data[0].length
|
100
|
+
end
|
101
|
+
|
102
|
+
#Is this page empty?
|
103
|
+
def empty?
|
104
|
+
@page_data.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
#Which column will receive the next item?
|
108
|
+
#<br>Returns
|
109
|
+
#* The index of the first non-filled column or nil if none found.
|
110
|
+
def find_next_column
|
111
|
+
(1...(@page_data.length)).each do |index|
|
112
|
+
if @page_data[index].length < @page_data[index-1].length
|
113
|
+
return index
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#* internal/format/object.rb - Support for displaying data formatted neatly.
|
4
|
+
class Object
|
5
|
+
|
6
|
+
#Create a bullet point description from this object.
|
7
|
+
def format_description(max_width)
|
8
|
+
self.to_s.format_description(max_width)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#* internal/format/string.rb - Support for displaying data formatted neatly.
|
4
|
+
class String
|
5
|
+
|
6
|
+
#Create a bullet point description from this string.
|
7
|
+
def format_description(max_width)
|
8
|
+
do_format_description(split(' ').each, max_width)
|
9
|
+
end
|
10
|
+
|
11
|
+
#Do the formatting legwork.
|
12
|
+
def do_format_description(input, max_width)
|
13
|
+
result, build = [], ""
|
14
|
+
|
15
|
+
loop do
|
16
|
+
build = build.split_if_over(input.next, max_width, result)
|
17
|
+
.split_if_huge(max_width, result)
|
18
|
+
end
|
19
|
+
|
20
|
+
result << build
|
21
|
+
end
|
22
|
+
|
23
|
+
#Split if adding a word goes over a little.
|
24
|
+
def split_if_over(word, max_width, buffer)
|
25
|
+
word.prepend(" ") unless self.empty?
|
26
|
+
word_len = word.length
|
27
|
+
|
28
|
+
if (length + word_len) >= max_width && word_len < max_width
|
29
|
+
buffer << self
|
30
|
+
word.lstrip
|
31
|
+
else
|
32
|
+
self + word
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
#Split up a overlong blob of text.
|
38
|
+
def split_if_huge(max_width, buffer)
|
39
|
+
while length >= max_width
|
40
|
+
buffer << slice!(0, max_width)
|
41
|
+
end
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
data/lib/mysh/internal/manage.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
#* internal/manage.rb -- Manage mysh internal commands.
|
4
4
|
module Mysh
|
5
5
|
|
6
|
-
#Set up the command
|
7
|
-
COMMANDS = ActionPool.new
|
6
|
+
#Set up the command action pool.
|
7
|
+
COMMANDS = ActionPool.new("COMMANDS")
|
8
8
|
|
9
9
|
#Parse a command string for use by commands.
|
10
10
|
def self.parse_command(str)
|
@@ -13,19 +13,19 @@ module Mysh
|
|
13
13
|
[COMMANDS[result.shift], result]
|
14
14
|
end
|
15
15
|
|
16
|
-
#Execute an internal command
|
16
|
+
#Execute the action of an internal command.
|
17
17
|
def self.execute(str)
|
18
18
|
unless str[0] == ' '
|
19
|
-
|
19
|
+
action, args = parse_command(str.chomp)
|
20
20
|
|
21
|
-
if (
|
22
|
-
|
21
|
+
if (action)
|
22
|
+
action.execute(args)
|
23
23
|
:internal
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
#Try to execute the string as an internal
|
28
|
+
#Try to execute the string as an internal action.
|
29
29
|
def self.try_execute_internal_command(str)
|
30
30
|
execute(str)
|
31
31
|
end
|
data/lib/mysh/version.rb
CHANGED
data/samples/show.txt
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
This is a sample show file.
|
2
|
+
|
3
|
+
{{ a = 5 #}}a = {{ a.to_s }}
|
4
|
+
Args = {{ args.inspect }}
|
5
|
+
|
6
|
+
{{ count = 100 #}}
|
7
|
+
|
8
|
+
{{ [["Numbers", Array.new(count){|i| (i+1)} ],
|
9
|
+
["Squares", Array.new(count){|i| (i+1)*(i+1)} ],
|
10
|
+
["Cubes", Array.new(count){|i| (i+1)*(i+1)*(i+1)} ]].mysh_bulletize.join("\n") }}
|
11
|
+
|
data/{test.rb → samples/test.rb}
RENAMED
File without changes
|
data/tests/my_shell_tests.rb
CHANGED
@@ -11,10 +11,23 @@ class MyShellTester < Minitest::Test
|
|
11
11
|
#Track mini-test progress.
|
12
12
|
include MinitestVisible
|
13
13
|
|
14
|
+
#Evaluate the string in a mysh context.
|
15
|
+
def mysh_eval(str)
|
16
|
+
@mysh_binding ||= binding
|
17
|
+
|
18
|
+
@mysh_binding.eval(str)
|
19
|
+
end
|
20
|
+
|
14
21
|
def test_that_module_entities_exists
|
22
|
+
assert_equal(String, Mysh::VERSION.class)
|
23
|
+
|
15
24
|
assert_equal(Module, Mysh.class)
|
16
25
|
assert_equal(Class, Mysh::Action.class)
|
26
|
+
assert_equal(Class, Mysh::ActionPool.class)
|
17
27
|
assert_equal(Class, Mysh::ExecHost.class)
|
28
|
+
|
29
|
+
assert_equal(Mysh::ActionPool, Mysh::COMMANDS.class)
|
30
|
+
assert_equal(Mysh::ActionPool, Mysh::Action::HELP.class)
|
18
31
|
end
|
19
32
|
|
20
33
|
def test_for_internal_commands
|
@@ -44,4 +57,57 @@ class MyShellTester < Minitest::Test
|
|
44
57
|
assert_equal("{{A}}", eval_handlebars("{{ '{'+'{A}'+'}' }}"))
|
45
58
|
end
|
46
59
|
|
60
|
+
def test_command_parsing
|
61
|
+
assert_equal([], Mysh.parse_args(""))
|
62
|
+
|
63
|
+
assert_equal(["1", "2", "3"], Mysh.parse_args("1 2 3"))
|
64
|
+
|
65
|
+
assert_equal(["1", "Trump", "loses", "election", "3"],
|
66
|
+
Mysh.parse_args("1 Trump loses election 3"))
|
67
|
+
|
68
|
+
assert_equal(["1", "Trump loses election", "3"],
|
69
|
+
Mysh.parse_args('1 "Trump loses election" 3'))
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_the_lineage_method
|
74
|
+
assert_equal("String instance < String < Object < BasicObject",
|
75
|
+
"Hello".lineage)
|
76
|
+
|
77
|
+
assert_equal("Fixnum instance < Fixnum < Integer < Numeric < Object < BasicObject",
|
78
|
+
(4).lineage)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_some_formatting
|
82
|
+
result =
|
83
|
+
["1 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 85 89 93 97 ",
|
84
|
+
"2 6 10 14 18 22 26 30 34 38 42 46 50 54 58 62 66 70 74 78 82 86 90 94 98 ",
|
85
|
+
"3 7 11 15 19 23 27 31 35 39 43 47 51 55 59 63 67 71 75 79 83 87 91 95 99 ",
|
86
|
+
"4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100"]
|
87
|
+
|
88
|
+
assert_equal(result, (1..100).to_a.format_mysh_columns)
|
89
|
+
|
90
|
+
data =
|
91
|
+
[["key_largo", "/long_folder_name_one/long_folder_name_two/long_folder_name_three/fine_descriptive_name"],
|
92
|
+
["key_west", "Semper ubi sub ubi. Semper ubi sub ubi. Semper ubi sub ubi. Semper ubi sub ubi. "],
|
93
|
+
["counting", Array.new(100) {|i| i} ],
|
94
|
+
["pie", Math::PI]
|
95
|
+
]
|
96
|
+
|
97
|
+
result =
|
98
|
+
["key_largo /long_folder_name_one/long_folder_name_two/long_folder_name_three/fin",
|
99
|
+
" e_descriptive_name",
|
100
|
+
"key_west Semper ubi sub ubi. Semper ubi sub ubi. Semper ubi sub ubi. Semper",
|
101
|
+
" ubi sub ubi.",
|
102
|
+
"counting 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95",
|
103
|
+
" 1 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 86 91 96",
|
104
|
+
" 2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 82 87 92 97",
|
105
|
+
" 3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 83 88 93 98",
|
106
|
+
" 4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 84 89 94 99",
|
107
|
+
"pie 3.141592653589793"
|
108
|
+
]
|
109
|
+
|
110
|
+
assert_equal(result, data.mysh_bulletize)
|
111
|
+
end
|
112
|
+
|
47
113
|
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.
|
4
|
+
version: 0.1.17
|
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-10-
|
11
|
+
date: 2016-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -160,15 +160,22 @@ files:
|
|
160
160
|
- lib/mysh/internal/actions/actions_path.rb
|
161
161
|
- lib/mysh/internal/actions/cd.rb
|
162
162
|
- lib/mysh/internal/actions/exit.rb
|
163
|
+
- lib/mysh/internal/actions/gls.rb
|
163
164
|
- 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
|
165
|
+
- lib/mysh/internal/actions/help/help.txt
|
166
|
+
- lib/mysh/internal/actions/help/help_expr.txt
|
167
|
+
- lib/mysh/internal/actions/help/help_help.txt
|
168
|
+
- lib/mysh/internal/actions/help/help_math.txt
|
168
169
|
- lib/mysh/internal/actions/history.rb
|
170
|
+
- lib/mysh/internal/actions/show.rb
|
171
|
+
- lib/mysh/internal/actions/vls.rb
|
169
172
|
- lib/mysh/internal/decorate.rb
|
170
173
|
- lib/mysh/internal/format.rb
|
174
|
+
- lib/mysh/internal/format/array.rb
|
171
175
|
- lib/mysh/internal/format/bullets.rb
|
176
|
+
- lib/mysh/internal/format/columns.rb
|
177
|
+
- lib/mysh/internal/format/object.rb
|
178
|
+
- lib/mysh/internal/format/string.rb
|
172
179
|
- lib/mysh/internal/manage.rb
|
173
180
|
- lib/mysh/user_input.rb
|
174
181
|
- lib/mysh/user_input/handlebars.rb
|
@@ -179,7 +186,8 @@ files:
|
|
179
186
|
- mysh.reek
|
180
187
|
- rakefile.rb
|
181
188
|
- reek.txt
|
182
|
-
-
|
189
|
+
- samples/show.txt
|
190
|
+
- samples/test.rb
|
183
191
|
- tests/my_shell_tests.rb
|
184
192
|
homepage: http://teuthida-technologies.com/
|
185
193
|
licenses:
|
@@ -204,6 +212,6 @@ rubyforge_project:
|
|
204
212
|
rubygems_version: 2.2.3
|
205
213
|
signing_key:
|
206
214
|
specification_version: 4
|
207
|
-
summary: mysh -- a Ruby inspired command shell.
|
215
|
+
summary: mysh -- a Ruby inspired command line shell.
|
208
216
|
test_files: []
|
209
217
|
has_rdoc:
|