irb_hacks 0.1.0
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/CHANGELOG.md +4 -0
- data/README.md +134 -0
- data/VERSION.yml +4 -0
- data/irb_hacks.gemspec +43 -0
- data/lib/irb_hacks/core_ext/kernel/a_and_ae.rb +33 -0
- data/lib/irb_hacks/core_ext/kernel/less.rb +89 -0
- data/lib/irb_hacks/snippet.rb +80 -0
- data/lib/irb_hacks.rb +15 -0
- metadata +62 -0
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
Yet another set of IRB hacks
|
2
|
+
============================
|
3
|
+
|
4
|
+
Setup
|
5
|
+
-----
|
6
|
+
|
7
|
+
$ gem sources --add http://gemcutter.org
|
8
|
+
$ gem install irb_hacks
|
9
|
+
|
10
|
+
Add to your `~/.irbrc`:
|
11
|
+
|
12
|
+
require "rubygems"
|
13
|
+
require "irb_hacks"
|
14
|
+
|
15
|
+
Now fire up IRB for a quick test:
|
16
|
+
|
17
|
+
$ irb
|
18
|
+
irb> ae
|
19
|
+
(snippet)>>
|
20
|
+
|
21
|
+
If you see "(snippet)", you're ready to go.
|
22
|
+
|
23
|
+
|
24
|
+
The Hacks
|
25
|
+
---------
|
26
|
+
|
27
|
+
### Code snippets -- `a` and `ae` ###
|
28
|
+
|
29
|
+
There's often a need to invoke our work-in-progress code a number of times using the same arguments, wrapping block, etc. For that, "code snippets" feature is quite handy.
|
30
|
+
|
31
|
+
`irb_hacks` gem provides the two methods with short, meaningless (and thus conflict-free) names -- `a` and `ae`. `a` means nothing, it's just the first letter of the alphabet. `a` **invokes** the last-edited snippet. `ae` **lets you edit** the actual snippet (it roughly stands for "a" + "edit").
|
32
|
+
|
33
|
+
A very basic example:
|
34
|
+
|
35
|
+
irb> ae
|
36
|
+
(snippet)>> puts "Hello, world"
|
37
|
+
irb> a
|
38
|
+
Hello, world
|
39
|
+
|
40
|
+
Snippet arguments are supported. It's an array called `args` in snippet context.
|
41
|
+
|
42
|
+
irb> ae
|
43
|
+
(snippet)>> p "args", args
|
44
|
+
irb> a 10, 1.0, "a string"
|
45
|
+
"args"
|
46
|
+
[10, 1.0, "a string"]
|
47
|
+
|
48
|
+
Snippets work just like normal Ruby methods -- they return the value of the last statement executed.
|
49
|
+
|
50
|
+
irb> ae
|
51
|
+
(snippet)>> ["alfa", "zulu", "bravo"] + args
|
52
|
+
irb> puts a("charlie").sort
|
53
|
+
alfa
|
54
|
+
bravo
|
55
|
+
charlie
|
56
|
+
zulu
|
57
|
+
|
58
|
+
Snippets support code blocks. It's a `Proc` called `block` in snippet context. Usage example follows (suppose we're building a simplistic `/etc/passwd` parser).
|
59
|
+
|
60
|
+
irb> ae
|
61
|
+
(snippet)>> File.readlines("/etc/passwd").map(&block).each {|s| p s}; nil
|
62
|
+
irb> a {|s| ar = s.split(":"); {:name => ar[0], :uid => ar[2]}}
|
63
|
+
{:uid=>"0", :name=>"root"}
|
64
|
+
{:uid=>"1", :name=>"bin"}
|
65
|
+
{:uid=>"2", :name=>"daemon"}
|
66
|
+
{:uid=>"3", :name=>"adm"}
|
67
|
+
...
|
68
|
+
|
69
|
+
Snippets are **persistent** though IRB invocations. That's quite handy, since not all stuff can be dynamically reloaded and sometimes we have to restart IRB to ensure clean reload.
|
70
|
+
|
71
|
+
irb> ae
|
72
|
+
(snippet)>> puts "Snippets are persistent!"
|
73
|
+
irb> exit
|
74
|
+
$ irb
|
75
|
+
irb> a
|
76
|
+
Snippets are persistent!
|
77
|
+
|
78
|
+
Just in case, snippet history file is called `.irb_snippet_history` in your `$HOME`.
|
79
|
+
|
80
|
+
Snippets maintain **their own** Realine history. When you press [Up] and [Down] keys in `ae`, you browse the previously used snippets, not just your previous IRB input. Don't retype the snippet you used yesterday -- press [Up] a couple times and you'll see it.
|
81
|
+
|
82
|
+
irb> ae
|
83
|
+
(snippet)>> puts "snippet one"
|
84
|
+
irb> hala
|
85
|
+
irb> bala
|
86
|
+
irb> ae
|
87
|
+
(snippet)>> puts "snippet two"
|
88
|
+
irb> foo
|
89
|
+
irb> moo
|
90
|
+
irb> ae
|
91
|
+
(snippet)>>
|
92
|
+
## Pressing [Up] will give you...
|
93
|
+
(snippet)>> puts "snippet two"
|
94
|
+
## Pressing [Up] again will give you...
|
95
|
+
(snippet)>> puts "snippet one"
|
96
|
+
|
97
|
+
### Browse program data with GNU `less` ###
|
98
|
+
|
99
|
+
Sometimes the data your code works with is too long to fit in a console window. The clearest example of this are variables filled with text content, e.g. [Hpricot](http://github.com/whymirror/hpricot) documents/elements.
|
100
|
+
|
101
|
+
To solve that, the greatest paging program of all times, GNU `less`, comes to the rescue.
|
102
|
+
|
103
|
+
$ irb
|
104
|
+
irb> files = Dir["/etc/*"].sort
|
105
|
+
## Some bulky array...
|
106
|
+
irb> less files
|
107
|
+
## ... which we browse interactively :).
|
108
|
+
|
109
|
+
In block form, `less` hack intercepts everything output to `STDOUT` (and, optionally, to `STDERR`), and feeds it to the pager.
|
110
|
+
|
111
|
+
$ irb
|
112
|
+
irb> less do
|
113
|
+
puts "Hello, world"
|
114
|
+
end
|
115
|
+
|
116
|
+
Now with `STDERR` capture:
|
117
|
+
|
118
|
+
$ irb
|
119
|
+
irb> less(:stderr) do
|
120
|
+
puts "to stdout"
|
121
|
+
STDERR.puts "to stderr"
|
122
|
+
end
|
123
|
+
|
124
|
+
To specify another paging program or tweak `less` options, write in your `~/.irbrc`:
|
125
|
+
|
126
|
+
IrbHacks.less_cmd = "more"
|
127
|
+
|
128
|
+
, or something else you find appropriate.
|
129
|
+
|
130
|
+
|
131
|
+
Feedback
|
132
|
+
--------
|
133
|
+
|
134
|
+
Send bug reports, suggestions and criticisms through [project's page on GitHub](http://github.com/dadooda/irb_hacks).
|
data/VERSION.yml
ADDED
data/irb_hacks.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{irb_hacks}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Alex Fortuna"]
|
12
|
+
s.date = %q{2010-01-14}
|
13
|
+
s.description = %q{Yet another set of IRB hacks}
|
14
|
+
s.email = %q{alex.r@askit.org}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"CHANGELOG.md",
|
20
|
+
"README.md",
|
21
|
+
"VERSION.yml",
|
22
|
+
"irb_hacks.gemspec",
|
23
|
+
"lib/irb_hacks.rb",
|
24
|
+
"lib/irb_hacks/core_ext/kernel/a_and_ae.rb",
|
25
|
+
"lib/irb_hacks/core_ext/kernel/less.rb",
|
26
|
+
"lib/irb_hacks/snippet.rb"
|
27
|
+
]
|
28
|
+
s.homepage = %q{http://github.com/dadooda/irb_hacks}
|
29
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
30
|
+
s.require_paths = ["lib"]
|
31
|
+
s.rubygems_version = %q{1.3.5}
|
32
|
+
s.summary = %q{Yet another set of IRB hacks}
|
33
|
+
|
34
|
+
if s.respond_to? :specification_version then
|
35
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
36
|
+
s.specification_version = 3
|
37
|
+
|
38
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
39
|
+
else
|
40
|
+
end
|
41
|
+
else
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module IrbHacks #:nodoc:
|
2
|
+
module CoreExtensions #:nodoc:
|
3
|
+
module Kernel #:nodoc:
|
4
|
+
module SingletonMethods
|
5
|
+
# Run code snippet.
|
6
|
+
# See <tt>IrbHacks::Snippet.run</tt>.
|
7
|
+
def a(*args, &block)
|
8
|
+
IrbHacks::Snippet.run(*args, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Edit code snippet.
|
12
|
+
# See <tt>IrbHacks::Snippet.edit</tt>.
|
13
|
+
def ae(*args)
|
14
|
+
IrbHacks::Snippet.edit(*args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
::Kernel.extend SingletonMethods
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Kernel #:nodoc:
|
24
|
+
private
|
25
|
+
|
26
|
+
def a(*args, &block)
|
27
|
+
::Kernel.a(*args, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def ae(*args)
|
31
|
+
::Kernel.ae(*args)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module IrbHacks #:nodoc:
|
2
|
+
module CoreExtensions #:nodoc:
|
3
|
+
module Kernel #:nodoc:
|
4
|
+
module SingletonMethods
|
5
|
+
# Dump program data with GNU <tt>less</tt>.
|
6
|
+
#
|
7
|
+
# Plain form:
|
8
|
+
# less "hello", "world"
|
9
|
+
# less mydata
|
10
|
+
#
|
11
|
+
# Block form:
|
12
|
+
# less do
|
13
|
+
# puts "hello, world"
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# less(:stderr => true) do
|
17
|
+
# puts "to stdout"
|
18
|
+
# STDERR.puts "to stderr"
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Block form options:
|
22
|
+
# :stderr => T|F # Redirect STDERR too.
|
23
|
+
#
|
24
|
+
# If block form option is <tt>String</tt> or <tt>Symbol</tt>, it's automatically
|
25
|
+
# converted to a hash like <tt>{:var => true}</tt>. Thus, <tt>less(:stderr => true)</tt>
|
26
|
+
# and <tt>less(:stderr)</tt> are identical.
|
27
|
+
def less(*args, &block)
|
28
|
+
less_cmd = IrbHacks.less_cmd
|
29
|
+
|
30
|
+
if not block
|
31
|
+
# Non-block invocation.
|
32
|
+
if args.size < 1
|
33
|
+
# We're interactive anyway. Why not give user a quick prompt?
|
34
|
+
STDERR.puts "Nothing to show. Invoke as less(args) or less(options, &block)."
|
35
|
+
else
|
36
|
+
File.popen(less_cmd, "w") do |f|
|
37
|
+
f.puts args
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
# Block invocation.
|
42
|
+
|
43
|
+
# Handle options.
|
44
|
+
options = {}
|
45
|
+
|
46
|
+
args.each do |arg|
|
47
|
+
if arg.is_a? Hash
|
48
|
+
##p "arg hash", arg
|
49
|
+
options.merge! arg
|
50
|
+
elsif [Symbol, String].include? arg.class
|
51
|
+
##p "arg sym/str", arg
|
52
|
+
options.merge! arg.to_sym => true
|
53
|
+
else
|
54
|
+
raise ArgumentError, "Unsupported argument #{arg.inspect}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
o_stderr = (v = options.delete(:stderr)).nil?? false : v
|
59
|
+
|
60
|
+
raise ArgumentError, "Unknown option(s): #{options.inspect}" if not options.empty?
|
61
|
+
|
62
|
+
old_stdout = STDOUT.clone
|
63
|
+
old_stderr = STDERR.clone if o_stderr
|
64
|
+
|
65
|
+
File.popen(less_cmd, "w") do |f|
|
66
|
+
STDOUT.reopen(f)
|
67
|
+
STDERR.reopen(f) if o_stderr
|
68
|
+
yield
|
69
|
+
STDOUT.reopen(old_stdout)
|
70
|
+
STDERR.reopen(old_stderr) if o_stderr
|
71
|
+
end
|
72
|
+
end # if block or no block.
|
73
|
+
|
74
|
+
nil
|
75
|
+
end # less
|
76
|
+
end # SingletonMethods
|
77
|
+
|
78
|
+
::Kernel.extend SingletonMethods
|
79
|
+
end # Kernel
|
80
|
+
end # CoreExtensions
|
81
|
+
end
|
82
|
+
|
83
|
+
module Kernel #:nodoc:
|
84
|
+
private
|
85
|
+
|
86
|
+
def less(*args, &block)
|
87
|
+
::Kernel.less(*args, &block)
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "readline"
|
2
|
+
|
3
|
+
module IrbHacks #:nodoc:
|
4
|
+
module Snippet
|
5
|
+
HISTORY_FILE = File.join(ENV["HOME"], ".irb_snippet_history")
|
6
|
+
HISTORY_SIZE = 20
|
7
|
+
|
8
|
+
# Edit code snippet.
|
9
|
+
def self.edit
|
10
|
+
##p "R::H before", Readline::HISTORY.to_a
|
11
|
+
##p "@history at inv", @history
|
12
|
+
|
13
|
+
# Push stuff into RL history.
|
14
|
+
npushed = @history.size
|
15
|
+
@history.each {|s| Readline::HISTORY.push s}
|
16
|
+
|
17
|
+
##p "R::H after push", Readline::HISTORY.to_a
|
18
|
+
|
19
|
+
# NOTE: Readline help is missing. Copied from somewhere else.
|
20
|
+
input = Readline.readline("(snippet)>> ", true)
|
21
|
+
|
22
|
+
if not input.empty?
|
23
|
+
# Accept entry.
|
24
|
+
@history << input
|
25
|
+
|
26
|
+
# ["wan", "tew", "free", "tew"] should render into ["wan", "free", "tew"] with "tew" being the last input shippet.
|
27
|
+
@history = (@history.reverse.uniq).reverse
|
28
|
+
end
|
29
|
+
|
30
|
+
# Pop stuff out of RL history.
|
31
|
+
(npushed + 1).times {Readline::HISTORY.pop}
|
32
|
+
|
33
|
+
##p "R::H after", Readline::HISTORY.to_a
|
34
|
+
|
35
|
+
# Save history -- we can't know when the session is going to end.
|
36
|
+
save_history
|
37
|
+
|
38
|
+
# Don't clutter IRB screen with anything extra.
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.history
|
43
|
+
@history
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.history=(ar)
|
47
|
+
@history = ar
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.load_history
|
51
|
+
@history = begin
|
52
|
+
content = File.read(HISTORY_FILE)
|
53
|
+
YAML.load(content)
|
54
|
+
rescue
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
@history = [%{puts "YOUR test code here"}] if not @history.is_a? Array
|
59
|
+
end
|
60
|
+
|
61
|
+
# Run code snippet.
|
62
|
+
def self.run(*args, &block)
|
63
|
+
if (code = @history.last)
|
64
|
+
eval(code, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.save_history
|
69
|
+
# Truncate a saved version of @history.
|
70
|
+
hist = @history.size > HISTORY_SIZE ? @history.slice(-HISTORY_SIZE..-1) : @history
|
71
|
+
File.open(HISTORY_FILE, "w") do |f|
|
72
|
+
f.write YAML.dump(hist)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#--------------------------------------- Init
|
77
|
+
|
78
|
+
load_history
|
79
|
+
end # Snippet
|
80
|
+
end # IrbHacks
|
data/lib/irb_hacks.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), "irb_hacks/**/*.rb")].each {|fn| require fn}
|
2
|
+
|
3
|
+
module IrbHacks
|
4
|
+
def self.less_cmd
|
5
|
+
@less_cmd
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.less_cmd=(cmd)
|
9
|
+
@less_cmd = cmd
|
10
|
+
end
|
11
|
+
|
12
|
+
#--------------------------------------- Defaults
|
13
|
+
|
14
|
+
self.less_cmd = "less -R"
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: irb_hacks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Fortuna
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-14 00:00:00 +03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Yet another set of IRB hacks
|
17
|
+
email: alex.r@askit.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.md
|
24
|
+
files:
|
25
|
+
- CHANGELOG.md
|
26
|
+
- README.md
|
27
|
+
- VERSION.yml
|
28
|
+
- irb_hacks.gemspec
|
29
|
+
- lib/irb_hacks.rb
|
30
|
+
- lib/irb_hacks/core_ext/kernel/a_and_ae.rb
|
31
|
+
- lib/irb_hacks/core_ext/kernel/less.rb
|
32
|
+
- lib/irb_hacks/snippet.rb
|
33
|
+
has_rdoc: true
|
34
|
+
homepage: http://github.com/dadooda/irb_hacks
|
35
|
+
licenses: []
|
36
|
+
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options:
|
39
|
+
- --charset=UTF-8
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.3.5
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: Yet another set of IRB hacks
|
61
|
+
test_files: []
|
62
|
+
|