vworkapp_ruby 0.1.0 → 0.7.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/Gemfile.lock +1 -1
- data/README.md +79 -12
- data/examples/example_1.rb +32 -0
- data/examples/example_2.rb +23 -0
- data/examples/example_3/Gemfile +5 -0
- data/examples/example_3/Gemfile.lock +35 -0
- data/examples/example_3/README.md +26 -0
- data/examples/example_3/public/style.css +25 -0
- data/examples/example_3/public/truck.png +0 -0
- data/examples/example_3/server.rb +41 -0
- data/examples/example_3/views/index.haml +47 -0
- data/lib/vworkapp_ruby.rb +13 -3
- data/lib/vworkapp_ruby/base/base.rb +173 -0
- data/lib/vworkapp_ruby/{httparty_monkey_patch.rb → base/httparty_monkey_patch.rb} +2 -4
- data/lib/vworkapp_ruby/base/resource.rb +105 -0
- data/lib/vworkapp_ruby/base/response_error.rb +26 -0
- data/lib/vworkapp_ruby/contact.rb +11 -0
- data/lib/vworkapp_ruby/custom_field.rb +11 -0
- data/lib/vworkapp_ruby/customer.rb +9 -97
- data/lib/vworkapp_ruby/job.rb +29 -146
- data/lib/vworkapp_ruby/location.rb +6 -23
- data/lib/vworkapp_ruby/step.rb +11 -0
- data/lib/vworkapp_ruby/telemetry.rb +6 -0
- data/lib/vworkapp_ruby/worker.rb +4 -81
- data/spec/base_spec.rb +174 -0
- data/spec/customer_spec.rb +30 -46
- data/spec/job_spec.rb +189 -143
- data/spec/resource_spec.rb +37 -0
- data/spec/support/active_model_lint.rb +19 -0
- data/spec/worker_spec.rb +6 -25
- data/vworkapp_ruby-0.1.0.gem +0 -0
- data/vworkapp_ruby.gemspec +5 -4
- metadata +46 -16
- data/examples/create_job.rb +0 -34
- data/examples/print_jobs.rb +0 -31
- data/lib/vworkapp_ruby/base.rb +0 -72
- data/lib/vworkapp_ruby/response_error.rb +0 -35
@@ -3,10 +3,8 @@
|
|
3
3
|
# Needed because the extra attributes on the below XML cause httparty to crash:
|
4
4
|
# <jobs type="array" current_page="1" per_page="27" total_pages="1" total_entries="27">
|
5
5
|
#
|
6
|
-
# We probably shouldn't have extra parameters here.
|
7
|
-
#
|
8
|
-
# && v.is_a?(Array)
|
9
|
-
|
6
|
+
# We probably shouldn't have extra parameters here. Change was:
|
7
|
+
# && (v.is_a?(Array) || v.is_a?(Hash))
|
10
8
|
module MultiXml
|
11
9
|
|
12
10
|
def MultiXml.typecast_xml_value(value)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module VWorkApp
|
2
|
+
|
3
|
+
class << self
|
4
|
+
@api_key = "YOU NEED TO SPECIFY YOUR API KEY!!"
|
5
|
+
def api_key=(key)
|
6
|
+
@api_key = key
|
7
|
+
end
|
8
|
+
def api_key
|
9
|
+
@api_key
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Resource < Base
|
14
|
+
extend ActiveModel::Naming
|
15
|
+
include ActiveModel::Conversion
|
16
|
+
include ActiveModel::Validations
|
17
|
+
include HTTParty
|
18
|
+
|
19
|
+
base_uri 'https://api.vworkapp.com/api/2.0'
|
20
|
+
# http_proxy 'localhost', 8888
|
21
|
+
|
22
|
+
headers({
|
23
|
+
"Content-Type" => "text/xml",
|
24
|
+
"User-Agent" => "Ruby.vWorkApp.API"
|
25
|
+
})
|
26
|
+
|
27
|
+
# ------------------
|
28
|
+
# Active Model
|
29
|
+
# ------------------
|
30
|
+
|
31
|
+
def persisted?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
# ------------------
|
36
|
+
# Rest Methods
|
37
|
+
# ------------------
|
38
|
+
|
39
|
+
def create
|
40
|
+
validate_and_raise
|
41
|
+
perform(:post, "/#{resource_name.pluralize}", {}, self.to_xml) do |res|
|
42
|
+
self.class.new(res[resource_name])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def update(options = {})
|
47
|
+
validate_and_raise
|
48
|
+
perform(:put, "/#{resource_name.pluralize}/#{id}.xml", options, self.to_xml)
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete(options = {})
|
52
|
+
perform(:delete, "/#{resource_name.pluralize}/#{id}.xml", options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.show(id, options = {})
|
56
|
+
perform(:get, "/#{resource_name.pluralize}/#{id}.xml", options) do |res|
|
57
|
+
self.new(res[resource_name])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.find(options = {})
|
62
|
+
self.perform(:get, "/#{resource_name.pluralize}.xml", options) do |res|
|
63
|
+
res[resource_name.pluralize].map { |h| self.new(h) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# ------------------
|
68
|
+
# Misc Methods
|
69
|
+
# ------------------
|
70
|
+
|
71
|
+
def self.resource_name
|
72
|
+
@resource_name ||= ActiveSupport::Inflector.demodulize(self).underscore
|
73
|
+
end
|
74
|
+
def resource_name
|
75
|
+
self.class.resource_name
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.perform(action, url, query = {}, body = nil)
|
79
|
+
options = {}
|
80
|
+
options[:query] = { :api_key => VWorkApp.api_key }.merge(query)
|
81
|
+
|
82
|
+
options[:body] = body
|
83
|
+
|
84
|
+
raw = self.send(action, url, options)
|
85
|
+
|
86
|
+
case raw.response
|
87
|
+
when Net::HTTPOK, Net::HTTPCreated
|
88
|
+
yield(raw) if block_given?
|
89
|
+
when Net::HTTPNotFound
|
90
|
+
nil
|
91
|
+
else
|
92
|
+
bad_response(raw)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
def perform(action, url, query = {}, body = nil, &block)
|
96
|
+
self.class.perform(action, url, query, body, &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.bad_response(raw)
|
100
|
+
raise ResponseError.new(raw)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module VWorkApp
|
2
|
+
|
3
|
+
# Error raised on a bad response
|
4
|
+
class ResponseError < StandardError
|
5
|
+
attr_reader :response, :code, :errors
|
6
|
+
|
7
|
+
def initialize(res)
|
8
|
+
@response = res.response
|
9
|
+
@code = res.response.code
|
10
|
+
@errors = extract_errors(res.parsed_response)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"#{code.to_s} - #{response.msg}:\n#{@errors && @errors.join("\n")}".strip
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def extract_errors(body)
|
20
|
+
return if body.empty?
|
21
|
+
errors = Array.wrap(body["errors"]["error"])
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module VWorkApp
|
2
|
+
class Contact < Base
|
3
|
+
hattr_accessor :first_name, :last_name, :phone, :mobile, :email, :location
|
4
|
+
self.include_root_in_json = false
|
5
|
+
|
6
|
+
def ==(other)
|
7
|
+
attributes_eql?(other, :first_name, :last_name, :phone, :mobile, :email, :location)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -1,106 +1,18 @@
|
|
1
1
|
module VWorkApp
|
2
|
-
|
3
|
-
|
4
|
-
attr_accessor :id, :third_party_id, :name, :site_contact, :billing_contact
|
2
|
+
class Customer < Resource
|
3
|
+
hattr_accessor :id, :third_party_id, :name, {:delivery_contact => VWorkApp::Contact}, {:billing_contact => VWorkApp::Contact}
|
5
4
|
|
6
|
-
#---------------
|
7
|
-
# Object Methods
|
8
|
-
#---------------
|
9
|
-
|
10
|
-
def initialize(name, id = nil, third_party_id = nil, site_contact = nil, billing_contact = nil)
|
11
|
-
@name = name
|
12
|
-
@id = id
|
13
|
-
@third_party_id = third_party_id
|
14
|
-
@site_contact = site_contact
|
15
|
-
@billing_contact = billing_contact
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.from_hash(attributes)
|
19
|
-
customer = Customer.new(attributes["name"], attributes["id"], attributes["third_party_id"])
|
20
|
-
customer.site_contact = Contact.from_hash(attributes["delivery_contact"])
|
21
|
-
customer.billing_contact = Contact.from_hash(attributes["billing_contact"])
|
22
|
-
customer
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_hash
|
26
|
-
cust_hash = { :customer => { :name => self.name } }
|
27
|
-
cust_hash[:customer][:third_party_id] = self.third_party_id if third_party_id
|
28
|
-
cust_hash[:customer][:delivery_contact] = self.site_contact.to_hash if site_contact
|
29
|
-
cust_hash[:customer][:billing_contact] = self.billing_contact.to_hash if billing_contact
|
30
|
-
cust_hash
|
31
|
-
end
|
32
|
-
|
33
5
|
def ==(other)
|
34
|
-
attributes_eql?(other,
|
35
|
-
end
|
36
|
-
|
37
|
-
#---------------
|
38
|
-
# REST Methods
|
39
|
-
#---------------
|
40
|
-
|
41
|
-
def create
|
42
|
-
perform(:post, "/customers.xml", {}, self.to_hash) do |res|
|
43
|
-
Customer.from_hash(res["customer"])
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def update(use_third_party_id = false)
|
48
|
-
perform(:put, "/customers/#{id}.xml", { :use_third_party_id => use_third_party_id }, self.to_hash)
|
49
|
-
end
|
50
|
-
|
51
|
-
def delete(use_third_party_id = false)
|
52
|
-
perform(:delete, "/customers/#{id}.xml", { :use_third_party_id => use_third_party_id })
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.show(id, use_third_party_id = false)
|
56
|
-
perform(:get, "/customers/#{id}.xml", :use_third_party_id => use_third_party_id) do |res|
|
57
|
-
Customer.from_hash(res["customer"])
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.find
|
62
|
-
self.perform(:get, "/customers.xml") do |res|
|
63
|
-
res["customers"].map { |h| Customer.from_hash(h) }
|
64
|
-
end
|
6
|
+
attributes_eql?(other, :id, :name, :third_party_id, :delivery_contact, :billing_contact)
|
65
7
|
end
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
class Contact < Base
|
70
|
-
include AttributeEquality
|
71
|
-
attr_accessor :first_name, :last_name, :phone, :mobile, :email, :location
|
72
8
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
@location = location
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.from_hash(attributes)
|
83
|
-
contact = Contact.new(attributes["first_name"], attributes["last_name"], attributes["phone"], attributes["mobile"], attributes["email"])
|
84
|
-
contact.location = Location.from_hash(attributes["location"])
|
85
|
-
contact
|
9
|
+
# XXX Work around for API bug that doesn't accept a nil contact detail.
|
10
|
+
def to_xml(options = {})
|
11
|
+
except = options[:except] || []
|
12
|
+
except << "delivery_contact" unless delivery_contact
|
13
|
+
except << "billing_contact" unless billing_contact
|
14
|
+
super(:except => except)
|
86
15
|
end
|
87
16
|
|
88
|
-
def to_hash
|
89
|
-
contact_hash = {
|
90
|
-
:first_name => self.first_name,
|
91
|
-
:last_name => self.last_name,
|
92
|
-
:phone => self.phone,
|
93
|
-
:mobile => self.mobile,
|
94
|
-
:email => self.email
|
95
|
-
}
|
96
|
-
contact_hash[:location] = self.location.to_hash if location
|
97
|
-
contact_hash
|
98
|
-
end
|
99
|
-
|
100
|
-
def ==(other)
|
101
|
-
attributes_eql?(other, [:first_name, :last_name, :phone, :mobile, :email, :location])
|
102
|
-
end
|
103
|
-
|
104
17
|
end
|
105
|
-
|
106
18
|
end
|
data/lib/vworkapp_ruby/job.rb
CHANGED
@@ -1,168 +1,51 @@
|
|
1
1
|
require 'gcoder'
|
2
2
|
|
3
3
|
module VWorkApp
|
4
|
+
class Job < Resource
|
5
|
+
hattr_accessor :id, :customer_name, :template_name, :planned_duration, {:steps => Array(VWorkApp::Step)}, :published_at,
|
6
|
+
{:custom_fields => Array(VWorkApp::CustomField)}, :third_party_id, :worker_id, :planned_start_at, :customer_id
|
4
7
|
|
5
|
-
|
6
|
-
attr_accessor :id, :customer_name, :template_name, :planned_duration, :steps, :custom_fields, :third_party_id
|
7
|
-
attr_accessor :assigned_to_id, :planned_start_at, :progress_state
|
8
|
+
hattr_reader :actual_start_at, :actual_duration, :progress_state, :state
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
#---------------
|
12
|
-
|
13
|
-
def initialize(customer_name, template_name, planned_duration, steps, custom_fields = nil, id = nil, third_party_id = nil, assigned_to_id = nil, planned_start_at = nil)
|
14
|
-
@id = id
|
15
|
-
@customer_name = customer_name
|
16
|
-
@template_name = template_name
|
17
|
-
@planned_duration = planned_duration
|
18
|
-
@steps = steps
|
19
|
-
@custom_fields = custom_fields
|
20
|
-
@third_party_id = third_party_id
|
21
|
-
@assigned_to_id = assigned_to_id
|
22
|
-
@planned_start_at = planned_start_at
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_hash
|
26
|
-
job_hash = {
|
27
|
-
:job => {
|
28
|
-
:customer_name => self.customer_name,
|
29
|
-
:template_name => self.template_name,
|
30
|
-
:planned_duration => self.planned_duration,
|
31
|
-
:steps => self.steps.map { |s| s.to_hash },
|
32
|
-
}
|
33
|
-
}
|
34
|
-
job_hash[:job][:third_party_id] = self.third_party_id if third_party_id
|
35
|
-
job_hash[:job][:custom_fields] = self.custom_fields.map { |s| s.to_hash } if custom_fields
|
36
|
-
job_hash
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.from_hash(attributes)
|
40
|
-
job = Job.new(attributes["customer_name"], attributes["template_name"], attributes["planned_duration"],
|
41
|
-
attributes["steps"].map { |h| Step.from_hash(h) }, nil, attributes["id"], attributes["third_party_id"])
|
42
|
-
job.custom_fields = attributes["custom_fields"].map { |h| CustomField.from_hash(h) } if attributes["custom_fields"]
|
43
|
-
|
44
|
-
job.assigned_to_id = attributes["worker_id"]
|
45
|
-
job.planned_start_at = attributes["planned_start_at"]
|
46
|
-
job.progress_state = attributes["progress_state"]
|
47
|
-
|
48
|
-
job
|
49
|
-
end
|
10
|
+
validates_presence_of :template_name, :planned_duration, :steps
|
11
|
+
validate :customer_validation
|
50
12
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
return nil if @assigned_to_id.nil?
|
57
|
-
@assigned_to ||= Worker.show(self.assigned_to_id)
|
58
|
-
end
|
59
|
-
|
60
|
-
def ==(other)
|
61
|
-
attributes_eql?(other, [
|
62
|
-
:id, :third_party_id, :customer_name, :template_name, :planned_duration, :planned_start_at,
|
63
|
-
:assigned_to_id, :steps, :custom_fields
|
64
|
-
])
|
65
|
-
end
|
66
|
-
|
67
|
-
#---------------
|
68
|
-
# REST Methods
|
69
|
-
#---------------
|
70
|
-
|
71
|
-
def create
|
72
|
-
perform(:post, "/jobs.xml", {}, self.to_hash) do |res|
|
73
|
-
Job.from_hash(res["job"])
|
74
|
-
end
|
75
|
-
|
76
|
-
# res = self.class.post("/jobs", :body => self.to_hash, :query => { :api_key => VWorkApp.api_key })
|
77
|
-
# if res.success?
|
78
|
-
# self.id = res["job"]["id"]
|
79
|
-
# self
|
80
|
-
# else
|
81
|
-
# self.class.bad_response(res)
|
82
|
-
# end
|
83
|
-
end
|
84
|
-
|
85
|
-
def update(id, attributes)
|
86
|
-
res = self.class.post("/jobs", :body => { :job => self.to_hash }, :query => { :api_key => VWorkApp.api_key })
|
87
|
-
res.success? ? res : bad_response(res)
|
88
|
-
end
|
89
|
-
|
90
|
-
def delete(use_third_party_id = false)
|
91
|
-
perform(:delete, "/jobs/#{id}.xml", { :use_third_party_id => use_third_party_id })
|
92
|
-
end
|
93
|
-
|
94
|
-
def self.show(id, use_third_party_id = false)
|
95
|
-
perform(:get, "/jobs/#{id}.xml", :use_third_party_id => use_third_party_id) do |res|
|
96
|
-
Job.from_hash(res["job"])
|
97
|
-
end
|
98
|
-
# raw = get("/jobs/#{id}.xml", :query => { :api_key => VWorkApp.api_key, :use_third_party_id => use_third_party_id })
|
99
|
-
# case res
|
100
|
-
# when Net::HTTPOK
|
101
|
-
# Job.from_hash(raw["job"])
|
102
|
-
# when Net::HTTPNotFound
|
103
|
-
# nil
|
104
|
-
# else
|
105
|
-
# bad_response(res)
|
106
|
-
# end
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.find(options={})
|
110
|
-
third_party_id = options.delete(:third_party_id)
|
111
|
-
options[:search] = "@third_party_id=#{third_party_id.to_s}" if (third_party_id)
|
112
|
-
options[:api_key] = VWorkApp.api_key
|
113
|
-
|
114
|
-
raw = get("/jobs.xml", :query => options)
|
115
|
-
raw["jobs"].map { |h| Job.from_hash(h) }
|
13
|
+
def customer_validation
|
14
|
+
return true if @customer_id || @customer_name
|
15
|
+
error_str = "- Either customer_name OR customer_id must be present"
|
16
|
+
self.errors.add :customer_name, error_str
|
17
|
+
self.errors.add :customer_id, error_str
|
116
18
|
end
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
class Step
|
121
|
-
include AttributeEquality
|
122
|
-
attr_accessor :name, :location
|
123
19
|
|
124
|
-
def
|
125
|
-
|
126
|
-
@
|
20
|
+
def customer
|
21
|
+
return nil if @customer_id.nil?
|
22
|
+
@customer ||= Customer.show(@customer_id)
|
127
23
|
end
|
128
24
|
|
129
|
-
def
|
130
|
-
|
131
|
-
|
132
|
-
h
|
133
|
-
end
|
134
|
-
|
135
|
-
def self.from_hash(attributes)
|
136
|
-
Step.new(attributes["name"], Location.from_hash(attributes["location"]))
|
25
|
+
def worker
|
26
|
+
return nil if @worker_id.nil?
|
27
|
+
@worker ||= Worker.show(@worker_id)
|
137
28
|
end
|
138
29
|
|
139
|
-
|
140
|
-
|
30
|
+
# XXX Published at also needs to be set.
|
31
|
+
def planned_start_at=(datetime)
|
32
|
+
self.published_at = datetime
|
33
|
+
@planned_start_at = datetime
|
141
34
|
end
|
142
|
-
|
143
|
-
end
|
144
|
-
|
145
|
-
class CustomField
|
146
|
-
include AttributeEquality
|
147
|
-
attr_accessor :name, :value
|
148
35
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
36
|
+
# XXX Work around for API bug that doesn't accept a nil contact detail.
|
37
|
+
def to_xml(options = {})
|
38
|
+
except = options[:except] || []
|
39
|
+
except << "custom_fields" unless custom_fields
|
40
|
+
except << "customer_id" unless customer_id && customer_name.nil?
|
153
41
|
|
154
|
-
|
155
|
-
{ :name => name.to_s, :value => value.to_s }
|
156
|
-
end
|
157
|
-
|
158
|
-
def self.from_hash(attributes)
|
159
|
-
CustomField.new(attributes["name"], attributes["value"])
|
42
|
+
super(:except => except)
|
160
43
|
end
|
161
44
|
|
162
45
|
def ==(other)
|
163
|
-
attributes_eql?(other,
|
46
|
+
attributes_eql?(other, :id, :third_party_id, :customer_name, :template_name, :planned_duration, :planned_start_at,
|
47
|
+
:worker_id, :steps, :custom_fields)
|
164
48
|
end
|
165
49
|
|
166
50
|
end
|
167
|
-
|
168
51
|
end
|