taskrabbit 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|