vworkapp_ruby 0.1.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- vworkapp_ruby (0.0.1)
4
+ vworkapp_ruby (0.1.0)
5
5
  gcoder
6
6
  httparty
7
7
 
data/README.md CHANGED
@@ -1,18 +1,85 @@
1
- # vWorkApp
1
+ # vWorkApp API's Ruby Wrapper
2
2
 
3
- This is a wrapper around vWorkApp's API. Go on! Use it!
3
+ This is a ruby wrapper around vWorkApp's developer API. You can use it to create, find, update, and delete vWorkApp's jobs, customers, and users from your account.
4
4
 
5
- VWorkApp::Job.api_key = "MY_API_KEY"
6
- jobs = VWorkApp::Job.find(:worker_id => 1971, :state => "not_started")
5
+ Although vWorkApp's API can be accessed directly through HTTP+XML this wrapper makes it easier and quicker for ruby developers to dive into using vWorkApp's API.
7
6
 
8
- ## Todo
7
+ ## Installation
9
8
 
10
- - Handle paging
9
+ gem install vworkapp_ruby
11
10
 
12
- - Broken API bits:
13
- * What's find(:search) for??.
14
- * Why can't I filter by template?
15
-
16
- - Missing job.actual_duration, job.actual_start_at
11
+ Or if you're using a Gemfile, include the following line:
17
12
 
