treerepl 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README +11 -0
- data/lib/treerepl.rb +30 -0
- data/lib/treerepl/cmds.rb +156 -0
- data/lib/treerepl/repl.rb +149 -0
- data/lib/treerepl/tree.rb +71 -0
- metadata +73 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Jeffrey Palm
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Provides a way to create command line itnerfaces for web services with
|
2
|
+
inherent tree-like structure. There are two examples:
|
3
|
+
|
4
|
+
- githubrepl
|
5
|
+
- rdiorepl
|
6
|
+
|
7
|
+
They both require some other libraries. The basic idea is this:
|
8
|
+
|
9
|
+
1. Define the tree model for your service
|
10
|
+
2. Define the mapping from tree to service
|
11
|
+
3. See the example and go from there
|
data/lib/treerepl.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
2
|
+
|
3
|
+
# stdlib
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
# internal requires
|
7
|
+
require 'treerepl/tree'
|
8
|
+
require 'treerepl/cmds'
|
9
|
+
require 'treerepl/repl'
|
10
|
+
|
11
|
+
module TreeRepl
|
12
|
+
VERSION = '0.0.1'
|
13
|
+
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :debug
|
17
|
+
attr_accessor :logger
|
18
|
+
def log(str)
|
19
|
+
logger.debug { str }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
self.debug = false
|
23
|
+
|
24
|
+
@logger ||= ::Logger.new(STDERR)
|
25
|
+
|
26
|
+
def self.version
|
27
|
+
VERSION
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module TreeRepl
|
2
|
+
|
3
|
+
class Cmd
|
4
|
+
attr_reader :repl,:name
|
5
|
+
def initialize(repl,name)
|
6
|
+
@repl = repl
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
def help
|
10
|
+
raise 'Implement help()'
|
11
|
+
end
|
12
|
+
def exec(args)
|
13
|
+
raise 'Implement exec(string[])'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Ls < Cmd
|
18
|
+
def initialize(repl)
|
19
|
+
super(repl,'ls')
|
20
|
+
end
|
21
|
+
def exec(args)
|
22
|
+
def ls_print(n,prefix=nil)
|
23
|
+
def pr(s,prefix)
|
24
|
+
if prefix
|
25
|
+
puts File.join prefix,s
|
26
|
+
else
|
27
|
+
puts s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
puts if prefix
|
31
|
+
['.','..'].each do |s|
|
32
|
+
pr s,prefix
|
33
|
+
end
|
34
|
+
n.children.each do |node|
|
35
|
+
pr node.name,prefix
|
36
|
+
end
|
37
|
+
end
|
38
|
+
args = ['.'] if args.empty?
|
39
|
+
args.uniq!
|
40
|
+
args.each do |path|
|
41
|
+
n = repl.eval_path repl.cur,path
|
42
|
+
if n
|
43
|
+
prefix = args.length > 1 ? path : nil
|
44
|
+
ls_print n,prefix
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def help
|
49
|
+
'List directory'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Cd < Cmd
|
54
|
+
def initialize(repl)
|
55
|
+
super(repl,'cd')
|
56
|
+
end
|
57
|
+
def exec(args)
|
58
|
+
args = ['/'] if args.empty?
|
59
|
+
path = args.join ' '
|
60
|
+
n = repl.eval_path repl.cur,path
|
61
|
+
repl.cur = n if n
|
62
|
+
end
|
63
|
+
def help
|
64
|
+
'Change directory'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Pwd < Cmd
|
69
|
+
def initialize(repl)
|
70
|
+
super(repl,'pwd')
|
71
|
+
end
|
72
|
+
def exec(args)
|
73
|
+
puts cwd
|
74
|
+
end
|
75
|
+
def help
|
76
|
+
'Print the current working directory'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Help < Cmd
|
81
|
+
def initialize(repl)
|
82
|
+
super(repl,'help')
|
83
|
+
end
|
84
|
+
def help
|
85
|
+
'Print this message'
|
86
|
+
end
|
87
|
+
def exec(args)
|
88
|
+
puts 'Commands:'
|
89
|
+
repl.commands.sort {|a,b| a.name <=> b.name}.each do |cmd|
|
90
|
+
printf " %-10s %s\n",cmd.name,cmd.help
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Quit < Cmd
|
96
|
+
def initialize(repl)
|
97
|
+
super(repl,'quit')
|
98
|
+
end
|
99
|
+
def help
|
100
|
+
'Quit'
|
101
|
+
end
|
102
|
+
def exec(args)
|
103
|
+
exit 0
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Use < Cmd
|
108
|
+
def initialize(repl)
|
109
|
+
super(repl,'use')
|
110
|
+
end
|
111
|
+
def help
|
112
|
+
'Start exploring a certain user'
|
113
|
+
end
|
114
|
+
def exec(args)
|
115
|
+
if args.empty?
|
116
|
+
STDERR.puts 'Missing required argument'
|
117
|
+
return
|
118
|
+
end
|
119
|
+
username = args[0]
|
120
|
+
user = nil
|
121
|
+
begin
|
122
|
+
user = repl.finder.call username
|
123
|
+
rescue Exception => e
|
124
|
+
STDERR.puts "Could not create user for name: #{username}"
|
125
|
+
STDERR.puts e
|
126
|
+
end
|
127
|
+
if user
|
128
|
+
repl.use repl.root_class.new user
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Cat < Cmd
|
134
|
+
def initialize(repl)
|
135
|
+
super(repl,'cat')
|
136
|
+
end
|
137
|
+
def help
|
138
|
+
'Shows the contents of whatever it is you selected'
|
139
|
+
end
|
140
|
+
def exec(args)
|
141
|
+
if args.empty?
|
142
|
+
STDERR.put 'Required argument'
|
143
|
+
return
|
144
|
+
end
|
145
|
+
args.each do |arg|
|
146
|
+
n = repl.eval_path repl.cur,arg
|
147
|
+
if n.kind_of? NamedNode
|
148
|
+
p n.obj
|
149
|
+
else
|
150
|
+
STDERR.puts "Uh, not with this node #{n}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module TreeRepl
|
2
|
+
|
3
|
+
class Repl
|
4
|
+
|
5
|
+
attr_reader :root
|
6
|
+
attr_accessor :commands
|
7
|
+
attr_accessor :cur
|
8
|
+
attr_accessor :root_class
|
9
|
+
|
10
|
+
# String -> TreeNode
|
11
|
+
attr_accessor :finder
|
12
|
+
|
13
|
+
def initialize(root_class)
|
14
|
+
@root_class = root_class
|
15
|
+
@finder = nil
|
16
|
+
@commands = [Ls,Cd,Pwd,Use,Help,Quit,Cat].map {|cls| cls.new self}
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_command(command_class)
|
20
|
+
@commands << command_class.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
# ----------------------------------------------------------------------
|
24
|
+
# Main
|
25
|
+
# ----------------------------------------------------------------------
|
26
|
+
|
27
|
+
# TreeNode -> Void
|
28
|
+
def main(root)
|
29
|
+
use root
|
30
|
+
strings2cmds = {}
|
31
|
+
commands.each do |cmd|
|
32
|
+
strings2cmds[cmd.name] = cmd
|
33
|
+
end
|
34
|
+
def loop(strings2cmds)
|
35
|
+
print cwd + '> '
|
36
|
+
orig_line = STDIN.readline.strip
|
37
|
+
line = orig_line.split(/ /)
|
38
|
+
return false if line.empty?
|
39
|
+
str = line.shift.downcase
|
40
|
+
return true if str == 'quit'
|
41
|
+
if not cur
|
42
|
+
if str != 'use' and str != 'help'
|
43
|
+
STDERR.puts 'You must set a user with \'use\' before anything else.'
|
44
|
+
return
|
45
|
+
end
|
46
|
+
end
|
47
|
+
args = line
|
48
|
+
cmd = strings2cmds[str]
|
49
|
+
if cmd
|
50
|
+
begin
|
51
|
+
cmd.exec args
|
52
|
+
rescue Exception => e
|
53
|
+
STDERR.puts 'Trouble executing: ' + orig_line
|
54
|
+
raise e
|
55
|
+
end
|
56
|
+
else
|
57
|
+
STDERR.puts 'Unknown command: ' + str
|
58
|
+
end
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
while true
|
62
|
+
break if loop strings2cmds
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# ----------------------------------------------------------------------
|
67
|
+
# 'Private'
|
68
|
+
# ----------------------------------------------------------------------
|
69
|
+
|
70
|
+
def use(user)
|
71
|
+
if user
|
72
|
+
@cur = user
|
73
|
+
@root = user
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# TreeNode string -> TreeNode
|
78
|
+
def eval_path(node,path)
|
79
|
+
path = '.' if not path or path == ''
|
80
|
+
if path =~ /^\//
|
81
|
+
args = ['/'] + path.gsub(/^\/+/,'').split(/\//)
|
82
|
+
else
|
83
|
+
args = path.split /\//
|
84
|
+
end
|
85
|
+
trav = node
|
86
|
+
args.each do |arg|
|
87
|
+
case arg
|
88
|
+
when '.'
|
89
|
+
trav = trav
|
90
|
+
when '..'
|
91
|
+
trav = trav.parent
|
92
|
+
when '/'
|
93
|
+
trav = root
|
94
|
+
else
|
95
|
+
found = false
|
96
|
+
trav.children.each do |n|
|
97
|
+
if n.name == arg
|
98
|
+
trav = n
|
99
|
+
found = true
|
100
|
+
break
|
101
|
+
end
|
102
|
+
end
|
103
|
+
if not found
|
104
|
+
STDERR.puts 'Invalid path: ' + path
|
105
|
+
return nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
return trav
|
110
|
+
end
|
111
|
+
|
112
|
+
# void -> string
|
113
|
+
def cwd
|
114
|
+
path(root,cur).map {|n| n.name}.join('/')
|
115
|
+
end
|
116
|
+
|
117
|
+
# TreeNode string -> TreeNode
|
118
|
+
def node_relative_to(node,name)
|
119
|
+
if name == '.'
|
120
|
+
return node
|
121
|
+
elsif name == '..'
|
122
|
+
return node.parent
|
123
|
+
elsif name == '/'
|
124
|
+
return root
|
125
|
+
else
|
126
|
+
node.children.each do |n|
|
127
|
+
if name == n.name
|
128
|
+
return n
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
return nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# TreeNode TreeNode -> TreeNode[]
|
136
|
+
def path(from,dest)
|
137
|
+
res = []
|
138
|
+
trav = dest
|
139
|
+
while trav
|
140
|
+
res.push trav
|
141
|
+
break if trav == from
|
142
|
+
trav = trav.parent
|
143
|
+
end
|
144
|
+
return res.reverse
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module TreeRepl
|
2
|
+
|
3
|
+
class TreeNode
|
4
|
+
|
5
|
+
attr_accessor :name, :parent, :leaf
|
6
|
+
|
7
|
+
@kids = nil
|
8
|
+
|
9
|
+
def find_children
|
10
|
+
raise 'Implement find_children'
|
11
|
+
end
|
12
|
+
|
13
|
+
def children(force=false)
|
14
|
+
if force or not @kids
|
15
|
+
@kids = find_children || []
|
16
|
+
@kids.each do |node|
|
17
|
+
node.parent = self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@kids
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class CmdNode < TreeNode
|
26
|
+
attr_reader :cmd
|
27
|
+
|
28
|
+
class << self
|
29
|
+
attr_accessor :classes2tree_nodes
|
30
|
+
end
|
31
|
+
self.classes2tree_nodes = {}
|
32
|
+
|
33
|
+
def initialize(obj,cmd,name)
|
34
|
+
@obj = obj
|
35
|
+
@cmd = cmd
|
36
|
+
@name = name || cmd.to_s
|
37
|
+
end
|
38
|
+
def find_children
|
39
|
+
@obj.send(@cmd).map {|v| CmdNode.classes2tree_nodes[v.class].new v}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class NamedNode < TreeNode
|
44
|
+
attr_reader :obj
|
45
|
+
attr_reader :name
|
46
|
+
def initialize(obj,name)
|
47
|
+
@obj = obj
|
48
|
+
@name = name
|
49
|
+
end
|
50
|
+
|
51
|
+
# Creates the children from the provided symbols defined by
|
52
|
+
# implementing symbols()
|
53
|
+
def find_children
|
54
|
+
lst = symbols
|
55
|
+
if lst.is_a? Hash
|
56
|
+
lst.keys.sort.map {|n| CmdNode.new @obj,lst[n],n}
|
57
|
+
else
|
58
|
+
lst.sort {|a,b| a.to_s <=> b.to_s}.map {|n| CmdNode.new @obj,n,nil}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# -> [symbol] | {string => symbol}
|
63
|
+
#
|
64
|
+
# The symbols (with possible) mapped names to use for children
|
65
|
+
#
|
66
|
+
def symbols
|
67
|
+
raise 'Implement symbols()'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: treerepl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jeffrey Palm
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-19 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: System for accessing web services for tree-like things via the repl
|
23
|
+
email: jeff@jeffpalm.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README
|
30
|
+
- LICENSE
|
31
|
+
files:
|
32
|
+
- README
|
33
|
+
- LICENSE
|
34
|
+
- lib/treerepl.rb
|
35
|
+
- lib/treerepl/tree.rb
|
36
|
+
- lib/treerepl/repl.rb
|
37
|
+
- lib/treerepl/cmds.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/spudtrooper/treerepl
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options:
|
44
|
+
- --charset=UTF-8
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
hash: 3
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: "%NAME"
|
68
|
+
rubygems_version: 1.6.1
|
69
|
+
signing_key:
|
70
|
+
specification_version: 2
|
71
|
+
summary: Tree Repl
|
72
|
+
test_files: []
|
73
|
+
|