nats 0.3.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,116 @@
1
+ #--
2
+ #
3
+ # Sublist implementation for a publish-subscribe system.
4
+ # This container class holds subscriptions and matches
5
+ # candidate subjects to those subscriptions.
6
+ # Certain wildcards are supported for subscriptions.
7
+ # '*' will match any given token at any level.
8
+ # '>' will match all subsequent tokens.
9
+ #--
10
+ # See included test for example usage:
11
+ ##
12
+
13
+ class Sublist #:nodoc:
14
+ PWC = '*'.freeze
15
+ FWC = '>'.freeze
16
+ CACHE_SIZE = 4096
17
+
18
+ attr_reader :count
19
+
20
+ SublistNode = Struct.new(:leaf_nodes, :next_level)
21
+ SublistLevel = Struct.new(:nodes, :pwc, :fwc)
22
+
23
+ def initialize(options = {})
24
+ @count = 0
25
+ @results = []
26
+ @root = SublistLevel.new({})
27
+ @cache = {}
28
+ end
29
+
30
+ # Ruby is a great language to make selective trade offs of space versus time.
31
+ # We do that here with a low tech front end cache. The cache holds results
32
+ # until it is exhausted or if the instance inserts or removes a subscription.
33
+ # The assumption is that the cache is best suited for high speed matching,
34
+ # and that once it is cleared out it will naturally fill with the high speed
35
+ # matches. This can obviously be improved with a smarter LRU structure that
36
+ # does not need to completely go away when a remove happens..
37
+ #
38
+ # front end caching is on by default, but we can turn it off here if needed
39
+
40
+ def disable_cache; @cache = nil; end
41
+ def enable_cache; @cache ||= {}; end
42
+ def clear_cache; @cache = {} if @cache; end
43
+
44
+ # Insert a subscriber into the sublist for the given subject.
45
+ def insert(subject, subscriber)
46
+ # TODO - validate subject as correct.
47
+ level, tokens = @root, subject.split('.')
48
+ for token in tokens
49
+ # This is slightly slower than direct if statements, but looks cleaner.
50
+ case token
51
+ when FWC then node = (level.fwc || (level.fwc = SublistNode.new([])))
52
+ when PWC then node = (level.pwc || (level.pwc = SublistNode.new([])))
53
+ else node = ((level.nodes[token]) || (level.nodes[token] = SublistNode.new([])))
54
+ end
55
+ level = (node.next_level || (node.next_level = SublistLevel.new({})))
56
+ end
57
+ node.leaf_nodes.push(subscriber)
58
+ @count += 1
59
+ clear_cache # Clear the cache
60
+ end
61
+
62
+ # Remove a given subscriber from the sublist for the given subject.
63
+ def remove(subject, subscriber)
64
+ # TODO: implement (remember cache and count cleanup if applicable)
65
+ # Reference counts and GC for long empty tree.
66
+ level, tokens = @root, subject.split('.')
67
+ for token in tokens
68
+ next unless level
69
+ case token
70
+ when FWC then node = level.fwc
71
+ when PWC then node = level.pwc
72
+ else node = level.nodes[token]
73
+ end
74
+ level = node.next_level
75
+ end
76
+ # This could be expensize if a large number of subscribers exist.
77
+ node.leaf_nodes.delete(subscriber) if (node && node.leaf_nodes)
78
+ clear_cache # Clear the cache
79
+ end
80
+
81
+ # Match a subject to all subscribers, return the array of matches.
82
+ def match(subject)
83
+ return @cache[subject] if (@cache && @cache[subject])
84
+ tokens = subject.split('.')
85
+ @results.clear
86
+ matchAll(@root, tokens)
87
+ # FIXME: This is too low tech, will revisit when needed.
88
+ if @cache
89
+ clear_cache if @cache.size > CACHE_SIZE
90
+ @cache[subject] = Array.new(@results).freeze # Avoid tampering of copy
91
+ end
92
+ @results
93
+ end
94
+
95
+ private
96
+
97
+ def matchAll(level, tokens)
98
+ node, pwc = nil, nil # Define for scope
99
+ i, ts = 0, tokens.size
100
+ while (i < ts) do
101
+ return if level == nil
102
+ # Handle a full wildcard here by adding all of the subscribers.
103
+ @results.concat(level.fwc.leaf_nodes) if level.fwc
104
+ # Handle an internal partial wildcard by branching recursively
105
+ lpwc = level.pwc
106
+ matchAll(lpwc.next_level, tokens[i+1, ts]) if lpwc
107
+ node, pwc = level.nodes[tokens[i]], lpwc
108
+ #level = node.next_level if node
109
+ level = node ? node.next_level : nil
110
+ i += 1
111
+ end
112
+ @results.concat(pwc.leaf_nodes) if pwc
113
+ @results.concat(node.leaf_nodes) if node
114
+ end
115
+
116
+ end
data/nats.gemspec ADDED
@@ -0,0 +1,46 @@
1
+
2
+
3
+ lib = File.expand_path('../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ require 'nats/server/const.rb'
7
+
8
+ spec = Gem::Specification.new do |s|
9
+ s.name = 'nats'
10
+ s.version = NATSD::VERSION
11
+ s.date = '2010-11-20'
12
+ s.summary = 'Simple Publish-Subscribe Messaging System'
13
+ s.homepage = "http://github.com/derekcollison/nats"
14
+ s.description = "A lightweight, fast, publish-subscribe messaging system."
15
+ s.has_rdoc = true
16
+
17
+ s.authors = ["Derek Collison"]
18
+ s.email = ["derek.collison@gmail.com"]
19
+
20
+ s.add_dependency('eventmachine', '>= 0.12.10')
21
+ s.add_dependency('yajl-ruby', '>= 0.7.8')
22
+ s.add_dependency('daemons', '>= 1.1.0')
23
+
24
+ s.require_paths = ['lib']
25
+ s.bindir = 'bin'
26
+ s.executables = [NATSD::APP_NAME, 'nats-pub', 'nats-sub']
27
+
28
+ s.files = %w[
29
+ COPYING
30
+ README.md
31
+ nats.gemspec
32
+ Rakefile
33
+ bin/nats-server
34
+ bin/nats-sub
35
+ bin/nats-pub
36
+ lib/nats/client.rb
37
+ lib/nats/ext/bytesize.rb
38
+ lib/nats/ext/em.rb
39
+ lib/nats/ext/json.rb
40
+ lib/nats/server.rb
41
+ lib/nats/server/options.rb
42
+ lib/nats/server/sublist.rb
43
+ lib/nats/server/const.rb
44
+ ]
45
+
46
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nats
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 12
9
+ version: 0.3.12
10
+ platform: ruby
11
+ authors:
12
+ - Derek Collison
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-20 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: eventmachine
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 12
31
+ - 10
32
+ version: 0.12.10
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: yajl-ruby
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 7
46
+ - 8
47
+ version: 0.7.8
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: daemons
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 1
60
+ - 1
61
+ - 0
62
+ version: 1.1.0
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ description: A lightweight, fast, publish-subscribe messaging system.
66
+ email:
67
+ - derek.collison@gmail.com
68
+ executables:
69
+ - nats-server
70
+ - nats-pub
71
+ - nats-sub
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - COPYING
78
+ - README.md
79
+ - nats.gemspec
80
+ - Rakefile
81
+ - bin/nats-server
82
+ - bin/nats-sub
83
+ - bin/nats-pub
84
+ - lib/nats/client.rb
85
+ - lib/nats/ext/bytesize.rb
86
+ - lib/nats/ext/em.rb
87
+ - lib/nats/ext/json.rb
88
+ - lib/nats/server.rb
89
+ - lib/nats/server/options.rb
90
+ - lib/nats/server/sublist.rb
91
+ - lib/nats/server/const.rb
92
+ has_rdoc: true
93
+ homepage: http://github.com/derekcollison/nats
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirements: []
118
+
119
+ rubyforge_project:
120
+ rubygems_version: 1.3.7
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: Simple Publish-Subscribe Messaging System
124
+ test_files: []
125
+