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.
Files changed (37) hide show
  1. data/Gemfile.lock +1 -1
  2. data/README.md +79 -12
  3. data/examples/example_1.rb +32 -0
  4. data/examples/example_2.rb +23 -0
  5. data/examples/example_3/Gemfile +5 -0
  6. data/examples/example_3/Gemfile.lock +35 -0
  7. data/examples/example_3/README.md +26 -0
  8. data/examples/example_3/public/style.css +25 -0
  9. data/examples/example_3/public/truck.png +0 -0
  10. data/examples/example_3/server.rb +41 -0
  11. data/examples/example_3/views/index.haml +47 -0
  12. data/lib/vworkapp_ruby.rb +13 -3
  13. data/lib/vworkapp_ruby/base/base.rb +173 -0
  14. data/lib/vworkapp_ruby/{httparty_monkey_patch.rb → base/httparty_monkey_patch.rb} +2 -4
  15. data/lib/vworkapp_ruby/base/resource.rb +105 -0
  16. data/lib/vworkapp_ruby/base/response_error.rb +26 -0
  17. data/lib/vworkapp_ruby/contact.rb +11 -0
  18. data/lib/vworkapp_ruby/custom_field.rb +11 -0
  19. data/lib/vworkapp_ruby/customer.rb +9 -97
  20. data/lib/vworkapp_ruby/job.rb +29 -146
  21. data/lib/vworkapp_ruby/location.rb +6 -23
  22. data/lib/vworkapp_ruby/step.rb +11 -0
  23. data/lib/vworkapp_ruby/telemetry.rb +6 -0
  24. data/lib/vworkapp_ruby/worker.rb +4 -81
  25. data/spec/base_spec.rb +174 -0
  26. data/spec/customer_spec.rb +30 -46
  27. data/spec/job_spec.rb +189 -143
  28. data/spec/resource_spec.rb +37 -0
  29. data/spec/support/active_model_lint.rb +19 -0
  30. data/spec/worker_spec.rb +6 -25
  31. data/vworkapp_ruby-0.1.0.gem +0 -0
  32. data/vworkapp_ruby.gemspec +5 -4
  33. metadata +46 -16
  34. data/examples/create_job.rb +0 -34
  35. data/examples/print_jobs.rb +0 -31
  36. data/lib/vworkapp_ruby/base.rb +0 -72
  37. 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
@@ -0,0 +1,11 @@
1
+ module VWorkApp
2
+ class CustomField < Base
3
+ hattr_accessor :name, :value
4
+ self.include_root_in_json = false
5
+
6
+ def ==(other)
7
+ attributes_eql?(other, :name, :value)
8
+ end
9
+
10
+ end
11
+ end
@@ -1,106 +1,18 @@
1
1
  module VWorkApp
2
-
3
- class Customer < Base
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, [:id, :name, :third_party_id, :site_contact, :billing_contact])
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
- def initialize(first_name = nil, last_name = nil, phone = nil, mobile = nil, email = nil, location = nil)
74
- @first_name = first_name
75
- @last_name = last_name
76
- @phone = phone
77
- @mobile = mobile
78
- @email = email
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
@@ -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
- class Job < Base
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
- # Object Methods
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 is_new?
52
- self.id == nil
53
- end
54
-
55
- def assigned_to
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 initialize(name, location = nil)
125
- @name = name
126
- @location = location
20
+ def customer
21
+ return nil if @customer_id.nil?
22
+ @customer ||= Customer.show(@customer_id)
127
23
  end
128
24
 
129
- def to_hash
130
- h = { :name => name }
131
- h.merge!({ :location => location.to_hash }) if location
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
- def ==(other)
140
- attributes_eql?(other, [:name, :location])
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
- def initialize(name, value)
150
- @name = name
151
- @value = value
152
- end
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
- def to_hash
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, [:name, :value])
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