workmesh 1.0.1

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 (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/workmesh.rb +391 -0
  3. metadata +172 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 729804b3c48a7bdc88018de15bb49f297da200e310ebd306d2f4f75bb8832d3d
4
+ data.tar.gz: 4477a6b46a6e2ee01ba3576b722d5ffcde51b1128b7c916f1875e9b9f59e3305
5
+ SHA512:
6
+ metadata.gz: 7306d782ff3a70b3e0e13f6436b9035e72e00b99d52103888d08713851af0685e06c31b62066c3f49b5452088498361632b1c0b6af6aa01a162f7cc90a5ba3ed
7
+ data.tar.gz: e9af5b428c21d0b6aa5ec5070a3a20121c54c4a9006893a1bef0e0a268efc7834b2ea8b78d5fa0e5e4c4d68ef3353727a8ea16fc3c2006ea39f4e4763fc9ba2e
data/lib/workmesh.rb ADDED
@@ -0,0 +1,391 @@
1
+ require 'colorize'
2
+ require 'sequel'
3
+ require 'blackstack-core'
4
+ require 'blackstack-nodes'
5
+ require 'blackstack-deployer'
6
+ require 'simple_command_line_parser'
7
+ require 'simple_cloud_logging'
8
+
9
+ require_relative '../deployment-routines/update-config'
10
+ require_relative '../deployment-routines/update-source'
11
+
12
+ =begin
13
+ # TODO: Move this to a gem with the CRDB module
14
+ #
15
+ # return a postgresql uuid
16
+ #
17
+ def guid()
18
+ DB['SELECT gen_random_uuid() AS id'].first[:id]
19
+ end
20
+ =end
21
+
22
+ # TODO: Move new() to a gem with the CRDB module
23
+ #
24
+ # return current datetime with format `%Y-%m-%d %H:%M:%S %Z`, using the timezone of the database (`select current_setting('TIMEZONE')`)
25
+ # TODO: I am hardcoding the value of `tz` because for any reason `SELECT current_setting('TIMEZONE')` returns `UTC` instead of
26
+ # `America/Argentina/Buenos_Aires` when I run it from Ruby. Be sure your database is ALWAYS configured with the correct timezone.
27
+ #
28
+ def now()
29
+ tz = 'America/Argentina/Buenos_Aires' #DB["SELECT current_setting('TIMEZONE') AS tz"].first[:tz]
30
+ DB["SELECT current_timestamp() at TIME ZONE '#{tz}' AS now"].first[:now]
31
+ end
32
+
33
+ module BlackStack
34
+ module Workmesh
35
+ # stub node class
36
+ # stub node class is already defined in the blackstack-nodes gem: https://github.com/leandrosardi/blackstack-nodes
37
+ # we inherit from it to add some extra methods and attributes
38
+ class Node
39
+ # stub node class is already defined in the blackstack-nodes gem: https://github.com/leandrosardi/blackstack-nodes
40
+ # we inherit from it to add some extra methods and attributes
41
+ include BlackStack::Infrastructure::NodeModule
42
+ # array of workers belonging to this node
43
+ attr_accessor :workmesh_api_key
44
+ attr_accessor :workmesh_port
45
+ attr_accessor :workmesh_service
46
+ # add validations to the node descriptor
47
+ def self.descriptor_errors(h)
48
+ errors = BlackStack::Infrastructure::NodeModule.descriptor_errors(h)
49
+ # validate: the key :max_workers exists and is an integer
50
+ errors << "The key :workmesh_api_key is missing" if h[:workmesh_api_key].nil?
51
+ errors << "The key :workmesh_api_key must be an String" unless h[:workmesh_api_key].is_a?(String)
52
+ # validate: the key :workmesh_port exists and is an integer
53
+ errors << "The key :workmesh_port is missing" if h[:workmesh_port].nil?
54
+ errors << "The key :workmesh_port must be an Integer" unless h[:workmesh_port].is_a?(Integer)
55
+ # validate: the key :workmesh_service exists and is an symbol, and its string matches with the name of one of the services in the @@services array
56
+ errors << "The key :workmesh_service is missing" if h[:workmesh_service].nil?
57
+ errors << "The key :workmesh_service must be an Symbol" unless h[:workmesh_service].is_a?(Symbol)
58
+ errors << "The key :workmesh_service must be one of the following: #{BlackStack::Workmesh.services.map { |s| s.name }}" unless BlackStack::Workmesh.services.map { |s| s.name }.include?(h[:workmesh_service].to_s)
59
+ # return list of errors
60
+ errors.uniq
61
+ end
62
+ # initialize the node
63
+ def initialize(h, i_logger=nil)
64
+ errors = BlackStack::Workmesh::Node.descriptor_errors(h)
65
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
66
+ super(h, i_logger)
67
+ self.workmesh_api_key = h[:workmesh_api_key]
68
+ self.workmesh_port = h[:workmesh_port]
69
+ self.workmesh_service = h[:workmesh_service]
70
+ end # def self.create(h)
71
+ # returh a hash descriptor of the node
72
+ def to_hash()
73
+ ret = super()
74
+ ret[:workmesh_api_key] = self.workmesh_api_key
75
+ ret[:workmesh_port] = self.workmesh_port
76
+ ret[:workmesh_service] = self.workmesh_service
77
+ ret
78
+ end
79
+ # run deployment routines
80
+ def deploy(l=nil)
81
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
82
+
83
+ l.logs 'Updating config.rb... '
84
+ BlackStack::Deployer::run_routine(self.name, 'workmesh-update-config')
85
+ l.done
86
+
87
+ l.logs 'Updating source... '
88
+ BlackStack::Deployer::run_routine(self.name, 'workmesh-update-source')
89
+ l.done
90
+ end
91
+ end # class Node
92
+
93
+ class Protocol
94
+ attr_accessor :name
95
+ attr_accessor :entity_table, :entity_field_id, :entity_field_sort
96
+ attr_accessor :push_function, :entity_field_push_time, :entity_field_push_success, :entity_field_push_error_description
97
+ attr_accessor :pull_status_access_point
98
+ attr_accessor :pull_function, :enttity_field_pull_time, :entity_field_pull_success, :entity_field_pull_error_description
99
+
100
+ def self.descriptor_errors(h)
101
+ errors = []
102
+ # validate: the key :name exists and is a string
103
+ errors << "The key :name is missing" if h[:name].nil?
104
+ errors << "The key :name must be an String" unless h[:name].is_a?(String)
105
+ # validate: the key :entity_table exists and is an symbol
106
+ errors << "The key :entity_table is missing" if h[:entity_table].nil?
107
+ errors << "The key :entity_table must be an Symbol" unless h[:entity_table].is_a?(Symbol)
108
+ # validate: the key :entity_field_id exists and is an symbol
109
+ errors << "The key :entity_field_id is missing" if h[:entity_field_id].nil?
110
+ errors << "The key :entity_field_id must be an Symbol" unless h[:entity_field_id].is_a?(Symbol)
111
+ # validate: the key :entity_field_sort exists and is an symbol
112
+ errors << "The key :entity_field_sort is missing" if h[:entity_field_sort].nil?
113
+ errors << "The key :entity_field_sort must be an Symbol" unless h[:entity_field_sort].is_a?(Symbol)
114
+
115
+ # validate: the key :push_function is null or it is a procedure
116
+ #errors << "The key :push_function must be an Symbol" unless h[:push_function].nil? || h[:push_function].is_a?()
117
+ # validate: if :push_function exists, the key :entity_field_push_time exists and it is a symbol
118
+ errors << "The key :entity_field_push_time is missing" if h[:push_function] && h[:entity_field_push_time].nil?
119
+ # validate: if :push_function exists, the key :entity_field_push_success exists and it is a symbol
120
+ errors << "The key :entity_field_push_success is missing" if h[:push_function] && h[:entity_field_push_success].nil?
121
+ # validate: if :push_function exists, the key :entity_field_push_error_description exists and it is a symbol
122
+ errors << "The key :entity_field_push_error_description is missing" if h[:push_function] && h[:entity_field_push_error_description].nil?
123
+
124
+ # valiudate: if :pull_function exists, the key :pull_status_access_point exists and it is a string
125
+ errors << "The key :pull_status_access_point is missing" if h[:pull_function] && h[:pull_status_access_point].nil?
126
+ errors << "The key :pull_status_access_point must be an String" unless h[:pull_function].nil? || h[:pull_status_access_point].is_a?(String)
127
+ # validate: if :pull_function exists, the key :entity_field_pull_time exists and it is a symbol
128
+ errors << "The key :entity_field_pull_time is missing" if h[:pull_function] && h[:entity_field_pull_time].nil?
129
+ # validate: if :pull_function exists, the key :entity_field_pull_success exists and it is a symbol
130
+ errors << "The key :entity_field_pull_success is missing" if h[:pull_function] && h[:entity_field_pull_success].nil?
131
+ # validate: if :pull_function exists, the key :entity_field_pull_error_description exists and it is a symbol
132
+ errors << "The key :entity_field_pull_error_description is missing" if h[:pull_function] && h[:entity_field_pull_error_description].nil?
133
+
134
+ # return list of errors
135
+ errors.uniq
136
+ end
137
+
138
+ def initialize(h)
139
+ errors = BlackStack::Workmesh::Protocol.descriptor_errors(h)
140
+ raise "The protocol descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
141
+ self.name = h[:name]
142
+ self.entity_table = h[:entity_table]
143
+ self.entity_field_id = h[:entity_field_id]
144
+ self.entity_field_sort = h[:entity_field_sort]
145
+ self.push_function = h[:push_function]
146
+ self.entity_field_push_time = h[:entity_field_push_time]
147
+ self.entity_field_push_success = h[:entity_field_push_success]
148
+ self.entity_field_push_error_description = h[:entity_field_push_error_description]
149
+ self.pull_function = h[:pull_function]
150
+ self.enttity_field_pull_time = h[:entity_field_pull_time]
151
+ self.entity_field_pull_success = h[:entity_field_pull_success]
152
+ self.entity_field_pull_error_description = h[:entity_field_pull_error_description]
153
+ end
154
+
155
+ def to_hash()
156
+ ret = super()
157
+ ret[:name] = self.name
158
+ ret[:entity_table] = self.entity_table
159
+ ret[:entity_field_id] = self.entity_field_id
160
+ ret[:entity_field_sort] = self.entity_field_sort
161
+ ret[:push_function] = self.push_function
162
+ ret[:entity_field_push_time] = self.entity_field_push_time
163
+ ret[:entity_field_push_success] = self.entity_field_push_success
164
+ ret[:entity_field_push_error_description] = self.entity_field_push_error_description
165
+ ret[:pull_function] = self.pull_function
166
+ ret[:enttity_field_pull_time] = self.enttity_field_pull_time
167
+ ret[:entity_field_pull_success] = self.entity_field_pull_success
168
+ ret[:entity_field_pull_error_description] = self.entity_field_pull_error_description
169
+ ret
170
+ end
171
+
172
+ # execute the push function of this protocol, and update the push flags
173
+ def push(entity, node)
174
+ raise 'The push function is not defined' if self.push_function.nil?
175
+ entity[entity_field_push_time] = now()
176
+ begin
177
+ self.push_function.call(entity, node)
178
+ entity[entity_field_push_success] = true
179
+ entity[entity_field_push_error_description] = nil
180
+ entity.save
181
+ rescue => e
182
+ entity[entity_field_push_success] = false
183
+ entity[entity_field_push_error_description] = e.message
184
+ entity.save
185
+ raise e
186
+ end
187
+ end
188
+ end # class Protocol
189
+
190
+ # stub worker class
191
+ class Service
192
+ ASSIGANTIONS = [:entityweight, :roundrobin, :entitynumber]
193
+ # name to identify uniquely the worker
194
+ attr_accessor :name, :entity_table, :entity_field_assignation, :protocols, :assignation
195
+ # return an array with the errors found in the description of the job
196
+ def self.descriptor_errors(h)
197
+ errors = []
198
+ # validate: the key :name exists and is an string
199
+ errors << "The key :name is missing" if h[:name].nil?
200
+ errors << "The key :name must be an String" unless h[:name].is_a?(String)
201
+ # validate: the key :entity_table exists and is an symbol
202
+ errors << "The key :entity_table is missing" if h[:entity_table].nil?
203
+ errors << "The key :entity_table must be an Symbol" unless h[:entity_table].is_a?(Symbol)
204
+ # validate: the key :entity_field_assignation exists and is an symbol
205
+ errors << "The key :entity_field_assignation is missing" if h[:entity_field_assignation].nil?
206
+ errors << "The key :entity_field_assignation must be an Symbol" unless h[:entity_field_assignation].is_a?(Symbol)
207
+ # validate: the key :protocols exists is nil or it is an array of valid hash descritors of the Protocol class.
208
+ errors << "The key :protocols must be an Array" unless h[:protocols].nil? || h[:protocols].is_a?(Array)
209
+ if h[:protocols].is_a?(Array)
210
+ h[:protocols].each do |protocol|
211
+ errors << "The key :protocols must be an Array of valid hash descritors of the Protocol class" unless protocol.is_a?(Hash)
212
+ errors << "The key :protocols must be an Array of valid hash descritors of the Protocol class" unless Protocol.descriptor_errors(protocol).length == 0
213
+ end
214
+ end
215
+ # validate: the key :assignation is nil or it is a symbol belonging the array ASSIGANTIONS
216
+ errors << "The key :assignation must be an Symbol" unless h[:assignation].nil? || h[:assignation].is_a?(Symbol)
217
+ unless h[:assignation].nil?
218
+ errors << "The key :assignation must be one of the following values: #{ASSIGANTIONS.join(", ")}" unless ASSIGANTIONS.include?(h[:assignation])
219
+ end
220
+ # return list of errors
221
+ errors.uniq
222
+ end
223
+ # setup dispatcher configuration here
224
+ def initialize(h)
225
+ errors = BlackStack::Workmesh::Service.descriptor_errors(h)
226
+ raise "The worker descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
227
+ self.name = h[:name]
228
+ self.entity_table = h[:entity_table]
229
+ self.entity_field_assignation = h[:entity_field_assignation]
230
+ self.protocols = []
231
+ if h[:protocols]
232
+ h[:protocols].each do |i|
233
+ self.protocols << BlackStack::Workmesh::Protocol.new(i)
234
+ end
235
+ end
236
+ self.assignation = h[:assignation]
237
+ end
238
+ # return a hash descriptor of the worker
239
+ def to_hash()
240
+ {
241
+ :name => self.name,
242
+ :entity_table => self.entity_table,
243
+ :entity_field_assignation => self.entity_field_assignation,
244
+ :protocols => self.protocols.map { |p| p.to_hash },
245
+ :assignation => self.assignation
246
+ }
247
+ end
248
+ # get a protocol from its name
249
+ def protocol(name)
250
+ self.protocols.select { |p| p.name.to_s == name.to_s }.first
251
+ end
252
+ end # class Service
253
+
254
+ # hash with the round-robin positions per service.
255
+ @@roundrobin = {}
256
+
257
+ # infrastructure configuration
258
+ @@nodes = []
259
+ @@services = []
260
+
261
+ # logger configuration
262
+ @@log_filename = nil
263
+ @@logger = BlackStack::DummyLogger.new(nil)
264
+
265
+ # Connection string to the database. Example: mysql2://user:password@localhost:3306/database
266
+ @@connection_string = nil
267
+
268
+ # define a filename for the log file.
269
+ def self.set_log_filename(s)
270
+ @@log_filename = s
271
+ @@logger = BlackStack::LocalLogger.new(s)
272
+ end
273
+
274
+ # return the logger.
275
+ def self.logger()
276
+ @@logger
277
+ end
278
+
279
+ def self.set_logger(l)
280
+ @@logger = l
281
+ end
282
+
283
+ # return the log filename.
284
+ def self.log_filename()
285
+ @@log_filename
286
+ end
287
+
288
+ # define the connectionstring to the database.
289
+ def self.set_connection_string(s)
290
+ @@connection_string = s
291
+ end
292
+
293
+ # return connection string to the database. Example: mysql2://user:password@localhost:3306/database
294
+ def self.connection_string()
295
+ @@connection_string
296
+ end
297
+
298
+ # add_node
299
+ # add a node to the infrastructure
300
+ def self.add_node(h)
301
+ @@nodes << BlackStack::Workmesh::Node.new(h)
302
+ # add to deployer
303
+ BlackStack::Deployer.add_node(h) #if @@integrate_with_blackstack_deployer
304
+ end
305
+
306
+ def self.nodes
307
+ @@nodes
308
+ end
309
+
310
+ def self.node(name)
311
+ @@nodes.select { |n| n.name.to_s == name.to_s }.first
312
+ end
313
+
314
+ # add_service
315
+ # add a service to the infrastructure
316
+ def self.add_service(h)
317
+ @@services << BlackStack::Workmesh::Service.new(h)
318
+ end
319
+
320
+ def self.services
321
+ @@services
322
+ end
323
+
324
+ def self.service(name)
325
+ @@services.select { |s| s.name.to_s == name.to_s }.first
326
+ end
327
+
328
+ # assign object to a node using a round-robin algorithm
329
+ # this method is used when the service assignation is :roundrobin
330
+ # this method is for internal use only, and it should not be called directly.
331
+ def self.roundrobin(o, service_name)
332
+ @@roundrobin[service_name] = 0 if @@roundrobin[service_name].nil?
333
+ # getting the service
334
+ s = @@services.select { |s| s.name.to_s == service_name.to_s }.first
335
+ # getting all the nodes assigned to the service
336
+ nodes = @@nodes.select { |n| n.workmesh_service.to_s == service_name.to_s }.sort_by { |n| n.name.to_s }
337
+ # increase i
338
+ @@roundrobin[service_name] += 1
339
+ @@roundrobin[service_name] = 0 if @@roundrobin[service_name] >= nodes.length
340
+ # assign the object to the node
341
+ n = nodes[@@roundrobin[service_name]]
342
+ o[s.entity_field_assignation] = n.name
343
+ o.save
344
+ # return
345
+ n
346
+ end
347
+
348
+ # return the assigned node to an object, for a specific service.
349
+ # if the object has not a node assigned, then return nil.
350
+ def self.assigned_node(o, service_name)
351
+ # getting the service
352
+ s = @@services.select { |s| s.name.to_s == service_name.to_s }.first
353
+ # validate: the service exists
354
+ raise "The service #{service_name} does not exists" if s.nil?
355
+ # validate: the object o is an instance of the Sequel Class mapping the table :entity_table
356
+ raise "The object o is not an instance of :entity_table (#{s.entity_table.to_s})" unless o.is_a?(Sequel::Model) && o.class.table_name.to_s == s.entity_table.to_s
357
+ # if the object has not a node assigned, then return nil.
358
+ return nil if o[s.entity_field_assignation].nil?
359
+ # return the node
360
+ @@nodes.select { |n| n.name.to_s == o[s.entity_field_assignation].to_s }.first
361
+ end
362
+
363
+ # assign object to a node
364
+ def self.assign(o, service_name, h = {})
365
+ # getting the service
366
+ s = @@services.select { |s| s.name.to_s == service_name.to_s }.first
367
+ # validate: the service exists
368
+ raise "The service #{service_name} does not exists" if s.nil?
369
+ # validate: the object o is an instance of the Sequel Class mapping the table :entity_table
370
+ raise "The object o is not an instance of :entity_table (#{s.entity_table.to_s})" unless o.is_a?(Sequel::Model) && o.class.table_name.to_s == s.entity_table.to_s
371
+ # reassign
372
+ if h[:reassign] == true
373
+ o[s.entity_field_assignation] = nil
374
+ o.save
375
+ end
376
+ # validate: the object o has not been assigned yet
377
+ raise "The object o has been already assigned to a node. Use the :reassign parameter for reassignation." unless o[s.entity_field_assignation].nil?
378
+ # decide the assignation method
379
+ if s.assignation == :entityweight
380
+ raise 'The assignation method :entityweight is not implemented yet.'
381
+ elsif s.assignation == :roundrobin
382
+ return BlackStack::Workmesh.roundrobin(o, service_name)
383
+ elsif s.assignation == :entitynumber
384
+ raise 'The assignation method :entitynumber is not implemented yet.'
385
+ else
386
+ raise "The assignation method #{s.assignation} is unknown."
387
+ end
388
+ end
389
+
390
+ end # module Workmesh
391
+ end # module BlackStack
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: workmesh
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Leandro Daniel Sardi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.56.0
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.56.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 5.56.0
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.56.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: blackstack-core
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.2.3
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.2.3
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 1.2.3
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.2.3
53
+ - !ruby/object:Gem::Dependency
54
+ name: blackstack-nodes
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 1.2.11
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.2.11
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 1.2.11
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.2.11
73
+ - !ruby/object:Gem::Dependency
74
+ name: blackstack-deployer
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 1.2.24
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.2.24
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.2.24
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.2.24
93
+ - !ruby/object:Gem::Dependency
94
+ name: simple_command_line_parser
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: 1.1.2
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 1.1.2
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 1.1.2
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.1.2
113
+ - !ruby/object:Gem::Dependency
114
+ name: simple_cloud_logging
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: 1.2.2
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 1.2.2
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: 1.2.2
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 1.2.2
133
+ description: "WorkMesh is an open-source micro-services orchestration system for automatng
134
+ software scaling and work distribution.\n\n Some hints:\n \n - In the **WorkMesh**
135
+ world, a **micro-service** is an **external web-service** who receives tasks for
136
+ any kind of offline processing, and returns the result to **master**. Just that.
137
+ Nothing more.\n \n - This library is for defininng the micro-service protocol
138
+ at the **master** side.\n \n - For creating your own micro-service, refer to [micro.template](https://github.com/leandrosardi/micro.template)
139
+ project.\n \n - If you are looking for a multi-threading processing framework,
140
+ you should refer to [Pampa](https://github.com/leandrosardi/pampa) instead.\n \nFind
141
+ documentation here: https://github.com/leandrosardi/workmesh\n"
142
+ email: leandro.sardi@expandedventure.com
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - lib/workmesh.rb
148
+ homepage: https://rubygems.org/gems/workmesh
149
+ licenses:
150
+ - MIT
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubygems_version: 3.3.7
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: WorkMesh is an open-source micro-services orchestration system for automatng
171
+ software scaling and work distribution.
172
+ test_files: []