amon 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ Ruby Client for the AMEE AMON API
2
+ =================================
3
+
4
+ This is a Ruby client library for the [AMEE](http://www.amee.com/) <abbr title="AMEE Monitoring Object Notation">AMON</abbr> <abbr title="Application Programming Interface">API</abbr>. _More details and links to be inserted once information about the API is publicly available_.
5
+
6
+ ## Installation ##
7
+
8
+ ### Dependencies ###
9
+
10
+ Currently, the library depends on:
11
+
12
+ * [json](http://flori.github.com/json/) 1.4.X
13
+ * [rest-client](http://github.com/archiloque/rest-client) 1.6.X
14
+
15
+ These will be automatically installed if installing as a gem.
16
+
17
+ ### Standard installation: as a rubygem ###
18
+
19
+ The library is not currently available via any public gem servers. However, it can be installed as a gem using the following steps:
20
+
21
+ 1. Grab the code: `git clone git@github.com:AMEE/amon.client.ruby.git`
22
+ 2. Build the gem: `gem build amon.gemspec`
23
+ 3. Install the gem: `sudo gem install amon-0.1.0.gem`
24
+
25
+ ### Alternative: manual installation ###
26
+
27
+ You can manually install the code somewhere, and then ensure that the `lib/` directory is listed in your `LOAD_PATH`.
28
+
29
+ ### Alternative: vendoring in a Rails app ###
30
+
31
+ If you are using the library inside a Rails (2.3) app, clone the code into `vendor/gems/amon-0.1.0`. Then list it as a gem dependency in `config/environment.rb` and Rails will automatically find and load it.
32
+
33
+ If using Rails 3, you could do a similar thing using Bundler.
34
+
35
+ ## Loading the library in your code ##
36
+
37
+ Once installed, the library can be loaded using `require "amon"`.
38
+
39
+ ## Configuration ##
40
+
41
+ Interaction with the API is achieved through {AMON::Session Session} objects. For convenience, you can configure some 'default session options' by setting the values in the {AMON.default_session_options} hash. You can then create a session from these options at any time by calling {AMON.create_default_session}.
42
+
43
+ See the {AMON::Session} page for a full explanation of the options available.
44
+
45
+ ### Example default session configuration ###
46
+
47
+ AMON.default_session_options[:base_uri] = "http://amon.amee.com/1"
48
+ AMON.default_session_options[:user] = "fred"
49
+ AMON.default_session_options[:password] = "s3cr3t"
50
+
51
+ ## Usage ##
52
+
53
+ Methods which can be called on a {AMON::Session Session} can be called directly on the {AMON} module, which will cause a {AMON.create_default_session default session} to be created and used.
54
+
55
+ ### Simple usage example ###
56
+
57
+ This example gets some measurements from an electricity meter over a 1 month period.
58
+
59
+ start_date = Time.local(2010, 3)
60
+ end_date = Time.local(2010, 4)
61
+
62
+ property = AMON.entity("da3906c0-7c6b-012d-a6d5-001c23973687")
63
+ concentrator = property.meters.first
64
+ electricity_reading = concentrator.readings_by_type["electricalInput"]
65
+ electricity_reading.measurements(start_date, end_date) # => Array of measurements
66
+
67
+ ## Development ##
68
+
69
+ Tests can be run with RSpec: `spec spec/`.
70
+
71
+ The documentation is generated with YARD: `rake yard`.
72
+
73
+ ## TODO List ##
74
+
75
+ * Error handling
76
+ * Rename Document and DocumentPart to Resource and ResourcePart
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/rails/init"
@@ -0,0 +1,45 @@
1
+ require 'forwardable'
2
+
3
+ # The AMON module responds to the same method names as an {AMON::Session}. When called,
4
+ # these methods use a 'default' session returned by {create_default_session}. So the following
5
+ # method calls are all equivalent:
6
+ #
7
+ # AMON::Session.new(AMON.default_session_options).entity("77e50660-93e7-012d-b57d-001c23973687")
8
+ # AMON.create_default_session.entity("77e50660-93e7-012d-b57d-001c23973687")
9
+ # AMON.entity("77e50660-93e7-012d-b57d-001c23973687")
10
+ #
11
+ # Note that using a default session implicitly (as in the final line above) will cause a new
12
+ # session to be created each time the method is called. This means caching will not be used.
13
+ #
14
+ # If you are likely to be requesting the same resources in short succession, it is advisable to
15
+ # hold on to a session object yourself. For example:
16
+ #
17
+ # @session = AMON.create_default_session
18
+ # @session.entity("77e50660-93e7-012d-b57d-001c23973687") # => <AMON::Entity> (from HTTP)
19
+ # @session.entity("77e50660-93e7-012d-b57d-001c23973687") # => <AMON::Entity> (from cache)
20
+ module AMON
21
+ class << self
22
+ # A hash of options which are used by {create_default_session}. The values of this hash
23
+ # should be configured by users as appropriate.
24
+ # @return [Hash] an options hash, initially empty
25
+ def default_session_options
26
+ @default_session_options ||= {}
27
+ end
28
+
29
+ # Assigns a new options hash as the {default_session_options}.
30
+ # @param [Hash] options the options hash
31
+ # @return [Hash] the options hash
32
+ def default_session_options=(options)
33
+ @default_session_options = options
34
+ end
35
+
36
+ # Creates a new session using the {default_session_options}.
37
+ # @return [Session]
38
+ def create_default_session
39
+ Session.new(default_session_options)
40
+ end
41
+
42
+ extend Forwardable
43
+ def_delegators :create_default_session, :entity, :meter, :metering_point, :measurements
44
+ end
45
+ end
@@ -0,0 +1,30 @@
1
+ module AMON
2
+ # A top-level object representing an AMON resource
3
+ # @abstract
4
+ class Document
5
+ include JSONHelper
6
+
7
+ # @return [Session] The session associated with this document
8
+ attr_reader :session
9
+
10
+ # @param [Session] session The session
11
+ # @param [Hash] json The JSON data as a Hash
12
+ def initialize(session, json)
13
+ @session, @json = session, json
14
+ end
15
+
16
+ # This should be implemented in subclasses, returning the UUID for the document
17
+ # @abstract
18
+ # @private
19
+ def id
20
+ raise NotImplementedError
21
+ end
22
+
23
+ # Two documents are equal if they have the same class and their id fields are equal
24
+ # @param [Document] other
25
+ # @return [Boolean]
26
+ def ==(other)
27
+ self.class == other.class && id == other.id
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ module AMON
2
+ # An object representing part of an AMON {Document}. For example, a {Reading} is part of a
3
+ # {Meter}, and cannot exist outside of it.
4
+ #
5
+ # @abstract
6
+ class DocumentPart
7
+ include JSONHelper
8
+
9
+ # @return [Document] The parent document of which this object is a part
10
+ attr_reader :parent
11
+
12
+ # @param [Document] parent The parent document
13
+ # @param [Hash] json The JSON data as a Hash
14
+ def initialize(parent, json)
15
+ @parent, @json = parent, json
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ module AMON
2
+ # An AMON entity, such as a house, business park, or building floor
3
+ class Entity < Document
4
+ # @return [String] The id for this entity
5
+ field :id, :name => 'entityId'
6
+
7
+ # @return [Array<String>] The ids of all the meters associated with this entity
8
+ field :meter_ids, :name => 'meterIds', :default => []
9
+
10
+ # @return [Array<String>] The qualified ids of all the metering points associated with this entity
11
+ # @todo Rename to qualified_metering_point_ids
12
+ field :metering_point_ids, :name => 'qualifiedMeteringPointIds', :default => []
13
+
14
+ # @return [Array<Meter>] The meters associated with this entity
15
+ def meters
16
+ @meters ||= meter_ids.map { |id| session.meter(id) }
17
+ end
18
+
19
+ # @return [Array<MeteringPoint>] The metering points associated with this entity
20
+ def metering_points
21
+ @metering_points ||= metering_point_ids.map { |id| session.metering_point(id) }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ module AMON
2
+ module JSONHelper
3
+ # @return [Hash] The raw JSON data for this object, represented as a Hash
4
+ attr_reader :json
5
+
6
+ private
7
+
8
+ def self.included(base)
9
+ base.class_eval do
10
+ extend ClassMethods
11
+ end
12
+ end
13
+
14
+ # @private
15
+ module ClassMethods
16
+ def field(name, options = {})
17
+ class_eval do
18
+ define_method(name) do
19
+ original_value = json[(options[:name] || name).to_s]
20
+
21
+ if original_value.nil?
22
+ options[:default]
23
+ else
24
+ case options[:as].to_s
25
+ when 'Time'
26
+ Time.parse(original_value)
27
+ else
28
+ original_value
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ module AMON
2
+ # A measurement corresponding to a {Reading} of a {Meter}
3
+ class Measurement < DocumentPart
4
+ # @return [String] The measurement's name
5
+ field :name
6
+
7
+ # @return [Numeric] The measurement's value
8
+ field :value
9
+
10
+ # @return [Time] The measurement's timestamp, if provided
11
+ field :timestamp, :as => Time
12
+
13
+ # @return [Time] The measurement's start date, if provided
14
+ field :start_date, :name => 'startDate', :as => Time
15
+
16
+ # @return [Time] The measurement's end date, if provided
17
+ field :end_date, :name => 'endDate', :as => Time
18
+
19
+ # @attr_reader [Meter] meter The meter which took this measurement
20
+ alias_method :meter, :parent
21
+
22
+ # A measurement is instantaneous if it is given with a single timestamp. If, on the other hand,
23
+ # start and end dates are provided, it is durational.
24
+ # @return [Boolean]
25
+ # @see Measurement#durational?
26
+ def instantaneous?
27
+ !timestamp.nil?
28
+ end
29
+
30
+ # The opposite of {Measurement#instantaneous? instantaneous?}
31
+ # @return [Boolean]
32
+ # @see Measurement#instantaneous?
33
+ def durational?
34
+ !instantaneous?
35
+ end
36
+
37
+ # The {Meter meter} {Reading reading} associated with this measurement.
38
+ # @return [Reading]
39
+ def reading
40
+ meter.readings_by_type[name]
41
+ end
42
+
43
+ # A single timestamp in the 'middle' of this measurement. If the measurement is instantaneous,
44
+ # then the mid timestamp is identical to the {Measurement#timestamp timestamp}. However, if it
45
+ # is durational then the mid timestamp is the halfway point between the {Measurement#start_date}
46
+ # and the {Measurement#end_date}.
47
+ def mid_timestamp
48
+ if instantaneous?
49
+ timestamp
50
+ else
51
+ start_date + (end_date - start_date) / 2
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,34 @@
1
+ module AMON
2
+ # Metadata associated with a {Meter}. The metadata section of an AMON document can have an
3
+ # arbitrary number of fields, so this class uses `method_missing` to look them up and respond
4
+ # appropriately.
5
+ #
6
+ # @todo
7
+ # This doesn't yet support arbitrarily nested metadata, so you can't have JSON like so:
8
+ #
9
+ # "metadata": {
10
+ # "foo": {
11
+ # "bar": "baz"
12
+ # }
13
+ # }
14
+ #
15
+ # And then access:
16
+ #
17
+ # object.metadata.foo.bar # => "baz"
18
+ #
19
+ # However, in the meantime you could do:
20
+ #
21
+ # object.metadata.foo[:bar] # => "baz"
22
+ #
23
+ # (As the JSON is full parsed into a nested hash.)
24
+
25
+ class Metadata < DocumentPart
26
+ def method_missing(name, *args, &block)
27
+ if args.empty? && block.nil?
28
+ json[name.to_s]
29
+ else
30
+ raise NoMethodError, "undefined method `#{name}'"
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/amon/meter.rb ADDED
@@ -0,0 +1,53 @@
1
+ module AMON
2
+ # An AMON meter, containing any number of {Reading readings}, and {Measurement measurements} for those readings.
3
+ class Meter < Document
4
+ # @return [String] The id of the meter
5
+ field :id, :name => 'meterId'
6
+
7
+ # @return <String> The description for the meter
8
+ def description
9
+ @description ||= json['description']
10
+ end
11
+
12
+ # @return <String> The location for the meter
13
+ def location_name
14
+ @location_name ||= json['location']['name']
15
+ end
16
+
17
+ # @return [Metadata] The metadata for the meter
18
+ def metadata
19
+ @metadata ||= Metadata.new(self, json['metadata'])
20
+ end
21
+
22
+ # @return [Array<Reading>] The readings for the meter
23
+ def readings
24
+ @readings ||= json['readings'].map { |reading| Reading.new(self, reading) }
25
+ end
26
+
27
+ # @return [Hash<String => Reading>] A hash allowing {Meter#readings} to be retrieved by their
28
+ # {Reading#type}
29
+ def readings_by_type
30
+ readings.inject({}) do |readings_by_type, reading|
31
+ readings_by_type[reading.type] = reading
32
+ readings_by_type
33
+ end
34
+ end
35
+
36
+ # @return [Array<Measurement>] Measurements for this meter
37
+ # @see Session#measurements
38
+ def measurements(start_date, end_date, raw = false)
39
+ session.measurements(id, start_date, end_date, raw)
40
+ end
41
+
42
+ # @return [Hash<String => Measurement>] A hash allowing {Meter#measurements} to be retrieved
43
+ # by their {Measurement#name}
44
+ # @see Meter#measurements
45
+ def measurements_by_name(start_date, end_date, raw = false)
46
+ measurements(start_date, end_date, raw).inject({}) do |measurements_by_name, measurement|
47
+ measurements_by_name[measurement.name] ||= []
48
+ measurements_by_name[measurement.name] << measurement
49
+ measurements_by_name
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,8 @@
1
+ module AMON
2
+ # An AMON metering point
3
+ # @todo Implement this class
4
+ class MeteringPoint < Document
5
+ # @return [String] The id for this metering point
6
+ field :id, :name => 'meteringPointId'
7
+ end
8
+ end
@@ -0,0 +1,59 @@
1
+ module AMON
2
+ # AMON readings are 'categories' of {Measurement measurements} which are taken by {Meter meters}.
3
+ # For example, one reading may be 'electricalInput' with the unit 'kWh'.
4
+ class Reading < DocumentPart
5
+ # @return [String] The type of reading, which is unique within the parent {Meter meter}
6
+ field :type
7
+
8
+ # @return [String] An arbitrary name for the reading
9
+ field :name
10
+
11
+ # @return [String] The units this reading records {Measurement measurements} in
12
+ field :unit
13
+
14
+ # @return [Numeric] The resolution of the reading
15
+ field :resolution
16
+
17
+ # @return [Numeric] The accuracy of the reading
18
+ field :accuracy
19
+
20
+ # @return ["instant", "duration"] Whether this reading records measurement values taken at a
21
+ # specific point in time ('instant'), or taken as an average over a period of time ('duration')
22
+ field :period, :default => "instant"
23
+
24
+ # @attr_reader [Meter] meter The meter which took this measurement
25
+ alias_method :meter, :parent
26
+
27
+ # @return [Array<Measurement>] Measurements for this reading
28
+ # @see Meter#measurements
29
+ def measurements(start_date, end_date, raw = false)
30
+ meter.measurements_by_name(start_date, end_date, raw)[type] || []
31
+ end
32
+
33
+ # @return [true] if the {#period} is 'instant'
34
+ # @return [false] if the {#period} is 'duration'
35
+ def instantaneous?
36
+ period == "instant"
37
+ end
38
+
39
+ # @return [Boolean] The opposite of {#instantaneous?}
40
+ def durational?
41
+ !instantaneous?
42
+ end
43
+
44
+ # One reading is equal to another if the meters are equal and they are the same type
45
+ # @return [Boolean]
46
+ # @param [Reading] other The other reading to compare with.
47
+ def ==(other)
48
+ meter == other.meter && type == other.type
49
+ end
50
+
51
+ # A unique id for the reading, composed of the {Meter meter} id, and the {#type}. So an
52
+ # '<code>electricalInput</code>' reading for a meter with id '<code>b7ddac00-9415-012d-b57e-001c23973687</code>' would
53
+ # have the id '<code>b7ddac00-9415-012d-b57e-001c23973687-electricalInput</code>'.
54
+ # @return [String] the id
55
+ def id
56
+ "#{meter.id}-#{type}"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,130 @@
1
+ require 'uri'
2
+
3
+ module AMON
4
+ # Session objects take care of making the actual HTTP requests to the AMON API
5
+ #
6
+ # ## Caching ##
7
+ #
8
+ # Sessions support caching which is enabled by default, but can be optionally turned off. When
9
+ # caching is enabled, the same request will not be performed twice in a session. Be aware that
10
+ # this has some important implications:
11
+ #
12
+ # * If a long running process uses the same session, the cache may become stale as the backend
13
+ # data changes, but new API requests are not made.
14
+ # * There is no mechanism for automatically expiring the cache; this is left up to users of the
15
+ # library.
16
+ # * The cache stores the AMON objects, rather than the raw response data. In this sense it
17
+ # functions as a sort of identity map - getting the same entity twice will return exactly the
18
+ # same object twice, rather than two different objects representing the same data.
19
+ #
20
+ # Often the most natural way to 'expire the cache' is to discard the session and use a new one
21
+ # at regular intervals. Alternatively, you can use {Session#clear_cache} and keep the same
22
+ # session.
23
+ class Session
24
+ # @return [Hash] The hash of configuration options to be used when making requests
25
+ attr_reader :options
26
+
27
+ # The default options for a session
28
+ DEFAULT_OPTIONS = {
29
+ :base_uri => nil,
30
+ :user => nil,
31
+ :password => nil,
32
+ :cache => true
33
+ }.freeze
34
+
35
+ # @param [Hash] options the options for the session
36
+ # @option options [String] :base_uri The base URI for the AMON API, e.g. "http://amon.amee.com/1"
37
+ # (note that this includes the API version, as this library only knows about version 1 of the API)
38
+ # @option options [String] :user The HTTP user for authentication
39
+ # @option options [String] :password The HTTP password for authentication
40
+ # @option options [Boolean] :cache (true) Whether to perform caching or not. See above for details.
41
+ def initialize(options = {})
42
+ @options = DEFAULT_OPTIONS.merge(options)
43
+ @options.freeze
44
+ end
45
+
46
+ # Retrieves an entity from the API
47
+ # @param [String] entity_id the ID of the entity to be requested
48
+ # @return [Entity]
49
+ def entity(entity_id)
50
+ cache(:entities, entity_id) do
51
+ json = get("/entities/#{URI.escape(entity_id)}")['entity']
52
+ Entity.new(self, json)
53
+ end
54
+ end
55
+
56
+ # Retrieves a meter from the API
57
+ # @param [String] meter_id the ID of the meter to be requested
58
+ # @return [Meter]
59
+ def meter(meter_id)
60
+ cache(:meters, meter_id) do
61
+ json = get("/meters/#{URI.escape(meter_id)}")['meter']
62
+ Meter.new(self, json)
63
+ end
64
+ end
65
+
66
+ # Retrieves a metering point from the API
67
+ # @param [String] metering_point_id the ID of the metering point to be requested
68
+ # @return [MeteringPoint]
69
+ def metering_point(metering_point_id)
70
+ cache(:metering_points, :metering_point_id) do
71
+ json = get("/metering-points/#{URI.escape(metering_point_id)}")['metering_point']
72
+ MeteringPoint.new(self, json)
73
+ end
74
+ end
75
+
76
+ # Retrieves measurements from the API
77
+ # @param [String] meter_id The ID of the meter to get the measurements from
78
+ # @param [Time] start_date The starting point for the measurements
79
+ # @param [Time] end_date The ending point for the measurements
80
+ # @param [Boolean] raw Whether to return raw measurements as submitted by the hardware (may
81
+ # contain a lot of data), or appropriate aggregations over the time period
82
+ def measurements(meter_id, start_date, end_date, raw = false)
83
+ cache(:measurements, [meter_id, start_date, end_date, raw]) do
84
+ meter = meter(meter_id)
85
+ measurements = get(
86
+ "/meters/#{meter.id}/measurements?" +
87
+ "raw=" + raw.to_s + "&" +
88
+ "startDate=" + start_date.utc.xmlschema + "&" +
89
+ "endDate=" + end_date.utc.xmlschema
90
+ )['measurements']
91
+ measurements.map { |measurement| Measurement.new(meter, measurement) }
92
+ end
93
+ end
94
+
95
+ # Completely clears the cache
96
+ def clear_cache
97
+ @cache = nil
98
+ end
99
+
100
+ private
101
+
102
+ def cache(group, id, &block)
103
+ if options[:cache]
104
+ @cache ||= {}
105
+ @cache[group] ||= {}
106
+ @cache[group][id] ||= block.call
107
+ else
108
+ block.call
109
+ end
110
+ end
111
+
112
+ def get(url, params = {})
113
+ JSON.parse(client[url].get(:params => params))
114
+ end
115
+
116
+ def client
117
+ raise ArgumentError, "Can't make a request without :base_uri option" unless options[:base_uri]
118
+
119
+ @client ||= RestClient::Resource.new(
120
+ options[:base_uri],
121
+ :user => options[:user],
122
+ :password => options[:password],
123
+ :headers => {
124
+ :content_type => 'application/json',
125
+ :accept => 'application/json'
126
+ }
127
+ )
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,10 @@
1
+ module AMON
2
+ # Provides details about the version of the AMON library in use.
3
+ module Version
4
+ MAJOR = 0
5
+ MINOR = 3
6
+ TINY = 0
7
+
8
+ STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
9
+ end
10
+ end
data/lib/amon.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+
4
+ module AMON
5
+ autoload :JSONHelper, 'amon/json_helper'
6
+ autoload :Document, 'amon/document'
7
+ autoload :DocumentPart, 'amon/document_part'
8
+ autoload :Entity, 'amon/entity'
9
+ autoload :Meter, 'amon/meter'
10
+ autoload :MeteringPoint, 'amon/metering_point'
11
+ autoload :Metadata, 'amon/metadata'
12
+ autoload :Reading, 'amon/reading'
13
+ autoload :Measurement, 'amon/measurement'
14
+ autoload :Version, 'amon/version'
15
+ autoload :Session, 'amon/session'
16
+
17
+ require 'amon/default_session'
18
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'amon'
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amon
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
+ platform: ruby
12
+ authors:
13
+ - AMEE
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-25 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 7
29
+ segments:
30
+ - 1
31
+ - 4
32
+ - 0
33
+ version: 1.4.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rest-client
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 15
45
+ segments:
46
+ - 1
47
+ - 6
48
+ - 0
49
+ version: 1.6.0
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rspec
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 27
61
+ segments:
62
+ - 1
63
+ - 3
64
+ - 0
65
+ version: 1.3.0
66
+ type: :development
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: mocha
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 43
77
+ segments:
78
+ - 0
79
+ - 9
80
+ - 8
81
+ version: 0.9.8
82
+ type: :development
83
+ version_requirements: *id004
84
+ - !ruby/object:Gem::Dependency
85
+ name: fakeweb
86
+ prerelease: false
87
+ requirement: &id005 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ~>
91
+ - !ruby/object:Gem::Version
92
+ hash: 27
93
+ segments:
94
+ - 1
95
+ - 3
96
+ - 0
97
+ version: 1.3.0
98
+ type: :development
99
+ version_requirements: *id005
100
+ description:
101
+ email:
102
+ executables: []
103
+
104
+ extensions: []
105
+
106
+ extra_rdoc_files: []
107
+
108
+ files:
109
+ - lib/amon/default_session.rb
110
+ - lib/amon/document.rb
111
+ - lib/amon/document_part.rb
112
+ - lib/amon/entity.rb
113
+ - lib/amon/json_helper.rb
114
+ - lib/amon/measurement.rb
115
+ - lib/amon/metadata.rb
116
+ - lib/amon/meter.rb
117
+ - lib/amon/metering_point.rb
118
+ - lib/amon/reading.rb
119
+ - lib/amon/session.rb
120
+ - lib/amon/version.rb
121
+ - lib/amon.rb
122
+ - README.md
123
+ - init.rb
124
+ - rails/init.rb
125
+ homepage: http://amee.com
126
+ licenses: []
127
+
128
+ post_install_message:
129
+ rdoc_options: []
130
+
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ hash: 3
139
+ segments:
140
+ - 0
141
+ version: "0"
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ hash: 3
148
+ segments:
149
+ - 0
150
+ version: "0"
151
+ requirements: []
152
+
153
+ rubyforge_project:
154
+ rubygems_version: 1.7.2
155
+ signing_key:
156
+ specification_version: 3
157
+ summary: Ruby client for the AMEE AMON API
158
+ test_files: []
159
+
160
+ has_rdoc: