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
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe Taskrabbit::Smash do
4
+
5
+ let(:api) { mock }
6
+ subject { Taskrabbit::Something.new({:name => 'Something'}, api) }
7
+
8
+ module Taskrabbit
9
+ class Something < Smash
10
+ property :name
11
+ end
12
+ end
13
+
14
+ describe ".find" do
15
+ it "should raise error if there is no id passed" do
16
+ expect {
17
+ Taskrabbit::Something.find(Taskrabbit::Api.new, nil)
18
+ }.to raise_error(Taskrabbit::Error, "Couldn't find Taskrabbit::Something without an ID")
19
+ end
20
+ end
21
+
22
+ it "should be not loaded by default" do
23
+ subject.loaded.should be_false
24
+ end
25
+
26
+ describe "#redirect_url" do
27
+ it "should return links['redirect']" do
28
+ subject.stub(:links => {'redirect' => 'something'})
29
+ subject.redirect_url.should == 'something'
30
+ end
31
+
32
+ it "should not throw error if links is empty" do
33
+ expect { subject.redirect_url }.to_not raise_error
34
+ end
35
+ end
36
+
37
+ describe "#redirect?" do
38
+ it "should return false if redirect_url is empty" do
39
+ subject.redirect?.should == false
40
+ end
41
+
42
+ it "should return true if redirect_url is not empty" do
43
+ subject.stub(:redirect_url => "something")
44
+ subject.redirect?.should == true
45
+ end
46
+ end
47
+
48
+ describe "#valid?" do
49
+ it "should return true" do
50
+ subject.should be_valid
51
+ end
52
+
53
+ it "should return false if errors is filled" do
54
+ subject.errors = ["something"]
55
+ subject.should_not be_valid
56
+ end
57
+
58
+ it "should return false if error is filled" do
59
+ subject.error = "something"
60
+ subject.should_not be_valid
61
+ end
62
+ end
63
+
64
+ describe "#reload" do
65
+
66
+ context "the request is valid" do
67
+ before do
68
+ api.stub(:request => {"name" => 'Test', "other" => 'Other Test'})
69
+ end
70
+
71
+ it "should return true" do
72
+ subject.reload(anything, anything).should be_true
73
+ end
74
+
75
+ it "should merge the object with the response" do
76
+ subject.reload(anything, anything)
77
+ subject.should == {"name" => 'Test', "other" => 'Other Test'}
78
+ end
79
+
80
+ it "should set loaded to true" do
81
+ subject.reload(anything, anything)
82
+ subject.loaded.should be_true
83
+ end
84
+ end
85
+
86
+ context "there is an error" do
87
+ before do
88
+ api.stub(:request).and_raise(Taskrabbit::Error.new("Something happened", {:something => "something"}))
89
+ end
90
+
91
+ it "should raise an exception" do
92
+ expect { subject.reload(anything, anything) }.to raise_error(Taskrabbit::Error, 'Something happened')
93
+ end
94
+
95
+ end
96
+
97
+ context "there is errors in attributes" do
98
+ let(:response) {
99
+ {
100
+ "errors" => {
101
+ "messages" => ["Task title can't be blank", "Amount you are willing to pay is not a number"],
102
+ "fields" => [["name","can't be blank"], ["named_price","is not a number"]]
103
+ },
104
+ "error" => "Task title can't be blank, \nAmount you are willing to pay is not a number"
105
+ }
106
+ }
107
+
108
+ before do
109
+ api.stub(:request).and_raise(Taskrabbit::Smash::Error.new("Error Message", response))
110
+ end
111
+
112
+ it "should catch taskrabbit errors and return false" do
113
+ subject.reload(anything, anything)
114
+ subject.should == {"name" => 'Something'}.merge(response)
115
+ end
116
+
117
+ it "should return false" do
118
+ subject.reload(anything, anything).should be_false
119
+ end
120
+ end
121
+
122
+ it "should empty errors if request does not raise exception" do
123
+ subject.error = "something"
124
+ api.stub(:request => {:name => 'Test', :other => 'Other Test'})
125
+ subject.reload(anything, anything).should be_true
126
+ subject.error.should be_nil
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,235 @@
1
+ require 'spec_helper'
2
+
3
+ describe Taskrabbit::Task do
4
+
5
+ describe "task properties" do
6
+
7
+ before :all do
8
+ tr = Taskrabbit::Api.new
9
+ VCR.use_cassette('tasks/properties', :record => :new_episodes) do
10
+ @tr_task = tr.tasks.find(22545)
11
+ @tr_task.fetch
12
+ end
13
+ end
14
+
15
+ subject { @tr_task }
16
+
17
+ its(:id) { should == 22545 }
18
+ its(:name) { should == "2 Hours of House Cleaning + 1 Hour of House Chores" }
19
+ its(:user) { should be_instance_of(Taskrabbit::User) }
20
+ its(:runner) { should be_instance_of(Taskrabbit::User) }
21
+ its(:runners) { should be_a(Taskrabbit::Collection) }
22
+ its(:cost_in_cents) { should == 0 }
23
+ its(:description) { should == '' }
24
+ its(:private_description) { should == '' }
25
+ its(:named_price) { should == nil }
26
+ its(:charge_price) { should == nil }
27
+ its(:cost_in_cents) { should == 0 }
28
+ its(:private_runner) { should == false }
29
+ its(:virtual) { should == false }
30
+ its(:state) { should == 'closed' }
31
+ its(:state_label) { should == 'closed' }
32
+ its(:location_visits) { should be_nil }
33
+ its(:city) { should be_instance_of(Taskrabbit::City) }
34
+ its(:assign_by_time) { should be_instance_of(Time) }
35
+ its(:complete_by_time) { should be_instance_of(Time) }
36
+ its(:state_changed_at) { should be_instance_of(Time) }
37
+ its(:links) { should be_instance_of(Hash) }
38
+ end
39
+
40
+ let(:valid_params) {
41
+ {
42
+ "name" => "My First Task",
43
+ "named_price" => 20,
44
+ "city_id" => 4
45
+ }
46
+ }
47
+
48
+ describe "api endpoints" do
49
+ describe "#tasks" do
50
+ it "should fetch tasks only once" do
51
+ tr = Taskrabbit::Api.new
52
+ VCR.use_cassette('tasks/all', :record => :new_episodes) do
53
+ tr_tasks = nil
54
+ expect { tr_tasks = tr.tasks }.to_not raise_error
55
+ tr_tasks.first.should be_instance_of(Taskrabbit::Task)
56
+ Taskrabbit::Api.should_not_receive(:get).with("/api/v1/tasks", anything).never
57
+ tr_tasks.last.should be_instance_of(Taskrabbit::Task)
58
+ end
59
+ end
60
+
61
+ it "should refetch tasks if passed :reload => true" do
62
+ tr = Taskrabbit::Api.new
63
+ VCR.use_cassette('tasks/all', :record => :new_episodes) do
64
+ tr_tasks = nil
65
+ expect { tr_tasks = tr.tasks }.to_not raise_error
66
+ tr_tasks.first.should be_instance_of(Taskrabbit::Task)
67
+ Taskrabbit::Api.should_receive(:get).with("/api/v1/tasks", anything).once.and_return []
68
+ tr_tasks.last(:reload => true)
69
+ Taskrabbit::Api.should_not_receive(:get).with("/api/v1/tasks", anything)
70
+ tr_tasks.last
71
+ end
72
+ end
73
+ end
74
+
75
+ describe "#find" do
76
+
77
+ it "should fetch tasks" do
78
+ tr = Taskrabbit::Api.new
79
+ VCR.use_cassette('tasks/find', :record => :new_episodes) do
80
+ tr_task = nil
81
+ expect {
82
+ tr_task = tr.tasks.find(22545)
83
+ tr_task.name
84
+ }.to_not raise_error
85
+ tr_task.should be_instance_of(Taskrabbit::Task)
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ describe "#delete!" do
92
+ it "should request DELETE /tasks/#id" do
93
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
94
+ VCR.use_cassette('tasks/delete', :record => :new_episodes) do
95
+ tr_task = nil
96
+ tr_task = tr.tasks.create(valid_params)
97
+ tr_task.should be_instance_of(Taskrabbit::Task)
98
+ expect { tr_task.delete! }.to_not raise_error
99
+ tr_task.should be_instance_of(Taskrabbit::Task)
100
+ tr_task.state.should == 'canceled'
101
+ end
102
+ end
103
+ end
104
+
105
+ describe "#save" do
106
+
107
+ it "should not do an extra query to tasks if the id is nil" do
108
+ Taskrabbit::Api.should_not_receive(:get).with("http://localhost:3000/api/v1/tasks/?")
109
+ Taskrabbit::Api.should_receive(:post).with("/api/v1/tasks", anything).and_return({:id => 1})
110
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
111
+ tr_task = tr.tasks.new(valid_params)
112
+ tr_task.save
113
+ end
114
+
115
+ it "should create a new task if new" do
116
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
117
+ VCR.use_cassette('tasks/save', :record => :new_episodes) do
118
+ tr_task = nil
119
+ tr_task = tr.tasks.new(valid_params)
120
+ tr_task.should be_instance_of(Taskrabbit::Task)
121
+ tr_task.save.should == true
122
+ tr_task.id.should_not be_nil
123
+ tr_task.state.should == 'opened'
124
+ tr.tasks.find(tr_task.id).name.should == tr_task.name
125
+ end
126
+ end
127
+
128
+ it "should update the task if existing" do
129
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
130
+ VCR.use_cassette('tasks/update', :record => :new_episodes) do
131
+ tr_task = tr.tasks.find(tr.tasks.create(valid_params).id)
132
+ tr_task.name = "New Name"
133
+ tr_task.save.should == true
134
+ tr_task.name.should == "New Name"
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "#update" do
140
+ context "with valid params" do
141
+ it "should return an error if the user is not logged in" do
142
+ tr = Taskrabbit::Api.new
143
+ VCR.use_cassette('tasks/create/without_user', :record => :new_episodes) do
144
+ tr_task = nil
145
+ expect { tr_task = tr.tasks.create(valid_params) }.to raise_error(Taskrabbit::Error, 'There must be an authenticated user for this action')
146
+ tr_task.should be_nil
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ describe "#create" do
153
+ context "with valid params" do
154
+ it "should return an error if the user is not logged in" do
155
+ tr = Taskrabbit::Api.new
156
+ VCR.use_cassette('tasks/create/without_user', :record => :new_episodes) do
157
+ tr_task = nil
158
+ expect { tr_task = tr.tasks.create(valid_params) }.to raise_error(Taskrabbit::Error, 'There must be an authenticated user for this action')
159
+ tr_task.should be_nil
160
+ end
161
+ end
162
+
163
+ it "should create the task if the user is authenticated but does not have a credit card" do
164
+ tr = Taskrabbit::Api.new(TR_USERS[:without_card][:secret])
165
+ VCR.use_cassette('tasks/create/without_credit_card', :record => :new_episodes) do
166
+ tr_task = tr.tasks.new(valid_params)
167
+ tr_task.save.should == false
168
+ tr_task.should be_instance_of(Taskrabbit::Task)
169
+ tr_task.redirect?.should be_true
170
+ tr_task.redirect_url.should =~ %r{https://local\.taskrabbit\.com/tasks/my-first-task--\d+\?card=true}
171
+ end
172
+ end
173
+
174
+ it "should create the task if the user is authenticated and has a credit card" do
175
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
176
+ VCR.use_cassette('tasks/create/default', :record => :new_episodes) do
177
+ tr_task = nil
178
+ expect { tr_task = tr.tasks.create(valid_params) }.to_not raise_error
179
+ tr_task.should be_instance_of(Taskrabbit::Task)
180
+ end
181
+ end
182
+
183
+ it "should create the task using the account" do
184
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
185
+ VCR.use_cassette('tasks/create/using_account', :record => :new_episodes) do
186
+ tr_task = nil
187
+ expect { tr_task = tr.account.tasks.create(valid_params) }.to_not raise_error
188
+ tr_task.should be_instance_of(Taskrabbit::Task)
189
+ end
190
+ end
191
+ end
192
+
193
+ context "with invalid params" do
194
+ let(:invalid_params) { {} }
195
+
196
+ it "should create the task if the user is authenticated and has a credit card" do
197
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
198
+ VCR.use_cassette('tasks/create/with_invalid_params', :record => :new_episodes) do
199
+ tr_task = nil
200
+ expect { tr_task = tr.tasks.create(invalid_params) }.to
201
+ raise_error(Taskrabbit::Error, "Task title can't be blank,\n" +
202
+ "Amount you are willing to pay needs to be a whole dollar amount greater than zero")
203
+ tr_task.should be_nil
204
+ end
205
+ end
206
+ end
207
+
208
+ it "should post locations" do
209
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
210
+ VCR.use_cassette('tasks/create/with_location', :record => :new_episodes) do
211
+ tr_task = nil
212
+ params_with_locations = valid_params.merge({:other_locations_attributes => [
213
+ {
214
+ "name" => "Home",
215
+ "address" => "123 Main St",
216
+ "city" => "Boston",
217
+ "state" => "MA",
218
+ "zip" => "02154",
219
+ "lat" => "42.358432",
220
+ "lng" => "-71.059774"
221
+ },
222
+ {
223
+ "name" => "Middle of the park",
224
+ "lat" => "42.358430",
225
+ "lng" => "-71.059772"
226
+ }
227
+ ]})
228
+ expect { tr_task = tr.tasks.create(params_with_locations) }.to_not raise_error
229
+ tr_task.should be_instance_of(Taskrabbit::Task)
230
+ tr_task.location_visits.count.should == 2
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Taskrabbit do
4
+ after do
5
+ Taskrabbit.reset
6
+ end
7
+
8
+ describe ".options" do
9
+ it "should returns the options" do
10
+ Taskrabbit.options
11
+ end
12
+ end
13
+
14
+ describe ".configure" do
15
+ Taskrabbit::Config::VALID_OPTIONS_KEYS.each do |key|
16
+ it "should set the #{key}" do
17
+ Taskrabbit.configure do |config|
18
+ config.send("#{key}=", key)
19
+ Taskrabbit.send(key).should == key
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "api defaults" do
26
+ it "should be able to set the client_secret" do
27
+ Taskrabbit.client_secret = 'asecret'
28
+ Taskrabbit.client_secret.should == 'asecret'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Taskrabbit::User do
4
+
5
+ describe "user properties" do
6
+
7
+ before :all do
8
+ tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret])
9
+ VCR.use_cassette('users/properties', :record => :new_episodes) do
10
+ @user = tr.users.find(TR_USERS[:with_card][:id])
11
+ @user.fetch
12
+ end
13
+ end
14
+
15
+ subject { @user }
16
+
17
+ its(:id) { should == TR_USERS[:with_card][:id] }
18
+ its(:short_name) { should == "Bob" }
19
+ its(:first_name) { should == "Bob" }
20
+ its(:full_name) { should == "Bob Sponge" }
21
+ its(:display_name) { should == "Bob S." }
22
+ its(:runner) { should == false }
23
+ its(:email) { should == 'bobsponge@example.com' }
24
+ its(:mobile_phone) { should == nil }
25
+ its(:home_phone) { should == nil }
26
+ its(:tasks) { should == Taskrabbit::Task }
27
+ its(:city) { should be_instance_of(Taskrabbit::City) }
28
+ its(:zip_code) { should == "64321" }
29
+ its(:locations) { should == Taskrabbit::Location }
30
+ its(:links) { should be_instance_of(Hash) }
31
+ end
32
+
33
+ describe "api endpoints" do
34
+ describe "#find" do
35
+ it "should fetch users" do
36
+ tr = Taskrabbit::Api.new
37
+ VCR.use_cassette('users/find', :record => :new_episodes) do
38
+ tr_user = nil
39
+ expect { tr_user = tr.users.find(TR_USERS[:without_card][:id]) }.to_not raise_error
40
+ tr_user.id.should == TR_USERS[:without_card][:id]
41
+ tr_user.short_name.should == 'John'
42
+ tr_user.should be_instance_of(Taskrabbit::User)
43
+ end
44
+ end
45
+
46
+ describe "tasks" do
47
+ let(:tr) { tr = Taskrabbit::Api.new(TR_USERS[:with_card][:secret]) }
48
+
49
+ it "should not do an extra query to users/#{TR_USERS[:with_card][:id]} when fetching all tasks" do
50
+ Taskrabbit::Api.should_not_receive(:get).with("/api/v1/users/#{TR_USERS[:with_card][:id]}", anything).never
51
+ tr.users.find(TR_USERS[:with_card][:id]).tasks.all
52
+ end
53
+
54
+ it "should not do an extra query to users/#{TR_USERS[:with_card][:id]} when finding task" do
55
+ Taskrabbit::Api.should_not_receive(:get).with("/api/v1/users/#{TR_USERS[:with_card][:id]}", anything).never
56
+ tr.users.find(TR_USERS[:with_card][:id]).tasks.find('some-id')
57
+ end
58
+
59
+ it "should fetch tasks with users/#{TR_USERS[:with_card][:id]}/tasks" do
60
+ VCR.use_cassette('users/tasks/all', :record => :new_episodes) do
61
+ tr_tasks = nil
62
+ expect { tr_tasks = tr.users.find(TR_USERS[:with_card][:id]).tasks.all }.to_not raise_error
63
+ tr_tasks.should be_a(Taskrabbit::Collection)
64
+ tr_tasks.first.should be_instance_of(Taskrabbit::Task)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end