hack_tree 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/.gitignore +10 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +74 -0
- data/Rakefile +1 -0
- data/hack_tree.gemspec +22 -0
- data/hacks/hack_tree/reload.rb +18 -0
- data/hacks/ls.rb +74 -0
- data/lib/generators/hack_tree/USAGE +7 -0
- data/lib/generators/hack_tree/hack_tree_generator.rb +9 -0
- data/lib/generators/hack_tree/templates/INSTALL +20 -0
- data/lib/generators/hack_tree/templates/hack_tree.rb +4 -0
- data/lib/generators/hack_tree/templates/hello.rb +16 -0
- data/lib/hack_tree/action_context.rb +125 -0
- data/lib/hack_tree/config.rb +27 -0
- data/lib/hack_tree/dsl_context.rb +95 -0
- data/lib/hack_tree/instance.rb +153 -0
- data/lib/hack_tree/node/base.rb +34 -0
- data/lib/hack_tree/node/group.rb +8 -0
- data/lib/hack_tree/node/hack.rb +10 -0
- data/lib/hack_tree/node.rb +13 -0
- data/lib/hack_tree/parser/base.rb +26 -0
- data/lib/hack_tree/parser/desc.rb +66 -0
- data/lib/hack_tree/tools.rb +26 -0
- data/lib/hack_tree.rb +233 -0
- data/spec/lib/hack_tree/parser/desc_spec/000,brief.txt +1 -0
- data/spec/lib/hack_tree/parser/desc_spec/000,full.txt +3 -0
- data/spec/lib/hack_tree/parser/desc_spec/000.txt +5 -0
- data/spec/lib/hack_tree/parser/desc_spec/010,brief.txt +1 -0
- data/spec/lib/hack_tree/parser/desc_spec/010,full.txt +3 -0
- data/spec/lib/hack_tree/parser/desc_spec/010.txt +8 -0
- data/spec/lib/hack_tree/parser/desc_spec.rb +50 -0
- data/spec/lib/hack_tree/parser/spec_helper.rb +3 -0
- data/spec/lib/hack_tree/spec_helper.rb +3 -0
- data/spec/lib/spec_helper.rb +3 -0
- data/spec/spec_helper.rb +53 -0
- metadata +94 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module HackTree
|
2
|
+
module Node
|
3
|
+
class Base
|
4
|
+
# Brief 1-line description, if present.
|
5
|
+
attr_accessor :brief_desc
|
6
|
+
|
7
|
+
# Multi-line description, if present.
|
8
|
+
attr_accessor :full_desc
|
9
|
+
|
10
|
+
# Node name, Symbol.
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
# Parent group or <tt>nil</tt>.
|
14
|
+
attr_accessor :parent
|
15
|
+
|
16
|
+
def initialize(attrs = {})
|
17
|
+
attrs.each {|k, v| send("#{k}=", v)}
|
18
|
+
end
|
19
|
+
|
20
|
+
# global_name # => "hello"
|
21
|
+
# global_name # => "rails.db.tables"
|
22
|
+
def global_name
|
23
|
+
pcs = []
|
24
|
+
cursor = self
|
25
|
+
begin
|
26
|
+
pcs << cursor.name
|
27
|
+
cursor = cursor.parent
|
28
|
+
end while cursor
|
29
|
+
|
30
|
+
pcs.reverse.join(".")
|
31
|
+
end
|
32
|
+
end # Base
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module HackTree
|
2
|
+
module Parser
|
3
|
+
# Base class for parsers. Parsers generally process text into collection(s).
|
4
|
+
class Base
|
5
|
+
def initialize(attrs = {})
|
6
|
+
attrs.each {|k, v| send("#{k}=", v)}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Synonym of #process.
|
10
|
+
def [](content)
|
11
|
+
process(content)
|
12
|
+
end
|
13
|
+
|
14
|
+
def process(content)
|
15
|
+
# NOTE: In parser meaning "content" argument name looks more solid. For mapper "data" is more appropriate. Both are okay for their cases.
|
16
|
+
raise "Redefine `process` in your class (#{self.class})"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def require_attr(attr)
|
22
|
+
send(attr) or raise "`#{attr}` is not set"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "base")
|
2
|
+
|
3
|
+
module HackTree
|
4
|
+
module Parser
|
5
|
+
# DSL <tt>desc</tt> parser.
|
6
|
+
class Desc < Base
|
7
|
+
# Parse description text, always return Array of 2 elements.
|
8
|
+
#
|
9
|
+
# process(content) # => [nil, nil]. Neither brief nor full description is present.
|
10
|
+
# process(content) # => ["...", nil]. Brief description is present, full isn't.
|
11
|
+
# process(content) # => ["...", "..."]. Both brief and full descriptions are present.
|
12
|
+
def process(content)
|
13
|
+
lines = content.lstrip.lines.to_a
|
14
|
+
return [nil, nil] if lines.empty?
|
15
|
+
|
16
|
+
# If we're here, `brief` is certainly present.
|
17
|
+
brief = lines.shift.rstrip
|
18
|
+
|
19
|
+
# Extract full lines with original indentation on the left.
|
20
|
+
|
21
|
+
indented_lines = []
|
22
|
+
gap = true # We're skipping the gap between the brief and the full.
|
23
|
+
|
24
|
+
lines.each do |line|
|
25
|
+
line = line.rstrip
|
26
|
+
next if gap and line.empty?
|
27
|
+
|
28
|
+
# First non-empty line, the gap is over.
|
29
|
+
gap = false
|
30
|
+
|
31
|
+
indented_lines << line
|
32
|
+
end
|
33
|
+
|
34
|
+
# Compute minimum indentation level. Empty lines don't count.
|
35
|
+
indent = indented_lines.reject(&:empty?).map do |s|
|
36
|
+
s.match(/\A(\s*)\S/)[1].size
|
37
|
+
end.min.to_i
|
38
|
+
|
39
|
+
# Apply indentation.
|
40
|
+
unindented_lines = indented_lines.map do |line|
|
41
|
+
line.empty?? line : line[indent..-1]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Reject empty lines at the end.
|
45
|
+
final_lines = []
|
46
|
+
buf = []
|
47
|
+
unindented_lines.each do |line|
|
48
|
+
# Accumulate empty lines.
|
49
|
+
if line.empty?
|
50
|
+
buf << line
|
51
|
+
next
|
52
|
+
end
|
53
|
+
|
54
|
+
# Non-empty line, flush `buf` and start over.
|
55
|
+
final_lines += buf + [line]
|
56
|
+
buf = []
|
57
|
+
end
|
58
|
+
|
59
|
+
[
|
60
|
+
brief,
|
61
|
+
(final_lines.join("\n") if not final_lines.empty?),
|
62
|
+
]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end # Parser
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module HackTree
|
2
|
+
module Tools
|
3
|
+
# compute_name_align(["alfa", "bravo"], 8..16) # => 8
|
4
|
+
def self.compute_name_align(names, limit)
|
5
|
+
(v = eval(vn = "names")).is_a?(klass = Array) or raise ArgumentError, "`#{vn}` must be #{klass}, #{v.class} (#{v.inspect}) given"
|
6
|
+
(v = eval(vn = "limit")).is_a?(klass = Range) or raise ArgumentError, "`#{vn}` must be #{klass}, #{v.class} (#{v.inspect}) given"
|
7
|
+
|
8
|
+
computed = names.map(&:size).select {|n| n <= limit.max}.max.to_i
|
9
|
+
|
10
|
+
[limit.min, computed].max
|
11
|
+
end
|
12
|
+
|
13
|
+
# format_node_name(node) # => "hello"
|
14
|
+
# format_node_name(node, "yo") # => "yo"
|
15
|
+
def self.format_node_name(node, name = nil)
|
16
|
+
case node
|
17
|
+
when Node::Group
|
18
|
+
::HackTree.conf.group_format
|
19
|
+
when Node::Hack
|
20
|
+
::HackTree.conf.hack_format
|
21
|
+
else
|
22
|
+
raise ArgumentError, "Unknown node class #{node.class}, SE"
|
23
|
+
end % (name || node.name).to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/hack_tree.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
[
|
2
|
+
"hack_tree/**/*.rb",
|
3
|
+
].each do |fmask|
|
4
|
+
Dir[File.join(File.dirname(__FILE__), fmask)].each do |fn|
|
5
|
+
require fn
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module HackTree
|
10
|
+
VERSION = "0.1.0"
|
11
|
+
|
12
|
+
# Standard hacks bundled with the gem, their global names.
|
13
|
+
STD_HACKS = [
|
14
|
+
"hack_tree.reload",
|
15
|
+
"ls",
|
16
|
+
]
|
17
|
+
|
18
|
+
# Clear everything. See Instance#clear.
|
19
|
+
def self.clear
|
20
|
+
instance.clear
|
21
|
+
end
|
22
|
+
|
23
|
+
# Clear config. See Instance#clear_conf.
|
24
|
+
def self.clear_conf
|
25
|
+
instance.clear_conf
|
26
|
+
end
|
27
|
+
|
28
|
+
# Clear group/hack definitions ("nodes" in general). See Instance#clear_nodes.
|
29
|
+
def self.clear_nodes
|
30
|
+
instance.clear_nodes
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get configuration object.
|
34
|
+
#
|
35
|
+
# See also:
|
36
|
+
#
|
37
|
+
# * HackTree::Config
|
38
|
+
# * Instance#conf
|
39
|
+
def self.conf
|
40
|
+
instance.conf
|
41
|
+
end
|
42
|
+
|
43
|
+
# Define hacks.
|
44
|
+
#
|
45
|
+
# HackTree.define do
|
46
|
+
# group :greeting do
|
47
|
+
# desc "Say hello"
|
48
|
+
# hack :hello do |*args|
|
49
|
+
# puts "Hello, %s!" % (args[0] || "world")
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
def self.define(&block)
|
54
|
+
instance.define(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Enable HackTree globally.
|
58
|
+
#
|
59
|
+
# >> HackTree.enable
|
60
|
+
# Greetings.
|
61
|
+
# >> c
|
62
|
+
# hello # Say hello
|
63
|
+
# >> c.hello
|
64
|
+
# Hello, world!
|
65
|
+
#
|
66
|
+
# Options:
|
67
|
+
#
|
68
|
+
# :completion => T|F # Enable completion enhancement. Default is true.
|
69
|
+
# :with_std => [...] # Load only these standard hacks.
|
70
|
+
# :without_std => [...] # Load all but these standard hacks.
|
71
|
+
# :quiet => T|F # Be quiet. Default is false.
|
72
|
+
#
|
73
|
+
# Examples:
|
74
|
+
#
|
75
|
+
# TODO.
|
76
|
+
def self.enable(method_name = :c, options = {})
|
77
|
+
options = options.dup
|
78
|
+
o = {}
|
79
|
+
|
80
|
+
o[k = :completion] = (v = options.delete(k)).nil?? true : v
|
81
|
+
o[k = :with_std] = options.delete(k)
|
82
|
+
o[k = :without_std] = options.delete(k)
|
83
|
+
o[k = :quiet] = (v = options.delete(k)).nil?? false : v
|
84
|
+
|
85
|
+
raise ArgumentError, "Unknown option(s): #{options.inspect}" if not options.empty?
|
86
|
+
|
87
|
+
if o[:with_std] and o[:without_std]
|
88
|
+
# Exception is better than warning. It has a stack trace.
|
89
|
+
raise ArgumentError, "Options `:with_std` and `:without_std` are mutually exclusive"
|
90
|
+
end
|
91
|
+
|
92
|
+
@enabled_as = method_name
|
93
|
+
|
94
|
+
if not o[:quiet]
|
95
|
+
# Print the banner before everything. If there are warnings, we'll know they are related to us somehow.
|
96
|
+
::Kernel.puts "Console hacks are available. Use `%s`, `%s.hack?`, `%s.hack [args]`" % ([@enabled_as]*3)
|
97
|
+
end
|
98
|
+
|
99
|
+
# NOTE: This can't be put into `Instance`, it's a name-based global.
|
100
|
+
eval <<-EOT
|
101
|
+
module ::Kernel
|
102
|
+
private
|
103
|
+
|
104
|
+
def #{method_name}
|
105
|
+
::HackTree.instance.action
|
106
|
+
end
|
107
|
+
end
|
108
|
+
EOT
|
109
|
+
|
110
|
+
# Install completion enhancement.
|
111
|
+
if o[:completion]
|
112
|
+
old_proc = Readline.completion_proc
|
113
|
+
|
114
|
+
Readline.completion_proc = lambda do |input|
|
115
|
+
candidates = instance.completion_logic(input, :enabled_as => @enabled_as)
|
116
|
+
|
117
|
+
# NOTE: Block result.
|
118
|
+
if candidates.is_a? Array
|
119
|
+
candidates
|
120
|
+
elsif old_proc
|
121
|
+
# Pass control.
|
122
|
+
old_proc.call(input)
|
123
|
+
else
|
124
|
+
# Nothing we can do.
|
125
|
+
[]
|
126
|
+
end
|
127
|
+
end # @completion_proc =
|
128
|
+
end # if o[:completion]
|
129
|
+
|
130
|
+
# Load standard hacks.
|
131
|
+
|
132
|
+
global_names = if (ar = o[:with_std])
|
133
|
+
# White list.
|
134
|
+
STD_HACKS & ar.map(&:to_s)
|
135
|
+
elsif (ar = o[:without_std])
|
136
|
+
# Black list.
|
137
|
+
STD_HACKS - ar.map(&:to_s)
|
138
|
+
else
|
139
|
+
# Default.
|
140
|
+
STD_HACKS
|
141
|
+
end
|
142
|
+
|
143
|
+
global_names.each do |global_name|
|
144
|
+
bn = global_name.gsub(".", "/") + ".rb"
|
145
|
+
fn = File.join(File.dirname(__FILE__), "../hacks", bn)
|
146
|
+
load fn
|
147
|
+
end
|
148
|
+
|
149
|
+
nil
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.enabled_as
|
153
|
+
@enabled_as
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.instance
|
157
|
+
@instance ||= Instance.new
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#--------------------------------------- Junk
|
162
|
+
|
163
|
+
if false
|
164
|
+
# * Using array is a reliable way to ensure a newline after the banner.
|
165
|
+
::Kernel.puts [
|
166
|
+
#"",
|
167
|
+
"Console hacks are available. Use `%s`, `%s.hack?`, `%s.hack [args]`" % ([@enabled_as]*3),
|
168
|
+
#"",
|
169
|
+
]
|
170
|
+
end
|
171
|
+
|
172
|
+
if false
|
173
|
+
# Node (group/hack) regexp without delimiters.
|
174
|
+
NAME_REGEXP = /[a-zA-Z_]\w*/
|
175
|
+
|
176
|
+
# Node names which can't be used due to serious reasons.
|
177
|
+
FORBIDDEN_NAMES = [
|
178
|
+
:inspect,
|
179
|
+
:method_missing,
|
180
|
+
:to_s,
|
181
|
+
]
|
182
|
+
end
|
183
|
+
|
184
|
+
if false
|
185
|
+
# Create the action object.
|
186
|
+
#
|
187
|
+
# module Kernel
|
188
|
+
# # Access our hacks via <tt>c</tt>.
|
189
|
+
# def c
|
190
|
+
# ::HackTree.action
|
191
|
+
# end
|
192
|
+
# end
|
193
|
+
#
|
194
|
+
# >> c
|
195
|
+
# hello # Say hello
|
196
|
+
# >> c.hello
|
197
|
+
# Hello, world!
|
198
|
+
#
|
199
|
+
# See also ::enable.
|
200
|
+
def self.action
|
201
|
+
ActionContext.new(@nodes)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Clear self.
|
205
|
+
def self.clear
|
206
|
+
# Request re-initialization upon first use of any kind.
|
207
|
+
@is_initialized = false
|
208
|
+
end
|
209
|
+
|
210
|
+
# Access nodes (groups/hacks) created via the DSL.
|
211
|
+
def self.nodes
|
212
|
+
@nodes
|
213
|
+
end
|
214
|
+
|
215
|
+
# See #nodes.
|
216
|
+
def self.nodes=(ar)
|
217
|
+
@nodes = ar
|
218
|
+
end
|
219
|
+
|
220
|
+
# NOTE: We need this wrapper to create private singletons.
|
221
|
+
class << self
|
222
|
+
private
|
223
|
+
|
224
|
+
# On-the-fly initializer.
|
225
|
+
def _otf_init
|
226
|
+
return if @is_initialized
|
227
|
+
|
228
|
+
@is_initialized = true
|
229
|
+
@nodes = []
|
230
|
+
@dsl_root = DslContext.new(@nodes)
|
231
|
+
end
|
232
|
+
end # class << self
|
233
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Brief
|
@@ -0,0 +1 @@
|
|
1
|
+
Краткое описание
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe HackTree::Parser::Desc do
|
4
|
+
before :each do
|
5
|
+
@parser = described_class.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should generally work" do
|
9
|
+
sets = [
|
10
|
+
["", [nil, nil]],
|
11
|
+
[" ", [nil, nil]],
|
12
|
+
[" \t\n \n ", [nil, nil]],
|
13
|
+
["Brief", ["Brief", nil]],
|
14
|
+
[" Brief\t\t\n\n", ["Brief", nil]],
|
15
|
+
["\n\n\nBrief\t\t\n\n", ["Brief", nil]],
|
16
|
+
["Brief\nFull 1 \nFull 2", ["Brief", "Full 1\nFull 2"]],
|
17
|
+
["Brief\nFull 1 \nFull 2\n\n ", ["Brief", "Full 1\nFull 2"]],
|
18
|
+
["Brief\n\n\nFull 1\nFull 2", ["Brief", "Full 1\nFull 2"]],
|
19
|
+
["Brief\n Full 1\n Full 2", ["Brief", "Full 1\nFull 2"]],
|
20
|
+
["Brief\n Full 1\n Full 2", ["Brief", "Full 1\n Full 2"]],
|
21
|
+
["Brief\n Full 1\n\n Full 2", ["Brief", "Full 1\n\n Full 2"]],
|
22
|
+
["Brief\n Full 1\n \n Full 2", ["Brief", "Full 1\n\n Full 2"]],
|
23
|
+
|
24
|
+
# File-based tests for more complex cases.
|
25
|
+
[["000"], [["000,brief"], ["000,full"]]],
|
26
|
+
[["010"], [["010,brief"], ["010,full"]]],
|
27
|
+
]
|
28
|
+
|
29
|
+
path = Pathname(__FILE__[0..-4])
|
30
|
+
|
31
|
+
sets.each do |input_spec, expected_spec|
|
32
|
+
input = if input_spec.is_a? Array
|
33
|
+
# Input is a file reference.
|
34
|
+
File.read(path + "#{input_spec[0]}.txt")
|
35
|
+
else
|
36
|
+
# Input is plain.
|
37
|
+
input_spec
|
38
|
+
end
|
39
|
+
|
40
|
+
expected = expected_spec.map do |spec|
|
41
|
+
# Same rule as for input.
|
42
|
+
spec.is_a?(Array) ? File.read(path + "#{spec[0]}.txt") : spec
|
43
|
+
end
|
44
|
+
|
45
|
+
print_on_failure("-- input_spec:#{input_spec.inspect}") do
|
46
|
+
@parser[input].should == expected
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
# Load stuff.
|
4
|
+
[
|
5
|
+
"lib/**/*.rb",
|
6
|
+
].each do |fmask|
|
7
|
+
Dir["./#{fmask}"].each do |fn|
|
8
|
+
##puts "-- req '#{fn}'"
|
9
|
+
require fn
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: When this becomes a gem, use gem instead of direct copy.
|
14
|
+
module RSpec
|
15
|
+
module PrintOnFailure
|
16
|
+
module Helpers
|
17
|
+
# Output <tt>message</tt> before the failed tests in <tt>block</tt>. Useful when input and expected data
|
18
|
+
# are defined as collections.
|
19
|
+
#
|
20
|
+
# sets = [
|
21
|
+
# ["hello", "HELLO"],
|
22
|
+
# ["123", "456"],
|
23
|
+
# ]
|
24
|
+
#
|
25
|
+
# sets.each do |input, expected|
|
26
|
+
# print_on_failure("-- input:'#{input}'") do
|
27
|
+
# input.upcase.should == expected
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
def print_on_failure(message, &block)
|
31
|
+
begin
|
32
|
+
yield
|
33
|
+
rescue Exception
|
34
|
+
# Catch just everything, report and then re-run. The test may fail due to an exception, not necessarily unmatched expectation.
|
35
|
+
puts message
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
# 2.x.
|
45
|
+
RSpec.configure do |config|
|
46
|
+
config.include ::RSpec::PrintOnFailure::Helpers
|
47
|
+
end
|
48
|
+
rescue NameError
|
49
|
+
# 1.3.
|
50
|
+
Spec::Runner.configure do |config|
|
51
|
+
config.include ::RSpec::PrintOnFailure::Helpers
|
52
|
+
end
|
53
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hack_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alex Fortuna
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-04 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &71051990 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *71051990
|
25
|
+
description: HackTree lets you organize and share your console hacks in an effective
|
26
|
+
and uniform way. Blah-blah-blah.
|
27
|
+
email:
|
28
|
+
- alex.r@askit.org
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- .rspec
|
35
|
+
- Gemfile
|
36
|
+
- MIT-LICENSE
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- hack_tree.gemspec
|
40
|
+
- hacks/hack_tree/reload.rb
|
41
|
+
- hacks/ls.rb
|
42
|
+
- lib/generators/hack_tree/USAGE
|
43
|
+
- lib/generators/hack_tree/hack_tree_generator.rb
|
44
|
+
- lib/generators/hack_tree/templates/INSTALL
|
45
|
+
- lib/generators/hack_tree/templates/hack_tree.rb
|
46
|
+
- lib/generators/hack_tree/templates/hello.rb
|
47
|
+
- lib/hack_tree.rb
|
48
|
+
- lib/hack_tree/action_context.rb
|
49
|
+
- lib/hack_tree/config.rb
|
50
|
+
- lib/hack_tree/dsl_context.rb
|
51
|
+
- lib/hack_tree/instance.rb
|
52
|
+
- lib/hack_tree/node.rb
|
53
|
+
- lib/hack_tree/node/base.rb
|
54
|
+
- lib/hack_tree/node/group.rb
|
55
|
+
- lib/hack_tree/node/hack.rb
|
56
|
+
- lib/hack_tree/parser/base.rb
|
57
|
+
- lib/hack_tree/parser/desc.rb
|
58
|
+
- lib/hack_tree/tools.rb
|
59
|
+
- spec/lib/hack_tree/parser/desc_spec.rb
|
60
|
+
- spec/lib/hack_tree/parser/desc_spec/000,brief.txt
|
61
|
+
- spec/lib/hack_tree/parser/desc_spec/000,full.txt
|
62
|
+
- spec/lib/hack_tree/parser/desc_spec/000.txt
|
63
|
+
- spec/lib/hack_tree/parser/desc_spec/010,brief.txt
|
64
|
+
- spec/lib/hack_tree/parser/desc_spec/010,full.txt
|
65
|
+
- spec/lib/hack_tree/parser/desc_spec/010.txt
|
66
|
+
- spec/lib/hack_tree/parser/spec_helper.rb
|
67
|
+
- spec/lib/hack_tree/spec_helper.rb
|
68
|
+
- spec/lib/spec_helper.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
homepage: http://github.com/dadooda/hack_tree
|
71
|
+
licenses: []
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 1.8.10
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: Organize and share your console hacks
|
94
|
+
test_files: []
|