Introduce a Location class.

This commit is contained in:
Akinori MUSHA 2014-09-19 19:06:55 +09:00
parent 1784eeb38a
commit b25142e793
4 changed files with 143 additions and 43 deletions

View file

@ -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
View 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
View 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

View file

@ -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