carbon 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +19 -28
- data/Rakefile +4 -3
- data/VERSION +1 -1
- data/carbon.gemspec +7 -7
- data/lib/carbon.rb +16 -34
- data/spec/lib/carbon_spec.rb +1 -16
- data/spec/spec_helper.rb +2 -0
- metadata +10 -11
data/README.rdoc
CHANGED
@@ -6,17 +6,19 @@ By querying the web service, it can estimate the carbon emissions of many real-l
|
|
6
6
|
|
7
7
|
== Quick start
|
8
8
|
|
9
|
-
|
9
|
+
*You'll need a Brighter Planet API key. See the "API keys" section below for details.*
|
10
|
+
|
11
|
+
First get the gem:
|
10
12
|
|
11
13
|
$ gem install carbon
|
12
14
|
|
13
|
-
Carbon works by extending any Ruby class you're using to represent an emission source. For instance, let's say you have a
|
15
|
+
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 carbon.)
|
14
16
|
|
15
17
|
class RentalCar
|
16
18
|
attr_accessor :year, :make, :model, :fuel_efficiency, :daily_distance_average, :purchase_date, :retirement_date
|
17
19
|
end
|
18
20
|
|
19
|
-
In order to calculate carbon emissions, we need to map the car's relevant attributes to
|
21
|
+
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:
|
20
22
|
|
21
23
|
class RentalCar
|
22
24
|
include Carbon
|
@@ -32,7 +34,7 @@ In order to calculate carbon emissions, we need to map the car's relevant attrib
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
When you want to calculate emissions, simply call <tt>RentalCar</tt>#<tt>emission</tt
|
37
|
+
When you want to calculate emissions, simply call <tt>RentalCar</tt>#<tt>emission</tt>. Memoize this method on your class or store the result; <tt>#emision</tt> makes a web service call every time it is invoked.
|
36
38
|
|
37
39
|
> my_car = RentalCar.new([...])
|
38
40
|
=> #<RentalCar [...]>
|
@@ -44,10 +46,20 @@ When you want to calculate emissions, simply call <tt>RentalCar</tt>#<tt>emissio
|
|
44
46
|
=> "kilograms"
|
45
47
|
> my_emission.methodology
|
46
48
|
=> "http://carbon.brighterplanet.com/automobiles.html?[...]"
|
49
|
+
|
50
|
+
=== Asynchronous queries
|
51
|
+
|
52
|
+
To request an emission estimate asynchronously, simply pass an URL as the +:callback+ option to +#emission+:
|
53
|
+
|
54
|
+
> RentalCar.new.emission :callback => http://example.com/my/callback/handler
|
55
|
+
|
56
|
+
A good way to test this is to set up a {PostBin}[http://postbin.org]
|
47
57
|
|
48
|
-
|
58
|
+
== Documentation
|
49
59
|
|
50
|
-
|
60
|
+
Read the {carbon gem RDoc}[http://rdoc.info/projects/brighterplanet/carbon] for more.
|
61
|
+
|
62
|
+
== Exceptions
|
51
63
|
|
52
64
|
Since this gem connects to a web service, you need to be ready for network problems and latency. For example:
|
53
65
|
|
@@ -71,7 +83,7 @@ Since this gem connects to a web service, you need to be ready for network probl
|
|
71
83
|
# Please contact us at staff@brighterplanet.com if you see too many of these.
|
72
84
|
end
|
73
85
|
|
74
|
-
==
|
86
|
+
== API keys
|
75
87
|
|
76
88
|
You should get an API key from http://keys.brighterplanet.com and set it globally:
|
77
89
|
|
@@ -79,27 +91,6 @@ You should get an API key from http://keys.brighterplanet.com and set it globall
|
|
79
91
|
|
80
92
|
Now all of your queries will use that key.
|
81
93
|
|
82
|
-
== A note on modes
|
83
|
-
|
84
|
-
When you send in a query, you have two options for how you want the result back:
|
85
|
-
|
86
|
-
* realtime - you get the answer back immediately. More expensive.
|
87
|
-
* async - the answer is POSTed back to a URL that you specify. You must have a server waiting for it! Cheaper.
|
88
|
-
|
89
|
-
The default is realtime:
|
90
|
-
|
91
|
-
> my_emission = RentalCar.new.emission
|
92
|
-
=> #<Carbon::EmissionEstimate [...]>
|
93
|
-
|
94
|
-
A good way to test the "async" mode is to set up a {PostBin}[http://postbin.org]
|
95
|
-
|
96
|
-
> RentalCar.new.emission :mode => :async, :callback => 'http://postbin.org/1dj0145'
|
97
|
-
=> true # useless, but go check out http://postbin.org/1dj0145
|
98
|
-
|
99
|
-
You can set the mode globally with
|
100
|
-
|
101
|
-
Carbon.mode = :async
|
102
|
-
|
103
94
|
== Copyright
|
104
95
|
|
105
96
|
Copyright (c) 2010 Brighter Planet.
|
data/Rakefile
CHANGED
@@ -10,12 +10,13 @@ begin
|
|
10
10
|
gemspec.email = 'derek.kastner@brighterplanet.com'
|
11
11
|
gemspec.homepage = 'http://carbon.brighterplanet.com/libraries'
|
12
12
|
gemspec.authors = ['Derek Kastner', 'Seamus Abshere']
|
13
|
-
gemspec.add_dependency 'activesupport', '>=3.
|
13
|
+
gemspec.add_dependency 'activesupport', '>=2.3.5'
|
14
14
|
gemspec.add_dependency 'nap', '>=0.4'
|
15
|
-
gemspec.add_dependency 'timeframe', '>=0.0.
|
15
|
+
gemspec.add_dependency 'timeframe', '>=0.0.7'
|
16
16
|
|
17
17
|
gemspec.add_development_dependency 'fakeweb', '>=1.2.8'
|
18
18
|
# sabshere 7/16/10 if you're having trouble running specs, try "rspec spec" and/or "sudo gem install rspec --pre"
|
19
|
+
# sabshere 7/20/10 this might not work with activesupport 2
|
19
20
|
gemspec.add_development_dependency 'rspec', '>=2.0.0.beta.17'
|
20
21
|
end
|
21
22
|
Jeweler::GemcutterTasks.new
|
@@ -52,7 +53,7 @@ Rake::RDocTask.new do |rdoc|
|
|
52
53
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
53
54
|
|
54
55
|
rdoc.rdoc_dir = 'rdoc'
|
55
|
-
rdoc.title = "
|
56
|
+
rdoc.title = "carbon #{version}"
|
56
57
|
rdoc.rdoc_files.include('README*')
|
57
58
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
58
59
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.5
|
data/carbon.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{carbon}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Derek Kastner", "Seamus Abshere"]
|
@@ -44,22 +44,22 @@ Gem::Specification.new do |s|
|
|
44
44
|
s.specification_version = 3
|
45
45
|
|
46
46
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
-
s.add_runtime_dependency(%q<activesupport>, [">= 3.
|
47
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
|
48
48
|
s.add_runtime_dependency(%q<nap>, [">= 0.4"])
|
49
|
-
s.add_runtime_dependency(%q<timeframe>, [">= 0.0.
|
49
|
+
s.add_runtime_dependency(%q<timeframe>, [">= 0.0.7"])
|
50
50
|
s.add_development_dependency(%q<fakeweb>, [">= 1.2.8"])
|
51
51
|
s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.17"])
|
52
52
|
else
|
53
|
-
s.add_dependency(%q<activesupport>, [">= 3.
|
53
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
54
54
|
s.add_dependency(%q<nap>, [">= 0.4"])
|
55
|
-
s.add_dependency(%q<timeframe>, [">= 0.0.
|
55
|
+
s.add_dependency(%q<timeframe>, [">= 0.0.7"])
|
56
56
|
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
|
57
57
|
s.add_dependency(%q<rspec>, [">= 2.0.0.beta.17"])
|
58
58
|
end
|
59
59
|
else
|
60
|
-
s.add_dependency(%q<activesupport>, [">= 3.
|
60
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
61
61
|
s.add_dependency(%q<nap>, [">= 0.4"])
|
62
|
-
s.add_dependency(%q<timeframe>, [">= 0.0.
|
62
|
+
s.add_dependency(%q<timeframe>, [">= 0.0.7"])
|
63
63
|
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
|
64
64
|
s.add_dependency(%q<rspec>, [">= 2.0.0.beta.17"])
|
65
65
|
end
|
data/lib/carbon.rb
CHANGED
@@ -2,6 +2,7 @@ require 'uri'
|
|
2
2
|
require 'blockenspiel'
|
3
3
|
require 'rest' # provided by nap gem
|
4
4
|
require 'timeframe'
|
5
|
+
require 'active_support/version'
|
5
6
|
%w{
|
6
7
|
active_support/core_ext/module/attribute_accessors
|
7
8
|
active_support/core_ext/class/attribute_accessors
|
@@ -13,7 +14,7 @@ require 'timeframe'
|
|
13
14
|
active_support/json/decoding
|
14
15
|
}.each do |active_support_3_requirement|
|
15
16
|
require active_support_3_requirement
|
16
|
-
end
|
17
|
+
end if ActiveSupport::VERSION::MAJOR == 3
|
17
18
|
require 'carbon/base'
|
18
19
|
require 'carbon/emission_estimate'
|
19
20
|
|
@@ -42,12 +43,9 @@ module Carbon
|
|
42
43
|
klass.extend ClassMethods
|
43
44
|
end
|
44
45
|
|
45
|
-
MODES = [ :realtime, :async ]
|
46
46
|
REALTIME_URL = 'http://carbon.brighterplanet.com'
|
47
47
|
ASYNC_URL = 'https://queue.amazonaws.com/121562143717/cm1_production_incoming'
|
48
48
|
|
49
|
-
class UnrecognizedMode < ArgumentError # :nodoc:
|
50
|
-
end
|
51
49
|
class BlankCallback < ArgumentError # :nodoc:
|
52
50
|
end
|
53
51
|
class RealtimeEstimateFailed < RuntimeError # :nodoc:
|
@@ -59,30 +57,15 @@ module Carbon
|
|
59
57
|
|
60
58
|
# The api key obtained from http://keys.brighterplanet.com
|
61
59
|
mattr_accessor :key
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
_mode || :realtime
|
67
|
-
end
|
68
|
-
# Set the current mode.
|
69
|
-
# * Realtime mode (<tt>:realtime</tt>) means you get the answer back immediately.
|
70
|
-
# * 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!
|
71
|
-
def self.mode=(str)
|
72
|
-
self._mode = str.to_sym
|
73
|
-
raise UnrecognizedMode unless MODES.include? mode
|
74
|
-
end
|
75
|
-
|
76
|
-
def self.default_options # :nodoc:
|
77
|
-
{
|
78
|
-
:key => key,
|
79
|
-
:mode => mode
|
80
|
-
}
|
60
|
+
|
61
|
+
def self.prepare_options(options) # :nodoc:
|
62
|
+
options[:key] ||= key
|
63
|
+
options[:mode] ||= options.has_key?(:callback) ? :async : :realtime
|
81
64
|
end
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
65
|
+
|
66
|
+
# You will probably never access this module directly. Instead, you'll use it through the DSL.
|
67
|
+
#
|
68
|
+
# It's mixed into any class that includes <tt>Carbon</tt>.
|
86
69
|
module ClassMethods
|
87
70
|
# Indicate that this class "emits as" an <tt>:automobile</tt>, <tt>:flight</tt>, or another of the Brighter Planet emitter classes.
|
88
71
|
#
|
@@ -108,7 +91,7 @@ module Carbon
|
|
108
91
|
# > my_car._carbon_request_url
|
109
92
|
# => 'http://carbon.brighterplanet.com/automobiles.json'
|
110
93
|
def _carbon_request_url(options = {})
|
111
|
-
|
94
|
+
::Carbon.prepare_options options
|
112
95
|
send "_#{options[:mode]}_carbon_request_url"
|
113
96
|
end
|
114
97
|
|
@@ -128,7 +111,7 @@ module Carbon
|
|
128
111
|
# > my_car._carbon_request_body
|
129
112
|
# => 'fuel_efficiency=41&model=Ford+Taurus'
|
130
113
|
def _carbon_request_body(options = {})
|
131
|
-
|
114
|
+
::Carbon.prepare_options options
|
132
115
|
send "_#{options[:mode]}_carbon_request_body", options
|
133
116
|
end
|
134
117
|
|
@@ -153,7 +136,7 @@ module Carbon
|
|
153
136
|
#
|
154
137
|
# Returns the params hash that will be send to the emission estimate server.
|
155
138
|
def _carbon_request_params(options)
|
156
|
-
|
139
|
+
::Carbon.prepare_options options
|
157
140
|
params = self.class.carbon_base.translation_table.inject(Hash.new) do |memo, translation|
|
158
141
|
characteristic, as = translation
|
159
142
|
current_value = send as
|
@@ -233,12 +216,11 @@ module Carbon
|
|
233
216
|
# === Options:
|
234
217
|
#
|
235
218
|
# * <tt>:timeframe</tt> (optional) pass an instance of Timeframe[http://github.com/rossmeissl/timeframe] to request an emission for a specific time period.
|
236
|
-
# * <tt>:callback</tt> (
|
237
|
-
# * <tt>:callback_content_type</tt> (optional
|
238
|
-
# * <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.
|
219
|
+
# * <tt>:callback</tt> (optional) where to POST the result when it's been calculated. You need a server waiting for it!
|
220
|
+
# * <tt>:callback_content_type</tt> (optional if <tt>:callback</tt> is specified, 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'.
|
239
221
|
# * <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.
|
240
222
|
def emission(options = {})
|
241
|
-
|
223
|
+
::Carbon.prepare_options options
|
242
224
|
send "_#{options[:mode]}_emission", options
|
243
225
|
end
|
244
226
|
end
|
data/spec/lib/carbon_spec.rb
CHANGED
@@ -39,10 +39,6 @@ describe Carbon do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
describe 'synchronous (realtime) requests' do
|
42
|
-
before(:each) do
|
43
|
-
Carbon.mode = :realtime
|
44
|
-
end
|
45
|
-
|
46
42
|
it 'should handle complex attributes like mixer[size]' do
|
47
43
|
d = DonutFactory.new
|
48
44
|
d.mixer_size = 20
|
@@ -80,20 +76,9 @@ describe Carbon do
|
|
80
76
|
end
|
81
77
|
|
82
78
|
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
79
|
it 'should post a message to SQS' do
|
95
80
|
c = RentalCar.new
|
96
|
-
c._carbon_request_url.should =~ /queue.amazonaws.com/
|
81
|
+
c._carbon_request_url(:callback => 'http://www.postbin.org/1dj0146').should =~ /queue.amazonaws.com/
|
97
82
|
c.emission :timeframe => Timeframe.new(:year => 2009), :callback => 'http://www.postbin.org/1dj0146'
|
98
83
|
end
|
99
84
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carbon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 5
|
10
|
+
version: 0.1.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Derek Kastner
|
@@ -27,13 +27,12 @@ dependencies:
|
|
27
27
|
requirements:
|
28
28
|
- - ">="
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
hash:
|
30
|
+
hash: 9
|
31
31
|
segments:
|
32
|
+
- 2
|
32
33
|
- 3
|
33
|
-
-
|
34
|
-
|
35
|
-
- beta2
|
36
|
-
version: 3.0.0.beta2
|
34
|
+
- 5
|
35
|
+
version: 2.3.5
|
37
36
|
type: :runtime
|
38
37
|
version_requirements: *id001
|
39
38
|
- !ruby/object:Gem::Dependency
|
@@ -59,12 +58,12 @@ dependencies:
|
|
59
58
|
requirements:
|
60
59
|
- - ">="
|
61
60
|
- !ruby/object:Gem::Version
|
62
|
-
hash:
|
61
|
+
hash: 17
|
63
62
|
segments:
|
64
63
|
- 0
|
65
64
|
- 0
|
66
|
-
-
|
67
|
-
version: 0.0.
|
65
|
+
- 7
|
66
|
+
version: 0.0.7
|
68
67
|
type: :runtime
|
69
68
|
version_requirements: *id003
|
70
69
|
- !ruby/object:Gem::Dependency
|