cheatr 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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +173 -0
- data/Rakefile +1 -0
- data/bin/cheatr +108 -0
- data/cheatr.gemspec +37 -0
- data/lib/cheatr.rb +6 -0
- data/lib/cheatr/client.rb +133 -0
- data/lib/cheatr/client/sheet.rb +137 -0
- data/lib/cheatr/error.rb +7 -0
- data/lib/cheatr/server.rb +22 -0
- data/lib/cheatr/server/app.rb +80 -0
- data/lib/cheatr/server/helpers.rb +47 -0
- data/lib/cheatr/server/public/404.html +157 -0
- data/lib/cheatr/server/public/css/main.css +300 -0
- data/lib/cheatr/server/public/css/normalize.css +533 -0
- data/lib/cheatr/server/public/favicon.ico +0 -0
- data/lib/cheatr/server/public/humans.txt +13 -0
- data/lib/cheatr/server/public/robots.txt +3 -0
- data/lib/cheatr/server/sheet.rb +139 -0
- data/lib/cheatr/server/views/index.md.erb +5 -0
- data/lib/cheatr/server/views/layout.html.erb +24 -0
- data/lib/cheatr/server/views/sheet.md.erb +1 -0
- data/lib/cheatr/version.rb +3 -0
- metadata +239 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/cheatr
ADDED
@@ -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
|
data/cheatr.gemspec
ADDED
@@ -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
|
data/lib/cheatr.rb
ADDED
@@ -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
|