Sutto-perennial 0.2.3.7 → 0.2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -40,15 +40,21 @@ module Perennial
40
40
  @dispatch_queue ||= []
41
41
  end
42
42
 
43
+ def dispatching?
44
+ @dispatching ||= false
45
+ end
46
+
43
47
  # Dispatch an 'event' with a given name to the handlers
44
48
  # registered on the current class. Used as a nicer way of defining
45
49
  # behaviours that should occur under a given set of circumstances.
46
50
  # == Params
47
51
  # +name+: The name of the current event
48
52
  # +opts+: an optional hash of options to pass
49
- def dispatch(name, opts = {}, from_queue = false)
50
- if dispatch_queue.empty? || from_queue
53
+ def dispatch(name, opts = {})
54
+ if !dispatching?
51
55
  Logger.debug "Dispatching #{name} event (#{dispatch_queue.size} queued - on #{self.class.name})"
56
+ # Add ourselves to the queue
57
+ @dispatching = true
52
58
  begin
53
59
  # The full handler name is the method we call given it exists.
54
60
  full_handler_name = :"handle_#{name.to_s.underscore}"
@@ -77,11 +83,11 @@ module Perennial
77
83
  rescue Exception => e
78
84
  Logger.log_exception(e)
79
85
  end
80
- dispatch_queue.shift if from_queue
81
- dispatch(*dispatch_queue.first) unless dispatch_queue.empty?
86
+ @dispatching = false
87
+ dispatch(*@dispatch_queue.shift) unless dispatch_queue.empty?
82
88
  else
83
89
  Logger.debug "Adding #{name} event to the end of the queue (on #{self.class.name})"
84
- dispatch_queue << [name, opts, true]
90
+ dispatch_queue << [name, opts]
85
91
  end
86
92
  end
87
93
 
@@ -114,6 +120,7 @@ module Perennial
114
120
  # Handlers are called in the order they are registered.
115
121
  def register_handler(handler)
116
122
  unless handler.blank? || !handler.respond_to?(:handle)
123
+ handler.registered = true if handler.respond_to?(:registered=)
117
124
  Dispatchable.handler_mapping[self] << handler
118
125
  end
119
126
  end
@@ -0,0 +1,161 @@
1
+ module Perennial
2
+ # A ninja hash. Like OpenStruct, but better
3
+ class Nash
4
+
5
+ attr_reader :table
6
+
7
+ def initialize(initial = {})
8
+ @table = {}
9
+ initial.to_hash.each_pair { |k,v| self[k] = v }
10
+ end
11
+
12
+ def [](key)
13
+ @table[real_key(key)]
14
+ end
15
+
16
+ def []=(key, *values)
17
+ @table.send(:[]=, real_key(key), *values)
18
+ end
19
+
20
+ def respond_to?(name, rec = nil)
21
+ true
22
+ end
23
+
24
+ def id
25
+ self.has_key?(:id) ? self.id : super
26
+ end
27
+
28
+ def dup
29
+ Nash.new(self.table)
30
+ end
31
+
32
+ def to_hash
33
+ @table.dup
34
+ end
35
+
36
+ def keys
37
+ @table.keys
38
+ end
39
+
40
+ def values
41
+ @table.values
42
+ end
43
+
44
+ def has_key?(key)
45
+ @table.has_key? real_key(key)
46
+ end
47
+
48
+ def has_value?(value)
49
+ @table.has_value? value
50
+ end
51
+
52
+ def each_pair
53
+ @table.each_pair { |k, v| yield k, v }
54
+ end
55
+
56
+ def each_key
57
+ @table.each_key { |k| yield k }
58
+ end
59
+
60
+ def each_value
61
+ @table.each_value { |v| yield v }
62
+ end
63
+
64
+ def delete(key)
65
+ @table.delete(real_key(key))
66
+ end
67
+
68
+ def merge!(hash_or_nash)
69
+ hash_or_nash.to_hash.each_pair do |k, v|
70
+ self[k] = v
71
+ end
72
+ return self
73
+ end
74
+
75
+ def merge(hash_or_nash)
76
+ dup.merge! hash_or_nash
77
+ end
78
+
79
+ def reverse_merge!(hash_or_nash)
80
+ replace Nash.new(hash_or_nash).merge!(self)
81
+ end
82
+
83
+ def reverse_merge(hash_or_nash)
84
+ dup.reverse_merge(hash_or_nash)
85
+ end
86
+
87
+ def replace(nash)
88
+ if nash.is_a?(self.class)
89
+ @table = nash.table
90
+ else
91
+ @table = {}
92
+ nash.to_hash.each_pair { |k, v| self[k] = v }
93
+ end
94
+ return self
95
+ end
96
+
97
+ def blank?
98
+ @table.blank?
99
+ end
100
+
101
+ def present?
102
+ @table.present?
103
+ end
104
+
105
+ def inspect
106
+ str = ""
107
+ if first = Thread.current[:inspect_stack].nil?
108
+ Thread.current[:inspect_stack] = [self]
109
+ str = _inspect
110
+ Thread.current[:inspect_stack] = nil
111
+ else
112
+ if Thread.current[:inspect_stack].include?(self)
113
+ return "..."
114
+ else
115
+ Thread.current[:inspect_stack] << self
116
+ str = _inspect
117
+ Thread.current[:inspect_stack].pop
118
+ end
119
+ end
120
+ return str
121
+ end
122
+
123
+ def _inspect
124
+ str = "#<Perennial::Nash:#{(object_id * 2).to_s(16)}"
125
+ if !blank?
126
+ str << " "
127
+ str << table.map { |k, v| "#{k}=#{v.inspect}" }.join(", ")
128
+ end
129
+ str << ">"
130
+ return str
131
+ end
132
+
133
+ protected
134
+
135
+ def method_missing(name, *args, &blk)
136
+ name = name.to_s
137
+ case name.to_s[-1]
138
+ when ??
139
+ self[name[0..-2]].present?
140
+ when ?=
141
+ send(:[]=, real_key(name[0..-2]), *args)
142
+ when ?!
143
+ self[name[0..-2]] = self.class.new
144
+ else
145
+ self[name]
146
+ end
147
+ end
148
+
149
+ def real_key(name)
150
+ name.to_sym
151
+ end
152
+
153
+
154
+ end
155
+ end
156
+
157
+ class Hash
158
+ def to_nash
159
+ Perennial::Nash.new(self)
160
+ end
161
+ end
@@ -4,7 +4,8 @@ module Perennial
4
4
  class Settings
