nandoc 0.0.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.
- data/README +124 -0
- data/Rakefile +53 -0
- data/bin/nandoc +6 -0
- data/doc/CREDITS.md +6 -0
- data/doc/FAQ/why-not-wiki.md +20 -0
- data/doc/FAQ.md +68 -0
- data/doc/TODOs-and-BUGs.md +15 -0
- data/doc/bar/baz.md +4 -0
- data/doc/bar/bliff.md +8 -0
- data/doc/foo.md +5 -0
- data/doc/getting-started.rb +13 -0
- data/doc/svg/less-fonts.svg +21 -0
- data/lib/nandoc/commands/create-nandoc-site.rb +225 -0
- data/lib/nandoc/commands/diff.rb +279 -0
- data/lib/nandoc/config.rb +58 -0
- data/lib/nandoc/cri-hacks.rb +13 -0
- data/lib/nandoc/data-source.rb +239 -0
- data/lib/nandoc/filters.rb +661 -0
- data/lib/nandoc/helpers/menu-bouncy.rb +109 -0
- data/lib/nandoc/helpers/site-map.rb +157 -0
- data/lib/nandoc/helpers/top-nav.rb +47 -0
- data/lib/nandoc/helpers.rb +42 -0
- data/lib/nandoc/item-class-hacks.rb +57 -0
- data/lib/nandoc/nandoc.persistent.json +3 -0
- data/lib/nandoc/parse-readme.rb +95 -0
- data/lib/nandoc/spec-doc/mini-test/spec-instance-methods.rb +0 -0
- data/lib/nandoc/spec-doc/mini-test.rb +105 -0
- data/lib/nandoc/spec-doc/mock-prompt.rb +121 -0
- data/lib/nandoc/spec-doc/support-modules.rb +158 -0
- data/lib/nandoc/spec-doc/test-case-agent.rb +57 -0
- data/lib/nandoc/spec-doc/test-framework-dispatcher.rb +15 -0
- data/lib/nandoc/spec-doc/test-framework-proxy.rb +78 -0
- data/lib/nandoc/spec-doc.rb +46 -0
- data/lib/nandoc/support/diff-proxy.rb +113 -0
- data/lib/nandoc/support/orphanage.rb +77 -0
- data/lib/nandoc/support/path-tardo.rb +85 -0
- data/lib/nandoc/support/regexp-enhance.rb +76 -0
- data/lib/nandoc/support/site-diff.rb +46 -0
- data/lib/nandoc/support/site-merge.rb +62 -0
- data/lib/nandoc/support/site-methods.rb +69 -0
- data/lib/nandoc/support/stream-colorizer.rb +203 -0
- data/lib/nandoc/support-modules.rb +270 -0
- data/lib/nandoc/test/diff-to-string.rb +251 -0
- data/lib/nandoc/test/minitest-extlib.rb +53 -0
- data/lib/nandoc/treebis/NOGIT-DOCS/NEWS.md +5 -0
- data/lib/nandoc/treebis/NOGIT-README.md +65 -0
- data/lib/nandoc/treebis/nandoc.persistent.json +3 -0
- data/lib/nandoc.rb +48 -0
- data/proto/README.md +31 -0
- data/proto/default/Rakefile +1 -0
- data/proto/default/Rules +46 -0
- data/proto/default/config.yaml +57 -0
- data/proto/default/content/css/nanoc-dist-altered.css +213 -0
- data/proto/default/content/css/trollop-subset.css +116 -0
- data/proto/default/content/js/menu-bouncy.js +126 -0
- data/proto/default/content/stylesheet.css.diff +20 -0
- data/proto/default/content/vendor/jquery-1.3.js +4241 -0
- data/proto/default/content/vendor/jquery.easing.1.3.js +205 -0
- data/proto/default/layouts/default.html +70 -0
- data/proto/default/lib/default.orig.rb +2 -0
- data/proto/default/lib/default.rb +5 -0
- data/proto/default/treebis-task.rb +28 -0
- data/proto/misc/orphan-surrogate.md +6 -0
- data/test/test.rb +102 -0
- metadata +166 -0
@@ -0,0 +1,270 @@
|
|
1
|
+
module NanDoc
|
2
|
+
module CliCommandHelpers
|
3
|
+
def command_name
|
4
|
+
(/::([_a-z0-9]+)\Z/i =~ self.class.to_s and base = $1) or fail('no')
|
5
|
+
base.gsub(/([a-z])([A-Z])/){ "#{$1}-#{$2}" }.downcase
|
6
|
+
end
|
7
|
+
def invite_to_more_command_help
|
8
|
+
"see `nandoc help #{command_name}` for more information."
|
9
|
+
end
|
10
|
+
def invocation_name
|
11
|
+
File.basename($PROGRAM_NAME)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
module StringFormatting; end
|
15
|
+
module OptsNormalizer
|
16
|
+
def normalize_opts opts
|
17
|
+
opts = opts.dup
|
18
|
+
opts.keys.select{|x| x.to_s.index('-') }.each do |k|
|
19
|
+
opts[k.to_s.gsub('-','_').to_sym] = opts.delete(k)
|
20
|
+
end
|
21
|
+
opts
|
22
|
+
end
|
23
|
+
def unnormalize_opt_keys keys
|
24
|
+
keys.map{|x| unnormalize_opt_key(x)}
|
25
|
+
end
|
26
|
+
def unnormalize_opt_key key
|
27
|
+
"--#{key.to_s.gsub('_','-')}"
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# only call this if you are like a ::Cri::Command object with
|
32
|
+
# all the nanDoc hacks. ick. This is a temprary hack. Trollop et al
|
33
|
+
# do this better.
|
34
|
+
#
|
35
|
+
def exclusive_opt_flags opts, &block
|
36
|
+
Exclusive.new(&block).parse(self, opts)
|
37
|
+
end
|
38
|
+
|
39
|
+
class Exclusive
|
40
|
+
include OptsNormalizer
|
41
|
+
def initialize &block
|
42
|
+
@exclusive_flag_keys = nil
|
43
|
+
@default_short = nil
|
44
|
+
@default_key = nil
|
45
|
+
@notice_stream = $stderr
|
46
|
+
instance_eval(&block)
|
47
|
+
fail('definition block needs at least flags()') unless
|
48
|
+
@exclusive_flag_keys
|
49
|
+
end
|
50
|
+
def flags * exclusive_flag_keys
|
51
|
+
@exclusive_flag_keys = exclusive_flag_keys
|
52
|
+
end
|
53
|
+
# params: [short_name_string] name_key
|
54
|
+
def default *a
|
55
|
+
if a.first.kind_of?(String)
|
56
|
+
@default_short = a.shift
|
57
|
+
end
|
58
|
+
if a.first.kind_of?(Symbol)
|
59
|
+
@default_key = a.shift
|
60
|
+
else
|
61
|
+
fail("bad args: #{a.first.inspect}")
|
62
|
+
end
|
63
|
+
fail("extra args: #{a.inspect}") if a.any?
|
64
|
+
end
|
65
|
+
def notice_stream mixed
|
66
|
+
@notice_stream = mixed
|
67
|
+
end
|
68
|
+
def parse cmd, opts
|
69
|
+
these = @exclusive_flag_keys & opts.keys
|
70
|
+
if these.empty? && @default_key
|
71
|
+
if @notice_stream
|
72
|
+
msg =
|
73
|
+
["using default: "+unnormalize_opt_key(@default_key),
|
74
|
+
@default_short ? "(#{@default_short})" : nil
|
75
|
+
].compact.join(' ')
|
76
|
+
@notice_stream.puts msg
|
77
|
+
end
|
78
|
+
these.push(@default_key)
|
79
|
+
end
|
80
|
+
if these.size > 1
|
81
|
+
flags = unnormalize_opt_keys(@exclusive_flag_keys)
|
82
|
+
cmd.task_abort <<-ABORT.gsub(/^ */,'')
|
83
|
+
#{flags.join(' and ')} are mutually exclusive.
|
84
|
+
usage: #{cmd.usage}
|
85
|
+
#{cmd.invite_to_more_command_help}
|
86
|
+
ABORT
|
87
|
+
end
|
88
|
+
these.first
|
89
|
+
end
|
90
|
+
end
|
91
|
+
class OptEnum
|
92
|
+
include OptsNormalizer, StringFormatting
|
93
|
+
def initialize(&block)
|
94
|
+
instance_eval(&block)
|
95
|
+
end
|
96
|
+
def command cmd
|
97
|
+
@command = cmd
|
98
|
+
end
|
99
|
+
def default str
|
100
|
+
@default = str
|
101
|
+
end
|
102
|
+
def name name
|
103
|
+
@name = name
|
104
|
+
end
|
105
|
+
def parse opts
|
106
|
+
found = nil
|
107
|
+
if opts.key?(@name)
|
108
|
+
v = opts[@name]
|
109
|
+
re = /\A#{Regexp.escape(v)}/
|
110
|
+
founds = @values.grep(re)
|
111
|
+
case founds.size
|
112
|
+
when 0; invalid(v)
|
113
|
+
when 1; found = founds.first
|
114
|
+
else found = founds.detect{|f| f==v} or too_many(founds)
|
115
|
+
end
|
116
|
+
elsif(@default)
|
117
|
+
found = @default
|
118
|
+
else
|
119
|
+
found = nil
|
120
|
+
end
|
121
|
+
opts[@name] = found if found # normalize short versions
|
122
|
+
found
|
123
|
+
end
|
124
|
+
def values *v
|
125
|
+
v = v.first if v.size==1 && Array === v
|
126
|
+
@values = v
|
127
|
+
end
|
128
|
+
private
|
129
|
+
def coda
|
130
|
+
"usage: #{@command.usage}\n#{@command.invite_to_more_command_help}"
|
131
|
+
end
|
132
|
+
def invalid val
|
133
|
+
@command.task_abort("invalid value #{val.inspect} for "<<
|
134
|
+
"#{long_name}. #{valid_values_are}\n#{coda}")
|
135
|
+
end
|
136
|
+
def long_name
|
137
|
+
unnormalize_opt_key(@name)
|
138
|
+
end
|
139
|
+
def too_many these
|
140
|
+
@command.task_abort("did you mean " <<
|
141
|
+
oxford_comma(these,' or ', "ed)<<" for #{long_name}?\n#{coda}")
|
142
|
+
end
|
143
|
+
def valid_values_are
|
144
|
+
"valid values are " << oxford_comma(@values,"ed)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
module PathHelper
|
149
|
+
def assert_path name, *paths
|
150
|
+
paths.each do |p|
|
151
|
+
unless File.exist?(p)
|
152
|
+
task_abort("#{name} does not exist: #{p}")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
module StringFormatting
|
158
|
+
def basename_no_extension str
|
159
|
+
/([^\/\.]+)(?:\.[^\.\/]+)?\Z/ =~ str ? $1 : nil
|
160
|
+
end
|
161
|
+
def indent str, indent
|
162
|
+
str.gsub(/^/, indent)
|
163
|
+
end
|
164
|
+
def no_blank_lines str
|
165
|
+
str.gsub(/\n[[:space:]]*\n/, "\n")
|
166
|
+
end
|
167
|
+
def no_leading_ws str
|
168
|
+
str.sub(/\A[[:space:]]+/, '')
|
169
|
+
end
|
170
|
+
def no_trailing_ws str
|
171
|
+
str.sub(/[[:space:]]+\Z/, '')
|
172
|
+
end
|
173
|
+
def oxford_comma items, final = ' and ', "er
|
174
|
+
items = items.map("er) if quoter
|
175
|
+
these = []
|
176
|
+
these.push final if items.size > 1
|
177
|
+
these.concat(Array.new(items.size-2,', ')) if items.size > 2
|
178
|
+
these.reverse!
|
179
|
+
items.zip(these).flatten.compact.join
|
180
|
+
end
|
181
|
+
def quoted
|
182
|
+
proc{|x| "\"#{x}\"" }
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# must respond to tab() and tabs()
|
187
|
+
# reindent a block by striping leading whitespace from lines evenly
|
188
|
+
# and then re-indenting each line according to our indent.
|
189
|
+
# this could be simpler, it has been more complicated
|
190
|
+
# we do it languidly because we can
|
191
|
+
#
|
192
|
+
def reindent h1, offset=0
|
193
|
+
indent_by = tab * (tabs+offset)
|
194
|
+
unindent_by = (/\A([[:space:]]+)/ =~ h1 && $1) or
|
195
|
+
fail('regex fail -- not sure if we need this to be so strict')
|
196
|
+
h2 = no_blank_lines(h1) # careful. will mess up with <pre> etc
|
197
|
+
return h2 if unindent_by == indent_by
|
198
|
+
h3 = unindent(h2, unindent_by)
|
199
|
+
h4 = indent(h3, indent_by)
|
200
|
+
h4
|
201
|
+
end
|
202
|
+
|
203
|
+
def unindent str, by
|
204
|
+
str.gsub(/^#{Regexp.escape(by)}/, '')
|
205
|
+
end
|
206
|
+
end
|
207
|
+
module SecretParent
|
208
|
+
#
|
209
|
+
# set parent attribute without it showing up in inspect() dumps
|
210
|
+
#
|
211
|
+
def parent= mixed
|
212
|
+
fail("no clear_parent() available yet.") unless mixed
|
213
|
+
@has_parent = !! mixed
|
214
|
+
class << self; self end.send(:define_method, :parent){mixed}
|
215
|
+
mixed # maybe chain assignmnet of 1 parent to several cx at once
|
216
|
+
end
|
217
|
+
def parent?
|
218
|
+
instance_variable_defined?('@has_parent') && @has_parent # no warnings
|
219
|
+
end
|
220
|
+
def parent
|
221
|
+
nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
module SharedAttrReader
|
225
|
+
#
|
226
|
+
# this is a specialized form of delegator pattern: let one object
|
227
|
+
# use the responses from another object for a set of accessors
|
228
|
+
#
|
229
|
+
def shared_attr_reader *list
|
230
|
+
fail('no inehiritance yet') if method_defined?(:shared=)
|
231
|
+
sm = Module.new
|
232
|
+
name = self.to_s+'::SharedAttrReaders'
|
233
|
+
sing = class << sm; self end
|
234
|
+
sing.send(:define_method, :name){name}
|
235
|
+
sing.send(:alias_method, :inspect, :name)
|
236
|
+
list.each do |attrib|
|
237
|
+
sm.send(:define_method, attrib){ shared.send(attrib) }
|
238
|
+
end
|
239
|
+
fail('no') if method_defined?(:shared)
|
240
|
+
define_method(:shared){ self }
|
241
|
+
define_method(:shared=) do |source|
|
242
|
+
sing = class << self; self end
|
243
|
+
sing.send(:define_method, :shared){ source }
|
244
|
+
sing.send(:include, sm) # wow cool that this works w/o having
|
245
|
+
# to Module#undef_method
|
246
|
+
source
|
247
|
+
end
|
248
|
+
nil
|
249
|
+
end
|
250
|
+
end
|
251
|
+
module TaskCommon
|
252
|
+
def task_abort msg
|
253
|
+
if msg.index("for more info") # not mr. right, mr. right now
|
254
|
+
tail = ''
|
255
|
+
else
|
256
|
+
last = msg[-1].chr
|
257
|
+
tail = ".?!".index(last) ? ' ' : ("\n"==last ? '' : '. ')
|
258
|
+
tail << 'Aborting.'
|
259
|
+
end
|
260
|
+
$stderr.puts "nanDoc: #{msg}#{tail}"
|
261
|
+
exit 1
|
262
|
+
end
|
263
|
+
end
|
264
|
+
module CliCommandHelpers
|
265
|
+
include OptsNormalizer, TaskCommon, PathHelper
|
266
|
+
end
|
267
|
+
module PathHelper
|
268
|
+
include TaskCommon
|
269
|
+
end
|
270
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
##
|
4
|
+
# turn the output of Diff::LCS.diff into a string similar
|
5
|
+
# to what would be retured by `diff`, optionally make it looks
|
6
|
+
# *sorta* like colorized output from git-diff
|
7
|
+
#
|
8
|
+
# @todo move this to minitest branch
|
9
|
+
# @todo this gives different results than diff for some stuff!!??
|
10
|
+
#
|
11
|
+
# poor man's diff:
|
12
|
+
# file_a, file_b = ARGV.shift(2)
|
13
|
+
# puts DiffToString.files_diff(file_a, file_b)
|
14
|
+
#
|
15
|
+
#
|
16
|
+
class DiffToString
|
17
|
+
module Style
|
18
|
+
Codes = {:red=>'31', :green=>'32', :bold=>'1', :red_bg=>'41',
|
19
|
+
:magenta => '35'
|
20
|
+
}
|
21
|
+
def stylize str, *codes
|
22
|
+
if 1 == codes.size
|
23
|
+
if codes.first.nil?
|
24
|
+
return str
|
25
|
+
elsif codes.first.kind_of?(Array)
|
26
|
+
codes = codes.first
|
27
|
+
end
|
28
|
+
end
|
29
|
+
codes = codes.map{|c| Codes[c]}
|
30
|
+
"\033[#{codes * ';'}m#{str}\033[0m";
|
31
|
+
end
|
32
|
+
end
|
33
|
+
include Style
|
34
|
+
|
35
|
+
class << self
|
36
|
+
# these are just convenience wrappers for instance methods
|
37
|
+
%w(diff files_diff strings_diff gitlike!).each do |meth|
|
38
|
+
define_method(meth){|*a| new.send(meth,*a) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
def initialize
|
42
|
+
@add_style = nil
|
43
|
+
@add_header = '%sa%s'
|
44
|
+
@change_header = '%sc%s'
|
45
|
+
@context = nil
|
46
|
+
@del_header = '%sd%s'
|
47
|
+
@del_style = nil
|
48
|
+
@last_range = nil
|
49
|
+
@left = '<'
|
50
|
+
@line_no_style = nil
|
51
|
+
@right = '>'
|
52
|
+
@separator_line = '---'
|
53
|
+
@trailing_whitespace_style = nil
|
54
|
+
end
|
55
|
+
attr_accessor :arr1, :arr2 # this is awful bleeding
|
56
|
+
def context= mixed
|
57
|
+
fail("no #{mixed.inspect}") unless mixed.kind_of?(Fixnum) && mixed >= 0
|
58
|
+
@context = mixed == 0 ? nil : mixed
|
59
|
+
end
|
60
|
+
def gitlike!
|
61
|
+
common_header = '@@ -%s, +%s @@'
|
62
|
+
@add_header = common_header
|
63
|
+
@add_style = [:bold, :green]
|
64
|
+
@change_header = common_header
|
65
|
+
@del_style = [:bold, :red]
|
66
|
+
@del_header = common_header
|
67
|
+
@header_style = [:bold, :magenta]
|
68
|
+
@left = '-'
|
69
|
+
@right = '+'
|
70
|
+
@separator_line = nil
|
71
|
+
@trailing_whitespace_style = [:red_bg]
|
72
|
+
self
|
73
|
+
end
|
74
|
+
def arrays_diff arr1, arr2, opts={}
|
75
|
+
diff = Diff::LCS.diff(arr1, arr2)
|
76
|
+
@arr1, @arr2 = arr1, arr2
|
77
|
+
consume_opts_for_diff(opts)
|
78
|
+
diff_to_str diff, opts
|
79
|
+
end
|
80
|
+
def diff mixed1, mixed2, opts={}
|
81
|
+
case (x=[mixed1.class, mixed2.class])
|
82
|
+
when [Array,Array]; arrays_diff(mixed1,mixed2,opts)
|
83
|
+
when [String,String]; strings_diff(mixed1,mixed2,opts)
|
84
|
+
else "no diff strategy for #{x.inspect}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
def files_diff a, b, opts={:sep=>"\n"}
|
88
|
+
str1 = File.read(a)
|
89
|
+
str2 = File.read(b)
|
90
|
+
strings_diff(str1, str2, opts)
|
91
|
+
end
|
92
|
+
def strings_diff a, b, opts={}
|
93
|
+
opts = opts.merge(:sep=>"\n")
|
94
|
+
arr1 = str_to_arr a, opts[:sep]
|
95
|
+
arr2 = str_to_arr b, opts[:sep]
|
96
|
+
arrays_diff(arr1, arr2, opts)
|
97
|
+
end
|
98
|
+
def str_to_arr str, sep
|
99
|
+
str.split(sep, -1)
|
100
|
+
end
|
101
|
+
def diff_to_str diff, opts
|
102
|
+
consume_opts_for_diff opts
|
103
|
+
@out = StringIO.new
|
104
|
+
@offset_offset = -1
|
105
|
+
diff.each do |chunk|
|
106
|
+
context_pre(chunk) if @context
|
107
|
+
dels = []
|
108
|
+
adds = []
|
109
|
+
start_add = last_add = start_del = last_del = nil
|
110
|
+
chunk.each do |change|
|
111
|
+
case change.action
|
112
|
+
when '+'
|
113
|
+
start_add ||= change.position + 1
|
114
|
+
last_add = change.position + 1
|
115
|
+
adds.push change.element
|
116
|
+
when '-'
|
117
|
+
start_del ||= change.position + 1
|
118
|
+
last_del = change.position + 1
|
119
|
+
dels.push change.element
|
120
|
+
else
|
121
|
+
fail("no: #{change.action}")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
if adds.any? && dels.any?
|
125
|
+
puts_change_header start_del, last_del, start_add, last_add
|
126
|
+
elsif adds.any?
|
127
|
+
puts_add_header start_add, last_add
|
128
|
+
else
|
129
|
+
puts_del_header start_del, last_del
|
130
|
+
end
|
131
|
+
@offset_offset -= ( dels.size - adds.size )
|
132
|
+
dels.each do |del|
|
133
|
+
puts_del "#{@left} #{del}"
|
134
|
+
end
|
135
|
+
if adds.any? && dels.any?
|
136
|
+
puts_sep
|
137
|
+
end
|
138
|
+
adds.each do |add|
|
139
|
+
puts_add "#{@right} #{add}"
|
140
|
+
end
|
141
|
+
context_post(chunk) if @context
|
142
|
+
end
|
143
|
+
@out.rewind
|
144
|
+
@out.read
|
145
|
+
end
|
146
|
+
private
|
147
|
+
def consume_opts_for_diff opts
|
148
|
+
if opts[:colors]
|
149
|
+
opts.delete[:colors]
|
150
|
+
gitlike!
|
151
|
+
end
|
152
|
+
if opts[:context]
|
153
|
+
self.context = opts.delete(:context)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
def context_pre chunk
|
157
|
+
pos = chunk.first.position - 1
|
158
|
+
puts_range_safe pos - @context, pos
|
159
|
+
end
|
160
|
+
def context_post chunk
|
161
|
+
pos = chunk.last.position + 1
|
162
|
+
puts_range_safe pos, pos + @context
|
163
|
+
end
|
164
|
+
def other_offset start
|
165
|
+
start + @offset_offset
|
166
|
+
end
|
167
|
+
def puts_del str
|
168
|
+
puts_change str, @del_style
|
169
|
+
end
|
170
|
+
def puts_add str
|
171
|
+
puts_change str, @add_style
|
172
|
+
end
|
173
|
+
def puts_add_header start_add, last_add
|
174
|
+
str = @add_header % [other_offset(start_add), range(start_add,last_add)]
|
175
|
+
@out.puts(stylize(str, @header_style))
|
176
|
+
end
|
177
|
+
def puts_change str, style
|
178
|
+
# separate string into three parts! main string,
|
179
|
+
# trailing non-newline whitespace, and trailing newlines
|
180
|
+
# we want to highlite the trailing whitespace, but if we are
|
181
|
+
# colorizing it we need to exclude the final trailing newlines
|
182
|
+
# for puts to work correctly
|
183
|
+
if /^(.*[^\s]|)([\t ]*)([\n]*)$/ =~ str
|
184
|
+
main_str, ws_str, nl_str = $1, $2, $3
|
185
|
+
@out.print(stylize(main_str, style))
|
186
|
+
@out.print(stylize(ws_str, @trailing_whitespace_style))
|
187
|
+
@out.puts(nl_str)
|
188
|
+
else
|
189
|
+
# hopefully regex never fails but it might
|
190
|
+
@out.puts(stylize(str, style))
|
191
|
+
end
|
192
|
+
end
|
193
|
+
def puts_change_header start_del, last_del, start_add, last_add
|
194
|
+
str = @change_header %
|
195
|
+
[range(start_del,last_del), range(start_add,last_add)]
|
196
|
+
@out.puts(stylize(str, @header_style))
|
197
|
+
end
|
198
|
+
def puts_del_header start_del, last_del
|
199
|
+
str = @del_header % [range(start_del,last_del), other_offset(start_del)]
|
200
|
+
@out.puts(stylize(str, @header_style))
|
201
|
+
end
|
202
|
+
def puts_range_safe start, final
|
203
|
+
start = [start, 0].max
|
204
|
+
final = [@arr1.size-1, final].min
|
205
|
+
if @last_range
|
206
|
+
start = [@last_range[1]+1, start].max
|
207
|
+
# assume sequential for now! no need to check about previous
|
208
|
+
# ones in front of us
|
209
|
+
end
|
210
|
+
return if start >= final
|
211
|
+
@last_range = [start, final]
|
212
|
+
@out.puts @arr1[start..final].map{|x| " #{x}"}
|
213
|
+
# @todo i don't know if i'm reading the chunks right
|
214
|
+
end
|
215
|
+
def puts_sep
|
216
|
+
if @separator_line
|
217
|
+
@out.puts(@separator_line)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
def range min, max
|
221
|
+
if min == max
|
222
|
+
min
|
223
|
+
else
|
224
|
+
"#{min},#{max}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
if __FILE__ == $PROGRAM_NAME
|
230
|
+
require 'test/unit'
|
231
|
+
require 'test/unit/ui/console/testrunner'
|
232
|
+
class DiffToString::TestCase < Test::Unit::TestCase
|
233
|
+
def test_context
|
234
|
+
before = <<-B
|
235
|
+
alpha
|
236
|
+
beta
|
237
|
+
gamma
|
238
|
+
tau
|
239
|
+
B
|
240
|
+
after = <<-A
|
241
|
+
alpha
|
242
|
+
gamma
|
243
|
+
zeta
|
244
|
+
tau
|
245
|
+
A
|
246
|
+
puts DiffToString.diff(before.split("\n"), after.split("\n"),
|
247
|
+
:colors=>true, :context=>3)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
Test::Unit::UI::Console::TestRunner.run(DiffToString::TestCase)
|
251
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'diff/lcs'
|
2
|
+
require(File.dirname(__FILE__)+'/diff-to-string.rb')
|
3
|
+
|
4
|
+
module MiniTest
|
5
|
+
module Assertions
|
6
|
+
|
7
|
+
##
|
8
|
+
# Fails unless <tt>exp == act</tt>.
|
9
|
+
# On failure use diff to show the diff, if +exp+
|
10
|
+
# and +act+ are of the same class and
|
11
|
+
#
|
12
|
+
|
13
|
+
def assert_no_diff exp, act, msg=nil, opts={}
|
14
|
+
if opts.kind_of?(String)
|
15
|
+
opts = {:sep=>opts}
|
16
|
+
end
|
17
|
+
opts = {:sep=>"\n"}.merge(opts)
|
18
|
+
msg = message(msg) do
|
19
|
+
exp_kind, act_kind = [exp,act].map do |x|
|
20
|
+
[String, Array].detect{|c| x.kind_of?(c)}
|
21
|
+
end
|
22
|
+
if exp_kind != act_kind
|
23
|
+
"Expecting #{exp_kind.inspect} had #{act_kind.inspect}"
|
24
|
+
elsif exp_kind.nil?
|
25
|
+
"Will only do diff for strings and arrays, not #{exp.class}"
|
26
|
+
else
|
27
|
+
differ = DiffToString.gitlike!
|
28
|
+
if exp_kind == String
|
29
|
+
use_exp = exp.split(opts[:sep], -1)
|
30
|
+
use_act = act.split(opts[:sep], -1)
|
31
|
+
else
|
32
|
+
use_exp = exp
|
33
|
+
use_act = act
|
34
|
+
end
|
35
|
+
diff = Diff::LCS.diff(use_exp, use_act)
|
36
|
+
if diff.empty?
|
37
|
+
fail("test test fail -- never expecting empty diff here")
|
38
|
+
else
|
39
|
+
differ.arr1 = use_exp
|
40
|
+
differ.arr2 = use_act # awful
|
41
|
+
differ.diff_to_str(diff, :context=>3)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if re = opts[:ignoring]
|
46
|
+
exp, act = [exp, act].map do |str|
|
47
|
+
str.kind_of?(String) ? str.gsub(re, re.source) : str
|
48
|
+
end
|
49
|
+
end
|
50
|
+
assert(exp == act, msg)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
## treebis
|
2
|
+
|
3
|
+
### in short,
|
4
|
+
Treebis is a minimal (small) task utility written in ruby designed expressly for wrapping common actions for moving, copying and altering filetrees. It's geared towards things like generators. Its look is comparable to a Rake task. Its effect is comparable to a shell script composed of mkdir, mv, cp commands etc.
|
5
|
+
|
6
|
+
This document is overkill. Treebis is underkill.
|
7
|
+
|
8
|
+
### it is:
|
9
|
+
- task wrapper for external commands that make filetrees, e.g.
|
10
|
+
- maybe the generators in things like rails, ramaze, nandoc
|
11
|
+
- copies filetrees to filetrees
|
12
|
+
- removes filetrees from filetrees
|
13
|
+
- applies diffs to filetrees
|
14
|
+
- different ways to represent trees and diffs - heredocs, diffs, filesystem.
|
15
|
+
- under 500 lines of code (?) (~300 SLOC ATTOTW)
|
16
|
+
- near 100% test coverage (?)
|
17
|
+
|
18
|
+
### it is not:
|
19
|
+
- a vcs or vcs wrapper (version control system)
|
20
|
+
- atomic
|
21
|
+
- a replacement for the heavy hitters used by web frameworks
|
22
|
+
- Safe. Little sanity checking and error checking is done. At present it
|
23
|
+
it is mostly a wrapper around FileUtils and patch. If you refer to files
|
24
|
+
that aren't there or try to read/move/remove files you don't have the
|
25
|
+
permissions to do so with, you will see the errors as you would from
|
26
|
+
FileUtils.
|
27
|
+
|
28
|
+
### faq
|
29
|
+
|
30
|
+
#### Q: why use it?
|
31
|
+
|
32
|
+
#### A:
|
33
|
+
<p class='ans'>Because you want a consistent way to wrap these common tasks that doesn't explicitly rely on shelling out to the underlying system, or other hodgepodges. (also see 'why did you make this?')
|
34
|
+
</p>
|
35
|
+
|
36
|
+
#### Q: why not use it?
|
37
|
+
|
38
|
+
#### A:
|
39
|
+
Because it doesn't do what you want or it does what you do not want.
|
40
|
+
|
41
|
+
#### Q: why is it named "Treebis?"
|
42
|
+
|
43
|
+
#### A:
|
44
|
+
because it rhymes with "Jeebus."
|
45
|
+
|
46
|
+
#### Q: why did you make this?
|
47
|
+
|
48
|
+
#### A:
|
49
|
+
by the third or fourth time i found myself re-writing this same kind of thing for different projects (or bleeding from its absence), i decided to abstract it. It's more readable than a bunch of FileUtils statements, it's more portable than a bunch of bash scripts (sorta), it's divorced from any heavy (or light) web frameworks, and it paves the way for possible future enhancements like atomicitiy and units of work; and wouldn't it be nice if every generator of every project used the same library?
|
50
|
+
|
51
|
+
### requirements
|
52
|
+
- ruby 1.8.7
|
53
|
+
- GNU patch 2.5.8 (if the diff-patching functionality is to be used)
|
54
|
+
(most versions of patch will likely work; it uses unified diffs.)
|
55
|
+
|
56
|
+
### installation
|
57
|
+
@todo
|
58
|
+
|
59
|
+
### usage
|
60
|
+
@todo
|
61
|
+
|
62
|
+
### future unfulfilled promises made today:
|
63
|
+
- dry run
|
64
|
+
- erb
|
65
|
+
- two-pass units of work !!??
|
data/lib/nandoc.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# this kind of sucks but for a wicked hack to work with symlinks
|
2
|
+
# we have to wrap this sucker.
|
3
|
+
|
4
|
+
unless Object.const_defined?('NanDoc')
|
5
|
+
|
6
|
+
require 'nanoc3'
|
7
|
+
require 'nanoc3/cli'
|
8
|
+
|
9
|
+
module NanDoc
|
10
|
+
#
|
11
|
+
# i make the D big so i can see it
|
12
|
+
# i move my head away from the microphone when i breathe
|
13
|
+
#
|
14
|
+
Root = File.expand_path('../..',__FILE__)
|
15
|
+
end
|
16
|
+
|
17
|
+
me = File.dirname(__FILE__)+'/nandoc'
|
18
|
+
|
19
|
+
# order is important:
|
20
|
+
require me + '/support-modules.rb'
|
21
|
+
require 'treebis' # gem
|
22
|
+
require me + '/config.rb'
|
23
|
+
|
24
|
+
module NanDoc
|
25
|
+
Treebis::PersistentDotfile.extend_to(self,
|
26
|
+
'./nandoc.persistent.json',
|
27
|
+
:file_utils => Config.file_utils
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
# order is not important: (alphabetical:)
|
32
|
+
require me + '/commands/create-nandoc-site.rb'
|
33
|
+
require me + '/commands/diff.rb'
|
34
|
+
require me + '/cri-hacks.rb'
|
35
|
+
require me + '/data-source.rb'
|
36
|
+
require me + '/filters.rb'
|
37
|
+
require me + '/helpers.rb'
|
38
|
+
require me + '/item-class-hacks.rb'
|
39
|
+
|
40
|
+
Nanoc3::DataSource.register ::NanDoc::DataSource, :nandoc
|
41
|
+
Nanoc3::Filter.register ::NanDoc::Filters::General, :nandoc
|
42
|
+
|
43
|
+
shared_base = Nanoc3::CLI::Base.shared_base
|
44
|
+
shared_base.remove_command Nanoc3::CLI::Commands::CreateSite
|
45
|
+
shared_base.add_command NanDoc::CreateNanDocSite.new
|
46
|
+
shared_base.add_command NanDoc::Commands::Diff.new
|
47
|
+
|
48
|
+
end
|