nats 0.3.12

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.
@@ -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
+