taskrabbit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +22 -0
- data/Guardfile +8 -0
- data/README.md +132 -0
- data/Rakefile +53 -0
- data/lib/taskrabbit.rb +21 -0
- data/lib/taskrabbit/account.rb +7 -0
- data/lib/taskrabbit/api.rb +47 -0
- data/lib/taskrabbit/association.rb +52 -0
- data/lib/taskrabbit/city.rb +19 -0
- data/lib/taskrabbit/client.rb +47 -0
- data/lib/taskrabbit/collection.rb +17 -0
- data/lib/taskrabbit/config.rb +43 -0
- data/lib/taskrabbit/error.rb +10 -0
- data/lib/taskrabbit/location.rb +19 -0
- data/lib/taskrabbit/proxy.rb +62 -0
- data/lib/taskrabbit/smash.rb +82 -0
- data/lib/taskrabbit/task.rb +54 -0
- data/lib/taskrabbit/transformer.rb +5 -0
- data/lib/taskrabbit/user.rb +26 -0
- data/lib/taskrabbit/version.rb +3 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/cassettes/account/no_user.yml +30 -0
- data/spec/support/cassettes/account/properties.yml +32 -0
- data/spec/support/cassettes/account/tasks.yml +63 -0
- data/spec/support/cassettes/account/with_user.yml +32 -0
- data/spec/support/cassettes/cities/all.yml +32 -0
- data/spec/support/cassettes/cities/find.yml +32 -0
- data/spec/support/cassettes/cities/properties.yml +32 -0
- data/spec/support/cassettes/errors/404.yml +362 -0
- data/spec/support/cassettes/locations/properties.yml +63 -0
- data/spec/support/cassettes/tasks/all.yml +32 -0
- data/spec/support/cassettes/tasks/create/default.yml +30 -0
- data/spec/support/cassettes/tasks/create/using_account.yml +30 -0
- data/spec/support/cassettes/tasks/create/with_location.yml +32 -0
- data/spec/support/cassettes/tasks/create/without_credit_card.yml +30 -0
- data/spec/support/cassettes/tasks/create/without_user.yml +30 -0
- data/spec/support/cassettes/tasks/delete.yml +63 -0
- data/spec/support/cassettes/tasks/find.yml +32 -0
- data/spec/support/cassettes/tasks/properties.yml +32 -0
- data/spec/support/cassettes/tasks/save.yml +63 -0
- data/spec/support/cassettes/tasks/update.yml +63 -0
- data/spec/support/cassettes/tasks/without_client.yml +30 -0
- data/spec/support/cassettes/users/find.yml +32 -0
- data/spec/support/cassettes/users/properties.yml +32 -0
- data/spec/support/cassettes/users/tasks/all.yml +32 -0
- data/spec/taskrabbit/account_spec.rb +65 -0
- data/spec/taskrabbit/api_spec.rb +34 -0
- data/spec/taskrabbit/city_spec.rb +61 -0
- data/spec/taskrabbit/collection_spec.rb +17 -0
- data/spec/taskrabbit/error_spec.rb +13 -0
- data/spec/taskrabbit/location_spec.rb +26 -0
- data/spec/taskrabbit/proxy_spec.rb +23 -0
- data/spec/taskrabbit/smash_spec.rb +129 -0
- data/spec/taskrabbit/task_spec.rb +235 -0
- data/spec/taskrabbit/taskrabbit_spec.rb +31 -0
- data/spec/taskrabbit/user_spec.rb +70 -0
- data/taskrabbit.gemspec +26 -0
- metadata +194 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in taskrabbit.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem 'rake'
|
8
|
+
gem 'yard'
|
9
|
+
gem 'ruby-debug'
|
10
|
+
gem 'pry'
|
11
|
+
gem 'awesome_print'
|
12
|
+
end
|
13
|
+
|
14
|
+
group :test do
|
15
|
+
gem 'guard'
|
16
|
+
gem 'guard-rspec'
|
17
|
+
gem 'rb-fsevent'
|
18
|
+
gem 'growl'
|
19
|
+
gem 'vcr'
|
20
|
+
gem 'fakeweb'
|
21
|
+
gem 'rcov', :require => nil
|
22
|
+
end
|
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# TaskRabbit Ruby Gem
|
2
|
+
|
3
|
+
Ruby wrapper for TaskRabbit API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
I have not published the gem yet so you will either have to vendor it or use a Gemfile:
|
8
|
+
|
9
|
+
gem 'taskrabbit', :git => 'git://github.com/jrichardlai/taskrabbit.git'
|
10
|
+
|
11
|
+
|
12
|
+
## Usage Example
|
13
|
+
|
14
|
+
### Configuration
|
15
|
+
|
16
|
+
In an initializer file.
|
17
|
+
|
18
|
+
Taskrabbit.configure do |config|
|
19
|
+
config.client_secret = 'your-client-secret'
|
20
|
+
end
|
21
|
+
|
22
|
+
Available configuration options:
|
23
|
+
|
24
|
+
* client_secret: client secret that has been given to you by TaskRabbit
|
25
|
+
* base_uri: uri of the server (not mandatory, default to www.taskrabbit.com)
|
26
|
+
* endpoint: endpoint (not mandatory, default to api/v1)
|
27
|
+
|
28
|
+
## Task
|
29
|
+
|
30
|
+
### use the API client
|
31
|
+
|
32
|
+
tr = Taskrabbit::Api.new
|
33
|
+
|
34
|
+
or with a user token
|
35
|
+
|
36
|
+
tr = Taskrabbit::Api.new(user_token)
|
37
|
+
|
38
|
+
### Get the list of all the tasks
|
39
|
+
|
40
|
+
tr = Taskrabbit::Api.new
|
41
|
+
# to get the /tasks
|
42
|
+
tasks = tr.tasks.all
|
43
|
+
# fetch the first task
|
44
|
+
tasks.first
|
45
|
+
|
46
|
+
tasks.all(:reload => true) # => will redo the query
|
47
|
+
|
48
|
+
### Find a task
|
49
|
+
|
50
|
+
tr = Taskrabbit::Api.new
|
51
|
+
t = tr.tasks.find(31231) # This actually wont do the request
|
52
|
+
|
53
|
+
To request the API:
|
54
|
+
|
55
|
+
t.fetch # force fetching
|
56
|
+
|
57
|
+
or simply access a property:
|
58
|
+
|
59
|
+
t.name # will do the query
|
60
|
+
|
61
|
+
### Find the tasks of an user
|
62
|
+
|
63
|
+
tr.users.find(user_id).tasks
|
64
|
+
|
65
|
+
### Create a task
|
66
|
+
|
67
|
+
tr = Taskrabbit::Api.new(user_token)
|
68
|
+
task = tr.tasks.create({:named_price => 32, :name => 'Ikea'})
|
69
|
+
|
70
|
+
or
|
71
|
+
|
72
|
+
task = tr.tasks.new({:named_price => 32, :name => 'Ikea'})
|
73
|
+
task.save
|
74
|
+
|
75
|
+
### Update a task
|
76
|
+
|
77
|
+
task = tr.tasks.find(32121)
|
78
|
+
task.named_price = 45
|
79
|
+
task.save
|
80
|
+
|
81
|
+
### Error for tasks creation or update
|
82
|
+
|
83
|
+
tr = Taskrabbit::Api.new(client_secret)
|
84
|
+
task = tr.tasks.new
|
85
|
+
unless task.save
|
86
|
+
task.error # => "Task title can't be blank, \nAmount you are willing to pay is not a number"
|
87
|
+
task.errors # => { "messages" => ["Task title can't be blank", "Amount you are willing to pay is not a number"],
|
88
|
+
"fields" => [["name","can't be blank"], ["named_price","is not a number"]] }
|
89
|
+
end
|
90
|
+
|
91
|
+
### Redirect
|
92
|
+
|
93
|
+
In some case TaskRabbit will return an url which should be used for further operations (i.e: when the user doesn't have a credit card).
|
94
|
+
|
95
|
+
tr = Taskrabbit::Api.new(client_secret)
|
96
|
+
task = tr.tasks.new
|
97
|
+
unless task.save
|
98
|
+
if task.redirect?
|
99
|
+
task.redirect_url #=> 'http://www.taskrabbit.com/somepath'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
## User account
|
104
|
+
|
105
|
+
tr = Taskrabbit::Api.new(client_secret)
|
106
|
+
tr.account # => Taskrabbit::User object
|
107
|
+
|
108
|
+
tr.account.tasks # => List of tasks
|
109
|
+
tr.account.tasks.create(some_params)
|
110
|
+
|
111
|
+
## Cities
|
112
|
+
|
113
|
+
### Get list of cities
|
114
|
+
|
115
|
+
tr.cities.each do |city|
|
116
|
+
city.name
|
117
|
+
end
|
118
|
+
|
119
|
+
### Find a city using the id
|
120
|
+
|
121
|
+
tr.cities.find(3).name # => "SF Bay Area"
|
122
|
+
|
123
|
+
### More informations
|
124
|
+
|
125
|
+
More informations: http://taskrabbit.github.com
|
126
|
+
|
127
|
+
## TODO
|
128
|
+
|
129
|
+
Add:
|
130
|
+
|
131
|
+
- Picture and sound upload
|
132
|
+
- Pages
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
require 'rspec/core'
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
|
10
|
+
spec = eval(File.read('taskrabbit.gemspec'))
|
11
|
+
|
12
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
13
|
+
pkg.gem_spec = spec
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Cleans out the documentation and cache'
|
17
|
+
task :clobber_doc do
|
18
|
+
rm_r '.yardoc' rescue nil
|
19
|
+
rm_r 'doc' rescue nil
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
desc 'Clear out YARD docs and generated packages'
|
24
|
+
task :clean => [:clobber_package, :clobber_doc]
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'yard'
|
28
|
+
YARD::Rake::YardocTask.new :doc
|
29
|
+
rescue LoadError => e
|
30
|
+
task :doc do
|
31
|
+
warn 'YARD is not available, to generate documentation please install yard.'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
begin
|
37
|
+
require 'ci/reporter/rake/rspec'
|
38
|
+
rescue LoadError
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :spec
|
42
|
+
|
43
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
44
|
+
RSpec::Core::RakeTask.new(:spec)
|
45
|
+
|
46
|
+
namespace :spec do
|
47
|
+
desc "Run all specs with rcov"
|
48
|
+
RSpec::Core::RakeTask.new(:rcov) do |t|
|
49
|
+
t.rcov = true
|
50
|
+
t.pattern = "./spec/**/*_spec.rb"
|
51
|
+
t.rcov_opts = '--exclude spec/,/gems/,/Library/,/usr/,lib/tasks,.bundle,config,/lib/rspec/,/lib/rspec-'
|
52
|
+
end
|
53
|
+
end
|
data/lib/taskrabbit.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Taskrabbit
|
2
|
+
autoload :Client, "taskrabbit/client"
|
3
|
+
autoload :Association, "taskrabbit/association"
|
4
|
+
autoload :Config, "taskrabbit/config"
|
5
|
+
autoload :Version, "taskrabbit/version"
|
6
|
+
autoload :Error, "taskrabbit/error"
|
7
|
+
autoload :Proxy, "taskrabbit/proxy"
|
8
|
+
autoload :Transformer, "taskrabbit/transformer"
|
9
|
+
autoload :Smash, "taskrabbit/smash"
|
10
|
+
autoload :Collection, "taskrabbit/collection"
|
11
|
+
autoload :Api, "taskrabbit/api"
|
12
|
+
autoload :Task, "taskrabbit/task"
|
13
|
+
autoload :Account, "taskrabbit/account"
|
14
|
+
autoload :User, "taskrabbit/user"
|
15
|
+
autoload :City, "taskrabbit/city"
|
16
|
+
autoload :Location, "taskrabbit/location"
|
17
|
+
|
18
|
+
extend Config
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'api_smith'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Taskrabbit
|
2
|
+
class Api
|
3
|
+
include Client
|
4
|
+
include Association
|
5
|
+
|
6
|
+
attr_accessor :user_token
|
7
|
+
attr_accessor *Config::VALID_OPTIONS_KEYS
|
8
|
+
|
9
|
+
has_many :users, User
|
10
|
+
has_many :tasks, Task, :on => 'tasks'
|
11
|
+
has_many :cities, City
|
12
|
+
|
13
|
+
def account
|
14
|
+
@account ||= Account.new({}, self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(user_token = nil, attrs = {})
|
18
|
+
attrs = Taskrabbit.options.merge(attrs)
|
19
|
+
# set the configuration for the api
|
20
|
+
Config::VALID_OPTIONS_KEYS.each do |key|
|
21
|
+
instance_variable_set("@#{key}".to_sym, attrs[key])
|
22
|
+
end
|
23
|
+
self.user_token = user_token if user_token
|
24
|
+
end
|
25
|
+
|
26
|
+
def request(method, path, transformer, options = {})
|
27
|
+
send(method, path, request_params(transformer, options))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def request_params(transformer, options = {})
|
33
|
+
{
|
34
|
+
:transform => transformer,
|
35
|
+
:extra_body => options,
|
36
|
+
:extra_request => {
|
37
|
+
:headers => {
|
38
|
+
'X-Client-Application' => client_secret.to_s,
|
39
|
+
'Authorization' => "OAuth #{user_token.to_s}"
|
40
|
+
},
|
41
|
+
:endpoint => endpoint.to_s,
|
42
|
+
:base_uri => base_uri.to_s
|
43
|
+
}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Taskrabbit
|
2
|
+
module Association
|
3
|
+
def self.included(base)
|
4
|
+
base.const_set('PATHS', {})
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
extend ClassMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# define has many that will proxy the association to the class
|
13
|
+
# if the association has been loaded previously, no proxy will be used
|
14
|
+
def has_many(association, klass, options = {})
|
15
|
+
class_eval <<-"END"
|
16
|
+
def #{association}
|
17
|
+
return self[:#{association}] if property_present?(:#{association})
|
18
|
+
@#{association} ||= Proxy.new(self, #{klass})
|
19
|
+
end
|
20
|
+
END
|
21
|
+
self::PATHS[klass] = options[:on]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module InstanceMethods
|
26
|
+
|
27
|
+
# return the association path on the api that correspond to the class
|
28
|
+
def association_path(klass)
|
29
|
+
case paths[klass]
|
30
|
+
when String
|
31
|
+
paths[klass]
|
32
|
+
when Proc
|
33
|
+
paths[klass][self]
|
34
|
+
else
|
35
|
+
raise Error.new("Action not defined for #{self.class} on the #{klass} association")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# check if the property has been loaded
|
42
|
+
def property_present?(property)
|
43
|
+
respond_to?(:loaded) and loaded and self.class.property?(property) and self[property]
|
44
|
+
end
|
45
|
+
|
46
|
+
def paths
|
47
|
+
self.class::PATHS
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Taskrabbit
|
2
|
+
class City < Smash
|
3
|
+
property :id
|
4
|
+
property :name
|
5
|
+
property :lat
|
6
|
+
property :lng
|
7
|
+
property :links
|
8
|
+
|
9
|
+
def fetch
|
10
|
+
reload('get', "cities/#{id.to_s}")
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def all(scope, options = {})
|
15
|
+
scope.request('get', 'cities', Api::collection_transformers[self], options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Taskrabbit
|
2
|
+
module Client
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include APISmith::Client
|
6
|
+
include InstanceMethods
|
7
|
+
extend ClassMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def collection_transformers
|
13
|
+
@collection_transformers ||= Hash.new do |h, k|
|
14
|
+
h[k] = Class.new(Collection).tap do |klass|
|
15
|
+
klass.transformer_for :items, k
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module InstanceMethods
|
22
|
+
# monkey patch APISmith::Client transform_response to set the api to the object
|
23
|
+
def transform_response(response, options)
|
24
|
+
transformer = options[:transform] || options[:transformer]
|
25
|
+
if transformer
|
26
|
+
obj = transformer.call response
|
27
|
+
obj.api = self if obj.respond_to?(:api=)
|
28
|
+
obj
|
29
|
+
else
|
30
|
+
response
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# check if an error has occured
|
35
|
+
def check_response_errors(response)
|
36
|
+
return unless net_http_response = response.response rescue nil
|
37
|
+
return unless [Net::HTTPClientError, Net::HTTPServerError].include?(net_http_response.class.superclass)
|
38
|
+
|
39
|
+
response_hash = response.to_hash
|
40
|
+
error = response_hash.fetch('error') { "#{net_http_response.code} #{net_http_response.message}".strip }
|
41
|
+
|
42
|
+
raise Smash::Error.new(error, response) if response_hash['errors']
|
43
|
+
raise Taskrabbit::Error.new(error, response)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Taskrabbit
|
2
|
+
class Collection < APISmith::Smash
|
3
|
+
property :items
|
4
|
+
property :links
|
5
|
+
|
6
|
+
ARRAY_METHODS = %w{first last count size length each}.freeze
|
7
|
+
|
8
|
+
alias :all :items
|
9
|
+
|
10
|
+
# define array methods for the collection and delegate it to items
|
11
|
+
ARRAY_METHODS.each do |method|
|
12
|
+
define_method(method) do |*args, &block|
|
13
|
+
items.send(method, *args, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|