houdini 0.2.4 → 0.3.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.
- data/.gitignore +3 -1
- data/Gemfile.lock +97 -88
- data/README.markdown +31 -27
- data/app/controllers/houdini/postbacks_controller.rb +7 -9
- data/config/routes.rb +2 -2
- data/houdini.gemspec +3 -2
- data/lib/houdini.rb +35 -7
- data/lib/houdini/model.rb +14 -49
- data/lib/houdini/postback_processor.rb +21 -0
- data/lib/houdini/task.rb +46 -10
- data/lib/houdini/task_manager.rb +20 -0
- data/lib/houdini/version.rb +1 -1
- data/spec/dummy/app/models/article.rb +12 -14
- data/spec/houdini/model_spec.rb +27 -0
- data/spec/houdini/postback_processor_spec.rb +44 -0
- data/spec/houdini/task_manager_spec.rb +26 -0
- data/spec/houdini/task_spec.rb +136 -0
- data/spec/houdini_spec.rb +25 -0
- data/spec/requests/integration_spec.rb +21 -19
- data/spec/spec_helper.rb +3 -3
- metadata +76 -118
- data/lib/houdini/base.rb +0 -35
- data/spec/controllers/houdini/postbacks_controller_spec.rb +0 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/inflections.rb +0 -10
- data/spec/dummy/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/public/404.html +0 -26
- data/spec/dummy/public/422.html +0 -26
- data/spec/dummy/public/500.html +0 -26
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/javascripts/application.js +0 -2
- data/spec/dummy/public/javascripts/controls.js +0 -965
- data/spec/dummy/public/javascripts/dragdrop.js +0 -974
- data/spec/dummy/public/javascripts/effects.js +0 -1123
- data/spec/dummy/public/javascripts/prototype.js +0 -6001
- data/spec/dummy/public/javascripts/rails.js +0 -175
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/houdini_rails_spec.rb +0 -4
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,115 +1,123 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
houdini (0.
|
5
|
-
rails (
|
4
|
+
houdini (0.3.0)
|
5
|
+
rails (> 3.0.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
rack (~> 1.
|
21
|
-
rack-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
arel (
|
38
|
-
builder (
|
39
|
-
capybara (
|
40
|
-
celerity (>= 0.7.9)
|
41
|
-
culerity (>= 0.2.4)
|
10
|
+
actionmailer (3.2.1)
|
11
|
+
actionpack (= 3.2.1)
|
12
|
+
mail (~> 2.4.0)
|
13
|
+
actionpack (3.2.1)
|
14
|
+
activemodel (= 3.2.1)
|
15
|
+
activesupport (= 3.2.1)
|
16
|
+
builder (~> 3.0.0)
|
17
|
+
erubis (~> 2.7.0)
|
18
|
+
journey (~> 1.0.1)
|
19
|
+
rack (~> 1.4.0)
|
20
|
+
rack-cache (~> 1.1)
|
21
|
+
rack-test (~> 0.6.1)
|
22
|
+
sprockets (~> 2.1.2)
|
23
|
+
activemodel (3.2.1)
|
24
|
+
activesupport (= 3.2.1)
|
25
|
+
builder (~> 3.0.0)
|
26
|
+
activerecord (3.2.1)
|
27
|
+
activemodel (= 3.2.1)
|
28
|
+
activesupport (= 3.2.1)
|
29
|
+
arel (~> 3.0.0)
|
30
|
+
tzinfo (~> 0.3.29)
|
31
|
+
activeresource (3.2.1)
|
32
|
+
activemodel (= 3.2.1)
|
33
|
+
activesupport (= 3.2.1)
|
34
|
+
activesupport (3.2.1)
|
35
|
+
i18n (~> 0.6)
|
36
|
+
multi_json (~> 1.0)
|
37
|
+
arel (3.0.2)
|
38
|
+
builder (3.0.0)
|
39
|
+
capybara (1.1.2)
|
42
40
|
mime-types (>= 1.16)
|
43
41
|
nokogiri (>= 1.3.3)
|
44
42
|
rack (>= 1.0.0)
|
45
43
|
rack-test (>= 0.5.4)
|
46
|
-
selenium-webdriver (
|
47
|
-
xpath (~> 0.1.
|
48
|
-
|
49
|
-
childprocess (0.1.8)
|
44
|
+
selenium-webdriver (~> 2.0)
|
45
|
+
xpath (~> 0.1.4)
|
46
|
+
childprocess (0.3.0)
|
50
47
|
ffi (~> 1.0.6)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
mail (2.2.15)
|
60
|
-
activesupport (>= 2.3.6)
|
48
|
+
diff-lcs (1.1.3)
|
49
|
+
erubis (2.7.0)
|
50
|
+
ffi (1.0.11)
|
51
|
+
hike (1.2.1)
|
52
|
+
i18n (0.6.0)
|
53
|
+
journey (1.0.1)
|
54
|
+
json (1.6.5)
|
55
|
+
mail (2.4.4)
|
61
56
|
i18n (>= 0.4.0)
|
62
57
|
mime-types (~> 1.16)
|
63
58
|
treetop (~> 1.4.8)
|
64
|
-
mime-types (1.
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
rack
|
69
|
-
|
70
|
-
|
59
|
+
mime-types (1.17.2)
|
60
|
+
multi_json (1.0.4)
|
61
|
+
nokogiri (1.5.0)
|
62
|
+
polyglot (0.3.3)
|
63
|
+
rack (1.4.1)
|
64
|
+
rack-cache (1.1)
|
65
|
+
rack (>= 0.4)
|
66
|
+
rack-ssl (1.3.2)
|
67
|
+
rack
|
68
|
+
rack-test (0.6.1)
|
71
69
|
rack (>= 1.0)
|
72
|
-
rails (3.
|
73
|
-
actionmailer (= 3.
|
74
|
-
actionpack (= 3.
|
75
|
-
activerecord (= 3.
|
76
|
-
activeresource (= 3.
|
77
|
-
activesupport (= 3.
|
70
|
+
rails (3.2.1)
|
71
|
+
actionmailer (= 3.2.1)
|
72
|
+
actionpack (= 3.2.1)
|
73
|
+
activerecord (= 3.2.1)
|
74
|
+
activeresource (= 3.2.1)
|
75
|
+
activesupport (= 3.2.1)
|
78
76
|
bundler (~> 1.0)
|
79
|
-
railties (= 3.
|
80
|
-
railties (3.
|
81
|
-
actionpack (= 3.
|
82
|
-
activesupport (= 3.
|
77
|
+
railties (= 3.2.1)
|
78
|
+
railties (3.2.1)
|
79
|
+
actionpack (= 3.2.1)
|
80
|
+
activesupport (= 3.2.1)
|
81
|
+
rack-ssl (~> 1.3.2)
|
83
82
|
rake (>= 0.8.7)
|
84
|
-
|
83
|
+
rdoc (~> 3.4)
|
84
|
+
thor (~> 0.14.6)
|
85
85
|
rake (0.8.7)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
rspec-
|
90
|
-
|
91
|
-
|
86
|
+
rdoc (3.12)
|
87
|
+
json (~> 1.4)
|
88
|
+
rspec (2.8.0)
|
89
|
+
rspec-core (~> 2.8.0)
|
90
|
+
rspec-expectations (~> 2.8.0)
|
91
|
+
rspec-mocks (~> 2.8.0)
|
92
|
+
rspec-core (2.8.0)
|
93
|
+
rspec-expectations (2.8.0)
|
92
94
|
diff-lcs (~> 1.1.2)
|
93
|
-
rspec-mocks (2.
|
94
|
-
rspec-rails (2.
|
95
|
-
actionpack (
|
96
|
-
activesupport (
|
97
|
-
railties (
|
98
|
-
rspec (~> 2.
|
99
|
-
rubyzip (0.9.
|
100
|
-
selenium-webdriver (
|
101
|
-
childprocess (>= 0.
|
102
|
-
ffi (
|
103
|
-
|
95
|
+
rspec-mocks (2.8.0)
|
96
|
+
rspec-rails (2.8.1)
|
97
|
+
actionpack (>= 3.0)
|
98
|
+
activesupport (>= 3.0)
|
99
|
+
railties (>= 3.0)
|
100
|
+
rspec (~> 2.8.0)
|
101
|
+
rubyzip (0.9.5)
|
102
|
+
selenium-webdriver (2.18.0)
|
103
|
+
childprocess (>= 0.2.5)
|
104
|
+
ffi (~> 1.0.9)
|
105
|
+
multi_json (~> 1.0.4)
|
104
106
|
rubyzip
|
105
|
-
|
107
|
+
sprockets (2.1.2)
|
108
|
+
hike (~> 1.2)
|
109
|
+
rack (~> 1.0)
|
110
|
+
tilt (~> 1.1, != 1.3.0)
|
111
|
+
sqlite3 (1.3.5)
|
106
112
|
sqlite3-ruby (1.3.3)
|
107
113
|
sqlite3 (>= 1.3.3)
|
108
114
|
thor (0.14.6)
|
109
|
-
|
115
|
+
tilt (1.3.3)
|
116
|
+
treetop (1.4.10)
|
117
|
+
polyglot
|
110
118
|
polyglot (>= 0.3.1)
|
111
|
-
tzinfo (0.3.
|
112
|
-
xpath (0.1.
|
119
|
+
tzinfo (0.3.33)
|
120
|
+
xpath (0.1.4)
|
113
121
|
nokogiri (~> 1.3)
|
114
122
|
|
115
123
|
PLATFORMS
|
@@ -118,5 +126,6 @@ PLATFORMS
|
|
118
126
|
DEPENDENCIES
|
119
127
|
capybara (>= 0.4.1)
|
120
128
|
houdini!
|
121
|
-
|
129
|
+
rake (~> 0.8.7)
|
130
|
+
rspec-rails (>= 2.8.1)
|
122
131
|
sqlite3-ruby
|
data/README.markdown
CHANGED
@@ -4,56 +4,60 @@ This ruby gem is a Rails Engine for using the Houdini Mechanical Turk API. It pr
|
|
4
4
|
|
5
5
|
Check out the [Houdini Documentation](http://houdiniapi.com/documentation) for more info about the API.
|
6
6
|
|
7
|
-
# Installation (Rails 3.
|
7
|
+
# Installation (Rails 3.x)
|
8
8
|
|
9
9
|
Add the gem to your Gemfile
|
10
10
|
|
11
11
|
gem 'houdini'
|
12
12
|
|
13
|
-
Configure a few constants in config/
|
13
|
+
Configure a few constants in config/initializers/houdini.rb
|
14
14
|
|
15
|
-
|
16
|
-
Houdini.setup(:sandbox, :api_key => 'YOUR_API_KEY', :app_host => 'https://your-app-domain.com')
|
17
|
-
end
|
15
|
+
Houdini.setup :sandbox, :api_key => 'YOUR_API_KEY', :app_host => 'https://your-app-domain.com'
|
18
16
|
|
19
17
|
You may want to configure Houdini differently for each of you environments.
|
20
18
|
|
21
19
|
# Example Usage
|
22
20
|
|
23
|
-
|
21
|
+
Request a beta account at http://houdiniapi.com to gain access to the Houdini Blueprint Editor.
|
24
22
|
|
25
23
|
Setup Houdini in your ActiveRecord model:
|
26
24
|
|
27
25
|
class Post < ActiveRecord::Base
|
28
26
|
include Houdini::Model
|
29
27
|
|
30
|
-
houdini :image_moderation,
|
31
|
-
:
|
32
|
-
:image_url =>
|
28
|
+
houdini :image_moderation,
|
29
|
+
:input => {
|
30
|
+
:image_url => :image_url, # call the input_url method for
|
31
|
+
:image_caption => lambda{ self.caption.titleize }, # use a lambda, called in the model's context
|
32
|
+
:image_size => "100x100" # just send this string
|
33
33
|
},
|
34
|
-
:
|
35
|
-
:after_submit => :update_houdini_attributes,
|
34
|
+
:on => :after_create,
|
36
35
|
:on_task_completion => :process_image_moderation_answer
|
37
36
|
|
38
|
-
after_create :moderate_image
|
39
|
-
|
40
|
-
def moderate_image
|
41
|
-
Houdini.perform!(:image_moderation, self)
|
42
|
-
end
|
43
|
-
|
44
|
-
def update_houdini_attributes
|
45
|
-
update_attribute(:houdini_request_sent_at, Time.now)
|
46
|
-
end
|
47
|
-
|
48
37
|
def process_image_moderation_answer(params)
|
49
|
-
update_attribute
|
38
|
+
update_attribute :flagged => params[:category] == 'flagged'
|
50
39
|
end
|
51
40
|
end
|
52
41
|
|
42
|
+
# Usage
|
43
|
+
|
44
|
+
`houdini(blueprint, options)`
|
45
|
+
|
46
|
+
* `blueprint` - The name of the Houdini blueprint to use. Must be symbol or string.
|
47
|
+
* `options` - Hash of options to use.
|
48
|
+
|
49
|
+
## Options
|
50
|
+
* `:input` - Rqeuired. Hash: any task specific info needed to populate your blueprint. Keys must match the blueprint's required input, and values must a `Symbol` of the method to call, a lambdas/procs to be called in the model's context, or just a value to send along.
|
51
|
+
* `:on_task_completion` - Method that should be called when the answer is posted back to your app. Can by a symbol or a lambda/proc. The method will be called with a hash of the returned output from Houdini.
|
52
|
+
* `:on` - Name of a callback to use in order to trigger the submission to Houdini. Must be a symbol/string. If you don't want to use a callback, call the model instance's `houdini_submit_#{blueprint}!` method, where `blueprint` is the first argument you provided for the `houdini` method.
|
53
|
+
* `:after_submit` - Method that should be called after submitting the task to Houdini. Can by a symbol or a lambda/proc.
|
54
|
+
* `:id_method` - Method to get an identifier for the object. It is `id` by default, but you may want to use `to_param` with your app. Can be a symbol or lambda/proc. Use this in conjunction with `:finder`.
|
55
|
+
* `:finder` - Method by which to find the model by an identifier. It is `find` by default. Can be a symbol or lambda/proc. Use this in conjunction with `:id_method`.
|
56
|
+
|
57
|
+
## Credits
|
58
|
+
|
59
|
+
* Thanks to Mike Nicholaides (https://github.com/nicholaides) for a huge refactoring to bring the gem up to date with the new version of Houdini.
|
53
60
|
|
54
|
-
|
61
|
+
## License
|
55
62
|
|
56
|
-
|
57
|
-
* :version - Version of the task design to use
|
58
|
-
* :after_submit - Method that should be called after submitting the task to Houdini.
|
59
|
-
* :on_task_completion - Method that should be called when the answer is posted back to your app.
|
63
|
+
MIT License. Copyright 2012 Houdini Inc. http://houdiniapi.com
|
@@ -1,12 +1,10 @@
|
|
1
1
|
class Houdini::PostbacksController < ApplicationController
|
2
|
-
|
2
|
+
skip_before_filter :protect_from_forgery
|
3
|
+
|
3
4
|
def create
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
else
|
9
|
-
render :json => {:success => false}, :status => 422
|
10
|
-
end
|
5
|
+
task_results = HashWithIndifferentAccess.new ActiveSupport::JSON.decode(request.raw_post)
|
6
|
+
|
7
|
+
Houdini::PostbackProcessor.process params[:object_class], params[:object_id], task_results
|
8
|
+
render :json => { :success => true }
|
11
9
|
end
|
12
|
-
end
|
10
|
+
end
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
|
-
scope "houdini/:object_class/:object_id
|
2
|
+
scope "houdini/:object_class/:object_id" do
|
3
3
|
resources :postbacks,
|
4
4
|
:as => 'houdini_postbacks',
|
5
5
|
:controller => 'houdini/postbacks',
|
6
6
|
:only => [:create]
|
7
7
|
end
|
8
|
-
end
|
8
|
+
end
|
data/houdini.gemspec
CHANGED
@@ -12,10 +12,11 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.description = %q{Rails 3 Engine for using the Houdini Mechanical Turk API}
|
13
13
|
s.homepage = %q{http://github.com/chrisconley/houdini-gem}
|
14
14
|
|
15
|
-
s.add_runtime_dependency "rails", "
|
16
|
-
s.add_development_dependency "rspec-rails", ">= 2.
|
15
|
+
s.add_runtime_dependency "rails", "> 3.0.0"
|
16
|
+
s.add_development_dependency "rspec-rails", ">= 2.8.1"
|
17
17
|
s.add_development_dependency "capybara", ">= 0.4.1"
|
18
18
|
s.add_development_dependency "sqlite3-ruby"
|
19
|
+
s.add_development_dependency "rake", "~> 0.8.7"
|
19
20
|
|
20
21
|
s.files = `git ls-files`.split("\n")
|
21
22
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/houdini.rb
CHANGED
@@ -1,19 +1,15 @@
|
|
1
1
|
require 'net/https'
|
2
2
|
require 'uri'
|
3
3
|
|
4
|
-
require 'houdini/base'
|
5
4
|
require 'houdini/model'
|
6
5
|
require 'houdini/task'
|
6
|
+
require 'houdini/task_manager'
|
7
|
+
require 'houdini/postback_processor'
|
7
8
|
|
8
9
|
require 'houdini/engine'
|
9
10
|
|
10
|
-
|
11
11
|
module Houdini
|
12
12
|
mattr_accessor :environment, :api_key, :app_url, :app_uri, :app_host
|
13
|
-
# Convenience methods
|
14
|
-
def self.perform!(task_name, object)
|
15
|
-
object.send_to_houdini(task_name)
|
16
|
-
end
|
17
13
|
|
18
14
|
def self.setup(environment, options)
|
19
15
|
self.environment = environment.to_s
|
@@ -21,4 +17,36 @@ module Houdini
|
|
21
17
|
self.app_url = options[:app_host] || options[:app_url]
|
22
18
|
self.app_uri = URI.parse(self.app_url)
|
23
19
|
end
|
24
|
-
|
20
|
+
|
21
|
+
RequestError = Class.new(NameError)
|
22
|
+
HostError = Class.new(NameError)
|
23
|
+
|
24
|
+
HOST = 'v1.houdiniapi.com'
|
25
|
+
|
26
|
+
def self.submit!(blueprint, class_name, object_id, input_params)
|
27
|
+
unless environment.to_s == 'test'
|
28
|
+
request(
|
29
|
+
:environment => environment,
|
30
|
+
:api_key => api_key,
|
31
|
+
:blueprint => blueprint,
|
32
|
+
:input => input_params,
|
33
|
+
:postback_url => "#{app_uri.scheme}://#{app_uri.host}:#{app_uri.port}/houdini/#{class_name}/#{object_id}/postbacks"
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.request(params)
|
39
|
+
# TODO: this should validate sooner
|
40
|
+
raise HostError, "Houdini.app_url should specify http:// or https://" unless app_url.match(/^https?\:\/\//)
|
41
|
+
|
42
|
+
url = File.join("https://", HOST, "tasks.json")
|
43
|
+
uri = URI.parse(url)
|
44
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
45
|
+
http.use_ssl = true
|
46
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
47
|
+
response, body = http.post(uri.path, params.to_json)
|
48
|
+
if response.code != "200"
|
49
|
+
raise RequestError, "The request to houdini failed with code #{response.code}: #{body}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/houdini/model.rb
CHANGED
@@ -1,62 +1,27 @@
|
|
1
1
|
module Houdini
|
2
2
|
module Model
|
3
|
-
# def self.included(base)
|
4
|
-
# base.extend ClassMethods
|
5
|
-
# base.include Rails.application.routes.url_helpers
|
6
|
-
# end
|
7
|
-
|
8
3
|
extend ActiveSupport::Concern
|
9
4
|
|
10
|
-
included do
|
11
|
-
include Rails.application.routes.url_helpers
|
12
|
-
extend ClassMethods
|
13
|
-
end
|
14
|
-
|
15
5
|
module ClassMethods
|
16
|
-
def houdini(
|
17
|
-
|
18
|
-
|
6
|
+
def houdini(blueprint, options={})
|
7
|
+
task_manager = options.delete(:task_manager) || TaskManager
|
8
|
+
callback = options.delete(:on)
|
19
9
|
|
20
|
-
|
21
|
-
@houdini_tasks ||= {}
|
22
|
-
end
|
10
|
+
task_manager.register self, blueprint.to_sym, options
|
23
11
|
|
24
|
-
|
12
|
+
submit_method_name = "houdini_submit_#{blueprint}!".to_sym
|
25
13
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
:postback_url => houdini_postbacks_url(self.class.name, self.id, houdini_task.short_name, {
|
34
|
-
:protocol => Houdini.app_uri.scheme,
|
35
|
-
:host => Houdini.app_uri.host,
|
36
|
-
:port => Houdini.app_uri.port
|
37
|
-
})
|
38
|
-
}
|
14
|
+
# Using a module so that you can use override/modify the method via `super`
|
15
|
+
m = Module.new do
|
16
|
+
define_method submit_method_name do
|
17
|
+
task_manager.submit! self, blueprint
|
18
|
+
end
|
19
|
+
end
|
20
|
+
include m
|
39
21
|
|
40
|
-
|
41
|
-
|
42
|
-
hash[info_name] = model_attribute.call if model_attribute.respond_to?(:call)
|
43
|
-
hash[info_name] = self.send(model_attribute) if self.respond_to?(model_attribute)
|
44
|
-
hash
|
22
|
+
# attach the submit method via the callback
|
23
|
+
send callback, submit_method_name if callback
|
45
24
|
end
|
46
|
-
|
47
|
-
result = Houdini::Base.request(params)
|
48
|
-
|
49
|
-
call_after_submit(task_name)
|
50
|
-
end
|
51
|
-
|
52
|
-
def process_postback(task_name, answer)
|
53
|
-
houdini_task = self.class.houdini_tasks[task_name.to_sym]
|
54
|
-
self.send(houdini_task.on_task_completion, answer)
|
55
|
-
end
|
56
|
-
|
57
|
-
def call_after_submit(task_name)
|
58
|
-
houdini_task = self.class.houdini_tasks[task_name.to_sym]
|
59
|
-
self.send(houdini_task.after_submit) if houdini_task.after_submit
|
60
25
|
end
|
61
26
|
end
|
62
27
|
end
|