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/MIT-LICENSE.txt DELETED
@@ -1,19 +0,0 @@
1
- Copyright (c) 2011 Brighter Planet, Inc.
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy
4
- of this software and associated documentation files (the "Software"), to deal
5
- in the Software without restriction, including without limitation the rights
6
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- copies of the Software, and to permit persons to whom the Software is
8
- furnished to do so, subject to the following conditions:
9
-
10
- The above copyright notice and this permission notice shall be included in
11
- all copies or substantial portions of the Software.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- THE SOFTWARE.
data/README.rdoc DELETED
@@ -1,266 +0,0 @@
1
- = Carbon
2
-
3
- Carbon is a Ruby API wrapper and command-line console for the {Brighter Planet emission estimate web service}[http://carbon.brighterplanet.com], which is located at 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.
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://carbon.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
- First get the gem:
84
-
85
- $ gem install carbon
86
-
87
- Carbon works by extending any Ruby class you're using to represent an emission source. For instance, let's say you have a Ruby class <tt>RentalCar</tt> that represents a rental car on your lot. (Note that ActiveRecord models work great with this gem.)
88
-
89
- class RentalCar
90
- attr_accessor :model, :model_year, :fuel_economy
91
- class Make
92
- attr_accessor :name
93
- def to_param
94
- name
95
- end
96
- end
97
- def make
98
- @make ||= Make.new
99
- end
100
- end
101
-
102
- In order to calculate carbon emissions, we need to map the car's relevant attributes to characteristics that the {web service}[http://carbon.brighterplanet.com] will recognize. In this case, a review of the available characteristics for Automobile[http://carbon.brighterplanet.com/automobiles/options] yields the following map:
103
-
104
- class RentalCar
105
- # [...]
106
- include Carbon
107
- emit_as :automobile do
108
- provide :make
109
- provide :model
110
- provide :model_year
111
- provide :fuel_economy, :as => :fuel_efficiency
112
- end
113
- end
114
-
115
- When you want to calculate emissions, simply call <tt>RentalCar#emission_estimate</tt>.
116
-
117
- > my_car = RentalCar.new([...])
118
- => #<RentalCar [...]>
119
- > my_emission = my_car.emission_estimate
120
- => #<Carbon::EmissionEstimate [...]>
121
- > my_emission.to_f
122
- => 4919.2
123
- > my_emission.emission_units
124
- => "kilograms"
125
- > my_emission.methodology
126
- => "http://carbon.brighterplanet.com/automobiles.html?[...]"
127
-
128
- == API keys
129
-
130
- You should get an API key from http://keys.brighterplanet.com and set it globally:
131
-
132
- Carbon.key = '12903019230128310293'
133
-
134
- Now all of your queries will use that key.
135
-
136
- == Timeframes and 0.0kg results
137
-
138
- You submit this query about a flight in 2009, but the result is 0.0 kilograms. Why?
139
-
140
- $ carbon
141
- carbon-> flight
142
- [...]
143
- flight*> date '2009-05-03'
144
- => 0.0 kg CO2e
145
- flight*> url
146
- => http://carbon.brighterplanet.com/flights.json?date=2009-05-03
147
-
148
- It's telling you that a flight in 2009 did not result in any 2011 emissions (the default timeframe is the current year).
149
-
150
- flight*> timeframe '2009'
151
- => 847.542137647608 kg CO2e
152
- flight*> url
153
- => http://carbon.brighterplanet.com/flights.json?date=2009-05-03&timeframe=2009-01-01/2010-01-01
154
-
155
- So, 850 kilograms emitted in 2009.
156
-
157
- == Association serialization
158
-
159
- Your objects' attributes are serialized via <tt>#to_characteristic</tt> or <tt>#to_param</tt> (in that order of preference) before being submitted to the web service.
160
-
161
- For example:
162
-
163
- class RentalCar < ActiveRecord::Base
164
- belongs_to :automobile_make
165
- emit_as :automobile do
166
- provide :automobile_make, :as => :make
167
- end
168
- end
169
-
170
- class AutomobileMake < ActiveRecord::Base # schema includes :name
171
- has_many :rental_cars
172
- alias :to_characteristic :name
173
- end
174
-
175
- Without <tt>AutomobileMake#to_characteristic</tt>, the library would have no way of knowing how to serialize.
176
-
177
- == Using timeouts
178
-
179
- > RentalCar.new.emission_estimate :timeout => 100
180
-
181
- Runs a realtime request but allows timing out the network call. Raises <tt>Timeout::Error</tt> if timeout is exceeded.
182
-
183
- == Certified calculations
184
-
185
- You can run {certified calculations}[http://brighterplanet.com/certified] by setting <tt>certified</tt> to true.
186
-
187
- > RentalCar.new.emission_estimate :certified => true
188
-
189
- == Persisted queries
190
-
191
- You can specify that the result be persisted in low-latency storage so that future identical requests can use the same estimate:
192
-
193
- > RentalCar.new.emission_estimate :guid => 'A_GLOBALLY_UNIQUE_IDENTIFIER_FOR_THIS_EMISSION_ESTIMATE'
194
-
195
- == Asynchronous queries
196
-
197
- === Using a callback
198
-
199
- To specify that the result of a query should be POSTed back to you, simply pass an URL as the <tt>:callback</tt> option to <tt>#emission_estimate</tt>:
200
-
201
- > RentalCar.new.emission_estimate :callback => 'http://example.com/my/callback/handler'
202
-
203
- A good way to test this is to set up a {PostBin}[http://postbin.org].
204
-
205
- === Using polling
206
-
207
- By combining <tt>:guid</tt> and <tt>:defer => true</tt>, you can poll for a result:
208
-
209
- > batmobile.emission_estimate :guid => '[...]', :defer => true
210
- # Do other things . . . and then:
211
- > batmobile.emission_estimate :guid => '[...]'
212
-
213
- If the result isn't available by the time you want it, a standard synchronous estimate will be provided.
214
-
215
- == Exceptions
216
-
217
- Since this gem connects to a web service, you need to be ready for network problems and latency. For example:
218
-
219
- begin
220
- my_emission = my_car.emission_estimate
221
- rescue ::SocketError, ::EOFError, ::Timeout::Error, ::Errno::ETIMEDOUT, ::Errno::ENETUNREACH, ::Errno::ECONNRESET, ::Errno::ECONNREFUSED
222
- # These are general network errors raised by Net::HTTP.
223
- # Your internet connection might be down.
224
- rescue ::Carbon::RateLimited
225
- # In order to prevent denial-of-service attacks, our servers limit request rates.
226
- # The gem will try up to three times to get an answer back from the server, waiting slightly longer each time.
227
- # If you still get this exception, please contact us at staff@brighterplanet.com and we'll lift your rate.
228
- rescue ::Carbon::RealtimeEstimateFailed
229
- # Our server returned a 4XX or 5XX error.
230
- # Please contact us at staff@brighterplanet.com.
231
- rescue ::Carbon::QueueingFailed
232
- # The gem connects directly to Amazon SQS in order to provide maximum throughput. If that service returns anything other than success, you get this exception.
233
- # Please contact us at staff@brighterplanet.com.
234
- end
235
-
236
- == Console
237
-
238
- This library includes a special console for performing calculations interactively. Quick Start #1 provides an example session. Here is a command reference:
239
-
240
- === Shell mode
241
-
242
- [+help+] Displays a list of emitter types.
243
- [+key+ _yourkey_] Set the {developer key}[http://keys.brighterplanet.com] that should be used for this session. Alternatively, put this key in <tt>~/.brighter_planet</tt> and it will be auto-selected on console startup.
244
- [+_emitter_type_+] (e.g. +flight+) Enters emitter mode using this emitter type.
245
- [<tt><i>emitter_type num</i></tt>] (e.g. <tt>flight 0</tt>) Recalls a previous emitter from this session.
246
- [+exit+] Quits.
247
-
248
- === Emitter mode
249
-
250
- 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.
251
-
252
- [+help+] Displays a list of characteristics for this emitter type.
253
- [<tt><i>characteristic value</i></tt>] (e.g. <tt>origin_airport 'lax'</tt>) Provide a characteristic. Remember, this is Ruby we're dealing with, so strings must be quoted.
254
- [+timeframe+] Display the current timeframe in effect on the emission estimate.
255
- [<tt>timeframe <i>timeframe</i></tt>] (e.g. <tt>timeframe '2009-01-01/2010-01-01'</tt> or just <tt>timeframe '2009'</tt>) Set a timeframe on the emission estimate.
256
- [+emission+] Displays the current emission in kilograms CO2e for this emitter.
257
- [+lbs+, +pounds+, or +tons+] Display the emission using different units.
258
- [+characteristics+] Lists the characteristics you have provided so far.
259
- [+methodology+] Summarizes how the calculation is being made.
260
- [+reports+] Displays intermediate calculations that were made in pursuit of the emission estimate.
261
- [+url+] Generates a methodology URL suitable for pasting into your browser for further inspection.
262
- [+done+] Saves this emitter and returns to shell mode.
263
-
264
- == Copyright
265
-
266
- Copyright (c) 2011 Brighter Planet.
@@ -1,1002 +0,0 @@
1
- = Emission estimate integration guide
2
-
3
- Using real examples from the July 2010 integration of http://brighterplanet.com with the carbon middleware emission estimate web service.
4
-
5
- == Step 1: Get an early win
6
-
7
- Convert just one legacy class to use the web service. All you want is a number back, any number!
8
-
9
- Here's what we changed:
10
-
11
- vidalia:~/app1 (practice_migration) $ git diff master..practice_migration
12
- diff --git a/Gemfile b/Gemfile
13
- index 1d582d4..acccc7e 100644
14
- --- a/Gemfile
15
- +++ b/Gemfile
16
- @@ -15,6 +15,7 @@ gem 'tzinfo', '0.3.20' # otherwise doesn't recognize timezone
17
- # gem 'mime-types', '1.16', :require => 'mime/types' # twitter_oauth
18
- # --
19
-
20
- +gem 'carbon', '0.2.4'
21
- gem 'simple-rss', '1.2.2'
22
- gem 'httparty', '0.6.1'
23
- gem 'thinking-sphinx', '1.3.16', :require => 'thinking_sphinx'
24
- diff --git a/app/models/automobile_fuel_type.rb b/app/models/automobile_fuel_type.rb
25
- index 30bb0c7..aaa4f49 100644
26
- --- a/app/models/automobile_fuel_type.rb
27
- +++ b/app/models/automobile_fuel_type.rb
28
- @@ -7,6 +7,10 @@ class AutomobileFuelType < ActiveRecord::Base
29
- attributes.slice 'code'
30
- end
31
-
32
- + def to_characteristic
33
- + code
34
- + end
35
- +
36
- class << self
37
- # don't show electricity (plug-in) per Matt https://brighterplanet.sifterapp.com/projects/30/issues/341/comments
38
- def for_collection_select(*args)
39
- diff --git a/app/models/automobile_make.rb b/app/models/automobile_make.rb
40
- index f3e7798..3133406 100644
41
- --- a/app/models/automobile_make.rb
42
- +++ b/app/models/automobile_make.rb
43
- @@ -17,6 +17,10 @@ class AutomobileMake < ActiveRecord::Base
44
- def to_s
45
- name
46
- end
47
- +
48
- + def to_characteristic
49
- + name
50
- + end
51
-
52
- # leave this for later if we need it
53
- SUBSIDIARIES = {
54
- diff --git a/app/models/automobile_model.rb b/app/models/automobile_model.rb
55
- index 1620b3a..870a6c5 100644
56
- --- a/app/models/automobile_model.rb
57
- +++ b/app/models/automobile_model.rb
58
- @@ -24,6 +24,10 @@ class AutomobileModel < ActiveRecord::Base
59
- name
60
- end
61
-
62
- + def to_characteristic
63
- + "#{make.to_characteristic} #{name}"
64
- + end
65
- +
66
- def fuel_efficiency(options = {})
67
- urbanity = options[:urbanity] || Automobile.fallback.urbanity
68
- urbanity * AutomobileVariant.average(:fuel_efficiency_city, :conditions => { :automobile_model_id => id }) +
69
- diff --git a/app/models/automobile_model_year.rb b/app/models/automobile_model_year.rb
70
- index 36a1714..32b5c1f 100644
71
- --- a/app/models/automobile_model_year.rb
72
- +++ b/app/models/automobile_model_year.rb
73
- @@ -29,4 +29,9 @@ class AutomobileModelYear < ActiveRecord::Base
74
- def to_i
75
- year.to_i
76
- end
77
- +
78
- + def to_characteristic
79
- + year
80
- + end
81
- +
82
- end
83
- diff --git a/app/models/automobile_size_class.rb b/app/models/automobile_size_class.rb
84
- index e62b80d..5ed1e2a 100644
85
- --- a/app/models/automobile_size_class.rb
86
- +++ b/app/models/automobile_size_class.rb
87
- @@ -7,6 +7,10 @@ class AutomobileSizeClass < ActiveRecord::Base
88
- attributes.slice 'name'
89
- end
90
-
91
- + def to_characteristic
92
- + name
93
- + end
94
- +
95
- class << self
96
- def for_collection_select(*args)
97
- all :order => :id
98
- diff --git a/app/models/emitters/automobile.rb b/app/models/emitters/automobile.rb
99
- index d2d852c..e7aa8a6 100644
100
- --- a/app/models/emitters/automobile.rb
101
- +++ b/app/models/emitters/automobile.rb
102
- @@ -1,4 +1,42 @@
103
- class Automobile < Emitter
104
- + include Carbon
105
- +
106
- + emit_as :automobile do
107
- + provide :make
108
- + provide :model
109
- + provide :model_year_proxy, :as => :model_year
110
- + # provide :variant - what to key on?
111
- + provide :fuel_type
112
- + provide :size_class
113
- + provide :fuel_efficiency
114
- + provide :urbanity
115
- + provide :hybridity
116
- + provide :daily_distance_estimate
117
- + provide :daily_duration
118
- + provide :weekly_distance_estimate
119
- + provide :annual_distance_estimate
120
- + provide :acquisition
121
- + provide :retirement
122
- + end
123
- +
124
- + # app1 model years are just years - 1999
125
- + # cm1 model years are composites of year and model - Acura TSX 1999
126
- + # so we have to proxy it
127
- + class ModelYearProxy
128
- + attr_reader :parent
129
- + def initialize(parent)
130
- + @parent = parent
131
- + end
132
- + def to_characteristic
133
- + if parent.model and parent.model_year
134
- + "#{parent.model.to_characteristic} #{parent.model_year.to_characteristic}"
135
- + end
136
- + end
137
- + end
138
- + def model_year_proxy
139
- + @model_year_proxy ||= ModelYearProxy.new self
140
- + end
141
- +
142
- diff --git a/config/initializers/carbon_middleware.rb b/config/initializers/carbon_middleware.rb
143
- new file mode 100644
144
- index 0000000..8becc5f
145
- --- /dev/null
146
- +++ b/config/initializers/carbon_middleware.rb
147
- @@ -0,0 +1 @@
148
- +::Carbon.key = '[Get your own at http://keys.brighterplanet.com]'
149
-
150
-
151
- And then we could do...
152
-
153
- >> a = Automobile.find 710
154
- => #<Automobile [...]>
155
- >> a.emission(Timeframe.this_year) # legacy call
156
- => 7040.88102017319
157
- >> a.emission_estimate(:timeframe => Timeframe.this_year).to_f # outsourced (new) call
158
- => 7040.88102017319
159
-
160
- An early win!
161
-
162
- === Comments on step 1
163
-
164
- * We're using Ruby on Rails and so we can use the official {carbon gem}[http://rubygems.org/gems/carbon]. If you're not on Ruby on Rails, please contact us for extra support in other languages or frameworks. If we don't have a library for it, we will help shoulder the burden of developing one.
165
-
166
- * We mapped all of the attributes of an Automobile that the web service recognizes.
167
-
168
- * One characteristic causes us trouble: model_year. The web service expects "Acura TSX 1999", but if you just called <tt>Automobile#model_year</tt> in the legacy app, you would get "1999". So, we map the characteristic "model_year" to <tt>Automobile#model_year_proxy</tt>, which responds with a <tt>ModelYearProxy</tt> object, which, in turn, responds to <tt>ModelYearProxy#to_characteristic</tt>.
169
-
170
- * Note the similarity of the legacy call <tt>Automobile#emission</tt> to the outsourced call <tt>Automobile#emission_estimate</tt>. That's because the carbon gem and our website have a lot of shared code history.
171
-
172
- == Step 2: Apply the outsourced method call in interface code
173
-
174
- Rather than getting really deep into replacing the business logic of your legacy emitter classes, try to hit interface problems early.
175
-
176
- Out of all the emitter classes, we've only converted the Automobile class, so for now we have to make sure the interface knows how to render both kinds: outsourced and legacy.
177
-
178
- Here's what we changed:
179
-
180
- vidalia:~/app1 (practice_migration) $ git diff master..practice_migration
181
- diff --git a/app/models/emitter.rb b/app/models/emitter.rb
182
- index 8603f22..93971b7 100644
183
- --- a/app/models/emitter.rb
184
- +++ b/app/models/emitter.rb
185
- @@ -1,5 +1,10 @@
186
- # todo dry emblem
187
- class Emitter < ActiveRecord::Base
188
- + # Whether emission estimates for this class have been outsourced to Brighter Planet
189
- + def outsourced?
190
- + respond_to? :emission_estimate
191
- + end
192
- +
193
- self.abstract_class = true
194
-
195
- extend ActiveSupport::Memoizable if Switches.memoization?
196
- @@ -304,7 +313,11 @@ class Emitter < ActiveRecord::Base
197
- if respond_to? :emission_date
198
- timeframe.include? emission_date
199
- elsif self.class.characterization.characteristics.include? :active_subtimeframe
200
- - timeframe & committee_reports[:active_subtimeframe]
201
- + if outsourced?
202
- + nil # don't know how to get active_subtimeframe
203
- + else
204
- + timeframe & committee_reports[:active_subtimeframe]
205
- + end
206
- end
207
- end
208
- diff --git a/app/models/component.rb b/app/models/component.rb
209
- index 24e359a..b00ba5d 100644
210
- --- a/app/models/component.rb
211
- +++ b/app/models/component.rb
212
- @@ -232,7 +232,13 @@ class Component
213
- def emission_from_imperfect_emitters(timeframe, perspective)
214
- # unlike preterite components, we have to leave data interpolation up to the emitter, so this is all-or-nothing
215
- if emitters_present_during? timeframe
216
- - emitters.values.flatten.sum { |emitter| emitter.as_of(perspective).emission(timeframe, perspective).to_f }
217
- + emitters.values.flatten.sum do |emitter|
218
- + if emitter.outsourced?
219
- + emitter.as_of(perspective).emission_estimate(:timeframe => timeframe).to_f # dropping perspective
220
- + else
221
- + emitter.as_of(perspective).emission(timeframe, perspective).to_f
222
- + end
223
- + end
224
- else
225
- typical_emission_from_emitters(timeframe)
226
- end
227
- @@ -248,7 +254,13 @@ class Component
228
- puts " Looking at subtimeframe #{subtimeframe.from} - #{subtimeframe.to}" if Component::DEBUG
229
- if emitters_present_during? subtimeframe # we want to use emissions committee for the period BETWEEN profile creation and the end of last month for which we have emitters
230
- puts " --> Emitters present!" if Component::DEBUG
231
- - e = preterite_emitters.sum { |emitter| emitter.emission(subtimeframe, perspective).to_f }
232
- + e = preterite_emitters.sum do |emitter|
233
- + if emitter.outsourced?
234
- + emitter.emission_estimate(:timeframe => subtimeframe).to_f # dropping perspective
235
- + else
236
- + emitter.emission(subtimeframe, perspective).to_f
237
- + end
238
- + end
239
- puts " --> Adding #{e}" if Component::DEBUG
240
- e
241
- elsif emitters_present? and profile.created_at.to_date <= subtimeframe.from and subtimeframe.to < Date.new(Time.now.year, Time.now.month, 1) # we want to use 0 for the period BETWEEN profile creation and the end of the last month for which we have no emitters IF there are any emitters at all, ever.
242
- @@ -261,7 +273,14 @@ class Component
243
- e
244
- end
245
- end
246
- - emission_from_patterns = (patterns.sum { |pattern| pattern.emission(timeframe, perspective).to_f }) || 0
247
- + emission_from_patterns = patterns.sum do |pattern|
248
- + if pattern.outsourced?
249
- + pattern.emission_estimate(:timeframe => timeframe).to_f # dropping perspective
250
- + else
251
- + pattern.emission(timeframe, perspective).to_f
252
- + end
253
- + end
254
- + emission_from_patterns ||= 0
255
- if emitters_present_during? timeframe # normally pattern emissions are *added to* preterite emitter emissions
256
- emission_during_timeframe += emission_from_patterns
257
- elsif emission_from_patterns.nonzero? # if there's no real emitters, but there are patterns established, the patterns *replace* placeholders
258
- @@ -270,6 +289,7 @@ class Component
259
- emission_during_timeframe
260
- end
261
-
262
- + # Component#emission, not to be confused with Emitter#emission
263
- def emission(timeframe, perspective = Time.zone.today)
264
- return 0 if disabled?
265
- unless @emission and @emission[timeframe.hash] and @emission[timeframe.hash][perspective.hash]
266
- diff --git a/app/helpers/emitters_helper.rb b/app/helpers/emitters_helper.rb
267
- index c1b75ee..e526ba1 100755
268
- --- a/app/helpers/emitters_helper.rb
269
- +++ b/app/helpers/emitters_helper.rb
270
- @@ -3,7 +3,7 @@
271
-
272
- module EmittersHelper
273
- def render_characteristic(emitter, characteristic)
274
- - characteristic_value = emitter.characteristics.has_key?(characteristic) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
275
- + characteristic_value = (emitter.outsourced? or emitter.characteristics.has_key?(characteristic)) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
276
- locals = { :emitter => emitter, :characteristic => characteristic, characteristic => characteristic_value }
277
-
278
- begin
279
- @@ -14,7 +14,7 @@ module EmittersHelper
280
- end
281
-
282
- def render_edit_characteristic(emitter, characteristic)
283
- - characteristic_value = emitter.characteristics.has_key?(characteristic) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
284
- + characteristic_value = (emitter.outsourced? or emitter.characteristics.has_key?(characteristic)) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
285
- locals = { :emitter => emitter, :characteristic => characteristic, characteristic => characteristic_value }
286
- template = pick_edit_partial(emitter, characteristic)
287
- if template == 'characteristics/controls/collection_select'
288
- @@ -80,7 +80,7 @@ module EmittersHelper
289
- end
290
-
291
- def render_placeholder_characteristic(emitter, characteristic)
292
- - characteristic_value = emitter.characteristics.has_key?(characteristic) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
293
- + characteristic_value = (emitter.outsourced? or emitter.characteristics.has_key?(characteristic)) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
294
- locals = { :emitter => emitter, :characteristic => characteristic, characteristic => characteristic_value }
295
- render :partial => "#{emitter.common_plural}/#{characteristic}/placeholder", :layout => 'characteristics/layouts/placeholder', :locals => locals
296
- rescue ActionView::MissingTemplate
297
- diff --git a/app/views/characteristics/_characteristic.html.erb b/app/views/characteristics/_characteristic.html.erb
298
- index c08003d..5e5ff1f 100644
299
- --- a/app/views/characteristics/_characteristic.html.erb
300
- +++ b/app/views/characteristics/_characteristic.html.erb
301
- @@ -1,7 +1,7 @@
302
- <%
303
- known = !(known == false)
304
- expanded = (expanded == true)
305
- -reported = emitter.committee_reports.keys.include? characteristic # this should never happen if known
306
- +reported = !emitter.outsourced? and emitter.committee_reports.keys.include? characteristic # this should never happen if known
307
- element_class = [characteristic]
308
- element_class << 'reported' if reported
309
- element_class << 'next' if expanded
310
- diff --git a/app/views/carbon_offsets/_emitter.html.erb b/app/views/carbon_offsets/_emitter.html.erb
311
- index 61cd499..b006892 100644
312
- --- a/app/views/carbon_offsets/_emitter.html.erb
313
- +++ b/app/views/carbon_offsets/_emitter.html.erb
314
- @@ -1,5 +1,9 @@
315
- <%
316
- -emission = emitter.emission timeframe
317
- +emission = if emitter.outsourced?
318
- + emitter.emission_estimate(:timeframe => timeframe).to_f
319
- +else
320
- + emitter.emission timeframe
321
- +end
322
- return unless emission.kilograms.to(:tons) > CustomPackage::MINIMUM_CARBON_OFFSET_IN_TONS
323
- custom_package = {
324
- :carbon_offset => emission,
325
- diff --git a/app/views/components/_gaps.html.erb b/app/views/components/_gaps.html.erb
326
- index 203d261..6e13b6f 100644
327
- --- a/app/views/components/_gaps.html.erb
328
- +++ b/app/views/components/_gaps.html.erb
329
- @@ -1,5 +1,5 @@
330
- <% if component.tense == :imperfect %>
331
- - <% covered_timeframes = component.emitters.values.flatten.collect { |e| e.committee_reports(timeframe)[:active_subtimeframe] } %>
332
- + <% covered_timeframes = component.emitters.values.flatten.collect { |e| e.outsourced? ? nil : e.committee_reports(timeframe)[:active_subtimeframe] } %>
333
- <% unless timeframe.covered_by? *covered_timeframes %>
334
- <% gaps = timeframe.gaps_left_by(*covered_timeframes) %>
335
- <p><strong>Note:</strong> you haven't told us about any <%= component.class.component_name %> emissions during <%= gaps.to_sentence %>. If you <%= component.verb[:past] %> <%= component.class.component_name.pluralize %> during these times, please tell us about it.</p>
336
- diff --git a/app/views/components/show.html.erb b/app/views/components/show.html.erb
337
- index 3a89238..1479f2e 100644
338
- --- a/app/views/components/show.html.erb
339
- +++ b/app/views/components/show.html.erb
340
- @@ -75,7 +75,11 @@
341
- <% @component.emitters.values.flatten.partition(&:retired?).reverse.flatten.each do |e| %>
342
- <li class="<%= e.emblem %><%= ' retired' if e.retired? %>">
343
- <p class="size">
344
- - <%= e.emission(@timeframe).kilograms.to(:tons, 2) %> tons
345
- + <% if e.outsourced? %>
346
- + <%= e.emission_estimate(:timeframe => @timeframe).to_f.kilograms.to(:tons, 2) %> tons
347
- + <% else %>
348
- + <%= e.emission(@timeframe).kilograms.to(:tons, 2) %> tons
349
- + <% end %>
350
- </p>
351
- <h5><%= link_to e.name, e %><%= ' (retired)' if e.retired? %></h5>
352
- <%= render :partial => 'carbon_offsets/emitter', :locals => { :emitter => e, :timeframe => @timeframe, :show_purchasing => viewing_self?(@user), :show_granting => false } %>
353
- diff --git a/app/views/emitters/update.js.rjs b/app/views/emitters/update.js.rjs
354
- index b325e97..5967500 100644
355
- --- a/app/views/emitters/update.js.rjs
356
- +++ b/app/views/emitters/update.js.rjs
357
- @@ -15,7 +15,11 @@ if @wizard
358
- page.visual_effect :fade, "#{common_name}_#{@characteristic}"
359
- page.visual_effect :appear, "#{common_name}_#{next_characteristic}"
360
- else
361
- - emission = @emitter.emission Timeframe.new(Time.zone.today, Time.zone.today.tomorrow.tomorrow)
362
- + emission = if @emitter.outsourced?
363
- + @emitter.emission Timeframe.new(Time.zone.today, Time.zone.today.tomorrow.tomorrow)
364
- + else
365
- + @emitter.emission_estimate(:timeframe => Timeframe.new(Time.zone.today, Time.zone.today.tomorrow.tomorrow)).to_f
366
- + end
367
- custom_package = {
368
- :carbon_offset => emission,
369
- :short_title => 'Travel emissions',
370
- diff --git a/app/views/emissions/_footprint.html.erb b/app/views/emissions/_footprint.html.erb
371
- index 89bcfe5..69c27a4 100644
372
- --- a/app/views/emissions/_footprint.html.erb
373
- +++ b/app/views/emissions/_footprint.html.erb
374
- @@ -5,7 +5,7 @@
375
- <%= render :partial => "#{footprintable.common_plural}/footprint", :locals => { :emitter => footprintable, :timeframe => timeframe } %>
376
- <% end %>
377
-
378
- - <span class="footprint"><span class="number"><%= footprintable.emission(timeframe).kilograms.to(:tons).adaptive_round %></span> tons CO<sub>2</sub></span>
379
- + <span class="footprint"><span class="number"><%= ((footprintable.is_a?(Emitter) and footprintable.outsourced?) ? footprintable.emission_estimate(:timeframe => timeframe) : footprintable.emission(timeframe)).to_f.kilograms.to(:tons).adaptive_round %></span> tons CO<sub>2</sub></span>
380
-
381
- <%= render :partial => 'emissions/extraneous', :locals => { :timeframe => timeframe } if footprintable.is_a?(Emitter) and !footprintable.emitted_during_timeframe?(timeframe) %>
382
- diff --git a/app/views/emitters/_sidebar.html.erb b/app/views/emitters/_sidebar.html.erb
383
- index 7f0c8d0..2da2214 100644
384
- --- a/app/views/emitters/_sidebar.html.erb
385
- +++ b/app/views/emitters/_sidebar.html.erb
386
- @@ -4,6 +4,11 @@
387
- <%= render :partial => 'emissions/footprint', :locals => { :footprintable => emitter, :timeframe => timeframe } %>
388
- <%#= render :partial => 'introduction', :locals => { :emitter => emitter, :component => component } %>
389
- <%= render :partial => 'carbon_offsets/emitter', :locals => { :emitter => emitter, :timeframe => timeframe, :show_purchasing => true, :show_granting => true } %>
390
- +<% if emitter.outsourced? %>
391
- + <p class="committee_reports">
392
- + <%= link_to 'Show calculation methods', emitter.emission_estimate.methodology %>
393
- + </p>
394
- +<% else %>
395
- <p class="committee_reports">
396
- <%=
397
- link_to_function 'Show calculation methods' do |page|
398
- @@ -28,6 +33,7 @@ end
399
- <% end %>
400
- </ol>
401
- <%= render :partial => 'emissions/methodology' %>
402
- - </div>
403
- +<% end %>
404
- +</div>
405
- <% end %>
406
-
407
- === Comments on step 2
408
-
409
- * The legacy method <tt>Automobile#committee_reports</tt> is disappearing. We're going to have to look into that.
410
-
411
- * We're don't have any way to pass in "perspective" for the outsourced emitters. Also going to have to deal with that.
412
-
413
- * One big win is to be found in <tt>app/views/emitters/_sidebar.html.erb</tt>. If we're dealing with an outsourced emitter class, we can link to the web service's methodology report instead of trying to explain the calculations locally. If you outsource the hard science to Brighter Planet, it's nice to know that we'll also explain it for you!
414
-
415
- == Step 3: Decide if you will be able to finish the job
416
-
417
- You know you can get a number (step 1) and apply it to the interface (step 2). But does the web service provide everything you need?
418
-
419
- For us, the potential show-stoppers were "committee reports", byproducts of the local calculation process. <b>Once we outsourced calculation, these values weren't being generated locally.</b>
420
-
421
- In step 2, we cheated by ignoring them and showing nothing. We used to exploit them as helpful defaults for interface elements:
422
-
423
- link:../doc/with-committee-reports.png
424
-
425
- After step two, it looked like this:
426
-
427
- link:../doc/without-committee-reports.png
428
-
429
- Let's take a closer look at what the emission estimate web service returns.
430
-
431
- >> a = Automobile.find 710
432
- => #<Automobile id [...] >
433
- >> pp a.emission_estimate.response.data
434
- {"emission_units"=>"kilograms",
435
- "timeframe"=>"2010-01-01/2011-01-01",
436
- "acquisition"=>Wed, 01 Jul 2009,
437
- "adjusted_fuel_efficiency"=>8.50287,
438
- "fuel_efficiency_multiplier"=>1.0,
439
- "annual_distance"=>24140.2,
440
- "fuel_type"=>
441
- {"automobile_fuel_type"=>
442
- {"name"=>"regular gasoline",
443
- "created_at"=>"2010-05-15T01:31:58Z",
444
- "code"=>"R",
445
- "annual_distance"=>18161.0,
446
- "emission_factor_units"=>"kilograms_per_litre",
447
- "updated_at"=>"2010-05-27T01:26:14Z",
448
- "annual_distance_units"=>"kilometres",
449
- "emission_factor"=>2.48}},
450
- "errors"=>[],
451
- "nominal_fuel_efficiency"=>7.88765178259689,
452
- "retirement"=>Sat, 01 Jan 2011,
453
- "methodology"=>
454
- "http://carbon.brighterplanet.com/automobiles.html?acquisition=2009-07-01&annual_distance_estimate=24140.2&fuel_efficiency=8.50287&fuel_type=R&key=3fca1403c1a561769a78ba462390ff01&make=Ford&size_class=Midsize+Pickup&timeframe=2010-01-01%2F2011-01-01",
455
- "speed"=>50.9438793670604,
456
- "emission_factor"=>2.48,
457
- "emission"=>7040.88102017319,
458
- "active_subtimeframe"=>
459
- <Timeframe(-629293078) 365 days starting 2010-01-01 ending 2011-01-01>,
460
- "make"=>
461
- {"automobile_make"=>
462
- {"name"=>"Ford",
463
- "created_at"=>nil,
464
- "fuel_efficiency_units"=>"kilometres_per_litre",
465
- "updated_at"=>"2010-05-27T01:21:08Z",
466
- "major"=>true,
467
- "fuel_efficiency"=>10.1994}},
468
- "fuel_consumed"=>2839.06492748919,
469
- "urbanity"=>0.43,
470
- "size_class"=>
471
- {"automobile_size_class"=>
472
- {"name"=>"Midsize Pickup",
473
- "created_at"=>"2010-05-14T22:55:43Z",
474
- "fuel_efficiency_city"=>6.8,
475
- "fuel_efficiency_highway"=>8.97,
476
- "annual_distance"=>20918.0,
477
- "hybrid_fuel_efficiency_highway_multiplier"=>1.2,
478
- "updated_at"=>"2010-05-14T22:55:44Z",
479
- "fuel_efficiency_highway_units"=>"kilometres_per_litre",
480
- "emblem"=>"",
481
- "annual_distance_units"=>"kilometres",
482
- "conventional_fuel_efficiency_city_multiplier"=>0.98,
483
- "conventional_fuel_efficiency_highway_multiplier"=>0.99,
484
- "hybrid_fuel_efficiency_city_multiplier"=>1.57,
485
- "fuel_efficiency_city_units"=>"kilometres_per_litre"}},
486
- "annual_distance_estimate"=>24140.2,
487
- "reports"=>
488
- [{"conclusion"=>7040.88102017319,
489
- "committee"=>
490
- {"name"=>"emission",
491
- "quorums"=>
492
- [{"name"=>"from fuel",
493
- "process"=>{},
494
- "supplements"=>["fuel_type"],
495
- "compliance"=>[],
496
- "requirements"=>["fuel_consumed", "emission_factor"]},
497
- {"name"=>"default",
498
- "process"=>{},
499
- "supplements"=>[],
500
- "compliance"=>[],
501
- "requirements"=>[]}]},
502
- "quorum"=>
503
- {"name"=>"from fuel",
504
- "process"=>{},
505
- "supplements"=>["fuel_type"],
506
- "compliance"=>[],
507
- "requirements"=>["fuel_consumed", "emission_factor"]}},
508
- {"conclusion"=>2.48,
509
- "committee"=>
510
- {"name"=>"emission_factor",
511
- "quorums"=>
512
- [{"name"=>"from fuel type",
513
- "process"=>{},
514
- "supplements"=>[],
515
- "compliance"=>[],
516
- "requirements"=>["fuel_type"]},
517
- {"name"=>"default",
518
- "process"=>{},
519
- "supplements"=>[],
520
- "compliance"=>[],
521
- "requirements"=>[]}]},
522
- "quorum"=>
523
- {"name"=>"from fuel type",
524
- "process"=>{},
525
- "supplements"=>[],
526
- "compliance"=>[],
527
- "requirements"=>["fuel_type"]}},
528
- {"conclusion"=>2839.06492748919,
529
- "committee"=>
530
- {"name"=>"fuel_consumed",
531
- "quorums"=>
532
- [{"name"=>"from adjusted fuel_efficiency and distance",
533
- "process"=>{},
534
- "supplements"=>[],
535
- "compliance"=>[],
536
- "requirements"=>["adjusted_fuel_efficiency", "distance"]}]},
537
- "quorum"=>
538
- {"name"=>"from adjusted fuel_efficiency and distance",
539
- "process"=>{},
540
- "supplements"=>[],
541
- "compliance"=>[],
542
- "requirements"=>["adjusted_fuel_efficiency", "distance"]}},
543
- {"conclusion"=>24140.2,
544
- "committee"=>
545
- {"name"=>"distance",
546
- "quorums"=>
547
- [{"name"=>"from annual distance",
548
- "process"=>{},
549
- "supplements"=>[],
550
- "compliance"=>[],
551
- "requirements"=>["annual_distance", "active_subtimeframe"]}]},
552
- "quorum"=>
553
- {"name"=>"from annual distance",
554
- "process"=>{},
555
- "supplements"=>[],
556
- "compliance"=>[],
557
- "requirements"=>["annual_distance", "active_subtimeframe"]}},
558
- {"conclusion"=>24140.2,
559
- "committee"=>
560
- {"name"=>"annual_distance",
561
- "quorums"=>
562
- [{"name"=>"from annual distance estimate",
563
- "process"=>{},
564
- "supplements"=>[],
565
- "compliance"=>[],
566
- "requirements"=>["annual_distance_estimate"]},
567
- {"name"=>"from weekly distance estimate",
568
- "process"=>{},
569
- "supplements"=>[],
570
- "compliance"=>[],
571
- "requirements"=>["weekly_distance_estimate"]},
572
- {"name"=>"from daily distance",
573
- "process"=>{},
574
- "supplements"=>[],
575
- "compliance"=>[],
576
- "requirements"=>["daily_distance"]},
577
- {"name"=>"from size class",
578
- "process"=>{},
579
- "supplements"=>[],
580
- "compliance"=>[],
581
- "requirements"=>["size_class"]},
582
- {"name"=>"from fuel type",
583
- "process"=>{},
584
- "supplements"=>[],
585
- "compliance"=>[],
586
- "requirements"=>["fuel_type"]},
587
- {"name"=>"default",
588
- "process"=>{},
589
- "supplements"=>[],
590
- "compliance"=>[],
591
- "requirements"=>[]}]},
592
- "quorum"=>
593
- {"name"=>"from annual distance estimate",
594
- "process"=>{},
595
- "supplements"=>[],
596
- "compliance"=>[],
597
- "requirements"=>["annual_distance_estimate"]}},
598
- {"conclusion"=>8.50287,
599
- "committee"=>
600
- {"name"=>"adjusted_fuel_efficiency",
601
- "quorums"=>
602
- [{"name"=>"from fuel efficiency",
603
- "process"=>{},
604
- "supplements"=>[],
605
- "compliance"=>[],
606
- "requirements"=>["fuel_efficiency"]},
607
- {"name"=>"from variant",
608
- "process"=>{},
609
- "supplements"=>[],
610
- "compliance"=>[],
611
- "requirements"=>["variant", "urbanity"]},
612
- {"name"=>"from nominal fuel efficiency and multiplier",
613
- "process"=>{},
614
- "supplements"=>[],
615
- "compliance"=>[],
616
- "requirements"=>
617
- ["nominal_fuel_efficiency", "fuel_efficiency_multiplier"]}]},
618
- "quorum"=>
619
- {"name"=>"from fuel efficiency",
620
- "process"=>{},
621
- "supplements"=>[],
622
- "compliance"=>[],
623
- "requirements"=>["fuel_efficiency"]}},
624
- {"conclusion"=>1.0,
625
- "committee"=>
626
- {"name"=>"fuel_efficiency_multiplier",
627
- "quorums"=>
628
- [{"name"=>"from_size_class_and_hybridity",
629
- "process"=>{},
630
- "supplements"=>[],
631
- "compliance"=>[],
632
- "requirements"=>["size_class", "hybridity", "urbanity"]},
633
- {"name"=>"from hybridity",
634
- "process"=>{},
635
- "supplements"=>[],
636
- "compliance"=>[],
637
- "requirements"=>["hybridity", "urbanity"]},
638
- {"name"=>"default",
639
- "process"=>{},
640
- "supplements"=>[],
641
- "compliance"=>[],
642
- "requirements"=>[]}]},
643
- "quorum"=>
644
- {"name"=>"default",
645
- "process"=>{},
646
- "supplements"=>[],
647
- "compliance"=>[],
648
- "requirements"=>[]}},
649
- {"conclusion"=>7.88765178259689,
650
- "committee"=>
651
- {"name"=>"nominal_fuel_efficiency",
652
- "quorums"=>
653
- [{"name"=>"from model",
654
- "process"=>{},
655
- "supplements"=>[],
656
- "compliance"=>[],
657
- "requirements"=>["model", "urbanity"]},
658
- {"name"=>"from make and model year",
659
- "process"=>{},
660
- "supplements"=>[],
661
- "compliance"=>[],
662
- "requirements"=>["model_year"]},
663
- {"name"=>"from size class",
664
- "process"=>{},
665
- "supplements"=>[],
666
- "compliance"=>[],
667
- "requirements"=>["size_class", "urbanity"]},
668
- {"name"=>"from model year",
669
- "process"=>{},
670
- "supplements"=>[],
671
- "compliance"=>[],
672
- "requirements"=>["model_year"]},
673
- {"name"=>"from make",
674
- "process"=>{},
675
- "supplements"=>[],
676
- "compliance"=>[],
677
- "requirements"=>["make"]},
678
- {"name"=>"default",
679
- "process"=>{},
680
- "supplements"=>[],
681
- "compliance"=>[],
682
- "requirements"=>[]}]},
683
- "quorum"=>
684
- {"name"=>"from size class",
685
- "process"=>{},
686
- "supplements"=>[],
687
- "compliance"=>[],
688
- "requirements"=>["size_class", "urbanity"]}},
689
- {"conclusion"=>50.9438793670604,
690
- "committee"=>
691
- {"name"=>"speed",
692
- "quorums"=>
693
- [{"name"=>"from urbanity",
694
- "process"=>{},
695
- "supplements"=>[],
696
- "compliance"=>[],
697
- "requirements"=>["urbanity"]}]},
698
- "quorum"=>
699
- {"name"=>"from urbanity",
700
- "process"=>{},
701
- "supplements"=>[],
702
- "compliance"=>[],
703
- "requirements"=>["urbanity"]}},
704
- {"conclusion"=>0.43,
705
- "committee"=>
706
- {"name"=>"urbanity",
707
- "quorums"=>
708
- [{"name"=>"default",
709
- "process"=>{},
710
- "supplements"=>[],
711
- "compliance"=>[],
712
- "requirements"=>[]}]},
713
- "quorum"=>
714
- {"name"=>"default",
715
- "process"=>{},
716
- "supplements"=>[],
717
- "compliance"=>[],
718
- "requirements"=>[]}},
719
- {"conclusion"=>"2010-01-01/2011-01-01",
720
- "committee"=>
721
- {"name"=>"active_subtimeframe",
722
- "quorums"=>
723
- [{"name"=>"from acquisition and retirement",
724
- "process"=>{},
725
- "supplements"=>[],
726
- "compliance"=>[],
727
- "requirements"=>["acquisition", "retirement"]}]},
728
- "quorum"=>
729
- {"name"=>"from acquisition and retirement",
730
- "process"=>{},
731
- "supplements"=>[],
732
- "compliance"=>[],
733
- "requirements"=>["acquisition", "retirement"]}},
734
- {"conclusion"=>Sat, 01 Jan 2011,
735
- "committee"=>
736
- {"name"=>"retirement",
737
- "quorums"=>
738
- [{"name"=>"from acquisition",
739
- "process"=>{},
740
- "supplements"=>["acquisition"],
741
- "compliance"=>[],
742
- "requirements"=>[]}]},
743
- "quorum"=>
744
- {"name"=>"from acquisition",
745
- "process"=>{},
746
- "supplements"=>["acquisition"],
747
- "compliance"=>[],
748
- "requirements"=>[]}}],
749
- "fuel_efficiency"=>8.50287,
750
- "distance"=>24140.2}
751
-
752
- An easier way to look is using the {JSONView add-on}[https://addons.mozilla.org/en-US/firefox/addon/10869/] for Firefox. Just take the methodology URL:
753
-
754
- "methodology"=>
755
- "http://carbon.brighterplanet.com/automobiles.html?acquisition=2009-07-01&annual_distance_estimate=24140.2&fuel_efficiency=8.50287&fuel_type=R&key=3fca1403c1a561769a78ba462390ff01&make=Ford&size_class=Midsize+Pickup&timeframe=2010-01-01%2F2011-01-01",
756
-
757
- ...and say you want JSON instead of HTML...
758
-
759
- http://carbon.brighterplanet.com/automobiles.html?acquisition=2009-07-01&annual_distance_estimate=24140.2&fuel_efficiency=8.50287&fuel_type=R&key=3fca1403c1a561769a78ba462390ff01&make=Ford&size_class=Midsize+Pickup&timeframe=2010-01-01%2F2011-01-01
760
- |
761
- change this
762
- |
763
- http://carbon.brighterplanet.com/automobiles.json?acquisition=2009-07-01&annual_distance_estimate=24140.2&fuel_efficiency=8.50287&fuel_type=R&key=3fca1403c1a561769a78ba462390ff01&make=Ford&size_class=Midsize+Pickup&timeframe=2010-01-01%2F2011-01-01
764
-
765
- ...and look what we found!
766
-
767
- link:../doc/examining-response-with-jsonview.png
768
-
769
- As you can see, the byproducts (really the internal variables) of the calculation are sent along with the response. Your legacy app may never use these values, but we do! We clarified things with a few helper methods:
770
-
771
- diff --git a/app/models/emitter.rb b/app/models/emitter.rb
772
- index 93971b7..c5d5709 100644
773
- --- a/app/models/emitter.rb
774
- +++ b/app/models/emitter.rb
775
- @@ -5,6 +5,35 @@ class Emitter < ActiveRecord::Base
776
- respond_to? :emission_estimate
777
- end
778
-
779
- + def real_or_assumed_characteristic_value(key)
780
- + return characteristics[key] if characteristics.has_key? key
781
- + if outsourced?
782
- + emission_estimate.response.data[key.to_s]
783
- + else
784
- + committee_reports[key]
785
- + end
786
- + end
787
- +
788
- + def assumed_characteristic_value?(key)
789
- + if outsourced?
790
- + emission_estimate.response.data.keys.include? key.to_s
791
- + else
792
- + committee_reports.keys.include? key
793
- + end
794
- + end
795
- +
796
- + def reporting_committee_name(key)
797
- + if outsourced?
798
- + begin
799
- + emission_estimate.response.data['reports'].detect { |i| i['committee']['name'] == key.to_s }['quorum']['name']
800
- + rescue NoMethodError
801
- + # couldn't find it
802
- + end
803
- + else
804
- + self.class.characterization.committees[key].called_quorum.name
805
- + end.to_s.dasherize.downcase
806
- + end
807
- +
808
- self.abstract_class = true
809
-
810
- extend ActiveSupport::Memoizable if Switches.memoization?
811
- @@ -54,6 +83,8 @@ class Emitter < ActiveRecord::Base
812
- value = options[:value]
813
- elsif characteristics.has_key?(name)
814
- value = characteristics[name]
815
- + elsif outsourced? and emission_estimate.response.data.has_key? name.to_s
816
- + value = emission_estimate.response.data[name.to_s]
817
- elsif committee_reports.has_key?(name)
818
- value = committee_reports[name]
819
- else
820
- @@ -314,7 +345,7 @@ class Emitter < ActiveRecord::Base
821
- timeframe.include? emission_date
822
- elsif self.class.characterization.characteristics.include? :active_subtimeframe
823
- if outsourced?
824
- - timeframe & emission_estimate(:timeframe => timeframe).active_subtimeframe
825
- + timeframe & emission_estimate(:timeframe => timeframe).response.data['active_subtimeframe']
826
- else
827
- timeframe & committee_reports[:active_subtimeframe]
828
- end
829
-
830
- Then we brought the internal variables back to life:
831
-
832
- diff --git a/app/helpers/emitters_helper.rb b/app/helpers/emitters_helper.rb
833
- index c1b75ee..b5c3a1c 100755
834
- --- a/app/helpers/emitters_helper.rb
835
- +++ b/app/helpers/emitters_helper.rb
836
- @@ -3,7 +3,7 @@
837
-
838
- module EmittersHelper
839
- def render_characteristic(emitter, characteristic)
840
- - characteristic_value = (emitter.outsourced? or emitter.characteristics.has_key?(characteristic)) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
841
- + characteristic_value = emitter.real_or_assumed_characteristic_value(characteristic)
842
- locals = { :emitter => emitter, :characteristic => characteristic, characteristic => characteristic_value }
843
-
844
- begin
845
- @@ -14,7 +14,7 @@ module EmittersHelper
846
- end
847
-
848
- def render_edit_characteristic(emitter, characteristic)
849
- - characteristic_value = (emitter.outsourced? or emitter.characteristics.has_key?(characteristic)) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
850
- + characteristic_value = emitter.real_or_assumed_characteristic_value(characteristic)
851
- locals = { :emitter => emitter, :characteristic => characteristic, characteristic => characteristic_value }
852
- template = pick_edit_partial(emitter, characteristic)
853
- if template == 'characteristics/controls/collection_select'
854
- @@ -80,7 +80,7 @@ module EmittersHelper
855
- end
856
-
857
- def render_placeholder_characteristic(emitter, characteristic)
858
- - characteristic_value = (emitter.outsourced? or emitter.characteristics.has_key?(characteristic)) ? emitter.characteristics[characteristic] : emitter.committee_reports[characteristic]
859
- + characteristic_value = emitter.real_or_assumed_characteristic_value(characteristic)
860
- locals = { :emitter => emitter, :characteristic => characteristic, characteristic => characteristic_value }
861
- render :partial => "#{emitter.common_plural}/#{characteristic}/placeholder", :layout => 'characteristics/layouts/placeholder', :locals => locals
862
- rescue ActionView::MissingTemplate
863
- diff --git a/app/views/characteristics/_characteristic.html.erb b/app/views/characteristics/_characteristic.html.erb
864
- index c08003d..b9bb073 100644
865
- --- a/app/views/characteristics/_characteristic.html.erb
866
- +++ b/app/views/characteristics/_characteristic.html.erb
867
- @@ -1,7 +1,7 @@
868
- <%
869
- known = !(known == false)
870
- expanded = (expanded == true)
871
- -reported = !emitter.outsourced? and emitter.committee_reports.keys.include? characteristic # this should never happen if known
872
- +reported = emitter.assumed_characteristic_value? characteristic # this should never happen if known
873
- element_class = [characteristic]
874
- element_class << 'reported' if reported
875
- element_class << 'next' if expanded
876
- @@ -16,7 +16,7 @@ element_class = element_class.join ' '
877
- <%= render_edit_characteristic emitter, characteristic if expanded %>
878
- <%= render_help_for_characteristic(emitter.common_symbol, characteristic) if expanded %>
879
- <% if reported %>
880
- - <span class="assumption">Assuming <%= render_characteristic emitter, characteristic %><span class="method"> via the <i><%= emitter.class.characterization.committees[characteristic].called_quorum.name.to_s.dasherize %></i> method</span></span>
881
- + <span class="assumption">Assuming <%= render_characteristic emitter, characteristic %><span class="method"> via the <i><%= emitter.reporting_committee_name(characteristic) %></i> method</span></span>
882
- <% end %>
883
- <% end %>
884
- </dd>
885
- diff --git a/app/views/components/_gaps.html.erb b/app/views/components/_gaps.html.erb
886
- index 203d261..4e0fa04 100644
887
- --- a/app/views/components/_gaps.html.erb
888
- +++ b/app/views/components/_gaps.html.erb
889
- @@ -1,5 +1,5 @@
890
- <% if component.tense == :imperfect %>
891
- - <% covered_timeframes = component.emitters.values.flatten.collect { |e| e.outsourced? ? nil : e.committee_reports(timeframe)[:active_subtimeframe] } %>
892
- + <% covered_timeframes = component.emitters.values.flatten.collect { |e| e.real_or_assumed_characteristic_value :active_subtimeframe } %>
893
- <% unless timeframe.covered_by? *covered_timeframes %>
894
- <% gaps = timeframe.gaps_left_by(*covered_timeframes) %>
895
- <p><strong>Note:</strong> you haven't told us about any <%= component.class.component_name %> emissions during <%= gaps.to_sentence %>. If you <%= component.verb[:past] %> <%= component.class.component_name.pluralize %> during these times, please tell us about it.</p>
896
-
897
- === Comments on step 3
898
-
899
- * A lot of data comes back from the emission estimate web service. You can inspect it by grabbing the methodology URL and changing it to JSON.
900
-
901
- * We realized that the "perspective" argument was mostly for caching. We don't need it anymore because the expensive computations are outsourced!
902
-
903
- == Step 4: Prepare for network problems (WORKING DRAFT)
904
-
905
- If you've decided that the data returned by the Brighter Planet emission estimate web service is sufficient for your needs, it's time to deal with network problems.
906
-
907
- link:../doc/timeout-error.png
908
-
909
- You don't want your application to go offline if there is a network problem between carbon middleware and you. Using the carbon gem, that means rescuing from exceptions.
910
-
911
- It's easy to fake a simple problem by adding this to <tt>/etc/hosts</tt>: (remember to take it out later)
912
-
913
- 10.0.22.22 carbon.brighterplanet.com
914
-
915
- Now everything will time out:
916
-
917
- >> a = Automobile.find 710
918
- => #<Automobile [...] >
919
- >> a.emission_estimate.to_f
920
- Errno::ETIMEDOUT: Connection timed out - connect(2)
921
- from /usr/lib/ruby/1.8/net/http.rb:560:in `initialize'
922
- from /usr/lib/ruby/1.8/net/http.rb:560:in `open'
923
- from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
924
- from /usr/lib/ruby/1.8/timeout.rb:53:in `timeout'
925
- from /usr/lib/ruby/1.8/timeout.rb:93:in `timeout'
926
- from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
927
- from /usr/lib/ruby/1.8/net/http.rb:553:in `do_start'
928
- from /usr/lib/ruby/1.8/net/http.rb:542:in `start'
929
- from /usr/lib/ruby/gems/1.8/gems/nap-0.4/lib/rest/request.rb:133:in `perform'
930
- from /usr/lib/ruby/gems/1.8/gems/carbon-0.2.4/lib/carbon/emission_estimate/response.rb:43:in `perform'
931
- from /usr/lib/ruby/gems/1.8/gems/carbon-0.2.4/lib/carbon/emission_estimate/response.rb:16:in `load_realtime_data'
932
- from /usr/lib/ruby/gems/1.8/gems/carbon-0.2.4/lib/carbon/emission_estimate/response.rb:11:in `send'
933
- from /usr/lib/ruby/gems/1.8/gems/carbon-0.2.4/lib/carbon/emission_estimate/response.rb:11:in `initialize'
934
- from /usr/lib/ruby/gems/1.8/gems/carbon-0.2.4/lib/carbon/emission_estimate.rb:66:in `new'
935
- from /usr/lib/ruby/gems/1.8/gems/carbon-0.2.4/lib/carbon/emission_estimate.rb:66:in `response'
936
- from /usr/lib/ruby/gems/1.8/gems/carbon-0.2.4/lib/carbon/emission_estimate.rb:41:in `method_missing'
937
- from (irb):5>>
938
-
939
- Here's an idea:
940
-
941
- diff --git a/app/views/emitters/_sidebar.html.erb b/app/views/emitters/_sidebar.html.erb
942
- index 2da2214..9190387 100644
943
- --- a/app/views/emitters/_sidebar.html.erb
944
- +++ b/app/views/emitters/_sidebar.html.erb
945
- @@ -6,7 +6,9 @@
946
- <%= render :partial => 'carbon_offsets/emitter', :locals => { :emitter => emitter, :timeframe => timeframe, :show_purchasing => true, :show_granting => true } %>
947
- <% if emitter.outsourced? %>
948
- <p class="committee_reports">
949
- - <%= link_to 'Show calculation methods', emitter.emission_estimate.methodology %>
950
- + <% outsourced_content do %>
951
- + <%= link_to 'Show calculation methods', emitter.emission_estimate.methodology %>
952
- + <% end %>
953
- </p>
954
- <% else %>
955
- <p class="committee_reports">
956
- diff --git a/app/helpers/emitters_helper.rb b/app/helpers/emitters_helper.rb
957
- index c1b75ee..bd0c78f 100755
958
- --- a/app/helpers/emitters_helper.rb
959
- +++ b/app/helpers/emitters_helper.rb
960
- @@ -2,8 +2,47 @@
961
- # doc/partials_that_use_local_variables_named_after_characteristics.rb
962
-
963
- module EmittersHelper
964
- + def outsourced_content(failsafe = '', &block)
965
- + failed = request.instance_variable_get :@outsourced_request_failed
966
- + str = failed ? failsafe : capture(&block)
967
- + if block_called_from_erb? block
968
- + # sabshere 7/28/10 use of #concat is specific to Rails 2, I believe
969
- + concat str
970
- + else
971
- + str
972
- + end
973
- + rescue ::SocketError, ::Timeout::Error, ::Errno::ETIMEDOUT, ::Errno::ENETUNREACH, ::Errno::ECONNRESET, ::Errno::ECONNREFUSED
974
- + # These are general network errors raised by Net::HTTP.
975
- + # Your internet connection might be down, or our servers might be down.
976
- + request.instance_variable_set :@outsourced_request_failed, true
977
- + retry
978
- + rescue ::Carbon::RateLimited
979
- + # Realtime mode only.
980
- + # In order to prevent denial-of-service attacks, our servers rate limit requests.
981
- + # The gem will try up to three times to get an answer back from the server, waiting slightly longer each time.
982
- + # If you still get this exception, please contact us at staff@brighterplanet.com and we'll lift your rate.
983
- + request.instance_variable_set :@outsourced_request_failed, true
984
- + retry
985
- + rescue ::Carbon::RealtimeEstimateFailed
986
- + # Realtime mode only.
987
- + # Our server returned a 4XX or 5XX error.
988
- + # Please contact us at staff@brighterplanet.com if you get these more than a couple times.
989
- + retry
990
- + rescue ::Carbon::QueueingFailed
991
- + # Async mode only.
992
- + # The gem connects directly to Amazon SQS in order to provide maximum throughput. If that service returns anything other than success, you get this exception.
993
- + # Please contact us at staff@brighterplanet.com if you see too many of these.
994
- + request.instance_variable_set :@outsourced_request_failed, true
995
- + retry
996
- + end
997
-
998
- === Comments on step 4
999
-
1000
- * This is a very Rails-specific solution. It might not be as easy in your language of choice.
1001
-
1002
- * Even if this is easy, it has to go in a lot of places in our app. Maybe we should proxy the response object (::Carbon::EmissionEstimate) and add useful methods there?