carbon 1.1.3 → 2.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/.gitignore +4 -22
- data/CHANGELOG +11 -0
- data/Gemfile +10 -1
- data/README.markdown +185 -0
- data/Rakefile +13 -26
- data/bin/carbon +3 -3
- data/carbon.gemspec +17 -23
- data/developer_notes/MULTI.markdown +25 -0
- data/developer_notes/REDUCE_HTTP_CONNECTIONS.markdown +46 -0
- data/features/shell.feature +1 -1
- data/features/support/env.rb +3 -4
- data/lib/carbon.rb +242 -96
- data/lib/carbon/registry.rb +50 -0
- data/lib/carbon/shell.rb +14 -8
- data/lib/carbon/shell/emitter.rb +33 -29
- data/lib/carbon/version.rb +1 -1
- data/test/carbon_test.rb +167 -0
- metadata +128 -182
- data/MIT-LICENSE.txt +0 -19
- data/README.rdoc +0 -266
- data/doc/INTEGRATION_GUIDE.rdoc +0 -1002
- data/doc/examining-response-with-jsonview.png +0 -0
- data/doc/shell_example +0 -43
- data/doc/timeout-error.png +0 -0
- data/doc/with-committee-reports.png +0 -0
- data/doc/without-committee-reports.png +0 -0
- data/lib/carbon/base.rb +0 -62
- data/lib/carbon/emission_estimate.rb +0 -165
- data/lib/carbon/emission_estimate/request.rb +0 -100
- data/lib/carbon/emission_estimate/response.rb +0 -61
- data/lib/carbon/emission_estimate/storage.rb +0 -33
- data/spec/fixtures/vcr_cassettes/flight.yml +0 -47
- data/spec/fixtures/vcr_cassettes/residence.yml +0 -44
- data/spec/lib/carbon/emission_estimate/request_spec.rb +0 -41
- data/spec/lib/carbon/emission_estimate/response_spec.rb +0 -33
- data/spec/lib/carbon/emission_estimate_spec.rb +0 -32
- data/spec/lib/carbon_spec.rb +0 -384
- data/spec/spec_helper.rb +0 -60
- data/spec/specwatchr +0 -60
data/.gitignore
CHANGED
@@ -1,23 +1,5 @@
|
|
1
|
-
## MAC OS
|
2
|
-
.DS_Store
|
3
|
-
|
4
|
-
## TEXTMATE
|
5
|
-
*.tmproj
|
6
|
-
tmtags
|
7
|
-
|
8
|
-
## EMACS
|
9
|
-
*~
|
10
|
-
\#*
|
11
|
-
.\#*
|
12
|
-
|
13
|
-
## VIM
|
14
|
-
*.swp
|
15
|
-
|
16
|
-
## PROJECT::GENERAL
|
17
|
-
coverage
|
18
|
-
rdoc
|
19
|
-
pkg
|
20
|
-
|
21
|
-
## PROJECT::SPECIFIC
|
22
1
|
Gemfile.lock
|
23
|
-
|
2
|
+
.DS_Store
|
3
|
+
.rvmrc
|
4
|
+
.yardoc/
|
5
|
+
doc/
|
data/CHANGELOG
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
2.0.0 / 2012-03-09
|
2
|
+
|
3
|
+
* Breaking changes
|
4
|
+
|
5
|
+
* #emission_estimate has been removed in favor of #impact
|
6
|
+
* Response structure now mirrors what you get from http://impact.brighterplanet.com
|
7
|
+
|
8
|
+
* Enhancements
|
9
|
+
|
10
|
+
* Carbon.multi method for parallelizing requests
|
11
|
+
* Tested with MRI 1.8 and MRI 1.9
|
data/Gemfile
CHANGED
data/README.markdown
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
# Carbon
|
2
|
+
|
3
|
+
Carbon is a Ruby API client and command-line console for the [Brighter Planet impact estimate web service](http://impact.brighterplanet.com), which is located at http://impact.brighterplanet.com. By querying the web service, it can estimate the carbon emissions, energy usage, and other environmental impacts of many real-life objects, such as cars and houses, based on particular characteristics that they may have.
|
4
|
+
|
5
|
+
Full documentation: [RDoc](http://rdoc.info/projects/brighterplanet/carbon)
|
6
|
+
|
7
|
+
## Quick start 1: experimenting with the console
|
8
|
+
|
9
|
+
<b>You'll need a Brighter Planet API key. See the "API keys" section below for details.</b>
|
10
|
+
|
11
|
+
First get the gem:
|
12
|
+
|
13
|
+
$ gem install carbon
|
14
|
+
|
15
|
+
Then start the console:
|
16
|
+
|
17
|
+
$ carbon
|
18
|
+
carbon->
|
19
|
+
|
20
|
+
Provide your key:
|
21
|
+
|
22
|
+
carbon-> key '123ABC'
|
23
|
+
=> Using key 123ABC
|
24
|
+
|
25
|
+
Start a flight calculation:
|
26
|
+
|
27
|
+
carbon-> flight
|
28
|
+
=> 1210.66889895298 kg CO2e
|
29
|
+
flight*>
|
30
|
+
|
31
|
+
Start providing characteristics:
|
32
|
+
|
33
|
+
flight*> origin_airport 'jfk'
|
34
|
+
=> 1593.46008200024 kg CO2e
|
35
|
+
flight*> destination_airport 'lax'
|
36
|
+
=> 1766.55536727522 kg CO2e
|
37
|
+
|
38
|
+
Review what you've entered:
|
39
|
+
|
40
|
+
flight*> characteristics
|
41
|
+
=> origin_airport: jfk
|
42
|
+
destination_airport: lax
|
43
|
+
|
44
|
+
See how the calculation's being made:
|
45
|
+
|
46
|
+
flight*> methodology
|
47
|
+
=> emission: from fuel and passengers with coefficients
|
48
|
+
[ ... ]
|
49
|
+
cohort: from t100
|
50
|
+
|
51
|
+
See intermediate calculations:
|
52
|
+
|
53
|
+
flight*> reports
|
54
|
+
=> emission: 1766.55536727522
|
55
|
+
[ ... ]
|
56
|
+
cohort: {"members"=>262}
|
57
|
+
|
58
|
+
Generate a methodology URL:
|
59
|
+
|
60
|
+
flight*> url
|
61
|
+
=> http://impact.brighterplanet.com/flights.json?origin_airport=jfk&destination_airport=lax&key=123ABC
|
62
|
+
|
63
|
+
And when you're done:
|
64
|
+
|
65
|
+
flight*> done
|
66
|
+
=> Saved as flight #0
|
67
|
+
carbon->
|
68
|
+
|
69
|
+
You can recall this flight anytime during this same session:
|
70
|
+
|
71
|
+
carbon-> flight 0
|
72
|
+
=> 1766.55536727522 kg CO2e
|
73
|
+
flight*> characteristics
|
74
|
+
=> origin_airport: jfk
|
75
|
+
destination_airport: lax
|
76
|
+
|
77
|
+
For more, see the "Console" section below.
|
78
|
+
|
79
|
+
## Quick start 2: using the library in your application
|
80
|
+
|
81
|
+
<b>You'll need a Brighter Planet API key. See the "API keys" section below for details.</b>
|
82
|
+
|
83
|
+
Carbon works by extending any Ruby class to be an emission source. You `include Carbon` and then use the `emit_as` DSL...
|
84
|
+
|
85
|
+
{render:Carbon::ClassMethods#emit_as}
|
86
|
+
|
87
|
+
The final URL will be something like
|
88
|
+
|
89
|
+
http://impact.brighterplanet.com/flights.json?segments_per_trip=1&trips=1&origin_airport[iata_code]=MSN&destination_airport[iata_code]=ORD&airline[iata_code]=UA&aircraft[icao_code]=B737
|
90
|
+
|
91
|
+
When you want to calculate impacts, simply call `MyFlight#impact`.
|
92
|
+
|
93
|
+
{render:Carbon#impact}
|
94
|
+
|
95
|
+
## API keys
|
96
|
+
|
97
|
+
You should get an API key from http://keys.brighterplanet.com and set it globally:
|
98
|
+
|
99
|
+
Carbon.key = '12903019230128310293'
|
100
|
+
|
101
|
+
Now all of your queries will use that key.
|
102
|
+
|
103
|
+
## Gotcha: timeframes and 0.0kg results
|
104
|
+
|
105
|
+
You submit this query about a flight in 2009, but the result is 0.0 kilograms. Why?
|
106
|
+
|
107
|
+
$ carbon
|
108
|
+
carbon-> flight
|
109
|
+
[...]
|
110
|
+
flight*> date '2009-05-03'
|
111
|
+
=> 0.0 kg CO2e
|
112
|
+
flight*> url
|
113
|
+
=> http://impact.brighterplanet.com/flights?date=2009-05-03
|
114
|
+
|
115
|
+
It's telling you that a flight in 2009 did not result in any 2011 emissions (the default timeframe is the current year).
|
116
|
+
|
117
|
+
flight*> timeframe '2009'
|
118
|
+
=> 847.542137647608 kg CO2e
|
119
|
+
flight*> url
|
120
|
+
=> http://impact.brighterplanet.com/flights?date=2009-05-03&timeframe=2009-01-01/2010-01-01
|
121
|
+
|
122
|
+
So, 850 kilograms emitted in 2009.
|
123
|
+
|
124
|
+
## Console
|
125
|
+
|
126
|
+
This library includes a special console for performing calculations interactively. Quick Start #1 provides an example session. Here is a command reference:
|
127
|
+
|
128
|
+
### Shell mode
|
129
|
+
|
130
|
+
`help`
|
131
|
+
: Displays a list of emitter types.
|
132
|
+
|
133
|
+
`key` _yourkey_
|
134
|
+
: Set the [developer key](http://keys.brighterplanet.com) that should be used for this session. Alternatively, put this key in `~/.brighter_planet` and it will be auto-selected on console startup.
|
135
|
+
|
136
|
+
_emitter_
|
137
|
+
: (e.g. `Flight`) Enters emitter mode using this emitter type.
|
138
|
+
|
139
|
+
_emitter num_
|
140
|
+
: (e.g. `Flight 0`) Recalls a previous emitter from this session.
|
141
|
+
|
142
|
+
`exit`
|
143
|
+
: Quits.
|
144
|
+
|
145
|
+
### Emitter mode
|
146
|
+
|
147
|
+
In Emitter mode, the prompt displays the emitter type in use. If a timeframe has been set, the timeframe is also included in the prompt.
|
148
|
+
|
149
|
+
`help`
|
150
|
+
: Displays a list of characteristics for this emitter type.
|
151
|
+
|
152
|
+
_characteristic value_
|
153
|
+
: (e.g. `origin_airport 'lax'`)
|
154
|
+
: Provide a characteristic. Remember, this is Ruby we're dealing with, so strings must be quoted.
|
155
|
+
|
156
|
+
`timeframe`
|
157
|
+
: Display the current timeframe in effect on the emission estimate.
|
158
|
+
|
159
|
+
`timeframe` _timeframe_
|
160
|
+
: (e.g. `timeframe '2009-01-01/2010-01-01'` or just `timeframe '2009'`) Set a timeframe on the emission estimate.
|
161
|
+
|
162
|
+
`emission`
|
163
|
+
: Displays the current emission in kilograms CO2e for this emitter.
|
164
|
+
|
165
|
+
`lbs`, `pounds`, or `tons`
|
166
|
+
: Display the emission using different units.
|
167
|
+
|
168
|
+
`characteristics`
|
169
|
+
: Lists the characteristics you have provided so far.
|
170
|
+
|
171
|
+
`methodology`
|
172
|
+
: Summarizes how the calculation is being made.
|
173
|
+
|
174
|
+
`reports`
|
175
|
+
: Displays intermediate calculations that were made in pursuit of the emission estimate.
|
176
|
+
|
177
|
+
`url`
|
178
|
+
: Generates a methodology URL suitable for pasting into your browser for further inspection.
|
179
|
+
|
180
|
+
`done`
|
181
|
+
: Saves this emitter and returns to shell mode.
|
182
|
+
|
183
|
+
## Copyright
|
184
|
+
|
185
|
+
Copyright (c) 2012 Brighter Planet.
|
data/Rakefile
CHANGED
@@ -1,33 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
|
6
|
-
desc "Run all examples"
|
7
|
-
RSpec::Core::RakeTask.new('examples') do |c|
|
8
|
-
c.rspec_opts = '-Ispec'
|
9
|
-
end
|
10
|
-
|
11
|
-
task :test => [:examples, :cucumber]
|
12
|
-
task :default => :test
|
13
|
-
|
14
|
-
desc "Run specs with RCov"
|
15
|
-
RSpec::Core::RakeTask.new(:examples_with_coverage) do |t|
|
16
|
-
t.rcov = true
|
17
|
-
t.rcov_opts = ['--exclude', 'spec']
|
18
|
-
t.rspec_opts = '-Ispec'
|
19
|
-
end
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
20
3
|
|
21
4
|
require 'rake'
|
22
|
-
require 'rake/
|
23
|
-
Rake::
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
rdoc.rdoc_files.include('README*')
|
28
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:test) do |test|
|
7
|
+
test.libs << 'lib'
|
8
|
+
test.pattern = 'test/**/*_test.rb'
|
9
|
+
test.verbose = true
|
29
10
|
end
|
30
11
|
|
31
12
|
require 'cucumber/rake/task'
|
32
13
|
Cucumber::Rake::Task.new
|
33
14
|
|
15
|
+
require 'yard'
|
16
|
+
YARD::Rake::YardocTask.new do |y|
|
17
|
+
y.options << '--no-private' << '--title' << "Brighter Planet CM1 client for Ruby"
|
18
|
+
end
|
19
|
+
|
20
|
+
task :default => [:test, :cucumber]
|
data/bin/carbon
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
$:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
|
3
|
-
|
4
2
|
require 'rubygems'
|
5
|
-
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) unless $LOAD_PATH.include?(File.expand_path('../../lib', __FILE__))
|
6
5
|
require 'carbon/shell'
|
6
|
+
|
7
7
|
Bombshell.launch(Carbon::Shell)
|
data/carbon.gemspec
CHANGED
@@ -1,33 +1,27 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require "carbon/version"
|
2
|
+
require File.expand_path('../lib/carbon/version', __FILE__)
|
4
3
|
|
5
4
|
Gem::Specification.new do |s|
|
6
|
-
s.name =
|
5
|
+
s.name = 'carbon'
|
7
6
|
s.version = Carbon::VERSION
|
8
|
-
s.
|
9
|
-
s.
|
10
|
-
s.
|
7
|
+
s.author = 'Seamus Abshere'
|
8
|
+
s.email = ['seamus@abshere.net', 'dkastner@gmail.com', 'andy@rossmeissl.net']
|
9
|
+
s.summary = 'Brighter Planet API client for Ruby'
|
10
|
+
s.description = 'Brighter Planet API client for Ruby'
|
11
11
|
s.homepage = 'https://github.com/brighterplanet/carbon'
|
12
|
-
s.summary = %q{Carbon is a Ruby API wrapper for the Brighter Planet emission estimate web service (http://carbon.brighterplanet.com).}
|
13
|
-
s.description = %q{Carbon is a Ruby API wrapper for the Brighter Planet emission estimate web service (http://carbon.brighterplanet.com). By querying the web service, it can estimate the carbon emissions of many real-life objects, such as cars and houses, based on particular attributes that they may have.}
|
14
12
|
|
13
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
14
|
s.files = `git ls-files`.split("\n")
|
16
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
16
|
s.require_paths = ["lib"]
|
19
|
-
|
20
|
-
s.
|
21
|
-
s.
|
22
|
-
s.
|
23
|
-
s.
|
24
|
-
|
25
|
-
|
26
|
-
s.
|
27
|
-
s.
|
28
|
-
s.
|
29
|
-
s.add_development_dependency 'rspec'
|
30
|
-
s.add_development_dependency 'aruba'
|
31
|
-
s.add_development_dependency 'rake'
|
32
|
-
s.add_development_dependency 'vcr'
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'em-http-request'
|
19
|
+
s.add_runtime_dependency 'activesupport'
|
20
|
+
s.add_runtime_dependency 'multi_json'
|
21
|
+
s.add_runtime_dependency 'hashie'
|
22
|
+
|
23
|
+
# CLI
|
24
|
+
s.add_runtime_dependency 'bombshell'
|
25
|
+
s.add_runtime_dependency 'conversions'
|
26
|
+
s.add_runtime_dependency 'brighter_planet_metadata'
|
33
27
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Had this for a while
|
2
|
+
|
3
|
+
def self.impacts(enumerable)
|
4
|
+
queries = enumerable.map do |instance|
|
5
|
+
[ Registry.instance[instance.class.name].emitter, instance.impact_params ]
|
6
|
+
end
|
7
|
+
multi queries
|
8
|
+
end
|
9
|
+
|
10
|
+
Tested like this
|
11
|
+
|
12
|
+
describe :impacts do
|
13
|
+
it "works" do
|
14
|
+
impacts = Carbon.impacts(MyNissanAltima.all(:order => :year))
|
15
|
+
impacts.length.must_equal 5
|
16
|
+
impacts.map do |impact|
|
17
|
+
impact.decisions.carbon.object.value.round
|
18
|
+
end.uniq.length.must_be :>, 3
|
19
|
+
impacts.each_with_index do |impact, idx|
|
20
|
+
impact.decisions.carbon.object.value.must_be :>, 0
|
21
|
+
impact.characteristics.make.description.must_match %r{Nissan}i
|
22
|
+
impact.characteristics.year.description.to_i.must_equal(2000+idx)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
One way to reduce the number of connections by a constant... but it makes it slower (because requests are serialized) and less reliable (because there is a 30s heroku limit)
|
2
|
+
|
3
|
+
def self.multi(queries)
|
4
|
+
unsorted = {}
|
5
|
+
pool_size = (queries.length.to_f / 3).ceil
|
6
|
+
$stderr.puts "Starting #{pool_size} workers"
|
7
|
+
::EventMachine.run do
|
8
|
+
multi = ::EventMachine::MultiRequest.new
|
9
|
+
pool = 0.upto(pool_size).map do
|
10
|
+
::EventMachine::HttpRequest.new("http://#{domain}")
|
11
|
+
end
|
12
|
+
pool_idx = 0
|
13
|
+
queries.each_with_index do |(emitter, params), query_idx|
|
14
|
+
params ||= {}
|
15
|
+
multi.add query_idx, pool[pool_idx].post(:path => "/#{emitter.underscore.pluralize}.json", :body => params, :keepalive => true)
|
16
|
+
pool_idx = (pool_idx + 1) % pool_size
|
17
|
+
end
|
18
|
+
multi.callback do
|
19
|
+
multi.responses[:callback].each do |query_idx, http|
|
20
|
+
response = ::Hashie::Mash.new
|
21
|
+
response.status = http.response_header.status
|
22
|
+
if (200..299).include?(response.status)
|
23
|
+
response.success = true
|
24
|
+
response.merge! ::MultiJson.decode(http.response)
|
25
|
+
else
|
26
|
+
response.success = false
|
27
|
+
response.errors = [http.response]
|
28
|
+
end
|
29
|
+
unsorted[query_idx] = response
|
30
|
+
end
|
31
|
+
multi.responses[:errback].each do |query_idx, http|
|
32
|
+
response = ::Hashie::Mash.new
|
33
|
+
response.status = http.response_header.status
|
34
|
+
response.success = false
|
35
|
+
response.errors = ['Timeout or other network error.']
|
36
|
+
unsorted[query_idx] = response
|
37
|
+
end
|
38
|
+
::EventMachine.stop
|
39
|
+
end
|
40
|
+
end
|
41
|
+
unsorted.sort_by do |query_idx, _|
|
42
|
+
query_idx
|
43
|
+
end.map do |_, response|
|
44
|
+
response
|
45
|
+
end
|
46
|
+
end
|
data/features/shell.feature
CHANGED
@@ -169,7 +169,7 @@ Feature: Shell
|
|
169
169
|
And I type "exit"
|
170
170
|
Then the output should contain:
|
171
171
|
"""
|
172
|
-
http://
|
172
|
+
http://impact.brighterplanet.com/computations?duration=10
|
173
173
|
"""
|
174
174
|
|
175
175
|
Scenario: Retrieving stored emitter
|
data/features/support/env.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
1
|
require 'aruba/cucumber'
|
3
2
|
require 'fileutils'
|
4
|
-
|
5
|
-
|
6
|
-
require '
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
|
5
|
+
require 'carbon/shell'
|
7
6
|
|
8
7
|
Before do
|
9
8
|
@aruba_io_wait_seconds = 2
|