how 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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.
@@ -0,0 +1,14 @@
1
+ all: style test
2
+
3
+ install:
4
+ gem install rubocop
5
+
6
+ style:
7
+ rubocop lib
8
+
9
+ test:
10
+ ruby spec.rb
11
+
12
+ .PHONY: test
13
+
14
+
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'how/version'
2
+
3
+ require 'how/io'
4
+ require 'how/syntax_highlighter'
5
+ require 'how/text'
@@ -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
@@ -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
@@ -0,0 +1,7 @@
1
+ module How
2
+ # This module holds the How version information.
3
+ module Version
4
+ # The current version
5
+ VERSION = '0.0.1'
6
+ end
7
+ end
@@ -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,12 @@
1
+ all: jshint test
2
+
3
+ install:
4
+ npm install mocha should jshint
5
+
6
+ jshint:
7
+ ./node_modules/.bin/jshint lib
8
+
9
+ test:
10
+ ./node_modules/.bin/mocha --reporter list
11
+
12
+ .PHONY: test
@@ -0,0 +1,14 @@
1
+ ## Style Guide
2
+
3
+ All code conforms to the
4
+ [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript).
5
+
6
+ jshint?
7
+
8
+ ## Documentation
9
+
10
+ All code is documented using [jsdoc](http://usejsdoc.org/).
11
+
12
+ ## Testing
13
+
14
+ mocha
@@ -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
@@ -0,0 +1,3 @@
1
+ Dir.glob('spec/**/*.rb').each do |file|
2
+ require_relative file.sub(/\.rb$/, '')
3
+ end
@@ -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