carbon 1.1.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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