5
5
 
6
6
  cattr_accessor :configuration, :log_level, :verbose, :daemon
7
-
7
+
8
+ @@configuration = Perennial::Nash.new
8
9
  @@verbose = false
9
10
  @@log_level = :info
10
11
  @@daemon = false
@@ -63,18 +64,13 @@ module Perennial
63
64
  end
64
65
 
65
66
  def setup!(options = {})
66
- @@configuration = {}
67
+ @@configuration ||= Perennial::Nash.new
67
68
  settings_file = self.default_settings_path
68
69
  if File.exist?(settings_file)
69
70
  loaded_yaml = YAML.load(File.read(settings_file))
70
71
  @@configuration.merge!(lookup_settings_from(loaded_yaml))
71
72
  end
72
73
  @@configuration.merge! options
73
- @@configuration.symbolize_keys!
74
- # Generate a module
75
- mod = generate_settings_accessor_mixin
76
- extend mod
77
- include mod
78
74
  @@setup = true
79
75
  end
80
76
 
@@ -89,36 +85,21 @@ module Perennial
89
85
  return true
90
86
  end
91
87
 
92
- def [](key)
93
- self.setup
94
- return self.configuration[key.to_sym]
88
+ def to_hash
89
+ @@configuration.to_hash
95
90
  end
96
91
 
97
- def []=(key, value)
98
- self.setup
99
- self.configuration[key.to_sym] = value
100
- return value
92
+ def method_missing(name, *args, &blk)
93
+ self.setup! unless self.setup?
94
+ @@configuration.send(name, *args, &blk)
101
95
  end
102
96
 
103
- def to_hash
104
- self.configuration.dup
97
+ def respond_to?(name, rec = nil)
98
+ true
105
99
  end
106
100
 
107
101
  protected
108
102
 
109
- def generate_settings_accessor_mixin
110
- Module.new do
111
- Settings.configuration.keys.each do |k|
112
- define_method(k) do
113
- return Settings.configuration[k]
114
- end
115
- define_method("#{k}=") do |val|
116
- Settings.configuration[k] = val
117
- end
118
- end
119
- end
120
- end
121
-
122
103
  def lookup_settings_from(settings_hash)
123
104
  lookup_key_path.inject(settings_hash) do |h, k|
