fourmer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "httparty", ">= 0"
4
+ gem "hashie", ">= 1.1.0"
5
+
6
+ group :development do
7
+ gem "mocha", ">= 0"
8
+ gem "shoulda", ">= 0"
9
+ gem "bundler", "~> 1.0.0"
10
+ gem "jeweler", "~> 1.6.4"
11
+ gem "rcov", ">= 0"
12
+ end
@@ -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.
@@ -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
+
@@ -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
@@ -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