crowdkit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +16 -0
  5. data/Gemfile.lock +97 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +178 -0
  8. data/Rakefile +54 -0
  9. data/VERSION +1 -0
  10. data/crowdkit.gemspec +122 -0
  11. data/lib/crowdkit.rb +77 -0
  12. data/lib/crowdkit/api.rb +171 -0
  13. data/lib/crowdkit/api/arguments.rb +187 -0
  14. data/lib/crowdkit/api/factory.rb +29 -0
  15. data/lib/crowdkit/api/request_methods.rb +70 -0
  16. data/lib/crowdkit/api/response_wrapper.rb +122 -0
  17. data/lib/crowdkit/client.rb +36 -0
  18. data/lib/crowdkit/client/account.rb +7 -0
  19. data/lib/crowdkit/client/jobs.rb +56 -0
  20. data/lib/crowdkit/client/judgments.rb +9 -0
  21. data/lib/crowdkit/client/poll.rb +48 -0
  22. data/lib/crowdkit/client/statuses.rb +14 -0
  23. data/lib/crowdkit/client/units.rb +54 -0
  24. data/lib/crowdkit/client/workers.rb +13 -0
  25. data/lib/crowdkit/client/worksets.rb +27 -0
  26. data/lib/crowdkit/config.rb +62 -0
  27. data/lib/crowdkit/core_ext/array.rb +7 -0
  28. data/lib/crowdkit/core_ext/hash.rb +30 -0
  29. data/lib/crowdkit/core_ext/sawyer.rb +7 -0
  30. data/lib/crowdkit/error.rb +198 -0
  31. data/lib/crowdkit/middleware/raise_error.rb +14 -0
  32. data/lib/crowdkit/ssl_certs/cacerts.pem +3868 -0
  33. data/lib/crowdkit/version.rb +3 -0
  34. data/spec/crowdkit/client/account_spec.rb +19 -0
  35. data/spec/crowdkit/client/jobs_spec.rb +275 -0
  36. data/spec/crowdkit/client/judgments_spec.rb +30 -0
  37. data/spec/crowdkit/client/statuses_spec.rb +30 -0
  38. data/spec/crowdkit/client/units_spec.rb +266 -0
  39. data/spec/crowdkit/client/workers_spec.rb +33 -0
  40. data/spec/crowdkit/client/worksets_spec.rb +113 -0
  41. data/spec/crowdkit/client_spec.rb +55 -0
  42. data/spec/crowdkit_spec.rb +139 -0
  43. data/spec/fixtures/account.json +7 -0
  44. data/spec/fixtures/jobs/copy.json +79 -0
  45. data/spec/fixtures/jobs/job.json +1 -0
  46. data/spec/fixtures/jobs/order.json +53 -0
  47. data/spec/fixtures/jobs/search.json +1 -0
  48. data/spec/fixtures/judgments/index.json +46 -0
  49. data/spec/fixtures/judgments/show.json +16 -0
  50. data/spec/fixtures/root.json +1 -0
  51. data/spec/fixtures/statuses/index.json +25 -0
  52. data/spec/fixtures/statuses/status.json +12 -0
  53. data/spec/fixtures/units/copy.json +34 -0
  54. data/spec/fixtures/units/index.json +1502 -0
  55. data/spec/fixtures/units/poll/normal.json +902 -0
  56. data/spec/fixtures/units/poll/small.json +602 -0
  57. data/spec/fixtures/units/show.json +34 -0
  58. data/spec/fixtures/upload.csv +25 -0
  59. data/spec/fixtures/worksets/index.json +20 -0
  60. data/spec/fixtures/worksets/show.json +9 -0
  61. data/spec/spec_helper.rb +53 -0
  62. data/spec/support/base.rb +11 -0
  63. metadata +218 -0
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Crowdkit::Client::Workers do
4
+ context "flag" do
5
+ let!(:request_stub) { stub_put("/workers/1/flag").to_return(status: 204) }
6
+ before { client.workers(worker_id: 1).flag(reason: 'Because') }
7
+
8
+ it "requests" do
9
+ expect(request_stub).to have_been_requested
10
+ end
11
+
12
+ it "response" do
13
+ expect(client.last_response.status).to eq(204)
14
+ end
15
+
16
+ it "blows up without reason" do
17
+ expect { client.workers(worker_id: 1).flag }.to raise_error Crowdkit::ValidationError
18
+ end
19
+ end
20
+
21
+ context "deflag" do
22
+ let!(:request_stub) { stub_put("/workers/2/deflag").to_return(status: 204) }
23
+ before { client.workers(worker_id: 2).deflag }
24
+
25
+ it "requests" do
26
+ expect(request_stub).to have_been_requested
27
+ end
28
+
29
+ it "response" do
30
+ expect(client.last_response.status).to eq(204)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe Crowdkit::Client::Worksets do
4
+ context "show" do
5
+ let!(:request_stub) { stub_get("/worksets/1").to_return(fixture_response("worksets/show.json")) }
6
+
7
+ context "requests" do
8
+ it "gets made" do
9
+ client.worksets.get(1)
10
+ expect(request_stub).to have_been_requested
11
+ end
12
+
13
+ it "has find alias" do
14
+ client.worksets.find(1)
15
+ expect(request_stub).to have_been_requested
16
+ end
17
+
18
+ it "blows up without id" do
19
+ expect { client.worksets.get }.to raise_error Crowdkit::ValidationError
20
+ end
21
+ end
22
+
23
+ context "response" do
24
+ it "parses" do
25
+ expect(client.worksets.get(1).id).to eq(1)
26
+ end
27
+ end
28
+ end
29
+
30
+ context "list" do
31
+ let!(:request_stub) { stub_get("/jobs/1/worksets").to_return(fixture_response("worksets/index.json")) }
32
+
33
+ context "requests" do
34
+ it "gets made" do
35
+ client.worksets(job_id: 1).list
36
+ expect(request_stub).to have_been_requested
37
+ end
38
+
39
+ it "has all alias" do
40
+ client.worksets.all(job_id: 1)
41
+ expect(request_stub).to have_been_requested
42
+ end
43
+
44
+ it "blows up without job_id" do
45
+ expect { client.worksets.list }.to raise_error Crowdkit::ValidationError
46
+ end
47
+ end
48
+
49
+ context "response" do
50
+ it "parses" do
51
+ expect(client.worksets(job_id: 1).list.length).to eq(2)
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ context "statuses" do
58
+ shared_examples_for "statuses" do
59
+ it "requests" do
60
+ expect(request_stub).to have_been_requested
61
+ end
62
+
63
+ it "response" do
64
+ expect(client.last_response.status).to eq(204)
65
+ end
66
+ end
67
+
68
+ context "flag" do
69
+ it_behaves_like "statuses" do
70
+ let!(:request_stub) { stub_put("/worksets/1/flag").to_return(status: 204) }
71
+ before { client.worksets(workset_id: 1).flag(reason: 'Because') }
72
+ end
73
+ end
74
+
75
+ context "deflag" do
76
+ it_behaves_like "statuses" do
77
+ let!(:request_stub) { stub_put("/worksets/1/deflag").to_return(status: 204) }
78
+ before { client.worksets(workset_id: 1).deflag(reason: 'Because') }
79
+ end
80
+ end
81
+
82
+ context "reject" do
83
+ it_behaves_like "statuses" do
84
+ let!(:request_stub) { stub_put("/worksets/1/reject").to_return(status: 204) }
85
+ before { client.worksets(workset_id: 1).reject(reason: 'Too bad!') }
86
+ end
87
+ end
88
+
89
+ context "approve" do
90
+ it_behaves_like "statuses" do
91
+ let!(:request_stub) { stub_put("/worksets/1/approve").to_return(status: 204) }
92
+ before { client.worksets(workset_id: 1).approve(reason: 'Cool!') }
93
+ end
94
+ end
95
+ end
96
+
97
+ context "bonus" do
98
+ let!(:request_stub) { stub_put("/worksets/1/bonus").to_return(status: 204) }
99
+ before { client.worksets(workset_id: 1).bonus(amount: 10) }
100
+
101
+ it "requests" do
102
+ expect(request_stub).to have_been_requested
103
+ end
104
+
105
+ it "response" do
106
+ expect(client.last_response.status).to eq(204)
107
+ end
108
+
109
+ it "blows up without amount" do
110
+ expect { client.worksets(workset_id: 1).bonus }.to raise_error Crowdkit::ValidationError
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+ require 'open-uri'
3
+ describe Crowdkit::Client do
4
+ context 'root' do
5
+ let!(:request_stub) { stub_get("").to_return(fixture_response("root.json")) }
6
+
7
+ it "requests the resource" do
8
+ client.root
9
+ expect(request_stub).to have_been_requested
10
+ end
11
+
12
+ it "provides links" do
13
+ expect(client.root.rels.size).to eq(2)
14
+ end
15
+ end
16
+
17
+ context "pagination" do
18
+ let!(:first_page) { stub_get("/jobs").to_return(fixture_response("jobs/search.json", 200, { Link: '<https://api.crowdflower.com/v2/jobs?page=2>; rel="last", <https://api.crowdflower.com/v2/jobs?page=2>; rel="next"'})) }
19
+ let!(:last_page) { stub_get("/jobs?page=2").to_return(fixture_response("jobs/search.json", 200, { Link: '<https://api.crowdflower.com/v2/jobs>; rel="first", <https://api.crowdflower.com/v2/jobs>; rel="prev"'}))}
20
+
21
+ it "auto paginates with a block" do
22
+ client.config.auto_paginate = true
23
+ total_jobs = 0
24
+ client.jobs.list { |job| total_jobs += 1 }
25
+ expect(total_jobs).to eq(10)
26
+ expect(first_page).to have_been_requested
27
+ expect(last_page).to have_been_requested
28
+ end
29
+
30
+ it "auto paginates without a block" do
31
+ client.config.auto_paginate = true
32
+ expect(client.jobs.list.to_a.length).to eq(10)
33
+ end
34
+
35
+ it "auto paginates with options" do
36
+ expect(client.config.auto_paginate).to be_falsey
37
+ expect(client.jobs.list(auto_pagination: true).to_a.length).to eq(10)
38
+ expect(client.auto_pagination).to be_falsey
39
+ end
40
+
41
+ it "next page" do
42
+ expect(client.jobs.list.next_page.length).to eq(5)
43
+ expect(last_page).to have_been_requested
44
+ end
45
+
46
+ it "prev page" do
47
+ client.jobs.list.next_page.prev_page
48
+ expect(first_page).to have_been_requested.twice
49
+ end
50
+
51
+ it "missing page" do
52
+ expect(client.jobs.list.first_page).to be_falsey
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ describe Crowdkit do
4
+ context "configuration" do
5
+ context "defaults" do
6
+ specify "connection_options" do
7
+ expect(Crowdkit.config.connection_options).to include({
8
+ headers: {
9
+ accept: "application/json",
10
+ user_agent: "Crowdkit Ruby Gem #{Crowdkit::VERSION}"
11
+ }
12
+ })
13
+ end
14
+ end
15
+
16
+ context "stack" do
17
+ specify "default" do
18
+ expect(Crowdkit.config.stack.handlers.first).to eq(Faraday::Request::TokenAuthentication)
19
+ end
20
+
21
+ context "overrides" do
22
+ before(:each) do
23
+ Crowdkit.reset!
24
+ end
25
+
26
+ it "modifies existing stack" do
27
+ Crowdkit.configure do |c|
28
+ c.stack.insert_before(Faraday::Request::TokenAuthentication, Crowdkit)
29
+ end
30
+ expect(Crowdkit.config.stack.handlers.first).to eq Crowdkit
31
+ end
32
+
33
+ it "overrides stack" do
34
+ Crowdkit.configure do |c|
35
+ c.stack do |builder|
36
+ builder.use Crowdkit
37
+ end
38
+ end
39
+ expect(Crowdkit.config.stack.handlers.first).to eq Crowdkit
40
+ end
41
+
42
+ it "use custom adapter" do
43
+ Crowdkit.configure(adapter: :typhoeus)
44
+ expect(Crowdkit.config.stack.handlers.last).to eq Faraday::Adapter::Typhoeus
45
+ end
46
+ end
47
+ end
48
+
49
+ context "non defaults" do
50
+ shared_examples "overrides" do
51
+ specify "user_agent" do
52
+ expect(client.config.user_agent).to eq("Foo")
53
+ end
54
+
55
+ specify "nested connection_options" do
56
+ expect(client.config.connection_options).to include({
57
+ headers: {
58
+ accept: "application/json",
59
+ user_agent: "Foo"
60
+ }
61
+ })
62
+ end
63
+
64
+ specify "logging" do
65
+ expect(client.config.stack.handlers.first).to eq(Faraday::Response::Logger)
66
+ end
67
+ end
68
+
69
+ context "global" do
70
+ let(:client) do
71
+ Crowdkit
72
+ end
73
+
74
+ before(:each) do
75
+ Crowdkit.reset!
76
+ Crowdkit.configure(debug: true, user_agent: "Foo")
77
+ end
78
+ end
79
+
80
+ context "block" do
81
+ let(:client) do
82
+ Crowdkit.new do |c|
83
+ c.debug = true
84
+ c.user_agent = "Foo"
85
+ end
86
+ end
87
+
88
+ it_behaves_like "overrides"
89
+ end
90
+
91
+ context "opts" do
92
+ let(:client) do
93
+ Crowdkit.new({debug: true, user_agent: "Foo"})
94
+ end
95
+
96
+ it_behaves_like "overrides"
97
+ end
98
+
99
+ context "env" do
100
+ around(:each) do |example|
101
+ Crowdkit.reset!
102
+ ENV["CROWDKIT_USER_AGENT"] = "Foo"
103
+ ENV["CROWDKIT_DEBUG"] = "true"
104
+ example.run
105
+ ENV["CROWDKIT_USER_AGENT"] = nil
106
+ ENV["CROWDKIT_DEBUG"] = nil
107
+ end
108
+
109
+ let(:client) do
110
+ Crowdkit
111
+ end
112
+
113
+ it_behaves_like "overrides"
114
+ end
115
+ end
116
+ end
117
+
118
+ context "client proxy" do
119
+ context "unconfigured" do
120
+ specify "exception" do
121
+ expect { Crowdkit.do_get("/foo") }.to raise_error Crowdkit::Unauthorized
122
+ end
123
+ end
124
+ end
125
+
126
+ context "rate limits" do
127
+ let!(:soft_request_stub) { stub_get(/jobs\/soft/).to_return(body: 'rate limit exceeded', status: 429) }
128
+ let!(:hard_request_stub) { stub_get(/jobs\/hard/).to_return(body: 'rate limit exceeded', status: 403) }
129
+
130
+ it "retries once on hard" do
131
+ expect { client.jobs.get("hard") }.to raise_error(Crowdkit::Forbidden)
132
+ end
133
+
134
+ it "raises error on soft" do
135
+ expect { client.jobs.get("soft") }.to raise_error(Crowdkit::TooManyRequests)
136
+ expect(soft_request_stub).to have_been_requested.times(2)
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,7 @@
1
+ {
2
+ "id": 1,
3
+ "email": "test@test.com",
4
+ "name": "Test",
5
+ "account_balance": 100,
6
+ "unused_allocated_funds": 0
7
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "id":12,
3
+ "created_at":"2014-06-12T23:24:05Z",
4
+ "updated_at":"2014-06-12T23:24:05Z",
5
+ "state":"unordered",
6
+ "tags":[],
7
+ "title":"Some really awesome job that is so very very cool",
8
+ "stats":{
9
+ "units_count":0,
10
+ "new_units_count":0,
11
+ "judgments_count":0,
12
+ "needed_judgments_count":0,
13
+ "golds_count":0,
14
+ "cost":0.0,
15
+ "allocated_funds":0.0
16
+ },
17
+ "user":{
18
+ "id":1,
19
+ "name":"cf_internal user",
20
+ "account_balance":0.0,
21
+ "allocated_funds":0.0,
22
+ "email":"cf_internal@crowdflower.com"
23
+ },
24
+ "_links":{
25
+ "self":{
26
+ "href":"https://api.crowdflower.dev/v2/jobs/12"
27
+ },
28
+ "units":{
29
+ "href":"https://api.crowdflower.dev/v2/jobs/12/units/{unit_id}",
30
+ "templated":true
31
+ },
32
+ "judgments":{
33
+ "href":"https://api.crowdflower.dev/v2/jobs/12/judgments/{judgment_id}",
34
+ "templated":true
35
+ }
36
+ },
37
+ "definition":{
38
+ "cml":"<p>I don't have any data this is a survey</p>\n<cml:checkboxes label=\"How cool is this?\" class=\"\" validates=\"required\">\n <cml:checkbox label=\"Category A\"/>\n <cml:checkbox label=\"Category B\"/>\n <cml:checkbox label=\"Category C\"/>\n </cml:checkboxes>",
39
+ "instructions":"Well you've got to do this awesome shit, and then later do that cool stuff"
40
+ },
41
+ "contributor_settings":{
42
+ "assignment_ttl_in_minutes":30,
43
+ "excluded_countries":[],
44
+ "included_countries":[],
45
+ "channels":[]
46
+ },
47
+ "quality_settings":{
48
+ "test_questions_per_assignment":0,
49
+ "reject_at":70,
50
+ "after_gold":10,
51
+ "variable_judgments_mode":"none"
52
+ },
53
+ "job_settings":{
54
+ "judgments_per_unit":3,
55
+ "units_per_assignment":5,
56
+ "pages_per_assignment":1,
57
+ "language":"en",
58
+ "explicit_content":false,
59
+ "payment_cents":5,
60
+ "public_data":false
61
+ },
62
+ "api_settings":{
63
+ "auto_order":false
64
+ },
65
+ "report_settings":{
66
+ "include_unfinished":true
67
+ },
68
+ "current_status":{
69
+ "id":"3d05fc4a1501dcebfb836434d8fc5cce",
70
+ "percent_complete":0,
71
+ "state":"queued",
72
+ "name":"Unit Copy",
73
+ "_links":{
74
+ "self":{
75
+ "href":"https://api.crowdflower.com/v2/statuses/3d05fc4a1501dcebfb836434d8fc5cce"
76
+ }
77
+ }
78
+ }
79
+ }
@@ -0,0 +1 @@
1
+ {"id":1,"created_at":"2014-02-07T16:43:29Z","updated_at":"2014-02-07T16:45:00Z","state":"unordered","tags":[],"title":"Some really awesome job that is so very very cool","stats":{"units_count":1,"new_units_count":1,"judgments_count":0,"needed_judgments_count":0,"golds_count":0},"user":{"id":1,"name":"cf_internal user","email":"cf_internal@crowdflower.com"},"_links":{"self":{"href":"https://api.crowdflower.dev/v2/jobs/1"},"units":{"href":"https://api.crowdflower.dev/v2/jobs/1/units/{unit_id}","templated":true},"judgments":{"href":"https://api.crowdflower.dev/v2/jobs/1/judgments/{judgment_id}","templated":true}},"definition":{"cml":"<p>I don't have any data this is a survey</p>\n<cml:checkboxes label=\"How cool is this?\" class=\"\" validates=\"required\">\n <cml:checkbox label=\"Category A\"/>\n <cml:checkbox label=\"Category B\"/>\n <cml:checkbox label=\"Category C\"/>\n </cml:checkboxes>","instructions":"Well you've got to do this awesome shit, and then later do that cool stuff"},"contributor_settings":{"assignment_ttl_in_minutes":30,"excluded_countries":[],"included_countries":[],"channels":[]},"quality_settings":{"test_questions_per_assignment":0,"reject_at":70,"after_gold":10,"variable_judgments_mode":"none"},"job_settings":{"judgments_per_unit":3,"units_per_assignment":5,"pages_per_assignment":1,"language":"en","explicit_content":false,"payment_cents":5,"public_data":false},"api_settings":{"auto_order":false},"report_settings":{"include_unfinished":true}}
@@ -0,0 +1,53 @@
1
+ {
2
+ "created_at":"2014-06-13T22:14:11Z",
3
+ "units_count":5,
4
+ "amount":10.0,
5
+ "id":1822,
6
+ "job":{
7
+ "id":9408,
8
+ "created_at":"2014-06-13T22:14:11Z",
9
+ "updated_at":"2014-06-13T22:14:11Z",
10
+ "state":"running",
11
+ "project_number":"PN001",
12
+ "tags":[],
13
+ "alias":"foo1",
14
+ "title":"Fantastic Job",
15
+ "stats":{
16
+ "units_count":5,
17
+ "new_units_count":5,
18
+ "judgments_count":0,
19
+ "needed_judgments_count":0,
20
+ "golds_count":0,
21
+ "cost":0.0,
22
+ "allocated_funds":0.0
23
+ },
24
+ "_links":{
25
+ "self":{
26
+ "href":"https://api.crowdflower.test/v2/jobs/9408"
27
+ }
28
+ }
29
+ },
30
+ "user":{
31
+ "id":18548,
32
+ "name":"Willy Beetus",
33
+ "account_balance":10.0,
34
+ "allocated_funds":10.0,
35
+ "email":"person1@crowdflower.com"
36
+ },
37
+ "current_status":{
38
+ "id":"9e61a51bad819441e3d5fcfc6045b555",
39
+ "percent_complete":0,
40
+ "state":"queued",
41
+ "name":"Unit Ordering",
42
+ "_links":{
43
+ "self":{
44
+ "href":"https://api.crowdflower.com/v2/statuses/9e61a51bad819441e3d5fcfc6045b555"
45
+ }
46
+ }
47
+ },
48
+ "_links":{
49
+ "self":{
50
+ "href":"https://api.crowdflower.test/v2/orders/1822"
51
+ }
52
+ }
53
+ }