how 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.
- 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
|