projector_pws 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 45d785b1f6f1ff2701b369ef782874d78c1205c2
4
+ data.tar.gz: d09550953bff128a3ab30daf083a5e91af78ac01
5
+ SHA512:
6
+ metadata.gz: dd771285d83de784fecd46c54ee9ba749c53a899b9ac097c4d285a45a3b3c45b01c33a0538dad3e0d8c6af99f919b5348c7c04f35d71dce93e9ba594103554ee
7
+ data.tar.gz: 7e414dfbaaab808c0a984b1c4e76c144e616348a6a0e5b10f729c9c3db95e54a7f745ae0f80eca76246c2448bd39b7651ab6cf36efe953e024c38017fb97fc00
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .idea
2
+ Gemfile.lock
3
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Paul Duncan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # ProjectorPWS
2
+
3
+ Client library for accessing Projector PSA\'s Web Services (PWS)
4
+
5
+ ## Presentation
6
+
7
+ This library provides access to Projector PSA\'s Web Services (PWS).
8
+
9
+ ## Installation
10
+
11
+ ### Gemfile
12
+ ```ruby
13
+ gem 'projector_pws'
14
+ ```
15
+
16
+ ### Terminal
17
+ ```bash
18
+ gem install -V projector_pws
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Symbolize Keys
24
+
25
+ A _sym_keys_ method is monkey-patched into the *Array* and *Hash* classes, and provides a way to recursively convert *keys* to *symbols*.
26
+ This is mostly useful when loading Hashes from external resources (YAML, CSV, JSON, etc...), where keys will be stored as strings.
27
+
28
+ ```ruby
29
+ a = { 'foo' => 'bar', 'nested' => [{ 'problems' => 'none' }] }
30
+ a.sym_keys
31
+ # => {:foo=>"bar", :nested=>[{:problems=>"none"}]}
32
+ ```
33
+
34
+ ### Deep-clone
35
+
36
+ A _dclone_ method is monkey-patched into the *Array* and *Hash* classes, and provides a way to recursively *deep-clone* an instance.
37
+
38
+ ```ruby
39
+ a = { foo: 'bar', bar: :kill_me, nested: [{ problems: 'none' }] }
40
+ b = a.dclone
41
+ b[:nested] << :foo
42
+ b[:nested][:problems] = 'no way'
43
+ b[:test] = :bork
44
+ b[:foo] = nil
45
+ b.delete :bar
46
+ a
47
+ # => {:foo=>nil, :nested=>[{:problems=>"no way"}, :foo], :test => :bork}
48
+ b
49
+ # => {:foo=>"bar", :nested=>[{:problems=>"none"}]}
50
+ ```
51
+
52
+ ### Right/Left Padding
53
+
54
+ Two padding methods _rpad_ and _lpad_ are monkey-patched into the *String* class, and provide a way to pad any string to a given length using a given filler string.
55
+
56
+ ```ruby
57
+ a = 'foo'
58
+ a.rpad 10
59
+ # => "foo "
60
+ a.rpad 10, '-'
61
+ # => "foo-------"
62
+ a.rpad 2, '-'
63
+ # => "fo"
64
+ a.lpad 10
65
+ # => " foo"
66
+ a.lpad 10, '-'
67
+ # => "-------foo"
68
+ a.rpad 2, '-'
69
+ # => "oo"
70
+ ```
71
+
72
+ ### Object Try (Undefined Method Call)
73
+
74
+ A _try_ method is monkey-patched into the *Object* root class, providing a means to call undefined methods on objects without raising anything (returning *nil* instead).
75
+
76
+ ```ruby
77
+ 'foobar'.try :foo
78
+ # => nil
79
+ 32.try :to_s
80
+ # => '32'
81
+ 32.try :+, 3
82
+ # => 35
83
+ ['foo', 'bar'].try(:collect) { |e| "-#{e}-" }
84
+ # => ['-foo-', '-bar-']
85
+ ```
86
+
87
+ ### Get Non-Empty String
88
+
89
+ A _nstr_ method is monkey-patched into the *String* class. This method returns nil when the String is empty, the String otherwise.
90
+
91
+ ```ruby
92
+ ''.nstr
93
+ # => nil
94
+ 'foo'.nstr
95
+ # => 'foo'
96
+ nil.try :nstr
97
+ # => nil
98
+ 'bar'.try :nstr
99
+ # => 'bar'
100
+ ```
101
+
102
+ ### String Case Conversions
103
+
104
+ Case-conversion methods are monkey-patched into the *String* class. The following methods are made available:
105
+ * camelcase ('foo-bar' => 'FooBar')
106
+ * snakecase ('FooBar' => 'foo-bar')
107
+ * kebabcase ('FooBar' => 'foo_bar')
108
+
109
+ ```ruby
110
+ 'foo_bar'.camelcase
111
+ # => 'FooBar'
112
+ 'foo-bar'.camelcase
113
+ # => 'FooBar'
114
+
115
+ 'FooBar'.snakecase
116
+ # => 'foo-bar'
117
+ 'foo_bar'.snakecase
118
+ # => 'foo-bar'
119
+
120
+ 'FooBar'.kebabcase
121
+ # => 'foo_bar'
122
+ 'foo-bar'.kebabcase
123
+ # => 'foo_bar'
124
+ ```
125
+
126
+ ### Module utilities
127
+
128
+ #### Get Module Name
129
+
130
+ A _mod_name_ method is monkey-patched into the *Module* class. This allows obtaining the name of any module, without its hierarchical tree.
131
+
132
+ ```ruby
133
+ module Foo
134
+ module Bar
135
+ end
136
+ end
137
+
138
+ Foo.name
139
+ # => 'Foo'
140
+
141
+ Foo.mod_name
142
+ # => 'Foo'
143
+
144
+ Foo::Bar.name
145
+ # => 'Foo::Bar'
146
+
147
+ Foo::Bar.mod_name # <= This is where it gets interesting
148
+ # => 'Bar'
149
+ ```
150
+
151
+ #### Get Module Parent Name
152
+
153
+ A _mod_parent_name_ method is monkey-patched into the *Module* class. This allows obtaining the name of any module's parent.
154
+
155
+ ```ruby
156
+ module Foo
157
+ module Bar
158
+ module Test
159
+ end
160
+ end
161
+ end
162
+
163
+ Foo.mod_parent_name
164
+ # => ''
165
+
166
+ Foo::Bar.mod_parent_name
167
+ # => 'Foo'
168
+
169
+ Foo::Bar::Test.mod_parent_name
170
+ # => 'Foo::Bar'
171
+ ```
172
+
173
+ #### Get Module Parent
174
+
175
+ A _mod_parent_ method is monkey-patched into the *Module* class. This allows obtaining any module's parent.
176
+
177
+ ```ruby
178
+ module Foo
179
+ module Bar
180
+ module Test
181
+ end
182
+ end
183
+ end
184
+
185
+ Foo.mod_parent
186
+ # => nil
187
+
188
+ Foo::Bar.mod_parent
189
+ # => Foo
190
+
191
+ Foo::Bar::Test.mod_parent_name
192
+ # => Foo::Bar
193
+ ```
194
+
195
+ ### Time Duration
196
+
197
+ A _duration_ method is monkey-patched into the 'Numeric' class. This method returns a textual string representation of the time duration.
198
+ An optional *elements* argument allows setting the maximum number of time-elements (days, hours, minutes, ...) to display.
199
+
200
+ ```ruby
201
+ 86400.duration
202
+ # => '1 Day'
203
+ ((3600 * 3) + 60 + 12).duration
204
+ # => '3 Hours 1 Minute 12 Seconds'
205
+ ((3600 * 3) + 60 + 12).duration 1
206
+ # => '3 Hours'
207
+ ((3600 * 3) + 60 + 12).duration 2
208
+ # => '3 Hours 1 Minute'
209
+ ```
210
+
211
+ ## License
212
+
213
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+ require 'bundler/gem_tasks'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = 'test/**/test*.rb'
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,9 @@
1
+ # ProjectorPWS
2
+ # by Eresse <eresse@eresse.net>
3
+
4
+ # ProjectorPWS Module
5
+ module ProjectorPWS
6
+
7
+ # Version
8
+ VERSION = '0.1.0'
9
+ end
@@ -0,0 +1,259 @@
1
+ # ProjectorPWS
2
+ # by Eresse <eresse@eresse.net>
3
+
4
+ # External Includes
5
+ require 'savon'
6
+ require 'aromat'
7
+
8
+ # Internal Includes
9
+ require 'projector_pws/version'
10
+
11
+ # ProjectorPWS Module:
12
+ # Root Module for ProjectorPWS.
13
+ module ProjectorPWS
14
+
15
+ # Service Definition
16
+ WSDL = 'https://secure.projectorpsa.com/OpsProjectorWcfSvc/PwsProjectorServices.svc?wsdl'
17
+
18
+ # Base URL
19
+ BASE_URL = 'https://secure.projectorpsa.com'
20
+
21
+ # URL Path
22
+ URL_PATH = 'OpsProjectorWcfSvc/PwsProjectorServices.svc'
23
+
24
+ # Namespaces
25
+ NAMESPACE = 'http://projectorpsa.com/PwsProjectorServices/'
26
+ EXTRA_NAMESPACES = {
27
+ 'xmlns:req' => 'http://projectorpsa.com/DataContracts/Requests/',
28
+ 'xmlns:tim' => 'http://projectorpsa.com/DataContracts/Shared/TimeAndCost/',
29
+ 'xmlns:com' => 'http://projectorpsa.com/DataContracts/Shared/Common/'
30
+ }
31
+
32
+ # Work Week
33
+ WEEK_START = :monday
34
+ WEEK_END = :friday
35
+
36
+ # Days Per Week
37
+ DAYS_PER_WEEK = 5
38
+
39
+ # Hours Per Day
40
+ HOURS_PER_DAY = 8
41
+
42
+ # Real Hours Per Day
43
+ HOURS_PER_DAY_REAL = 24
44
+
45
+ # Minutes Per Hour
46
+ MINUTES_PER_HOUR = 60
47
+
48
+ # Minutes Per Day
49
+ MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR
50
+
51
+ # Minutes Per Week
52
+ MINUTES_PER_WEEK = MINUTES_PER_DAY * DAYS_PER_WEEK
53
+
54
+ # Open Client
55
+ def self.open url = BASE_URL
56
+ c = Savon.client wsdl: WSDL, endpoint: service_url(url), namespace: NAMESPACE, env_namespace: :soapenv, namespace_identifier: :pws, namespaces: EXTRA_NAMESPACES
57
+ return yield c if block_given?
58
+ c
59
+ end
60
+
61
+ # Authenticate
62
+ def self.authenticate username, password, account_code = nil
63
+
64
+ # Prepare URLs
65
+ next_url = BASE_URL
66
+ last_url = ''
67
+
68
+ # Prepare Credentials
69
+ creds = {}
70
+ creds['req:AccountCode'] = account_code if account_code
71
+ creds['req:Password'] = password
72
+ creds['req:UserName'] = username
73
+
74
+ # Loop to obtain Ticket
75
+ c = nil
76
+ ticket = nil
77
+ until ticket
78
+
79
+ # Authenticate
80
+ begin
81
+ c = open next_url
82
+ r = c.call(:pws_authenticate, message: { 'pws:serviceRequest' => creds }).body[:pws_authenticate_response][:pws_authenticate_result]
83
+ last_url = next_url
84
+ next_url = r[:redirect_url]
85
+ rescue Savon::SOAPFault => e
86
+ raise "Authentication failed - #{e.to_hash[:fault][:detail][:pws_fault][:messages][:pws_message][:error_text]}"
87
+ end
88
+
89
+ raise 'API Error (redirect loop)' if next_url == last_url
90
+ ticket = r[:session_ticket]
91
+ end
92
+
93
+ # Yield if block given
94
+ if block_given?
95
+ r = yield c, ticket
96
+ unauthenticate c, ticket
97
+ return r
98
+ end
99
+
100
+ [c, ticket]
101
+ end
102
+
103
+ # Unauthenticate
104
+ def self.unauthenticate c, ticket
105
+
106
+ # Unauthenticate
107
+ c.call(:pws_unauthenticate, message: { 'pws:serviceRequest' => { 'req:SessionTicket' => ticket } }).body
108
+ end
109
+
110
+ # Get Resources
111
+ def self.get_resources c, ticket
112
+
113
+ # Fetch Resources
114
+ c.call(:pws_get_resource_list, message: { 'pws:serviceRequest' => { 'req:SessionTicket' => ticket } }).body[:pws_get_resource_list_response][:pws_get_resource_list_result][:resources][:pws_resource_summary]
115
+ end
116
+
117
+ # Get Resource Schedule
118
+ def self.get_resource_schedule c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end
119
+
120
+ # Prepare Params
121
+ params = { 'req:SessionTicket' => ticket }
122
+ params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
123
+ params['tim:IncludeScheduledTimeFlag'] = :true
124
+ params['tim:IncludeTimeOffFlag'] = :true
125
+ params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid }
126
+ params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'
127
+
128
+ # Fetch Resource Schedule
129
+ c.call(:pws_get_resource_schedule, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_schedule_response][:pws_get_resource_schedule_result][:resource_schedule]
130
+ end
131
+
132
+ # Get Resource Working Schedule
133
+ def self.get_resource_working_schedule c, ticket, resource_uid, start_date = current_week_start, end_date = current_week_end
134
+
135
+ # Prepare Params
136
+ params = { 'req:SessionTicket' => ticket }
137
+ params['tim:EndDate'] = end_date.strftime '%Y-%m-%dz'
138
+ params['tim:ResourceIdentity'] = { 'com:ResourceUid' => resource_uid }
139
+ params['tim:StartDate'] = start_date.strftime '%Y-%m-%dz'
140
+
141
+ # Fetch Resource Working Schedule
142
+ c.call(:pws_get_resource_working_schedule, message: { 'pws:serviceRequest' => params }).body[:pws_get_resource_working_schedule_response][:pws_get_resource_working_schedule_result][:working_schedule]
143
+ end
144
+
145
+ # Get Free Resources (non-busy) with associated minutes
146
+ def self.get_free_resources c, ticket, start_date = current_week_start, end_date = current_week_end
147
+
148
+ # Get all resources
149
+ res = ProjectorPWS.get_resources c, ticket
150
+
151
+ # Collect free time
152
+ res.collect { |r| {
153
+ resource: r,
154
+ free_hours: get_resource_free_hours(c, ticket, r, start_date, end_date).to_f
155
+ } }.select { |x| x[:free_hours] > 0 }
156
+ end
157
+
158
+ # Get Resource Free Hours
159
+ def self.get_resource_free_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end
160
+
161
+ # Get scheduled hours
162
+ scheduled_hours = get_resource_scheduled_hours c, ticket, resource, start_date, end_date
163
+
164
+ # Get active hours
165
+ active_hours = get_resource_active_hours c, ticket, resource, start_date, end_date
166
+
167
+ # Compute time off into active hours
168
+ active_hours = active_hours - scheduled_hours[:toff]
169
+
170
+ # Compute free time (active unscheduled time)
171
+ active_hours - scheduled_hours[:work]
172
+ end
173
+
174
+ # Get Resource Active Hours
175
+ def self.get_resource_active_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end
176
+
177
+ # Acquire working schedule for resource
178
+ wsched = ProjectorPWS.get_resource_working_schedule(c, ticket, resource[:resource_uid], start_date, end_date)[:pws_working_schedule_day] rescue(return(0))
179
+ wsched = [wsched] unless wsched.is_a? Array
180
+ wsched.compact!
181
+
182
+ # Extract Working Minutes
183
+ work = wsched.inject(0) { |a, e| a + e[:working_minutes].to_i }
184
+
185
+ # Determine Active Hours
186
+ work.to_f / MINUTES_PER_HOUR.to_f
187
+ end
188
+
189
+ # Get Resource Scheduled Hours
190
+ def self.get_resource_scheduled_hours c, ticket, resource, start_date = current_week_start, end_date = current_week_end
191
+
192
+ # Get schedule
193
+ sched = get_resource_schedule(c, ticket, resource[:resource_uid], start_date, end_date) rescue(return({ work: 0, toff: 0 }))
194
+
195
+ # Extract scheduled work
196
+ sched_work = sched[:roles][:pws_schedule_role] rescue []
197
+ sched_work = [sched_work] unless sched_work.is_a? Array
198
+ sched_work.compact!
199
+
200
+ # Extract scheduled time off
201
+ time_off = sched[:time_off][:pws_schedule_time_off] rescue []
202
+ time_off = [time_off] unless time_off.is_a? Array
203
+ time_off.compact!
204
+
205
+ # Run through time off { collect total minutes }
206
+ time_off_minutes = time_off.inject(0) do |a, e|
207
+
208
+ # Extract Dates
209
+ dates = e[:time_off_dates][:pws_schedule_time_off_date] rescue []
210
+ dates = [dates] unless dates.is_a? Array
211
+ dates.compact!
212
+
213
+ # Collect time off dates
214
+ a + dates.inject(0) { |da, de| da + time_off_minutes(de[:time_off_minutes].to_i) }
215
+ end
216
+
217
+ # Run through schedule { collect total minutes }
218
+ scheduled_minutes = sched_work.inject(0) do |a, e|
219
+
220
+ # Extract bookings
221
+ bookings = e[:bookings][:pws_schedule_booking] rescue []
222
+ bookings = [bookings] unless bookings.is_a? Array
223
+ bookings.compact!
224
+
225
+ # Collect 'busy' bookings
226
+ e[:project_descriptor][:engagement_descriptor][:engagement_type_descriptor][:busy_flag] ? a + (bookings.inject(0) { |ba, be| ba + be[:scheduled_minutes].to_i }) : a
227
+ end
228
+
229
+ {
230
+ work: scheduled_minutes.to_f / MINUTES_PER_HOUR.to_f,
231
+ toff: time_off_minutes.to_f / MINUTES_PER_HOUR.to_f
232
+ }
233
+ end
234
+
235
+ # Generate Service URL
236
+ def self.service_url base_url
237
+ "#{base_url}/#{URL_PATH}"
238
+ end
239
+
240
+ # Current Week Start
241
+ def self.current_week_start
242
+ is_weekend? ? Date.today.wnext(WEEK_START) : Date.today.wlast(WEEK_START)
243
+ end
244
+
245
+ # Current Week End
246
+ def self.current_week_end
247
+ current_week_start.wnext(WEEK_END)
248
+ end
249
+
250
+ # Is Weekend
251
+ def self.is_weekend?
252
+ Date.today.friday? || Date.today.saturday? || Date.today.sunday?
253
+ end
254
+
255
+ # Time Off Minutes (adjust for full-day holidays)
256
+ def self.time_off_minutes x
257
+ (x == HOURS_PER_DAY_REAL * MINUTES_PER_HOUR) ? (HOURS_PER_DAY * MINUTES_PER_HOUR) : x
258
+ end
259
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'projector_pws/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'projector_pws'
8
+ spec.version = ProjectorPWS::VERSION
9
+ spec.authors = ['Eresse']
10
+ spec.email = ['eresse@eresse.net']
11
+
12
+ spec.summary = 'Projector PWS Client Library'
13
+ spec.description = 'Provides access to Projector PSA\'s Web Services (PWS)'
14
+ spec.homepage = 'http://redmine.eresse.net/projects/projector_pws'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler'
21
+ spec.add_development_dependency 'rake'
22
+ spec.add_runtime_dependency 'minitest'
23
+ spec.add_runtime_dependency 'aromat'
24
+ spec.add_runtime_dependency 'savon'
25
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: projector_pws
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eresse
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aromat
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: savon
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Provides access to Projector PSA's Web Services (PWS)
84
+ email:
85
+ - eresse@eresse.net
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - lib/projector_pws.rb
96
+ - lib/projector_pws/version.rb
97
+ - projector_pws.gemspec
98
+ homepage: http://redmine.eresse.net/projects/projector_pws
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.5.1
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Projector PWS Client Library
122
+ test_files: []