cheatr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cheatr.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ernesto Garcia
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,173 @@
1
+ # cheatr
2
+
3
+ Cheatr is a simple command line utility to access cheat sheets from an online
4
+ repository.
5
+
6
+ Generally speaking, cheatr is a tool that allows to host an online collection
7
+ of cheat sheets, and lets users query and maintain this repository remotely via
8
+ a command line client utility.
9
+
10
+ ### What is a cheat sheet?
11
+
12
+ In the context of cheatr, a cheat sheet is a small document with quick tips and
13
+ hints on a concrete and specific topic. Typically it contains notes intended
14
+ to aid one's memory, like listing common and useful shortcuts used on
15
+ a program, quick and easy tips on how to use a library, or the command line
16
+ options of a shell command, for instance.
17
+
18
+ Cheatr however enforces very little on the contents of the cheat sheets in
19
+ a repository. The above specification is just a guideline on the intended use
20
+ of this tool, but in practice you can use it to store and maintain any
21
+ collection of text documents in it.
22
+
23
+ Further down this document you'll find more information about the format of
24
+ [cheat sheet contents](#cheat-sheet-contents).
25
+
26
+ ## Installation
27
+
28
+ $ gem install cheatr
29
+
30
+ ## Usage
31
+
32
+ Cheatr retrieves cheat sheets from a remote server, so we need to tell
33
+ cheatr where to look.
34
+
35
+ $ cheatr config server cheatr.gnapse.com
36
+
37
+ This generates a configuration file in `~/.cheatr/config.yml`. Now we can
38
+ start querying the remote server for cheat sheets, or start creating new ones.
39
+
40
+ $ cheatr show "ruby*" # Cheat sheets with names starting with 'ruby'
41
+ ruby.strings
42
+ ruby.blocks
43
+ rubygems
44
+
45
+ $ cheatr search ruby # With this command you can ommit the wildcard
46
+ ruby.strings
47
+ ruby.blocks
48
+ rubygems
49
+
50
+ $ cheatr all # Lists all cheat sheets available
51
+ ack
52
+ bash
53
+ cplusplus
54
+ ...
55
+ ruby
56
+ vim
57
+ zsh
58
+
59
+ Tell cheatr to show a given cheat sheet.
60
+
61
+ $ cheatr show ruby.blocks
62
+ Ruby blocks are an awesome language feature!
63
+
64
+ The contents of a cheat sheet are cached once it has been fetched. You can
65
+ force cheatr to fetch remote contents.
66
+
67
+ $ cheatr show ruby.blocks -r
68
+
69
+ You can edit existing cheat sheets, or create new ones.
70
+
71
+ $ cheatr edit ruby.blocks
72
+
73
+ This will launch you preferred `$EDITOR` with the current contents of the
74
+ specified cheat sheet. After you save the file and quit the editor, `cheatr`
75
+ will update the cheat sheet contents.
76
+
77
+ Cheatr commands provide help, so you can discover all its features and
78
+ possibilities. Type `cheatr --help` or `cheatr <command> --help` for details.
79
+
80
+ ## Web server
81
+
82
+ This gem includes the server side component as well, so anyone can host their
83
+ own cheat sheets service. This server offers the basic API-like calls to
84
+ query, retrieve and modify cheat sheet contents. You can start a cheatr
85
+ server with the following command:
86
+
87
+ $ cheatr server /path/to/cheatr/repository
88
+
89
+ The repository path refers to the location where the cheats repository can be
90
+ found. A cheatr repository is nothing more than a git repository holding the
91
+ cheat sheets as files in it. If omitted, the current working directory is
92
+ assummed.
93
+
94
+ A cheatr server is a [sinatra][] app, so it can also be started using [rack][].
95
+ Every cheatr repository, when created, is initialized with a standard
96
+ `config.ru` file. So while placed in the repository's root folder, the server
97
+ can be started with the following command:
98
+
99
+ $ rackup
100
+
101
+ Also try `rackup --help` for more options.
102
+
103
+ [sinatra]: http://www.sinatrarb.com
104
+ [rack]: https://github.com/rack/rack
105
+
106
+ ### Web interface
107
+
108
+ Additionally, cheatr servers can be accessed directly on a browser, where
109
+ content is delivered as traditional hyperlinked web pages instead of mere plain
110
+ text, with the possibility to search for cheat sheets, or [link between
111
+ them](#cheat-sheet-hyperlinks), converting cheatr in a simple but powerful
112
+ cheat sheets wiki.
113
+
114
+ To try this, type the following command:
115
+
116
+ $ cheatr browse ruby.blocks
117
+
118
+ It will open the contents of the specified cheat sheet in your preferred
119
+ browser. Or you can always type the URLs in your browser yourself, provided
120
+ you know the cheatr server you want to access.
121
+
122
+ ## Cheat sheet contents
123
+
124
+ Although cheatr does not enforce any format to the contents of cheat sheets,
125
+ it assumes they consist of markdown text. Thus is highly beneficial to adhere
126
+ to this convention, and embrace it.
127
+
128
+ ### Cheat sheet hyperlinks
129
+
130
+ In addition to standard markdown syntax, cheatr supports a minor custom
131
+ extension, that becomes useful when browsing a cheatr server in a web
132
+ browser. This is related to linking in between cheat sheets, as hinted in the
133
+ previous section.
134
+
135
+ This is best shown with an example.
136
+
137
+ ```
138
+ Dynamic languages, like {{ruby}} and {{python}}, are more versatile than
139
+ {{c++|cplusplus}} in many situations.
140
+ ```
141
+
142
+ Noticed the slices of text surrounded by doble braces? These are cheatr
143
+ hyperlinks. When showing pages through the web interface in a browser, these
144
+ will be converted to links to the appropriate cheat sheet.
145
+
146
+ Note also the link to the `cplusplus` cheat sheet. The text before the
147
+ vertical bar will be used as the link text, whereas the text after it is the
148
+ name of the cheat sheet to link to.
149
+
150
+ In the command line, these "links" will be shown unmodified, still serving the
151
+ purpose of discovery, because the user can identify these "links" and discover
152
+ new related topics to continue "browsing" and learning.
153
+
154
+ ## Contributing
155
+
156
+ Feel free to dive into the code to understand more about how cheatr works, or
157
+ just for fun. Please note that this is a very young project with very rough
158
+ edges still. Issues reports and pull requests are welcome!
159
+
160
+ In case you decide to step in:
161
+
162
+ 1. Fork the repo (`git clone https://github.com/gnapse/cheatr.git`)
163
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
164
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
165
+ 4. Push to the branch (`git push origin my-new-feature`)
166
+ 5. Create new Pull Request
167
+
168
+ ## Thanks
169
+
170
+ Thanks to [defunkt][] for [cheat][], which provided the idea for this project.
171
+
172
+ [defunkt]: https://github.com/defunkt
173
+ [cheat]: https://github.com/defunkt/cheat
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "slop"
4
+ require "cheatr"
5
+
6
+ def check(condition, message)
7
+ unless condition
8
+ $stderr.puts message
9
+ exit
10
+ end
11
+ end
12
+
13
+ unless ARGV.first == 'config'
14
+ check Cheatr::Client.server, "Run 'cheatr config server <location>' to set the remote cheatr server."
15
+ end
16
+
17
+ Slop.parse help: true do
18
+
19
+ command :server do
20
+ banner 'Usage: cheatr server [options] [repository-path]'
21
+ on 'p', 'port=', 'The port where the server will listen to.'
22
+ run do |opts, args|
23
+ check args.length <= 1, opts
24
+ repository = args.first || Dir.pwd
25
+ Cheatr::Server.run(repository, opts)
26
+ end
27
+ end
28
+
29
+ command :show do
30
+ banner 'Usage: cheatr show [options] <sheet-name>'
31
+ on 'r', 'remote', 'Forces retrieving contents from remote, ignoring the cache, if any.'
32
+ run do |opts, args|
33
+ check args.length == 1, opts
34
+ name = args.first
35
+ if name.include?('*')
36
+ Cheatr::Client.search_sheets(name)
37
+ else
38
+ Cheatr::Client.display_sheet(name, ignore_cache: opts[:remote])
39
+ end
40
+ end
41
+ end
42
+
43
+ command :browse do
44
+ banner 'Usage: cheatr browse [query]'
45
+ run do |opts, args|
46
+ check args.length <= 1, opts
47
+ Cheatr::Client.browse(args.first)
48
+ end
49
+ end
50
+
51
+ command :search do
52
+ banner 'Usage: cheatr search <query>'
53
+ run do |opts, args|
54
+ check args.length == 1, opts
55
+ Cheatr::Client.search_sheets(args.first)
56
+ end
57
+ end
58
+
59
+ command :all do
60
+ banner 'Usage: cheatr all'
61
+ run do |opts, args|
62
+ check args.length == 0, opts
63
+ Cheatr::Client.search_sheets
64
+ end
65
+ end
66
+
67
+ command :edit do
68
+ banner 'Usage: cheatr edit [options] <sheet-name>'
69
+ on 'y', 'yes', 'Do not ask for confirmation after editing.'
70
+ run do |opts, args|
71
+ check args.length == 1, opts
72
+ Cheatr::Client.edit_sheet(args.first, skip_confirmation: opts[:yes])
73
+ end
74
+ end
75
+
76
+ command :fetch do
77
+ banner 'Usage: cheatr fetch [options] <sheet-name>+'
78
+ on 'q', 'quiet', 'Do not print anything to standard output. Overrides --errors.'
79
+ on 'e', 'errors', 'Print error messages only.'
80
+ run do |opts, args|
81
+ Cheatr::Client.fetch_sheets(args, opts)
82
+ end
83
+ end
84
+
85
+ command :clear do
86
+ banner 'Usage: cheatr clear [options] [sheet-name]'
87
+ on 'a', 'all', 'Clear all cached cheat sheets. Requires confirmation by default.'
88
+ on 'y', 'yes', 'Do not ask for confirmation for clearing the cache completely.'
89
+ on 'q', 'quiet', 'Do not print anything to standard output. Implies --yes, skipping confirmation.'
90
+ run do |opts, args|
91
+ if opts[:all]
92
+ Cheatr::Client.clear_cache(:all, skip_confirmation: opts[:yes], quiet: opts[:quiet])
93
+ else
94
+ check(args.length == 1, opts)
95
+ Cheatr::Client.clear_cache(args.first, quiet: opts[:quiet])
96
+ end
97
+ end
98
+ end
99
+
100
+ command :config do
101
+ banner 'Usage: cheatr config <param> <value>'
102
+ run do |opts, args|
103
+ check args.length == 2, opts
104
+ Cheatr::Client.set_config(args.first => args.last)
105
+ end
106
+ end
107
+
108
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cheatr/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cheatr"
8
+ spec.version = Cheatr::VERSION
9
+ spec.authors = ["Ernesto García"]
10
+ spec.email = ["gnapse@gmail.com"]
11
+ spec.description = %q{Display cheat sheets for a variety of programs, tools and libraries, right on the command line.}
12
+ spec.summary = %q{Display cheat sheets right on the command line.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "slop"
22
+
23
+ # server dependencies
24
+ spec.add_dependency "git"
25
+ spec.add_dependency "sinatra"
26
+ spec.add_dependency "activemodel"
27
+ spec.add_dependency "redcarpet"
28
+
29
+ # client dependencies
30
+ spec.add_dependency "rest-client"
31
+ spec.add_dependency "pager"
32
+
33
+ # test dependencies
34
+ spec.add_development_dependency "bundler", "~> 1.3"
35
+ spec.add_development_dependency "rake"
36
+ spec.add_development_dependency "rspec"
37
+ end
@@ -0,0 +1,6 @@
1
+ module Cheatr
2
+ autoload :Client, 'cheatr/client'
3
+ autoload :Server, 'cheatr/server'
4
+
5
+ SHEET_NAME_REGEXP = /\A[a-z]+([\.\-\_][a-z]+)*\z/
6
+ end
@@ -0,0 +1,133 @@
1
+ require "yaml"
2
+ require "pager"
3
+ require "cheatr/client/sheet"
4
+
5
+ module Cheatr
6
+ module Client
7
+ extend Pager
8
+
9
+ # Actions
10
+
11
+ def self.search_sheets(query = nil)
12
+ query += '*' unless query.nil? || query.include?('*')
13
+ page
14
+ puts Sheet.all(query)
15
+ end
16
+
17
+ def self.display_sheet(name, opts = {})
18
+ sheet = Sheet.new(name, opts)
19
+ if sheet.contents
20
+ page
21
+ puts "(cached version)" unless sheet.remote?
22
+ puts sheet.contents
23
+ else
24
+ puts sheet.errors.first
25
+ end
26
+ end
27
+
28
+ def self.edit_sheet(name, opts = {})
29
+ sheet = Sheet.new(name, ignore_cache: true)
30
+
31
+ file = Tempfile.new([name, '.md'])
32
+ file.write(sheet.contents) if sheet.contents
33
+ file.close
34
+ system "#{editor} #{file.path}"
35
+ file.open
36
+ contents = file.read
37
+ file.close
38
+
39
+ if contents.strip.empty?
40
+ puts "Edited sheet contents were empty so nothing was saved."
41
+ return
42
+ end
43
+
44
+ if opts[:skip_confirmation] || confirm("Do you want to update the '#{name}' cheat sheet?")
45
+ sheet.contents = contents
46
+ if sheet.save
47
+ puts "Sheet '#{name}' was updated successfully."
48
+ else
49
+ puts "Sheet '#{name}' could not be updated."
50
+ $stderr.puts "Error: #{sheet.errors.first}" if sheet.errors
51
+ end
52
+ else
53
+ puts "Sheet '#{name}' was left unchanged."
54
+ end
55
+ end
56
+
57
+ def self.browse(query = nil)
58
+ open_cmd = `uname` =~ /Darwin/ ? 'open' : 'xdg-open'
59
+ uri = "http://#{server}/#{query}"
60
+ exec "#{open_cmd} #{uri}"
61
+ end
62
+
63
+ def self.fetch_sheets(arr, opts = {})
64
+ arr.each do |name|
65
+ sheet = Sheet.new(name, ignore_cache: true)
66
+ if sheet.contents
67
+ puts "Sheet '#{name}' fetched successfully." unless opts[:quiet] || opts[:errors]
68
+ else
69
+ message = sheet.errors.first || "Sheet '#{name}' could not be fetched."
70
+ puts message unless opts[:quiet]
71
+ end
72
+ end
73
+ end
74
+
75
+ def self.clear_cache(name = :all, opts = {})
76
+ if name == :all
77
+ if opts[:quiet] || opts[:skip_confirmation] || confirm("Are you sure you want clear all cheat sheets cache?")
78
+ FileUtils.rm Dir.glob("#{cache_dir}/*.md")
79
+ puts "All cached cheat sheets have been removed." unless opts[:quiet]
80
+ else
81
+ puts "Cached cheat sheets were not removed." unless opts[:quiet]
82
+ end
83
+ else
84
+ FileUtils.rm "#{cache_dir}/#{name}.md", force: true
85
+ puts "Removed cached version of '#{name}'." unless opts[:quiet]
86
+ end
87
+ end
88
+
89
+ # Configuration settings
90
+
91
+ def self.mkdir(dir)
92
+ FileUtils.mkdir_p dir
93
+ dir
94
+ end
95
+
96
+ def self.cheatr_dir
97
+ @@cheatr_dir ||= mkdir File.join(File.expand_path("~"), ".cheatr")
98
+ end
99
+
100
+ def self.cache_dir
101
+ @@cache_dir ||= mkdir File.join(cheatr_dir, 'cache', server.gsub(/:\d+\z/, ''))
102
+ end
103
+
104
+ def self.server
105
+ @@sever ||= config['server']
106
+ end
107
+
108
+ def self.editor
109
+ @@editor ||= ENV['EDITOR']
110
+ end
111
+
112
+ def self.config_file
113
+ @@config_file ||= File.join(cheatr_dir, 'config.yml')
114
+ end
115
+
116
+ def self.config
117
+ @@config ||= YAML.load_file(config_file) || {} rescue {}
118
+ end
119
+
120
+ def self.set_config(options)
121
+ config.merge!(options)
122
+ File.open(config_file, 'w') { |f| f.write config.to_yaml }
123
+ end
124
+
125
+ private
126
+
127
+ def self.confirm(message)
128
+ print "#{message} (yes/no) "
129
+ answer = $stdin.gets.chomp
130
+ %w(yes Yes YES y Y).include? answer
131
+ end
132
+ end
133
+ end