message_channel 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,82 @@
1
+ require "observer"
2
+ require "json"
3
+
4
+ module MessageChannel
5
+
6
+ class Observer
7
+
8
+ class Agent
9
+ include ::Observable
10
+
11
+ def notify( topic, message )
12
+ changed
13
+ notify_observers( topic, message )
14
+ end
15
+ end
16
+
17
+ def initialize( **options )
18
+ @asyncs = {}
19
+ @awaits = {}
20
+ @queues = {}
21
+ @@Agent ||= Agent.new
22
+ @@Agent.add_observer( self, :action )
23
+ end
24
+
25
+ def action( topic, message )
26
+ items = JSON.parse( message, symbolize_names: true )
27
+ @asyncs.keys.each do |pattern|
28
+ if File.fnmatch( pattern, topic, File::FNM_PATHNAME )
29
+ if action = @asyncs[pattern]
30
+ action.call( topic, items )
31
+ end
32
+ end
33
+ end
34
+ @awaits.keys.each do |queue|
35
+ @awaits[queue].each do |pattern|
36
+ if File.fnmatch( pattern, topic, File::FNM_PATHNAME )
37
+ queue.push( [topic, items] )
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def listen_once( *patterns )
44
+ queue = Queue.new
45
+ @awaits[queue] = patterns
46
+ topic, items = * queue.pop
47
+ @awaits.delete( queue ) rescue nil
48
+ [topic, items]
49
+ end
50
+
51
+ def listen_each( *patterns, &block )
52
+ patterns.each do |pattern|
53
+ @asyncs[pattern] = block
54
+ end
55
+ end
56
+
57
+ def listen( *patterns, &block )
58
+ if block.nil?
59
+ listen_once( *patterns )
60
+ else
61
+ listen_each( *patterns ) do |topic, items|
62
+ block.call( topic, items )
63
+ end
64
+ end
65
+ end
66
+
67
+ def unlisten( **patterns )
68
+ patterns.each do |pattern|
69
+ if action = @asyncs[pattern]
70
+ @asyncs.delete( pattern )
71
+ end
72
+ end
73
+ end
74
+
75
+ def notify( topic, **items )
76
+ @@Agent.notify( topic, items.to_json )
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
@@ -0,0 +1,90 @@
1
+ require "redis"
2
+ require "json"
3
+
4
+ module MessageChannel
5
+ class Redis
6
+
7
+ def initialize( host: nil, port: nil, db: nil )
8
+ @host = host || "127.0.0.1"
9
+ @port = ( port || 6379 ).to_i
10
+ @db = ( db || 0 ).to_i
11
+ @redis = ::Redis.new( host: @host, port: @port, db: @db )
12
+ @threads = {}
13
+ end
14
+
15
+ def listen_once( *patterns )
16
+ queue = Queue.new
17
+ threads = {}
18
+ patterns.each do |pattern|
19
+ threads[pattern] = ::Thread.start(pattern) do |pattern|
20
+ redis = ::Redis.new( host: @host, port: @port, db: @db )
21
+ begin
22
+ redis.psubscribe( pattern ) do |on|
23
+ on.pmessage do |pattern, channel, message|
24
+ items = JSON.parse( message, symbolize_names: true )
25
+ redis.punsubscribe( topic ) rescue nil
26
+ queue.push [channel, items]
27
+ end
28
+ end
29
+ rescue ::Redis::BaseConnectionError => error
30
+ sleep 1
31
+ retry
32
+ ensure
33
+ end
34
+ end
35
+ end
36
+
37
+ topic, items = queue.pop
38
+ patterns.each do |pattern|
39
+ threads[pattern].kill rescue nil
40
+ threads.delete( pattern ) rescue nil
41
+ end
42
+ [topic, items]
43
+ end
44
+
45
+ def listen_each( *patterns, &block )
46
+ patterns.each do |pattern|
47
+ @threads[pattern] = ::Thread.start(pattern) do |pattern|
48
+ redis = ::Redis.new( host: @host, port: @port, db: @db )
49
+ begin
50
+ redis.psubscribe( pattern ) do |on|
51
+ on.pmessage do |pattern, channel, message|
52
+ items = JSON.parse( message, symbolize_names: true )
53
+ block.call( channel, items )
54
+ end
55
+ end
56
+ rescue ::Redis::BaseConnectionError => error
57
+ sleep 1
58
+ retry
59
+ ensure
60
+ redis.punsubscribe( topic ) rescue nil
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def listen( *patterns, &block )
67
+ if block.nil?
68
+ listen_once( *patterns )
69
+ else
70
+ listen_each( *patterns ) do |topic, items|
71
+ block.call( topic, items )
72
+ end
73
+ end
74
+ end
75
+
76
+ def unlisten( **patterns )
77
+ patterns.each do |pattern|
78
+ @threads[pattern].kill rescue nil
79
+ @threads.delete( pattern ) rescue nil
80
+ end
81
+ end
82
+
83
+ def notify( topic, **items )
84
+ @redis.publish( topic, items.to_json )
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
@@ -0,0 +1,3 @@
1
+ module MessageChannel
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'lib/message_channel/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "message_channel"
5
+ spec.version = MessageChannel::VERSION
6
+ spec.authors = ["arimay"]
7
+ spec.email = ["arima.yasuhiro@gmail.com"]
8
+
9
+ spec.summary = %q{ Wrapper library for publish/subscribe pattern. }
10
+ spec.description = %q{ Yet another observer pattern wrapper library via Observable, DRb, MQTT, Redis and Mongo. }
11
+ spec.homepage = "https://github.com/arimay/message_channel"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+ # Specify which files should be added to the gem when it is released.
15
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
16
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "mqtt"
24
+ spec.add_development_dependency "redis"
25
+ spec.add_development_dependency "msgpack"
26
+ spec.add_development_dependency "mongo"
27
+
28
+ spec.add_development_dependency "rake", "~> 12.0"
29
+ spec.add_development_dependency "rspec", "~> 3.0"
30
+ end
@@ -0,0 +1,48 @@
1
+ require "message_channel"
2
+
3
+ ObserverChannel = MessageChannel.new( "observer" )
4
+ DrubyChannel = MessageChannel.new( "druby" )
5
+ MqttChannel = MessageChannel.new( "mqtt" )
6
+ RedisChannel = MessageChannel.new( "redis" )
7
+ MongodbChannel = MessageChannel.new( "mongodb" )
8
+
9
+ Signal.trap( :INT ) do
10
+ exit
11
+ end
12
+
13
+ mask1 = "observer/*"
14
+ ObserverChannel.listen( mask1 ) do |topic, items|
15
+ p [:observer, mask1, topic, items]
16
+ end
17
+
18
+ mask2 = "druby___/*"
19
+ DrubyChannel.listen( mask2 ) do |topic, items|
20
+ p [:druby___, mask2, topic, items]
21
+ end
22
+
23
+ mask3 = "mqtt____/+"
24
+ MqttChannel.listen( mask3 ) do |topic, items|
25
+ p [:mqtt____, mask3, topic, items]
26
+ end
27
+
28
+ mask4 = "redis___/*"
29
+ RedisChannel.listen( mask4 ) do |topic, items|
30
+ p [:redis___, mask4, topic, items]
31
+ end
32
+
33
+ mask5 = "mongodb_/*"
34
+ MongodbChannel.listen( mask5 ) do |topic, items|
35
+ p [:mongodb_, mask5, topic, items]
36
+ end
37
+
38
+ i = 0
39
+ while true
40
+ sleep 1
41
+ i += 1
42
+ now = Time.now.to_s
43
+ ObserverChannel.notify "observer/#{i}", at: now
44
+ DrubyChannel.notify "druby___/#{i}", at: now
45
+ MqttChannel.notify "mqtt____/#{i}", at: now
46
+ RedisChannel.notify "redis___/#{i}", at: now
47
+ MongodbChannel.notify "mongodb_/#{i}", at: now
48
+ end
@@ -0,0 +1,31 @@
1
+
2
+ URI defailt params for message_channel.
3
+
4
+ uri:
5
+ "observer"
6
+ "observer:"
7
+
8
+ "druby"
9
+ "druby:"
10
+ "druby://127.0.0.1"
11
+ "druby://127.0.0.1:8787"
12
+
13
+ "mqtt"
14
+ "mqtt:"
15
+ "mqtt://127.0.0.1"
16
+ "mqtt://127.0.0.1:1883"
17
+
18
+ "redis"
19
+ "redis:"
20
+ "redis://127.0.0.1"
21
+ "redis://127.0.0.1:6379"
22
+ "redis://127.0.0.1:6379/1"
23
+
24
+ "mongodb"
25
+ "mongodb://127.0.0.1"
26
+ "mongodb://127.0.0.1:27017"
27
+ "mongodb://127.0.0.1:27017/test"
28
+ "mongodb://127.0.0.1:27017/test?size=8000"
29
+ "mongodb://127.0.0.1:27017/test?name=_event_queue"
30
+ "mongodb://127.0.0.1:27017/test?size=8000&name=_event_queue"
31
+
@@ -0,0 +1,18 @@
1
+ require "message_channel"
2
+
3
+ p uri = ARGV.shift
4
+ p Channel = MessageChannel.new( uri )
5
+
6
+ Signal.trap( :INT ) do
7
+ exit
8
+ end
9
+
10
+ Channel.listen( "hello" ) do |topic, items|
11
+ p [topic, items]
12
+ end
13
+
14
+ while true
15
+ Channel.notify( "hello", at: Time.now.to_s )
16
+ sleep 1
17
+ end
18
+
@@ -0,0 +1,21 @@
1
+ require "message_channel"
2
+
3
+ p uri = ARGV.shift
4
+ p Channel = MessageChannel.new( uri )
5
+
6
+ Signal.trap( :INT ) do
7
+ exit
8
+ end
9
+
10
+ Thread.start do
11
+ while true
12
+ topic, items = Channel.listen( "hello" )
13
+ p [topic, items]
14
+ end
15
+ end
16
+
17
+ while true
18
+ Channel.notify( "hello", at: Time.now.to_s )
19
+ sleep 1
20
+ end
21
+
@@ -0,0 +1,31 @@
1
+ require "message_channel"
2
+
3
+ p uri = ARGV.shift
4
+ p Channel = MessageChannel.new( uri )
5
+
6
+ Signal.trap( :INT ) do
7
+ exit
8
+ end
9
+
10
+ Channel.listen( "hello", "world" ) do |topic, items|
11
+ p [:async, topic, items]
12
+ end
13
+
14
+ Thread.start do
15
+ while true
16
+ topic, items = Channel.listen( "hello", "world" )
17
+ p [:await, topic, items]
18
+ end
19
+ end
20
+
21
+ Thread.start do
22
+ while true
23
+ Channel.notify( "hello", at: Time.now.to_s )
24
+ sleep 1
25
+ Channel.notify( "world", at: Time.now.to_s )
26
+ sleep 1
27
+ end
28
+ end
29
+
30
+ sleep
31
+
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: message_channel
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - arimay
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-08-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mqtt
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: redis
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: msgpack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
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: mongo
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
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: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '12.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '12.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: " Yet another observer pattern wrapper library via Observable, DRb, MQTT,
98
+ Redis and Mongo. "
99
+ email:
100
+ - arima.yasuhiro@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - README.adoc
110
+ - README.ja.adoc
111
+ - Rakefile
112
+ - bin/console
113
+ - bin/setup
114
+ - lib/message_channel.rb
115
+ - lib/message_channel/base.rb
116
+ - lib/message_channel/druby.rb
117
+ - lib/message_channel/mongodb.rb
118
+ - lib/message_channel/mqtt.rb
119
+ - lib/message_channel/observer.rb
120
+ - lib/message_channel/redis.rb
121
+ - lib/message_channel/version.rb
122
+ - message_channel.gemspec
123
+ - sample/all_1.rb
124
+ - sample/sample.txt
125
+ - sample/sample_1.rb
126
+ - sample/sample_2.rb
127
+ - sample/sample_3.rb
128
+ homepage: https://github.com/arimay/message_channel
129
+ licenses: []
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 2.3.0
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubygems_version: 3.1.4
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: Wrapper library for publish/subscribe pattern.
150
+ test_files: []