mirror of
https://github.com/Fishwaldo/huginn.git
synced 2025-03-15 19:31:26 +00:00
Introduce a Location class.
This commit is contained in:
parent
1784eeb38a
commit
b25142e793
4 changed files with 143 additions and 43 deletions
|
@ -1,3 +1,5 @@
|
|||
require 'location'
|
||||
|
||||
# Events are how Huginn Agents communicate and log information about the world. Events can be emitted and received by
|
||||
# Agents. They contain a serialized `payload` of arbitrary JSON data, as well as optional `lat`, `lng`, and `expires_at`
|
||||
# fields.
|
||||
|
@ -33,10 +35,10 @@ class Event < ActiveRecord::Base
|
|||
}
|
||||
|
||||
def location
|
||||
@location ||= {
|
||||
# lat and lng are BigDecimal, so convert them to Float
|
||||
lat: (lat.to_f if lat),
|
||||
lng: (lng.to_f if lng),
|
||||
@location ||= Location.new(
|
||||
# lat and lng are BigDecimal, but converted to Float by the Location class
|
||||
lat: lat,
|
||||
lng: lng,
|
||||
radius:
|
||||
begin
|
||||
h = payload[:horizontal_accuracy].presence
|
||||
|
@ -47,21 +49,8 @@ class Event < ActiveRecord::Base
|
|||
(h || v || payload[:accuracy]).to_f
|
||||
end
|
||||
end,
|
||||
course:
|
||||
begin
|
||||
if (v = payload[:course].presence) &&
|
||||
(v = v.to_f) >= 0
|
||||
v
|
||||
end
|
||||
end,
|
||||
speed:
|
||||
begin
|
||||
if (v = payload[:speed].presence) &&
|
||||
(v = v.to_f) >= 0
|
||||
v
|
||||
end
|
||||
end,
|
||||
}
|
||||
course: payload[:course],
|
||||
speed: payload[:speed].presence)
|
||||
end
|
||||
|
||||
# Emit this event again, as a new Event.
|
||||
|
|
82
lib/location.rb
Normal file
82
lib/location.rb
Normal file
|
@ -0,0 +1,82 @@
|
|||
Location = Struct.new(:lat, :lng, :radius, :speed, :course)
|
||||
|
||||
class Location
|
||||
protected :[]=
|
||||
|
||||
def initialize(data = {})
|
||||
super()
|
||||
|
||||
case data
|
||||
when Array
|
||||
raise ArgumentError, 'unsupported location data' unless data.size == 2
|
||||
self.lat, self.lng = data
|
||||
when Hash, Location
|
||||
data.each { |key, value|
|
||||
begin
|
||||
__send__("#{key}=", value)
|
||||
rescue NameError
|
||||
end
|
||||
}
|
||||
else
|
||||
raise ArgumentError, 'unsupported location data'
|
||||
end
|
||||
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
def lat=(value)
|
||||
self[:lat] = floatify(value) { |f|
|
||||
if f.abs <= 90
|
||||
f
|
||||
else
|
||||
raise ArgumentError, 'out of bounds'
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def lng=(value)
|
||||
self[:lng] = floatify(value) { |f|
|
||||
if f.abs <= 180
|
||||
f
|
||||
else
|
||||
raise ArgumentError, 'out of bounds'
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def radius=(value)
|
||||
self[:radius] = floatify(value) { |f| f if f >= 0 }
|
||||
end
|
||||
|
||||
def speed=(value)
|
||||
self[:speed] = floatify(value) { |f| f if f >= 0 }
|
||||
end
|
||||
|
||||
def course=(value)
|
||||
self[:course] = floatify(value) { |f| f if (0..360).cover?(f) }
|
||||
end
|
||||
|
||||
def present?
|
||||
lat && lng
|
||||
end
|
||||
|
||||
def empty?
|
||||
!present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def floatify(value)
|
||||
case value
|
||||
when nil, ''
|
||||
return nil
|
||||
else
|
||||
float = Float(value)
|
||||
if block_given?
|
||||
yield(float)
|
||||
else
|
||||
float
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
49
spec/lib/location_spec.rb
Normal file
49
spec/lib/location_spec.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Location do
|
||||
let(:location) {
|
||||
Location.new(
|
||||
lat: BigDecimal.new('2.0'),
|
||||
lng: BigDecimal.new('3.0'),
|
||||
radius: 300,
|
||||
speed: 2,
|
||||
course: 30)
|
||||
}
|
||||
|
||||
it "converts values to Float" do
|
||||
expect(location.lat).to equal 2.0
|
||||
expect(location.lng).to equal 3.0
|
||||
expect(location.radius).to equal 300.0
|
||||
expect(location.speed).to equal 2.0
|
||||
expect(location.course).to equal 30.0
|
||||
end
|
||||
|
||||
it "provides hash-style access to its properties with both symbol and string keys" do
|
||||
expect(location[:lat]).to equal 2.0
|
||||
expect(location['lat']).to equal 2.0
|
||||
end
|
||||
|
||||
it "does not allow hash-style assignment" do
|
||||
expect {
|
||||
location[:lat] = 2.0
|
||||
}.to raise_error
|
||||
end
|
||||
|
||||
it "ignores invalid values" do
|
||||
location2 = Location.new(
|
||||
lat: 2,
|
||||
lng: 3,
|
||||
radius: -1,
|
||||
speed: -1,
|
||||
course: -1)
|
||||
expect(location2.radius).to be_nil
|
||||
expect(location2.speed).to be_nil
|
||||
expect(location2.course).to be_nil
|
||||
end
|
||||
|
||||
it "considers a location empty if either latitude or longitude is missing" do
|
||||
expect(Location.new.empty?).to be_truthy
|
||||
expect(Location.new(lat: 2, radius: 1).present?).to be_falsy
|
||||
expect(Location.new(lng: 3, radius: 1).present?).to be_falsy
|
||||
end
|
||||
end
|
|
@ -18,13 +18,12 @@ describe Event do
|
|||
describe "#location" do
|
||||
it "returns a default hash when an event does not have a location" do
|
||||
event = events(:bob_website_agent_event)
|
||||
event.location.should == {
|
||||
event.location.should == Location.new(
|
||||
lat: nil,
|
||||
lng: nil,
|
||||
radius: 0.0,
|
||||
speed: nil,
|
||||
course: nil,
|
||||
}
|
||||
course: nil)
|
||||
end
|
||||
|
||||
it "returns a hash containing location information" do
|
||||
|
@ -37,31 +36,12 @@ describe Event do
|
|||
course: 90.0,
|
||||
}
|
||||
event.save!
|
||||
event.location.should == {
|
||||
event.location.should == Location.new(
|
||||
lat: 2.0,
|
||||
lng: 3.0,
|
||||
radius: 0.0,
|
||||
speed: 0.5,
|
||||
course: 90.0,
|
||||
}
|
||||
end
|
||||
|
||||
it "ignores invalid speed and course" do
|
||||
event = events(:bob_website_agent_event)
|
||||
event.lat = 2
|
||||
event.lng = 3
|
||||
event.payload = {
|
||||
speed: -1,
|
||||
course: -1,
|
||||
}
|
||||
event.save!
|
||||
event.location.should == {
|
||||
lat: 2.0,
|
||||
lng: 3.0,
|
||||
radius: 0.0,
|
||||
speed: nil,
|
||||
course: nil,
|
||||
}
|
||||
course: 90.0)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue