diff --git a/TODO.markdown b/TODO.markdown
new file mode 100644
index 0000000..3c8ddc0
--- /dev/null
+++ b/TODO.markdown
@@ -0,0 +1,3 @@
+Fakeweb all requests, should be able to turn off
+Better error handling and checking
+Operations should include request?
\ No newline at end of file
diff --git a/lib/rturk/base.rb b/lib/rturk/base.rb
new file mode 100644
index 0000000..e69de29
diff --git a/lib/rturk/builders/qualifications_builder.rb b/lib/rturk/builders/qualifications_builder.rb
new file mode 100644
index 0000000..5f6b7ef
--- /dev/null
+++ b/lib/rturk/builders/qualifications_builder.rb
@@ -0,0 +1,98 @@
+module RTurk
+
+ class Qualifications
+
+ # For more information about qualification requirements see:
+ # http://docs.amazonwebservices.com/AWSMturkAPI/2008-08-02/index.html?ApiReference_QualificationRequirementDataStructureArticle.html
+ #
+
+ COMPARATORS = {:gt => 'GreaterThan', :lt => 'LessThan', :gte => 'GreaterThanOrEqualTo',
+ :lte => 'LessThanOrEqualTo', :eql => 'EqualTo', :not => 'NotEqualTo', :exists => 'Exists'}
+
+ TYPES = {:approval_rate => '000000000000000000L0', :submission_rate => '00000000000000000000',
+ :abandoned_rate => '0000000000000000007', :return_rate => '000000000000000000E0',
+ :rejection_rate => '000000000000000000S0', :hits_approved => '00000000000000000040',
+ :adult => '00000000000000000060', :country => '00000000000000000071'}
+
+ attr_accessor :requirements, :types
+
+ def initialize
+ @requirements = []
+ @types = {}
+ end
+
+
+ # Builds the basic requirements for a qualification
+ # needs at the minimum
+ # :type_id, :comparator => :value
+ #
+ def build (opts)
+ # If the value is a string, we can assume it's the country since,
+ # Amazon states that there can be only integer values and countries
+ operation = opts.reject{|k,v| !COMPARATORS.include?(k)}
+ comparator = COMPARATORS[operation.keys.first]
+ value = operation.values.first
+ params = {}
+ value = 1 if value == true # For boolean types eg. Adult
+ if value.to_s.match(/[A-Z]./)
+ params[:Country] = value
+ else
+ params[:IntegerValue] = value
+ end
+ params = params.merge({:QualificationTypeId => opts[:type_id],
+ :Comparator => comparator, :RequiredToPreview => opts[:required_to_preview]})
+ end
+
+ def to_aws_params
+ params = {}
+ @requirements.each_with_index do |qualifier, i|
+ params["QualificationRequirement.#{i+1}.QualificationTypeId"] = qualifier[:QualificationTypeId]
+ params["QualificationRequirement.#{i+1}.Comparator"] = qualifier[:Comparator]
+ params["QualificationRequirement.#{i+1}.IntegerValue"] = qualifier[:IntegerValue] if qualifier[:IntegerValue]
+ params["QualificationRequirement.#{i+1}.LocaleValue.Country"] = qualifier[:Country] if qualifier[:Country]
+ params["QualificationRequirement.#{i+1}.RequiredToPreview"] = qualifier[:RequiredToPreview] || 'true'
+ end
+ params
+ end
+
+ # Can use this to manually add custom requirement types
+ # Needs a type name(you can reference this later)
+ # and the operation as a hash: ':gt => 85'
+ # Example
+ # qualifications.add('EnglishSkillsRequirement', :gt => 66, :type_id => '1234567890123456789ABC')
+ #
+ def add(opts)
+ @requirements << self.build(opts)
+ end
+
+ # This lets you add a custom named type to the list
+ # Example
+ # qualifications.add_type(:custom_requirement, '1234567890123456789ABC')
+ # qualifications.custom_requirement(:gte => 55)
+ #
+ def add_type(name, type_id)
+ @types[name.to_sym] = type_id
+ end
+
+ def method_missing(method, opts)
+ if opts == true || opts == false
+ # allows us to call booleans on a method
+ # e.g. qualifications.adult(true)
+ opts = {:eql => opts}
+ end
+ if types.include?(method)
+ opts.merge!({:type_id => types[method]})
+ self.add(opts)
+ end
+ end
+
+ def types
+ TYPES.merge(@types)
+ end
+
+ end
+
+
+end
+
+
diff --git a/lib/rturk/builders/question_builder.rb b/lib/rturk/builders/question_builder.rb
new file mode 100644
index 0000000..bd8a147
--- /dev/null
+++ b/lib/rturk/builders/question_builder.rb
@@ -0,0 +1,50 @@
+require 'cgi'
+
+module RTurk
+ class Question
+
+ attr_accessor :url, :url_params, :frame_height
+
+ def initialize(url = nil, opts = {})
+ @url = url
+ self.frame_height = opts.delete(:frame_height) || 400
+ self.url_params = opts
+ end
+
+ def querystring
+ @url_params.collect { |key, value| [CGI.escape(key.to_s), CGI.escape(value.to_s)].join('=') }.join('&')
+ end
+
+ def url
+ unless querystring.empty?
+ # slam the params onto url, if url already has params, add 'em with a &
+ @url.index('?') ? "#{@url}{querystring}" : "#{@url}?#{querystring}"
+ else
+ @url
+ end
+ end
+
+ def params
+ @url_params
+ end
+
+ def params=(param_set)
+ @url_params = param_set
+ end
+
+ def to_aws_params
+ raise MissingURL, "needs a url to build an external question" unless @url
+ # TODO: update the xmlns schema... maybe
+ xml = <<-XML
+
+ #{url}
+ #{frame_height}
+
+ XML
+ xml
+ end
+
+ end
+
+
+end
\ No newline at end of file
diff --git a/lib/rturk/credentials.rb b/lib/rturk/credentials.rb
new file mode 100644
index 0000000..d422a78
--- /dev/null
+++ b/lib/rturk/credentials.rb
@@ -0,0 +1,16 @@
+module RTurk
+ module Credentials
+
+ SANDBOX = 'http://mechanicalturk.sandbox.amazonaws.com/'
+ PRODUCTION = 'http://mechanicalturk.amazonaws.com/'
+
+ attr_reader :access_key, :secret_key, :host
+
+ def initialize(access_key, secret_key, opts ={})
+ @access_key = access_key
+ @secret_key = secret_key
+ @host = opts[:sandbox] ? SANDBOX : PRODUCTION
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/lib/rturk/logging.rb b/lib/rturk/logging.rb
new file mode 100644
index 0000000..9a22638
--- /dev/null
+++ b/lib/rturk/logging.rb
@@ -0,0 +1,21 @@
+require 'logger'
+
+module RTurk
+ module Logging
+ def logger=(logger_obj)
+ @logger = logger_obj
+ end
+
+ def logger
+ unless @logger
+ @logger = Logger.new(STDOUT)
+ @logger.level = Logger::INFO
+ end
+ @logger
+ end
+
+ def log_level=(level=Logger::INFO)
+ logger.level = level
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/rturk/operations/create_hit.rb b/lib/rturk/operations/create_hit.rb
new file mode 100644
index 0000000..4893c2c
--- /dev/null
+++ b/lib/rturk/operations/create_hit.rb
@@ -0,0 +1,61 @@
+module RTurk
+ class CreateHit < Operation
+ #
+ # We perform the magic here to create a HIT with the minimum amount of fuss.
+ # You should be able to pass in a hash with all the setting(ala YAML) or
+ # do all the config in a block.
+ #
+
+ attr_accessor :title, :keywords, :description, :reward, :currency, :assignments
+ attr_accessor :lifetime, :duration, :auto_approval, :note, :qualifications
+
+
+ def initialize(opts = {})
+ opts.each_pair do |k,v|
+ if v.is_a? Hash
+ obj = self.send k.to_sym
+ v.each_pair do |key,val|
+ obj.send key.to_sym, val
+ end
+ elsif self.respond_to?("#{k.to_sym}=")
+ self.send "#{k}=".to_sym, v
+ elsif self.respond_to?(k.to_sym)
+ self.send k.to_sym, v
+ end
+ end
+ yield(self) if block_given?
+ end
+
+ def qualification
+ @qualifications ||= RTurk::Qualifications.new
+ end
+
+ def question
+ @question ||= RTurk::Question.new
+ end
+
+ def to_aws_params
+ hit_params.merge(qualification.to_aws_params)
+ end
+
+ def response(xml)
+ RTurk::CreateHitResponse.parse(xml)
+ end
+
+ private
+
+ def hit_params
+ {'Title'=>self.title,
+ 'MaxAssignments' => self.assignments,
+ 'LifetimeInSeconds'=> self.lifetime,
+ 'Reward.Amount' => self.reward,
+ 'Reward.CurrencyCode' => (self.currency || 'USD'),
+ 'Keywords' => self.keywords,
+ 'Description' => self.description,
+ 'RequesterAnnotation' => note}
+ end
+
+ RTurk::Operation.register('create_hit', self)
+
+ end
+end
diff --git a/lib/rturk/operations/get_answer.rb b/lib/rturk/operations/get_answer.rb
new file mode 100644
index 0000000..8c61c32
--- /dev/null
+++ b/lib/rturk/operations/get_answer.rb
@@ -0,0 +1,7 @@
+module RTurk
+ class GetAnswer < Operation
+ def get_it
+
+ end
+ end
+end
diff --git a/lib/rturk/parsers/answer_parser.rb b/lib/rturk/parsers/answer_parser.rb
new file mode 100644
index 0000000..71c08ba
--- /dev/null
+++ b/lib/rturk/parsers/answer_parser.rb
@@ -0,0 +1,20 @@
+module RTurk
+ class AnswerParser
+
+ def self.parse(xml)
+ answer = XmlSimple.xml_in(xml, {'ForceArray' => false})
+ response = {}
+ answers = answer['Answer']
+ answers = Array.new(1) { answers } unless answers.instance_of? Array
+ answers.each do |a|
+ question = a['QuestionIdentifier']
+ a.delete('QuestionIdentifier')
+ a.each_value do |v|
+ response[question] = v
+ end
+ end
+ response
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/lib/rturk/responses/basic_response.rb b/lib/rturk/responses/basic_response.rb
new file mode 100644
index 0000000..8a10c04
--- /dev/null
+++ b/lib/rturk/responses/basic_response.rb
@@ -0,0 +1,20 @@
+module RTurk
+
+ class BasicResponse < Response
+
+ def parsed_xml
+ @parsed_xml ||= XmlSimple.xml_in(@xml.to_s, {'ForceArray' => false})
+ end
+
+ def success?
+ !parsed_xml['HIT']['Request'].include?('Errors')
+ end
+
+ def [](key)
+ parsed_xml[key]
+ end
+
+
+ end
+
+end
diff --git a/lib/rturk/responses/create_hit_response.rb b/lib/rturk/responses/create_hit_response.rb
new file mode 100644
index 0000000..8e53a48
--- /dev/null
+++ b/lib/rturk/responses/create_hit_response.rb
@@ -0,0 +1,5 @@
+module RTurk
+ class HitResponse < Response
+
+ end
+end
\ No newline at end of file
diff --git a/spec/fake_responses/create_hit.xml b/spec/fake_responses/create_hit.xml
new file mode 100644
index 0000000..d611f4e
--- /dev/null
+++ b/spec/fake_responses/create_hit.xml
@@ -0,0 +1,12 @@
+
+
+ ece2785b-6292-4b12-a60e-4c34847a7916
+
+
+
+ True
+
+ GBHZVQX3EHXZ2AYDY2T0
+ NYVZTQ1QVKJZXCYZCZVZ
+
+
\ No newline at end of file
diff --git a/spec/hit_operation_spec.rb b/spec/hit_operation_spec.rb
new file mode 100644
index 0000000..2610786
--- /dev/null
+++ b/spec/hit_operation_spec.rb
@@ -0,0 +1,41 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe "The creation of a HIT operation" do
+
+ before(:all) do
+ aws = YAML.load(File.open(File.join(SPEC_ROOT, 'mturk.yml')))
+ @turk = RTurk::Requester.new(aws['AWSAccessKeyId'], aws['AWSAccessKey'], :sandbox => true)
+ end
+
+ it "should allow us to create HIT's in a ruby'esque way" do
+
+ hit = RTurk::Hit.new(:assignments => 5) do |hit|
+ hit.qualification.approval :gt => 90
+ hit.qualification.country :not => 'PH'
+ hit.qualification.adult true
+ hit.title = "Look at some dirty pictures from 4Chan"
+ hit.question('http://mpercival.com', :chapter => 1)
+ hit.question.params[:chapter] = 2 #change the parameters
+ end
+
+ hit.assignments.should eql 5
+ hit.title.should eql "Look at some dirty pictures from 4Chan"
+ hit.question.url.should == "http://mpercival.com?chapter=2"
+ end
+
+ it "should allow us to create HIT's with a hash from yaml" do
+
+ hit = RTurk::Hit.new(:assignments => 5, :title => "Look at some dirty pictures from 4Chan",
+ :question => 'http://mpercival.com', :assignments => 5,
+ :qualification =>
+ {
+ :approval => {:gt => 90},
+ :adult => true,
+ :country => {:not => 'PH'}
+ },
+ :description => "Get paid in nickels!")
+ hit.assignments.should eql 5
+ hit.title.should eql "Look at some dirty pictures from 4Chan"
+ end
+
+end
\ No newline at end of file
diff --git a/spec/hit_spec.yml b/spec/hit_spec.yml
new file mode 100644
index 0000000..94bb8d2
--- /dev/null
+++ b/spec/hit_spec.yml
@@ -0,0 +1,17 @@
+---
+:Description: Simply write a twitter update for me
+:LifetimeInSeconds: 3600
+:RequesterAnnotation: OptionalNote
+:Reward:
+ :Amount: 0.1
+ :CurrencyCode: USD
+:AssignmentDurationInSeconds: 3600
+:AutoApprovalDelayInSeconds: 3600
+:QualificationRequirement:
+- :IntegerValue: 90
+ :Comparator: GreaterThan
+ :RequiredToPreview: "false"
+ :QualificationTypeId: 000000000000000000L0
+:Title: Write a twitter update
+:Keywords: twitter, blogging, writing, english
+:MaxAssignments: 1
diff --git a/spec/operation_spec.rb b/spec/operation_spec.rb
new file mode 100644
index 0000000..7976ac6
--- /dev/null
+++ b/spec/operation_spec.rb
@@ -0,0 +1,22 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+
+describe RTurk::Operation do
+
+ before(:all) do
+ class Mark < RTurk::Operation
+
+ def is_awesome?
+ true
+ end
+ RTurk::Operation.register('mark', self)
+ end
+
+ end
+
+ it "should build a question" do
+ p RTurk::Operation.defined_operations
+ RTurk::Operation.defined_operations.include?('mark').should be_true
+ end
+
+end
\ No newline at end of file
diff --git a/spec/what_i_want_spec.rb b/spec/what_i_want_spec.rb
new file mode 100644
index 0000000..5a0966d
--- /dev/null
+++ b/spec/what_i_want_spec.rb
@@ -0,0 +1,29 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe "using mechanical turk with RTurk" do
+
+ before(:all) do
+ aws = YAML.load(File.open(File.join(SPEC_ROOT, 'mturk.yml')))
+ @turk = RTurk.setup(aws['AWSAccessKeyId'], aws['AWSAccessKey'], :sandbox => true)
+ end
+
+ it "should let me create a hit" do
+
+ response = RTurk::CreateHit.new(:title => "Look at some pictures from 4Chan") do |hit|
+ hit.assignments = 5
+ hit.question.url = "http://mpercival.com"
+ hit.question.params = {:picture_set => 1234}
+ hit.reward = 0.05
+ hit.qualifications.approval_rate.greater_than 80
+ hit.qualifications.add(:country => 'PH')
+ hit.qualifications.add(:adult => true)
+ end
+ response.success?.should be_true
+ end
+
+
+ it "should let me delete all my hits" do
+ @turk.delete_all_hits
+ end
+
+end
\ No newline at end of file