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.
Files changed (39) hide show
  1. data/.gitignore +4 -22
  2. data/CHANGELOG +11 -0
  3. data/Gemfile +10 -1
  4. data/README.markdown +185 -0
  5. data/Rakefile +13 -26
  6. data/bin/carbon +3 -3
  7. data/carbon.gemspec +17 -23
  8. data/developer_notes/MULTI.markdown +25 -0
  9. data/developer_notes/REDUCE_HTTP_CONNECTIONS.markdown +46 -0
  10. data/features/shell.feature +1 -1
  11. data/features/support/env.rb +3 -4
  12. data/lib/carbon.rb +242 -96
  13. data/lib/carbon/registry.rb +50 -0
  14. data/lib/carbon/shell.rb +14 -8
  15. data/lib/carbon/shell/emitter.rb +33 -29
  16. data/lib/carbon/version.rb +1 -1
  17. data/test/carbon_test.rb +167 -0
  18. metadata +128 -182
  19. data/MIT-LICENSE.txt +0 -19
  20. data/README.rdoc +0 -266
  21. data/doc/INTEGRATION_GUIDE.rdoc +0 -1002
  22. data/doc/examining-response-with-jsonview.png +0 -0
  23. data/doc/shell_example +0 -43
  24. data/doc/timeout-error.png +0 -0
  25. data/doc/with-committee-reports.png +0 -0
  26. data/doc/without-committee-reports.png +0 -0
  27. data/lib/carbon/base.rb +0 -62
  28. data/lib/carbon/emission_estimate.rb +0 -165
  29. data/lib/carbon/emission_estimate/request.rb +0 -100
  30. data/lib/carbon/emission_estimate/response.rb +0 -61
  31. data/lib/carbon/emission_estimate/storage.rb +0 -33
  32. data/spec/fixtures/vcr_cassettes/flight.yml +0 -47
  33. data/spec/fixtures/vcr_cassettes/residence.yml +0 -44
  34. data/spec/lib/carbon/emission_estimate/request_spec.rb +0 -41
  35. data/spec/lib/carbon/emission_estimate/response_spec.rb +0 -33
  36. data/spec/lib/carbon/emission_estimate_spec.rb +0 -32
  37. data/spec/lib/carbon_spec.rb +0 -384
  38. data/spec/spec_helper.rb +0 -60
  39. 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
- *.gem
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
@@ -1,3 +1,12 @@
1
1
  source :rubygems
2
2
 
3
- gemspec :path => '.'
3
+ gemspec
4
+
5
+ # dev deps
6
+ gem 'minitest'
7
+ gem 'minitest-reporters'
8
+ gem 'timeframe'
9
+ gem 'webmock'
10
+ gem 'aruba'
11
+ gem 'cucumber'
12
+ gem 'yard'
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
- require 'bundler'
2
- Bundler::GemHelper.install_tasks
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/rdoctask'
23
- Rake::RDocTask.new do |rdoc|
24
- rdoc.rdoc_dir = 'rdoc'
25
- rdoc.title = 'carbon'
26
- rdoc.options << '--line-numbers' << '--inline-source'
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
- require 'carbon'
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
- $:.unshift File.expand_path("../lib", __FILE__)
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 = "carbon"
5
+ s.name = 'carbon'
7
6
  s.version = Carbon::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ['Derek Kastner', 'Seamus Abshere', 'Andy Rossmeissl']
10
- s.email = ['derek.kastner@brighterplanet.com']
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.add_dependency 'activesupport', '>=2.3.5'
21
- s.add_dependency 'i18n' # activesupport?
22
- s.add_dependency 'nap'
23
- s.add_dependency 'timeframe'
24
- s.add_dependency 'blockenspiel'
25
- s.add_dependency 'conversions'
26
- s.add_dependency 'brighter_planet_metadata'
27
- s.add_dependency 'bombshell'
28
- s.add_development_dependency 'fakeweb'
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
@@ -169,7 +169,7 @@ Feature: Shell
169
169
  And I type "exit"
170
170
  Then the output should contain:
171
171
  """
172
- http://carbon.brighterplanet.com/computations.json?duration=10
172
+ http://impact.brighterplanet.com/computations?duration=10
173
173
  """
174
174
 
175
175
  Scenario: Retrieving stored emitter
@@ -1,9 +1,8 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
1
  require 'aruba/cucumber'
3
2
  require 'fileutils'
4
- require 'rspec/expectations'
5
- require 'carbon'
6
- require 'conversions'
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