how 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/LICENSE +27 -0
- data/Makefile +14 -0
- data/README.md +34 -0
- data/bin/how +36 -0
- data/how.gemspec +18 -0
- data/lib/how.rb +5 -0
- data/lib/how/io.rb +65 -0
- data/lib/how/syntax_highlighter.rb +18 -0
- data/lib/how/text.rb +32 -0
- data/lib/how/version.rb +7 -0
- data/reference/javascript/.jshintrc +57 -0
- data/reference/javascript/Makefile +12 -0
- data/reference/javascript/README.md +14 -0
- data/reference/javascript/lib/string.js +16 -0
- data/reference/javascript/test/string.js +21 -0
- data/spec.rb +3 -0
- data/spec/lib/io_spec.rb +40 -0
- data/spec/lib/syntax_highlighter_spec.rb +38 -0
- data/spec/lib/text_spec.rb +134 -0
- metadata +67 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 48c8bf4b4a85f36d528ac753d12c49abb0757417
|
4
|
+
data.tar.gz: d6eab7ef8176e1656aa72ee98804afcde05f194a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57db65eea8c56c6ae4ec4962500a1dfed07657abf34ff8293d19612f2f54c666d13cf388847d6e88486bba9d6b54244140b37ed2b2df3a6d766797562d286de7
|
7
|
+
data.tar.gz: 253e335a0d5336a8d29dba75ad26ff30e8bca00b504ff7113f434b06f18d5664a17e45e2b3c6fe4e3441a90b65e1dd1acc7489c12f18a8cff40078bb7d1acc90
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
/*.gem
|
2
|
+
/*.rbc
|
3
|
+
/.bundle
|
4
|
+
/.config
|
5
|
+
/coverage
|
6
|
+
/InstalledFiles
|
7
|
+
/lib/bundler/man
|
8
|
+
/pkg
|
9
|
+
/rdoc
|
10
|
+
/spec/reports
|
11
|
+
/test/tmp
|
12
|
+
/test/version_tmp
|
13
|
+
/tmp
|
14
|
+
|
15
|
+
# YARD artifacts
|
16
|
+
/.yardoc
|
17
|
+
/_yardoc
|
18
|
+
/doc/
|
19
|
+
|
20
|
+
|
21
|
+
/reference/javascript/node_modules
|
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2014, Nick Sinopoli <nsinopoli@gmail.com>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice, this
|
11
|
+
list of conditions and the following disclaimer in the documentation and/or
|
12
|
+
other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of Nick Sinopoli nor the names of his
|
15
|
+
contributors may be used to endorse or promote products derived from
|
16
|
+
this software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# how
|
2
|
+
|
3
|
+
**how** is a reference manual for programmers.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```bash
|
8
|
+
gem install how
|
9
|
+
```
|
10
|
+
|
11
|
+
For syntax highlighting, be sure to install [Pygments](http://pygments.org/).
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
```bash
|
16
|
+
how [domain] [keywords]
|
17
|
+
```
|
18
|
+
|
19
|
+
**how** takes two arguments:
|
20
|
+
|
21
|
+
1. `domain` is the domain to be searched. The following domains are currently supported:
|
22
|
+
|
23
|
+
+ [js](reference/javascript/README.md) (JavaScript)
|
24
|
+
|
25
|
+
1. `keywords` are the search terms. (All keywords are used to find a match. If your search yields no results, try using fewer keywords.)
|
26
|
+
|
27
|
+
### Example
|
28
|
+
|
29
|
+
```bash
|
30
|
+
# How do I capitalize a string in JavaScript?
|
31
|
+
how js capitalize string
|
32
|
+
```
|
33
|
+
|
34
|
+
The results will be paged in the program set in `$PAGER`. (If `$PAGER` isn't set, [less](http://www.greenwoodsoftware.com/less/) will be used.) Additionally, if [Pygments](http://pygments.org/) is installed, the syntax will be highlighted.
|
data/bin/how
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
4
|
+
|
5
|
+
require 'how'
|
6
|
+
|
7
|
+
def run(argv)
|
8
|
+
args = { domain: argv.shift, keywords: argv }
|
9
|
+
|
10
|
+
domain_path = How::IO.get_domain_path args[:domain]
|
11
|
+
abort 'Invalid domain.' if !domain_path
|
12
|
+
|
13
|
+
matches = Dir.glob(domain_path).reduce([]) do |list, filename|
|
14
|
+
contents = File.read filename
|
15
|
+
blocks = How::Text.find_blocks contents, args[:keywords]
|
16
|
+
next list if blocks.empty?
|
17
|
+
list << blocks.map { |block| How::Text.drop_first_and_last_lines block }
|
18
|
+
end
|
19
|
+
|
20
|
+
abort 'No results found. Try changing your search terms.' if matches.empty?
|
21
|
+
|
22
|
+
results = matches.join "\n\n"
|
23
|
+
|
24
|
+
if RUBY_PLATFORM =~ /win32/ || !$stdout.tty?
|
25
|
+
return $stdout.puts results
|
26
|
+
end
|
27
|
+
|
28
|
+
if How::IO.which 'pygmentize'
|
29
|
+
lexer = How::SyntaxHighlighter.get_pygments_lexer args[:domain]
|
30
|
+
results = How::IO.pygmentize results, lexer
|
31
|
+
end
|
32
|
+
|
33
|
+
How::IO.page results
|
34
|
+
end
|
35
|
+
|
36
|
+
run ARGV
|
data/how.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'how/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'how'
|
6
|
+
gem.version = How::Version::VERSION
|
7
|
+
gem.summary = 'A reference manual for programmers'
|
8
|
+
gem.description = 'Eliminates the tedium of hunting for answers to common programming questions'
|
9
|
+
gem.license = 'BSD (3-Clause)'
|
10
|
+
gem.authors = ['Nick Sinopoli']
|
11
|
+
gem.email = 'NSinopoli@gmail.com'
|
12
|
+
gem.homepage = 'https://github.com/NSinopoli/how'
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(spec)/})
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
end
|
data/lib/how.rb
ADDED
data/lib/how/io.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module How
|
2
|
+
# This module contains methods to handle IO-related operations.
|
3
|
+
module IO
|
4
|
+
# Retrieves the filepath (relative to the root of this project) of the
|
5
|
+
# reference files for the supplied domain.
|
6
|
+
#
|
7
|
+
# @param [String] domain The domain
|
8
|
+
# @return [String, nil]
|
9
|
+
def self.get_domain_path(domain)
|
10
|
+
root = File.expand_path '../..', File.dirname(__FILE__)
|
11
|
+
|
12
|
+
case domain.downcase
|
13
|
+
when 'rb', 'ruby'
|
14
|
+
File.join root, 'reference', 'ruby', 'lib', '*.rb'
|
15
|
+
when 'js', 'javascript'
|
16
|
+
File.join root, 'reference', 'javascript', 'lib', '*.js'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Runs the supplied text through the pager defined in $PAGER.
|
21
|
+
#
|
22
|
+
# @param [String] text The text
|
23
|
+
def self.page(text)
|
24
|
+
pager = ENV['PAGER'] || 'less'
|
25
|
+
::IO.popen(pager, 'w') { |process| process.puts text }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Pygmentizes the supplied text. A lexer will be inferred (based on
|
29
|
+
# syntax) if it is not supplied.
|
30
|
+
#
|
31
|
+
# @param [String] text The text to be pygmentized
|
32
|
+
# @param [String] lexer The lexer
|
33
|
+
# @return [String]
|
34
|
+
def self.pygmentize(text, lexer = nil)
|
35
|
+
if !lexer
|
36
|
+
command = 'pygmentize -g'
|
37
|
+
else
|
38
|
+
command = "pygmentize -l '#{lexer}'"
|
39
|
+
end
|
40
|
+
|
41
|
+
::IO.popen(command, 'r+') do |process|
|
42
|
+
process.puts text
|
43
|
+
process.close_write
|
44
|
+
process.read
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Finds the supplied command (if it exists) within the $PATH.
|
49
|
+
#
|
50
|
+
# @param [String] command The command
|
51
|
+
# @return [String, nil]
|
52
|
+
def self.which(command)
|
53
|
+
extensions = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
54
|
+
|
55
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
56
|
+
extensions.each do |extension|
|
57
|
+
exe = File.join path, "#{command}#{extension}"
|
58
|
+
return exe if File.executable? exe
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module How
|
2
|
+
# This module contains methods to handle syntax highlighting.
|
3
|
+
module SyntaxHighlighter
|
4
|
+
# Retrieves the Pygments lexer corresponding to the supplied syntax type.
|
5
|
+
#
|
6
|
+
# @see http://pygments.org/docs/lexers/
|
7
|
+
# @param [String] syntax The type of syntax
|
8
|
+
# @return [String, nil]
|
9
|
+
def self.get_pygments_lexer(syntax)
|
10
|
+
case syntax.downcase
|
11
|
+
when 'rb', 'ruby'
|
12
|
+
'rb'
|
13
|
+
when 'js', 'javascript'
|
14
|
+
'js'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/how/text.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module How
|
2
|
+
# This module contains methods to handle text manipulation.
|
3
|
+
module Text
|
4
|
+
# Drops the first and last lines of the supplied text.
|
5
|
+
#
|
6
|
+
# @param [String] text The text
|
7
|
+
# @return [String]
|
8
|
+
def self.drop_first_and_last_lines(text)
|
9
|
+
_, *body, _ = text.lines
|
10
|
+
body.join.chomp
|
11
|
+
end
|
12
|
+
|
13
|
+
# Finds the blocks identified by the supplied keywords within the
|
14
|
+
# supplied text.
|
15
|
+
#
|
16
|
+
# @param [String] text The text
|
17
|
+
# @param [Array] keywords The keywords
|
18
|
+
# @return [Array]
|
19
|
+
def self.find_blocks(text, keywords)
|
20
|
+
return [] if keywords.empty?
|
21
|
+
|
22
|
+
# If keywords is, for example, ['capitalize', 'string'], this will
|
23
|
+
# create a regex that looks like this:
|
24
|
+
# (?=Tags:.*\bcapitalize\b)(?=Tags:.*\bstring\b)[\S\s]*?!~!
|
25
|
+
block_regex = keywords.reduce('') do |result, keyword|
|
26
|
+
result + '(?=Tags:.*\b' + keyword.downcase + '\b)'
|
27
|
+
end + '[\S\s]*?!~!'
|
28
|
+
|
29
|
+
text.scan(/#{block_regex}/)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/how/version.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
{
|
2
|
+
/*
|
3
|
+
* ENVIRONMENTS
|
4
|
+
* =================
|
5
|
+
*/
|
6
|
+
|
7
|
+
// Define globals exposed by modern browsers.
|
8
|
+
"browser": true,
|
9
|
+
|
10
|
+
// Define globals exposed by jQuery.
|
11
|
+
"jquery": true,
|
12
|
+
|
13
|
+
// Define globals exposed by Node.js.
|
14
|
+
"node": true,
|
15
|
+
|
16
|
+
/*
|
17
|
+
* ENFORCING OPTIONS
|
18
|
+
* =================
|
19
|
+
*/
|
20
|
+
|
21
|
+
// Force all variable names to use either camelCase style or UPPER_CASE
|
22
|
+
// with underscores.
|
23
|
+
"camelcase": true,
|
24
|
+
|
25
|
+
// Prohibit use of == and != in favor of === and !==.
|
26
|
+
"eqeqeq": true,
|
27
|
+
|
28
|
+
// Suppress warnings about == null comparisons.
|
29
|
+
"eqnull": true,
|
30
|
+
|
31
|
+
// Enforce tab width of 2 spaces.
|
32
|
+
"indent": 2,
|
33
|
+
|
34
|
+
// Prohibit use of a variable before it is defined.
|
35
|
+
"latedef": true,
|
36
|
+
|
37
|
+
// Require capitalized names for constructor functions.
|
38
|
+
"newcap": true,
|
39
|
+
|
40
|
+
// Enforce use of single quotation marks for strings.
|
41
|
+
"quotmark": "single",
|
42
|
+
|
43
|
+
// Prohibit trailing whitespace.
|
44
|
+
"trailing": true,
|
45
|
+
|
46
|
+
// Prohibit use of explicitly undeclared variables.
|
47
|
+
"undef": true,
|
48
|
+
|
49
|
+
// Warn when variables are defined but never used.
|
50
|
+
"unused": true,
|
51
|
+
|
52
|
+
// Enforce line length to 80 characters
|
53
|
+
"maxlen": 80,
|
54
|
+
|
55
|
+
// Enforce placing 'use strict' at the top function scope
|
56
|
+
"strict": true
|
57
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
// Tags: capitalize capital uppercase first letter string
|
4
|
+
// Compatibility notes: TODO
|
5
|
+
/**
|
6
|
+
* Capitalizes the first letter of the supplied string.
|
7
|
+
*
|
8
|
+
* @param {string} string The string
|
9
|
+
* @returns {string} The capitalized string
|
10
|
+
*/
|
11
|
+
function capitalize(string) {
|
12
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
13
|
+
}
|
14
|
+
// !~!
|
15
|
+
|
16
|
+
exports.capitalize = capitalize;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var should = require('should'),
|
4
|
+
string = require('../lib/string.js');
|
5
|
+
|
6
|
+
describe('capitalize', function() {
|
7
|
+
|
8
|
+
it('capitalizes the first letter of the supplied string', function() {
|
9
|
+
string.capitalize('hello world!').should.equal('Hello world!');
|
10
|
+
string.capitalize('Hello world!').should.equal('Hello world!');
|
11
|
+
});
|
12
|
+
|
13
|
+
it('leaves the other letters unchanged', function() {
|
14
|
+
string.capitalize('FOO').should.equal('FOO');
|
15
|
+
string.capitalize('bar BAR bar').should.equal('Bar BAR bar');
|
16
|
+
});
|
17
|
+
|
18
|
+
it('returns an empty string when supplied with an empty string', function() {
|
19
|
+
string.capitalize('').should.equal('');
|
20
|
+
});
|
21
|
+
});
|
data/spec.rb
ADDED
data/spec/lib/io_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../lib/how/io'
|
3
|
+
|
4
|
+
describe How::IO do
|
5
|
+
|
6
|
+
describe '.get_domain_path' do
|
7
|
+
|
8
|
+
root = File.expand_path '../..', File.dirname(__FILE__)
|
9
|
+
|
10
|
+
describe 'when the domain can be identified' do
|
11
|
+
it 'returns the correct path' do
|
12
|
+
%w(rb ruby RB Ruby).each do |contents|
|
13
|
+
actual = How::IO.get_domain_path contents
|
14
|
+
expected = File.join root, 'reference', 'ruby', 'lib', '*.rb'
|
15
|
+
actual.must_equal expected
|
16
|
+
end
|
17
|
+
|
18
|
+
%w(js javascript JS JavaScript).each do |contents|
|
19
|
+
actual = How::IO.get_domain_path contents
|
20
|
+
expected = File.join root, 'reference', 'javascript', 'lib', '*.js'
|
21
|
+
actual.must_equal expected
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'when the domain cannot be identified' do
|
27
|
+
it 'returns nil' do
|
28
|
+
contents = 'doesnotexist'
|
29
|
+
actual = How::IO.get_domain_path contents
|
30
|
+
expected = nil
|
31
|
+
actual.must_equal expected
|
32
|
+
|
33
|
+
contents = ''
|
34
|
+
actual = How::IO.get_domain_path contents
|
35
|
+
expected = nil
|
36
|
+
actual.must_equal expected
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../lib/how/syntax_highlighter'
|
3
|
+
|
4
|
+
describe How::SyntaxHighlighter do
|
5
|
+
|
6
|
+
describe '.get_pygments_lexer' do
|
7
|
+
describe 'when the syntax can be identified' do
|
8
|
+
it 'returns the correct lexer' do
|
9
|
+
%w(rb ruby RB Ruby).each do |contents|
|
10
|
+
actual = How::SyntaxHighlighter.get_pygments_lexer contents
|
11
|
+
expected = 'rb'
|
12
|
+
actual.must_equal expected
|
13
|
+
end
|
14
|
+
|
15
|
+
%w(js javascript JS JavaScript).each do |contents|
|
16
|
+
actual = How::SyntaxHighlighter.get_pygments_lexer contents
|
17
|
+
expected = 'js'
|
18
|
+
actual.must_equal expected
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'when the syntax cannot be identified' do
|
24
|
+
it 'returns nil' do
|
25
|
+
contents = 'doesnotexist'
|
26
|
+
actual = How::SyntaxHighlighter.get_pygments_lexer contents
|
27
|
+
expected = nil
|
28
|
+
actual.must_equal expected
|
29
|
+
|
30
|
+
contents = ''
|
31
|
+
actual = How::SyntaxHighlighter.get_pygments_lexer contents
|
32
|
+
expected = nil
|
33
|
+
actual.must_equal expected
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../lib/how/text'
|
3
|
+
|
4
|
+
describe How::Text do
|
5
|
+
|
6
|
+
describe '.drop_first_and_last_lines' do
|
7
|
+
describe 'when the supplied string contains at least three lines' do
|
8
|
+
it 'removes the first and last lines' do
|
9
|
+
contents = "first\nmiddle\nlast"
|
10
|
+
actual = How::Text.drop_first_and_last_lines contents
|
11
|
+
expected = 'middle'
|
12
|
+
actual.must_equal expected
|
13
|
+
|
14
|
+
contents = "first\nsecond\nthird\nfourth"
|
15
|
+
actual = How::Text.drop_first_and_last_lines contents
|
16
|
+
expected = "second\nthird"
|
17
|
+
actual.must_equal expected
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'when the supplied string contains less than three lines' do
|
22
|
+
it 'returns an empty string' do
|
23
|
+
contents = "first\nlast"
|
24
|
+
actual = How::Text.drop_first_and_last_lines contents
|
25
|
+
expected = ''
|
26
|
+
actual.must_equal expected
|
27
|
+
|
28
|
+
contents = 'first'
|
29
|
+
actual = How::Text.drop_first_and_last_lines contents
|
30
|
+
expected = ''
|
31
|
+
actual.must_equal expected
|
32
|
+
|
33
|
+
contents = ''
|
34
|
+
actual = How::Text.drop_first_and_last_lines contents
|
35
|
+
expected = ''
|
36
|
+
actual.must_equal expected
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '.find_blocks' do
|
42
|
+
contents = []
|
43
|
+
contents << <<-BLOCK
|
44
|
+
// Tags: capitalize capital uppercase first letter string
|
45
|
+
/**
|
46
|
+
* Capitalizes the first letter of the supplied string.
|
47
|
+
*
|
48
|
+
* @param {string} string The string
|
49
|
+
* @returns {string} The capitalized string
|
50
|
+
*/
|
51
|
+
function capitalize(string) {
|
52
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
53
|
+
}
|
54
|
+
// !~!
|
55
|
+
BLOCK
|
56
|
+
|
57
|
+
contents << <<-BLOCK
|
58
|
+
# Tags: nothing
|
59
|
+
# Does nothing.
|
60
|
+
#
|
61
|
+
# @param [String] flavor The flavor of nothing
|
62
|
+
# @return [nil]
|
63
|
+
def nothing(flavor)
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
# !~!
|
67
|
+
BLOCK
|
68
|
+
|
69
|
+
contents << <<-BLOCK
|
70
|
+
# Tags: reverse string
|
71
|
+
def reverse(string):
|
72
|
+
"""Reverse a string.
|
73
|
+
|
74
|
+
:Parameters:
|
75
|
+
- `string`: The string to reverse
|
76
|
+
"""
|
77
|
+
return string[::-1]
|
78
|
+
# !~!
|
79
|
+
BLOCK
|
80
|
+
|
81
|
+
describe 'when the supplied keyword matches multiple blocks' do
|
82
|
+
it 'finds all of the matching blocks' do
|
83
|
+
actual = How::Text.find_blocks contents.join, ['string']
|
84
|
+
expected = trim_blocks [contents[0], contents[2]]
|
85
|
+
actual.must_equal expected
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'when the supplied keyword matches only one block' do
|
90
|
+
it 'finds the one matching block' do
|
91
|
+
actual = How::Text.find_blocks contents.join, ['capitalize']
|
92
|
+
expected = trim_blocks [contents[0]]
|
93
|
+
actual.must_equal expected
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'when the keyword matches only one block' do
|
98
|
+
it 'finds the one matching block' do
|
99
|
+
actual = How::Text.find_blocks contents.join, ['capitalize']
|
100
|
+
expected = trim_blocks [contents[0]]
|
101
|
+
actual.must_equal expected
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'when the (case-insensitive) keyword matches a block' do
|
106
|
+
it 'finds the one matching block' do
|
107
|
+
actual = How::Text.find_blocks contents.join, ['NOTHING']
|
108
|
+
expected = trim_blocks [contents[1]]
|
109
|
+
actual.must_equal expected
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'when the keywords match no blocks' do
|
114
|
+
it 'returns an empty array' do
|
115
|
+
actual = How::Text.find_blocks contents.join, %w(nothing sorry)
|
116
|
+
expected = []
|
117
|
+
actual.must_equal expected
|
118
|
+
|
119
|
+
actual = How::Text.find_blocks contents.join, ['sorry']
|
120
|
+
expected = []
|
121
|
+
actual.must_equal expected
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
def trim_blocks(blocks)
|
130
|
+
blocks.map do |block|
|
131
|
+
first, *rest = block.lines
|
132
|
+
first.gsub(/^.*?T/, 'T') + rest.join.chomp
|
133
|
+
end
|
134
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: how
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Sinopoli
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-30 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Eliminates the tedium of hunting for answers to common programming questions
|
14
|
+
email: NSinopoli@gmail.com
|
15
|
+
executables:
|
16
|
+
- how
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- LICENSE
|
22
|
+
- Makefile
|
23
|
+
- README.md
|
24
|
+
- bin/how
|
25
|
+
- how.gemspec
|
26
|
+
- lib/how.rb
|
27
|
+
- lib/how/io.rb
|
28
|
+
- lib/how/syntax_highlighter.rb
|
29
|
+
- lib/how/text.rb
|
30
|
+
- lib/how/version.rb
|
31
|
+
- reference/javascript/.jshintrc
|
32
|
+
- reference/javascript/Makefile
|
33
|
+
- reference/javascript/README.md
|
34
|
+
- reference/javascript/lib/string.js
|
35
|
+
- reference/javascript/test/string.js
|
36
|
+
- spec.rb
|
37
|
+
- spec/lib/io_spec.rb
|
38
|
+
- spec/lib/syntax_highlighter_spec.rb
|
39
|
+
- spec/lib/text_spec.rb
|
40
|
+
homepage: https://github.com/NSinopoli/how
|
41
|
+
licenses:
|
42
|
+
- BSD (3-Clause)
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 2.1.4
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: A reference manual for programmers
|
64
|
+
test_files:
|
65
|
+
- spec/lib/io_spec.rb
|
66
|
+
- spec/lib/syntax_highlighter_spec.rb
|
67
|
+
- spec/lib/text_spec.rb
|