carbon 0.2.5 → 0.2.6
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/VERSION +1 -1
- data/carbon.gemspec +5 -3
- data/lib/carbon.rb +1 -0
- data/lib/carbon/base.rb +3 -0
- data/lib/carbon/emission_estimate.rb +27 -9
- data/lib/carbon/emission_estimate/request.rb +9 -0
- data/lib/carbon/emission_estimate/response.rb +16 -7
- data/lib/carbon/emission_estimate/storage.rb +4 -0
- data/spec/lib/carbon/emission_estimate/response_spec.rb +34 -0
- data/spec/lib/carbon_spec.rb +3 -3
- data/spec/spec_helper.rb +4 -4
- metadata +6 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.6
|
data/carbon.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{carbon}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.6"
|
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", "Andy Rossmeissl"]
|
12
|
-
s.date = %q{2010-08-
|
12
|
+
s.date = %q{2010-08-18}
|
13
13
|
s.description = %q{Carbon is a Ruby API wrapper for the Brighter Planet emission estimate web service (http://carbon.brighterplanet.com). By querying the web service, it can estimate the carbon emissions of many real-life objects, such as cars and houses, based on particular attributes that they may have.}
|
14
14
|
s.email = %q{derek.kastner@brighterplanet.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
|
|
33
33
|
"lib/carbon/emission_estimate/request.rb",
|
34
34
|
"lib/carbon/emission_estimate/response.rb",
|
35
35
|
"lib/carbon/emission_estimate/storage.rb",
|
36
|
+
"spec/lib/carbon/emission_estimate/response_spec.rb",
|
36
37
|
"spec/lib/carbon_spec.rb",
|
37
38
|
"spec/spec_helper.rb",
|
38
39
|
"spec/specwatchr"
|
@@ -43,7 +44,8 @@ Gem::Specification.new do |s|
|
|
43
44
|
s.rubygems_version = %q{1.3.7}
|
44
45
|
s.summary = %q{Carbon is a Ruby API wrapper for the Brighter Planet emission estimate web service (http://carbon.brighterplanet.com).}
|
45
46
|
s.test_files = [
|
46
|
-
"spec/lib/
|
47
|
+
"spec/lib/carbon/emission_estimate/response_spec.rb",
|
48
|
+
"spec/lib/carbon_spec.rb",
|
47
49
|
"spec/spec_helper.rb"
|
48
50
|
]
|
49
51
|
|
data/lib/carbon.rb
CHANGED
@@ -5,6 +5,7 @@ require 'digest/sha1'
|
|
5
5
|
require 'rest' # provided by nap gem
|
6
6
|
gem 'SystemTimer' # >=1.2, so as not to be confused with system_timer 1.0
|
7
7
|
require 'system_timer'
|
8
|
+
require 'active_support'
|
8
9
|
require 'active_support/version'
|
9
10
|
%w{
|
10
11
|
active_support/core_ext/module/attribute_accessors
|
data/lib/carbon/base.rb
CHANGED
@@ -10,6 +10,7 @@ module Carbon
|
|
10
10
|
@klass = klass
|
11
11
|
@emitter_common_name = emitter_common_name.to_s
|
12
12
|
end
|
13
|
+
|
13
14
|
# A completed translation table will look like:
|
14
15
|
# {[:mixer, :size]=>"mixer_size",
|
15
16
|
# :personnel=>:employees,
|
@@ -19,6 +20,7 @@ module Carbon
|
|
19
20
|
def translation_table # :nodoc:
|
20
21
|
@translation_table ||= Hash.new
|
21
22
|
end
|
23
|
+
|
22
24
|
# Indicate that you will send in a piece of data about the emitter.
|
23
25
|
#
|
24
26
|
# Two general rules:
|
@@ -50,6 +52,7 @@ module Carbon
|
|
50
52
|
# translation_table[:make] = 'epa_code'
|
51
53
|
translation_table[characteristic] = options[:key]
|
52
54
|
end
|
55
|
+
|
53
56
|
# Third-person singular preferred.
|
54
57
|
alias :provides :provide
|
55
58
|
end
|
@@ -17,6 +17,15 @@ module Carbon
|
|
17
17
|
data['updated_at'] = ::Time.parse(data['updated_at']) if data.has_key? 'updated_at'
|
18
18
|
data
|
19
19
|
end
|
20
|
+
|
21
|
+
attr_writer :callback_content_type
|
22
|
+
attr_writer :key
|
23
|
+
attr_writer :timeout
|
24
|
+
attr_writer :defer
|
25
|
+
attr_accessor :callback
|
26
|
+
attr_accessor :timeframe
|
27
|
+
attr_accessor :guid
|
28
|
+
attr_reader :emitter
|
20
29
|
|
21
30
|
def initialize(emitter)
|
22
31
|
@emitter = emitter
|
@@ -55,14 +64,7 @@ module Carbon
|
|
55
64
|
super
|
56
65
|
end
|
57
66
|
end
|
58
|
-
|
59
|
-
attr_writer :key
|
60
|
-
attr_writer :timeout
|
61
|
-
attr_writer :defer
|
62
|
-
attr_accessor :callback
|
63
|
-
attr_accessor :timeframe
|
64
|
-
attr_accessor :guid
|
65
|
-
attr_reader :emitter
|
67
|
+
|
66
68
|
def data
|
67
69
|
if storage.present?
|
68
70
|
storage.data
|
@@ -70,58 +72,74 @@ module Carbon
|
|
70
72
|
response.data
|
71
73
|
end
|
72
74
|
end
|
75
|
+
|
73
76
|
def storage
|
74
77
|
@storage ||= {}
|
75
78
|
return @storage[guid] if @storage.has_key? guid
|
76
79
|
@storage[guid] = Storage.new self
|
77
80
|
end
|
81
|
+
|
78
82
|
def request
|
79
83
|
@request ||= Request.new self
|
80
84
|
end
|
85
|
+
|
81
86
|
# Here's where caching takes place.
|
82
87
|
def response
|
83
88
|
current_params = request.params
|
84
89
|
@response ||= {}
|
85
90
|
return @response[current_params] if @response.has_key? current_params
|
86
|
-
|
91
|
+
response_object = Response.new self
|
92
|
+
response_object.load_data
|
93
|
+
@response[current_params] = response_object
|
87
94
|
end
|
95
|
+
|
88
96
|
def defer?
|
89
97
|
@defer == true
|
90
98
|
end
|
99
|
+
|
91
100
|
def async?
|
92
101
|
callback or defer?
|
93
102
|
end
|
103
|
+
|
94
104
|
def mode
|
95
105
|
async? ? :async : :realtime
|
96
106
|
end
|
107
|
+
|
97
108
|
# Timeout on realtime requests in seconds, if desired.
|
98
109
|
def timeout
|
99
110
|
@timeout
|
100
111
|
end
|
112
|
+
|
101
113
|
def callback_content_type
|
102
114
|
@callback_content_type || 'application/json'
|
103
115
|
end
|
116
|
+
|
104
117
|
def key
|
105
118
|
@key || ::Carbon.key
|
106
119
|
end
|
120
|
+
|
107
121
|
# The timeframe being looked at in the emission calculation.
|
108
122
|
def active_subtimeframe
|
109
123
|
data['active_subtimeframe']
|
110
124
|
end
|
125
|
+
|
111
126
|
# Another way to access the emission value.
|
112
127
|
# Useful if you don't like treating <tt>EmissionEstimate</tt> objects like <tt>Numeric</tt> objects (even though they do quack like numbers...)
|
113
128
|
def number
|
114
129
|
async? ? nil : data['emission'].to_f.freeze
|
115
130
|
end
|
131
|
+
|
116
132
|
# The units of the emission.
|
117
133
|
def emission_units
|
118
134
|
data['emission_units']
|
119
135
|
end
|
136
|
+
|
120
137
|
# Errors (and warnings) as reported in the response.
|
121
138
|
# Note: may contain HTML tags like KBD or A
|
122
139
|
def errors
|
123
140
|
data['errors']
|
124
141
|
end
|
142
|
+
|
125
143
|
# The URL of the methodology report indicating how this estimate was calculated.
|
126
144
|
# > my_car.emission_estimate.methodology
|
127
145
|
# => 'http://carbon.brighterplanet.com/automobiles.html?[...]'
|
@@ -2,15 +2,19 @@ module Carbon
|
|
2
2
|
class EmissionEstimate
|
3
3
|
class Request
|
4
4
|
attr_reader :parent
|
5
|
+
|
5
6
|
def initialize(parent)
|
6
7
|
@parent = parent
|
7
8
|
end
|
9
|
+
|
8
10
|
def body
|
9
11
|
params.to_query
|
10
12
|
end
|
13
|
+
|
11
14
|
def params
|
12
15
|
send "#{parent.mode}_params"
|
13
16
|
end
|
17
|
+
|
14
18
|
def async_params # :nodoc:
|
15
19
|
raise ::ArgumentError, "When using :callback you cannot specify :defer" if parent.defer? and parent.callback
|
16
20
|
raise ::ArgumentError, "When using :defer => true you must specify :guid" if parent.defer? and parent.guid.blank?
|
@@ -25,9 +29,11 @@ module Carbon
|
|
25
29
|
:MessageBody => hash.to_query
|
26
30
|
}
|
27
31
|
end
|
32
|
+
|
28
33
|
def realtime_params # :nodoc:
|
29
34
|
_params
|
30
35
|
end
|
36
|
+
|
31
37
|
# Used internally, but you can look if you want.
|
32
38
|
#
|
33
39
|
# Returns the params hash that will be send to the emission estimate server.
|
@@ -60,12 +66,15 @@ module Carbon
|
|
60
66
|
hash[:key] = parent.key if parent.key
|
61
67
|
hash
|
62
68
|
end
|
69
|
+
|
63
70
|
def realtime_url # :nodoc:
|
64
71
|
"#{::Carbon::REALTIME_URL}/#{parent.emitter.class.carbon_base.emitter_common_name.pluralize}.json"
|
65
72
|
end
|
73
|
+
|
66
74
|
def async_url # :nodoc:
|
67
75
|
::Carbon::ASYNC_URL
|
68
76
|
end
|
77
|
+
|
69
78
|
def url
|
70
79
|
send "#{parent.mode}_url"
|
71
80
|
end
|
@@ -5,17 +5,23 @@ module Carbon
|
|
5
5
|
attr_reader :data
|
6
6
|
attr_reader :raw_request
|
7
7
|
attr_reader :raw_response
|
8
|
+
|
8
9
|
def initialize(parent)
|
9
10
|
@parent = parent
|
11
|
+
end
|
12
|
+
|
13
|
+
def load_data
|
10
14
|
send "load_#{parent.mode}_data"
|
11
15
|
end
|
16
|
+
|
17
|
+
private
|
12
18
|
def load_realtime_data # :nodoc:
|
13
19
|
attempts = 0
|
14
20
|
response = nil
|
15
21
|
begin
|
16
22
|
response = perform
|
17
|
-
raise ::Carbon::RateLimited if response.status_code == 403 and response.body =~ /Rate Limit/i
|
18
|
-
rescue ::Carbon::RateLimited
|
23
|
+
raise ::Carbon::RateLimited if response.status_code == 403 and response.body =~ /Rate Limit/i #TODO: Should we expect an HTTP 402: payment required, instead?
|
24
|
+
rescue ::Carbon::RateLimited, EOFError
|
19
25
|
if attempts < 4
|
20
26
|
attempts += 1
|
21
27
|
sleep 0.2 * attempts
|
@@ -24,26 +30,29 @@ module Carbon
|
|
24
30
|
raise $!
|
25
31
|
end
|
26
32
|
end
|
27
|
-
raise ::Carbon::RealtimeEstimateFailed unless response.success?
|
33
|
+
raise ::Carbon::RealtimeEstimateFailed unless response.success? #TODO: should we expect 300s as well as 200s? Also, we may want to include response code and body in our exception.
|
28
34
|
@data = ::Carbon::EmissionEstimate.parse response.body
|
29
35
|
end
|
36
|
+
|
30
37
|
def load_async_data # :nodoc:
|
31
38
|
response = perform
|
32
|
-
raise ::Carbon::QueueingFailed unless response.success?
|
39
|
+
raise ::Carbon::QueueingFailed unless response.success? #TODO: should we expect 300s as well as 200s? Also, we may want to include response code and body in our exception.
|
33
40
|
@data = {}
|
34
41
|
end
|
42
|
+
|
35
43
|
def perform # :nodoc:
|
36
44
|
response = nil
|
37
45
|
if parent.timeout
|
38
46
|
::SystemTimer.timeout_after(parent.timeout) do
|
39
|
-
response =
|
47
|
+
response = perform_request
|
40
48
|
end
|
41
49
|
else
|
42
|
-
response =
|
50
|
+
response = perform_request
|
43
51
|
end
|
44
52
|
response
|
45
53
|
end
|
46
|
-
|
54
|
+
|
55
|
+
def perform_request # :nodoc:
|
47
56
|
@raw_request = ::REST::Request.new :post, ::URI.parse(parent.request.url), parent.request.body, {'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'}
|
48
57
|
@raw_response = raw_request.perform
|
49
58
|
end
|
@@ -4,15 +4,19 @@ module Carbon
|
|
4
4
|
attr_accessor :parent
|
5
5
|
attr_reader :raw_request
|
6
6
|
attr_reader :raw_response
|
7
|
+
|
7
8
|
def initialize(parent)
|
8
9
|
@parent = parent
|
9
10
|
end
|
11
|
+
|
10
12
|
def url
|
11
13
|
"#{::Carbon::STORAGE_URL}/#{::Digest::SHA1.hexdigest(parent.key+parent.guid)}"
|
12
14
|
end
|
15
|
+
|
13
16
|
def present?
|
14
17
|
parent.guid.present? and data.present?
|
15
18
|
end
|
19
|
+
|
16
20
|
def data
|
17
21
|
return @data[0] if @data.is_a? ::Array
|
18
22
|
@raw_request = ::REST::Request.new :get, ::URI.parse(url)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Carbon::EmissionEstimate::Response do
|
4
|
+
let(:parent) { mock(Object, :mode => :realtime) }
|
5
|
+
let(:response) { Carbon::EmissionEstimate::Response.new parent }
|
6
|
+
let(:rest_response) { mock(Object, :success? => true, :status_code => 200,
|
7
|
+
:body => 'OK') }
|
8
|
+
|
9
|
+
describe '#load_realtime_data' do
|
10
|
+
before do
|
11
|
+
::Carbon::EmissionEstimate.stub! :parse
|
12
|
+
response.stub! :sleep
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should retry a request that is rate limited' do
|
16
|
+
response.should_receive(:perform).once.ordered.
|
17
|
+
and_raise ::Carbon::RateLimited
|
18
|
+
response.should_receive(:perform).once.ordered.
|
19
|
+
and_return rest_response
|
20
|
+
|
21
|
+
response.send :load_realtime_data
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should retry a request that gets cut off (server disappears mid-request)' do
|
25
|
+
response.should_receive(:perform).once.ordered.
|
26
|
+
and_raise EOFError
|
27
|
+
response.should_receive(:perform).once.ordered.
|
28
|
+
and_return rest_response
|
29
|
+
|
30
|
+
response.send :load_realtime_data
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/spec/lib/carbon_spec.rb
CHANGED
@@ -130,11 +130,11 @@ module Carbon
|
|
130
130
|
attr_accessor :sleep_before_performing
|
131
131
|
VALID_OPTIONS.push :sleep_before_performing
|
132
132
|
class Response
|
133
|
-
def
|
133
|
+
def perform_request_with_delay
|
134
134
|
sleep parent.sleep_before_performing if parent.sleep_before_performing
|
135
|
-
|
135
|
+
perform_request_without_delay
|
136
136
|
end
|
137
|
-
alias_method_chain :
|
137
|
+
alias_method_chain :perform_request, :delay
|
138
138
|
end
|
139
139
|
end
|
140
140
|
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: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 6
|
10
|
+
version: 0.2.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Derek Kastner
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-08-
|
20
|
+
date: 2010-08-18 00:00:00 -04:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -158,6 +158,7 @@ files:
|
|
158
158
|
- lib/carbon/emission_estimate/request.rb
|
159
159
|
- lib/carbon/emission_estimate/response.rb
|
160
160
|
- lib/carbon/emission_estimate/storage.rb
|
161
|
+
- spec/lib/carbon/emission_estimate/response_spec.rb
|
161
162
|
- spec/lib/carbon_spec.rb
|
162
163
|
- spec/spec_helper.rb
|
163
164
|
- spec/specwatchr
|
@@ -196,5 +197,6 @@ signing_key:
|
|
196
197
|
specification_version: 3
|
197
198
|
summary: Carbon is a Ruby API wrapper for the Brighter Planet emission estimate web service (http://carbon.brighterplanet.com).
|
198
199
|
test_files:
|
200
|
+
- spec/lib/carbon/emission_estimate/response_spec.rb
|
199
201
|
- spec/lib/carbon_spec.rb
|
200
202
|
- spec/spec_helper.rb
|