mfe 1.1

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.
Files changed (6) hide show
  1. data/bin/mfe +37 -0
  2. data/doc/LICENSE +19 -0
  3. data/doc/README +136 -0
  4. data/lib/mfe-config.rb +8 -0
  5. data/lib/mfe.rb +260 -0
  6. metadata +75 -0
data/bin/mfe ADDED
@@ -0,0 +1,37 @@
1
+ #! /usr/bin/ruby
2
+ # MFE the meta-frontend
3
+ # Reducing clutter in ~/bin since 2005.
4
+ #
5
+ # By Pete Elmore (1337p337@gmail.com)
6
+ # http://debu.gs/mfe
7
+ #
8
+ # For licensing and copyright information, see the file LICENSE in doc/ .
9
+
10
+ require 'rubygems'
11
+ require 'mfe'
12
+
13
+ # int main(int argc, char *argv[])
14
+ include MFE
15
+ cmdname = ARGV.shift
16
+ case cmdname
17
+ when nil
18
+ usage
19
+ when '-h', /^-?-?help$/ # Help
20
+ usage $stdout
21
+ when '-V', /^-?-?version$/ # Version info
22
+ puts version_string
23
+ when CommandSig # MFE mode
24
+ methname = MLook[ARGV.first]
25
+ unless methname
26
+ usage
27
+ exit 1
28
+ end
29
+ ARGV.shift
30
+ send methname, *ARGV
31
+ else # Execution mode
32
+ exec_cmd(cmdname, *ARGV)
33
+ # exec_cmd() doesn't return unless there was an error.
34
+ exit 1
35
+ end
36
+
37
+ exit 0
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2005-2006, 2008-2010, 2012 Peter Elmore (pete at debu dot gs)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
+ Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,136 @@
1
+ = MFE
2
+
3
+ == Installation
4
+
5
+ You can install via rubygems,
6
+
7
+ gem install mfe
8
+
9
+ or plain old setup.rb:
10
+
11
+ ruby setup.rb install
12
+
13
+ == How to use MFE
14
+
15
+ MFE has two main modes: command mode, and execution mode.
16
+
17
+ === Quick Version
18
+
19
+ Here's the quick version of this README, because seriously, you're not going
20
+ to read this until you get confused:
21
+
22
+ mfe - add say-hi echo hi
23
+ mfe say-hi
24
+ => hi
25
+ mfe - add say-whatever echo ARGS
26
+ mfe say-whatever it can be whatever
27
+ => it can be whatever
28
+ mfe - add argplay echo 'ARGEVAL[ arg.sub(/d/, "date"); ]' 'EVAL[(argv.map { |arg| arg.sub "date", `date`.chomp }).join " -- "]'
29
+ mfe argplay a s d f
30
+ => "a -- s -- Tue Sep 19 01:28:48 PDT 2006 -- f"
31
+
32
+ Also, several variations of 'mfe -h', 'mfe --help', 'mfe help', etc., do what
33
+ you expect.
34
+
35
+ === Command Mode
36
+
37
+ In command mode, you can add, edit, remove, or otherwise manipulate the
38
+ user-defined commands.
39
+
40
+ ==== To add a new command
41
+
42
+ mfe - add cmdname command-and-args
43
+
44
+ This is the format for adding a command to MFE. The first argument to mfe is
45
+ '-', the command character. This signals to MFE that we will be running an
46
+ MFE command, as opposed to executing a command we have defined. After that
47
+ comes 'add', the keyword that tells MFE which command to perform. 'cmdname'
48
+ is the name given to the command. It can be whatever you want, but must be
49
+ legal as a filename. The 'command-and-args' part is the command you actually
50
+ want to run when you type 'mfe cmdname'. As a more concrete example, have a
51
+ look at this:
52
+
53
+ mfe - add hgr grep ARGS /usr/log/httpd.log
54
+
55
+ Which creates a command named 'hgr' that greps /usr/log/httpd.log for whatever
56
+ arguments you pass it:
57
+
58
+ mfe hgr -i googlebot
59
+
60
+ Executing the previous command is equivalent to running
61
+
62
+ grep -i googlebot /usr/log/httpd.log
63
+
64
+ As you might have guessed, ARGS is replaced by whatever arguments you pass
65
+ when you execute the command. There are two other special strings: EVAL[]
66
+ and ARGEVAL[]. EVAL[] is replaced with the value of the Ruby code inside the
67
+ brackets. The Ruby inside ARGVEVAL[] is evaluated once for each argument
68
+ (bound to 'arg') and its value replaces the argument. The variable 'argv' is
69
+ exposed to both constructs. A couple of examples should make it clearer:
70
+
71
+ mfe - add reverse-args echo 'ARGEVAL[ arg.reverse ]' ARGS
72
+ mfe reverse-args Hello, World.
73
+ => ,olleH .dlroW
74
+ mfe - add reverse-argv echo 'EVAL[ argv.reverse.join(" ") ]'
75
+ mfe reverse-argv Hello, World.
76
+ => World. Hello,
77
+ mfe - add reverse-both echo 'ARGEVAL[ arg.reverse ]' 'EVAL[ argv.reverse.join(" ") ]'
78
+ mfe reverse-both Hello, World.
79
+ => .dlroW ,olleH
80
+ mfe reverse-both $(mfe reverse-args $(mfe reverse-argv Hello, World.))
81
+ => Hello, World.
82
+
83
+ ==== To edit a command
84
+
85
+ mfe - edit cmdname
86
+
87
+ This brings up the JSON representing the command in your editor.
88
+
89
+ ==== To remove a command
90
+
91
+ Simply do
92
+
93
+ mfe - rm cmdname1 cmdname2
94
+
95
+ ==== To generate a shell script from a command
96
+
97
+ mfe - add echo-args echo ARGS
98
+ mfe - to-shell echo-args A S D F > echoargs.sh
99
+
100
+ Or, a little more creatively,
101
+
102
+ mfe - to-shell useful-command | ssh user@host-without-ruby 'cat > bin/cmd'
103
+
104
+ === Execution Mode
105
+
106
+ mfe cmdname args
107
+
108
+ Execution mode refers to the loading and evaluation of a command. Execution
109
+ mode is invoked by running mfe followed by the name of a command and
110
+ (optionally) any arguments you wish to pass to it. For example:
111
+
112
+ Add a command,
113
+
114
+ mfe - add testcommand for i in ARGS; do indent \$i; done
115
+
116
+ then execute it:
117
+
118
+ mfe testcommand *.c
119
+
120
+ == Environment Variables
121
+
122
+ There are two relevant environment variables: MFE_HOME and MFE_COMMAND_SIG .
123
+ MFE_HOME can be used to change the location of your MFE command directory
124
+ (the default is ~/.mfe). MFE_COMMAND_SIG changes the string used for
125
+ signaling command mode (default '-'). For example,
126
+
127
+ MFE_HOME=/tmp/foo MFE_COMMAND_SIG=c_mode mfe c_mode add hi echo hi
128
+
129
+ creates a command named 'hi' which executes 'echo hi', and places it in
130
+ /tmp/foo/hi . Note that the mfe home is created automatically, with
131
+ permissions set to 0700.
132
+
133
+ == Credits
134
+
135
+ Pete Elmore -- (pete at debu dot gs)
136
+
@@ -0,0 +1,8 @@
1
+ # This module houses a pile of constants for MFE.
2
+ module MFEConfig
3
+ Authors = 'Pete Elmore'
4
+ Email = 'pete@debu.gs'
5
+ PkgName = 'mfe'
6
+ Version = '1.1'
7
+ URL = 'http://debu.gs/mfe'
8
+ end
@@ -0,0 +1,260 @@
1
+ # MFE the meta-frontend!
2
+ #
3
+ # Reducing clutter in ~/bin since 2005. And, although it seems to have survived
4
+ # several major revisions of Ruby without blinking, it finally needed
5
+ # maintenance when the damned YAML parser changed. It's JSON now; MFE was
6
+ # written before JSON was cool. In fact, it is so old there is no Rakefile, but
7
+ # there is a _darcs. Hopefully JSON will change less frequently and I won't
8
+ # have to open this file again for another few years.
9
+ #
10
+ # By Pete Elmore (http://debu.gs/)
11
+ #
12
+ # For licensing and copyright information, see the file LICENSE in doc/ .
13
+
14
+ %w(mfe-config fileutils json tempfile etc metaid escape
15
+ ).each(&method(:require))
16
+
17
+ class String
18
+ def shell_escape
19
+ Escape.shell_single_word self
20
+ end
21
+ end
22
+
23
+ # This module houses the main logic for MFE.
24
+ module MFE
25
+ # Method lookup table for commands
26
+ MLook = {
27
+ 'add' => :add_cmd,
28
+ 'edit' => :edit_json,
29
+ 'show' => :show_cmd,
30
+ 'to-shell' => :cmd_to_shell,
31
+ 'rm' => :rm_cmd,
32
+ 'remove' => :rm_cmd,
33
+ }
34
+
35
+ # This is the character (or string) that indicates that we will be
36
+ # running an MFE command rather than loading and executing a
37
+ # user-defined command.
38
+ CommandSig = ENV['MFE_COMMAND_SIG'] || '-'
39
+
40
+ def dirnames
41
+ # Possible directories for housing mfe commands.
42
+ @dirnames ||= [ENV['MFE_HOME'],
43
+ (File.join(ENV['HOME'], '/.mfe') rescue nil),
44
+ File.join(Etc.getpwuid(Process.euid).dir, '/.mfe'),
45
+ ].compact
46
+ end
47
+
48
+ # Loads a command with the given name
49
+ def load_cmd(name)
50
+ Cmd.load full_path(name)
51
+ end
52
+
53
+ # Loads and executes a command named by +name+ with args +argv+.
54
+ def exec_cmd(name, *argv)
55
+ begin
56
+ exec load_cmd(name).expand(argv)
57
+ rescue Errno::E2BIG
58
+ fp = full_path name
59
+ $stderr.puts "Couldn't execute #{name}!", "Argument list too long."
60
+ rescue Exception => e
61
+ fp = full_path name
62
+ $stderr.puts "Couldn't execute #{name}! (#{e.message})"
63
+
64
+ if !File.exist? fp
65
+ $stderr.puts 'File does not exist.'
66
+ elsif !File.readable? fp
67
+ $stderr.puts 'File is not readable.'
68
+ elsif File.directory? fp
69
+ $stderr.puts 'It\'s a directory.'
70
+ else
71
+ $stderr.puts e.inspect
72
+ end
73
+ end
74
+ end
75
+
76
+ # Given a list of command names, show_cmd will print what each one does.
77
+ # In the absense of a list of names, it will print all commands in the
78
+ # .mfe directory.
79
+ def show_cmd(*names)
80
+ if names.empty?
81
+ names = Dir["#{dirname}/*"] - %w(. ..)
82
+ names.map! &File.method(:basename)
83
+ end
84
+
85
+ names.sort.each { |name|
86
+ puts "#{name}: #{load_cmd(name).to_s}"
87
+ }
88
+ end
89
+
90
+ # Given a name and a list of args, this method prints a shell script
91
+ # that would have the effect of running the command.
92
+ def cmd_to_shell(name, *argv)
93
+ cmd = load_cmd(name)
94
+ shell = [
95
+ `which ENV['SHELL'] 2>/dev/null`,
96
+ Etc.getpwuid(Process.uid).shell,
97
+ '/bin/sh',
98
+ ].find { |shell| !shell.chomp.empty? }
99
+
100
+ puts("#!#{shell}",
101
+ cmd.expand(argv))
102
+ true
103
+ end
104
+
105
+ # Removes commands! :O
106
+ def rm_cmd(names)
107
+ names.each { |name|
108
+ File.unlink full_path(name)
109
+ }
110
+ end
111
+
112
+ # Adds a new command.
113
+ def add_cmd(name, *cmd)
114
+ if cmd.empty?
115
+ cmd = edit_json(name)
116
+ return false if cmd.to_s.empty?
117
+ else
118
+ cmd = Cmd.new(name, cmd)
119
+ end
120
+ cmd.save(dirname!)
121
+ end
122
+
123
+ # Allows the user to directly edit the JSON representation of a command.
124
+ def edit_json(name, *argv)
125
+ system editor, full_path!(name)
126
+ $?.exited?
127
+ end
128
+
129
+ # Returns the first viable directory for the MFE directory. If none is
130
+ # found, returns nil. Otherwise, it's guaranteed to always return the
131
+ # same value.
132
+ def dirname
133
+ dir = dirnames.find { |d| File.directory?(d) }
134
+ if dir
135
+ # It's important for this to be consistent (and faster anyway).
136
+ meta_def(:dirname) { dir }
137
+ return dir
138
+ end
139
+ nil
140
+ end
141
+
142
+ # Returns the dirname, but if it's nil, makes the directory with
143
+ # permissions 0700.
144
+ def dirname!
145
+ return dirname if dirname
146
+ Dir.mkdir(dirnames.first, 0700)
147
+ dirname
148
+ end
149
+
150
+ # Returns the user's (suspected) preferred editor.
151
+ def editor
152
+ ENV['VISUAL'] ||
153
+ ENV['EDITOR'] ||
154
+ 'vi' # Reasonable default.
155
+ end
156
+
157
+ # Returns the full path for a given command with name +name+.
158
+ def full_path(name)
159
+ File.join(dirname, name)
160
+ end
161
+
162
+ # Returns the full path for a given command with name +name+, and
163
+ # creates the path if it does not exist.
164
+ def full_path!(name)
165
+ fn = File.join(dirname!, name)
166
+ File.open(fn, 'a').close
167
+ fn
168
+ end
169
+
170
+ def usage(out = $stderr)
171
+ out.puts [ "",
172
+ "MFE, the meta front-end (#{MFEConfig::URL})",
173
+ "Usage:",
174
+ "#{$0} #{CommandSig} command [args ...]",
175
+ " Where command is one of",
176
+ " add name script [args ...]:",
177
+ " Adds a command named 'name' that executes " +
178
+ "'script [args ...]'.",
179
+ " show [name(s)]",
180
+ " Shows the command(s) named, or all commands if none " +
181
+ "are named.",
182
+ " edit name",
183
+ " Runs your favorite editor on the named command.",
184
+ " to-shell name [args ...]",
185
+ " Turns the user-defined command into a shell script.",
186
+ "#{$0} your_command [args ...]",
187
+ " Runs the user-defined command named 'your_command' with the " +
188
+ "specified\n arguments.",
189
+ "#{$0} ['-h'|'-help'|'--help']",
190
+ " This help screen.",
191
+ "See the README for more info about usage and influential " +
192
+ "environment\nvariables.",
193
+ ]
194
+ end
195
+
196
+ def version_string
197
+ "MFE, version #{MFEConfig::Version}."
198
+ end
199
+ end
200
+
201
+ # Represents an individual command
202
+ class Cmd
203
+ attr_accessor :name
204
+
205
+ # Turns a filename into a command! Amazing! Pass the full path.
206
+ def self.load(filename)
207
+ obj = JSON.parse(File.read(filename))
208
+
209
+ new(File.basename(filename), obj['cmd'])
210
+ end
211
+
212
+ # +save+, saves the command to a file.
213
+ def save(dirname)
214
+ File.open(File.join(dirname, @name), 'w') { |f|
215
+ f.puts JSON.pretty_unparse(to_hash)
216
+ }
217
+ end
218
+
219
+ # Creates a new command.
220
+ def initialize(name, cmd)
221
+ @name = name
222
+ @cmd = cmd
223
+ end
224
+
225
+ # +to_s+ simply returns the command string.
226
+ def to_s
227
+ @cmd.join(' ')
228
+ end
229
+
230
+ def to_hash
231
+ {name: @name, cmd: @cmd}
232
+ end
233
+
234
+ # Turns the command into a line of shell, fit to be exec()'d or ``'d.
235
+ def expand(argv)
236
+ argv = argv.map(&:shell_escape)
237
+ @cmd.map { |this_arg|
238
+ a = this_arg.dup
239
+ a.gsub!(/\bARGS\b/, argv.join(' '))
240
+ a.gsub!(/\bARGEVAL\[(.*)\]/) { |m|
241
+ argeval = $1
242
+ argv.map! { |frozen_arg|
243
+ arg = frozen_arg.dup
244
+ eval(argeval, binding).to_s
245
+ }
246
+ ''
247
+ }
248
+ a.gsub!(/\bEVAL\[(.*)\]/) { |m|
249
+ eval($1, binding).to_s
250
+ }
251
+ a
252
+ }.join(' ')
253
+ end
254
+
255
+ # +run+ runs the command, returning the output.
256
+ def run(argv)
257
+ `#{expand argv}`
258
+ end
259
+ end
260
+
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mfe
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pete Elmore
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: metaid
16
+ requirement: &6886860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *6886860
25
+ - !ruby/object:Gem::Dependency
26
+ name: escape
27
+ requirement: &8227460 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *8227460
36
+ description:
37
+ email: pete@debu.gs
38
+ executables:
39
+ - mfe
40
+ extensions: []
41
+ extra_rdoc_files:
42
+ - doc/README
43
+ - doc/LICENSE
44
+ files:
45
+ - bin/mfe
46
+ - doc/LICENSE
47
+ - doc/README
48
+ - lib/mfe.rb
49
+ - lib/mfe-config.rb
50
+ homepage: http://debu.gs/mfe
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 1.8.6
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: A program which remembers and executes brief scripts.
75
+ test_files: []