124
105
  h[k.to_s] ||= {}
@@ -126,6 +107,15 @@ module Perennial
126
107
  end
127
108
 
128
109
  end
110
+
111
+ def method_missing(name, *args, &blk)
112
+ self.class.setup
113
+ @@configuration.send(name, *args, &blk)
114
+ end
115
+
116
+ def respond_to?(name, rec = nil)
117
+ true
118
+ end
129
119
 
130
120
  end
131
121
  end
data/lib/perennial.rb CHANGED
@@ -9,9 +9,9 @@ require 'perennial/exceptions'
9
9
 
10
10
  module Perennial
11
11
 
12
- VERSION = "0.2.3.7"
12
+ VERSION = "0.2.4.0"
13
13
 
14
- has_library :dispatchable, :hookable, :loader, :logger,
14
+ has_library :dispatchable, :hookable, :loader, :logger, :nash,
15
15
  :loggable, :manifest, :settings, :argument_parser,
16
16
  :option_parser, :application, :generator, :daemon,
17
17
  :delegateable
@@ -22,6 +22,43 @@ class DispatchableTest < Test::Unit::TestCase
22
22
 
23
23
  class ExampleHandlerB < ExampleHandlerA; end
24
24
 
25
+ class ExampleHandlerC
26
+ attr_reader :max_count, :messages, :call_stack
27
+ def initialize(d)
28
+ @messages = []
29
+ @max_count = 0
30
+ @current_count = 0
31
+ @dispatcher = d
32
+ @call_stack = []
33
+ end
34
+
35
+ def handle(name, opts)
36
+ @call_stack << "start-#{name}"
37
+ @current_count += 1
38
+ @messages << name
39
+ @dispatcher.dispatch(:b) if name == :a
40
+ @max_count = @current_count if @current_count > @max_count
41
+ @current_count -= 1
42
+ @call_stack << "end-#{name}"
43
+ end
44
+
45
+ end
46
+
47
+ class RegisterableHandler
48
+
49
+ def registered=(value)
50
+ @registered = value
51
+ end
52
+
53
+ def registered?
54
+ @registered ||= false
55
+ end
56
+
57
+ def handle(name, opts = {})
58
+ end
59
+
60
+ end
61
+
25
62
  context 'marking a class as dispatchable' do
26
63
 
27
64
  setup do
@@ -126,4 +163,50 @@ class DispatchableTest < Test::Unit::TestCase
126
163
 
127
164
  end
128
165
 
166
+ context 'dispatching nested events' do
167
+
168
+ setup do
169
+ @dispatcher = class_via(ExampleDispatcher).new
170
+ @handler = ExampleHandlerC.new(@dispatcher)
171
+ @dispatcher.class.register_handler @handler
172
+ @dispatcher.dispatch :a
173
+ end
174
+
175
+ should 'call them in the correct order' do
176
+ assert_equal [:a, :b], @handler.messages
177
+ end
178
+
179
+ should 'only call 1 dispatch at a time' do
180
+ assert_equal 1, @handler.max_count
181
+ end
182
+
183
+ should 'finish a before dispatching b' do
184
+ assert_equal ["start-a", "end-a", "start-b", "end-b"], @handler.call_stack
185
+ end
186
+
187
+ end
188
+
189
+ context 'registering handlers' do
190
+
191
+ setup do
192
+ @dispatcher = class_via(ExampleDispatcher).new
193
+ @handler = class_via(RegisterableHandler).new
194
+ end
195
+
196
+ should 'default to not being registered' do
197
+ assert !@handler.registered?
198
+ end
199
+
200
+ should 'set registered on register_handler' do
201
+ @dispatcher.class.register_handler @handler
202
+ assert @handler.registered?
203
+ end
204
+
205
+ should 'call registered= on the handler' do
206
+ mock(@handler).registered = true
207
+ @dispatcher.class.register_handler @handler
208
+ end
209
+
210
+ end
211
+
129
212
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Sutto-perennial
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3.7
4
+ version: 0.2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darcy Laycock
@@ -53,6 +53,7 @@ files:
53
53
  - lib/perennial/loggable.rb
54
54
  - lib/perennial/logger.rb
55
55
  - lib/perennial/manifest.rb
56
+ - lib/perennial/nash.rb
56
57
  - lib/perennial/option_parser.rb
57
58
  - lib/perennial/settings.rb
58
59
  - lib/perennial.rb