crowdkit 0.1.0

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 (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
+ }