carbon 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- First get the gem!
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 plain Ruby class <tt>RentalCar</tt> that represents a rental car on your lot: (<tt>ActiveRecord</tt> models work fine too!)
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 correctly-named characteristics that the {web service}[http://carbon.brighterplanet.com] will recognize:
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>... (and <b>store the result to a local variable!</b> Otherwise it will make a web service call every time.)
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
- Read the {carbon gem RDoc}[http://rdoc.info/projects/brighterplanet/carbon] for more!
58
+ == Documentation
49
59
 
50
- == Exceptions to watch out for
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
- == A note on API keys
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.0.0.beta2'
13
+ gemspec.add_dependency 'activesupport', '>=2.3.5'
14
14
  gemspec.add_dependency 'nap', '>=0.4'
15
- gemspec.add_dependency 'timeframe', '>=0.0.6'
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 = "decider #{version}"
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.4
1
+ 0.1.5
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{carbon}
8
- s.version = "0.1.4"
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.0.0.beta2"])
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.6"])
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.0.0.beta2"])
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.6"])
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.0.0.beta2"])
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.6"])
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
@@ -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
- mattr_accessor :_mode
64
- # Return the current mode. Defaults to <tt>:realtime</tt>.
65
- def self.mode
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
- # You will probably never access this module directly. Instead, you'll use it through the DSL.
84
- #
85
- # It's mixed into any class that includes <tt>Carbon</tt>.
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
- options.reverse_merge! ::Carbon.default_options
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
- options.reverse_merge! ::Carbon.default_options
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
- options.reverse_merge! ::Carbon.default_options
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> (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!
237
- # * <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'.
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
- options.reverse_merge! ::Carbon.default_options
223
+ ::Carbon.prepare_options options
242
224
  send "_#{options[:mode]}_emission", options
243
225
  end
244
226
  end
@@ -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
@@ -5,6 +5,8 @@ begin
5
5
  rescue
6
6
  end
7
7
 
8
+ require 'active_support/json/encoding'
9
+
8
10
  require 'carbon'
9
11
 
10
12
  require 'fakeweb'
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: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 4
10
- version: 0.1.4
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: 299253626
30
+ hash: 9
31
31
  segments:
32
+ - 2
32
33
  - 3
33
- - 0
34
- - 0
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: 19
61
+ hash: 17
63
62
  segments:
64
63
  - 0
65
64
  - 0
66
- - 6
67
- version: 0.0.6
65
+ - 7
66
+ version: 0.0.7
68
67
  type: :runtime
69
68
  version_requirements: *id003
70
69
  - !ruby/object:Gem::Dependency