carbon 1.1.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/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.
|
data/doc/INTEGRATION_GUIDE.rdoc
DELETED
@@ -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?
|