harp 0.2.6 → 0.2.7

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.
data/examples/usage.rb CHANGED
@@ -1,3 +1,4 @@
1
+ $:.unshift "#{File.expand_path(File.dirname(__FILE__))}/../lib"
1
2
  require "harp"
2
3
 
3
4
  class UsefulThing
@@ -12,32 +13,26 @@ class UsefulThing
12
13
 
13
14
  # Set it up
14
15
  setup_harp do |harp|
16
+ command_names = harp.command_names.select {|name| name.size > 1 }
15
17
 
16
- command("help") do
17
- commands = harp.command_names
18
- puts "* Available commands: " << commands.sort.join(" ")
18
+ command("help", :alias => "h") do
19
+ puts "* Available commands: " << command_names.sort.join(", ")
19
20
  puts "* Tab completion works for commands."
20
21
  end
21
22
 
22
- # Harp provides a "quit" command by default, but you can
23
+ # Harp's repl provides a "quit" command by default, but you can
23
24
  # override it to add value.
24
- command("quit") do
25
+ command("quit", :alias => "q") do
25
26
  puts "Farewell to the girl with the sun in her eyes."
26
27
  exit
27
28
  end
28
29
 
29
- ## Set up a handler for a command where the first token is "!"
30
- ## I.e., shell out like Vim does.
31
- #on_bang do |args|
32
- #system args.first
33
- #end
34
-
35
30
  # define a command that calls an instance method of your class.
36
31
  # The block parameter is always an array, even if your regex
37
32
  # had only one match group.
38
33
  # This command will only accept a single-word argument (no
39
34
  # whitespace allowed).
40
- command("use", :adverb) do |args|
35
+ command("use", :adverb, :alias => "abuse") do |args|
41
36
  self.use(args.first)
42
37
  end
43
38
 
@@ -45,5 +40,5 @@ class UsefulThing
45
40
 
46
41
  end
47
42
 
48
- UsefulThing.new.repl
43
+ #UsefulThing.new.repl
49
44
 
data/lib/harp.rb CHANGED
@@ -1,119 +1,37 @@
1
- # stdlib
2
- require "set"
3
- require "readline"
4
-
5
- Readline.completion_append_character = nil
6
- #Readline.basic_word_break_characters = ""
7
-
8
- require "harp/dispatcher"
1
+ require "harp/command_manager"
2
+ require "harp/repl"
3
+ require "harp/cli"
9
4
 
10
5
  module Harp
11
6
  def self.included(mod)
12
7
  mod.module_eval do
13
- @dispatcher = Dispatcher.new
8
+ @command_manager = CommandManager.new
14
9
 
15
10
  def self.setup_harp(&block)
16
- dispatcher = @dispatcher
11
+ command_manager = @command_manager
17
12
  # This should either be baked in to REPL, or non-existent.
18
- @dispatcher.command("quit") do
13
+ @command_manager.command("quit") do
19
14
  exit
20
15
  end
21
- @dispatcher.instance_exec(dispatcher, &block)
16
+ @command_manager.instance_exec(command_manager, &block)
22
17
  end
23
18
 
24
19
  def self.repl
25
- REPL.new(@dispatcher)
20
+ REPL.new(@command_manager)
26
21
  end
27
22
 
28
23
  def repl
29
24
  self.class.repl.run(self)
30
25
  end
31
- end
32
- end
33
-
34
- class REPL
35
-
36
- attr_reader :store, :commands
37
- def initialize(dispatcher)
38
- @dispatcher = dispatcher
39
- @commands = dispatcher.commands.keys
40
- Readline.completion_proc = self.method(:complete)
41
- end
42
-
43
- def complete(str)
44
- case Readline.line_buffer
45
- when /^\s*!/
46
- # if we're in the middle of a bang-exec command, completion
47
- # should look at the file system.
48
- self.complete_path(str)
49
- else
50
- # otherwise use the internal dict.
51
- self.complete_term(str)
52
- end
53
- end
54
-
55
- def complete_path(str)
56
- Dir.glob("#{str}*")
57
- end
58
26
 
