portero 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.simplecov ADDED
@@ -0,0 +1,3 @@
1
+ SimpleCov.start do
2
+ add_filter "lib/portero/version.rb"
3
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in portero.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Robert Rouse
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Portero
2
+
3
+ Portero is another word for someone that may act as a concierge. Portero will help you find venues around a location using as many services known.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'portero'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install portero
18
+
19
+ ## Usage
20
+
21
+ Configure Portero with providers, either in a Rails initializer or elsewhere:
22
+
23
+ Portero.providers = [Portero::SearchProvider::Foursquare.new(client_id: YOUR_ID, client_secret: YOUR_SECRET)]
24
+ Portero.init!
25
+
26
+ If more than one provider is given, it will search both providers and return combined results.
27
+
28
+ To search
29
+
30
+ results = Portero.search("pizza", latitude, longitude, options)
31
+ results #=> Array of Portero::SearchResult
32
+
33
+ Options can be anything the provider allows. All providers may not utilize all options. It's provider implementation dependant
34
+
35
+ SearchResult objects contain the following fields
36
+
37
+ name
38
+ address
39
+ latitude
40
+ longitude
41
+ city
42
+ state
43
+ postal_code
44
+ country
45
+ category
46
+ icon
47
+ extra
48
+
49
+ A provider may put whatever extraneous data that might be important in extra. It is also not forced to provide usable data in all fields.
50
+
51
+ ## Writing a provider
52
+
53
+ A new provider must include Porter::SearchProvider and implement (at minimum) the search method that returns the SearchResult objects. A provider my require specific options that need to be passed to the constructor with requires_option. If those options are not given, an exception is raised. See one of the bundled providers in lib/portero/providers for examples.
54
+
55
+ ## TODO
56
+
57
+ 1. When using multiple providers, parallelize the requests.
58
+ 2. When using multiple providers, analyze the result set and remove duplication as much as possible.
59
+ 3. Always make it better
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,33 @@
1
+ require 'portero/search_provider'
2
+ require 'portero/search_result'
3
+ require 'faraday'
4
+ require 'json'
5
+
6
+ module Portero
7
+ module SearchProvider
8
+ autoload :Foursquare, 'portero/providers/foursquare'
9
+ autoload :GooglePlaces, 'portero/providers/google_places'
10
+ end
11
+
12
+ def self.init!
13
+ @conn = Faraday.new do |builder|
14
+ builder.adapter Faraday.default_adapter
15
+ end
16
+ end
17
+
18
+ def self.search(query, latitude, longitude, options = {})
19
+ results = []
20
+ @providers.each do |provider|
21
+ results += provider.search(@conn, query, latitude, longitude, options)
22
+ end
23
+ results
24
+ end
25
+
26
+ def self.providers=(providers)
27
+ @providers = providers
28
+ end
29
+
30
+ def self.providers
31
+ @providers
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ module Portero
2
+ module SearchProvider
3
+ class Foursquare
4
+ include SearchProvider
5
+
6
+ requires_option :client_id
7
+ requires_option :client_secret
8
+
9
+ def search(conn, query, latitude, longitude, options = {})
10
+ results = conn.get("https://api.foursquare.com/v2/venues/search", {query: query, client_id: @provider_options[:client_id], client_secret: @provider_options[:client_secret], v: "20120709",
11
+ limit: options[:limit], radius: options[:radius], ll: [latitude, longitude].join(",")})
12
+ parse_results(results)
13
+ end
14
+
15
+ def parse_results(results)
16
+ json = JSON.parse(results.body)
17
+ venues = json["response"]["venues"]
18
+ results = []
19
+ venues.each do |found_venue|
20
+ venue = Portero::SearchResult.new
21
+ venue.name = found_venue["name"]
22
+ venue.address = found_venue["location"]["address"]
23
+ venue.latitude = found_venue["location"]["lat"]
24
+ venue.longitude = found_venue["location"]["lng"]
25
+ venue.city = found_venue["location"]["city"]
26
+ venue.state = found_venue["location"]["state"]
27
+ venue.postal_code = found_venue["location"]["postalCode"]
28
+ venue.country = found_venue["location"]["country"]
29
+ venue.category = found_venue["categories"].first["name"]
30
+ venue.icon = found_venue["categories"].first["icon"]["prefix"] + "64" + found_venue["categories"].first["icon"]["suffix"]
31
+ venue.extra = {categories: found_venue["categories"], url: found_venue["url"]}
32
+
33
+ results << venue
34
+ end
35
+ results
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ module Portero
2
+ module SearchProvider
3
+
4
+ class GooglePlaces
5
+ include SearchProvider
6
+
7
+ requires_option :key
8
+
9
+ def search(conn, query, latitude, longitude, options = {})
10
+ results = conn.get("https://maps.googleapis.com/maps/api/place/textsearch/json", {query: query, key: @provider_options[:key],
11
+ radius: options[:radius], location: [latitude, longitude].join(","), sensor: false})
12
+ parse_results(results)
13
+ end
14
+
15
+ def parse_results(results)
16
+ json = JSON.parse(results.body)
17
+ venues = json["results"]
18
+ results = []
19
+ venues.each do |found_venue|
20
+ venue = Portero::SearchResult.new
21
+ venue.name = found_venue["name"]
22
+ venue.address = found_venue["formatted_address"]
23
+ venue.latitude = found_venue["geometry"]["location"]["lat"]
24
+ venue.longitude = found_venue["geometry"]["location"]["lng"]
25
+ venue.city = ""
26
+ venue.state = ""
27
+ venue.postal_code = ""
28
+ venue.country = ""
29
+ venue.category = found_venue["types"].first
30
+ venue.icon = found_venue["icon"]
31
+ venue.extra = {types: found_venue["types"]}
32
+
33
+ results << venue
34
+ end
35
+ results
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
3
+ module Portero
4
+ module SearchProvider
5
+ module ClassMethods
6
+
7
+ def api_not_implemented(klass)
8
+ caller.first.match(/in \`(.+)\'/)
9
+ method_name = $1
10
+ raise InterfaceNotImplementedError.new("#{klass.class.name} needs to implement '#{method_name}'!")
11
+ end
12
+
13
+ def requires_option(key)
14
+ required_options << key
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+
20
+ def initialize(options = {})
21
+ @provider_options = options
22
+ validate_options
23
+ end
24
+
25
+ def search(connection, query, latitude, longitude, options = {})
26
+ self.class.api_not_implemented(self)
27
+ end
28
+
29
+ def validate_options
30
+ self.class.required_options.each do |key|
31
+ raise MissingRequirementError.new("#{self.class.name} needs an options hash key of '#{key}' to function") unless @provider_options.has_key?(key)
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ def self.included(receiver)
38
+ receiver.extend ClassMethods
39
+ receiver.send :include, InstanceMethods
40
+ class << receiver
41
+ class_attribute(:required_options)
42
+ self.required_options = []
43
+ end
44
+ end
45
+
46
+ class InterfaceNotImplementedError < NoMethodError
47
+ end
48
+
49
+ class MissingRequirementError < Exception
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ module Portero
2
+ class SearchResult
3
+ attr_accessor :name, :address, :latitude, :longitude, :city, :state, :postal_code, :country, :category, :icon, :extra
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Portero
2
+ VERSION = "0.0.1"
3
+ end
data/lib/portero.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "portero/version"
2
+ require "portero/portero"
data/portero.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/portero/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Robert Rouse"]
6
+ gem.email = ["robert@theymaybecoders.com"]
7
+ gem.description = %q{Portero will help you find venues around a location using as many services known}
8
+ gem.summary = %q{Portero will help you find venues around a location using as many services known}
9
+ gem.homepage = "http://github.com/theymaybecoders"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "portero"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Portero::VERSION
17
+
18
+ gem.add_development_dependency "rspec", '~>2.0'
19
+ gem.add_development_dependency "shoulda-matchers", '~>1.2'
20
+ gem.add_development_dependency "simplecov"
21
+ gem.add_development_dependency "fakeweb"
22
+
23
+ gem.add_dependency "faraday", '~>0.8'
24
+ gem.add_dependency "activesupport", '~> 3'
25
+ end