shared_workforce 0.2.2 → 0.2.3

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.
@@ -1,26 +1,26 @@
1
1
  module SharedWorkforce
2
2
  class EndPoint
3
3
 
4
- def initialize(app)
4
+ def initialize(app = nil)
5
5
  @app = app
6
6
  end
7
7
 
8
8
  def call(env)
9
9
  if env["PATH_INFO"] =~ /^\/#{callback_path}/
10
- process_request(env)
10
+ Rack::Request.new(env)
11
+ process_response(req.body.read)
11
12
  else
12
- @app.call(env)
13
+ @app.call(env) if @app.respond_to? :call
13
14
  end
14
-
15
15
  end
16
16
 
17
- private
18
-
19
- def process_request(env)
20
- req = Rack::Request.new(env)
21
- body = JSON.parse(req.body.read)
22
- puts "processing task callback"
17
+ def process_response(body)
18
+ body = JSON.parse(body)
19
+ puts "Processing Shared Workforce task callback"
23
20
  puts body.inspect
21
+
22
+ raise SecurityTransgression unless valid_api_key?(body['api_key'])
23
+
24
24
  SharedWorkforce::TaskResult.new(body).process!
25
25
 
26
26
  [ 200,
@@ -34,5 +34,11 @@ module SharedWorkforce
34
34
  SharedWorkforce.configuration.callback_path
35
35
  end
36
36
 
37
+ private
38
+
39
+ def valid_api_key?(key)
40
+ key == SharedWorkforce.configuration.api_key
41
+ end
42
+
37
43
  end
38
44
  end
@@ -2,4 +2,5 @@ module SharedWorkforce
2
2
  class Error < RuntimeError; end
3
3
  class ConfigurationError < Error; end
4
4
  class TaskNotFound < Error; end
5
+ class SecurityTransgression < Error; end
5
6
  end
@@ -11,6 +11,7 @@ module SharedWorkforce
11
11
  :replace,
12
12
  :answer_options,
13
13
  :image_url,
14
+ :image_crop_ratio,
14
15
  :text,
15
16
  :on_success,
16
17
  :on_failure,
@@ -6,15 +6,16 @@ module SharedWorkforce
6
6
  end
7
7
 
8
8
  def to_hash
9
- {:answer=>answer, :answered_by=>username}
9
+ {:answer=>answer, :answered_by=>username, :new_image_url=>new_image_url}.reject {|k,v| v.nil? }.with_indifferent_access
10
10
  end
11
11
 
12
- attr_accessor :answer, :callback_params, :username
12
+ attr_accessor :answer, :callback_params, :username, :new_image_url
13
13
 
14
14
  def initialize(params)
15
15
  @answer = params['answer']
16
16
  @callback_params = params['callback_params']
17
17
  @username = params['username']
18
+ @new_image_url = params['new_image_url']
18
19
  end
19
20
 
20
21
  end
@@ -1,17 +1,28 @@
1
1
  module SharedWorkforce
2
2
  class TaskResult
3
3
 
4
- attr_accessor :callback_params
4
+ attr_reader :callback_params
5
5
  attr_accessor :responses
6
6
  attr_accessor :name
7
7
  attr_accessor :status
8
+ attr_accessor :task_hash
8
9
 
9
10
  def initialize(params)
10
11
  params = params.with_indifferent_access
11
- self.callback_params = params[:callback_params]
12
- @responses = TaskResponse.create_collection_from_array(params[:responses]) if params[:responses]
12
+
13
+ self.task_hash = if params[:task]
14
+ params[:task]
15
+ else
16
+ params
17
+ end
18
+
19
+ @responses = TaskResponse.create_collection_from_array(task_hash[:responses]) if task_hash[:responses]
13
20
  self.name = callback_params[:_task][:class_name] if callback_params && callback_params[:_task] && callback_params[:_task][:class_name]
14
21
  end
22
+
23
+ def callback_params
24
+ task_hash[:callback_params]
25
+ end
15
26
 
16
27
  def responses
17
28
  @responses.map(&:to_hash)
@@ -1,4 +1,4 @@
1
1
  module SharedWorkforce
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
 
4
4
  end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'rack'
3
+ describe "EndPoint" do
4
+
5
+ describe "#process_request" do
6
+ it "should not raise a security transgression with a valid api key" do
7
+ lambda {
8
+ SharedWorkforce::EndPoint.new.process_response({:api_key => SharedWorkforce.configuration.api_key}.to_json)
9
+ }.should_not raise_error(SharedWorkforce::SecurityTransgression)
10
+ end
11
+
12
+ it "should raise a security transgression with an invalid api key" do
13
+ lambda {
14
+ SharedWorkforce::EndPoint.new.process_response({:api_key=>'GIBBERISH'}.to_json)
15
+ }.should raise_error(SharedWorkforce::SecurityTransgression)
16
+ end
17
+
18
+ it "should raise a security transgression with a missing api key" do
19
+ lambda {
20
+ SharedWorkforce::EndPoint.new.process_response({}.to_json)
21
+ }.should raise_error(SharedWorkforce::SecurityTransgression)
22
+ end
23
+ end
24
+
25
+ end
@@ -5,7 +5,7 @@ describe "TaskRequest::Http" do
5
5
  it "should invoke a task request" do
6
6
 
7
7
  task = ApprovePhotoTask.new
8
- task_request = SharedWorkforce::TaskRequest::Http.new(task, :image_url=>"http://www.google.com/logo.png", :callback_params=>{:resource_id=>'1234'})
8
+ task_request = SharedWorkforce::TaskRequest::Http.new(task, :image_url=>"http://www.google.com/logo.png", :image_crop_ratio=>1.7, :callback_params=>{:resource_id=>'1234'})
9
9
 
10
10
  stub_request(:post, "api.sharedworkforce.com/tasks")
11
11
  task_request.create
@@ -14,6 +14,7 @@ describe "TaskRequest::Http" do
14
14
  'title'=>"Approve Photo",
15
15
  'instruction'=>"Please classify this photo by choosing the appropriate tickboxes.",
16
16
  'image_url'=>"http://www.google.com/logo.png",
17
+ 'image_crop_ratio'=>1.7,
17
18
  'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
18
19
  'responses_required'=>3,
19
20
  'answer_type'=>'tags',
@@ -14,4 +14,21 @@ describe "TaskResponse" do
14
14
  r[1].username.should == "frodo"
15
15
  end
16
16
  end
17
+
18
+ describe "#to_hash" do
19
+ it "should include answered_by" do
20
+ t = SharedWorkforce::TaskResponse.new('username'=>'bilbo')
21
+ t.to_hash[:answered_by].should == 'bilbo'
22
+ end
23
+
24
+ it "should include new_image_url" do
25
+ t = SharedWorkforce::TaskResponse.new('new_image_url'=>'http://www.example.com/image.jpg')
26
+ t.to_hash[:new_image_url].should == 'http://www.example.com/image.jpg'
27
+ end
28
+
29
+ it "should have indifferent access" do
30
+ t = SharedWorkforce::TaskResponse.new('new_image_url'=>'http://www.example.com/image.jpg')
31
+ t.to_hash['new_image_url'].should == 'http://www.example.com/image.jpg'
32
+ end
33
+ end
17
34
  end
@@ -1,37 +1,67 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "TaskResult" do
4
+
5
+ before(:each) do
6
+ @task_result_hash = {
7
+ 'task' => {
8
+ 'name'=>"Approve photo please",
9
+ 'callback_params' =>
10
+ {
11
+ '_task'=>{'class_name'=>'ApprovePhotoTask'},
12
+ 'resource_id' => '2',
13
+ },
14
+ 'responses' => [{'answer' => 'yes'}]
15
+ }
16
+ }
17
+ end
18
+
4
19
  describe "#process!" do
5
20
  it "should invoke Task#process_result" do
6
21
  ApprovePhotoTask.any_instance.should_receive(:process_result).once
7
- SharedWorkforce::TaskResult.new({'callback_params'=>{'_task'=>{'class_name'=>'ApprovePhotoTask'}, 'resource_id' => '2', 'responses'=>[{'answer'=>'yes'}], 'name'=>"Approve photo please"}}).process!
22
+ SharedWorkforce::TaskResult.new(@task_result_hash).process!
8
23
  end
9
24
  end
10
25
 
11
26
  describe "#responses" do
12
27
  it "should convert the result to a hash" do
13
28
  r = SharedWorkforce::TaskResult.new(
14
- {'callback_params'=>{'resource_id' => '2'},
15
- 'responses'=>[
16
- {'answer'=>'yes', 'username'=>'bilbo'},
17
- {'answer'=>'no', 'username'=>'frodo'},
18
- {'answer'=>'yes', 'username'=>'sam'}
19
- ],
20
- 'name'=>"Approve photo"
29
+ {'task' =>
30
+ {
31
+ 'name'=>"Approve photo",
32
+ 'callback_params'=>{'resource_id' => '2'},
33
+ 'responses'=>[
34
+ {'answer'=>'yes', 'username'=>'bilbo'},
35
+ {'answer'=>'no', 'username'=>'frodo'},
36
+ {'answer'=>'yes', 'username'=>'sam'}
37
+ ]
38
+ }
21
39
  }
22
40
  )
23
41
 
24
42
  r.responses.should == [
25
- {:answer=>'yes', :answered_by=>'bilbo'},
26
- {:answer=>'no', :answered_by=>'frodo'},
27
- {:answer=>'yes', :answered_by=>'sam'}
43
+ {'answer'=>'yes', 'answered_by'=>'bilbo'},
44
+ {'answer'=>'no', 'answered_by'=>'frodo'},
45
+ {'answer'=>'yes', 'answered_by'=>'sam'}
28
46
  ]
29
47
  end
30
48
  end
49
+
50
+ describe "#task_attributes" do
51
+ it "should assign the task hash" do
52
+ task = SharedWorkforce::TaskResult.new(@task_result_hash)
53
+ task.task_hash.should == @task_result_hash['task']
54
+ end
55
+
56
+ it "should accept a task that is not nested in a :task key (backwards compatibility)" do
57
+ task = SharedWorkforce::TaskResult.new(@task_result_hash['task'])
58
+ task.task_hash.should == @task_result_hash['task']
59
+ end
60
+ end
31
61
 
32
62
  describe "#usernames" do
33
63
  it "should collect the usernames" do
34
- r = SharedWorkforce::TaskResult.new({'callback_params'=>{'resource_id' => '2'}, 'responses'=>[{'answer'=>'yes', 'username'=>'bilbo'}, {'answer'=>'no', 'username'=>'frodo'}, {'answer'=>'yes', 'username'=>'sam'}], 'name'=>"Approve photo"})
64
+ r = SharedWorkforce::TaskResult.new('task'=>{'callback_params'=>{'resource_id' => '2'}, 'responses'=>[{'answer'=>'yes', 'username'=>'bilbo'}, {'answer'=>'no', 'username'=>'frodo'}, {'answer'=>'yes', 'username'=>'sam'}], 'name'=>"Approve photo"})
35
65
  r.usernames.should == ['bilbo', 'frodo', 'sam']
36
66
  end
37
67
  end
data/spec/task_spec.rb CHANGED
@@ -15,6 +15,8 @@ describe "Task" do
15
15
  instruction 'Please classify this photo by choosing the appropriate tickboxes.'
16
16
  responses_required 3
17
17
  image_url "http://www.google.com/logo.png"
18
+ answer_type :crop
19
+ image_crop_ratio 1.7
18
20
 
19
21
  answer_options ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways']
20
22
 
@@ -35,6 +37,7 @@ describe "Task" do
35
37
  task.title.should == "Approve Photo"
36
38
  task.responses_required.should == 3
37
39
  task.image_url.should == "http://www.google.com/logo.png"
40
+ task.image_crop_ratio.should == 1.7
38
41
  task.answer_options.should == ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways']
39
42
  end
40
43
 
@@ -85,7 +88,7 @@ describe "Task" do
85
88
  task_class = Class.new { include SharedWorkforce::Task; on_success :do_work; def do_work(*args); end; def setup(*args); end }
86
89
  task_class.any_instance.should_receive(:success!)
87
90
  task_class.any_instance.should_receive(:complete!)
88
- task = task_class.new(SharedWorkforce::TaskResult.new({'callback_params'=>{'key'=>'value'}}))
91
+ task = task_class.new(SharedWorkforce::TaskResult.new({'task'=>{'callback_params'=>{'key'=>'value'}}}))
89
92
  task.attributes[:key].should == 'value'
90
93
  end
91
94
  end
@@ -152,7 +155,7 @@ describe "Task" do
152
155
  end
153
156
 
154
157
  it "should pass the resource to the callback method as the first argument and the result as the second argument" do
155
- result = SharedWorkforce::TaskResult.new(:callback_params=>@task.attributes)
158
+ result = SharedWorkforce::TaskResult.new({'task'=>{'callback_params'=>@task.attributes}})
156
159
  @task.should_receive(:do_work).with(@resource, @result.responses)
157
160
  @task.success!(@result)
158
161
  end
@@ -290,13 +293,13 @@ describe "Task" do
290
293
  it "should return the resource from the callback params" do
291
294
  class ResourceFinder; def self.find(id); return "#{id}ABCD"; end; end
292
295
  task_class = Class.new { include SharedWorkforce::Task }
293
- task = task_class.new(SharedWorkforce::TaskResult.new({'callback_params'=>{'_task'=>{'resource'=>{'class_name'=>'ResourceFinder', 'id' => '2'}}}}))
296
+ task = task_class.new(SharedWorkforce::TaskResult.new({'api_key'=>SharedWorkforce.configuration.api_key, 'task'=>{'callback_params'=>{'_task'=>{'resource'=>{'class_name'=>'ResourceFinder', 'id' => '2'}}}}}))
294
297
  task.resource.should == "2ABCD"
295
298
  end
296
299
 
297
300
  it "should return nil if the callback params do not specify a resource" do
298
301
  task_class = Class.new { include SharedWorkforce::Task }
299
- task = task_class.new(SharedWorkforce::TaskResult.new({'callback_params'=>{}}))
302
+ task = task_class.new(SharedWorkforce::TaskResult.new({'api_key'=>SharedWorkforce.configuration.api_key, 'task'=>{'callback_params'=>{}}}))
300
303
  task.resource.should == nil
301
304
  end
302
305
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shared_workforce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-27 00:00:00.000000000 Z
12
+ date: 2012-04-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
16
- requirement: &70130604732600 !ruby/object:Gem::Requirement
16
+ requirement: &70269574726440 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70130604732600
24
+ version_requirements: *70269574726440
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: json
27
- requirement: &70130604732120 !ruby/object:Gem::Requirement
27
+ requirement: &70269574725980 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70130604732120
35
+ version_requirements: *70269574725980
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: activesupport
38
- requirement: &70130604731700 !ruby/object:Gem::Requirement
38
+ requirement: &70269574725520 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70130604731700
46
+ version_requirements: *70269574725520
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec
49
- requirement: &70130604731020 !ruby/object:Gem::Requirement
49
+ requirement: &70269574724960 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.2.9
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70130604731020
57
+ version_requirements: *70269574724960
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: webmock
60
- requirement: &70130604719020 !ruby/object:Gem::Requirement
60
+ requirement: &70269574724540 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70130604719020
68
+ version_requirements: *70269574724540
69
69
  description: Shared Workforce is a service and simple API for human intelligence tasks
70
70
  email:
71
71
  - sam@samoliver.com
@@ -96,6 +96,7 @@ files:
96
96
  - shared_workforce.gemspec
97
97
  - spec/.rspec
98
98
  - spec/client_spec.rb
99
+ - spec/end_point_spec.rb
99
100
  - spec/spec_helper.rb
100
101
  - spec/support/configuration_helper.rb
101
102
  - spec/task_request/black_hole_spec.rb
@@ -130,6 +131,7 @@ specification_version: 3
130
131
  summary: Shared Workforce Client
131
132
  test_files:
132
133
  - spec/client_spec.rb
134
+ - spec/end_point_spec.rb
133
135
  - spec/spec_helper.rb
134
136
  - spec/support/configuration_helper.rb
135
137
  - spec/task_request/black_hole_spec.rb