59
- def complete_term(str)
60
- # Terms can be either commands or indexes into the configuration
61
- # data structure. No command contains a ".", so that's the test
62
- # we use to distinguish.
63
- bits = str.split(".")
64
- if bits.size > 1
65
- # An attempt to allow completion of either full configuration index
66
- # strings, or of component parts. E.g., if the configuration contains
67
- # foo.bar.baz, this code will offer both "foo" and "foo.bar.baz"
68
- # as completions for "fo".
69
- v1 = @completions.grep(/^#{Regexp.escape(str)}/)
70
- v2 = @completions.grep(/^#{Regexp.escape(bits.last)}/)
71
- (v1 + v2.map {|x| (bits.slice(0..-2) << x).join(".") }).uniq
72
- else
73
- self.command_complete(str) +
74
- @completions.grep(/^#{Regexp.escape(str)}/)
27
+ def self.cli
28
+ CLI.new(@command_manager)
75
29
  end
76
- end
77
-
78
- def command_complete(str)
79
- @commands.grep(/^#{Regexp.escape(str)}/)
80
- end
81
-
82
- def sanitize(str)
83
- # ANSI code stripper regex cargo culted from
84
- # http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed
85
- str.gsub(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/, "")
86
- end
87
-
88
- def run(context)
89
- @completions = context.completions rescue Set.new
90
- @run = true
91
- puts
92
- while @run && (line = Readline.readline("<3: ", true).strip)
93
- if line[0] == "!"
94
- system(line.slice(1..-1))
95
- next
96
- end
97
-
98
- if line.empty?
99
- next
100
- end
101
30
 
102
- name, *args = line.split(/\s+/)
103
-
104
- # TODO: check for bang command
105
- if command = @dispatcher.commands[name]
106
- if block = command.block_for(args)
107
- context.instance_exec(args, &block)
108
- else
109
- puts "invalid arguments for command"
110
- end
111
- else
112
- puts "command not found"
113
- end
31
+ def cli
32
+ self.class.cli.run(self)
114
33
  end
115
34
  end
116
-
117
35
  end
118
36
 
119
37
  end
data/lib/harp/cli.rb ADDED
@@ -0,0 +1,24 @@
1
+
2
+ module Harp
3
+
4
+ class CLI
5
+
6
+ def initialize(command_manager)
7
+ @command_manager = command_manager
8
+ @commands = command_manager.commands.keys
9
+ end
10
+
11
+ def run(context)
12
+ name, *args = parse(ARGV)
13
+ @command_manager.handle(name, args, context)
14
+ end
15
+
16
+ def parse(array)
17
+ array
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+
@@ -0,0 +1,74 @@
1
+ module Harp
2
+
3
+ class CommandManager
4
+
5
+ attr_reader :commands
6
+ def initialize
7
+ @commands = {}
8
+ end
9
+
10
+ def handle(name, args, context)
11
+ block = find_command(name, args)
12
+ context.instance_exec(args, &block)
13
+ end
14
+
15
+ def find_command(name, args)
16
+ if command = @commands[name]
17
+ if block = command.block_for(args)
18
+ return block
19
+ else
20
+ raise ArgumentError, "Invalid arguments for command."
21
+ end
22
+ else
23
+ raise ArgumentError, "Command not found: '#{name}'."
24
+ end
25
+ end
26
+
27
+ def command(command_name, *spec, &block)
28
+ names = [command_name]
29
+ if spec.last.is_a?(Hash)
30
+ options = spec.pop
31
+ if command_alias = options[:alias]
32
+ names << command_alias
33
+ end
34
+ end
35
+ names.each do |name|
36
+ command = @commands[name] ||= Command.new(name)
37
+ command.add_spec(spec, block)
38
+ end
39
+ end
40
+
41
+ def command_names
42
+ @commands.keys.sort
43
+ end
44
+
45
+ end
46
+
47
+ class Command
48
+ def initialize(name)
49
+ @name = name
50
+ @blocks = {}
51
+ end
52
+
53
+ def add_spec(spec, block)
54
+ # TODO validate block and arity
55
+ @blocks[spec]= block
56
+ end
57
+
58
+ def block_for(args)
59
+ @blocks.each do |spec, block|
60
+ if match(spec, args)
61
+ return block
62
+ end
63
+ end
64
+ return nil
65
+ end
66
+
67
+ def match(spec, args)
68
+ spec.size == args.size
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
data/lib/harp/repl.rb ADDED
@@ -0,0 +1,94 @@
1
+ # stdlib
2
+ require "set"
3
+ require "readline"
4
+
5
+ Readline.completion_append_character = nil
6
+ #Readline.basic_word_break_characters = ""
7
+
8
+ module Harp
9
+
10
+ class REPL
11
+
12
+ def initialize(command_manager)
13
+ @command_manager = command_manager
14
+ Readline.completion_proc = self.method(:complete)
15
+ end
16
+
17
+ def commands
18
+ @command_manager.commands.keys
19
+ end
20
+
21
+ def complete(str)
22
+ case Readline.line_buffer
23
+ when /^\s*!/
24
+ # if we're in the middle of a bang-exec command, completion
25
+ # should look at the file system.
26
+ self.complete_path(str)
27
+ else
28
+ # otherwise use the internal dict.
29
+ self.complete_term(str)
30
+ end
31
+ end
32
+
33
+ def complete_path(str)
34
+ Dir.glob("#{str}*")
35
+ end
36
+
37
+ def complete_term(str)
38
+ # Terms can be either commands or indexes into the configuration
39
+ # data structure. No command contains a ".", so that's the test
40
+ # we use to distinguish.
41
+ bits = str.split(".")
42
+ if bits.size > 1
43
+ # An attempt to allow completion of either full configuration index
44
+ # strings, or of component parts. E.g., if the configuration contains
45
+ # foo.bar.baz, this code will offer both "foo" and "foo.bar.baz"
46
+ # as completions for "fo".
47
+ v1 = @completions.grep(/^#{Regexp.escape(str)}/)
48
+ v2 = @completions.grep(/^#{Regexp.escape(bits.last)}/)
49
+ (v1 + v2.map {|x| (bits.slice(0..-2) << x).join(".") }).uniq
50
+ else
51
+ self.command_complete(str) +
52
+ @completions.grep(/^#{Regexp.escape(str)}/)
53
+ end
54
+ end
55
+
56
+ def command_complete(str)
57
+ commands.grep(/^#{Regexp.escape(str)}/)
58
+ end
59
+
60
+ def sanitize(str)
61
+ # ANSI code stripper regex cargo culted from
62
+ # http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed
63
+ str.gsub(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/, "")
64
+ end
65
+
66
+ def run(context)
67
+ @completions = context.completions rescue Set.new
68
+ @run = true
69
+ puts
70
+ while @run && (line = Readline.readline("<3: ", true).strip)
71
+ # Treat ! as the shell out command
72
+ if line[0] == "!"
73
+ system(line.slice(1..-1))
74
+ next
75
+ end
76
+
77
+ if line.empty?
78
+ next
79
+ end
80
+
81
+ name, *args = line.split(/\s+/)
82
+
83
+ begin
84
+ @command_manager.handle(name, args, context)
85
+ rescue ArgumentError => e
86
+ puts e.message
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-10 00:00:00.000000000 Z
12
+ date: 2012-09-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: starter
@@ -35,8 +35,11 @@ extra_rdoc_files: []
35
35
  files:
36
36
  - README.md
37
37
  - LICENSE
38
- - lib/harp.rb
39
38
  - examples/usage.rb
39
+ - lib/harp/cli.rb
40
+ - lib/harp/command_manager.rb
41
+ - lib/harp/repl.rb
42
+ - lib/harp.rb
40
43
  homepage: https://github.com/automatthew/harp
41
44
  licenses: []
42
45
  post_install_message: