harp 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/LICENSE +9 -0
  2. data/README.md +81 -0
  3. data/examples/usage.rb +49 -0
  4. data/lib/harp.rb +153 -0
  5. metadata +80 -0
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2012 Matthew King
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
9
+
@@ -0,0 +1,81 @@
1
+ # HARP
2
+
3
+ A mixin for creating application REPLs with Readline support.
4
+
5
+ ## Example
6
+
7
+ gem install harp
8
+
9
+ ```ruby
10
+ require "harp"
11
+
12
+ class UsefulThing
13
+
14
+ # define all the useful methods and behaviors you need
15
+ def use(adverb)
16
+ puts "You used me #{adverb}!"
17
+ end
18
+
19
+ # Mix it in
20
+ include Harp
21
+
22
+ # Set it up
23
+ setup_repl do |repl|
24
+
25
+ on("help") do
26
+ commands = repl.commands
27
+ puts "* Available commands: " << commands.sort.join(" ")
28
+ puts "* Tab completion works for commands."
29
+ end
30
+
31
+ # Harp provides a "quit" command by default, but you can
32
+ # override it to add value.
33
+ on("quit") do
34
+ puts "Farewell to the girl with the sun in her eyes."
35
+ exit
36
+ end
37
+
38
+ # Set up a handler for a command where the first token is "!"
39
+ # I.e., shell out like Vim does.
40
+ on_bang do |args|
41
+ system args.first
42
+ end
43
+
44
+ # define a command that calls an instance method of your class.
45
+ # The block parameter is always an array, even if your regex
46
+ # had only one match group.
47
+ # This command will only accept a single-word argument (no
48
+ # whitespace allowed).
49
+ on(/use (\S+)$/) do |args|
50
+ self.use(args.first)
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ UsefulThing.new.repl
58
+ ```
59
+
60
+
61
+ ## Usage
62
+
63
+
64
+ $ ruby -I lib/ examples/usage.rb
65
+
66
+ <3: help
67
+ * Available commands: help quit use
68
+ * Tab completion works for commands.
69
+ <3: use well
70
+ You used me well!
71
+ <3: use badly
72
+ You used me badly!
73
+ <3: use without proper care
74
+ command not found
75
+ <3: ! ls
76
+ LICENSE README.md consolize.gemspec examples lib
77
+ <3: quit
78
+ Farewell to the girl with the sun in her eyes.
79
+
80
+
81
+
@@ -0,0 +1,49 @@
1
+ require "harp"
2
+
3
+ class UsefulThing
4
+
5
+ # define all the useful methods and behaviors you need
6
+ def use(adverb)
7
+ puts "You used me #{adverb}!"
8
+ end
9
+
10
+ # Mix it in
11
+ include Harp
12
+
13
+ # Set it up
14
+ setup_repl do |repl|
15
+
16
+ on("help") do
17
+ commands = repl.commands
18
+ puts "* Available commands: " << commands.sort.join(" ")
19
+ puts "* Tab completion works for commands."
20
+ end
21
+
22
+ # Harp provides a "quit" command by default, but you can
23
+ # override it to add value.
24
+ on("quit") do
25
+ puts "Farewell to the girl with the sun in her eyes."
26
+ exit
27
+ end
28
+
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
+ # define a command that calls an instance method of your class.
36
+ # The block parameter is always an array, even if your regex
37
+ # had only one match group.
38
+ # This command will only accept a single-word argument (no
39
+ # whitespace allowed).
40
+ on(/use (\S+)$/) do |args|
41
+ self.use(args.first)
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ UsefulThing.new.repl
49
+
@@ -0,0 +1,153 @@
1
+ # stdlib
2
+ require "set"
3
+
4
+ require "readline"
5
+
6
+ Readline.completion_append_character = nil
7
+ #Readline.basic_word_break_characters = ""
8
+
9
+
10
+ module Harp
11
+ def self.included(mod)
12
+ mod.module_eval do
13
+ @repl = REPL.new
14
+ def self.repl
15
+ @repl
16
+ end
17
+
18
+ def self.setup_repl(&block)
19
+ repl = @repl
20
+ @repl.on("quit") do
21
+ repl.exit
22
+ end
23
+ @repl.on("") do
24
+ puts "Giving me the silent treatment, eh?"
25
+ end
26
+ @repl.instance_exec(repl, &block)
27
+ end
28
+
29
+ def repl
30
+ self.class.repl.run(self)
31
+ end
32
+ end
33
+ end
34
+
35
+ class REPL
36
+
37
+ attr_reader :store, :commands
38
+ def initialize
39
+ @patterns = {}
40
+ @commands = Set.new
41
+ Readline.completion_proc = self.method(:complete)
42
+ end
43
+
44
+ def complete(str)
45
+ case Readline.line_buffer
46
+ when /^\s*!/
47
+ # if we're in the middle of a bang-exec command, completion
48
+ # should look at the file system.
49
+ self.dir_complete(str)
50
+ else
51
+ # otherwise use the internal dict.
52
+ self.term_complete(str)
53
+ end
54
+ end
55
+
56
+ def dir_complete(str)
57
+ Dir.glob("#{str}*")
58
+ end
59
+
60
+ def term_complete(str)
61
+ # Terms can be either commands or indexes into the configuration
62
+ # data structure. No command contains a ".", so that's the test
63
+ # we use to distinguish.
64
+ bits = str.split(".")
65
+ if bits.size > 1
66
+ # Somebody should have documented this when he wrote it, because
67
+ # he now does not remember exactly what he was trying to achieve.
68
+ # He thinks that it's an attempt to allow completion of either
69
+ # full configuration index strings, or of component parts.
70
+ # E.g., if the configuration contains foo.bar.baz, this code
71
+ # will offer both "foo" and "foo.bar.baz" as completions for "fo".
72
+ v1 = @completions.grep(/^#{Regexp.escape(str)}/)
73
+ v2 = @completions.grep(/^#{Regexp.escape(bits.last)}/)
74
+ (v1 + v2.map {|x| (bits.slice(0..-2) << x).join(".") }).uniq
75
+ else
76
+ self.command_complete(str) +
77
+ @completions.grep(/^#{Regexp.escape(str)}/)
78
+ end
79
+ end
80
+
81
+ def command_complete(str)
82
+ @commands.grep(/^#{Regexp.escape(str)}/)
83
+ end
84
+
85
+ def sanitize(str)
86
+ # ANSI code stripper regex cargo culted from
87
+ # http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed
88
+ str.gsub(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/, "")
89
+ end
90
+
91
+ def on(*pattern, &block)
92
+ pattern.flatten.each do |pattern|
93
+ @patterns[pattern] = block
94
+ self.add_command(pattern)
95
+ end
96
+ end
97
+
98
+ # Helper for defining the action for the "!" command.
99
+ # Typically used to shell out, a la Vim.
100
+ def on_bang(&block)
101
+ on(/^\!\s*(.*)$/, &block)
102
+ end
103
+
104
+ def add_command(pattern)
105
+ if pattern.is_a?(String)
106
+ @commands << pattern
107
+ else
108
+ bits = pattern.source.split(" ")
109
+ # TODO: figure out why you did this, then document it.
110
+ if bits.size > 1
111
+ @commands << bits.first
112
+ end
113
+ end
114
+ end
115
+
116
+ def run(context)
117
+ @completions = context.completions rescue Set.new
118
+ @run = true
119
+ puts
120
+ while @run && line = Readline.readline("<3: ", true)
121
+ self.parse(context, line.chomp)
122
+ end
123
+ end
124
+
125
+ def exit
126
+ @run = false
127
+ end
128
+
129
+ # Attempt to find a registered command that matches the input
130
+ # string. Upon failure, print an encouraging message.
131
+ def parse(context, input_string)
132
+ _p, block= @patterns.detect do |pattern, block|
133
+ pattern === input_string
134
+ end
135
+ if block
136
+ # Perlish global ugliness necessitated by the use of
137
+ # Enumerable#detect above. FIXME.
138
+ if $1
139
+ # if the regex had a group (based on the assumption that $1
140
+ # represents the result of the === that matched), call the block
141
+ # with all the group matches as arguments.
142
+ context.instance_exec($~[1..-1], &block)
143
+ else
144
+ context.instance_eval(&block)
145
+ end
146
+ else
147
+ puts "command not found"
148
+ end
149
+ end
150
+
151
+ end
152
+
153
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: harp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matthew King
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rb-readline
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.4.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.4.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: starter
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.1.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.1.0
46
+ description:
47
+ email:
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - README.md
53
+ - LICENSE
54
+ - lib/harp.rb
55
+ - examples/usage.rb
56
+ homepage: https://github.com/automatthew/harp
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.23
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: A mixin for creating simple application repls with Readline support
80
+ test_files: []