ipc_transit 0.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.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.test_files = FileList['test/t*.rb']
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
data/bin/trlist ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ipc_transit'
3
+
4
+
5
+ IPCTransit.all_queues.each_pair do |qname, info|
6
+ puts "#{qname} = #{info['count']}"
7
+ end
8
+
9
+ #{"tqueue"=>{"qid"=>3, "count"=>0}, "test_qname"=>{"qid"=>2, "count"=>0}}
10
+ #
data/bin/trrecv ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ipc_transit'
3
+ require 'json'
4
+
5
+ qname = ARGV[0]
6
+ nowait = ARGV[1]
7
+
8
+
9
+ #puts qname
10
+ #puts message
11
+
12
+ while ret = IPCTransit.receive('qname' => qname, 'nowait' => 1)
13
+ puts ret
14
+ end
15
+
data/bin/trsend ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ipc_transit'
3
+ require 'json'
4
+
5
+ qname = ARGV[0]
6
+ message = JSON.parse(ARGV[1])
7
+
8
+
9
+ ret = IPCTransit.send('message' => message, 'qname' => qname)
10
+ puts ret
@@ -0,0 +1,253 @@
1
+ require 'json'
2
+ require 'SysVIPC'
3
+ include SysVIPC
4
+
5
+ ##
6
+ # Fast, serverless message queueing
7
+ #
8
+ # Author:: Dana M. Diederich (diederich@gmail.com)
9
+ # Copyright:: Copyright (c) 2012 Dana M. Diederich
10
+ # License:: Distributes under the same terms as Ruby
11
+
12
+ class IPCTransit
13
+ @@queues = {}
14
+
15
+ @@ipc_transit_wire_header_args = {
16
+ 'e' => {
17
+ 'json' => 1,
18
+ 'yaml' => 1,
19
+ },
20
+ 'c' => {
21
+ 'zlib' => 1,
22
+ 'snappy' => 1,
23
+ 'none' => 1,
24
+ },
25
+ }
26
+ @@ipc_transit_std_args = {
27
+ 'message' => 1,
28
+ 'qname' => 1,
29
+ 'nowait' => 1,
30
+ }
31
+
32
+
33
+ ##
34
+ # Send message to a queue
35
+ #
36
+ # Arguments:
37
+ # message - hash reference
38
+ # qname - name of queue to send to
39
+ # nowait - do not block if the queue is full (optional)
40
+
41
+ def self.send(args)
42
+ ret = nil
43
+ flags = IPC_NOWAIT
44
+ begin
45
+ key = self.get_queue_id(args)
46
+ mq = MessageQueue.new(key, IPC_CREAT | 0666)
47
+ ret = mq.snd(1, pack_message(args), flags)
48
+ rescue Exception => msg
49
+ puts "Exception: #{msg}"
50
+ end
51
+ return ret
52
+ end
53
+ ##
54
+ # Receive a message from a queue
55
+ #
56
+ # Arguments:
57
+ # qname - name of queue to send to
58
+ # nowait - do not block if the queue is full (optional)
59
+ # raw - return the full meta-data (optional)
60
+ #
61
+ # Returns:
62
+ # Normally: message
63
+ # Raw: the message and its meta data
64
+
65
+ def self.receive(args)
66
+ ret = nil
67
+ flags = 0
68
+ if args['nowait']
69
+ flags = IPC_NOWAIT
70
+ end
71
+ begin
72
+ key = self.get_queue_id(args)
73
+ mq = MessageQueue.new(key, IPC_CREAT | 0666)
74
+ args['serialized_wire_data'] = mq.receive(0, 10000, flags)
75
+ self.unpack_data(args)
76
+ rescue Exception => msg
77
+ # puts "Exception: #{msg}"
78
+ end
79
+ if args['raw']
80
+ ret = args;
81
+ else
82
+ ret = args['message']
83
+ end
84
+ return ret
85
+ end
86
+ def self.all_queue_info()
87
+ self.gather_queue_info()
88
+ return @@queues
89
+ end
90
+
91
+ def self.all_queues()
92
+ ret = {}
93
+ self.all_queue_info().each_pair do |qname,v|
94
+ qid = v['qid']
95
+ x = MessageQueue.new(qid, IPC_CREAT | 0666)
96
+ y = x.ipc_stat
97
+ ct = y.msg_qnum
98
+ ret[qname] = {
99
+ 'qid' => qid,
100
+ 'count' => ct,
101
+ }
102
+ end
103
+ return ret
104
+ end
105
+
106
+ private
107
+ def self.get_next_id
108
+ new_id = 1
109
+ @@queues.each_pair do |k,v|
110
+ if v['qid'] > new_id
111
+ new_id = v['qid']
112
+ end
113
+ end
114
+ return new_id + 1
115
+ end
116
+ def self.get_queue_id(args)
117
+ qname = args['qname']
118
+ self.mk_queue_dir()
119
+ if @@queues[qname]
120
+ return @@queues[qname]['qid']
121
+ end
122
+ self.gather_queue_info()
123
+ if @@queues[qname]
124
+ return @@queues[qname]['qid']
125
+ end
126
+ begin
127
+ self.lock_dir()
128
+ file = File.open("/tmp/transit/#{qname}", 'w')
129
+ new_qid = get_next_id
130
+ file.puts("qid=#{new_qid}")
131
+ file.puts("qname=#{qname}")
132
+ file.close
133
+ rescue Exception => msg
134
+ self.unlock_dir()
135
+ raise msg
136
+ end
137
+ self.unlock_dir()
138
+ self.gather_queue_info()
139
+ return @@queues[qname]['qid']
140
+ end
141
+
142
+ def self.lock_dir()
143
+ File.open('/tmp/transit.lock', File::WRONLY|File::EXCL|File::CREAT, 0666)
144
+ end
145
+ def self.unlock_dir()
146
+ File.delete('/tmp/transit.lock')
147
+ end
148
+
149
+ def self.gather_queue_info()
150
+ self.mk_queue_dir()
151
+ Dir.glob('/tmp/transit/*').each do |filename|
152
+ info = {}
153
+ file = File.new(filename, 'r')
154
+ while (line = file.gets)
155
+ line.chomp!
156
+ (key, value) = line.split('=')
157
+ info[key] = value
158
+ end
159
+ if not info['qid']
160
+ raise "required key 'qid' not found"
161
+ end
162
+ if not info['qname']
163
+ raise "required key 'qname' not found"
164
+ end
165
+ info['qid'] = Integer(info['qid'])
166
+ @@queues[info['qname']] = info
167
+ end
168
+ end
169
+
170
+ def self.mk_queue_dir()
171
+ begin
172
+ Dir.mkdir('/tmp/transit', 0777)
173
+ rescue
174
+ end
175
+ end
176
+
177
+
178
+ def self.transit_freeze(args)
179
+ return args['message'].to_json
180
+ end
181
+
182
+ def self.transit_thaw(args)
183
+ return JSON.parse(args['serialized_message'])
184
+ end
185
+
186
+
187
+
188
+ #returns a scalar, ready to be sent on the wire
189
+ #it takes message and wire_meta_data
190
+ def self.pack_message(args)
191
+ args['message']['.ipc_transit_meta'] = {}
192
+ args.keys.each do |k|
193
+ next if @@ipc_transit_wire_header_args[k]
194
+ next if @@ipc_transit_std_args[k]
195
+ args['message']['.ipc_transit_meta'][k] = args[k]
196
+ end
197
+ args['serialized_message'] = self.transit_freeze(args)
198
+ self.serialize_wire_meta(args)
199
+ l = args['serialized_wire_meta_data'].length
200
+ ret = "#{l}:#{args['serialized_wire_meta_data']}#{args['serialized_message']}"
201
+ return ret
202
+ end
203
+
204
+ def self.serialize_wire_meta(args)
205
+ s = ''
206
+ args.keys.each do |k|
207
+ if @@ipc_transit_wire_header_args[k]
208
+ #make sure a valid value is passed in
209
+ if @@ipc_transit_wire_header_args[k] == 1
210
+ s = "#{s}#{k}=#{args[k]},"
211
+ elsif @@ipc_transit_wire_header_args[k][args[k]]
212
+ s = "#{s}#{k}=#{args[k]},"
213
+ else
214
+ raise "passed wire argument #{k} had value #{args[k]} not of allowed type"
215
+ end
216
+ end
217
+ end
218
+ if s
219
+ s = s[0..-2] #teh google says this is the way to remove last character
220
+ end
221
+ args['serialized_wire_meta_data'] = s
222
+ end
223
+
224
+ #NB: I know this is all hideously inefficient. I'm still learning Ruby,
225
+ #and I'm focusing on getting this correct first.
226
+ #
227
+ #returns a serialized_message and wire_meta_data
228
+ #takes serialized_wire_data
229
+ def self.unpack_data(args)
230
+ stuff = args['serialized_wire_data'].split(':')
231
+ offset = Integer(stuff.shift)
232
+ header_and_message = stuff.join(':')
233
+ if offset == 0
234
+ args['serialized_header'] = ''
235
+ else
236
+ args['serialized_header'] = header_and_message[0..offset-1]
237
+ self.thaw_wire_headers(args)
238
+ end
239
+ args['serialized_message'] = header_and_message[offset..header_and_message.length]
240
+ args['message'] = self.transit_thaw(args)
241
+ return true
242
+ end
243
+
244
+ def self.thaw_wire_headers(args)
245
+ h = args['serialized_header']
246
+ ret = {}
247
+ h.split(',').each do |val|
248
+ (k,v) = val.split('=')
249
+ ret[k] = v
250
+ end
251
+ args['wire_headers'] = ret
252
+ end
253
+ end
@@ -0,0 +1,76 @@
1
+ require 'test/unit'
2
+ require 'ipc_transit'
3
+
4
+ #kind of ghetto, but I don't have a better way right now
5
+ def drain_test_queue
6
+ begin
7
+ while ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
8
+ end
9
+ rescue Exception => msg
10
+ end
11
+ end
12
+
13
+ class TestIPCTransit < Test::Unit::TestCase
14
+ def test_typical
15
+ drain_test_queue()
16
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname')
17
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
18
+ assert(ret, 'IPCTransit.receive returned true')
19
+ assert_equal(ret['foo'], 'bar')
20
+ end
21
+
22
+ def test_wire_raw
23
+ drain_test_queue()
24
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname', 'e' => 'json', 'c' => 'none')
25
+ ret = IPCTransit.receive('qname' => 'test_qname', 'raw' => 1, 'nowait' => 1)
26
+ assert(ret, 'IPCTransit.receive returned true')
27
+ assert_equal(ret['message']['foo'], 'bar')
28
+ assert_equal(ret['wire_headers']['e'], 'json')
29
+ assert_equal(ret['wire_headers']['c'], 'none')
30
+ end
31
+ def test_message_meta
32
+ drain_test_queue()
33
+ IPCTransit.send( 'qname' => 'test_qname',
34
+ 'message' => { 'foo' => 'bar' },
35
+ 'e' => 'json',
36
+ 'c' => 'none',
37
+ 'something' => 'else',
38
+ 'x' => { 'this' => 'that' },
39
+ 'once' => ['more',2],
40
+ )
41
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
42
+ assert(ret, 'IPCTransit.receive returned true')
43
+ assert_equal(ret['foo'], 'bar')
44
+ assert_equal(ret['.ipc_transit_meta']['something'], 'else')
45
+ assert_equal(ret['.ipc_transit_meta']['x']['this'], 'that')
46
+ assert_equal(ret['.ipc_transit_meta']['once'][0], 'more')
47
+ assert_equal(ret['.ipc_transit_meta']['once'][1], 2)
48
+ end
49
+
50
+ def test_get_queue_info
51
+ drain_test_queue()
52
+ all_info = IPCTransit.all_queue_info()
53
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname')
54
+ assert(all_info, 'We received some queue info')
55
+
56
+ assert(all_info['test_qname']['qname'] == 'test_qname', 'test queue is in all_queue_info')
57
+ qid = all_info['test_qname']['qid']
58
+ assert(qid, 'queue_id for test_qname exists')
59
+
60
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
61
+ assert(ret, 'IPCTransit.receive returned true')
62
+ assert_equal(ret['foo'], 'bar')
63
+ end
64
+
65
+ def test_all_queues
66
+ drain_test_queue()
67
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname')
68
+ ret = IPCTransit.all_queues()
69
+ assert(ret, 'IPCTransit.all_queues returned true')
70
+ assert(ret['test_qname']['count'] == 1, 'exactly one message in test_qname')
71
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
72
+ assert(ret, 'IPCTransit.receive returned true')
73
+ assert_equal(ret['foo'], 'bar')
74
+ end
75
+ end
76
+
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ipc_transit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dana M. Diederich
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: SysVIPC
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Serverless Message Queue
47
+ email: diederich@gmail.com
48
+ executables:
49
+ - trrecv
50
+ - trsend
51
+ - trlist
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - Rakefile
56
+ - lib/ipc_transit.rb
57
+ - bin/trrecv
58
+ - bin/trsend
59
+ - bin/trlist
60
+ - test/tc_transit_simple.rb
61
+ homepage: http://rubygems.org/gems/ipc_transit
62
+ licenses: []
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 1.8.24
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Serverless, cross-language, fast message queue library
85
+ test_files:
86
+ - test/tc_transit_simple.rb