carbon 0.0.7 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|