wordnik 4.07 → 4.08
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +14 -10
- data/README.md +24 -18
- data/lib/wordnik.rb +36 -22
- data/lib/wordnik/configuration.rb +20 -8
- data/lib/wordnik/load_balancer.rb +71 -0
- data/lib/wordnik/request.rb +53 -32
- data/lib/wordnik/version.rb +1 -3
- data/spec/100words.txt +100 -0
- data/spec/load_balancer_spec.rb +28 -0
- data/spec/performance.rb +140 -0
- data/spec/request_spec.rb +14 -14
- data/spec/resource_spec.rb +24 -24
- data/spec/response_spec.rb +24 -24
- data/spec/spec_helper.rb +21 -17
- data/spec/wordnik_spec.rb +73 -41
- data/wordnik.gemspec +3 -1
- metadata +125 -25
data/lib/wordnik/version.rb
CHANGED
data/spec/100words.txt
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
sustain
|
2
|
+
almandite
|
3
|
+
source
|
4
|
+
dessert
|
5
|
+
scorching
|
6
|
+
illuminant
|
7
|
+
impeccancy
|
8
|
+
impetrated
|
9
|
+
acetaminophen
|
10
|
+
phreatophyte
|
11
|
+
churlish
|
12
|
+
pitchblende
|
13
|
+
delicious
|
14
|
+
clinquant
|
15
|
+
balmacaan
|
16
|
+
mordant
|
17
|
+
exhume
|
18
|
+
scribbling
|
19
|
+
kookaburra
|
20
|
+
cogwheel
|
21
|
+
pallesthesia
|
22
|
+
expertise
|
23
|
+
attitudinal
|
24
|
+
omission
|
25
|
+
paralipomena
|
26
|
+
hooroosh
|
27
|
+
caret
|
28
|
+
gristliness
|
29
|
+
jaded
|
30
|
+
provolone
|
31
|
+
quizzical
|
32
|
+
grotto
|
33
|
+
flammeous
|
34
|
+
courageous
|
35
|
+
phoneme
|
36
|
+
chough
|
37
|
+
ooze
|
38
|
+
soundproof
|
39
|
+
cynodont
|
40
|
+
conjunction
|
41
|
+
mamba
|
42
|
+
garlicky
|
43
|
+
pageantry
|
44
|
+
gnosis
|
45
|
+
heifer
|
46
|
+
binturong
|
47
|
+
emissaries
|
48
|
+
ombrophobous
|
49
|
+
forbiddance
|
50
|
+
junior
|
51
|
+
sporophore
|
52
|
+
namaycush
|
53
|
+
dwindling
|
54
|
+
covinous
|
55
|
+
parent
|
56
|
+
jackroller
|
57
|
+
saccharine
|
58
|
+
gabelle
|
59
|
+
exequy
|
60
|
+
demurred
|
61
|
+
effervescently
|
62
|
+
ickiness
|
63
|
+
criophore
|
64
|
+
deity
|
65
|
+
aliquant
|
66
|
+
threshold
|
67
|
+
chessman
|
68
|
+
isallotherm
|
69
|
+
abominable
|
70
|
+
grease
|
71
|
+
smuggleable
|
72
|
+
heraldry
|
73
|
+
bleakness
|
74
|
+
rhododendron
|
75
|
+
paradoxical
|
76
|
+
ronquil
|
77
|
+
graminaceous
|
78
|
+
submerge
|
79
|
+
rivulet
|
80
|
+
asthma
|
81
|
+
anemochore
|
82
|
+
animadversion
|
83
|
+
glabrescent
|
84
|
+
argot
|
85
|
+
insincere
|
86
|
+
sponson
|
87
|
+
biennial
|
88
|
+
keenness
|
89
|
+
stade
|
90
|
+
hesitancy
|
91
|
+
charades
|
92
|
+
brilliantly
|
93
|
+
dillydally
|
94
|
+
fibrinogen
|
95
|
+
apyretic
|
96
|
+
japonaiserie
|
97
|
+
oxlip
|
98
|
+
amass
|
99
|
+
satiny
|
100
|
+
unpronounceable
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wordnik::LoadBalancer do
|
4
|
+
before(:each) do
|
5
|
+
@hosts = ["alpha","beta","gamma","delta"].map{|x| "#{x}.wordnik.com"}
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Load Balancer" do
|
9
|
+
it "allows creation with a list of hosts" do
|
10
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
11
|
+
h = lb.host
|
12
|
+
@hosts.should include(h)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns different (random) hosts" do
|
16
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
17
|
+
counts = Hash.new(0)
|
18
|
+
1.upto(1000) do
|
19
|
+
h = lb.host
|
20
|
+
counts[h] += 1
|
21
|
+
end
|
22
|
+
@hosts.each {|host| counts[host].should > 10}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should not leak memory"
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/spec/performance.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ruby-prof'
|
3
|
+
|
4
|
+
describe "Load Balancer Performance" do
|
5
|
+
|
6
|
+
describe "With single host -- host performance" do
|
7
|
+
Wordnik.configuration.clear
|
8
|
+
Wordnik.configure do |config|
|
9
|
+
config.api_key = CREDENTIALS[:api_key]
|
10
|
+
config.username = CREDENTIALS[:username]
|
11
|
+
config.password = CREDENTIALS[:password]
|
12
|
+
config.logger = Logger.new('/dev/null')
|
13
|
+
config.host = CREDENTIALS[:host] = 'api.wordnik.com'
|
14
|
+
config.hosts = []
|
15
|
+
config.base_path = CREDENTIALS[:base_path] || '/v4'
|
16
|
+
end
|
17
|
+
start_time = Time.now
|
18
|
+
RubyProf.start
|
19
|
+
100000.times {|i| host = Wordnik.configuration.host}
|
20
|
+
result = RubyProf.stop
|
21
|
+
printer = RubyProf::FlatPrinter.new(result)
|
22
|
+
printer.print(STDOUT)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "With multiple hosts -- host performance" do
|
26
|
+
Wordnik.configuration.clear
|
27
|
+
Wordnik.configure do |config|
|
28
|
+
config.api_key = CREDENTIALS[:api_key]
|
29
|
+
config.username = CREDENTIALS[:username]
|
30
|
+
config.password = CREDENTIALS[:password]
|
31
|
+
config.logger = Logger.new('/dev/null')
|
32
|
+
config.host = CREDENTIALS[:host] = 'api.wordnik.com'
|
33
|
+
config.hosts = CREDENTIALS[:hosts] || ['ec2-50-18-25-12.us-west-1.compute.amazonaws.com',' ec2-50-18-67-92.us-west-1.compute.amazonaws.com']
|
34
|
+
config.base_path = CREDENTIALS[:base_path] || '/v4'
|
35
|
+
end
|
36
|
+
start_time = Time.now
|
37
|
+
RubyProf.start
|
38
|
+
100000.times {|i| host = Wordnik.configuration.host}
|
39
|
+
result = RubyProf.stop
|
40
|
+
printer = RubyProf::FlatPrinter.new(result)
|
41
|
+
printer.print(STDOUT)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "Wordnik GET performance" do
|
47
|
+
|
48
|
+
describe "With single host" do
|
49
|
+
RubyProf.start
|
50
|
+
Wordnik.configuration.clear
|
51
|
+
Wordnik.configure do |config|
|
52
|
+
config.api_key = CREDENTIALS[:api_key]
|
53
|
+
config.username = CREDENTIALS[:username]
|
54
|
+
config.password = CREDENTIALS[:password]
|
55
|
+
config.logger = Logger.new('/dev/null')
|
56
|
+
config.host = CREDENTIALS[:host] = 'api.wordnik.com'
|
57
|
+
config.hosts = []
|
58
|
+
config.base_path = CREDENTIALS[:base_path] || '/v4'
|
59
|
+
end
|
60
|
+
words = File.open(File.dirname(__FILE__) + "/100words.txt").readlines.map{|w| w.strip}
|
61
|
+
start_time = Time.now
|
62
|
+
Wordnik.logger.debug "Starting at #{start_time}"
|
63
|
+
found = 0
|
64
|
+
defs = 0
|
65
|
+
examples = 0
|
66
|
+
words.each do |word|
|
67
|
+
found += 1 if Wordnik.word.get_word(word)["id"]==0
|
68
|
+
defs += Wordnik.word.get_definitions(word).size
|
69
|
+
examples += Wordnik.word.get_examples(word).size
|
70
|
+
end
|
71
|
+
total = Time.now - start_time
|
72
|
+
result = RubyProf.stop
|
73
|
+
STDOUT.puts "Found #{found} words out of #{words.size}; #{defs} definitions; #{examples} examples in #{total} seconds"
|
74
|
+
printer = RubyProf::FlatPrinter.new(result)
|
75
|
+
printer.print(STDOUT)
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "With two hosts" do
|
79
|
+
RubyProf.start
|
80
|
+
Wordnik.configuration.clear
|
81
|
+
Wordnik.configure do |config|
|
82
|
+
config.api_key = CREDENTIALS[:api_key]
|
83
|
+
config.username = CREDENTIALS[:username]
|
84
|
+
config.password = CREDENTIALS[:password]
|
85
|
+
config.logger = Logger.new('/dev/null')
|
86
|
+
config.host = CREDENTIALS[:host] = 'api.wordnik.com'
|
87
|
+
config.hosts = CREDENTIALS[:hosts] || ['ec2-50-18-25-12.us-west-1.compute.amazonaws.com',' ec2-50-18-67-92.us-west-1.compute.amazonaws.com']
|
88
|
+
config.base_path = CREDENTIALS[:base_path] || '/v4'
|
89
|
+
end
|
90
|
+
words = File.open(File.dirname(__FILE__) + "/100words.txt").readlines.map{|w| w.strip}
|
91
|
+
start_time = Time.now
|
92
|
+
Wordnik.logger.debug "Starting at #{start_time}"
|
93
|
+
found = 0
|
94
|
+
defs = 0
|
95
|
+
examples = 0
|
96
|
+
words.each do |word|
|
97
|
+
found += 1 if Wordnik.word.get_word(word)["id"]==0
|
98
|
+
defs += Wordnik.word.get_definitions(word).size
|
99
|
+
examples += Wordnik.word.get_examples(word).size
|
100
|
+
end
|
101
|
+
total = Time.now - start_time
|
102
|
+
result = RubyProf.stop
|
103
|
+
STDOUT.puts "Found #{found} words out of #{words.size}; #{defs} definitions; #{examples} examples in #{total} seconds"
|
104
|
+
printer = RubyProf::FlatPrinter.new(result)
|
105
|
+
printer.print(STDOUT)
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "With three hosts -- one invalid" do
|
109
|
+
RubyProf.start
|
110
|
+
Wordnik.configuration.clear
|
111
|
+
Wordnik.configure do |config|
|
112
|
+
config.api_key = CREDENTIALS[:api_key]
|
113
|
+
config.username = CREDENTIALS[:username]
|
114
|
+
config.password = CREDENTIALS[:password]
|
115
|
+
config.logger = Logger.new('/dev/null')
|
116
|
+
config.host = CREDENTIALS[:host] = 'api.wordnik.com'
|
117
|
+
config.hosts = CREDENTIALS[:hosts] || ['ec2-50-18-25-12.us-west-1.compute.amazonaws.com',
|
118
|
+
'ec2-50-18-67-92.us-west-1.compute.amazonaws.com',
|
119
|
+
'localhost']
|
120
|
+
config.base_path = CREDENTIALS[:base_path] || '/v4'
|
121
|
+
end
|
122
|
+
words = File.open(File.dirname(__FILE__) + "/100words.txt").readlines.map{|w| w.strip}
|
123
|
+
start_time = Time.now
|
124
|
+
Wordnik.logger.debug "Starting at #{start_time}"
|
125
|
+
found = 0
|
126
|
+
defs = 0
|
127
|
+
examples = 0
|
128
|
+
words.each do |word|
|
129
|
+
found += 1 if Wordnik.word.get_word(word)["id"]==0
|
130
|
+
defs += Wordnik.word.get_definitions(word).size
|
131
|
+
examples += Wordnik.word.get_examples(word).size
|
132
|
+
end
|
133
|
+
total = Time.now - start_time
|
134
|
+
result = RubyProf.stop
|
135
|
+
STDOUT.puts "Found #{found} words out of #{words.size}; #{defs} definitions; #{examples} examples in #{total} seconds"
|
136
|
+
printer = RubyProf::FlatPrinter.new(result)
|
137
|
+
printer.print(STDOUT)
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
data/spec/request_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe Wordnik::Request do
|
|
15
15
|
it "sets default response format to json" do
|
16
16
|
@request.format.should == 'json'
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
it "allows params to be nil" do
|
20
20
|
@request = Wordnik::Request.new(@default_http_method, @default_path, :params => nil)
|
21
21
|
@request.query_string.should == ""
|
@@ -44,11 +44,11 @@ describe Wordnik::Request do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
it "constructs a full url" do
|
47
|
-
@request.url.
|
47
|
+
@request.url.has_suffix?("#{Wordnik.configuration.base_path}/words.json/fancy?bar=2&foo=1").should == true
|
48
48
|
end
|
49
49
|
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
describe "body" do
|
53
53
|
|
54
54
|
it "camelCases parameters" do
|
@@ -60,7 +60,7 @@ describe Wordnik::Request do
|
|
60
60
|
}))
|
61
61
|
@request.body.keys.should == [:badDog, :goodDog]
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
end
|
65
65
|
|
66
66
|
describe "path" do
|
@@ -72,7 +72,7 @@ describe Wordnik::Request do
|
|
72
72
|
:word => "cat"
|
73
73
|
}
|
74
74
|
}))
|
75
|
-
@request.url.
|
75
|
+
@request.url.has_suffix?("#{Wordnik.configuration.base_path}/word.xml/cat/entries").should == true
|
76
76
|
end
|
77
77
|
|
78
78
|
it "does string substitution on path params" do
|
@@ -82,7 +82,7 @@ describe Wordnik::Request do
|
|
82
82
|
:word => "cat"
|
83
83
|
}
|
84
84
|
}))
|
85
|
-
@request.url.
|
85
|
+
@request.url.has_suffix?("#{Wordnik.configuration.base_path}/word.xml/cat/entries").should == true
|
86
86
|
end
|
87
87
|
|
88
88
|
it "leaves path-bound params out of the query string" do
|
@@ -135,7 +135,7 @@ describe Wordnik::Request do
|
|
135
135
|
@request.query_string.should =~ /\?limit=100/
|
136
136
|
@request.url.should =~ /\?limit=100/
|
137
137
|
end
|
138
|
-
|
138
|
+
|
139
139
|
it "camelCases parameters" do
|
140
140
|
@request = Wordnik::Request.new(@default_http_method, @default_path, @default_params.merge({
|
141
141
|
:params => {
|
@@ -145,22 +145,22 @@ describe Wordnik::Request do
|
|
145
145
|
}))
|
146
146
|
@request.query_string.should == "?badDog=bud&goodDog=dud"
|
147
147
|
end
|
148
|
-
|
148
|
+
|
149
149
|
it "converts boolean values to their string representation" do
|
150
150
|
params = {:stringy => "fish", :truthy => true, :falsey => false}
|
151
151
|
@request = Wordnik::Request.new(:get, 'fakeMethod', :params => params)
|
152
152
|
@request.query_string.should == "?falsey=false&stringy=fish&truthy=true"
|
153
153
|
end
|
154
|
-
|
154
|
+
|
155
155
|
end
|
156
|
-
|
156
|
+
|
157
157
|
describe "API key" do
|
158
|
-
|
158
|
+
|
159
159
|
it "is inferred from the Wordnik base configuration by default" do
|
160
160
|
Wordnik.configure {|c| c.api_key = "xyz" }
|
161
161
|
Wordnik::Request.new(:get, "word/json").headers[:api_key].should == "xyz"
|
162
162
|
end
|
163
|
-
|
163
|
+
|
164
164
|
it "can be obfuscated for public display" do
|
165
165
|
@request = Wordnik::Request.new(:get, "words/fancy", @default_params.merge({
|
166
166
|
:params => {
|
@@ -178,7 +178,7 @@ describe Wordnik::Request do
|
|
178
178
|
@request_with_key = Wordnik::Request.new(:get, "word/json", :params => {:api_key => "jkl"})
|
179
179
|
@request_with_key.headers[:api_key].should be_nil
|
180
180
|
@request_with_key.params[:api_key].should == "jkl"
|
181
|
-
|
181
|
+
|
182
182
|
@request_without_key = Wordnik::Request.new(:get, "word/json", :params => {:api_key => nil})
|
183
183
|
@request_without_key.headers[:api_key].should be_nil
|
184
184
|
@request_without_key.params[:api_key].should be_nil
|
@@ -193,4 +193,4 @@ describe Wordnik::Request do
|
|
193
193
|
|
194
194
|
end
|
195
195
|
|
196
|
-
end
|
196
|
+
end
|
data/spec/resource_spec.rb
CHANGED
@@ -21,7 +21,7 @@ describe Wordnik::Resource do
|
|
21
21
|
@resource.endpoints.size.should >= 10
|
22
22
|
@resource.endpoints.first.class.should == Wordnik::Endpoint
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
it "defines a method for each operation nickname" do
|
26
26
|
@resource.public_methods.should include(:get_word)
|
27
27
|
@resource.public_methods.should include(:get_definitions)
|
@@ -30,14 +30,14 @@ describe Wordnik::Resource do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
describe "auto-generated methods" do
|
35
|
-
|
35
|
+
|
36
36
|
before(:each) do
|
37
37
|
configure_wordnik
|
38
|
-
VCR.use_cassette('wordnik_authenticate', :record => :new_episodes) do
|
39
|
-
|
40
|
-
end
|
38
|
+
#VCR.use_cassette('wordnik_authenticate', :record => :new_episodes) do
|
39
|
+
# Wordnik.authenticate
|
40
|
+
#end
|
41
41
|
end
|
42
42
|
|
43
43
|
it "builds requests but doesn't run them if :request_only is passed" do
|
@@ -46,25 +46,25 @@ describe Wordnik::Resource do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it "runs requests and returns their body if :request_only is absent" do
|
49
|
-
VCR.use_cassette('get_word_dynamo', :record => :new_episodes) do
|
49
|
+
#VCR.use_cassette('get_word_dynamo', :record => :new_episodes) do
|
50
50
|
@response_body = Wordnik.word.get_word('dynamo')
|
51
|
-
end
|
51
|
+
#end
|
52
52
|
@response_body.class.should == Hash
|
53
53
|
@response_body.keys.sort.should == %w(canonicalForm id lookupCount scrabble word)
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
it "allows the same auto-generated method to be called with different parameters" do
|
57
57
|
request1 = Wordnik.word_list.get_word_list_by_id('dog', :request_only => true)
|
58
58
|
request2 = Wordnik.word_list.get_word_list_by_id('cat', :request_only => true)
|
59
59
|
request1.path.should_not == request2.path
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
context "argument handling" do
|
63
63
|
|
64
64
|
before(:each) do
|
65
65
|
@request = Wordnik.word.get_examples('dynamo', :skip => 2, :limit => 10, :request_only => true)
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
it "injects required arguments into the path" do
|
69
69
|
@request.path.should == "/word/dynamo/examples"
|
70
70
|
end
|
@@ -72,7 +72,7 @@ describe Wordnik::Resource do
|
|
72
72
|
it "passes optional key-value arguments to the query string" do
|
73
73
|
@request.query_string.should == "?limit=10&skip=2"
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
it "puts key-value arguments in the request body instead of the query params for POSTs and PUTs" do
|
77
77
|
body = {
|
78
78
|
:name => "Wordnik Ruby Test List #{RAND}",
|
@@ -87,19 +87,19 @@ describe Wordnik::Resource do
|
|
87
87
|
@request.body.should have_key(:type)
|
88
88
|
@request.body.should have_key(:userId)
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
context "response transmogrification" do
|
94
|
-
|
95
|
-
it "converts definitions response into an array of definition objects" #
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
|
94
|
+
|
95
|
+
it "converts definitions response into an array of definition objects" #do
|
96
|
+
#defs = Wordnik.word.get_definitions('set')
|
97
|
+
#defs.should be_an(Array)
|
98
|
+
#defs.first.should be_a(Wordnik::Definition)
|
99
|
+
#end
|
100
|
+
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
end
|
104
|
-
|
105
|
-
end
|
104
|
+
|
105
|
+
end
|