carbon 2.2.3 → 3.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/CHANGELOG +15 -0
- data/Gemfile +0 -10
- data/Rakefile +10 -11
- data/carbon.gemspec +18 -6
- data/lib/carbon.rb +9 -61
- data/lib/carbon/query.rb +140 -0
- data/lib/carbon/query_pool.rb +11 -0
- data/lib/carbon/shell/emitter.rb +1 -1
- data/lib/carbon/version.rb +1 -1
- data/spec/carbon/query_spec.rb +106 -0
- data/spec/carbon_spec.rb +237 -0
- data/spec/cassettes/2006_Altima.yml +69 -0
- data/spec/cassettes/2006_Altima_in_2010.yml +230 -0
- data/spec/cassettes/2006_Altima_in_2011.yml +1239 -0
- data/spec/cassettes/Flight.yml +59 -0
- data/spec/cassettes/Flight_and_Automobile.yml +60 -0
- data/spec/cassettes/LAX-_SFO_flight.yml +74 -0
- data/spec/cassettes/Monkey.yml +42 -0
- data/spec/cassettes/carbon_bp_com_flight.yml +58 -0
- data/spec/cassettes/flight_with_key_1.yml +59 -0
- data/spec/cassettes/flight_with_key_2.yml +59 -0
- data/spec/cassettes/timeframed_flight.yml +59 -0
- data/spec/helper.rb +25 -0
- data/spec/support/my_nissan_altima.rb +36 -0
- metadata +200 -13
- data/lib/carbon/future.rb +0 -109
- data/test/carbon_test.rb +0 -302
- data/test/helper.rb +0 -25
data/test/carbon_test.rb
DELETED
@@ -1,302 +0,0 @@
|
|
1
|
-
require File.expand_path("../helper", __FILE__)
|
2
|
-
|
3
|
-
Thread.abort_on_exception = true
|
4
|
-
Carbon.key = 'carbon_test'
|
5
|
-
|
6
|
-
class MyNissan
|
7
|
-
def name
|
8
|
-
'Nissan'
|
9
|
-
end
|
10
|
-
def to_s
|
11
|
-
raise "Not fair!"
|
12
|
-
end
|
13
|
-
alias :inspect :to_s
|
14
|
-
end
|
15
|
-
|
16
|
-
class MyNissanAltima
|
17
|
-
class << self
|
18
|
-
def all(options)
|
19
|
-
raise unless options == { :order => :year }
|
20
|
-
[ new(2000), new(2001), new(2002), new(2003), new(2004) ]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
def initialize(model_year)
|
24
|
-
@model_year = model_year
|
25
|
-
end
|
26
|
-
def make; MyNissan.new end
|
27
|
-
def model; 'Altima' end
|
28
|
-
def model_year; @model_year end # what BP knows as "year"
|
29
|
-
def fuel_type; 'R' end # what BP knows as "automobile_fuel" and keys on "code"
|
30
|
-
def nil_make; nil end
|
31
|
-
def nil_model; nil end
|
32
|
-
include Carbon
|
33
|
-
emit_as 'Automobile' do
|
34
|
-
provide(:make) { |my_nissan_altima| my_nissan_altima.make.try(:name) }
|
35
|
-
provide :model
|
36
|
-
provide :model_year, :as => :year
|
37
|
-
provide :fuel_type, :as => :automobile_fuel, :key => :code
|
38
|
-
provide(:nil_make) { |my_nissan_altima| my_nissan_altima.nil_make.try(:blam!) }
|
39
|
-
provide :nil_model
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
describe Carbon do
|
44
|
-
before do
|
45
|
-
flush_cache!
|
46
|
-
end
|
47
|
-
|
48
|
-
describe :query do
|
49
|
-
describe '(one at a time)' do
|
50
|
-
it "calculates flight impact" do
|
51
|
-
result = Carbon.query('Flight', :origin_airport => 'LAX', :destination_airport => 'SFO', :segments_per_trip => 1, :trips => 1)
|
52
|
-
result.decisions.carbon.object.value.must_be_close_to 200, 50
|
53
|
-
end
|
54
|
-
it "can be used on an object that response to #as_impact_query" do
|
55
|
-
Carbon.query(MyNissanAltima.new(2006)).decisions.must_equal MyNissanAltima.new(2006).impact.decisions
|
56
|
-
end
|
57
|
-
it "gets back characteristics" do
|
58
|
-
result = Carbon.query('Flight', :origin_airport => 'LAX', :destination_airport => 'SFO', :segments_per_trip => 1, :trips => 1)
|
59
|
-
result.characteristics.origin_airport.description.must_match %r{lax}i
|
60
|
-
end
|
61
|
-
it "tells you if the query is successful" do
|
62
|
-
result = Carbon.query('Flight')
|
63
|
-
result.success.must_equal true
|
64
|
-
end
|
65
|
-
it "is gentle about errors" do
|
66
|
-
result = Carbon.query('Monkey')
|
67
|
-
result.success.must_equal false
|
68
|
-
end
|
69
|
-
it "sends timeframe properly" do
|
70
|
-
result = Carbon.query('Flight', :timeframe => Timeframe.new(:year => 2009))
|
71
|
-
result.timeframe.startDate.must_equal '2009-01-01'
|
72
|
-
result.timeframe.endDate.must_equal '2010-01-01'
|
73
|
-
end
|
74
|
-
it "sends key properly" do
|
75
|
-
with_web_mock do
|
76
|
-
WebMock.stub_request(:post, 'http://impact.brighterplanet.com/flights.json').with(:body => hash_including(:key => 'carbon_test')).to_return(:status => 500, :body => 'default')
|
77
|
-
WebMock.stub_request(:post, 'http://impact.brighterplanet.com/flights.json').with(:body => hash_including(:key => 'carbon_test1')).to_return(:status => 500, :body => 'A')
|
78
|
-
WebMock.stub_request(:post, 'http://impact.brighterplanet.com/flights.json').with(:body => hash_including(:key => 'carbon_test2')).to_return(:status => 500, :body => 'B')
|
79
|
-
Carbon.query('Flight', :key => 'carbon_test2').errors.first.must_equal 'B'
|
80
|
-
Carbon.query('Flight').errors.first.must_equal 'default'
|
81
|
-
Carbon.query('Flight', :key => 'carbon_test1').errors.first.must_equal 'A'
|
82
|
-
end
|
83
|
-
end
|
84
|
-
it "allows choosing domain" do
|
85
|
-
with_web_mock do
|
86
|
-
WebMock.stub_request(:post, 'http://impact.brighterplanet.com/flights.json').to_return(:status => 500, :body => 'used impact')
|
87
|
-
WebMock.stub_request(:post, 'http://foo.brighterplanet.com/flights.json').to_return(:status => 500, :body => 'used foo')
|
88
|
-
Carbon.query('Flight', :domain => 'http://foo.brighterplanet.com').errors.first.must_equal 'used foo'
|
89
|
-
Carbon.query('Flight').errors.first.must_equal 'used impact'
|
90
|
-
end
|
91
|
-
end
|
92
|
-
it "raises ArgumentError if args are bad" do
|
93
|
-
lambda {
|
94
|
-
Carbon.query(['Flight'])
|
95
|
-
}.must_raise ArgumentError
|
96
|
-
end
|
97
|
-
end
|
98
|
-
describe '(in parallel)' do
|
99
|
-
before do
|
100
|
-
flunk if ENV['SKIP_MULTI'] == 'true'
|
101
|
-
@queries = []
|
102
|
-
@queries << ['Flight', {:origin_airport => 'LAX', :destination_airport => 'SFO', :segments_per_trip => 1, :trips => 1}]
|
103
|
-
@queries << ['Flight', {:origin_airport => 'MSN', :destination_airport => 'ORD', :segments_per_trip => 1, :trips => 1}]
|
104
|
-
@queries << ['Flight', {:origin_airport => 'IAH', :destination_airport => 'DEN', :segments_per_trip => 1, :trips => 1}]
|
105
|
-
@queries << ['RailTrip', {:distance => 25}]
|
106
|
-
@queries << ['RailTrip', {:rail_class => 'commuter'}]
|
107
|
-
@queries << ['RailTrip', {:rail_traction => 'electric'}]
|
108
|
-
@queries << ['AutomobileTrip', {:make => 'Nissan', :model => 'Altima'}]
|
109
|
-
@queries << ['AutomobileTrip', {:make => 'Toyota', :model => 'Prius'}]
|
110
|
-
@queries << ['AutomobileTrip', {:make => 'Ford', :model => 'Taurus'}]
|
111
|
-
@queries << ['Residence', {:urbanity => 'City'}]
|
112
|
-
@queries << ['Residence', {:zip_code => '53703'}]
|
113
|
-
@queries << ['Residence', {:bathrooms => 4}]
|
114
|
-
@queries << ['Monkey', {:bananas => '1'}]
|
115
|
-
@queries << ['Monkey', {:bananas => '2'}]
|
116
|
-
@queries << ['Monkey', {:bananas => '3'}]
|
117
|
-
@queries = @queries.sort_by { rand }
|
118
|
-
end
|
119
|
-
it "is easy to use" do
|
120
|
-
flight = ['Flight']
|
121
|
-
rail_trip = ['RailTrip']
|
122
|
-
results = Carbon.query([flight, rail_trip])
|
123
|
-
results[flight].decisions.must_equal Carbon.query('Flight').decisions
|
124
|
-
results[rail_trip].decisions.must_equal Carbon.query('RailTrip').decisions
|
125
|
-
end
|
126
|
-
it "doesn't hang up on 0 queries" do
|
127
|
-
Timeout.timeout(0.5) { Carbon.query([]) }.must_equal(Hash.new)
|
128
|
-
end
|
129
|
-
it "raises if you pass it a block directly" do
|
130
|
-
lambda {
|
131
|
-
Carbon.query([]) { }
|
132
|
-
}.must_raise(ArgumentError)
|
133
|
-
end
|
134
|
-
it "can be used on objects that respond to #as_impact_query" do
|
135
|
-
a = MyNissanAltima.new(2001)
|
136
|
-
b = MyNissanAltima.new(2006)
|
137
|
-
ab1 = Carbon.query([a, b])
|
138
|
-
ab2 = Carbon.query([a.as_impact_query, b.as_impact_query])
|
139
|
-
ab1.each do |k, v|
|
140
|
-
ab2[k.as_impact_query].must_equal v
|
141
|
-
end
|
142
|
-
end
|
143
|
-
it "runs multiple queries at once" do
|
144
|
-
reference_results = @queries.inject({}) do |memo, query|
|
145
|
-
memo[query] = Carbon.query(*query)
|
146
|
-
memo
|
147
|
-
end
|
148
|
-
ts = []
|
149
|
-
3.times do
|
150
|
-
ts << Thread.new do
|
151
|
-
flush_cache! # important!
|
152
|
-
multi_results = Carbon.query(@queries)
|
153
|
-
error_count = 0
|
154
|
-
multi_results.each do |query, result|
|
155
|
-
if result.success
|
156
|
-
result.decisions.carbon.object.value.must_be :>, 0
|
157
|
-
result.decisions.carbon.object.value.must_be :<, 10_000
|
158
|
-
else
|
159
|
-
error_count += 1
|
160
|
-
end
|
161
|
-
end
|
162
|
-
error_count.must_equal 3
|
163
|
-
reference_results.each do |query, reference_result|
|
164
|
-
if reference_result.success
|
165
|
-
multi_results[query].decisions.must_equal reference_result.decisions
|
166
|
-
else
|
167
|
-
multi_results[query].must_equal reference_result
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
ts.each do |t|
|
173
|
-
t.join
|
174
|
-
end
|
175
|
-
end
|
176
|
-
it "is faster than single threaded" do
|
177
|
-
# warm up the cache on the other end
|
178
|
-
@queries.each { |query| Carbon.query(*query) }
|
179
|
-
flush_cache! # important!
|
180
|
-
single_threaded_time = ::Benchmark.realtime do
|
181
|
-
@queries.each { |query| Carbon.query(*query) }
|
182
|
-
end
|
183
|
-
flush_cache! # important!
|
184
|
-
multi_threaded_time = ::Benchmark.realtime do
|
185
|
-
Carbon.query(@queries)
|
186
|
-
end
|
187
|
-
cached_single_threaded_time = ::Benchmark.realtime do
|
188
|
-
@queries.each { |query| Carbon.query(*query) }
|
189
|
-
end
|
190
|
-
cached_multi_threaded_time = ::Benchmark.realtime do
|
191
|
-
Carbon.query(@queries)
|
192
|
-
end
|
193
|
-
multi_threaded_time.must_be :<, single_threaded_time
|
194
|
-
cached_single_threaded_time.must_be :<, multi_threaded_time
|
195
|
-
cached_multi_threaded_time.must_be :<, multi_threaded_time
|
196
|
-
$stderr.puts " Multi-threaded was #{((single_threaded_time - multi_threaded_time) / single_threaded_time * 100).round}% faster than single-threaded"
|
197
|
-
$stderr.puts " Cached single-threaded was #{((multi_threaded_time - cached_single_threaded_time) / multi_threaded_time * 100).round}% faster than uncached multi-threaded"
|
198
|
-
$stderr.puts " Cached multi-threaded was #{((multi_threaded_time - cached_multi_threaded_time) / multi_threaded_time * 100).round}% faster than uncached multi-threaded"
|
199
|
-
end
|
200
|
-
it "safely uniq's and caches queries" do
|
201
|
-
reference_results = @queries.inject({}) do |memo, query|
|
202
|
-
memo[query] = Carbon.query(*query)
|
203
|
-
memo
|
204
|
-
end
|
205
|
-
flush_cache! # important!
|
206
|
-
3.times do
|
207
|
-
multi_results = Carbon.query(@queries)
|
208
|
-
reference_results.each do |query, reference_result|
|
209
|
-
if reference_result.success
|
210
|
-
multi_results[query].decisions.must_equal reference_result.decisions
|
211
|
-
else
|
212
|
-
multi_results[query].must_equal reference_result
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
describe :method_signature do
|
221
|
-
it "recognizes emitter_param" do
|
222
|
-
Carbon.method_signature('Flight').must_equal :plain_query
|
223
|
-
Carbon.method_signature('Flight', :origin_airport => 'LAX').must_equal :plain_query
|
224
|
-
Carbon.method_signature(:flight).must_equal :plain_query
|
225
|
-
Carbon.method_signature(:flight, :origin_airport => 'LAX').must_equal :plain_query
|
226
|
-
end
|
227
|
-
it "recognizes obj" do
|
228
|
-
Carbon.method_signature(MyNissanAltima.new(2006)).must_equal :obj
|
229
|
-
end
|
230
|
-
it "recognizes array" do
|
231
|
-
Carbon.method_signature([MyNissanAltima.new(2001)]).must_equal :array
|
232
|
-
Carbon.method_signature([['Flight']]).must_equal :array
|
233
|
-
Carbon.method_signature([['Flight', {:origin_airport => 'LAX'}]]).must_equal :array
|
234
|
-
Carbon.method_signature([['Flight'], ['Flight']]).must_equal :array
|
235
|
-
Carbon.method_signature([['Flight', {:origin_airport => 'LAX'}], ['Flight', {:origin_airport => 'LAX'}]]).must_equal :array
|
236
|
-
[MyNissanAltima.new(2006), ['Flight'], ['Flight', {:origin_airport => 'LAX'}]].permutation.each do |p|
|
237
|
-
Carbon.method_signature(p).must_equal :array
|
238
|
-
end
|
239
|
-
end
|
240
|
-
it "does not want splats for concurrent queries" do
|
241
|
-
Carbon.method_signature(['Flight'], ['Flight']).must_be_nil
|
242
|
-
Carbon.method_signature(MyNissanAltima.new(2001), MyNissanAltima.new(2001)).must_be_nil
|
243
|
-
[MyNissanAltima.new(2006), ['Flight'], ['Flight', {:origin_airport => 'LAX'}]].permutation.each do |p|
|
244
|
-
Carbon.method_signature(*p).must_be_nil
|
245
|
-
end
|
246
|
-
end
|
247
|
-
it "does not like weirdness" do
|
248
|
-
Carbon.method_signature('Flight', 'Flight').must_be_nil
|
249
|
-
Carbon.method_signature('Flight', ['Flight']).must_be_nil
|
250
|
-
Carbon.method_signature(['Flight'], 'Flight').must_be_nil
|
251
|
-
Carbon.method_signature(['Flight', 'Flight']).must_be_nil
|
252
|
-
Carbon.method_signature(['Flight', ['Flight']]).must_be_nil
|
253
|
-
Carbon.method_signature([['Flight'], 'Flight']).must_be_nil
|
254
|
-
Carbon.method_signature(MyNissanAltima.new(2001), [MyNissanAltima.new(2001)]).must_be_nil
|
255
|
-
Carbon.method_signature([MyNissanAltima.new(2001)], MyNissanAltima.new(2001)).must_be_nil
|
256
|
-
Carbon.method_signature([MyNissanAltima.new(2001)], [MyNissanAltima.new(2001)]).must_be_nil
|
257
|
-
Carbon.method_signature([MyNissanAltima.new(2001), [MyNissanAltima.new(2001)]]).must_be_nil
|
258
|
-
Carbon.method_signature([[MyNissanAltima.new(2001)], MyNissanAltima.new(2001)]).must_be_nil
|
259
|
-
Carbon.method_signature([[MyNissanAltima.new(2001)], [MyNissanAltima.new(2001)]]).must_be_nil
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
describe "mixin" do
|
264
|
-
describe :emit_as do
|
265
|
-
it "overwrites old emit_as blocks" do
|
266
|
-
eval %{class MyFoo; include Carbon; end}
|
267
|
-
MyFoo.emit_as('Automobile') { provide(:make) }
|
268
|
-
Carbon::Registry.instance['MyFoo'].characteristics.keys.must_equal [:make]
|
269
|
-
MyFoo.emit_as('Automobile') { provide(:model) }
|
270
|
-
Carbon::Registry.instance['MyFoo'].characteristics.keys.must_equal [:model]
|
271
|
-
end
|
272
|
-
end
|
273
|
-
describe '#as_impact_query' do
|
274
|
-
it "sets up an query to be run by Carbon.query" do
|
275
|
-
a = MyNissanAltima.new(2006)
|
276
|
-
a.as_impact_query.must_equal ["Automobile", {:make=>"Nissan", :model=>"Altima", :year=>2006, "automobile_fuel[code]"=>"R"}]
|
277
|
-
end
|
278
|
-
it "only includes non-nil params" do
|
279
|
-
a = MyNissanAltima.new(2006)
|
280
|
-
a.as_impact_query[1].keys.must_include :year
|
281
|
-
a.as_impact_query[1].keys.wont_include :nil_model
|
282
|
-
a.as_impact_query[1].keys.wont_include :nil_make
|
283
|
-
end
|
284
|
-
end
|
285
|
-
describe '#impact' do
|
286
|
-
it "works" do
|
287
|
-
impact = MyNissanAltima.new(2006).impact
|
288
|
-
impact.decisions.carbon.object.value.must_be :>, 0
|
289
|
-
impact.characteristics.make.description.must_match %r{Nissan}i
|
290
|
-
impact.characteristics.model.description.must_match %r{Altima}i
|
291
|
-
impact.characteristics.year.description.to_i.must_equal 2006
|
292
|
-
impact.characteristics.automobile_fuel.description.must_match %r{regular gasoline}
|
293
|
-
end
|
294
|
-
it "takes timeframe" do
|
295
|
-
impact_2010 = MyNissanAltima.new(2006).impact(:timeframe => Timeframe.new(:year => 2010))
|
296
|
-
impact_2011 = MyNissanAltima.new(2006).impact(:timeframe => Timeframe.new(:year => 2011))
|
297
|
-
impact_2010.timeframe.startDate.must_equal '2010-01-01'
|
298
|
-
impact_2011.timeframe.startDate.must_equal '2011-01-01'
|
299
|
-
end
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
data/test/helper.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'benchmark'
|
4
|
-
require 'webmock/minitest'
|
5
|
-
WebMock.disable!
|
6
|
-
def with_web_mock
|
7
|
-
WebMock.enable!
|
8
|
-
WebMock.disable_net_connect!
|
9
|
-
yield
|
10
|
-
ensure
|
11
|
-
WebMock.disable!
|
12
|
-
end
|
13
|
-
require 'minitest/spec'
|
14
|
-
require 'minitest/autorun'
|
15
|
-
require 'minitest/reporters'
|
16
|
-
MiniTest::Unit.runner = MiniTest::SuiteRunner.new
|
17
|
-
MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
|
18
|
-
require 'timeframe'
|
19
|
-
require 'carbon'
|
20
|
-
|
21
|
-
class MiniTest::Spec
|
22
|
-
def flush_cache!
|
23
|
-
CacheMethod.config.storage.flush
|
24
|
-
end
|
25
|
-
end
|