taskrabbit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +22 -0
  4. data/Guardfile +8 -0
  5. data/README.md +132 -0
  6. data/Rakefile +53 -0
  7. data/lib/taskrabbit.rb +21 -0
  8. data/lib/taskrabbit/account.rb +7 -0
  9. data/lib/taskrabbit/api.rb +47 -0
  10. data/lib/taskrabbit/association.rb +52 -0
  11. data/lib/taskrabbit/city.rb +19 -0
  12. data/lib/taskrabbit/client.rb +47 -0
  13. data/lib/taskrabbit/collection.rb +17 -0
  14. data/lib/taskrabbit/config.rb +43 -0
  15. data/lib/taskrabbit/error.rb +10 -0
  16. data/lib/taskrabbit/location.rb +19 -0
  17. data/lib/taskrabbit/proxy.rb +62 -0
  18. data/lib/taskrabbit/smash.rb +82 -0
  19. data/lib/taskrabbit/task.rb +54 -0
  20. data/lib/taskrabbit/transformer.rb +5 -0
  21. data/lib/taskrabbit/user.rb +26 -0
  22. data/lib/taskrabbit/version.rb +3 -0
  23. data/spec/spec_helper.rb +33 -0
  24. data/spec/support/cassettes/account/no_user.yml +30 -0
  25. data/spec/support/cassettes/account/properties.yml +32 -0
  26. data/spec/support/cassettes/account/tasks.yml +63 -0
  27. data/spec/support/cassettes/account/with_user.yml +32 -0
  28. data/spec/support/cassettes/cities/all.yml +32 -0
  29. data/spec/support/cassettes/cities/find.yml +32 -0
  30. data/spec/support/cassettes/cities/properties.yml +32 -0
  31. data/spec/support/cassettes/errors/404.yml +362 -0
  32. data/spec/support/cassettes/locations/properties.yml +63 -0
  33. data/spec/support/cassettes/tasks/all.yml +32 -0
  34. data/spec/support/cassettes/tasks/create/default.yml +30 -0
  35. data/spec/support/cassettes/tasks/create/using_account.yml +30 -0
  36. data/spec/support/cassettes/tasks/create/with_location.yml +32 -0
  37. data/spec/support/cassettes/tasks/create/without_credit_card.yml +30 -0
  38. data/spec/support/cassettes/tasks/create/without_user.yml +30 -0
  39. data/spec/support/cassettes/tasks/delete.yml +63 -0
  40. data/spec/support/cassettes/tasks/find.yml +32 -0
  41. data/spec/support/cassettes/tasks/properties.yml +32 -0
  42. data/spec/support/cassettes/tasks/save.yml +63 -0
  43. data/spec/support/cassettes/tasks/update.yml +63 -0
  44. data/spec/support/cassettes/tasks/without_client.yml +30 -0
  45. data/spec/support/cassettes/users/find.yml +32 -0
  46. data/spec/support/cassettes/users/properties.yml +32 -0
  47. data/spec/support/cassettes/users/tasks/all.yml +32 -0
  48. data/spec/taskrabbit/account_spec.rb +65 -0
  49. data/spec/taskrabbit/api_spec.rb +34 -0
  50. data/spec/taskrabbit/city_spec.rb +61 -0
  51. data/spec/taskrabbit/collection_spec.rb +17 -0
  52. data/spec/taskrabbit/error_spec.rb +13 -0
  53. data/spec/taskrabbit/location_spec.rb +26 -0
  54. data/spec/taskrabbit/proxy_spec.rb +23 -0
  55. data/spec/taskrabbit/smash_spec.rb +129 -0
  56. data/spec/taskrabbit/task_spec.rb +235 -0
  57. data/spec/taskrabbit/taskrabbit_spec.rb +31 -0
  58. data/spec/taskrabbit/user_spec.rb +70 -0
  59. data/taskrabbit.gemspec +26 -0
  60. metadata +194 -0
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ .idea
4
+ Gemfile.lock
5
+ .rvmrc
6
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --debug
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
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(/^spec\/(.*)_spec\.rb/)
6
+ watch(/^lib\/(.*)\.rb/) { "spec" }
7
+ watch(/^spec\/spec_helper\.rb/) { "spec" }
8
+ end
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,7 @@
1
+ module Taskrabbit
2
+ class Account < User
3
+ def fetch
4
+ reload('get', "account")
5
+ end
6
+ end
7
+ end
@@ -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