Skip to content

Commit

Permalink
Initial commit; extracted Rack::Geo from Square API backend.
Browse files Browse the repository at this point in the history
  • Loading branch information
ydnar committed Jul 18, 2010
0 parents commit 81a6375
Show file tree
Hide file tree
Showing 11 changed files with 462 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
@@ -0,0 +1,9 @@
*.gem
*.gemspec
*.log
*.pid
*.sqlite3
*.tmproj
.DS_Store
log/*
pkg/*
3 changes: 3 additions & 0 deletions HISTORY.rdoc
@@ -0,0 +1,3 @@
=== Version 0.1.0 (git)

Initial release of Rack::Geo middleware.
7 changes: 7 additions & 0 deletions LICENSE.txt
@@ -0,0 +1,7 @@
Copyright © 2010 Square, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 changes: 21 additions & 0 deletions README.rdoc
@@ -0,0 +1,21 @@
= Rack::Geo

Rack middleware for parsing and serializing IEFT draft {Geo-Position}[http://tools.ietf.org/html/draft-daviel-html-geo-tag-08] HTTP headers.

== Prerequisites

* {Rack}[http://rack.rubyforge.org/]

== Testing

Testing requires the RSpec gem:

* {Rspec}[http://rspec.info/]

To test, run:
`rake`

== License

Copyright © 2010 Square, Inc.
See LICENSE.txt in this directory.
49 changes: 49 additions & 0 deletions Rakefile
@@ -0,0 +1,49 @@
require 'rubygems'
require 'rake'
require 'spec/rake/spectask'

require File.join(File.dirname(__FILE__), 'lib', 'rack', 'geo', 'version')

begin
require 'jeweler'
Jeweler::Tasks.new do |gemspec|
gemspec.version = Rack::Geo::VERSION::STRING
gemspec.name = "rack-geo"
gemspec.summary = "Rack middleware for Geo-Position HTTP headers"
gemspec.description = "Parse and serialize geospatial HTTP headers."
gemspec.email = "github@squareup.com"
gemspec.homepage = "http://github.com/square/rack-geo"
gemspec.authors = [
"Randy Reddig",
"Cameron Walters",
"Paul McKellar",
]
gemspec.extra_rdoc_files = [
'README.rdoc',
'HISTORY.rdoc',
'LICENSE.txt',
]
gemspec.add_dependency "rack", ">=1.0.0"
gemspec.add_development_dependency "rack-test", ">=0.5.3"
gemspec.add_development_dependency "rspec", ">=1.3.0"
end
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
end

desc "Run all specs"
Spec::Rake::SpecTask.new do |t|
t.spec_opts = ["--options", "spec/spec.opts"]
t.spec_files = FileList["spec/**/*_spec.rb"]
t.rcov = ENV["RCOV"]
t.rcov_opts = %w{--exclude osx\/objc,gems\/,spec\/}
t.verbose = true
end

task :spec => :check_dependencies
task :default => :spec

desc "Remove trailing whitespace"
task :whitespace do
sh %{find . -name '*.rb' -exec sed -i '' 's/ *$//g' {} \\;}
end
1 change: 1 addition & 0 deletions lib/rack-geo.rb
@@ -0,0 +1 @@
require 'rack/geo'
92 changes: 92 additions & 0 deletions lib/rack/geo.rb
@@ -0,0 +1,92 @@
require 'rack/geo/version'

module Rack
class Geo
def initialize(app)
@app = app
end

def call(env)
position = Position.new(env["HTTP_GEO_POSITION"] || env["HTTP_X_GEO_POSITION"])
env["geo.position"] = position if position.valid?
@app.call(env)
end

class Position
attr_accessor :latitude, :longitude, :altitude, :uncertainty, :heading, :speed

def initialize(value=nil)
parse! value
end

MATCH_LLA = /\A([+-]?[0-9\.]+);([+-]?[0-9\.]+)(?:;([+-]?[0-9\.]+))?/.freeze
MATCH_EPU = /\sepu=([0-9\.]+)(?:\s|\z)/i.freeze
MATCH_HDN = /\shdn=([0-9\.]+)(?:\s|\z)/i.freeze
MATCH_SPD = /\sspd=([0-9\.]+)(?:\s|\z)/i.freeze

def parse!(value)
reset!
value = value.to_s.strip

if lla = MATCH_LLA.match(value)
@latitude = lla[1].to_f
@longitude = lla[2].to_f
@altitude = lla[3].to_f if lla[3]
end

if epu = MATCH_EPU.match(value)
@uncertainty = epu[1].to_f
end

if hdn = MATCH_HDN.match(value)
@heading = hdn[1].to_f
end

if spd = MATCH_SPD.match(value)
@speed = spd[1].to_f
end

nil
end

def valid?
(!latitude.nil? && latitude.respond_to?(:to_f) && (-90..90).include?(latitude.to_f)) &&
(!longitude.nil? && longitude.respond_to?(:to_f) && (-180..180).include?(longitude.to_f)) &&
(altitude.nil? || altitude.respond_to?(:to_f)) &&
(uncertainty.nil? || uncertainty.respond_to?(:to_f) && uncertainty.to_f >= 0) &&
(heading.nil? || heading.respond_to?(:to_f) && (0..360).include?(heading.to_f)) &&
(speed.nil? || speed.respond_to?(:to_f) && speed.to_f >= 0)
end

def attributes
{
:latitude => latitude,
:longitude => longitude,
:altitude => altitude,
:uncertainty => uncertainty,
:heading => heading,
:speed => speed,
}
end

def to_http_header
value = "%f;%f" % [latitude.to_f, longitude.to_f]
value += ";%f" % altitude.to_f if altitude
value += " epu=%f" % uncertainty.to_f if uncertainty
value += " hdn=%f" % heading.to_f if heading
value += " spd=%f" % speed.to_f if speed
value
end

def self.from_http_header(value)
self.new(value)
end

private

def reset!
@latitude = @longitude = @altitude = @uncertainty = @heading = @speed = nil
end
end
end
end
11 changes: 11 additions & 0 deletions lib/rack/geo/version.rb
@@ -0,0 +1,11 @@
module Rack
class Geo
module VERSION #:nodoc:
MAJOR = 0
MINOR = 1
TINY = 0

STRING = [MAJOR, MINOR, TINY].join('.')
end
end
end

0 comments on commit 81a6375

Please sign in to comment.