carbon 0.1.4 → 0.1.5
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/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
|