sg-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Dan Dofter
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,120 @@
1
+ = sg-ruby
2
+
3
+ A SimpleGeo Ruby client.
4
+
5
+ For the specific documentation on APIs (and the full list of parameters) see:
6
+
7
+ http://help.simplegeo.com/faqs/api-documentation/endpoints
8
+
9
+ == Examples
10
+
11
+ Start by requiring SimpleGeo and setting your authentication credentials:
12
+
13
+ require 'simplegeo'
14
+ SimpleGeo::Client.set_credentials('token', 'secret')
15
+
16
+ === Getting a record
17
+
18
+ record = SimpleGeo::Client.get_record('com.simplegeo.global.geonames', '5373629')
19
+
20
+ === Getting multiple records
21
+
22
+ records = SimpleGeo::Client.get_records('com.simplegeo.global.geonames', ['1234', '5678'])
23
+
24
+ === Adding a record
25
+
26
+ record = SimpleGeo::Record.new({
27
+ :id => '1234',
28
+ :created => 1269832510,
29
+ :lat => 37.759650000000001,
30
+ :lon => -122.42608,
31
+ :layer => 'com.example.testlayer',
32
+ :properties => {
33
+ :test_property => 'foobar'
34
+ }
35
+ })
36
+ SimpleGeo::Client.add_record(record)
37
+
38
+ === Updating a record
39
+
40
+ record = SimpleGeo::Client.get_record('com.example.testlayer', '1234')
41
+ record.lat = 40.714269
42
+ record.lon = -74.005973
43
+ SimpleGeo::Client.add_record(record)
44
+
45
+ === Adding / updating multiple records
46
+
47
+ records = [
48
+ SimpleGeo::Record.new({
49
+ :id => '1234',
50
+ :created => 1269832510,
51
+ :lat => 37.759650000000001,
52
+ :lon => -122.42608,
53
+ :layer => 'com.example.testlayer',
54
+ :properties => {
55
+ :test_property => 'foobar'
56
+ }
57
+ }),
58
+ SimpleGeo::Record.new({
59
+ :id => '5678',
60
+ :created => 1269832510,
61
+ :lat => 37.755470000000003,
62
+ :lon => -122.420646,
63
+ :layer => 'com.example.testlayer',
64
+ :properties => {
65
+ :mad_prop => 'baz'
66
+ }
67
+ })
68
+ ]
69
+ SimpleGeo::Client.add_records('com.example.testlayer', records)
70
+
71
+ === Deleting a record
72
+
73
+ SimpleGeo::Client.delete_record('1234')
74
+
75
+ === Getting a record's history
76
+
77
+ history = SimpleGeo::Client.get_history('com.example.testlayer', '1234')
78
+
79
+ === Getting nearby records
80
+
81
+ See http://help.simplegeo.com/faqs/api-documentation/endpoints for other optional params
82
+
83
+ # by lat, lon
84
+ records = SimpleGeo::Client.get_nearby_records('com.example.testlayer',
85
+ :lat => 37.759650000000001,
86
+ :lon => -122.42608)
87
+
88
+ # by geohash
89
+ records = SimpleGeo::Client.get_nearby_records('com.example.testlayer',
90
+ :geohash => '9q8yy1ujcsfm')
91
+
92
+ === Getting a nearby address for a lat and lon
93
+
94
+ nearby_address = SimpleGeo::Client.get_nearby_address(37.759650000000001, -122.42608)
95
+
96
+ === Getting SpotRank density information
97
+
98
+ # by day
99
+ density_info = SimpleGeo::Client.get_density(37.75965, -122.42608, 'sat')
100
+
101
+ # by hour
102
+ density_info = SimpleGeo::Client.get_density(37.75965, -122.42608, 'sat', '16' )
103
+
104
+ === Other APIs
105
+
106
+ For more examples see: spec/client_spec.rb
107
+
108
+ == Note on Patches/Pull Requests
109
+
110
+ * Fork the project.
111
+ * Make your feature addition or bug fix.
112
+ * Add tests for it. This is important so I don't break it in a
113
+ future version unintentionally.
114
+ * Commit, do not mess with rakefile, version, or history.
115
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
116
+ * Send me a pull request. Bonus points for topic branches.
117
+
118
+ == Copyright
119
+
120
+ Copyright (c) 2010 Dan Dofter. See LICENSE for details.
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "sg-ruby"
8
+ gem.summary = %Q{A SimpleGeo Ruby Client}
9
+ gem.email = "dan@dofter.com"
10
+ gem.homepage = "http://github.com/archfear/sg-ruby"
11
+ gem.authors = ["Dan Dofter"]
12
+
13
+ gem.add_dependency("oauth", ">= 0.4.0")
14
+ gem.add_dependency("json_pure")
15
+
16
+ gem.add_development_dependency "rspec", ">= 1.2.0"
17
+ gem.add_development_dependency("fakeweb", ">= 1.2.0")
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'spec/rake/spectask'
25
+ Spec::Rake::SpecTask.new(:spec) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.spec_files = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
31
+ spec.libs << 'lib' << 'spec'
32
+ spec.pattern = 'spec/**/*_spec.rb'
33
+ spec.rcov = true
34
+ spec.rcov_opts << "--sort coverage"
35
+ spec.rcov_opts << "--exclude gems,spec"
36
+ end
37
+
38
+ task :spec => :check_dependencies
39
+
40
+ task :default => :spec
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "sg-ruby #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,23 @@
1
+ require 'uri'
2
+ require 'json'
3
+ require 'oauth'
4
+
5
+ require 'simple_geo/hash_utils'
6
+ require 'simple_geo/connection'
7
+ require 'simple_geo/endpoint'
8
+ require 'simple_geo/client'
9
+ require 'simple_geo/record'
10
+
11
+ module SimpleGeo
12
+ API_VERSION = '0.1'.freeze
13
+ REALM = "http://api.simplegeo.com"
14
+ VERSION = File.read(File.join(File.dirname(__FILE__), '..', 'VERSION'))
15
+
16
+ class SimpleGeoError < StandardError; end
17
+ class Unauthorized < SimpleGeoError; end
18
+ class NotFound < SimpleGeoError; end
19
+ class ServerError < SimpleGeoError; end
20
+ class Unavailable < SimpleGeoError; end
21
+ class DecodeError < SimpleGeoError; end
22
+ class NoConnectionEstablished < SimpleGeoError; end
23
+ end
@@ -0,0 +1,166 @@
1
+ module SimpleGeo
2
+
3
+ class Client
4
+
5
+ @@connection = nil
6
+ @@debug = false
7
+
8
+ class << self
9
+
10
+ def set_credentials(token, secret)
11
+ @@connection = Connection.new(token, secret)
12
+ @@connection.debug = @@debug
13
+ end
14
+
15
+ def debug=(debug_flag)
16
+ @@debug = debug_flag
17
+ @@connection.debug = @@debug if @@connection
18
+ end
19
+
20
+ def debug
21
+ @@debug
22
+ end
23
+
24
+ def add_record(record)
25
+ raise SimpleGeoError, "Record has no layer" if record.layer.nil?
26
+ put Endpoint.record(record.layer, record.id), record
27
+ end
28
+
29
+ def delete_record(layer, id)
30
+ delete Endpoint.record(layer, id)
31
+ end
32
+
33
+ def get_record(layer, id)
34
+ record_hash = get Endpoint.record(layer, id)
35
+ record = Record.parse_geojson_hash(record_hash)
36
+ record.layer = layer
37
+ record
38
+ end
39
+
40
+ def add_records(layer, records)
41
+ features = {
42
+ :type => 'FeatureCollection',
43
+ :features => records.collect { |record| record.to_hash }
44
+ }
45
+ post Endpoint.add_records(layer), features
46
+ end
47
+
48
+ # This request currently generates a 500 error if an unknown id is passed in.
49
+ def get_records(layer, ids)
50
+ features_hash = get Endpoint.records(layer, ids)
51
+ records = []
52
+ features_hash['features'].each do |feature_hash|
53
+ record = Record.parse_geojson_hash(feature_hash)
54
+ record.layer = layer
55
+ records << record
56
+ end
57
+ records
58
+ end
59
+
60
+ def get_history(layer, id)
61
+ history_geojson = get Endpoint.history(layer, id)
62
+ history = []
63
+ history_geojson['geometries'].each do |point|
64
+ history << {
65
+ :created => Time.at(point['created']),
66
+ :lat => point['coordinates'][1],
67
+ :lon => point['coordinates'][0]
68
+ }
69
+ end
70
+ history
71
+ end
72
+
73
+ def get_nearby_records(layer, options)
74
+ if options[:geohash]
75
+ endpoint = Endpoint.nearby_geohash(layer, options.delete(:geohash))
76
+ elsif options[:lat] && options[:lon]
77
+ endpoint = Endpoint.nearby_coordinates(layer,
78
+ options.delete(:lat), options.delete(:lon))
79
+ else
80
+ raise SimpleGeoError, "Either geohash or lat and lon is required"
81
+ end
82
+
83
+ options = nil if options.empty?
84
+ features_hash = get(endpoint, options)
85
+ nearby_records = {
86
+ :next_cursor => features_hash['next_cursor'],
87
+ :records => []
88
+ }
89
+ features_hash['features'].each do |feature_hash|
90
+ record = Record.parse_geojson_hash(feature_hash)
91
+ record.layer = layer
92
+ record_info = {
93
+ :distance => feature_hash['distance'],
94
+ :record => record
95
+ }
96
+ nearby_records[:records] << record_info
97
+ end
98
+ nearby_records
99
+ end
100
+
101
+ def get_nearby_address(lat, lon)
102
+ geojson_hash = get Endpoint.nearby_address(lat, lon)
103
+ HashUtils.symbolize_keys geojson_hash['properties']
104
+ end
105
+
106
+ def get_layer_information(layer)
107
+ layer_info = get Endpoint.layer(layer)
108
+ layer_info.delete('selfLink')
109
+ HashUtils.symbolize_keys(layer_info)
110
+ end
111
+
112
+ def get_density(lat, lon, day, hour=nil)
113
+ geojson_hash = get Endpoint.density(lat, lon, day, hour)
114
+ geojson_hash = HashUtils.recursively_symbolize_keys(geojson_hash)
115
+ if hour.nil?
116
+ density_info = []
117
+ geojson_hash[:features].each do |hour_geojson_hash|
118
+ density_info << hour_geojson_hash[:properties].merge(
119
+ {:geometry => hour_geojson_hash[:geometry]})
120
+ end
121
+ density_info
122
+ else
123
+ geojson_hash[:properties].merge({:geometry => geojson_hash[:geometry]})
124
+ end
125
+ end
126
+
127
+ def get_overlaps(south, west, north, east, options=nil)
128
+ info = get Endpoint.overlaps(south, west, north, east), options
129
+ HashUtils.recursively_symbolize_keys(info)
130
+ end
131
+
132
+ # this API call seems to always return a 404
133
+ def get_boundary(id)
134
+ info = get Endpoint.boundary(id)
135
+ HashUtils.recursively_symbolize_keys(info)
136
+ end
137
+
138
+ def get_contains(lat, lon)
139
+ info = get Endpoint.contains(lat, lon)
140
+ HashUtils.recursively_symbolize_keys(info)
141
+ end
142
+
143
+ def get(endpoint, data=nil)
144
+ raise NoConnectionEstablished if @@connection.nil?
145
+ @@connection.get endpoint, data
146
+ end
147
+
148
+ def delete(endpoint, data=nil)
149
+ raise NoConnectionEstablished if @@connection.nil?
150
+ @@connection.delete endpoint, data
151
+ end
152
+
153
+ def post(endpoint, data=nil)
154
+ raise NoConnectionEstablished if @@connection.nil?
155
+ @@connection.post endpoint, data
156
+ end
157
+
158
+ def put(endpoint, data=nil)
159
+ raise NoConnectionEstablished if @@connection.nil?
160
+ @@connection.put endpoint, data
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -0,0 +1,109 @@
1
+ module SimpleGeo
2
+ class Connection
3
+
4
+ attr_accessor :debug
5
+
6
+ def initialize(token, secret)
7
+ consumer = OAuth::Consumer.new(token, secret, :site => REALM)
8
+ @access_token = OAuth::AccessToken.new(consumer)
9
+ debug = false
10
+ end
11
+
12
+ def get(endpoint, data=nil)
13
+ request :get, endpoint, data
14
+ end
15
+
16
+ def delete(endpoint, data=nil)
17
+ request :delete, endpoint, data
18
+ end
19
+
20
+ def post(endpoint, data=nil)
21
+ request :post, endpoint, data
22
+ end
23
+
24
+ def put(endpoint, data=nil)
25
+ request :put, endpoint, data
26
+ end
27
+
28
+ private
29
+
30
+ def request(method, endpoint, data)
31
+ headers = {'User-Agent' => "SimpleGeo Ruby Client v#{VERSION}"}
32
+
33
+ if [:get, :delete].include?(method) && !data.nil?
34
+ endpoint = endpoint + '?' + build_query(data)
35
+ end
36
+
37
+ if debug
38
+ puts "request: #{method.to_s.upcase} #{endpoint}"
39
+ puts "headers:"
40
+ headers.each do |key, value|
41
+ puts "#{key}=#{value}"
42
+ end
43
+ if [:post, :put].include?(method) && !data.nil?
44
+ puts "data:"
45
+ puts data.to_json
46
+ end
47
+ end
48
+
49
+ case method
50
+ when :get, :delete
51
+ response = @access_token.request(method, endpoint, headers)
52
+ when :post, :put
53
+ data = data.to_json unless data.nil?
54
+ response = @access_token.request(method, endpoint, data, headers)
55
+ end
56
+
57
+ if debug
58
+ puts "\nresponse: #{response.code}"
59
+ puts "headers:"
60
+ response.header.each do |key, value|
61
+ puts "#{key}=#{value}"
62
+ end
63
+ puts "body:"
64
+ puts response.body
65
+ end
66
+
67
+ raise_errors(response)
68
+
69
+ if response.body.empty?
70
+ content = nil
71
+ else
72
+ begin
73
+ content = JSON.parse(response.body)
74
+ rescue JSON::ParserError
75
+ raise DecodeError, "content: <#{response.body}>"
76
+ end
77
+ end
78
+
79
+ content
80
+ end
81
+
82
+ def build_query(data)
83
+ data.map do |key, value|
84
+ [key.to_s, URI.escape(value.to_s)].join('=')
85
+ end.join('&')
86
+ end
87
+
88
+ def raise_errors(response)
89
+ response_description = "(#{response.code}): #{response.message}"
90
+ response_description += " - #{response.body}" unless response.body.empty?
91
+
92
+ case response.code.to_i
93
+ when 401
94
+ raise Unauthorized
95
+ when 404
96
+ raise NotFound
97
+ when 500
98
+ raise ServerError, "SimpleGeo had an internal error. Please let them know. #{response_description}"
99
+ when 502..503
100
+ raise Unavailable, response_description
101
+ else
102
+ unless response.is_a? Net::HTTPSuccess
103
+ raise SimpleGeoError, response_description
104
+ end
105
+ end
106
+ end
107
+
108
+ end
109
+ end