tarsius-cheat 1.2.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.
Files changed (9) hide show
  1. data/LICENSE +18 -0
  2. data/README +38 -0
  3. data/bin/cheat +4 -0
  4. data/lib/cheat.rb +240 -0
  5. data/lib/diffr.rb +49 -0
  6. data/lib/responder.rb +42 -0
  7. data/lib/site.rb +611 -0
  8. data/lib/wrap.rb +42 -0
  9. metadata +64 -0
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2006 Chris Wanstrath
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,38 @@
1
+ == Cheat
2
+
3
+ Cheat is a simple command line reference utility. Use it to retrieve handy
4
+ information from the cheat sheet repository.
5
+
6
+ $ cheat sheets
7
+ $ cheat cheat
8
+ $ cheat recent
9
+
10
+ To freshen your local cache, supply the --new edit.
11
+
12
+ $ cheat sheets --new
13
+
14
+ To edit a cheat sheet, use the --edit switch.
15
+
16
+ $ cheat markaby --edit
17
+
18
+ To add a cheat sheet, use the --add switch.
19
+
20
+ $ cheat readme --add
21
+
22
+ To execute a sheet, use the --execute or -x switch.
23
+
24
+ $ cheat rspec_rails_install_edge --execute
25
+ $ cheat rspec_rails_install_edge --x
26
+
27
+ Special Thanks To:
28
+ - Evan Weaver
29
+ - Kevin Marsh
30
+ - Jeremy Apthorp
31
+
32
+ The Cheat Sheet Repository:
33
+ - http://cheat.errtheblog.com/
34
+
35
+ Enjoy.
36
+
37
+ >> Chris Wanstrath
38
+ => chris[at]ozmm[dot]org
data/bin/cheat ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'cheat'
4
+ Cheat.sheets(ARGV)
data/lib/cheat.rb ADDED
@@ -0,0 +1,240 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ %w[rubygems tempfile fileutils net/http yaml open-uri wrap].each { |f| require f }
3
+
4
+ module Cheat
5
+ extend self
6
+
7
+ HOST = ARGV.include?('debug') ? 'localhost' : 'cheat.errtheblog.com'
8
+ PORT = ARGV.include?('debug') ? 3001 : 80
9
+ SUFFIX = ''
10
+
11
+ def sheets(args)
12
+ args = args.dup
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
+ options = headers.update(proxy_options)
22
+ uri = uri.sub('/y/', @sheet == 'recent' ? '/yr/' : '/ya/')
23
+ return open(uri,options) { |body| process(body.read) }
24
+ end
25
+
26
+ return process(File.read(cache_file)) if File.exists?(cache_file) rescue clear_cache if cache_file
27
+
28
+ fetch_sheet(uri + @sheet) if @sheet
29
+ end
30
+
31
+ def fetch_sheet(uri, try_to_cache = true)
32
+ options = headers.update(proxy_options)
33
+ open(uri, options) do |body|
34
+ sheet = body.read
35
+ FileUtils.mkdir_p(cache_dir) unless File.exists?(cache_dir)
36
+ File.open(cache_file, 'w') { |f| f.write(sheet) } if try_to_cache && cache_file && !@edit
37
+ @edit ? edit(sheet) : show(sheet)
38
+ end
39
+ exit
40
+ rescue OpenURI::HTTPError => e
41
+ puts "Whoa, some kind of Internets error!", "=> #{e} from #{uri}"
42
+ end
43
+
44
+ def parse_args(args)
45
+ puts "Looking for help? Try http://cheat.errtheblog.com or `$ cheat cheat'" and return if args.empty?
46
+
47
+ if args.delete('--clear-cache') || args.delete('--new')
48
+ clear_cache
49
+ return if args.empty?
50
+ end
51
+
52
+ if i = args.index('--diff')
53
+ diff_sheets(args.first, args[i+1])
54
+ end
55
+
56
+ show_versions(args.first) if args.delete('--versions')
57
+
58
+ add(args.shift) and return if args.delete('--add')
59
+ clear_cache if @edit = args.delete('--edit')
60
+
61
+ @execute = true if args.delete("--execute") || args.delete("-x")
62
+ @sheet = args.shift
63
+
64
+ true
65
+ end
66
+
67
+ # $ cheat greader --versions
68
+ def show_versions(sheet)
69
+ fetch_sheet("http://#{cheat_uri}/h/#{sheet}/", false)
70
+ end
71
+
72
+ # $ cheat greader --diff 1[:3]
73
+ def diff_sheets(sheet, version)
74
+ return unless version =~ /^(\d+)(:(\d+))?$/
75
+ old_version, new_version = $1, $3
76
+
77
+ uri = "http://#{cheat_uri}/d/#{sheet}/#{old_version}"
78
+ uri += "/#{new_version}" if new_version
79
+
80
+ fetch_sheet(uri, false)
81
+ end
82
+
83
+ def cache_file
84
+ "#{cache_dir}/#{@sheet}.yml" if cache_dir
85
+ end
86
+
87
+ def headers
88
+ { 'User-Agent' => 'cheat!', 'Accept' => 'text/yaml' }
89
+ end
90
+
91
+ def proxy_options
92
+ if ENV['HTTP_PROXY'] || ENV['http_proxy']
93
+ proxy_array = []
94
+ proxy_array << (ENV['HTTP_PROXY'] || ENV['http_proxy'])
95
+ proxy_array << (ENV['HTTP_PROXY_USER'] || ENV['http_proxy_user'] || '')
96
+ proxy_array << (ENV['HTTP_PROXY_PASS'] || ENV['http_proxy_pass'] || '')
97
+ {:proxy_http_basic_authentication => proxy_array}
98
+ else
99
+ {}
100
+ end
101
+ end
102
+
103
+ def cheat_uri
104
+ "#{HOST}:#{PORT}#{SUFFIX}"
105
+ end
106
+
107
+ def execute(sheet_yaml)
108
+ sheet_body = YAML.load(sheet_yaml).to_a.flatten.last
109
+ puts "\n " + sheet_body.gsub("\r",'').gsub("\n", "\n ").wrap
110
+ puts "\nWould you like to execute the above sheet? (Y/N)"
111
+ answer = STDIN.gets
112
+ case answer.chomp
113
+ when "Y" then system YAML.load(sheet_yaml).to_a.flatten.last
114
+ when "N" then puts "Not executing sheet."
115
+ else
116
+ puts "Must be Y or N!"
117
+ end
118
+ rescue Errno::EPIPE
119
+ # do nothing
120
+ rescue
121
+ puts "That didn't work. Maybe try `$ cheat cheat' for help?" # Fix Emacs ruby-mode highlighting bug: `"
122
+ end
123
+
124
+ def process(sheet_yaml)
125
+ if @execute
126
+ execute(sheet_yaml)
127
+ else
128
+ show(sheet_yaml)
129
+ end
130
+ end
131
+
132
+ def show(sheet_yaml)
133
+ sheet = YAML.load(sheet_yaml).to_a.first
134
+ sheet[-1] = sheet.last.join("\n") if sheet[-1].is_a?(Array)
135
+ run_pager
136
+ puts sheet.first + ':'
137
+ puts ' ' + sheet.last.gsub("\r",'').gsub("\n", "\n ").wrap
138
+ rescue Errno::EPIPE
139
+ # do nothing
140
+ rescue
141
+ puts "That didn't work. Maybe try `$ cheat cheat' for help?" # Fix Emacs ruby-mode highlighting bug: `"
142
+ end
143
+
144
+ def edit(sheet_yaml)
145
+ sheet = YAML.load(sheet_yaml).to_a.first
146
+ sheet[-1] = sheet.last.gsub("\r", '')
147
+ body, title = write_to_tempfile(*sheet), sheet.first
148
+ return if body.strip == sheet.last.strip
149
+ res = post_sheet(title, body)
150
+ check_errors(res, title, body)
151
+ end
152
+
153
+ def add(title)
154
+ body = write_to_tempfile(title)
155
+ res = post_sheet(title, body, true)
156
+ check_errors(res, title, body)
157
+ end
158
+
159
+ def post_sheet(title, body, new = false)
160
+ uri = "http://#{cheat_uri}/w/"
161
+ uri += title unless new
162
+ Net::HTTP.post_form(URI.parse(uri), "sheet_title" => title, "sheet_body" => body.strip, "from_gem" => true)
163
+ end
164
+
165
+ def write_to_tempfile(title, body = nil)
166
+ # god dammit i hate tempfile, this is so messy but i think it's
167
+ # the only way.
168
+ tempfile = Tempfile.new(title + '.cheat')
169
+ tempfile.write(body) if body
170
+ tempfile.close
171
+ system "#{editor} #{tempfile.path}"
172
+ tempfile.open
173
+ body = tempfile.read
174
+ tempfile.close
175
+ body
176
+ end
177
+
178
+ def check_errors(result, title, text)
179
+ if result.body =~ /<p class="error">(.+?)<\/p>/m
180
+ puts $1.gsub(/\n/, '').gsub(/<.+?>/, '').squeeze(' ').wrap(80)
181
+ puts
182
+ puts "Here's what you wrote, so it isn't lost in the void:"
183
+ puts text
184
+ else
185
+ puts "Success! Try it!", "$ cheat #{title} --new"
186
+ end
187
+ end
188
+
189
+ def editor
190
+ ENV['VISUAL'] || ENV['EDITOR'] || "vim"
191
+ end
192
+
193
+ def cache_dir
194
+ PLATFORM =~ /win32/ ? win32_cache_dir : File.join(File.expand_path("~"), ".cheat")
195
+ end
196
+
197
+ def win32_cache_dir
198
+ unless File.exists?(home = ENV['HOMEDRIVE'] + ENV['HOMEPATH'])
199
+ puts "No HOMEDRIVE or HOMEPATH environment variable. Set one to save a" +
200
+ "local cache of cheat sheets."
201
+ return false
202
+ else
203
+ return File.join(home, 'Cheat')
204
+ end
205
+ end
206
+
207
+ def clear_cache
208
+ FileUtils.rm_rf(cache_dir) if cache_dir
209
+ end
210
+
211
+ def run_pager
212
+ return if PLATFORM =~ /win32/
213
+ return unless STDOUT.tty?
214
+
215
+ read, write = IO.pipe
216
+
217
+ unless Kernel.fork # Child process
218
+ STDOUT.reopen(write)
219
+ STDERR.reopen(write) if STDERR.tty?
220
+ read.close
221
+ write.close
222
+ return
223
+ end
224
+
225
+ # Parent process, become pager
226
+ STDIN.reopen(read)
227
+ read.close
228
+ write.close
229
+
230
+ ENV['LESS'] = 'FSRX' # Don't page if the input is short enough
231
+
232
+ # wait until we have input before we start the pager
233
+ Kernel.select [STDIN]
234
+ pager = ENV['PAGER'] || 'less'
235
+ exec pager rescue exec "/bin/sh", "-c", pager
236
+ rescue
237
+ end
238
+ end
239
+
240
+ Cheat.sheets(ARGV) if __FILE__ == $0
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 ADDED
@@ -0,0 +1,611 @@
1
+ #
2
+ # According to Wikipedia, Cheat can refer to:
3
+ # Cheating, to take advantage of a situation by the breaking of accepted rules
4
+ # or standards
5
+ # Cheating (casino)
6
+ # Cheating in poker
7
+ # Cheating in online games
8
+ # In relationships, to have an affair
9
+ # A cheat code, a hidden means of gaining an advantage in a video game
10
+ # Cheating, parasitic abuse of symbiotic relationships
11
+ # The Cheat, a character in the cartoon series Homestar Runner
12
+ # Cheat!, a television show on the G4 network
13
+ # The Cheat, a 1915 Cecil B. DeMille movie about a wealthy and domineering
14
+ # Asian gentleman taking advantage of an American female
15
+ # Cheats, a 2002 comedy, starring Matthew Lawrence and Mary Tyler Moore
16
+ # Cheat, a song by The Clash from the UK version of their album The Clash
17
+ # Bullshit, sometimes known as "Cheat," a card game
18
+ # An alternate term for defection in the prisoner's dilemma in game theory
19
+ # Cheat River, a tributary of the Monongahela River in Appalachia; the Cheat
20
+ # starts in West Virginia, and flows westward
21
+ # Cheat Lake, a nearby resevoir
22
+ # Cheat Mountain, one of the highest mountains in the Alleghenies
23
+ #
24
+ %w[rubygems camping camping/db erb rubygems/open-uri acts_as_versioned wrap diffr responder ambition].each { |f| require f }
25
+ gem 'camping', '>=1.4.152'
26
+
27
+ Camping.goes :Cheat
28
+
29
+ # for defunkt. campistrano.
30
+ if ARGV.include? '--update'
31
+ ssh = 'ssh deploy@errtheblog.com'
32
+ puts `#{ssh} 'cd /var/www/cheat; svn up'`
33
+ system "#{ssh} 'sudo /etc/init.d/rv restart'"
34
+ exit
35
+ end
36
+
37
+ URL = ARGV.include?('debug') ? 'http://localhost:8020' : 'http://cheat.errtheblog.com'
38
+ FEED = 'http://feeds.feedburner.com/cheatsheets' # rss feed
39
+
40
+ module Cheat::Models
41
+ class Sheet < Base
42
+ validates_uniqueness_of :title
43
+ validates_format_of :title, :with => /^[a-z]+[a-z0-9_]*$/i
44
+ validates_presence_of :title, :body
45
+ before_save { |r| r.title = r.title.gsub(' ', '_').underscore.downcase }
46
+ acts_as_versioned
47
+ end
48
+
49
+ class SetUpUsTheCheat < V 1.0
50
+ def self.up
51
+ create_table :cheat_sheets, :force => true do |t|
52
+ t.column :id, :integer, :null => false
53
+ t.column :title, :string, :null => false
54
+ t.column :body, :text
55
+ t.column :created_at, :datetime, :null => false
56
+ t.column :updated_at, :datetime, :null => false
57
+ end
58
+ Sheet.create_versioned_table
59
+ Sheet.reset_column_information
60
+ end
61
+ def self.down
62
+ drop_table :cheat_sheets
63
+ Sheet.drop_versioned_table
64
+ end
65
+ end
66
+ end
67
+
68
+ module Cheat::Controllers
69
+ class APIShow < R '/y/(\w+)'
70
+ def get(title)
71
+ @headers['Content-Type'] = 'text/plain'
72
+
73
+ sheet = Sheet.detect { |s| s.title == title }
74
+ return { 'Error!' => "Cheat sheet `#{title}' not found." }.to_yaml unless sheet
75
+
76
+ return { sheet.title => sheet.body }.to_yaml
77
+ end
78
+ end
79
+
80
+ class APIRecent < R '/yr'
81
+ def get
82
+ @headers['Content-Type'] = 'text/plain'
83
+
84
+ sheets = Sheet.sort_by { |s| -s.created_at }.first(15).map(&:title)
85
+ return { 'Recent Cheat Sheets' => sheets }.to_yaml
86
+ end
87
+ end
88
+
89
+ class APIAll < R '/ya'
90
+ def get
91
+ @headers['Content-Type'] = 'text/plain'
92
+
93
+ sheets = Sheet.sort_by(&:title).map(&:title)
94
+ return { 'All Cheat Sheets' => sheets }.to_yaml
95
+ end
96
+ end
97
+
98
+ class Feed < R '/f'
99
+ def get
100
+ @headers['Content-Type'] = 'application/xml'
101
+ return Cheat::Views.feed
102
+ end
103
+ end
104
+
105
+ class Index < R '/'
106
+ def get
107
+ render :index
108
+ end
109
+ end
110
+
111
+ class Add < R '/a'
112
+ def get
113
+ @sheet = Sheet.new
114
+ render :add
115
+ end
116
+ end
117
+
118
+ class Edit < R '/e/(\w+)/(\d+)', '/e/(\w+)'
119
+ def get(title, version = nil)
120
+ @sheet = Sheet.detect { |s| s.title == title }
121
+
122
+ @error = "Cheat sheet not found." unless @sheet
123
+ unless version.nil? || version == @sheet.version.to_s
124
+ @sheet = @sheet.find_version(version)
125
+ end
126
+ render @error ? :error : :edit
127
+ end
128
+ end
129
+
130
+ class Write < R '/w', '/w/(\w+)'
131
+ def post(title = nil)
132
+ @sheet = title ? Sheet.find_by_title(title) : Sheet.new
133
+ @sheet = title ? Sheet.detect { |s| s.title == title } : Sheet.new
134
+
135
+ check_captcha! unless input.from_gem
136
+
137
+ if !@error && @sheet.update_attributes(:title => input.sheet_title, :body => input.sheet_body)
138
+ redirect "#{URL}/s/#{@sheet.title}"
139
+ else
140
+ @error = true
141
+ render title ? :edit : :add
142
+ end
143
+ end
144
+
145
+ def check_captcha!
146
+ @error ||= !(@cookies[:passed] ||= captcha_pass?(input.chunky, input.bacon))
147
+ end
148
+
149
+ def captcha_pass?(session, answer)
150
+ open("http://captchator.com/captcha/check_answer/#{session}/#{answer}").read.to_i.nonzero? rescue false
151
+ end
152
+ end
153
+
154
+ class Browse < R '/b'
155
+ def get
156
+ @sheets = Sheet.sort_by(&:title)
157
+ render :browse
158
+ end
159
+ end
160
+
161
+ class Show < R '/s/(\w+)', '/s/(\w+)/(\d+)'
162
+ def get(title, version = nil)
163
+ @sheet = Sheet.detect { |s| s.title == title }
164
+ @sheet = @sheet.find_version(version) if version && @sheet
165
+
166
+ @sheet ? render(:show) : redirect("#{URL}/b/")
167
+ end
168
+ end
169
+
170
+ # we are going to start consolidating classes with respond_to and what not.
171
+ # diff is the first, as the api and the site will use the same code
172
+ class Diff < R '/d/(\w+)/(\d+)', '/d/(\w+)/(\d+)/(\d+)'
173
+ include Responder
174
+
175
+ def get(title, old_version, new_version = nil)
176
+ redirect "#{URL}/b/" and return unless old_version.to_i.nonzero?
177
+
178
+ @sheet = Sheet.detect { |s| s.title == title }
179
+ @old_sheet = @sheet.find_version(old_version)
180
+ @new_sheet = (new_version ? @sheet.find_version(new_version) : @sheet)
181
+
182
+ @diffed = Diffr.diff(@old_sheet, @new_sheet) rescue nil
183
+
184
+ respond_to do |wants|
185
+ wants.html { render :diff }
186
+ wants.yaml { { @sheet.title => @diffed }.to_yaml }
187
+ end
188
+ end
189
+ end
190
+
191
+ class History < R '/h/(\w+)'
192
+ include Responder
193
+
194
+ def get(title)
195
+ if sheets = Sheet.detect { |s| s.title == title }
196
+ @sheets = sheets.find_versions(:order => 'version DESC')
197
+ end
198
+
199
+ respond_to do |wants|
200
+ wants.html { render :history }
201
+ wants.yaml { { @sheets.first.title => @sheets.map(&:version) }.to_yaml }
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ module Cheat::Views
208
+ def layout
209
+ html {
210
+ head {
211
+ _style
212
+ link :href => FEED, :rel => "alternate", :title => "Recently Updated Cheat Sheets", :type => "application/atom+xml"
213
+ title @page_title ? "$ cheat #{@page_title}" : "$ command line ruby cheat sheets"
214
+ }
215
+ body {
216
+ div.main {
217
+ div.header {
218
+ h1 { logo_link 'cheat sheets.' }
219
+ code.header @sheet_title ? "$ cheat #{@sheet_title}" : "$ command line ruby cheat sheets"
220
+ }
221
+ div.content { self << yield }
222
+ div.side { _side }
223
+ div.clear { '' }
224
+ div.footer { _footer }
225
+ }
226
+ _clicky
227
+ }
228
+ }
229
+ end
230
+
231
+ def _clicky
232
+ text '<script src="http://getclicky.com/1070.js"> </script><noscript><img height=0 width=0 src="http://getclicky.com/1070ns.gif"></noscript>'
233
+ end
234
+
235
+ def error
236
+ @page_title = "error"
237
+ p "An error:"
238
+ code.version @error
239
+ p ":("
240
+ end
241
+
242
+ def show
243
+ @page_title = @sheet.title
244
+ @sheet_title = @sheet.title
245
+ pre.sheet { text h(@sheet.body.wrap) }
246
+ div.version {
247
+ text "Version "
248
+ strong sheet.version
249
+ text ", updated "
250
+ text last_updated(@sheet)
251
+ text " ago. "
252
+ br
253
+ text ". o 0 ( "
254
+ if @sheet.version == current_sheet.version
255
+ a "edit", :href => R(Edit, @sheet.title)
256
+ end
257
+ if @sheet.version > 1
258
+ text " | "
259
+ a "previous", :href => R(Show, @sheet.title, @sheet.version - 1)
260
+ end
261
+ text " | "
262
+ a "history", :href => R(History, @sheet.title)
263
+ unless @sheet.version == current_sheet.version
264
+ text " | "
265
+ a "revert to", :href => R(Edit, @sheet.title, @sheet.version)
266
+ text " | "
267
+ a "current", :href => R(Show, @sheet.title)
268
+ end
269
+ diff_version =
270
+ if @sheet.version == current_sheet.version
271
+ @sheet.version == 1 ? nil : @sheet.version - 1
272
+ else
273
+ @sheet.version
274
+ end
275
+ if diff_version
276
+ text " | "
277
+ a "diff", :href => R(Diff, @sheet.title, diff_version)
278
+ end
279
+ text " )"
280
+ }
281
+ end
282
+
283
+ def diff
284
+ @page_title = @sheet.title
285
+ @sheet_title = @sheet.title
286
+ pre.sheet { color_diff(h(@diffed)) if @diffed }
287
+ div.version {
288
+ text ". o 0 ("
289
+ if @old_sheet.version > 1
290
+ a "diff previous", :href => R(Diff, @sheet.title, @old_sheet.version - 1)
291
+ text " | "
292
+ end
293
+ a "history", :href => R(History, @sheet.title)
294
+ text " | "
295
+ a "current", :href => R(Show, @sheet.title)
296
+ text " )"
297
+ }
298
+ end
299
+
300
+ def browse
301
+ @page_title = "browse"
302
+ p { "Wowzers, we've got <strong>#{@sheets.size}</strong> cheat sheets hereabouts." }
303
+ ul {
304
+ @sheets.each do |sheet|
305
+ li { sheet_link sheet.title }
306
+ end
307
+ }
308
+ p {
309
+ text "Are we missing a cheat sheet? Why don't you do the whole world a favor and "
310
+ a "add it", :href => R(Add)
311
+ text " yourself!"
312
+ }
313
+ end
314
+
315
+ def history
316
+ @page_title = "history"
317
+ @sheet_title = @sheets.first.title
318
+ h2 @sheets.first.title
319
+ ul {
320
+ @sheets.each_with_index do |sheet, i|
321
+ li {
322
+ a "version #{sheet.version}", :href => R(Show, sheet.title, sheet.version)
323
+ text " - created "
324
+ text last_updated(sheet)
325
+ text " ago"
326
+ strong " (current)" if i.zero?
327
+ text " "
328
+ a "(diff to current)", :href => R(Diff, sheet.title, sheet.version) if i.nonzero?
329
+ }
330
+ end
331
+ }
332
+ end
333
+
334
+ def add
335
+ @page_title = "add"
336
+ p {
337
+ text "Thanks for wanting to add a cheat sheet. If you need an example of
338
+ the standard cheat sheet format, check out the "
339
+ a "cheat", :href => R(Show, 'cheat')
340
+ text " cheat sheet. (There's really no standard format, though)."
341
+ }
342
+ _form
343
+ end
344
+
345
+ def edit
346
+ @page_title = "edit"
347
+ _form
348
+ end
349
+
350
+ def _form
351
+ if @error
352
+ p.error {
353
+ strong "HEY! "
354
+ text "Something is wrong! You can't give your cheat sheet the same name
355
+ as another, alphanumeric titles only, and you need to make sure
356
+ you filled in all (two) of the fields. Okay?"
357
+ }
358
+ end
359
+ form :method => 'post', :action => R(Write, @sheet.title) do
360
+ p {
361
+ p {
362
+ text 'Cheat Sheet Title: '
363
+ input :value => @sheet.title, :name => 'sheet_title', :size => 30,
364
+ :type => 'text'
365
+ small " [ no_spaces_alphanumeric_only ]"
366
+ }
367
+ p {
368
+ text 'Cheat Sheet:'
369
+ br
370
+ textarea @sheet.body, :name => 'sheet_body', :cols => 80, :rows => 30
371
+ unless @cookies[:passed]
372
+ random = rand(10_000)
373
+ br
374
+ img :src => "http://captchator.com/captcha/image/#{random}"
375
+ input :name => 'chunky', :type => 'hidden', :value => random
376
+ input :name => 'bacon', :size => 10, :type => 'text'
377
+ end
378
+ }
379
+ }
380
+ p "Your cheat sheet will be editable (fixable) by anyone. Each cheat
381
+ sheet is essentially a wiki page. It may also be used by millions of
382
+ people for reference purposes from the comfort of their command line.
383
+ If this is okay with you, please save."
384
+ input :value => "Save the Damn Thing!", :name => "save", :type => 'submit'
385
+ end
386
+ end
387
+
388
+ def index
389
+ p {
390
+ text "Welcome. You've reached the central repository for "
391
+ strong "cheat"
392
+ text ", the RubyGem which puts Ruby-centric cheat sheets right into your
393
+ terminal. The inaugural blog entry "
394
+ a "is here", :href => "http://errtheblog.com/post/23"
395
+ text "."
396
+ }
397
+ p "Get started:"
398
+ code "$ sudo gem install cheat"
399
+ br
400
+ code "$ cheat strftime"
401
+ p "A magnificent cheat sheet for Ruby's strftime method will be printed to
402
+ your terminal."
403
+ p "To get some help on cheat itself:"
404
+ code "$ cheat cheat"
405
+ p "How meta."
406
+ p {
407
+ text "Cheat sheets are basically wiki pages accessible from the command
408
+ line. You can "
409
+ a 'browse', :href => R(Browse)
410
+ text ', '
411
+ a 'add', :href => R(Add)
412
+ text ', or '
413
+ a 'edit', :href => R(Edit, 'cheat')
414
+ text ' cheat sheets. Try to keep them concise. For a style guide, check
415
+ out the '
416
+ a 'cheat', :href => R(Edit, 'cheat')
417
+ text ' cheat sheet.'
418
+ }
419
+ p "To access a cheat sheet, simply pass the program the desired sheet's
420
+ name:"
421
+ code "$ cheat <sheet name>"
422
+ p
423
+ end
424
+
425
+ def self.feed
426
+ xml = Builder::XmlMarkup.new(:indent => 2)
427
+
428
+ xml.instruct!
429
+ xml.feed "xmlns"=>"http://www.w3.org/2005/Atom" do
430
+
431
+ xml.title "Recently Updated Cheat Sheets"
432
+ xml.id URL + '/'
433
+ xml.link "rel" => "self", "href" => FEED
434
+
435
+ sheets = Cheat::Models::Sheet.sort_by { |s| -s.updated_at }.first(20)
436
+ xml.updated sheets.first.updated_at.xmlschema
437
+
438
+ sheets.each do |sheet|
439
+ xml.entry do
440
+ xml.id URL + '/s/' + sheet.title
441
+ xml.title sheet.title
442
+ xml.author { xml.name "An Anonymous Cheater" }
443
+ xml.updated sheet.updated_at.xmlschema
444
+ xml.link "rel" => "alternate", "href" => URL + '/s/' + sheet.title
445
+ xml.summary "A cheat sheet about #{sheet.title}. Run it: `$ cheat #{sheet.title}'"
446
+ xml.content 'type' => 'html' do
447
+ xml.text! sheet.body.gsub("\n", '<br/>').gsub("\r", '')
448
+ end
449
+ end
450
+ end
451
+ end
452
+ end
453
+
454
+ def _side
455
+ text '( '
456
+ a 'add new', :href => R(Add)
457
+ text ' | '
458
+ a 'see all', :href => R(Browse)
459
+ text ' )'
460
+ ul {
461
+ li { strong "updated sheets" }
462
+ li do
463
+ a :href => FEED do
464
+ img(:border => 0, :alt => "Recently Updated Cheat Sheets Feed", :src => "http://errtheblog.com/images/feed.png")
465
+ end
466
+ end
467
+ recent_sheets.each do |sheet|
468
+ li { sheet_link sheet.title }
469
+ end
470
+ }
471
+ end
472
+
473
+ def _footer
474
+ text "Powered by "
475
+ a 'Camping', :href => "http://code.whytheluckystiff.net/camping"
476
+ text ", "
477
+ a 'Mongrel', :href => "http://mongrel.rubyforge.org/"
478
+ text " and, to a lesser extent, "
479
+ a 'Err the Blog', :href => "http://errtheblog.com/"
480
+ text "."
481
+ end
482
+
483
+ def _style
484
+ bg = "#fff"
485
+ h1 = "#4fa3da"
486
+ link = h1
487
+ hover = "#f65077"
488
+ dash = hover
489
+ version = "#fcf095"
490
+ style :type => "text/css" do
491
+ text %[
492
+ body { font-family: verdana, sans-serif; background-color: #{bg};
493
+ line-height: 20px; }
494
+ a:link, a:visited { color: #{link}; }
495
+ a:hover { text-decoration: none; color: #{hover}; }
496
+ div.header { border-bottom: 1px dashed #{dash}; }
497
+ code.header { margin-left: 30px; font-weight: bold;
498
+ background-color: #{version}; }
499
+ h1 { font-size: 5em; margin: 0; padding-left: 30px; color: #{h1};
500
+ clear: both; font-weight: bold; letter-spacing: -5px; }
501
+ h1 a { text-decoration: none; }
502
+ div.main { float: left; width: 100%; }
503
+ div.content { float: left; width: 70%; padding: 15px 0 15px 30px;
504
+ line-height: 20px; }
505
+ div.side { float: left; padding: 10px; text-align: right;
506
+ width: 20%; }
507
+ div.footer { text-align: center; border-top: 1px dashed #{dash};
508
+ padding-top: 10px; font-size: small; }
509
+ div.sheet { font-size: .8em; line-height: 17px; padding: 5px;
510
+ font-family: courier, fixed-width; background-color: #e8e8e8; }
511
+ pre.sheet { line-height: 15px; }
512
+ li { list-style: none; }
513
+ div.version { background-color: #{version}; padding: 5px;
514
+ width: 450px; margin-top: 50px; }
515
+ p.error { background-color: #{version}; padding: 5px; }
516
+ div.clear { clear: both; }
517
+ div.clear_10 { clear: both; font-size: 10px; line-height: 10px; }
518
+ textarea { font-family: courier; }
519
+ code { background-color: #{version} }
520
+ span.diff_cut { color: #f65077; }
521
+ span.diff_add { color: #009933; }
522
+ @media print {
523
+ .side, .version, .footer { display: none; }
524
+ div.content { width: 100%; }
525
+ h1 a:link, h1 a:visited { color: #eee;}
526
+ .header code { font-size: 18px; background: none; }
527
+ div.header { border-bottom: none; }
528
+ }
529
+ ].gsub(/(\s{2,})/, '').gsub("\n", '')
530
+ end
531
+ end
532
+ end
533
+
534
+ module Cheat::Helpers
535
+ def logo_link(title)
536
+ ctr = Cheat::Controllers
537
+ if @sheet && !@sheet.new_record? && @sheet.version != current_sheet.version
538
+ a title, :href => R(ctr::Show, @sheet.title)
539
+ else
540
+ a title, :href => R(ctr::Index)
541
+ end
542
+ end
543
+
544
+ def current_sheet
545
+ title = @sheet.title
546
+ @current_sheet ||= Cheat::Models::Sheet.detect { |s| s.title == title }
547
+ end
548
+
549
+ def recent_sheets
550
+ Cheat::Models::Sheet.sort_by { |s| -s.updated_at }.first(15)
551
+ end
552
+
553
+ def sheet_link(title, version = nil)
554
+ a title, :href => R(Cheat::Controllers::Show, title, version)
555
+ end
556
+
557
+ def last_updated(sheet)
558
+ from = sheet.updated_at.to_i
559
+ to = Time.now.to_i
560
+ from = from.to_time if from.respond_to?(:to_time)
561
+ to = to.to_time if to.respond_to?(:to_time)
562
+ distance = (((to - from).abs)/60).round
563
+ case distance
564
+ when 0..1 then return (distance==0) ? 'less than a minute' : '1 minute'
565
+ when 2..45 then "#{distance} minutes"
566
+ when 46..90 then 'about 1 hour'
567
+ when 90..1440 then "about #{(distance.to_f / 60.0).round} hours"
568
+ when 1441..2880 then '1 day'
569
+ else "#{(distance / 1440).round} days"
570
+ end
571
+ end
572
+
573
+ def self.h(text)
574
+ ERB::Util::h(text)
575
+ end
576
+
577
+ def h(text)
578
+ ::Cheat::Helpers.h(text)
579
+ end
580
+
581
+ def color_diff(diff)
582
+ diff.split("\n").map do |line|
583
+ action = case line
584
+ when /^-/ then 'cut'
585
+ when /^\+/ then 'add'
586
+ end
587
+ action ? span.send("diff_#{action}", line) : line
588
+ end * "\n"
589
+ end
590
+ end
591
+
592
+ def Cheat.create
593
+ Cheat::Models.create_schema
594
+ end
595
+
596
+ if __FILE__ == $0
597
+ begin
598
+ require 'mongrel/camping'
599
+ rescue LoadError => e
600
+ abort "** Try running `camping #$0' instead."
601
+ end
602
+
603
+ Cheat::Models::Base.establish_connection :adapter => 'mysql', :user => 'root', :database => 'camping', :host => 'localhost'
604
+ Cheat::Models::Base.logger = nil
605
+ Cheat::Models::Base.threaded_connections = false
606
+ Cheat.create
607
+
608
+ server = Mongrel::Camping.start("0.0.0.0", 8020, "/", Cheat)
609
+ puts "** Cheat is running at http://0.0.0.0:8020/"
610
+ server.run.join
611
+ end
data/lib/wrap.rb ADDED
@@ -0,0 +1,42 @@
1
+ # >> Evan Weaver
2
+ # => http://blog.evanweaver.com/articles/2006/09/03/smart-plaintext-wrapping
3
+ class String
4
+ def wrap(width = 80, hanging_indent = 0, magic_lists = false)
5
+ lines = self.split(/\n/)
6
+
7
+ lines.collect! do |line|
8
+
9
+ if magic_lists
10
+ line =~ /^([\s\-\d\.\:]*\s)/
11
+ else
12
+ line =~ /^([\s]*\s)/
13
+ end
14
+
15
+ indent = $1.length + hanging_indent rescue hanging_indent
16
+
17
+ buffer = ""
18
+ first = true
19
+
20
+ while line.length > 0
21
+ first ? (i, first = 0, false) : i = indent
22
+ pos = width - i
23
+
24
+ if line.length > pos and line[0..pos] =~ /^(.+)\s/
25
+ subline = $1
26
+ else
27
+ subline = line[0..pos]
28
+ end
29
+ buffer += " " * i + subline + "\n"
30
+ line.tail!(subline.length)
31
+ end
32
+ buffer[0..-2]
33
+ end
34
+
35
+ lines.join("\n")
36
+ end
37
+
38
+ def tail!(pos)
39
+ self[0..pos] = ""
40
+ strip!
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tarsius-cheat
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Chris Wanstrath
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ date: 2006-11-10 00:00:00 -08:00
12
+ default_executable: cheat
13
+ dependencies: []
14
+
15
+ description: Cheat is a simple command line utility reference program. Use it to, well, cheat.
16
+ email: chris@ozmm.org
17
+ executables:
18
+ - cheat
19
+ extensions: []
20
+
21
+ extra_rdoc_files: []
22
+
23
+ files:
24
+ - README
25
+ - LICENSE
26
+ - bin/cheat
27
+ - lib/cheat
28
+ - lib/cheat.rb
29
+ - lib/diffr.rb
30
+ - lib/new_cheat.rb
31
+ - lib/responder.rb
32
+ - lib/site.rb
33
+ - lib/wrap.rb
34
+ - lib/cheat/commands.rb
35
+ - test/fixtures
36
+ - test/test_cheat.rb
37
+ has_rdoc: false
38
+ homepage: http://cheat.errtheblog.com/
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">"
47
+ - !ruby/object:Gem::Version
48
+ version: 0.0.0
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.2.0
60
+ signing_key:
61
+ specification_version: 1
62
+ summary: Cheat is a simple command line utility reference program.
63
+ test_files: []
64
+