urban 0.1.3 → 1.0.0
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/.travis.yml +0 -5
- data/HISTORY.rdoc +25 -0
- data/README.rdoc +32 -8
- data/Rakefile +24 -0
- data/bin/urban +0 -1
- data/lib/urban/cli.rb +75 -31
- data/lib/urban/dictionary.rb +21 -17
- data/lib/urban/version.rb +1 -1
- data/lib/urban/web.rb +19 -5
- data/test/data/missing.html +272 -0
- data/test/minitest/stop_light.rb +57 -0
- data/test/test_helper.rb +11 -17
- data/test/urban/cli_test.rb +175 -64
- data/test/urban/dictionary_test.rb +22 -6
- data/test/urban/web_test.rb +45 -10
- data/urban.gemspec +4 -5
- metadata +80 -47
@@ -0,0 +1,57 @@
|
|
1
|
+
class MiniTest::StopLight
|
2
|
+
ESC = "\e["
|
3
|
+
NND = "#{ESC}0m"
|
4
|
+
|
5
|
+
attr_reader :io
|
6
|
+
|
7
|
+
def initialize io
|
8
|
+
@io = io
|
9
|
+
end
|
10
|
+
|
11
|
+
def red str
|
12
|
+
normal_color(31, str)
|
13
|
+
end
|
14
|
+
|
15
|
+
def yellow str
|
16
|
+
normal_color(33, str)
|
17
|
+
end
|
18
|
+
|
19
|
+
def green str
|
20
|
+
normal_color(32, str)
|
21
|
+
end
|
22
|
+
|
23
|
+
def normal_color(color_code, str)
|
24
|
+
"#{ESC}#{color_code}m#{str}#{NND}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def print(o)
|
28
|
+
case o
|
29
|
+
when '.'; io.print green(o)
|
30
|
+
when 'E', 'F'; io.print red(o)
|
31
|
+
when 'S'; io.print yellow(o)
|
32
|
+
else; io.print o
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def puts(*o)
|
37
|
+
o.map! do |str|
|
38
|
+
case str
|
39
|
+
when /Failure:/, /Error:/, /[1-9]+ failures/, /[1-9]+ errors/;
|
40
|
+
red(str)
|
41
|
+
when /Skipped:/
|
42
|
+
yellow(str)
|
43
|
+
when /0 failures, 0 errors/;
|
44
|
+
green(str).gsub(/([1-9]+ skips)/, yellow('\1'))
|
45
|
+
else;
|
46
|
+
str.gsub(/([1-9]+ skips)/, yellow('\1'))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(msg, *args)
|
53
|
+
io.send(msg, *args)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
MiniTest::Unit.output = MiniTest::StopLight.new(MiniTest::Unit.output)
|
data/test/test_helper.rb
CHANGED
@@ -1,34 +1,28 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
+
gem 'minitest' if RUBY_VERSION > '1.9'
|
4
5
|
require 'minitest/autorun'
|
5
6
|
require 'urban'
|
6
7
|
require 'urban/cli'
|
8
|
+
require 'minitest/stop_light'
|
9
|
+
require 'ostruct'
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
:definitions => [
|
11
|
-
'Something that is made up on the spot and given little time to gather and present. Usually referring to speeches that are given only a few minutes to prepare for.',
|
11
|
+
TEST_ENTRY = Urban::Dictionary::Entry.new('impromptu',
|
12
|
+
[ 'Something that is made up on the spot and given little time to gather and present. Usually referring to speeches that are given only a few minutes to prepare for.',
|
12
13
|
'On the spot',
|
13
|
-
'Something that is made up on the spot. Can also mean a speech that was made with little or no preparation.'
|
14
|
-
|
14
|
+
'Something that is made up on the spot. Can also mean a speech that was made with little or no preparation.' ],
|
15
|
+
'http://www.urbandictionary.com/define.php?term=impromptu')
|
16
|
+
|
17
|
+
EMPTY_ENTRY = Urban::Dictionary::Entry.new('gubble', nil, nil)
|
15
18
|
|
16
19
|
def load_file(filename)
|
17
20
|
IO.read(File.expand_path("../data/#{filename}", __FILE__))
|
18
21
|
end
|
19
22
|
|
20
|
-
['refute', 'assert'].each do |action|
|
21
|
-
eval <<-EOM
|
22
|
-
def #{action}_cli_prints(matches, &block)
|
23
|
-
out, err = capture_io(&block)
|
24
|
-
[*matches].each { |match| #{action}_match(match, out) }
|
25
|
-
end
|
26
|
-
EOM
|
27
|
-
end
|
28
|
-
|
29
23
|
module Stub
|
30
24
|
def stub(name, &block)
|
31
|
-
|
32
|
-
|
25
|
+
singleton_class = class << self; self; end
|
26
|
+
singleton_class.send(:define_method, name, &block)
|
33
27
|
end
|
34
28
|
end
|
data/test/urban/cli_test.rb
CHANGED
@@ -1,101 +1,212 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'socket'
|
4
|
+
require 'shellwords'
|
2
5
|
|
3
6
|
class CLITest < MiniTest::Unit::TestCase
|
4
7
|
|
8
|
+
HELP_SCREEN = <<-EOS
|
9
|
+
Usage: urban [OPTION]... [PHRASE]
|
10
|
+
Search http://urbandictionary.com for definitions of phrases
|
11
|
+
|
12
|
+
Options:
|
13
|
+
-a, --all List all definitions
|
14
|
+
-r, --random Return a random phrase and definition
|
15
|
+
-u, --url Print the definition's url after the definition
|
16
|
+
-h, --help Show this message
|
17
|
+
-v, --version Show version information
|
18
|
+
-l, --list DEPRECATED please use --all or -a instead
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
urban cookie monster Search for "cookie monster" and print its
|
22
|
+
first definition
|
23
|
+
urban -a cookie monster Search for "cookie monster" and print all of
|
24
|
+
its available definitions
|
25
|
+
urban -r Print a random phrase and its first definition
|
26
|
+
urban -ra Print a random phrase and all of its available
|
27
|
+
definitions
|
28
|
+
|
29
|
+
EOS
|
30
|
+
|
5
31
|
def setup
|
6
32
|
@program = Urban::CLI.new
|
7
33
|
end
|
8
34
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
/-r, --random\s*Find random word on urban dictionary/,
|
15
|
-
/-h, --help\s*Show this message/,
|
16
|
-
/-version\s*Show version/
|
17
|
-
]
|
18
|
-
assert_cli_prints(expectations) { @program.run(["-h"]) }
|
35
|
+
# Helpers
|
36
|
+
def assert_program_output(argument_variations, stdout=nil, stderr=nil)
|
37
|
+
argument_variations.each do |args|
|
38
|
+
assert_output(stdout, stderr) { @program.run(Shellwords.shellwords(args)) }
|
39
|
+
end
|
19
40
|
end
|
20
41
|
|
21
|
-
|
22
|
-
args = ['-v']
|
23
|
-
assert_cli_prints(/^Urban \d+\.\d+\.\d+ \(c\) Thomas Miller$/) { @program.run(args) }
|
24
|
-
end
|
42
|
+
class CLIArgumentParsingTest < CLITest
|
25
43
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
44
|
+
# Helpers
|
45
|
+
def assert_flag_is_set(name)
|
46
|
+
["-#{name.chars.first}", "--#{name}"].each do |args|
|
47
|
+
options = @program.send(:parse, [args])
|
48
|
+
assert_equal(true, options.send(name))
|
49
|
+
end
|
50
|
+
end
|
30
51
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
52
|
+
# Tests
|
53
|
+
def test_defaults
|
54
|
+
assert_silent do
|
55
|
+
options = @program.send(:parse, [])
|
56
|
+
assert_equal(false, options.help)
|
57
|
+
assert_equal(false, options.version)
|
58
|
+
assert_equal(false, options.random)
|
59
|
+
assert_equal(false, options.all)
|
60
|
+
assert_equal('', options.phrase)
|
61
|
+
end
|
62
|
+
end
|
35
63
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
assert(actual.random, 'Args --random Expected true, returned false')
|
64
|
+
def test_phrase
|
65
|
+
assert_silent do
|
66
|
+
options = @program.send(:parse, ['foo bar'])
|
67
|
+
assert_equal('foo bar', options.phrase)
|
68
|
+
end
|
42
69
|
end
|
43
|
-
end
|
44
70
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
actual = @program.send(:parse, ['--list'])
|
50
|
-
assert(actual.list, 'Args: --list; Expected true, returned false')
|
71
|
+
def test_help_flag
|
72
|
+
assert_silent do
|
73
|
+
assert_flag_is_set('help')
|
74
|
+
end
|
51
75
|
end
|
52
|
-
end
|
53
76
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
77
|
+
def test_version_flag
|
78
|
+
assert_silent do
|
79
|
+
assert_flag_is_set('version')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_random_flag
|
84
|
+
assert_silent do
|
85
|
+
assert_flag_is_set('random')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_all_flag
|
90
|
+
assert_silent do
|
91
|
+
assert_flag_is_set('all')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_all_flag
|
96
|
+
assert_silent do
|
97
|
+
assert_flag_is_set('url')
|
98
|
+
end
|
60
99
|
end
|
61
100
|
end
|
62
101
|
|
63
|
-
class
|
102
|
+
class CLIRunnerStandardOutputTest < CLITest
|
103
|
+
|
104
|
+
SINGLE_DEFINITION = "\n#{TEST_ENTRY.phrase.upcase}\n\n#{TEST_ENTRY.definitions.first}\n\n"
|
105
|
+
MULTIPLE_DEFINITIONS = "\n#{TEST_ENTRY.phrase.upcase}\n\n#{TEST_ENTRY.definitions.join("\n\n")}\n\n"
|
106
|
+
DEFINITION_WITH_URL = "\n#{TEST_ENTRY.phrase.upcase}\n\n#{TEST_ENTRY.definitions.first}\n\nURL: #{TEST_ENTRY.url}\n\n"
|
107
|
+
|
64
108
|
def setup
|
65
|
-
@dictionary = MiniTest::Mock.new
|
66
109
|
super
|
110
|
+
@dictionary = MiniTest::Mock.new
|
111
|
+
end
|
112
|
+
|
113
|
+
# Tests
|
114
|
+
def test_help_flag_prints_help
|
115
|
+
assert_output(HELP_SCREEN) { @program.run([]) }
|
67
116
|
end
|
68
117
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
118
|
+
def test_version_flag_prints_version
|
119
|
+
['-v', '--v'].each do |args|
|
120
|
+
assert_output("Urban #{Urban::VERSION} (c) Thomas Miller\n") { @program.run([args]) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_random_flag_prints_single_definition
|
125
|
+
@program.dictionary = @dictionary.expect(:random, TEST_ENTRY)
|
126
|
+
argument_variations = ['-r', '--random']
|
127
|
+
assert_program_output(argument_variations, SINGLE_DEFINITION)
|
128
|
+
@dictionary.verify
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_phrase_prints_single_definition
|
132
|
+
@program.dictionary = @dictionary.expect(:search, TEST_ENTRY, ['impromptu'])
|
133
|
+
argument_variations = ['impromptu']
|
134
|
+
assert_program_output(argument_variations, SINGLE_DEFINITION)
|
135
|
+
@dictionary.verify
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_random_and_all_flag_prints_multiple_definitions
|
139
|
+
@program.dictionary = @dictionary.expect(:random, TEST_ENTRY)
|
140
|
+
argument_variations = ['-ra', '-r -a', '--random -a', '-r --all', '--all --random']
|
141
|
+
assert_program_output(argument_variations, MULTIPLE_DEFINITIONS)
|
74
142
|
@dictionary.verify
|
75
143
|
end
|
76
144
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
assert_cli_prints(expected) { @program.run(args) }
|
145
|
+
def test_phrase_and_all_flag_prints_multiple_definitions
|
146
|
+
@program.dictionary = @dictionary.expect(:search, TEST_ENTRY, ['impromptu'])
|
147
|
+
argument_variations = ['impromptu -a', '--all impromptu']
|
148
|
+
assert_program_output(argument_variations, MULTIPLE_DEFINITIONS)
|
82
149
|
@dictionary.verify
|
83
150
|
end
|
84
151
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
assert_cli_prints(expected) { @program.run(['impromptu']) }
|
152
|
+
def test_random_and_url_flag_prints_definition_with_url
|
153
|
+
@program.dictionary = @dictionary.expect(:random, TEST_ENTRY)
|
154
|
+
argument_variations = ['-ru', '-r -u', '--random -u', '-r --url', '--url --random']
|
155
|
+
assert_program_output(argument_variations, DEFINITION_WITH_URL)
|
90
156
|
@dictionary.verify
|
91
157
|
end
|
92
158
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
assert_cli_prints(expected) { @program.run(args) }
|
159
|
+
def test_phrase_and_url_flag_prints_definition_with_url
|
160
|
+
@program.dictionary = @dictionary.expect(:search, TEST_ENTRY, ['impromptu'])
|
161
|
+
argument_variations = ['impromptu -u', '--url impromptu']
|
162
|
+
assert_program_output(argument_variations, DEFINITION_WITH_URL)
|
98
163
|
@dictionary.verify
|
99
164
|
end
|
165
|
+
|
166
|
+
def test_list_flag_prints_deprecation_warning
|
167
|
+
expected = /WARNING: --list and -l are deprecated please use --all or -a instead/
|
168
|
+
@program.dictionary = @dictionary.expect(:search, TEST_ENTRY, ['impromptu'])
|
169
|
+
@program.dictionary = @dictionary.expect(:random, TEST_ENTRY)
|
170
|
+
stdout, stederr = capture_io { @program.run(Shellwords.shellwords('--list impromptu')) }
|
171
|
+
assert_match expected, stdout
|
172
|
+
stdou, stederr = capture_io { @program.run(Shellwords.shellwords('-rl')) }
|
173
|
+
assert_match expected, stdout
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class CLIRunnerErrorOutputTest < CLITest
|
178
|
+
|
179
|
+
ERROR_MISSING_PHRASE = "urban: no definitions found for #{EMPTY_ENTRY.phrase.upcase}.\n"
|
180
|
+
ERROR_NO_INTERNET = "urban: no internet connection available.\n"
|
181
|
+
ERROR_INVALID_OPTION = <<-EOE
|
182
|
+
urban: invalid option: -b
|
183
|
+
Try `urban --help' for more information.
|
184
|
+
EOE
|
185
|
+
|
186
|
+
def setup
|
187
|
+
super
|
188
|
+
end
|
189
|
+
|
190
|
+
# Tests
|
191
|
+
def test_search_missing_phrase_prints_error
|
192
|
+
dictionary = MiniTest::Mock.new
|
193
|
+
@program.dictionary = dictionary.expect(:search, EMPTY_ENTRY, ['gubble'])
|
194
|
+
assert_program_output(['gubble'], nil, ERROR_MISSING_PHRASE)
|
195
|
+
dictionary.verify
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_search_missing_phrase_prints_error
|
199
|
+
dictionary = (Object.new).extend Stub
|
200
|
+
dictionary.stub(:search) { |phrase| raise SocketError }
|
201
|
+
@program.dictionary = dictionary
|
202
|
+
assert_program_output(['gubble'], nil, ERROR_NO_INTERNET)
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_invalid_option_prints_help
|
206
|
+
dictionary = (Object.new).extend Stub
|
207
|
+
dictionary.stub(:search) { |phrase| raise OptionParser::InvalidOption }
|
208
|
+
@program.dictionary = dictionary
|
209
|
+
assert_program_output(['-b'], nil, ERROR_INVALID_OPTION)
|
210
|
+
end
|
100
211
|
end
|
101
212
|
end
|
@@ -4,18 +4,34 @@ class DictionaryTest < MiniTest::Unit::TestCase
|
|
4
4
|
|
5
5
|
def setup
|
6
6
|
@web_service = MiniTest::Mock.new
|
7
|
-
@dictionary = Urban::Dictionary
|
7
|
+
@dictionary = Urban::Dictionary
|
8
|
+
|
9
|
+
@response = OpenStruct.new
|
10
|
+
@response.url = 'http://www.urbandictionary.com/define.php?term=impromptu'
|
11
|
+
@response.stream = load_file('impromptu.html')
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_process_extracts_elements_from_html
|
15
|
+
entry = @dictionary.send(:process, @response )
|
16
|
+
assert_equal(TEST_ENTRY, entry)
|
8
17
|
end
|
9
18
|
|
10
19
|
def test_dictionary_calls_random
|
11
|
-
@dictionary.web_service = @web_service.expect(:
|
12
|
-
assert_equal(
|
20
|
+
@dictionary.web_service = @web_service.expect(:random, @response)
|
21
|
+
assert_equal(TEST_ENTRY, @dictionary.random)
|
13
22
|
@web_service.verify
|
14
23
|
end
|
15
24
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
25
|
+
def test_dictionary_calls_search
|
26
|
+
@dictionary.web_service = @web_service.expect(:search, @response, ['impromptu'])
|
27
|
+
assert_equal(TEST_ENTRY, @dictionary.search('impromptu'))
|
28
|
+
@web_service.verify
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_dictionary_returns_empty_for_missing_phrases
|
32
|
+
@response.stream = load_file('missing.html')
|
33
|
+
@dictionary.web_service = @web_service.expect(:search, @response, ['gubble'])
|
34
|
+
assert_equal(EMPTY_ENTRY, @dictionary.search('gubble'))
|
19
35
|
@web_service.verify
|
20
36
|
end
|
21
37
|
end
|
data/test/urban/web_test.rb
CHANGED
@@ -3,19 +3,54 @@ require 'test_helper'
|
|
3
3
|
class WebTest < MiniTest::Unit::TestCase
|
4
4
|
|
5
5
|
def setup
|
6
|
-
@
|
7
|
-
@web_service.stub(:open) { |arg| return arg; }
|
6
|
+
@web_module = (Object.new).extend(Urban::Web).extend(Stub)
|
8
7
|
end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
class WebFetchTest < WebTest
|
10
|
+
def setup
|
11
|
+
super
|
12
|
+
@web_module.stub(:open) { |arg| arg }
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_fetch_with_no_params
|
16
|
+
expected = 'http://www.urbandictionary.com/test.php'
|
17
|
+
actual = @web_module.fetch('test.php')
|
18
|
+
assert_equal(expected, actual)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_fetch_with_params
|
22
|
+
expected = /http:\/\/www\.urbandictionary\.com\/test\.php\?\w+=\w+&\w+=\w+/
|
23
|
+
actual = @web_module.fetch('test.php', :name => 'foo', :term => 'bar')
|
24
|
+
assert_match(expected, actual)
|
25
|
+
end
|
14
26
|
end
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
28
|
+
class WebInterfaceTest < WebTest
|
29
|
+
|
30
|
+
def setup
|
31
|
+
super
|
32
|
+
@expected = OpenStruct.new
|
33
|
+
@expected.base_uri = 'http://www.urbandictionary.com/define.php?term=impromptu'
|
34
|
+
|
35
|
+
@web_module.stub(:open) do |arg|
|
36
|
+
result = OpenStruct.new
|
37
|
+
result.base_uri = 'http://www.urbandictionary.com/define.php?term=impromptu'
|
38
|
+
result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_returns_response_for_random_word
|
43
|
+
actual = @web_module.random
|
44
|
+
|
45
|
+
assert_equal(@expected.base_uri, actual.url)
|
46
|
+
assert_equal(@expected, actual.stream)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_returns_response_for_define_with_phrase
|
50
|
+
actual = @web_module.search('cookie monster')
|
51
|
+
|
52
|
+
assert_equal(@expected.base_uri, actual.url)
|
53
|
+
assert_equal(@expected, actual.stream)
|
54
|
+
end
|
20
55
|
end
|
21
56
|
end
|