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.
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