agis 0.2.7

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/agis.rb +287 -0
  3. metadata +73 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6c2efcb1eba59940bd4c9fa4c63baa465d2324a7
4
+ data.tar.gz: 936b33de7cfcdcb35eba8d0cf8400472968383fc
5
+ SHA512:
6
+ metadata.gz: b598596dc39f627f3efdc55ef62001a885cf59fcb930710f30523289dc1bb198558ad4641aeeec3b3fd5e81a3c307d2b890b31256e7844fcdea5d6370736c77e
7
+ data.tar.gz: 47ff87901ca0673f24085843de435f136283d5b8f234ccc1fcef7d0957ff4ee4392c4e7b2b77c48ca75b1110c8850fb9a12dfc35bde28dbf3247a83a6d603775
@@ -0,0 +1,287 @@
1
+ module Agis
2
+ require 'redis'
3
+ require 'redis-lock'
4
+ require 'json'
5
+
6
+ # called whenever a parameter in the queue is of type method
7
+ # this is unusual behavior
8
+ class MethodCallInParameters < StandardError
9
+ end
10
+
11
+ class AgisRetryAttemptsExceeded < StandardError
12
+ end
13
+
14
+ class NoAgisIDAvailable < StandardError
15
+ end
16
+
17
+ class RedisLockExpired < StandardError
18
+ end
19
+
20
+ class MessageBoxEmpty < StandardError
21
+ end
22
+
23
+ # the name of the key used for the Agis message box in Redis
24
+ # the lock is this string followed by ".LOCK"
25
+ def agis_mailbox
26
+ begin
27
+ mid = self.agis_id
28
+ rescue NoMethodError
29
+ end
30
+ begin
31
+ mid ||= self.id
32
+ rescue NoMethodError
33
+ end
34
+ raise NoAgisIDAvailable unless mid
35
+ a = "AGIS TERMINAL : " + self.class.to_s + " : " + mid.to_s
36
+ end
37
+
38
+ def agis_aconv(v)
39
+ a = ""
40
+ case v
41
+ when String
42
+ a = "s:" + v
43
+ when Symbol
44
+ a = "s:" + v.to_s
45
+ when Integer
46
+ a = "i:" + v.to_s
47
+ when Hash
48
+ a = "h:" + v.to_json.to_s
49
+ when Array
50
+ a = "a:" + v.to_json.to_s
51
+ when Float
52
+ a = "d:" + v.to_s
53
+ when TrueClass
54
+ a = "t:"
55
+ when FalseClass
56
+ a = "f:"
57
+ when NilClass
58
+ a = "n:"
59
+ else
60
+ a = "h:" + v.to_json.to_s
61
+ end
62
+ return a
63
+ end
64
+
65
+ def agis_fconv(v)
66
+ case v[0..1]
67
+ when "s:"
68
+ v[2..-1]
69
+ when "i:"
70
+ v[2..-1].to_i
71
+ when "h:"
72
+ JSON.parse!(v[2..-1], symbolize_names: false)
73
+ when "a:"
74
+ JSON.parse!(v[2..-1], symbolize_names: false)
75
+ when "d:"
76
+ v[2..-1].to_f
77
+ when "t:"
78
+ true
79
+ when "f:"
80
+ false
81
+ when "n:"
82
+ nil
83
+ when "m:"
84
+ raise MethodCallInParameters
85
+ end
86
+ end
87
+
88
+ # create a method with no parameters
89
+ def agis_defm0(name, timeout=nil, &b)
90
+ @agis_methods ||= Hash.new
91
+ @agis_methods[name] = [0, b, timeout]
92
+ end
93
+
94
+ # create a method with one parameter
95
+ def agis_defm1(name, timeout=nil, &b)
96
+ @agis_methods ||= Hash.new
97
+ @agis_methods[name] = [1, b, timeout]
98
+ end
99
+
100
+ # create a method with two parameters
101
+ def agis_defm2(name, timeout=nil, &b)
102
+ @agis_methods ||= Hash.new
103
+ @agis_methods[name] = [2, b, timeout]
104
+ end
105
+
106
+ # create a method with three parameters
107
+ def agis_defm3(name, timeout=nil, &b)
108
+ @agis_methods ||= Hash.new
109
+ @agis_methods[name] = [3, b, timeout]
110
+ end
111
+
112
+ # alias for agis_defm3
113
+ def agis_def(name, timeout=nil, &b)
114
+ agis_defm3(name, timeout, b)
115
+ end
116
+
117
+ def pretty_exception(args, e)
118
+ ret = []
119
+ ret << "Agis method call failed: " + args.to_s
120
+ ret << " " + e.class.to_s
121
+ e.backtrace.each do |v|
122
+ ret << v.to_s
123
+ end
124
+ ret
125
+ end
126
+
127
+ def popfive(redis)
128
+ redis.multi do
129
+ redis.lpop self.agis_mailbox
130
+ redis.lpop self.agis_mailbox
131
+ redis.lpop self.agis_mailbox
132
+ redis.lpop self.agis_mailbox
133
+ redis.lpop self.agis_mailbox
134
+ end
135
+ end
136
+
137
+ def agis_boxlock
138
+ self.agis_mailbox + ".LOCK"
139
+ end
140
+
141
+ def agis_returnbox
142
+ self.agis_mailbox + ".RETN"
143
+ end
144
+
145
+ def agis_chew(redis, lock)
146
+ args = redis.lrange(self.agis_mailbox, 0, 4)
147
+ mni = args[0]
148
+ if(mni and mni[0..1] == "m:")
149
+ # don't do any signatures twice ever
150
+ lusig = args[4][2..-1]
151
+ #puts lusig
152
+ if redis.hget self.agis_returnbox, lusig
153
+ popfive redis
154
+ return nil
155
+ end
156
+ mn = mni[2..-1]
157
+ mc = @agis_methods[mn.to_sym][0]
158
+ meti = @agis_methods[mn.to_sym][1]
159
+ case meti
160
+ when Proc
161
+ met = meti
162
+ when Symbol
163
+ met = self.method(meti)
164
+ when NilClass
165
+ met = self.method(mn.to_sym) # when proc is Nil, call the class methods all the same
166
+ end
167
+
168
+ begin
169
+ #raise Agis::RedisLockExpired if lock.stale_key?
170
+ #begin
171
+ # lock.extend_life (@agis_methods[mn.to_sym][2] or 5)
172
+ #rescue Redis::Lock::LockNotAcquired
173
+ # raise Agis::RedisLockExpired
174
+ #end
175
+ case mc
176
+ when 0
177
+ ret = agis_aconv(met.call())
178
+ when 1
179
+ ret = agis_aconv(met.call(agis_fconv(args[1])))
180
+ when 2
181
+ ret = agis_aconv(met.call(agis_fconv(args[1]), agis_fconv(args[2])))
182
+ when 3
183
+ ret = agis_aconv(met.call(agis_fconv(args[1]), agis_fconv(args[2]), agis_fconv(args[3])))
184
+ end
185
+ redis.multi do
186
+ redis.hset self.agis_returnbox, lusig, ret
187
+ popfive redis
188
+ end
189
+ return :next
190
+ rescue Agis::RedisLockExpired => e
191
+ puts "Agis lock expired for " + args.to_s if (@agis_debugmode == true)
192
+ # popfive redis
193
+ return :relock
194
+ rescue => e
195
+ #puts "feck"
196
+ lock.unlock
197
+ raise e
198
+ end
199
+ elsif not mni
200
+ return :empty
201
+ else
202
+ puts "AGIS error: Unrecognized line!" + mni.to_s
203
+ end
204
+ end
205
+
206
+ def agis_try_usig(redis, usig)
207
+ mayb = redis.hget self.agis_returnbox, usig
208
+ if mayb
209
+ redis.hdel self.agis_returnbox, usig
210
+ return mayb
211
+ else
212
+ return nil
213
+ end
214
+ end
215
+
216
+ def _agis_crunch(redis, usig)
217
+ loop do
218
+ redis.lock(self.agis_boxlock, life: 10) do |lock|
219
+ a = agis_chew(redis, lock)
220
+ next if lock.stale_key?
221
+ u = agis_try_usig(redis, usig)
222
+ if a == :empty
223
+ raise Agis::MessageBoxEmpty unless u
224
+ end
225
+ return agis_fconv(u) if u
226
+ end
227
+ end
228
+ end
229
+
230
+ # Get method in the format
231
+ # [arity, method body]
232
+ def agis_method(name)
233
+ @agis_methods[name]
234
+ end
235
+
236
+ # Push a call and ncrunch immediately
237
+ # this returns the last return value from the queue
238
+ def agis_call(redis, name, arg1=nil, arg2=nil, arg3=nil)
239
+ until_sig = Time.now.to_s + ":" + Process.pid.to_s + Random.new.rand(4000000000).to_s + Random.new.rand(4000000000).to_s
240
+ loop do
241
+ begin
242
+ redis.multi do
243
+ redis.rpush self.agis_mailbox, "m:" + name.to_s
244
+ redis.rpush self.agis_mailbox, agis_aconv(arg1)
245
+ redis.rpush self.agis_mailbox, agis_aconv(arg2)
246
+ redis.rpush self.agis_mailbox, agis_aconv(arg3)
247
+ redis.rpush self.agis_mailbox, "r:" + until_sig
248
+ end
249
+ return _agis_crunch(redis, until_sig)
250
+ rescue Agis::MessageBoxEmpty
251
+ end
252
+ end
253
+ end
254
+
255
+ # Alias for agis_call
256
+ def acall(redis, name, arg1=nil, arg2=nil, arg3=nil)
257
+ agis_call(redis, name, arg1, arg2, arg3)
258
+ end
259
+
260
+ # Method for calling another Agis method, or retrying.
261
+ # this doesn't touch the message box because it should
262
+ # only be called inside an Agis method, where the box
263
+ # is already guaranteed to be locked
264
+ def agis_recall(mn, arg1=nil, arg2=nil, arg3=nil)
265
+ meti = @agis_methods[mn.to_sym][1]
266
+ case meti
267
+ when Proc
268
+ met = meti
269
+ when Symbol
270
+ met = self.method(meti)
271
+ when NilClass
272
+ met = self.method(mn.to_sym) # when proc is Nil, call the class methods all the same
273
+ end
274
+
275
+ case @agis_methods[mn.to_sym][0]
276
+ when 0
277
+ return met.call()
278
+ when 1
279
+ return met.call(arg1)
280
+ when 2
281
+ return met.call(arg1, arg2)
282
+ when 3
283
+ return met.call(arg1, arg2, arg3)
284
+ end
285
+ end
286
+ end
287
+
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.7
5
+ platform: ruby
6
+ authors:
7
+ - Gert Oja
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: '0'
25
+ prerelease: false
26
+ type: :development
27
+ - !ruby/object:Gem::Dependency
28
+ name: mlanett-redis-lock
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ prerelease: false
40
+ type: :development
41
+ description: Messagebox Redis Actors for Ruby and ActiveRecord
42
+ email: gertoja1@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/agis.rb
48
+ homepage: http://rubygems.org/gems/agis
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.4.6
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: Messagebox Redis Actors for Ruby
72
+ test_files: []
73
+ has_rdoc: