cheat 1.1.0 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cheat.rb +61 -20
- data/lib/diffr.rb +49 -0
- data/lib/responder.rb +42 -0
- data/lib/site.rb +95 -45
- data/lib/wrap.rb +1 -1
- metadata +4 -2
data/lib/cheat.rb
CHANGED
@@ -4,13 +4,41 @@ $:.unshift File.dirname(__FILE__)
|
|
4
4
|
module Cheat
|
5
5
|
extend self
|
6
6
|
|
7
|
-
HOST = 'cheat.errtheblog.com'
|
8
|
-
PORT = 80
|
7
|
+
HOST = ARGV.include?('debug') ? 'localhost' : 'cheat.errtheblog.com'
|
8
|
+
PORT = ARGV.include?('debug') ? 3001 : 80
|
9
9
|
SUFFIX = ''
|
10
10
|
|
11
11
|
def sheets(args)
|
12
12
|
args = args.dup
|
13
13
|
|
14
|
+
return unless parse_args(args)
|
15
|
+
|
16
|
+
FileUtils.mkdir(cache_dir) unless File.exists?(cache_dir) if cache_dir
|
17
|
+
|
18
|
+
uri = "http://#{cheat_uri}/y/"
|
19
|
+
|
20
|
+
if %w[sheets all recent].include? @sheet
|
21
|
+
uri = uri.sub('/y/', sheet == 'recent' ? '/yr/' : '/ya/')
|
22
|
+
return open(uri) { |body| show(body.read) }
|
23
|
+
end
|
24
|
+
|
25
|
+
return show(File.read(cache_file)) if File.exists?(cache_file) rescue clear_cache if cache_file
|
26
|
+
|
27
|
+
fetch_sheet(uri, @sheet) if @sheet
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_sheet(uri, try_to_cache = true)
|
31
|
+
open(uri, headers) do |body|
|
32
|
+
sheet = body.read
|
33
|
+
File.open(cache_file, 'w') { |f| f.write(sheet) } if try_to_cache && cache_file && !@edit
|
34
|
+
@edit ? edit(sheet) : show(sheet)
|
35
|
+
end
|
36
|
+
exit
|
37
|
+
rescue OpenURI::HTTPError => e
|
38
|
+
puts "Whoa, some kind of Internets error!", "=> #{e} from #{uri}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_args(args)
|
14
42
|
puts "Looking for help? Try http://cheat.errtheblog.com or `$ cheat cheat'" and return if args.empty?
|
15
43
|
|
16
44
|
if args.delete('--clear-cache') || args.delete('--new')
|
@@ -18,31 +46,42 @@ module Cheat
|
|
18
46
|
return if args.empty?
|
19
47
|
end
|
20
48
|
|
49
|
+
if i = args.index('--diff')
|
50
|
+
diff_sheets(args.first, args[i+1])
|
51
|
+
end
|
52
|
+
|
53
|
+
show_versions(args.first) if args.delete('--versions')
|
54
|
+
|
21
55
|
add(args.shift) and return if args.delete('--add')
|
56
|
+
clear_cache if @edit = args.delete('--edit')
|
22
57
|
|
23
|
-
|
24
|
-
clear_cache
|
25
|
-
end
|
58
|
+
@sheet = args.shift
|
26
59
|
|
27
|
-
|
60
|
+
true
|
61
|
+
end
|
28
62
|
|
29
|
-
|
30
|
-
|
63
|
+
# $ cheat greader --versions
|
64
|
+
def show_versions(sheet)
|
65
|
+
fetch_sheet("http://#{cheat_uri}/h/#{sheet}/")
|
66
|
+
end
|
31
67
|
|
32
|
-
|
68
|
+
# $ cheat greader --diff 1[:3]
|
69
|
+
def diff_sheets(sheet, version)
|
70
|
+
return unless version =~ /^(\d+)(:(\d+))?$/
|
71
|
+
old_version, new_version = $1, $3
|
33
72
|
|
34
|
-
|
35
|
-
|
36
|
-
return open(uri) { |body| show(body.read) }
|
37
|
-
end
|
73
|
+
uri = "http://#{cheat_uri}/d/#{sheet}/#{old_version}"
|
74
|
+
uri += "/#{new_version}" if new_version
|
38
75
|
|
39
|
-
|
76
|
+
fetch_sheet(uri, false)
|
77
|
+
end
|
40
78
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
79
|
+
def cache_file
|
80
|
+
"#{cache_dir}/#{@sheet}.yml" if cache_dir
|
81
|
+
end
|
82
|
+
|
83
|
+
def headers
|
84
|
+
{ 'User-Agent' => 'cheat!', 'Accept' => 'text/yaml' }
|
46
85
|
end
|
47
86
|
|
48
87
|
def cheat_uri
|
@@ -53,7 +92,9 @@ module Cheat
|
|
53
92
|
sheet = YAML.load(sheet_yaml).to_a.first
|
54
93
|
sheet[-1] = sheet.last.join("\n") if sheet[-1].is_a?(Array)
|
55
94
|
puts sheet.first + ':'
|
56
|
-
puts ' ' + sheet.last.gsub("\r",'').gsub("\n", "\n ").wrap
|
95
|
+
puts ' ' + sheet.last.gsub("\r",'').gsub("\n", "\n ").wrap
|
96
|
+
rescue
|
97
|
+
puts "That didn't work. Try something else?"
|
57
98
|
end
|
58
99
|
|
59
100
|
def edit(sheet_yaml)
|
data/lib/diffr.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'diff/lcs'
|
3
|
+
require 'diff/lcs/hunk'
|
4
|
+
|
5
|
+
module Cheat
|
6
|
+
class Diffr
|
7
|
+
def self.diff(sheet_old, sheet_new)
|
8
|
+
format, lines, output = :unified, 10000, ''
|
9
|
+
file_length_difference = 0
|
10
|
+
|
11
|
+
data_old = sheet_old.body.wrap.split(/\n/).map! { |e| e.chomp }
|
12
|
+
data_new = sheet_new.body.wrap.split(/\n/).map! { |e| e.chomp }
|
13
|
+
|
14
|
+
diffs = Diff::LCS.diff(data_old, data_new)
|
15
|
+
return if diffs.empty?
|
16
|
+
|
17
|
+
header = ''
|
18
|
+
ft = sheet_old.updated_at
|
19
|
+
header << "#{'-' * 3} #{sheet_new.title} version #{sheet_old.version}\t#{ft}\n"
|
20
|
+
ft = sheet_new.updated_at
|
21
|
+
header << "#{'+' * 3} #{sheet_new.title} version #{sheet_new.version}\t#{ft}\n"
|
22
|
+
|
23
|
+
oldhunk = hunk = nil
|
24
|
+
|
25
|
+
diffs.each do |piece|
|
26
|
+
begin
|
27
|
+
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference)
|
28
|
+
file_length_difference = hunk.file_length_difference
|
29
|
+
|
30
|
+
next unless oldhunk
|
31
|
+
|
32
|
+
if lines > 0 && hunk.overlaps?(oldhunk)
|
33
|
+
hunk.unshift(oldhunk)
|
34
|
+
else
|
35
|
+
output << oldhunk.diff(format)
|
36
|
+
end
|
37
|
+
ensure
|
38
|
+
oldhunk = hunk
|
39
|
+
output << "\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
output << oldhunk.diff(format)
|
44
|
+
output << "\n"
|
45
|
+
|
46
|
+
return header + output.lstrip
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/responder.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# class Something < R 'route'
|
2
|
+
# include Responder
|
3
|
+
#
|
4
|
+
# def get
|
5
|
+
# ... important code ...
|
6
|
+
#
|
7
|
+
# respond_to do |wants|
|
8
|
+
# wants.html { render :something }
|
9
|
+
# wants.text { "Just some text." }
|
10
|
+
# wants.yaml { "Something neat!".to_yaml }
|
11
|
+
# wants.xml { "Also, XML.".to_xml }
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
module Cheat::Controllers
|
16
|
+
module Responder
|
17
|
+
def respond_to
|
18
|
+
yield response = Response.new(env.HTTP_ACCEPT)
|
19
|
+
@headers['Content-Type'] = response.content_type
|
20
|
+
response.body
|
21
|
+
end
|
22
|
+
|
23
|
+
class Response
|
24
|
+
attr_reader :body, :content_type
|
25
|
+
def initialize(accept) @accept = accept end
|
26
|
+
|
27
|
+
TYPES = {
|
28
|
+
:yaml => %w[application/yaml text/yaml],
|
29
|
+
:text => %w[text/plain],
|
30
|
+
:html => %w[text/html */* application/html],
|
31
|
+
:xml => %w[application/xml]
|
32
|
+
}
|
33
|
+
|
34
|
+
def method_missing(method, *args)
|
35
|
+
if TYPES[method] && @accept =~ Regexp.union(*TYPES[method])
|
36
|
+
@content_type = TYPES[method].first
|
37
|
+
@body = yield if block_given?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/site.rb
CHANGED
@@ -21,12 +21,12 @@
|
|
21
21
|
# Cheat Lake, a nearby resevoir
|
22
22
|
# Cheat Mountain, one of the highest mountains in the Alleghenies
|
23
23
|
#
|
24
|
-
%w[rubygems camping camping/db erb open-uri acts_as_versioned wrap].each { |f| require f }
|
24
|
+
%w[rubygems camping camping/db erb open-uri acts_as_versioned wrap diffr responder].each { |f| require f }
|
25
25
|
require_gem 'camping', '>=1.4.152'
|
26
26
|
|
27
27
|
Camping.goes :Cheat
|
28
28
|
|
29
|
-
URL = 'http://cheat.errtheblog.com'
|
29
|
+
URL = ARGV.include?('debug') ? 'http://localhost:3001' : 'http://cheat.errtheblog.com'
|
30
30
|
FEED = 'http://feeds.feedburner.com/cheatsheets' # rss feed
|
31
31
|
|
32
32
|
module Cheat::Models
|
@@ -108,14 +108,11 @@ module Cheat::Controllers
|
|
108
108
|
class Edit < R '/e/(\w+)/(\d+)', '/e/(\w+)'
|
109
109
|
def get(title, version = nil)
|
110
110
|
@sheet = Sheet.find_by_title(title)
|
111
|
-
unless @sheet
|
112
|
-
@error = "Cheat sheet not found."
|
113
|
-
return render(:error)
|
114
|
-
end
|
111
|
+
@error = "Cheat sheet not found." unless @sheet
|
115
112
|
unless version.nil? || version == @sheet.version.to_s
|
116
113
|
@sheet = @sheet.find_version(version)
|
117
114
|
end
|
118
|
-
render :edit
|
115
|
+
render @error ? :error : :edit
|
119
116
|
end
|
120
117
|
end
|
121
118
|
|
@@ -125,12 +122,9 @@ module Cheat::Controllers
|
|
125
122
|
|
126
123
|
@error = input.sheet_body =~ /<a\s*href=/
|
127
124
|
|
128
|
-
unless input.from_gem
|
129
|
-
@error = true unless captcha_pass?(input.chunky, input.bacon)
|
130
|
-
end
|
125
|
+
check_captcha! unless input.from_gem
|
131
126
|
|
132
|
-
if !@error && @sheet.update_attributes(:title => input.sheet_title,
|
133
|
-
:body => input.sheet_body)
|
127
|
+
if !@error && @sheet.update_attributes(:title => input.sheet_title, :body => input.sheet_body)
|
134
128
|
redirect "#{URL}/s/#{@sheet.title}"
|
135
129
|
else
|
136
130
|
@error = true
|
@@ -138,6 +132,10 @@ module Cheat::Controllers
|
|
138
132
|
end
|
139
133
|
end
|
140
134
|
|
135
|
+
def check_captcha!
|
136
|
+
@error ||= !(@cookies[:passed] ||= captcha_pass?(input.chunky, input.bacon))
|
137
|
+
end
|
138
|
+
|
141
139
|
def captcha_pass?(session, answer)
|
142
140
|
open("http://captchator.ruby-forum.com/captcha/check_answer/#{session}/#{answer}").read.to_i.nonzero? rescue false
|
143
141
|
end
|
@@ -150,31 +148,46 @@ module Cheat::Controllers
|
|
150
148
|
end
|
151
149
|
end
|
152
150
|
|
153
|
-
class Show < R '/s/(\w+)', '/s/(\w+)/(\d+)'
|
151
|
+
class Show < R '/s/(\w+)', '/s/(\w+)/(\d+)'
|
154
152
|
def get(title, version = nil)
|
155
|
-
# fake respond_to
|
156
|
-
if title =~ /\.txt/ && title.sub!('.txt', '').size
|
157
|
-
text = true
|
158
|
-
@headers['Content-Type'] = 'text/plain'
|
159
|
-
end
|
160
|
-
|
161
153
|
@sheet = Sheet.find_by_title(title)
|
162
154
|
@sheet = @sheet.find_version(version) if version && @sheet
|
163
155
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
156
|
+
@sheet ? render(:show) : redirect("#{URL}/b/")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# we are going to start consolidating classes with respond_to and what not.
|
161
|
+
# diff is the first, as the api and the site will use the same code
|
162
|
+
class Diff < R '/d/(\w+)/(\d+)', '/d/(\w+)/(\d+)/(\d+)'
|
163
|
+
include Responder
|
164
|
+
|
165
|
+
def get(title, old_version, new_version = nil)
|
166
|
+
redirect "#{URL}/b/" and return unless old_version.to_i.nonzero?
|
167
|
+
|
168
|
+
@sheet = Sheet.find_by_title(title)
|
169
|
+
@old_sheet = @sheet.find_version(old_version)
|
170
|
+
@new_sheet = (new_version ? @sheet.find_version(new_version) : @sheet)
|
171
|
+
|
172
|
+
@diffed = Diffr.diff(@old_sheet, @new_sheet) rescue nil
|
173
|
+
|
174
|
+
respond_to do |wants|
|
175
|
+
wants.html { render :diff }
|
176
|
+
wants.yaml { { @sheet.title => @diffed }.to_yaml }
|
170
177
|
end
|
171
178
|
end
|
172
179
|
end
|
173
180
|
|
174
181
|
class History < R '/h/(\w+)'
|
182
|
+
include Responder
|
183
|
+
|
175
184
|
def get(title)
|
176
185
|
@sheets = Sheet.find_by_title(title).find_versions(:order => 'version DESC')
|
177
|
-
|
186
|
+
|
187
|
+
respond_to do |wants|
|
188
|
+
wants.html { render :history }
|
189
|
+
wants.yaml { { @sheets.first.title => @sheets.map { |s| s.version } }.to_yaml }
|
190
|
+
end
|
178
191
|
end
|
179
192
|
end
|
180
193
|
end
|
@@ -185,15 +198,13 @@ module Cheat::Views
|
|
185
198
|
head {
|
186
199
|
_style
|
187
200
|
link :href => FEED, :rel => "alternate", :title => "Recently Updated Cheat Sheets", :type => "application/atom+xml"
|
188
|
-
title
|
189
|
-
"$ command line ruby cheat sheets"
|
201
|
+
title @page_title ? "$ cheat #{@page_title}" : "$ command line ruby cheat sheets"
|
190
202
|
}
|
191
203
|
body {
|
192
204
|
div.main {
|
193
205
|
div.header {
|
194
206
|
h1 { logo_link 'cheat sheets.' }
|
195
|
-
code.header
|
196
|
-
"$ command line ruby cheat sheets"
|
207
|
+
code.header @sheet_title ? "$ cheat #{@sheet_title}" : "$ command line ruby cheat sheets"
|
197
208
|
}
|
198
209
|
div.content { self << yield }
|
199
210
|
div.side { _side }
|
@@ -214,7 +225,7 @@ module Cheat::Views
|
|
214
225
|
def show
|
215
226
|
@page_title = @sheet.title
|
216
227
|
@sheet_title = @sheet.title
|
217
|
-
pre.sheet { text h(sheet.body.wrap
|
228
|
+
pre.sheet { text h(@sheet.body.wrap) }
|
218
229
|
div.version {
|
219
230
|
text "Version "
|
220
231
|
strong sheet.version
|
@@ -223,26 +234,49 @@ module Cheat::Views
|
|
223
234
|
text " ago. "
|
224
235
|
br
|
225
236
|
text ". o 0 ( "
|
226
|
-
|
227
|
-
|
237
|
+
if @sheet.version == current_sheet.version
|
238
|
+
a "edit", :href => R(Edit, @sheet.title)
|
228
239
|
text " | "
|
240
|
+
end
|
241
|
+
if @sheet.version > 1
|
229
242
|
a "previous", :href => R(Show, @sheet.title, @sheet.version - 1)
|
230
243
|
text " | "
|
231
|
-
a "history", :href => R(History, @sheet.title)
|
232
244
|
end
|
233
|
-
|
234
|
-
|
245
|
+
a "history", :href => R(History, @sheet.title)
|
246
|
+
text " | "
|
247
|
+
unless @sheet.version == current_sheet.version
|
235
248
|
a "revert to", :href => R(Edit, @sheet.title, @sheet.version)
|
236
249
|
text " | "
|
237
250
|
a "current", :href => R(Show, @sheet.title)
|
238
|
-
end
|
239
|
-
if @sheet.version == current_sheet.version
|
240
251
|
text " | "
|
241
|
-
a "text", :href => R(Show, @sheet.title + '.txt')
|
242
252
|
end
|
253
|
+
diff_version =
|
254
|
+
if @sheet.version == current_sheet.version
|
255
|
+
@sheet.version == 1 ? nil : @sheet.version - 1
|
256
|
+
else
|
257
|
+
@sheet.version
|
258
|
+
end
|
259
|
+
a "diff", :href => R(Diff, @sheet.title, diff_version) if diff_version
|
243
260
|
text " )"
|
244
261
|
}
|
245
262
|
end
|
263
|
+
|
264
|
+
def diff
|
265
|
+
@page_title = @sheet.title
|
266
|
+
@sheet_title = @sheet.title
|
267
|
+
pre.sheet { color_diff(@diffed) if @diffed }
|
268
|
+
div.version {
|
269
|
+
text ". o 0 ("
|
270
|
+
if @old_sheet.version > 1
|
271
|
+
a "diff previous", :href => R(Diff, @sheet.title, @old_sheet.version - 1)
|
272
|
+
text " | "
|
273
|
+
end
|
274
|
+
a "history", :href => R(History, @sheet.title)
|
275
|
+
text " | "
|
276
|
+
a "current", :href => R(Show, @sheet.title)
|
277
|
+
text " )"
|
278
|
+
}
|
279
|
+
end
|
246
280
|
|
247
281
|
def browse
|
248
282
|
@page_title = "browse"
|
@@ -269,7 +303,9 @@ module Cheat::Views
|
|
269
303
|
text " - created "
|
270
304
|
text last_updated(sheet)
|
271
305
|
text " ago"
|
272
|
-
strong " (current)" if i
|
306
|
+
strong " (current)" if i.zero?
|
307
|
+
text " "
|
308
|
+
a "(diff to current)", :href => R(Diff, sheet.title, sheet.version) if i.nonzero?
|
273
309
|
}
|
274
310
|
end
|
275
311
|
}
|
@@ -312,11 +348,13 @@ module Cheat::Views
|
|
312
348
|
text 'Cheat Sheet:'
|
313
349
|
br
|
314
350
|
textarea @sheet.body, :name => 'sheet_body', :cols => 80, :rows => 30
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
351
|
+
unless @cookies[:passed]
|
352
|
+
random = rand(10_000)
|
353
|
+
br
|
354
|
+
img :src => "http://captchator.ruby-forum.com/captcha/image/#{random}"
|
355
|
+
input :name => 'chunky', :type => 'hidden', :value => random
|
356
|
+
input :name => 'bacon', :size => 10, :type => 'text'
|
357
|
+
end
|
320
358
|
}
|
321
359
|
}
|
322
360
|
p "Your cheat sheet will be editable (fixable) by anyone. Each cheat
|
@@ -459,6 +497,8 @@ module Cheat::Views
|
|
459
497
|
div.clear_10 { clear: both; font-size: 10px; line-height: 10px; }
|
460
498
|
textarea { font-family: courier; }
|
461
499
|
code { background-color: #{version} }
|
500
|
+
span.diff_cut { color: #f65077; }
|
501
|
+
span.diff_add { color: #009933; }
|
462
502
|
@media print {
|
463
503
|
.side, .version, .footer { display: none; }
|
464
504
|
div.content { width: 100%; }
|
@@ -516,6 +556,16 @@ module Cheat::Helpers
|
|
516
556
|
def h(text)
|
517
557
|
::Cheat::Helpers.h(text)
|
518
558
|
end
|
559
|
+
|
560
|
+
def color_diff(diff)
|
561
|
+
diff.split("\n").map do |line|
|
562
|
+
action = case line
|
563
|
+
when /^-/ then 'cut'
|
564
|
+
when /^\+/ then 'add'
|
565
|
+
end
|
566
|
+
action ? span.send("diff_#{action}", line) : line
|
567
|
+
end * "\n"
|
568
|
+
end
|
519
569
|
end
|
520
570
|
|
521
571
|
def Cheat.create
|
data/lib/wrap.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# >> Evan Weaver
|
2
2
|
# => http://blog.evanweaver.com/articles/2006/09/03/smart-plaintext-wrapping
|
3
3
|
class String
|
4
|
-
def wrap(width, hanging_indent = 0, magic_lists = false)
|
4
|
+
def wrap(width = 80, hanging_indent = 0, magic_lists = false)
|
5
5
|
lines = self.split(/\n/)
|
6
6
|
|
7
7
|
lines.collect! do |line|
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: cheat
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date: 2006-
|
6
|
+
version: "1.2"
|
7
|
+
date: 2006-11-09 00:00:00 -08:00
|
8
8
|
summary: Cheat is a simple command line utility reference program.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,6 +33,8 @@ files:
|
|
33
33
|
- LICENSE
|
34
34
|
- bin/cheat
|
35
35
|
- lib/cheat.rb
|
36
|
+
- lib/diffr.rb
|
37
|
+
- lib/responder.rb
|
36
38
|
- lib/site.rb
|
37
39
|
- lib/wrap.rb
|
38
40
|
- test/fixtures
|