18
- - Should have connivence method to lookup a customer from a job
13
+ gem 'vworkapp_ruby'
14
+
15
+ ## Usage
16
+
17
+ Before you use the API you need to specify your API Key. If you don't have an API Key then contact support@vworkapp.com to be given one.
18
+
19
+ VW.api_key = "MY_API_KEY"
20
+
21
+ You can create a job in vWorkApp:
22
+
23
+ job = VW::Job.new(
24
+ :customer_name => "ACME Baking",
25
+ :template_name => "Standard Booking",
26
+ :planned_duration => 10 * MIN,
27
+ :steps => [
28
+ {:name => "Start", :location => {:formatted_address => "880 Harrison St, SF, USA", :lat => 37.779536, :lng => -122.401503}},
29
+ {:name => "End", :location => VW::Location.from_address("201 1st Street, SF", :us).attributes}
30
+ ],
31
+ :custom_fields => [
32
+ :name => "Note", :value => "Hi There!"
33
+ ]
34
+ )
35
+ job.create
36
+
37
+ Search for jobs:
38
+
39
+ jobs = VW::Job.find(:state => "not_started")
40
+ jobs.each { |job| puts job.customer_name }
41
+
42
+ Update jobs:
43
+
44
+ job = VW::Job.find(101) # assuming a job with this ID exists already
45
+ job.steps.first.location = VW::Location.from_address("101 1st Street, SF", :us)
46
+ job.update
47
+
48
+ And delete them:
49
+
50
+ job = VW::Job.find(101) # assuming a job with this ID exists already
51
+ job.delete
52
+
53
+ Full documentation on the API can be found [here](http://api.vworkapp.com/api/).
54
+
55
+ ## Future Work / Improvements
56
+
57
+ - The wrapper doesn't handle paging of search results yet.
58
+
59
+ - Proof-of-delivery needs to be added.
60
+
61
+ - Base classes could be refactored out into their own gem and made a little more generic.
62
+
63
+ ## License
64
+
65
+ This API wrapper is published under the MIT License.
66
+
67
+ Copyright (C) 2012 vWorkApp Inc.
68
+
69
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
70
+ this software and associated documentation files (the "Software"), to deal in
71
+ the Software without restriction, including without limitation the rights to
72
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
73
+ of the Software, and to permit persons to whom the Software is furnished to do
74
+ so, subject to the following conditions:
75
+
76
+ The above copyright notice and this permission notice shall be included in all
77
+ copies or substantial portions of the Software.
78
+
79
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
80
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
81
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
82
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
83
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
84
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
85
+ SOFTWARE.
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
2
+
3
+ # -------------
4
+ # Example 1: Create a job
5
+ # -------------
6
+
7
+ require "rubygems"
8
+ require "vworkapp_ruby"
9
+
10
+ MIN = 60
11
+
12
+ VWorkApp.api_key = "MY_API_KEY"
13
+
14
+ puts "vWorkApp - Create Job"
15
+ puts "---------------------"
16
+
17
+ job = VW::Job.new(
18
+ :customer_name => "ACME Baking",
19
+ :template_name => "Standard Booking",
20
+ :planned_duration => 10 * MIN,
21
+ :steps => [
22
+ {:name => "Start", :location => {:formatted_address => "880 Harrison St, SF, USA", :lat => 37.779536, :lng => -122.401503}},
23
+ {:name => "End", :location => VW::Location.from_address("201 1st Street, SF", :us).attributes}
24
+ ],
25
+ :custom_fields => [
26
+ :name => "Note", :value => "Hi There!"
27
+ ]
28
+ )
29
+
30
+ job = job.create
31
+
32
+ puts "Job Created!. Check out the job in vWorkApp with id: #{job.id}"
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
2
+
3
+ # -------------
4
+ # Example 2: Print each worker's jobs
5
+ # -------------
6
+ require "rubygems"
7
+ require "vworkapp_ruby"
8
+
9
+ VW.api_key = "MY_API_KEY"
10
+
11
+ puts "vWorkApp - List of jobs"
12
+ puts "----------------------------"
13
+
14
+ workers = VW::Worker.find
15
+
16
+ workers.each do |worker|
17
+ puts "Worker: #{worker.name}"
18
+ jobs = VW::Job.find(:worker_id => worker.id, :state => "not_started")
19
+ jobs.each do |job|
20
+ puts "\t #{job.third_party_id || job.id}, #{job.customer_name}, #{job.template_name}"
21
+ end
22
+ puts "----------------------------"
23
+ end
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'vworkapp_ruby'
4
+ gem 'sinatra'
5
+ gem 'haml'
@@ -0,0 +1,35 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ gcoder (0.12.1)
5
+ hashie
6
+ ruby-hmac
7
+ yajl-ruby
8
+ haml (3.1.4)
9
+ hashie (1.2.0)
10
+ httparty (0.8.1)
11
+ multi_json
12
+ multi_xml
13
+ multi_json (1.0.4)
14
+ multi_xml (0.4.1)
15
+ rack (1.4.0)
16
+ rack-protection (1.2.0)
17
+ rack
18
+ ruby-hmac (0.4.0)
19
+ sinatra (1.3.2)
20
+ rack (~> 1.3, >= 1.3.6)
21
+ rack-protection (~> 1.2)
22
+ tilt (~> 1.3, >= 1.3.3)
23
+ tilt (1.3.3)
24
+ vworkapp_ruby (0.1.0)
25
+ gcoder
26
+ httparty
27
+ yajl-ruby (1.1.0)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ haml
34
+ sinatra
35
+ vworkapp_ruby
@@ -0,0 +1,26 @@
1
+ # Example 3: Worker Map
2
+
3
+ This example creates a web server on your local computer that shows the location of each vWorkApp worker on a map, along with
4
+
5
+ ## Installation and Usage
6
+
7
+ If you aren't already using bundler, install it here:
8
+
9
+ sudo gem install bundler
10
+
11
+ Open <code>server.rb</code> and replace MY\_API\_KEY with your own vWorkApp API Key.
12
+
13
+ VW.api_key = "MY_API_KEY"
14
+
15
+ And open <code>/views/index.haml</code> and replace MY\_GOOGLE\_API\_KEY with your own Google API Key. If you don't have one it can be obtained here: http://code.google.com/apis/maps/signup.html
16
+
17
+ %script{:src => "http://maps.googleapis.com/maps/api/js?key=MY_GOOGLE_API_KEY&sensor=false", :type => "text/javascript"}
18
+
19
+ Then run:
20
+
21
+ bundle install
22
+ ruby server
23
+
24
+ Finally, open your web browser and go to:
25
+
26
+ http://localhost:4567
@@ -0,0 +1,25 @@
1
+ html { height: 100% }
2
+
3
+ body { height: 100%; margin: 0; padding: 0 }
4
+
5
+ #map_canvas { height: 100% }
6
+
7
+ .job {
8
+ font: 12px arial,sans-serif;
9
+ color: #333;
10
+ padding: 10px 0px;
11
+ border-bottom: dashed 1px #ddd;
12
+ }
13
+
14
+ h1.infobox {
15
+ font: 11px arial,sans-serif;
16
+ font-weight: bold;
17
+ color: #999;
18
+ margin: 0px;
19
+ border-bottom: solid 1px #ddd;
20
+ }
21
+
22
+ .job .time {
23
+ color: #e22;
24
+ padding-right: 10px;
25
+ }
Binary file
@@ -0,0 +1,41 @@
1
+ require "rubygems"
2
+ require "sinatra"
3
+ require "vworkapp_ruby"
4
+ require "json"
5
+ require "haml"
6
+
7
+ VW.api_key = "MY_API_KEY"
8
+
9
+ get '/' do
10
+ @workers = VW::Worker.find
11
+ haml :index
12
+ end
13
+
14
+ helpers do
15
+
16
+ def job_json(workers)
17
+ data = @workers.map do |w|
18
+ next unless w.latest_telemetry
19
+
20
+ jobs = VW::Job.find(:worker_id => w.id, :state => "started")
21
+
22
+ jobs_html = jobs.inject("<h1 class='infobox'>ACTIVE JOBS:</h1>") do |str, job|
23
+ str << <<-EOL
24
+ <div class="job">
25
+ <span class="time">#{job.planned_start_at.strftime("%H:%M")}</span> #{job.customer_name}, #{job.template_name}
26
+ </div>
27
+ EOL
28
+ end
29
+
30
+ {
31
+ :lat => w.latest_telemetry.lat,
32
+ :lng => w.latest_telemetry.lng,
33
+ :name => w.name,
34
+ :jobs => jobs_html
35
+ }
36
+ end
37
+
38
+ data.to_json
39
+ end
40
+
41
+ end
@@ -0,0 +1,47 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %meta{:content => "initial-scale=1.0, user-scalable=no", :name => "viewport"}/
5
+
6
+ %link{:rel => "StyleSheet", :href => "/style.css", :type => "text/css" }
7
+
8
+ %script{:src => "http://maps.googleapis.com/maps/api/js?key=MY_GOOGLE_API_KEY&sensor=false", :type => "text/javascript"}
9
+
10
+ :javascript
11
+ infoWindow = new google.maps.InfoWindow();
12
+
13
+ function makeMarker(data, map) {
14
+ var marker = new google.maps.Marker({
15
+ position: new google.maps.LatLng(data.lat, data.lng),
16
+ map: map,
17
+ icon: 'truck.png'
18
+ });
19
+
20
+ google.maps.event.addListener(marker, 'click', function() {
21
+ infoWindow.close();
22
+ infoWindow.setContent(data.jobs)
23
+ infoWindow.open(map, marker);
24
+ });
25
+ }
26
+
27
+ function initialize() {
28
+
29
+ var myOptions = {
30
+ center: new google.maps.LatLng(37.779536,-122.401503),
31
+ zoom: 12,
32
+ mapTypeId: google.maps.MapTypeId.ROADMAP
33
+ };
34
+
35
+ var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
36
+
37
+ workers = #{job_json(@workers)}
38
+
39
+ for (var i = 0; i < workers.length; i++) {
40
+ if (workers[i] != null)
41
+ makeMarker(workers[i], map);
42
+ }
43
+
44
+ }
45
+
46
+ %body{:onload => "initialize()"}
47
+ #map_canvas{:style => "width:100%; height:100%"}
data/lib/vworkapp_ruby.rb CHANGED
@@ -1,11 +1,21 @@
1
- require 'vworkapp_ruby/base'
1
+ require 'httparty'
2
+ require 'active_support/core_ext/hash'
3
+ require 'active_model'
4
+
5
+ require 'vworkapp_ruby/base/base'
6
+ require 'vworkapp_ruby/base/resource'
7
+ require 'vworkapp_ruby/base/response_error'
8
+
2
9
  require 'vworkapp_ruby/location'
10
+ require 'vworkapp_ruby/step'
11
+ require 'vworkapp_ruby/contact'
12
+ require 'vworkapp_ruby/telemetry'
13
+ require 'vworkapp_ruby/custom_field'
3
14
  require 'vworkapp_ruby/job'
4
15
  require 'vworkapp_ruby/worker'
5
16
  require 'vworkapp_ruby/customer'
6
- require 'vworkapp_ruby/response_error'
7
17
  # require 'vworkapp/proof_of_delivery'
8
18
 
9
- require 'vworkapp_ruby/httparty_monkey_patch'
19
+ require 'vworkapp_ruby/base/httparty_monkey_patch'
10
20
 
11
21
  VW = VWorkApp
@@ -0,0 +1,173 @@
1
+ require 'set'
2
+
3
+ module VWorkApp
4
+ class Base
5
+ include ActiveModel::Serializers::JSON
6
+ include ActiveModel::Serializers::Xml
7
+ include ActiveModel::Validations
8
+
9
+ def initialize(attributes = {})
10
+ self.attributes = attributes
11
+ end
12
+
13
+ # -----------------
14
+ # Hattr Methods
15
+ # ----------------
16
+
17
+ def self.hattr_reader(*hattrs)
18
+ hattrs.each do |hattr|
19
+ self.read_hattrs << hattr
20
+ attr_reader(hattr.is_a?(Hash) ? hattr.keys.first : hattr)
21
+ end
22
+ end
23
+
24
+ def self.hattr_writer(*hattrs)
25
+ hattrs.each do |hattr|
26
+ self.write_hattrs << hattr
27
+ attr_writer(hattr.is_a?(Hash) ? hattr.keys.first : hattr)
28
+ end
29
+ end
30
+
31
+ def self.hattr_accessor(*hattrs)
32
+ hattr_reader(*hattrs)
33
+ hattr_writer(*hattrs)
34
+ end
35
+
36
+ def self.write_hattrs
37
+ @write_hattrs ||= Set.new
38
+ end
39
+ def write_hattrs
40
+ self.class.write_hattrs
41
+ end
42
+
43
+ def self.read_hattrs
44
+ @read_hattrs ||= Set.new
45
+ end
46
+ def read_hattrs
47
+ self.class.read_hattrs
48
+ end
49
+
50
+ def self.hattrs
51
+ read_hattrs + write_hattrs
52
+ end
53
+ def hattrs
54
+ self.class.hattrs
55
+ end
56
+
57
+ # -------------------
58
+ # Attributes Methods
59
+ # -------------------
60
+
61
+ def attributes
62
+ extract_attributes(hattrs)
63
+ end
64
+
65
+ def read_attributes
66
+ extract_attributes(read_hattrs)
67
+ end
68
+
69
+ def write_attributes
70
+ extract_attributes(write_hattrs)
71
+ end
72
+
73
+ def attributes=(hash)
74
+ inject_attributes(hattrs, hash)
75
+ end
76
+
77
+ def write_attributes=(hash)
78
+ inject_attributes(write_hattrs, hash)
79
+ end
80
+
81
+ def read_attributes=(hash)
82
+ inject_attributes(read_hattrs, hash)
83
+ end
84
+
85
+ # -----------------
86
+ # Misc Methods
87
+ # -----------------
88
+
89
+ def validate_and_raise
90
+ raise Exception.new(self.errors.full_messages.join("\n")) unless valid?
91
+ end
92
+
93
+ def attributes_eql?(other, *test_attrs)
94
+ test_attrs.each do |attribute|
95
+ return false unless self.send(attribute.to_sym) == other.send(attribute.to_sym)
96
+ end
97
+ true
98
+ end
99
+
100
+ private
101
+
102
+ def extract_attributes(hattrs)
103
+ attr = hattrs.map do |hattr|
104
+ hattr = hattr.keys.first if hattr.is_a?(Hash)
105
+ [hattr.to_s, send(hattr)]
106
+ end
107
+ Hash[attr]
108
+ end
109
+
110
+ def inject_attributes(hattrs, hash)
111
+ hash = Hash[hash.map { |k,v| [k.to_sym, v] }]
112
+
113
+ hattrs.each do |hattr|
114
+ value = case hattr
115
+ when Hash
116
+ hattr, klass = hattr.first
117
+ next unless hash[hattr.to_sym]
118
+ klass.is_a?(Array) ? hash[hattr.to_sym].map {|h| klass.first.new(h)} : klass.new(hash[hattr.to_sym])
119
+ else
120
+ next unless hash[hattr.to_sym]
121
+ hash[hattr.to_sym]
122
+ end
123
+
124
+ set_hattr(hattr, value)
125
+ end
126
+ end
127
+
128
+ def set_hattr(hattr, value)
129
+ write_hattrs.include?(hattr) ? send("#{hattr}=", value) : self.instance_variable_set("@#{hattr.to_s}", value)
130
+ end
131
+
132
+ end
133
+ end
134
+
135
+ # -----------------
136
+ # AM Monkey Patches. Yuk.
137
+ # -----------------
138
+
139
+ module ActiveModel::Serialization
140
+ def serializable_hash(options = nil)
141
+ options ||= {}
142
+
143
+ only = Array.wrap(options[:only]).map(&:to_s)
144
+ except = Array.wrap(options[:except]).map(&:to_s)
145
+
146
+ # AF: Changed to write_attributes
147
+ attribute_names = write_attributes.keys.sort
148
+ if only.any?
149
+ attribute_names &= only
150
+ elsif except.any?
151
+ attribute_names -= except
152
+ end
153
+
154
+ method_names = Array.wrap(options[:methods]).map { |n| n if respond_to?(n.to_s) }.compact
155
+ Hash[(attribute_names + method_names).map { |n| [n, send(n)] }]
156
+ end
157
+ end
158
+
159
+ module ActiveModel::Serializers::Xml
160
+ class Serializer
161
+ def attributes_hash
162
+ # AF: Changed to write_attributes
163
+ attributes = @serializable.write_attributes
164
+ if options[:only].any?
165
+ attributes.slice(*options[:only])
166
+ elsif options[:except].any?
167
+ attributes.except(*options[:except])
168
+ else
169
+ attributes
170
+ end
171
+ end
172
+ end
173
+ end