carbon 0.0.7 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/README.rdoc +81 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/carbon.gemspec +67 -0
- data/lib/carbon.rb +220 -21
- data/lib/carbon/base.rb +52 -0
- data/lib/carbon/emission_estimate.rb +58 -0
- data/spec/lib/carbon_spec.rb +109 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/specwatchr +60 -0
- metadata +76 -32
- data/lib/carbon/emissions_calculation.rb +0 -64
- data/lib/carbon/emitter.rb +0 -18
- data/lib/carbon/emitter/characteristic.rb +0 -19
- data/lib/carbon/emitter/class_methods.rb +0 -20
- data/lib/carbon/emitter/options.rb +0 -30
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
= Carbon
|
2
|
+
|
3
|
+
Carbon is a Ruby API wrapper for the {Brighter Planet emission estimate web service}[http://carbon.brighterplanet.com], which is located at http://carbon.brighterplanet.com.
|
4
|
+
|
5
|
+
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.
|
6
|
+
|
7
|
+
== Quick start
|
8
|
+
|
9
|
+
First get the gem!
|
10
|
+
|
11
|
+
$ gem install carbon
|
12
|
+
|
13
|
+
Carbon works by extending any Ruby class you're using to represent an emission source. For instance, let's say you have a plain Ruby class <tt>RentalCar</tt> that represents a rental car on your lot: (<tt>ActiveRecord</tt> models work fine too!)
|
14
|
+
|
15
|
+
class RentalCar
|
16
|
+
attr_accessor :year, :make, :model, :fuel_efficiency, :daily_distance_average, :purchase_date, :retirement_date
|
17
|
+
end
|
18
|
+
|
19
|
+
In order to calculate carbon emissions, we need to map the car's relevant attributes to correctly-named characteristics that the {web service}[http://carbon.brighterplanet.com] will recognize:
|
20
|
+
|
21
|
+
class RentalCar
|
22
|
+
include Carbon
|
23
|
+
[...]
|
24
|
+
emit_as :automobile do
|
25
|
+
provide :model_year, :as => :year # you say tomayto, I say tomahto
|
26
|
+
provide :make
|
27
|
+
provide :model
|
28
|
+
provide :fuel_efficiency
|
29
|
+
provide :daily_distance_estimate, :as => daily_distance_average
|
30
|
+
provide :acquisition, :as => :purchase_date
|
31
|
+
provide :retirement, :as => :retirement_date
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
When you want to calculate emissions, simply call <tt>RentalCar</tt>#<tt>emission</tt>... (and <b>store the result to a local variable!</b> Otherwise it will make a web service call every time.)
|
36
|
+
|
37
|
+
> my_car = RentalCar.new([...])
|
38
|
+
=> #<RentalCar [...]>
|
39
|
+
> my_emission = my_car.emission
|
40
|
+
=> #<Carbon::EmissionEstimate [...]>
|
41
|
+
> my_emission.to_f
|
42
|
+
=> 4919.2
|
43
|
+
> my_emission.emission_units
|
44
|
+
=> "kilograms"
|
45
|
+
> my_emission.methodology
|
46
|
+
=> "http://carbon.brighterplanet.com/automobiles.html?[...]"
|
47
|
+
|
48
|
+
Read the {carbon gem RDoc}[http://rdoc.info/projects/brighterplanet/carbon] for more!
|
49
|
+
|
50
|
+
== A note on API keys
|
51
|
+
|
52
|
+
You should get an API key from http://keys.brighterplanet.com and set it globally:
|
53
|
+
|
54
|
+
Carbon.key = '12903019230128310293'
|
55
|
+
|
56
|
+
Now all of your queries will use that key.
|
57
|
+
|
58
|
+
== A note on modes
|
59
|
+
|
60
|
+
When you send in a query, you have two options for how you want the result back:
|
61
|
+
|
62
|
+
* realtime - you get the answer back immediately. More expensive.
|
63
|
+
* async - the answer is POSTed back to a URL that you specify. You must have a server waiting for it! Cheaper.
|
64
|
+
|
65
|
+
The default is realtime:
|
66
|
+
|
67
|
+
> my_emission = RentalCar.new.emission
|
68
|
+
=> #<Carbon::EmissionEstimate [...]>
|
69
|
+
|
70
|
+
A good way to test the "async" mode is to set up a {PostBin}[http://postbin.org]
|
71
|
+
|
72
|
+
> RentalCar.new.emission :mode => :async, :callback => 'http://postbin.org/1dj0145'
|
73
|
+
=> true # useless, but go check out http://postbin.org/1dj0145
|
74
|
+
|
75
|
+
You can set the mode globally with
|
76
|
+
|
77
|
+
Carbon.mode = :async
|
78
|
+
|
79
|
+
== Copyright
|
80
|
+
|
81
|
+
Copyright (c) 2010 Brighter Planet.
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gemspec|
|
7
|
+
gemspec.name = 'carbon'
|
8
|
+
gemspec.summary = %q{A gem for calculating carbon footprints using Brighter Planet's Carbon Middleware service}
|
9
|
+
gemspec.description = %q{Carbon allows you to easily calculate the carbon footprint of various activities. This is an API for the Brighter Planet Carbon Middleware service.}
|
10
|
+
gemspec.email = 'derek.kastner@brighterplanet.com'
|
11
|
+
gemspec.homepage = 'http://carbon.brighterplanet.com/libraries'
|
12
|
+
gemspec.authors = ['Derek Kastner', 'Seamus Abshere']
|
13
|
+
gemspec.add_dependency 'activesupport', '>=3.0.0.beta2'
|
14
|
+
gemspec.add_dependency 'nap', '>=0.4'
|
15
|
+
gemspec.add_dependency 'timeframe', '>=0.0.6'
|
16
|
+
|
17
|
+
gemspec.add_development_dependency 'fakeweb', '>=1.2.8'
|
18
|
+
# sabshere 7/16/10 if you're having trouble running specs, try "rspec spec" and/or "sudo gem install rspec --pre"
|
19
|
+
gemspec.add_development_dependency 'rspec', '>=2.0.0.beta.17'
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
Rake::TestTask.new(:test) do |test|
|
28
|
+
test.libs << 'lib' << 'test'
|
29
|
+
test.pattern = 'test/**/test_*.rb'
|
30
|
+
test.verbose = true
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
require 'rcov/rcovtask'
|
35
|
+
Rcov::RcovTask.new do |test|
|
36
|
+
test.libs << 'test'
|
37
|
+
test.pattern = 'test/**/test_*.rb'
|
38
|
+
test.verbose = true
|
39
|
+
end
|
40
|
+
rescue LoadError
|
41
|
+
task :rcov do
|
42
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
task :test => :check_dependencies
|
47
|
+
|
48
|
+
task :default => :test
|
49
|
+
|
50
|
+
require 'rake/rdoctask'
|
51
|
+
Rake::RDocTask.new do |rdoc|
|
52
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
53
|
+
|
54
|
+
rdoc.rdoc_dir = 'rdoc'
|
55
|
+
rdoc.title = "decider #{version}"
|
56
|
+
rdoc.rdoc_files.include('README*')
|
57
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
58
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/carbon.gemspec
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{carbon}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Derek Kastner", "Seamus Abshere"]
|
12
|
+
s.date = %q{2010-07-19}
|
13
|
+
s.description = %q{Carbon allows you to easily calculate the carbon footprint of various activities. This is an API for the Brighter Planet Carbon Middleware service.}
|
14
|
+
s.email = %q{derek.kastner@brighterplanet.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"MIT-LICENSE.txt",
|
21
|
+
"README.rdoc",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"carbon.gemspec",
|
25
|
+
"lib/carbon.rb",
|
26
|
+
"lib/carbon/base.rb",
|
27
|
+
"lib/carbon/emission_estimate.rb",
|
28
|
+
"spec/lib/carbon_spec.rb",
|
29
|
+
"spec/spec_helper.rb",
|
30
|
+
"spec/specwatchr"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://carbon.brighterplanet.com/libraries}
|
33
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = %q{1.3.7}
|
36
|
+
s.summary = %q{A gem for calculating carbon footprints using Brighter Planet's Carbon Middleware service}
|
37
|
+
s.test_files = [
|
38
|
+
"spec/lib/carbon_spec.rb",
|
39
|
+
"spec/spec_helper.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0.beta2"])
|
48
|
+
s.add_runtime_dependency(%q<nap>, [">= 0.4"])
|
49
|
+
s.add_runtime_dependency(%q<timeframe>, [">= 0.0.6"])
|
50
|
+
s.add_development_dependency(%q<fakeweb>, [">= 1.2.8"])
|
51
|
+
s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.17"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0.beta2"])
|
54
|
+
s.add_dependency(%q<nap>, [">= 0.4"])
|
55
|
+
s.add_dependency(%q<timeframe>, [">= 0.0.6"])
|
56
|
+
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
|
57
|
+
s.add_dependency(%q<rspec>, [">= 2.0.0.beta.17"])
|
58
|
+
end
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0.beta2"])
|
61
|
+
s.add_dependency(%q<nap>, [">= 0.4"])
|
62
|
+
s.add_dependency(%q<timeframe>, [">= 0.0.6"])
|
63
|
+
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
|
64
|
+
s.add_dependency(%q<rspec>, [">= 2.0.0.beta.17"])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
data/lib/carbon.rb
CHANGED
@@ -1,29 +1,228 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require '
|
4
|
-
require '
|
1
|
+
require 'uri'
|
2
|
+
require 'blockenspiel'
|
3
|
+
require 'rest' # provided by nap gem
|
4
|
+
require 'timeframe'
|
5
|
+
%w{
|
6
|
+
active_support/core_ext/module/attribute_accessors
|
7
|
+
active_support/core_ext/class/attribute_accessors
|
8
|
+
active_support/core_ext/hash/keys
|
9
|
+
active_support/core_ext/hash/reverse_merge
|
10
|
+
active_support/core_ext/object/to_query
|
11
|
+
active_support/core_ext/array/wrap
|
12
|
+
active_support/inflector/inflections
|
13
|
+
active_support/json/decoding
|
14
|
+
}.each do |active_support_3_requirement|
|
15
|
+
require active_support_3_requirement
|
16
|
+
end
|
17
|
+
require 'carbon/base'
|
18
|
+
require 'carbon/emission_estimate'
|
5
19
|
|
20
|
+
# A module (aka mixin) that lets you estimate carbon emissions by querying the {Brighter Planet carbon middleware emission estimate web service}[http://carbon.brighterplanet.com].
|
21
|
+
#
|
22
|
+
# class RentalCar
|
23
|
+
# include Carbon
|
24
|
+
# [...]
|
25
|
+
# emit_as :automobile do
|
26
|
+
# provide :make
|
27
|
+
# provide :model
|
28
|
+
# provide :model_year
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# The DSL consists of the methods <tt>emit_as</tt> and <tt>provide</tt>.
|
33
|
+
#
|
34
|
+
# In this example, the DSL says:
|
35
|
+
# * A rental car emits carbon like an "automobile", which is one of Brighter Planet's recognized emitter classes.
|
36
|
+
# * Your implementation can provide up to three data points about a rental car: its make, its model, and its model year (but not necessarily all of them, all the time.)
|
37
|
+
#
|
38
|
+
# Once you've mixed in <tt>Carbon</tt>, you get the method <tt>emission</tt>, which you can call at any time to request an emission estimate.
|
6
39
|
module Carbon
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
40
|
+
def self.included(klass) # :nodoc:
|
41
|
+
klass.cattr_accessor :carbon_base
|
42
|
+
klass.extend ClassMethods
|
43
|
+
end
|
44
|
+
|
45
|
+
MODES = [ :realtime, :async ]
|
46
|
+
REALTIME_URL = 'http://carbon.brighterplanet.com'
|
47
|
+
ASYNC_URL = 'https://queue.amazonaws.com/121562143717/cm1_production_incoming'
|
48
|
+
|
49
|
+
class UnrecognizedMode < ArgumentError # :nodoc:
|
50
|
+
end
|
51
|
+
class BlankCallback < ArgumentError # :nodoc:
|
52
|
+
end
|
53
|
+
class RealtimeEstimateFailed < RuntimeError # :nodoc:
|
54
|
+
end
|
55
|
+
class QueueingFailed < RuntimeError # :nodoc:
|
56
|
+
end
|
14
57
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
def base_url=(val)
|
19
|
-
@base_url = val
|
20
|
-
end
|
58
|
+
# The api key obtained from http://keys.brighterplanet.com
|
59
|
+
mattr_accessor :key
|
21
60
|
|
22
|
-
|
23
|
-
|
61
|
+
mattr_accessor :_mode
|
62
|
+
# Return the current mode. Defaults to <tt>:realtime</tt>.
|
63
|
+
def self.mode
|
64
|
+
_mode || :realtime
|
65
|
+
end
|
66
|
+
# Set the current mode.
|
67
|
+
# * Realtime mode (<tt>:realtime</tt>) means you get the answer back immediately.
|
68
|
+
# * Async mode (<tt>:async</tt>) means the answer will be POSTed back to you at a URL you specify. You must have a server waiting to receive it!
|
69
|
+
def self.mode=(str)
|
70
|
+
self._mode = str.to_sym
|
71
|
+
raise UnrecognizedMode unless MODES.include? mode
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.default_options # :nodoc:
|
75
|
+
{
|
76
|
+
:key => key,
|
77
|
+
:mode => mode
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
# You will probably never access this module directly. Instead, you'll use it through the DSL.
|
82
|
+
#
|
83
|
+
# It's mixed into any class that includes <tt>Carbon</tt>.
|
84
|
+
module ClassMethods
|
85
|
+
# Indicate that this class "emits as" an <tt>:automobile</tt>, <tt>:flight</tt>, or another of the Brighter Planet emitter classes.
|
86
|
+
#
|
87
|
+
# See the {emission estimate web service use documentation}[http://carbon.brighterplanet.com/use]
|
88
|
+
#
|
89
|
+
# For example,
|
90
|
+
# emit_as :automobile do
|
91
|
+
# provide :make
|
92
|
+
# end
|
93
|
+
def emit_as(emitter_common_name, &block)
|
94
|
+
self.carbon_base ||= ::Carbon::Base.new self, emitter_common_name
|
95
|
+
::Blockenspiel.invoke block, carbon_base
|
24
96
|
end
|
25
|
-
|
26
|
-
|
97
|
+
# japanese-style preferred
|
98
|
+
alias :emits_as :emit_as
|
99
|
+
end
|
100
|
+
|
101
|
+
# Used internally, but you can look if you want.
|
102
|
+
#
|
103
|
+
# Returns the URL to which emissions estimate queries will be POSTed.
|
104
|
+
#
|
105
|
+
# For example:
|
106
|
+
# > my_car._carbon_request_url
|
107
|
+
# => 'http://carbon.brighterplanet.com/automobiles.json'
|
108
|
+
def _carbon_request_url(options = {})
|
109
|
+
options.reverse_merge! ::Carbon.default_options
|
110
|
+
send "_#{options[:mode]}_carbon_request_url"
|
111
|
+
end
|
112
|
+
|
113
|
+
def _realtime_carbon_request_url # :nodoc:
|
114
|
+
"#{::Carbon::REALTIME_URL}/#{self.class.carbon_base.emitter_common_name.pluralize}.json"
|
115
|
+
end
|
116
|
+
|
117
|
+
def _async_carbon_request_url # :nodoc:
|
118
|
+
::Carbon::ASYNC_URL
|
119
|
+
end
|
120
|
+
|
121
|
+
# Used internally, but you can look if you want.
|
122
|
+
#
|
123
|
+
# Returns the request body that will be posted.
|
124
|
+
#
|
125
|
+
# For example:
|
126
|
+
# > my_car._carbon_request_body
|
127
|
+
# => 'fuel_efficiency=41&model=Ford+Taurus'
|
128
|
+
def _carbon_request_body(options = {})
|
129
|
+
options.reverse_merge! ::Carbon.default_options
|
130
|
+
send "_#{options[:mode]}_carbon_request_body", options
|
131
|
+
end
|
132
|
+
|
133
|
+
def _async_carbon_request_body(options) # :nodoc:
|
134
|
+
params = _carbon_request_params options
|
135
|
+
params[:emitter] = self.class.carbon_base.emitter_common_name.pluralize
|
136
|
+
raise ::Carbon::BlankCallback unless options[:callback].present?
|
137
|
+
params[:callback] = options[:callback]
|
138
|
+
params[:callback_content_type] = options[:callback_content_type] || 'application/json'
|
139
|
+
{
|
140
|
+
:Action => 'SendMessage',
|
141
|
+
:Version => '2009-02-01',
|
142
|
+
:MessageBody => params.to_query
|
143
|
+
}.to_query
|
144
|
+
end
|
145
|
+
|
146
|
+
def _realtime_carbon_request_body(options) # :nodoc:
|
147
|
+
_carbon_request_params(options).to_query
|
148
|
+
end
|
149
|
+
|
150
|
+
# Used internally, but you can look if you want.
|
151
|
+
#
|
152
|
+
# Returns the params hash that will be send to the emission estimate server.
|
153
|
+
def _carbon_request_params(options)
|
154
|
+
options.reverse_merge! ::Carbon.default_options
|
155
|
+
params = self.class.carbon_base.translation_table.inject(Hash.new) do |memo, translation|
|
156
|
+
characteristic, as = translation
|
157
|
+
current_value = send as
|
158
|
+
if current_value.present?
|
159
|
+
if characteristic.is_a? Array # [:mixer, :size]
|
160
|
+
memo[characteristic[0]] ||= Hash.new # { :mixer => Hash.new }
|
161
|
+
memo[characteristic[0]][characteristic[1]] = current_value # { :mixer => { :size => 'foo' }}
|
162
|
+
else # :oven_count
|
163
|
+
memo[characteristic] = current_value # { :oven_count => 'bar' }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
memo
|
27
167
|
end
|
168
|
+
params.merge! options.slice(:timeframe, :key)
|
169
|
+
params
|
170
|
+
end
|
171
|
+
|
172
|
+
def _realtime_emission(options = {}) # :nodoc:
|
173
|
+
response = _carbon_response options
|
174
|
+
raise ::Carbon::RealtimeEstimateFailed unless response.success?
|
175
|
+
::Carbon::EmissionEstimate.new ::ActiveSupport::JSON.decode(response.body)
|
176
|
+
end
|
177
|
+
|
178
|
+
def _async_emission(options = {}) # :nodoc:
|
179
|
+
response = _carbon_response options
|
180
|
+
raise ::Carbon::QueueingFailed unless response.success?
|
181
|
+
true
|
182
|
+
end
|
183
|
+
|
184
|
+
# Used internally, but you can look if you want.
|
185
|
+
#
|
186
|
+
# Runs the query and returns the raw response body, which will be in JSON.
|
187
|
+
#
|
188
|
+
# For example:
|
189
|
+
# > my_car._carbon_response.body
|
190
|
+
# => "{ 'emission' => 410.29, 'emission_units' => 'kilograms', [...] }"
|
191
|
+
def _carbon_response(options = {})
|
192
|
+
@last_carbon_request = ::REST::Request.new :post, ::URI.parse(_carbon_request_url(options)), _carbon_request_body(options), {'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'}
|
193
|
+
@last_carbon_response = @last_carbon_request.perform
|
194
|
+
end
|
195
|
+
|
196
|
+
# Returns an object representing the last emission estimate request.
|
197
|
+
def last_carbon_request
|
198
|
+
@last_carbon_request
|
199
|
+
end
|
200
|
+
|
201
|
+
# Returns an object representing the last emission estimate response.
|
202
|
+
def last_carbon_response
|
203
|
+
@last_carbon_response
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns an emission estimate.
|
207
|
+
#
|
208
|
+
# Note: <b>You need to take care of storing the return value to a local variable!</b> Every call to <tt>emission</tt> runs a query.
|
209
|
+
#
|
210
|
+
# You can use it like a number...
|
211
|
+
# > my_car.emission + 5.1
|
212
|
+
# => 415.39
|
213
|
+
# Or you can get information about the response
|
214
|
+
# > my_car.emission.methodology
|
215
|
+
# => 'http://carbon.brighterplanet.com/automobiles.html?[...]'
|
216
|
+
#
|
217
|
+
# === Options:
|
218
|
+
#
|
219
|
+
# * <tt>:timeframe</tt> (optional) pass an instance of Timeframe[http://github.com/rossmeissl/timeframe] to request an emission for a specific time period.
|
220
|
+
# * <tt>:callback</tt> (required in <tt>:async</tt> mode, ignored otherwise) where to POST the result when it's been calculated. You need a server waiting for it!
|
221
|
+
# * <tt>:callback_content_type</tt> (optional in <tt>:async</tt> mode, ignored otherwise) pass a MIME type like 'text/yaml' so we know how to format the result when we send it to your waiting server. Defaults to 'application/json'.
|
222
|
+
# * <tt>:mode</tt> (optional, overrides general <tt>Carbon</tt>.<tt>mode</tt> setting just for this query) If some of your queries are realtime and some are asynchronous, you can override the setting here.
|
223
|
+
# * <tt>:key</tt> (optional, overrides general <tt>Carbon</tt>.<tt>key</tt> setting just for this query) If you want to use different API keys for different queries.
|
224
|
+
def emission(options = {})
|
225
|
+
options.reverse_merge! ::Carbon.default_options
|
226
|
+
send "_#{options[:mode]}_emission", options
|
28
227
|
end
|
29
228
|
end
|
data/lib/carbon/base.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Carbon
|
2
|
+
# You will probably never access this class directly. Instead, you'll touch it through the DSL.
|
3
|
+
#
|
4
|
+
# An instance of this appears on any class that includes <tt>Carbon</tt>.
|
5
|
+
class Base
|
6
|
+
include Blockenspiel::DSL
|
7
|
+
attr_reader :klass
|
8
|
+
attr_reader :emitter_common_name
|
9
|
+
def initialize(klass, emitter_common_name)
|
10
|
+
@klass = klass
|
11
|
+
@emitter_common_name = emitter_common_name.to_s
|
12
|
+
end
|
13
|
+
# A completed translation table will look like:
|
14
|
+
# {[:mixer, :size]=>"mixer_size",
|
15
|
+
# :personnel=>:employees,
|
16
|
+
# :smokestack_size=>"smokestack_size",
|
17
|
+
# :oven_count=>"oven_count",
|
18
|
+
# [:mixer, :wattage]=>"mixer_wattage"}
|
19
|
+
def translation_table # :nodoc:
|
20
|
+
@translation_table ||= Hash.new
|
21
|
+
end
|
22
|
+
# Indicate that you will send in a piece of data about the emitter.
|
23
|
+
#
|
24
|
+
# If you don't use the <tt>:as</tt> option, the name of the getter method will be guessed.
|
25
|
+
#
|
26
|
+
# There are two optional parameters:
|
27
|
+
# * <tt>:of</tt> - the owner of a nested characteristic. So to send <tt>model[name]</tt> you write <tt>provide :name, :of => :model</tt>
|
28
|
+
# * <tt>:as</tt> - the local getter name. So if Brighter Planet expects <tt>fuel_efficiency</tt>, and you only have <tt>fuel_economy</tt>, you can write <tt>provide :fuel_efficiency, :as => :fuel_economy</tt>.
|
29
|
+
#
|
30
|
+
# For example:
|
31
|
+
#
|
32
|
+
# emit_as :automobile do
|
33
|
+
# # Official Brighter Planet characteristic name Your name for it
|
34
|
+
# provide :model, :as => :carline_class # model carline_class
|
35
|
+
# provide :name, :of => :make, :as => :mfr_name # make[name] mfr_name
|
36
|
+
# end
|
37
|
+
def provide(attr_name, options = {})
|
38
|
+
options = options.symbolize_keys
|
39
|
+
characteristic = if options.has_key? :of
|
40
|
+
# [ :mixer, :size ]
|
41
|
+
[options[:of], attr_name]
|
42
|
+
else
|
43
|
+
# :oven_count
|
44
|
+
attr_name
|
45
|
+
end
|
46
|
+
# translation_table[[:mixer,:size]] = 'mixer_size'
|
47
|
+
translation_table[characteristic] = options[:as] || Array.wrap(characteristic).join('_')
|
48
|
+
end
|
49
|
+
# japanese-style preferred
|
50
|
+
alias :provides :provide
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Carbon
|
2
|
+
# Let's start off by saying that <tt>EmissionEstimate</tt> objects quack like numbers.
|
3
|
+
#
|
4
|
+
# So, you can just say <tt>my_car.emission</tt> and you'll get something like <tt>4308.29</tt>.
|
5
|
+
#
|
6
|
+
# At the same time, they contain all the data you get back from the emission estimate web service. For example, you could say <tt>puts my_donut_factor.emission.oven_count</tt> (see the tests) and you'd get back the oven count used in the calculation, if any.
|
7
|
+
#
|
8
|
+
# Note: <b>you need to take care of storing emission estimates to local variables!</b> The gem doesn't cache these for you. Every time you call <tt>emission</tt> it will send another query to the server!
|
9
|
+
class EmissionEstimate
|
10
|
+
attr_reader :data
|
11
|
+
def initialize(data)
|
12
|
+
@data = data
|
13
|
+
@number = data['emission'].to_f.freeze
|
14
|
+
end
|
15
|
+
def ==(other) # :nodoc:
|
16
|
+
other == @number
|
17
|
+
end
|
18
|
+
# Another way to access the emission value.
|
19
|
+
# Useful if you don't like treating <tt>EmissionEstimate</tt> objects like <tt>Numeric</tt> objects (even though they do quack like numbers...)
|
20
|
+
def emission_value
|
21
|
+
@number
|
22
|
+
end
|
23
|
+
# The units of the emission.
|
24
|
+
def emission_units
|
25
|
+
data['emission_units']
|
26
|
+
end
|
27
|
+
# The Timeframe the emission estimate covers.
|
28
|
+
# > my_car.emission.timeframe.to_param
|
29
|
+
# => '2009-01-01/2010-01-01'
|
30
|
+
def timeframe
|
31
|
+
Timeframe.interval data['timeframe']
|
32
|
+
end
|
33
|
+
# Errors (and warnings) as reported in the response.
|
34
|
+
# Note: may contain HTML tags like KBD or A
|
35
|
+
def errors
|
36
|
+
data['errors']
|
37
|
+
end
|
38
|
+
# The URL of the methodology report indicating how this estimate was calculated.
|
39
|
+
# > my_car.emission.methodology
|
40
|
+
# => 'http://carbon.brighterplanet.com/automobiles.html?[...]'
|
41
|
+
def methodology
|
42
|
+
data['methodology']
|
43
|
+
end
|
44
|
+
# You can ask an EmissionEstimate object for any of the response data provided.
|
45
|
+
# This is useful for characteristics that are unique to an emitter.
|
46
|
+
#
|
47
|
+
# For example:
|
48
|
+
# > my_car.emission.model
|
49
|
+
# => 'Ford Taurus'
|
50
|
+
def method_missing(method_id, *args, &blk)
|
51
|
+
if !block_given? and args.empty? and data.has_key? method_id.to_s
|
52
|
+
data[method_id.to_s]
|
53
|
+
else
|
54
|
+
@number.send method_id, *args, &blk
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class RentalCar
|
4
|
+
include Carbon
|
5
|
+
attr_accessor :make, :model, :model_year, :fuel_economy
|
6
|
+
emit_as :automobile do
|
7
|
+
provide :make
|
8
|
+
provide :model
|
9
|
+
provide :model_year
|
10
|
+
provide :fuel_efficiency, :as => :fuel_economy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class DonutFactory
|
15
|
+
include Carbon
|
16
|
+
attr_accessor :smokestack_size, :oven_count, :mixer_size, :mixer_wattage, :employees
|
17
|
+
emit_as :factory do
|
18
|
+
provide :smokestack_size
|
19
|
+
provide :oven_count
|
20
|
+
provide :size, :of => :mixer
|
21
|
+
provide :wattage, :of => :mixer
|
22
|
+
provide :personnel, :as => :employees
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Carbon do
|
27
|
+
before(:each) do
|
28
|
+
Carbon.key = 'valid'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should be simple to use' do
|
32
|
+
c = RentalCar.new
|
33
|
+
c.model = 'Acura'
|
34
|
+
c.model_year = 2003
|
35
|
+
c.fuel_economy = 32
|
36
|
+
e = c.emission
|
37
|
+
e.should == 134.599
|
38
|
+
e.emission_units.should == 'kilograms'
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'synchronous (realtime) requests' do
|
42
|
+
before(:each) do
|
43
|
+
Carbon.mode = :realtime
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should handle complex attributes like mixer[size]' do
|
47
|
+
d = DonutFactory.new
|
48
|
+
d.mixer_size = 20
|
49
|
+
d._carbon_request_body.should =~ /mixer\[size\]=20/
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should not send attributes that are blank' do
|
53
|
+
d = DonutFactory.new
|
54
|
+
d.mixer_size = 20
|
55
|
+
d._carbon_request_body.should_not =~ /oven_count/
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should send the key' do
|
59
|
+
d = DonutFactory.new
|
60
|
+
d._carbon_request_body.should =~ /key=valid/
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should override defaults' do
|
64
|
+
d = DonutFactory.new
|
65
|
+
d.emission(:key => 'ADifferentOne')
|
66
|
+
d.last_carbon_request.body.should =~ /key=ADifferentOne/
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should accept timeframes' do
|
70
|
+
c = RentalCar.new
|
71
|
+
c.emission :timeframe => Timeframe.new(:year => 2009)
|
72
|
+
c.last_carbon_request.body.should =~ /timeframe=2009-01-01%2F2010-01-01/
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should not generate post bodies with lots of empty params' do
|
76
|
+
c = RentalCar.new
|
77
|
+
c.emission :timeframe => Timeframe.new(:year => 2009)
|
78
|
+
c.last_carbon_request.body.should_not include('&&')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'asynchronous (queued) requests' do
|
83
|
+
before(:each) do
|
84
|
+
Carbon.mode = :async
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should raise an exception if no callback is provided' do
|
88
|
+
c = RentalCar.new
|
89
|
+
lambda {
|
90
|
+
c.emission :timeframe => Timeframe.new(:year => 2009)
|
91
|
+
}.should raise_error(Carbon::BlankCallback)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should post a message to SQS' do
|
95
|
+
c = RentalCar.new
|
96
|
+
c._carbon_request_url.should =~ /queue.amazonaws.com/
|
97
|
+
c.emission :timeframe => Timeframe.new(:year => 2009), :callback => 'http://example.com/callback?id=999'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# an average car emits 6 tons of carbon in a year
|
103
|
+
# it 'should actually do a request!' do
|
104
|
+
# FakeWeb.clean_registry
|
105
|
+
# c = RentalCar.new
|
106
|
+
# c.emission.to_i.should be_close(5500, 500)
|
107
|
+
# c.emission.emission_units.should == 'kilograms'
|
108
|
+
# end
|
109
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
begin
|
4
|
+
require 'ruby-debug'
|
5
|
+
rescue
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'carbon'
|
9
|
+
|
10
|
+
require 'fakeweb'
|
11
|
+
{
|
12
|
+
'automobiles' => {
|
13
|
+
'emission' => '134.599',
|
14
|
+
'emission_units' => 'kilograms',
|
15
|
+
'methodology' => 'http://carbon.brighterplanet.com/something'
|
16
|
+
},
|
17
|
+
'factories' => {
|
18
|
+
'emission' => 1000.0,
|
19
|
+
'emission_units' => 'kilograms',
|
20
|
+
'methodology' => 'http://carbon.brighterplanet.com/something'
|
21
|
+
}
|
22
|
+
}.each do |k, v|
|
23
|
+
FakeWeb.register_uri :post, /http:\/\/carbon.brighterplanet.com\/#{k}/, :body => v.to_json
|
24
|
+
end
|
data/spec/specwatchr
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Run me with:
|
2
|
+
#
|
3
|
+
# $ watchr specs.watchr.rb
|
4
|
+
|
5
|
+
# --------------------------------------------------
|
6
|
+
# Convenience Methods
|
7
|
+
# --------------------------------------------------
|
8
|
+
def all_test_files
|
9
|
+
Dir['spec/**/*_spec.rb'] - ['spec/spec_helper.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(cmd)
|
13
|
+
puts "\e[H\e[2J" #clear console
|
14
|
+
puts(cmd)
|
15
|
+
system(cmd)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_all_tests
|
19
|
+
cmd = "spec spec"
|
20
|
+
run(cmd)
|
21
|
+
end
|
22
|
+
|
23
|
+
def run_spec(file_name)
|
24
|
+
return unless File.exist?(file_name)
|
25
|
+
file_text = File.read(file_name)
|
26
|
+
if file_text =~ /#\s*wip/
|
27
|
+
current_line = 2
|
28
|
+
exec_line = nil
|
29
|
+
file_text.each_line do |line|
|
30
|
+
if line =~ /#\s*wip/
|
31
|
+
exec_line ||= current_line
|
32
|
+
end
|
33
|
+
current_line += 1
|
34
|
+
end
|
35
|
+
run "spec #{file_name}:#{exec_line}"
|
36
|
+
else
|
37
|
+
run "spec #{file_name}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# --------------------------------------------------
|
42
|
+
# Watchr Rules
|
43
|
+
# --------------------------------------------------
|
44
|
+
watch('^spec/.+_spec.rb' ) { |m| run_spec(m[0]) }
|
45
|
+
watch('^app/(.+)/(.+)\.rb') { |m| run_spec("spec/#{m[1]}/#{m[2]}_spec.rb") }
|
46
|
+
watch('^lib/(.*)\.rb' ) { |m| run_spec("spec/lib/#{m[1]}_spec.rb") }
|
47
|
+
watch('^spec/spec_helper\.rb') { run_all_tests }
|
48
|
+
watch('actors_controller') { |m| run "spec spec/controllers" }
|
49
|
+
|
50
|
+
# --------------------------------------------------
|
51
|
+
# Signal Handling
|
52
|
+
# --------------------------------------------------
|
53
|
+
# Ctrl-\
|
54
|
+
Signal.trap('QUIT') do
|
55
|
+
puts " --- Running all tests ---\n\n"
|
56
|
+
run_all_tests
|
57
|
+
end
|
58
|
+
|
59
|
+
# Ctrl-C
|
60
|
+
Signal.trap('INT') { abort("\n") }
|
metadata
CHANGED
@@ -1,117 +1,161 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carbon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Derek Kastner
|
14
|
+
- Seamus Abshere
|
13
15
|
autorequire:
|
14
16
|
bindir: bin
|
15
17
|
cert_chain: []
|
16
18
|
|
17
|
-
date: 2010-
|
19
|
+
date: 2010-07-19 00:00:00 -05:00
|
18
20
|
default_executable:
|
19
21
|
dependencies:
|
20
22
|
- !ruby/object:Gem::Dependency
|
21
23
|
name: activesupport
|
22
24
|
prerelease: false
|
23
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
24
27
|
requirements:
|
25
28
|
- - ">="
|
26
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 299253626
|
27
31
|
segments:
|
32
|
+
- 3
|
33
|
+
- 0
|
28
34
|
- 0
|
29
|
-
|
35
|
+
- beta2
|
36
|
+
version: 3.0.0.beta2
|
30
37
|
type: :runtime
|
31
38
|
version_requirements: *id001
|
32
39
|
- !ruby/object:Gem::Dependency
|
33
|
-
name:
|
40
|
+
name: nap
|
34
41
|
prerelease: false
|
35
42
|
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
36
44
|
requirements:
|
37
45
|
- - ">="
|
38
46
|
- !ruby/object:Gem::Version
|
47
|
+
hash: 3
|
39
48
|
segments:
|
40
49
|
- 0
|
41
|
-
|
50
|
+
- 4
|
51
|
+
version: "0.4"
|
42
52
|
type: :runtime
|
43
53
|
version_requirements: *id002
|
44
54
|
- !ruby/object:Gem::Dependency
|
45
|
-
name:
|
55
|
+
name: timeframe
|
46
56
|
prerelease: false
|
47
57
|
requirement: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
48
59
|
requirements:
|
49
60
|
- - ">="
|
50
61
|
- !ruby/object:Gem::Version
|
62
|
+
hash: 19
|
51
63
|
segments:
|
52
64
|
- 0
|
53
|
-
|
54
|
-
|
65
|
+
- 0
|
66
|
+
- 6
|
67
|
+
version: 0.0.6
|
68
|
+
type: :runtime
|
55
69
|
version_requirements: *id003
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
71
|
+
name: fakeweb
|
58
72
|
prerelease: false
|
59
73
|
requirement: &id004 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
60
75
|
requirements:
|
61
76
|
- - ">="
|
62
77
|
- !ruby/object:Gem::Version
|
78
|
+
hash: 15
|
63
79
|
segments:
|
64
|
-
-
|
65
|
-
|
80
|
+
- 1
|
81
|
+
- 2
|
82
|
+
- 8
|
83
|
+
version: 1.2.8
|
66
84
|
type: :development
|
67
85
|
version_requirements: *id004
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: rspec
|
88
|
+
prerelease: false
|
89
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 62196417
|
95
|
+
segments:
|
96
|
+
- 2
|
97
|
+
- 0
|
98
|
+
- 0
|
99
|
+
- beta
|
100
|
+
- 17
|
101
|
+
version: 2.0.0.beta.17
|
102
|
+
type: :development
|
103
|
+
version_requirements: *id005
|
68
104
|
description: Carbon allows you to easily calculate the carbon footprint of various activities. This is an API for the Brighter Planet Carbon Middleware service.
|
69
|
-
email: derek@brighterplanet.com
|
105
|
+
email: derek.kastner@brighterplanet.com
|
70
106
|
executables: []
|
71
107
|
|
72
108
|
extensions: []
|
73
109
|
|
74
110
|
extra_rdoc_files:
|
75
|
-
-
|
111
|
+
- README.rdoc
|
76
112
|
files:
|
77
|
-
-
|
78
|
-
- lib/carbon/emitter/characteristic.rb
|
79
|
-
- lib/carbon/emitter/class_methods.rb
|
80
|
-
- lib/carbon/emitter/options.rb
|
81
|
-
- lib/carbon/emitter.rb
|
82
|
-
- lib/carbon.rb
|
113
|
+
- .gitignore
|
83
114
|
- MIT-LICENSE.txt
|
115
|
+
- README.rdoc
|
116
|
+
- Rakefile
|
117
|
+
- VERSION
|
118
|
+
- carbon.gemspec
|
119
|
+
- lib/carbon.rb
|
120
|
+
- lib/carbon/base.rb
|
121
|
+
- lib/carbon/emission_estimate.rb
|
122
|
+
- spec/lib/carbon_spec.rb
|
123
|
+
- spec/spec_helper.rb
|
124
|
+
- spec/specwatchr
|
84
125
|
has_rdoc: true
|
85
|
-
homepage:
|
126
|
+
homepage: http://carbon.brighterplanet.com/libraries
|
86
127
|
licenses: []
|
87
128
|
|
88
129
|
post_install_message:
|
89
|
-
rdoc_options:
|
90
|
-
|
130
|
+
rdoc_options:
|
131
|
+
- --charset=UTF-8
|
91
132
|
require_paths:
|
92
133
|
- lib
|
93
134
|
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
94
136
|
requirements:
|
95
137
|
- - ">="
|
96
138
|
- !ruby/object:Gem::Version
|
139
|
+
hash: 3
|
97
140
|
segments:
|
98
141
|
- 0
|
99
142
|
version: "0"
|
100
143
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
101
145
|
requirements:
|
102
|
-
- - "
|
146
|
+
- - ">="
|
103
147
|
- !ruby/object:Gem::Version
|
148
|
+
hash: 3
|
104
149
|
segments:
|
105
|
-
-
|
106
|
-
|
107
|
-
- 1
|
108
|
-
version: 1.3.1
|
150
|
+
- 0
|
151
|
+
version: "0"
|
109
152
|
requirements: []
|
110
153
|
|
111
154
|
rubyforge_project:
|
112
|
-
rubygems_version: 1.3.
|
155
|
+
rubygems_version: 1.3.7
|
113
156
|
signing_key:
|
114
157
|
specification_version: 3
|
115
158
|
summary: A gem for calculating carbon footprints using Brighter Planet's Carbon Middleware service
|
116
|
-
test_files:
|
117
|
-
|
159
|
+
test_files:
|
160
|
+
- spec/lib/carbon_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
@@ -1,64 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'active_support/inflector'
|
3
|
-
require 'httparty'
|
4
|
-
|
5
|
-
module Carbon
|
6
|
-
class EmissionsCalculation
|
7
|
-
class NotYetCalculated < StandardError; end
|
8
|
-
class CalculationRequestFailed < StandardError; end
|
9
|
-
|
10
|
-
include HTTParty
|
11
|
-
|
12
|
-
attr_accessor :options, :source
|
13
|
-
attr_reader :value, :methodology_url, :committees
|
14
|
-
|
15
|
-
def initialize(options, source)
|
16
|
-
self.options = options
|
17
|
-
self.source = source
|
18
|
-
end
|
19
|
-
|
20
|
-
def methodology_url
|
21
|
-
raise NotYetCalculated if result.nil?
|
22
|
-
@methodology_url
|
23
|
-
end
|
24
|
-
def value
|
25
|
-
raise NotYetCalculated if result.nil?
|
26
|
-
@value
|
27
|
-
end
|
28
|
-
|
29
|
-
def calculate!
|
30
|
-
fetch_calculation
|
31
|
-
@value = result['emission']
|
32
|
-
@methodology_url = result['methodology']
|
33
|
-
@committees = result['committees']
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
def result
|
38
|
-
@result
|
39
|
-
end
|
40
|
-
|
41
|
-
def fields
|
42
|
-
fields_hash = options.characteristics.inject({}) do |hsh, characteristic|
|
43
|
-
value = source.send(characteristic.field)
|
44
|
-
hsh[characteristic.name.to_sym] = value if value
|
45
|
-
hsh
|
46
|
-
end
|
47
|
-
{ :body => { options.emitter_type => fields_hash } }
|
48
|
-
end
|
49
|
-
|
50
|
-
def fetch_calculation
|
51
|
-
url = URI.join(Carbon.base_url, options.emitter_type.to_s.pluralize)
|
52
|
-
options = fields.merge(:headers => { 'Accept' => 'application/json' })
|
53
|
-
response = self.class.post(url.to_s, options)
|
54
|
-
|
55
|
-
unless (200..399).include?(response.code)
|
56
|
-
raise CalculationRequestFailed, response.body
|
57
|
-
end
|
58
|
-
|
59
|
-
puts response.body if Carbon.debug
|
60
|
-
|
61
|
-
@result = JSON.parse(response.body)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
data/lib/carbon/emitter.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'carbon/emitter/class_methods'
|
2
|
-
|
3
|
-
module Carbon
|
4
|
-
module Emitter
|
5
|
-
class NotYetCalculated < StandardError; end
|
6
|
-
|
7
|
-
def self.included(target)
|
8
|
-
target.extend Carbon::Emitter::ClassMethods
|
9
|
-
end
|
10
|
-
|
11
|
-
def emission
|
12
|
-
return @emission unless @emission.nil?
|
13
|
-
@emission = Carbon::EmissionsCalculation.new(self.class.emitter_options, self)
|
14
|
-
@emission.calculate!
|
15
|
-
@emission
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Carbon
|
2
|
-
module Emitter
|
3
|
-
class Characteristic
|
4
|
-
attr_accessor :name, :field
|
5
|
-
|
6
|
-
def self.from_options_hash(name, options)
|
7
|
-
new(:name => name, :field => options[:with])
|
8
|
-
end
|
9
|
-
|
10
|
-
def initialize(options = {})
|
11
|
-
options.each { |name, value| self.send("#{name}=", value) unless value.nil? }
|
12
|
-
end
|
13
|
-
|
14
|
-
def field
|
15
|
-
@field ||= self.name
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'carbon/emitter/options'
|
2
|
-
|
3
|
-
module Carbon
|
4
|
-
module Emitter
|
5
|
-
module ClassMethods
|
6
|
-
def emits_as(emitter_type, &blk)
|
7
|
-
self.emitter_type = emitter_type
|
8
|
-
|
9
|
-
self.emitter_options = Carbon::Emitter::Options.new(emitter_type)
|
10
|
-
self.emitter_options.instance_eval &blk if block_given?
|
11
|
-
end
|
12
|
-
|
13
|
-
def emitter_type; @emitter_type; end
|
14
|
-
def emitter_type=(val); @emitter_type = val; end
|
15
|
-
|
16
|
-
def emitter_options; @emitter_options; end
|
17
|
-
def emitter_options=(val); @emitter_options = val; end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'carbon/emitter/characteristic'
|
2
|
-
|
3
|
-
module Carbon
|
4
|
-
module Emitter
|
5
|
-
class Options
|
6
|
-
attr_accessor :emitter_type, :characteristics
|
7
|
-
|
8
|
-
def initialize(emitter_type)
|
9
|
-
self.emitter_type = emitter_type.to_sym
|
10
|
-
end
|
11
|
-
|
12
|
-
def keys
|
13
|
-
characteristics.map { |c| c.name }
|
14
|
-
end
|
15
|
-
|
16
|
-
def [](name)
|
17
|
-
characteristics.find { |c| c.name == name }
|
18
|
-
end
|
19
|
-
|
20
|
-
def characteristics
|
21
|
-
@characteristics ||= []
|
22
|
-
end
|
23
|
-
|
24
|
-
def provides(characteristic_name, options = {})
|
25
|
-
characteristics <<
|
26
|
-
Carbon::Emitter::Characteristic.from_options_hash(characteristic_name, options)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|