cheat 1.1.0 → 1.2
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/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
|