fourmer 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +27 -0
- data/.document +5 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +20 -0
- data/README.md +124 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/fourmer.rb +20 -0
- data/lib/foursquare/base.rb +32 -0
- data/lib/foursquare/campaign.rb +49 -0
- data/lib/foursquare/campaigns.rb +25 -0
- data/lib/foursquare/consumer.rb +62 -0
- data/lib/foursquare/errors.rb +24 -0
- data/lib/foursquare/model.rb +32 -0
- data/lib/foursquare/requests.rb +49 -0
- data/lib/foursquare/special.rb +39 -0
- data/lib/foursquare/specials.rb +33 -0
- data/lib/foursquare/timeseries.rb +13 -0
- data/lib/foursquare/venue.rb +52 -0
- data/lib/foursquare/venue_group.rb +31 -0
- data/lib/foursquare/venue_groups.rb +26 -0
- data/lib/foursquare/venue_stats.rb +19 -0
- data/lib/foursquare/venues.rb +38 -0
- data/test/helper.rb +19 -0
- data/test/test_base.rb +62 -0
- data/test/test_campaign.rb +36 -0
- data/test/test_campaigns.rb +46 -0
- data/test/test_consumer.rb +107 -0
- data/test/test_errors.rb +17 -0
- data/test/test_fourmer.rb +7 -0
- data/test/test_model.rb +31 -0
- data/test/test_special.rb +28 -0
- data/test/test_specials.rb +71 -0
- data/test/test_timeseries.rb +13 -0
- data/test/test_venue.rb +39 -0
- data/test/test_venue_group.rb +38 -0
- data/test/test_venue_groups.rb +47 -0
- data/test/test_venue_stats.rb +11 -0
- data/test/test_venues.rb +66 -0
- metadata +166 -0
data/.autotest
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'autotest/growl'
|
2
|
+
require 'autotest/fsevent'
|
3
|
+
require 'redgreen/autotest'
|
4
|
+
require 'autotest/restart'
|
5
|
+
|
6
|
+
module Autotest::Growl
|
7
|
+
def self.growl title, msg, img, pri=0, stick=""
|
8
|
+
system "growlnotify -n autotest --image #{img} -p #{pri} -m #{ msg.inspect} #{title} #{stick}"
|
9
|
+
end
|
10
|
+
|
11
|
+
Autotest.add_hook :ran_command do |autotest|
|
12
|
+
filtered = autotest.results.grep(/\d+\s.*tests?/)
|
13
|
+
|
14
|
+
output = filtered.empty? ? "" : filtered.last.slice(/(\d+)\s.*tests?,\s(\d+)\s.*assertions?,\s(\d+)\s.*failures?,\s(\d+)\s.*errors?/)
|
15
|
+
if output =~ /[1-9]\sfailures?/ or output =~ /[1-9]\serrors?/
|
16
|
+
growl "Test Results", "#{output}", "~/Library/Autotest/rails_fail.png"
|
17
|
+
else
|
18
|
+
growl "Test Results", "#{output}", "~/Library/Autotest/rails_ok.png"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
Autotest.add_hook :initialize do |at|
|
26
|
+
%w{.svn .hg .git vendor}.each {|exception| at.add_exception(exception)}
|
27
|
+
end
|
data/.document
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Tim Olshansky
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# fourmer
|
2
|
+
|
3
|
+
fourmer is an unofficial wrapper for the [Foursquare Merchant API](https://developer.foursquare.com/merchant/).
|
4
|
+
|
5
|
+
fourmer makes it easy for you to create new campaigns, specials and venue groups for the venues you manage.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Fourmer is distributed as a gem, which is how it should be used in your app. Assuming you have rubygems or bundler, simply do the following:
|
10
|
+
|
11
|
+
gem install fourmer
|
12
|
+
|
13
|
+
Or include the gem in your Gemfile.
|
14
|
+
|
15
|
+
gem "fourmer", ">= 0"
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Fourmer is designed to be as simple to use as possible, to mimic the layout and actions available in the Foursquare Merchant API.
|
20
|
+
|
21
|
+
### Web Applications
|
22
|
+
|
23
|
+
Initialize your consumer with userless access:
|
24
|
+
|
25
|
+
merchant = Foursquare::Merchant::Consumer.new('CLIENT_ID', 'CLIENT_SECRET')
|
26
|
+
|
27
|
+
Redirect users to the Foursquare authentication page:
|
28
|
+
|
29
|
+
# If using Rails:
|
30
|
+
redirect_to merchant.authorize_url('YOUR_CALLBACK_URL')
|
31
|
+
|
32
|
+
# Otherwise, redirect your users to:
|
33
|
+
merchant.authorize_url('YOUR_CALLBACK_URL')
|
34
|
+
|
35
|
+
The user will then be asked whether to authorize your application. If the user accepts, then Foursquare will redirect the user to your callback url (`YOUR_CALLBACK_URL`) with a `code` in the url. You then exchange this `code` for an access token:
|
36
|
+
|
37
|
+
access_token = merchant.access_token(params[:code], 'YOUR_CALLBACK_URL')
|
38
|
+
|
39
|
+
You can then make requests on the user's behalf by re-initializing your consumer:
|
40
|
+
|
41
|
+
merchant = Foursquare::Merchant::Consumer.new(access_token)
|
42
|
+
|
43
|
+
For a description of the Foursquare Authentication flow, go [here](https://developer.foursquare.com/merchant/oauth.html).
|
44
|
+
|
45
|
+
### Quick Start
|
46
|
+
|
47
|
+
Initialize your consumer:
|
48
|
+
|
49
|
+
merchant = Foursquare::Merchant::Base.new('ACCESS_TOKEN')
|
50
|
+
|
51
|
+
Or for userless access (only works for some objects and actions, such as venue search):
|
52
|
+
|
53
|
+
merchant = Foursquare::Merchant::Base.new('CLIENT_ID', 'CLIENT_SECRET')
|
54
|
+
|
55
|
+
#### Campaigns
|
56
|
+
|
57
|
+
Find an existing campaign:
|
58
|
+
|
59
|
+
campaign = merchant.campaigns.find('campaign_id')
|
60
|
+
#=> #<Foursquare::Merchant::Campaign: ...>
|
61
|
+
|
62
|
+
Delete an existing campaign that has never been activated:
|
63
|
+
|
64
|
+
campaign.delete
|
65
|
+
#=> nil
|
66
|
+
|
67
|
+
#### Specials
|
68
|
+
|
69
|
+
Get a list of all specials for the current user:
|
70
|
+
|
71
|
+
specials = merchant.specials.list
|
72
|
+
#=> [#<Foursquare::Merchant::Special: ...>, ...]
|
73
|
+
|
74
|
+
Create a new special:
|
75
|
+
|
76
|
+
params = { 'text' => 'Some text', 'unlockedText' => 'Some other text', ... }
|
77
|
+
special = merchant.specials.add(params)
|
78
|
+
#=> #<Foursquare::Merchant::Special: ...>
|
79
|
+
|
80
|
+
#### Venuegroups
|
81
|
+
|
82
|
+
Create a new venue group:
|
83
|
+
|
84
|
+
venuegroup = merchant.venue_groups.add('NAME_OF_VENUEGROUP')
|
85
|
+
#=> #<Foursquare::Merchant::Venuegroup: ...>
|
86
|
+
|
87
|
+
Add venues to it:
|
88
|
+
|
89
|
+
venues = [#<Foursquare::Merchant::Venue: ...>, ...]
|
90
|
+
venuegroup.addvenue(venues)
|
91
|
+
#=> nil
|
92
|
+
|
93
|
+
Note that venues can either be a single venue id string/venue object, an array of venue ids, or an array of venue objects.
|
94
|
+
|
95
|
+
#### Venues
|
96
|
+
|
97
|
+
Get a list of all of the venues managed by the user:
|
98
|
+
|
99
|
+
managed_venues = merchant.venues.managed
|
100
|
+
#=> [#<Foursquare::Merchant::Venue: ...>, ...]
|
101
|
+
|
102
|
+
Edit a venue managed by the user:
|
103
|
+
|
104
|
+
venue = merchant.venues.find('VENUE_ID')
|
105
|
+
venue.update({'url' => 'http://some.domain.com/'})
|
106
|
+
#=> #<Foursquare::Merchant::Venue: ...>
|
107
|
+
|
108
|
+
See the [Foursquare Merchant API](https://developer.foursquare.com/merchant/) for further actions available for campaigns, specials, venuegroups and venues.
|
109
|
+
|
110
|
+
## Contributing to fourmer
|
111
|
+
|
112
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
113
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
114
|
+
* Fork the project
|
115
|
+
* Start a feature/bugfix branch
|
116
|
+
* Commit and push until you are happy with your contribution
|
117
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
118
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
119
|
+
|
120
|
+
## Copyright
|
121
|
+
|
122
|
+
Copyright (c) 2011 Tim Olshansky. See LICENSE.txt for
|
123
|
+
further details.
|
124
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "fourmer"
|
18
|
+
gem.homepage = "http://github.com/timols/fourmer"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{An unofficial wrapper for the Foursquare Merchant API}
|
21
|
+
gem.description = %Q{An easy to use library for interacting with the Foursquare Merchant API}
|
22
|
+
gem.email = "tim.olshansky@gmail.com"
|
23
|
+
gem.authors = ["Tim Olshansky"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "fourmer #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/lib/fourmer.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hashie'
|
5
|
+
require 'httparty'
|
6
|
+
require 'foursquare/requests'
|
7
|
+
require 'foursquare/base'
|
8
|
+
require 'foursquare/model'
|
9
|
+
require 'foursquare/campaigns'
|
10
|
+
require 'foursquare/campaign'
|
11
|
+
require 'foursquare/consumer'
|
12
|
+
require 'foursquare/errors'
|
13
|
+
require 'foursquare/specials'
|
14
|
+
require 'foursquare/special'
|
15
|
+
require 'foursquare/timeseries'
|
16
|
+
require 'foursquare/venue_groups'
|
17
|
+
require 'foursquare/venue_group'
|
18
|
+
require 'foursquare/venues'
|
19
|
+
require 'foursquare/venue'
|
20
|
+
require 'foursquare/venue_stats'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Foursquare
|
2
|
+
module Merchant
|
3
|
+
|
4
|
+
class Base
|
5
|
+
include HTTParty
|
6
|
+
include Merchant::Requests
|
7
|
+
|
8
|
+
base_uri Merchant::Requests::API
|
9
|
+
format :json
|
10
|
+
|
11
|
+
attr_accessor :consumer
|
12
|
+
def initialize(consumer)
|
13
|
+
@consumer = consumer
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def listify(venues)
|
18
|
+
case venues
|
19
|
+
when Array
|
20
|
+
venues.join(',')
|
21
|
+
when String
|
22
|
+
venues
|
23
|
+
else
|
24
|
+
raise ArgumentError, "Please ensure you're attempting to use either an " +
|
25
|
+
"array of venue ids, or a comma delimited list of ids"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Foursquare
|
2
|
+
module Merchant
|
3
|
+
|
4
|
+
class Campaign < Model
|
5
|
+
property :venue_group_ids, :from => :venueGroupIds
|
6
|
+
property :starts_at, :from => :startsAt
|
7
|
+
property :ends_at, :from => :endsAt
|
8
|
+
property :venue_ids, :from => :venues
|
9
|
+
property :special_id, :from => :specialId
|
10
|
+
property :venue_groups, :from => :venueGroups
|
11
|
+
property :id
|
12
|
+
property :special
|
13
|
+
|
14
|
+
def initialize(hash, consumer)
|
15
|
+
super
|
16
|
+
self.venue_ids = self.venue_ids['items'].map { |item| item['id'] } if self.venue_ids
|
17
|
+
self.venue_group_ids = self.venue_group_ids['items'].map { |item| item['id'] } if self.venue_group_ids
|
18
|
+
self.venue_groups = self.venue_groups['items'].map { |item| item['id'] } if self.venue_groups
|
19
|
+
self.special = Special.new(self.special, consumer) if self.special
|
20
|
+
end
|
21
|
+
|
22
|
+
def timeseries(start_time=nil, end_time=nil)
|
23
|
+
params = {}
|
24
|
+
params[:start_at] = start_time if start_time
|
25
|
+
params[:end_at] = end_time if end_time
|
26
|
+
|
27
|
+
response = self.get("/campaigns/#{id}/timeseries", params)['timeseries']
|
28
|
+
response.map { |ts| TimeSeries.new(ts) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def start(start_time=nil)
|
32
|
+
params = {}
|
33
|
+
params[:start_at] = start_time if start_time
|
34
|
+
self.post("/campaigns/#{id}/start", params)
|
35
|
+
end
|
36
|
+
|
37
|
+
def end(end_time=nil)
|
38
|
+
params = {}
|
39
|
+
params[:end_at] = end_time if end_time
|
40
|
+
self.post("/campaigns/#{id}/start", params)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete
|
44
|
+
self.post("/campaigns/#{id}/delete", {})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Foursquare
|
2
|
+
module Merchant
|
3
|
+
|
4
|
+
class Campaigns < Base
|
5
|
+
base_uri "#{API}/campaigns"
|
6
|
+
|
7
|
+
def find(campaign_id, params={})
|
8
|
+
response = self.get("/#{campaign_id}", params)
|
9
|
+
Foursquare::Merchant::Campaign.new(response['campaign'], @consumer)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(params)
|
13
|
+
response = self.post("/add", params)
|
14
|
+
Foursquare::Merchant::Campaign.new(response['campaign'], @consumer)
|
15
|
+
end
|
16
|
+
|
17
|
+
def list(params={})
|
18
|
+
response = self.get("/list", params)
|
19
|
+
campaigns = response['campaigns']['items']
|
20
|
+
campaigns.map { |item| Foursquare::Merchant::Campaign.new(item, @consumer) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Foursquare
|
2
|
+
module Merchant
|
3
|
+
|
4
|
+
class Consumer < Base
|
5
|
+
base_uri Merchant::Requests::OAUTH
|
6
|
+
|
7
|
+
attr_reader :oauth_token, :client_id, :client_secret
|
8
|
+
def initialize(*args)
|
9
|
+
if args.size == 1
|
10
|
+
@oauth_token = args.first
|
11
|
+
elsif args.size == 2
|
12
|
+
@client_id, @client_secret = args
|
13
|
+
else
|
14
|
+
raise ArgumentError, "Please include either an access token or client id & secret"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def authorize_url(callback_url)
|
19
|
+
raise ArgumentError.new("Please use a valid client_id") unless @client_id
|
20
|
+
if callback_url =~ /http/
|
21
|
+
query = {
|
22
|
+
:client_id => @client_id,
|
23
|
+
:response_type => 'code',
|
24
|
+
:redirect_uri => callback_url
|
25
|
+
}.map { |k,v| "#{k}=#{v}"}.join('&')
|
26
|
+
"#{OAUTH}/authenticate?#{query}"
|
27
|
+
else
|
28
|
+
raise ArgumentError, "Please ensure you specify a valid callback url, including the protocol (http://)"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def access_token(code, callback_url)
|
33
|
+
raise "Please ensure you've defined the client_id" if @client_id.nil?
|
34
|
+
raise "Please ensure you've defined the client_secret" if @client_secret.nil?
|
35
|
+
raise "No code was provided" if code.nil?
|
36
|
+
raise "Invalid callback url" if (callback_url.nil? or callback_url.scan(/http\:\/\//).empty?)
|
37
|
+
|
38
|
+
query = {
|
39
|
+
:client_id => @client_id,
|
40
|
+
:client_secret => @client_secret,
|
41
|
+
:grant_type => 'authorization_code',
|
42
|
+
:redirect_uri => callback_url,
|
43
|
+
:code => code
|
44
|
+
}
|
45
|
+
request = self.class.get("/access_token", {:query => query})
|
46
|
+
if request.code == 200
|
47
|
+
request.parsed_response['access_token']
|
48
|
+
else
|
49
|
+
raise Errors::OAuthError, request.parsed_response['error']
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
%w(campaigns specials venue_groups venues).each do |endpoint|
|
54
|
+
define_method "#{endpoint}" do
|
55
|
+
elem = camel_case(endpoint)
|
56
|
+
Foursquare::Merchant.module_eval("#{elem}").new(self)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|