carnivore 0.1.10 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +9 -0
- data/README.md +2 -2
- data/carnivore.gemspec +2 -2
- data/lib/carnivore/autoloader.rb +13 -0
- data/lib/carnivore/callback.rb +8 -3
- data/lib/carnivore/config.rb +4 -3
- data/lib/carnivore/container.rb +1 -1
- data/lib/carnivore/errors.rb +7 -0
- data/lib/carnivore/message.rb +15 -8
- data/lib/carnivore/runner.rb +26 -7
- data/lib/carnivore/source.rb +154 -44
- data/lib/carnivore/source_container.rb +30 -0
- data/lib/carnivore/spec_helper.rb +60 -0
- data/lib/carnivore/supervisor.rb +53 -0
- data/lib/carnivore/utils/logging.rb +28 -0
- data/lib/carnivore/utils/message_registry.rb +41 -0
- data/lib/carnivore/utils/params.rb +40 -0
- data/lib/carnivore/utils.rb +5 -39
- data/lib/carnivore/version.rb +3 -1
- data/lib/carnivore.rb +4 -1
- metadata +13 -16
- data/Gemfile +0 -5
- data/examples/test_block.rb +0 -10
- data/examples/test_class.rb +0 -20
- data/examples/test_http.rb +0 -10
- data/test/spec.rb +0 -11
- data/test/specs/config.rb +0 -31
- data/test/specs/container.rb +0 -13
- data/test/specs/message.rb +0 -29
- data/test/specs/source.rb +0 -82
- data/test/specs/utils.rb +0 -47
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# v0.2.0
|
2
|
+
* Remove `fog` from dependency list
|
3
|
+
* Add common spec helper for testing
|
4
|
+
* Only start sources if callbacks are defined
|
5
|
+
* Add custom supervisor with isolated registry
|
6
|
+
* Include auto-restart support
|
7
|
+
* Loop local messages internally instead of transmit/retrieve loop
|
8
|
+
* Use auto loading to clean things up
|
9
|
+
|
1
10
|
# v0.1.10
|
2
11
|
* Remove builtin sources
|
3
12
|
* Allow optional auto-symbolize
|
data/README.md
CHANGED
@@ -30,7 +30,7 @@ Under the hood, callbacks are built into `Carnivore::Callback`
|
|
30
30
|
instances. This class can be subclassed and provided directly
|
31
31
|
instead of a simple block. This has the added bonus of being
|
32
32
|
able to define the number of worker instances to be created
|
33
|
-
for the callback (blocks
|
33
|
+
for the callback (blocks only get 1):
|
34
34
|
|
35
35
|
```ruby
|
36
36
|
require 'carnivore'
|
@@ -56,7 +56,7 @@ end.start!
|
|
56
56
|
|
57
57
|
### Block execution
|
58
58
|
|
59
|
-
It is important to note that when providing blocks
|
59
|
+
It is important to note that when providing blocks they will
|
60
60
|
lose all reference to the scope in which they are defined. This
|
61
61
|
is due to how `Callback` is implemented and is by design. Simply
|
62
62
|
ensure that blocks are fully autonomous and everything will be
|
data/carnivore.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.homepage = 'https://github.com/carnivore-rb/carnivore'
|
10
10
|
s.description = 'Message processing helper'
|
11
11
|
s.require_path = 'lib'
|
12
|
-
s.add_dependency 'fog'
|
13
12
|
s.add_dependency 'celluloid'
|
14
13
|
s.add_dependency 'mixlib-config'
|
15
|
-
s.
|
14
|
+
s.add_dependency 'multi_json'
|
15
|
+
s.files = Dir['lib/**/*'] + %w(carnivore.gemspec README.md CHANGELOG.md)
|
16
16
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Register all base autoloading requirements
|
2
|
+
|
3
|
+
module Carnivore
|
4
|
+
autoload :Config, 'carnivore/config'
|
5
|
+
autoload :Callback, 'carnivore/callback'
|
6
|
+
autoload :Container, 'carnivore/container'
|
7
|
+
autoload :Error, 'carnivore/errors'
|
8
|
+
autoload :Message, 'carnivore/message'
|
9
|
+
autoload :Source, 'carnivore/source'
|
10
|
+
autoload :Supervisor, 'carnivore/supervisor'
|
11
|
+
autoload :Utils, 'carnivore/utils'
|
12
|
+
autoload :Version, 'carnivore/version'
|
13
|
+
end
|
data/lib/carnivore/callback.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'carnivore
|
1
|
+
require 'carnivore'
|
2
2
|
|
3
3
|
module Carnivore
|
4
4
|
class Callback
|
@@ -9,10 +9,14 @@ module Carnivore
|
|
9
9
|
end
|
10
10
|
|
11
11
|
include Celluloid
|
12
|
-
include Utils::Logging
|
12
|
+
include Carnivore::Utils::Logging
|
13
13
|
|
14
14
|
attr_reader :name
|
15
15
|
|
16
|
+
# name:: Name of the callback
|
17
|
+
# block:: Optional `Proc` to define the callback behavior
|
18
|
+
# Creates a new callback. Optional block to define callback
|
19
|
+
# behavior must be passed as a `Proc` instance, not a block.
|
16
20
|
def initialize(name, block=nil)
|
17
21
|
@name = name
|
18
22
|
if(block.nil? && self.class == Callback)
|
@@ -42,9 +46,10 @@ module Carnivore
|
|
42
46
|
# Pass message to registered callbacks
|
43
47
|
def call(message)
|
44
48
|
if(valid?(message))
|
49
|
+
debug ">> Received message is valid for this callback (#{message})"
|
45
50
|
execute(message)
|
46
51
|
else
|
47
|
-
debug
|
52
|
+
debug "Invalid message for this callback #{message})"
|
48
53
|
end
|
49
54
|
rescue => e
|
50
55
|
error "[callback: #{self}, source: #{message[:source]}, message: #{message[:message].object_id}]: #{e.class} - #{e}"
|
data/lib/carnivore/config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'mixlib/config'
|
3
|
+
require 'carnivore'
|
3
4
|
|
4
5
|
module Carnivore
|
5
6
|
class Config
|
@@ -8,6 +9,8 @@ module Carnivore
|
|
8
9
|
|
9
10
|
class << self
|
10
11
|
|
12
|
+
# v:: Boolean value
|
13
|
+
# Set/get automatic symbolization of hash keys
|
11
14
|
def auto_symbolize(v=nil)
|
12
15
|
unless(v.nil?)
|
13
16
|
@hash_symbolizer = !!v
|
@@ -51,9 +54,7 @@ module Carnivore
|
|
51
54
|
# Config.get(:other_app, :port) => nil
|
52
55
|
# Config.get(:my_app, :mail, :server) => nil
|
53
56
|
def get(*ary)
|
54
|
-
value =
|
55
|
-
memo[key.to_s] || memo[key.to_sym] || break
|
56
|
-
end
|
57
|
+
value = Carnivore::Utils.retrieve(self, *ary)
|
57
58
|
if(value.is_a?(Hash) && auto_symbolize)
|
58
59
|
Carnivore::Utils.symbolize_hash(value)
|
59
60
|
else
|
data/lib/carnivore/container.rb
CHANGED
data/lib/carnivore/message.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'carnivore
|
1
|
+
require 'carnivore'
|
2
2
|
|
3
3
|
module Carnivore
|
4
4
|
class Message
|
@@ -12,22 +12,29 @@ module Carnivore
|
|
12
12
|
@args = args.dup
|
13
13
|
end
|
14
14
|
|
15
|
+
# Helper method to return keys available from `args`
|
16
|
+
def keys
|
17
|
+
args.keys
|
18
|
+
end
|
19
|
+
|
20
|
+
# k:: key
|
21
|
+
# Accessor into message
|
15
22
|
def [](k)
|
16
|
-
|
17
|
-
Celluloid::Actor[@args[:source]]
|
18
|
-
else
|
19
|
-
@args[k.to_sym] || @args[k.to_s]
|
20
|
-
end
|
23
|
+
args[k.to_sym] || args[k.to_s]
|
21
24
|
end
|
22
25
|
|
26
|
+
# args:: Arguments
|
27
|
+
# Confirm message was received on source
|
23
28
|
def confirm!(*args)
|
24
|
-
self[:source].confirm(*([self] + args))
|
29
|
+
self[:source].confirm(*([self] + args).flatten(1).compact)
|
25
30
|
end
|
26
31
|
|
32
|
+
# Formatted inspection string
|
27
33
|
def inspect
|
28
|
-
"<Carnivore::Message[#{self.object_id}] @args=#{args}>"
|
34
|
+
"<Carnivore::Message[#{self.object_id}] @args=#{args.inspect}>"
|
29
35
|
end
|
30
36
|
|
37
|
+
# String representation
|
31
38
|
def to_s
|
32
39
|
"<Carnivore::Message:#{self.object_id}>"
|
33
40
|
end
|
data/lib/carnivore/runner.rb
CHANGED
@@ -1,29 +1,48 @@
|
|
1
|
-
require '
|
2
|
-
require 'carnivore/config'
|
3
|
-
require 'carnivore/source'
|
4
|
-
require 'carnivore/container'
|
1
|
+
require 'carnivore/autoloader'
|
5
2
|
|
6
3
|
module Carnivore
|
7
4
|
class << self
|
5
|
+
|
6
|
+
# block:: Block of configuration
|
7
|
+
# Add configuration to Carnivore
|
8
8
|
def configure(&block)
|
9
9
|
mod = Container.new
|
10
10
|
mod.instance_exec(mod, &block)
|
11
11
|
self
|
12
12
|
end
|
13
13
|
|
14
|
+
# Start carnivore
|
14
15
|
def start!
|
15
16
|
supervisor = nil
|
16
17
|
begin
|
17
18
|
require 'carnivore/supervisor'
|
18
|
-
supervisor = Carnivore::Supervisor.
|
19
|
+
supervisor = Carnivore::Supervisor.build!
|
19
20
|
Source.sources.each do |source|
|
21
|
+
source.klass.reset_comms!
|
20
22
|
supervisor.supervise_as(
|
21
23
|
source.source_hash[:name],
|
22
24
|
source.klass,
|
23
|
-
source.source_hash
|
25
|
+
source.source_hash.dup
|
24
26
|
)
|
25
27
|
end
|
26
|
-
|
28
|
+
loop do
|
29
|
+
# We do a sleep loop so we can periodically check on the
|
30
|
+
# supervisor and ensure it is still alive. If it has died,
|
31
|
+
# raise exception to allow cleanup and restart attempt
|
32
|
+
sleep Carnivore::Config.get(:carnivore, :supervisor, :poll) || 5 while supervisor.alive?
|
33
|
+
Celluloid::Logger.error 'Carnivore supervisor has died!'
|
34
|
+
raise Carnivore::Error::DeadSupervisor.new
|
35
|
+
end
|
36
|
+
rescue Carnivore::Error::DeadSupervisor
|
37
|
+
Celluloid::Logger.warn "Received dead supervisor exception. Attempting to restart."
|
38
|
+
begin
|
39
|
+
supervisor.terminate
|
40
|
+
rescue => e
|
41
|
+
Celluloid::Logger.debug "Exception raised during supervisor termination (restart cleanup): #{e}"
|
42
|
+
end
|
43
|
+
Celluloid::Logger.debug "Pausing restart for 10 seconds to prevent restart thrashing cycles"
|
44
|
+
sleep 10
|
45
|
+
retry
|
27
46
|
rescue Exception => e
|
28
47
|
supervisor.terminate
|
29
48
|
# Gracefully shut down
|
data/lib/carnivore/source.rb
CHANGED
@@ -1,31 +1,11 @@
|
|
1
|
+
require 'digest/sha2'
|
1
2
|
require 'celluloid'
|
2
|
-
require 'carnivore
|
3
|
-
require 'carnivore/callback'
|
4
|
-
require 'carnivore/message'
|
3
|
+
require 'carnivore'
|
5
4
|
|
6
5
|
module Carnivore
|
7
6
|
class Source
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
attr_reader :klass
|
12
|
-
attr_reader :source_hash
|
13
|
-
|
14
|
-
# class_name:: Name of source class
|
15
|
-
# args:: argument hash to pass to source instance
|
16
|
-
def initialize(class_name, args={})
|
17
|
-
@klass = class_name
|
18
|
-
@source_hash = args || {}
|
19
|
-
@source_hash[:callbacks] = {}
|
20
|
-
end
|
21
|
-
|
22
|
-
# name:: Name of callback
|
23
|
-
# klass:: Class of callback (optional)
|
24
|
-
# Add a callback to a source via Class or block
|
25
|
-
def add_callback(name, klass=nil, &block)
|
26
|
-
@source_hash[:callbacks][name] = klass || block
|
27
|
-
end
|
28
|
-
end
|
8
|
+
autoload :SourceContainer, 'carnivore/source_container'
|
29
9
|
|
30
10
|
class << self
|
31
11
|
|
@@ -48,12 +28,17 @@ module Carnivore
|
|
48
28
|
inst
|
49
29
|
end
|
50
30
|
|
31
|
+
# type:: Symbol of type of source
|
32
|
+
# require_path:: Path to feed to `require`
|
33
|
+
# Registers a source
|
51
34
|
def provide(type, require_path)
|
52
35
|
@source_klass ||= {}
|
53
36
|
@source_klass[type.to_sym] = require_path
|
54
37
|
true
|
55
38
|
end
|
56
39
|
|
40
|
+
# type: Symbol of source type
|
41
|
+
# Returns register path for given type of source
|
57
42
|
def require_path(type)
|
58
43
|
@source_klass ||= {}
|
59
44
|
@source_klass[type.to_sym]
|
@@ -82,6 +67,19 @@ module Carnivore
|
|
82
67
|
def sources
|
83
68
|
@sources ? @sources.values : []
|
84
69
|
end
|
70
|
+
|
71
|
+
def reset_comms!
|
72
|
+
self.class_eval do
|
73
|
+
unless(method_defined?(:reset_communications?))
|
74
|
+
alias_method :custom_transmit, :transmit
|
75
|
+
alias_method :transmit, :_transmit
|
76
|
+
def reset_communications?
|
77
|
+
true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
85
83
|
end
|
86
84
|
|
87
85
|
include Celluloid
|
@@ -93,14 +91,22 @@ module Carnivore
|
|
93
91
|
attr_reader :auto_process
|
94
92
|
attr_reader :run_process
|
95
93
|
attr_reader :callback_supervisor
|
94
|
+
attr_reader :message_registry
|
95
|
+
attr_reader :message_loop
|
96
|
+
attr_reader :processing
|
96
97
|
|
97
98
|
def initialize(args={})
|
98
99
|
@callbacks = []
|
100
|
+
@message_loop = []
|
99
101
|
@callback_names = {}
|
100
102
|
@auto_process = args.fetch(:auto_process, true)
|
101
|
-
@run_process =
|
103
|
+
@run_process = true
|
102
104
|
@auto_confirm = !!args[:auto_confirm]
|
103
|
-
@callback_supervisor =
|
105
|
+
@callback_supervisor = Carnivore::Supervisor.create!.last
|
106
|
+
if(args[:prevent_duplicates])
|
107
|
+
init_registry
|
108
|
+
end
|
109
|
+
@processing = false
|
104
110
|
@name = args[:name] || Celluloid.uuid
|
105
111
|
if(args[:callbacks])
|
106
112
|
args[:callbacks].each do |name, block|
|
@@ -109,45 +115,72 @@ module Carnivore
|
|
109
115
|
end
|
110
116
|
setup(args)
|
111
117
|
connect
|
112
|
-
|
118
|
+
if(auto_process && !callbacks.empty?)
|
119
|
+
async.process
|
120
|
+
end
|
113
121
|
rescue => e
|
114
122
|
debug "Failed to initialize: #{self} - #{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
115
123
|
raise
|
116
124
|
end
|
117
125
|
|
126
|
+
# Ensure we cleanup our internal supervisor before bailing out
|
127
|
+
def terminate
|
128
|
+
callback_supervisor.terminate
|
129
|
+
super
|
130
|
+
end
|
131
|
+
|
132
|
+
# Automatically confirm messages after dispatch
|
118
133
|
def auto_confirm?
|
119
134
|
@auto_confirm
|
120
135
|
end
|
121
136
|
|
137
|
+
# Return string for inspection
|
122
138
|
def inspect
|
123
139
|
"<#{self.class.name}:#{object_id} @name=#{name} @callbacks=#{Hash[*callbacks.map{|k,v| [k,v.object_id]}.flatten]}>"
|
124
140
|
end
|
125
141
|
|
142
|
+
# Return string of instance
|
126
143
|
def to_s
|
127
144
|
"<#{self.class.name}:#{object_id} @name=#{name}>"
|
128
145
|
end
|
129
146
|
|
147
|
+
# args:: Argument hash used to initialize instance
|
148
|
+
# Setup called during initialization for child sources to override
|
130
149
|
def setup(args={})
|
131
150
|
debug 'No custom setup declared'
|
132
151
|
end
|
133
152
|
|
153
|
+
# args:: Argument hash
|
154
|
+
# Connection method to be overridden in child sources
|
134
155
|
def connect(args={})
|
135
156
|
debug 'No custom connect declared'
|
136
157
|
end
|
137
158
|
|
159
|
+
# args:: number of messages to read
|
160
|
+
# Returns messages from source
|
138
161
|
def receive(n=1)
|
139
162
|
raise NoMethodError.new('Abstract method not valid for runtime')
|
140
163
|
end
|
141
164
|
|
142
|
-
|
165
|
+
# message:: Payload to transmit
|
166
|
+
# original_message:: Original `Carnivore::Message`
|
167
|
+
# args:: Custom arguments
|
168
|
+
# Transmit message on source
|
169
|
+
def transmit(message, original_message=nil, args={})
|
143
170
|
raise NoMethodError.new('Abstract method not valid for runtime')
|
144
171
|
end
|
145
172
|
|
173
|
+
# message:: Carnivore::Message
|
174
|
+
# Confirm receipt of the message on source
|
146
175
|
def confirm(message)
|
147
176
|
debug 'No custom confirm declared'
|
148
177
|
end
|
149
178
|
|
150
|
-
|
179
|
+
# callback_name:: Name of callback
|
180
|
+
# block_or_class:: Carnivore::Callback class or a block
|
181
|
+
# Adds the given callback to the source for message processing
|
182
|
+
def add_callback(callback_name, block_or_class)
|
183
|
+
name = "#{self.name}:#{callback_name}"
|
151
184
|
if(block_or_class.is_a?(Class))
|
152
185
|
size = block_or_class.workers || 1
|
153
186
|
if(size < 1)
|
@@ -155,19 +188,21 @@ module Carnivore
|
|
155
188
|
return self
|
156
189
|
elsif(size == 1)
|
157
190
|
debug "Adding callback class (#{block_or_class}) under supervision. Name: #{callback_name(name)}"
|
158
|
-
|
191
|
+
callback_supervisor.supervise_as callback_name(name), block_or_class, name
|
159
192
|
else
|
160
193
|
debug "Adding callback class (#{block_or_class}) under supervision pool (#{size} workers). Name: #{callback_name(name)}"
|
161
|
-
|
194
|
+
callback_supervisor.pool block_or_class, as: callback_name(name), size: size, args: [name]
|
162
195
|
end
|
163
196
|
else
|
164
197
|
debug "Adding custom callback class from block (#{block_or_class}) under supervision. Name: #{callback_name(name)}"
|
165
|
-
|
198
|
+
callback_supervisor.supervise_as callback_name(name), Callback, name, block_or_class
|
166
199
|
end
|
167
|
-
|
200
|
+
callbacks.push(name).uniq!
|
168
201
|
self
|
169
202
|
end
|
170
203
|
|
204
|
+
# name:: Name of callback
|
205
|
+
# Remove the named callback from the source
|
171
206
|
def remove_callback(name)
|
172
207
|
unless(@callbacks.include?(callback_name(name)))
|
173
208
|
raise NameError.new("Failed to locate callback named: #{name}")
|
@@ -177,6 +212,8 @@ module Carnivore
|
|
177
212
|
self
|
178
213
|
end
|
179
214
|
|
215
|
+
# name:: Name of callback
|
216
|
+
# Returns namespaced name (prefixed with source name and instance id)
|
180
217
|
def callback_name(name)
|
181
218
|
unless(@callback_names[name])
|
182
219
|
@callback_names[name] = [@name, self.object_id, name].join(':').to_sym
|
@@ -184,26 +221,99 @@ module Carnivore
|
|
184
221
|
@callback_names[name]
|
185
222
|
end
|
186
223
|
|
224
|
+
# msg:: New message received from source
|
225
|
+
# Returns formatted Carnivore::Message
|
187
226
|
def format(msg)
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
227
|
+
actor = Carnivore::Supervisor.supervisor[name]
|
228
|
+
if(actor)
|
229
|
+
Message.new(
|
230
|
+
:message => msg,
|
231
|
+
:source => actor
|
232
|
+
)
|
233
|
+
else
|
234
|
+
abort "Failed to locate self in registry (#{name})"
|
235
|
+
end
|
192
236
|
end
|
193
237
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
238
|
+
# m:: Carnivore::Message
|
239
|
+
# Returns true if message is valid to be processed
|
240
|
+
def valid_message?(m)
|
241
|
+
if(message_registry)
|
242
|
+
if(message_registry.valid?(m))
|
243
|
+
true
|
244
|
+
else
|
245
|
+
warn "Message was already received. Discarding: #{m.inspect}"
|
246
|
+
false
|
198
247
|
end
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
248
|
+
else
|
249
|
+
true
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# args:: Arguments
|
254
|
+
# Start processing messages from source
|
255
|
+
def process(*args)
|
256
|
+
begin
|
257
|
+
while(run_process && !callbacks.empty?)
|
258
|
+
@processing = true
|
259
|
+
loop_msgs = future.loop_receive unless loop_msgs
|
260
|
+
remote_msgs = future.receive unless remote_msgs
|
261
|
+
if(loop_msgs.ready?)
|
262
|
+
msgs = loop_msgs.value
|
263
|
+
loop_msgs = nil
|
264
|
+
elsif(remote_msgs.ready?)
|
265
|
+
msgs = remote_msgs.value
|
266
|
+
remote_msgs = nil
|
267
|
+
else
|
268
|
+
msgs = []
|
269
|
+
end
|
270
|
+
msgs = [msgs].flatten.compact.map do |m|
|
271
|
+
if(valid_message?(m))
|
272
|
+
format(m)
|
273
|
+
end
|
274
|
+
end.compact
|
275
|
+
msgs.each do |msg|
|
276
|
+
@callbacks.each do |name|
|
277
|
+
debug "Dispatching message<#{msg[:message].object_id}> to callback<#{name} (#{callback_name(name)})>"
|
278
|
+
callback_supervisor[callback_name(name)].async.call(msg)
|
279
|
+
end
|
203
280
|
end
|
281
|
+
sleep(1) if msgs.empty?
|
204
282
|
end
|
283
|
+
ensure
|
284
|
+
@processing = false
|
205
285
|
end
|
206
286
|
end
|
207
287
|
|
288
|
+
# args:: unused
|
289
|
+
# Return queued message from internal loop
|
290
|
+
def loop_receive(*args)
|
291
|
+
@message_loop.shift
|
292
|
+
end
|
293
|
+
|
294
|
+
# message:: Message for delivery
|
295
|
+
# original_message:: unused
|
296
|
+
# args:: unused
|
297
|
+
# Push message onto internal loop queue
|
298
|
+
def loop_transmit(message, original_message=nil, args={})
|
299
|
+
@message_loop.push message
|
300
|
+
end
|
301
|
+
|
302
|
+
# args:: transmit args
|
303
|
+
# Send to local loop if processing otherwise use regular transmit
|
304
|
+
def _transmit(*args)
|
305
|
+
if(processing)
|
306
|
+
loop_transmit(*args)
|
307
|
+
else
|
308
|
+
custom_transmit(*args)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# Load and initialize the message registry
|
313
|
+
def init_registry
|
314
|
+
require 'carnivore/message_registry'
|
315
|
+
@message_registry = MessageRegistry.new
|
316
|
+
end
|
317
|
+
|
208
318
|
end
|
209
319
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'carnivore'
|
2
|
+
|
3
|
+
module Carnivore
|
4
|
+
class Source
|
5
|
+
|
6
|
+
# Container for holding source configuration. This allows setup to
|
7
|
+
# occur prior to the supervisor actually starting the sources
|
8
|
+
class SourceContainer
|
9
|
+
|
10
|
+
attr_reader :klass
|
11
|
+
attr_reader :source_hash
|
12
|
+
|
13
|
+
# class_name:: Name of source class
|
14
|
+
# args:: argument hash to pass to source instance
|
15
|
+
def initialize(class_name, args={})
|
16
|
+
@klass = class_name
|
17
|
+
@source_hash = args || {}
|
18
|
+
@source_hash[:callbacks] = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# name:: Name of callback
|
22
|
+
# klass:: Class of callback (optional)
|
23
|
+
# Add a callback to a source via Class or block
|
24
|
+
def add_callback(name, klass=nil, &block)
|
25
|
+
@source_hash[:callbacks][name] = klass || block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'carnivore'
|
2
|
+
require 'celluloid'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
|
5
|
+
Celluloid.logger.level = 4
|
6
|
+
|
7
|
+
if(File.directory?(dir = File.join(Dir.pwd, 'test', 'specs')))
|
8
|
+
Dir.glob(File.join(dir, '*.rb')).each do |path|
|
9
|
+
require path
|
10
|
+
end
|
11
|
+
else
|
12
|
+
puts 'Failed to locate `test/specs` directory. Are you in project root directory?'
|
13
|
+
exit -1
|
14
|
+
end
|
15
|
+
|
16
|
+
MiniTest::Spec.before do
|
17
|
+
Celluloid.shutdown
|
18
|
+
Celluloid.boot
|
19
|
+
end
|
20
|
+
|
21
|
+
# Simple waiter method to stall testing
|
22
|
+
def source_wait(name='wait')
|
23
|
+
sleep(ENV.fetch("CARNIVORE_SOURCE_#{name.to_s.upcase}", 0.2).to_f)
|
24
|
+
end
|
25
|
+
|
26
|
+
# dummy store that should never be used for anything real
|
27
|
+
class MessageStore
|
28
|
+
class << self
|
29
|
+
|
30
|
+
def init
|
31
|
+
@messages = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def messages
|
35
|
+
@messages
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# dummy source to hold final tranmission and stuff payload in store
|
42
|
+
module Carnivore
|
43
|
+
class Source
|
44
|
+
class Spec < Source
|
45
|
+
def setup(*args)
|
46
|
+
MessageStore.init
|
47
|
+
end
|
48
|
+
|
49
|
+
def receive(*args)
|
50
|
+
wait(:forever)
|
51
|
+
end
|
52
|
+
|
53
|
+
def transmit(*args)
|
54
|
+
MessageStore.messages << args.first
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Carnivore::Source.provide(:spec, 'carnivore/spec_helper')
|
data/lib/carnivore/supervisor.rb
CHANGED
@@ -1,4 +1,57 @@
|
|
1
|
+
require 'carnivore'
|
2
|
+
require 'celluloid/supervision_group'
|
3
|
+
|
1
4
|
module Carnivore
|
2
5
|
class Supervisor < Celluloid::SupervisionGroup
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
attr_reader :registry, :supervisor
|
10
|
+
|
11
|
+
# Build a new supervisor
|
12
|
+
def build!
|
13
|
+
@registry, @supervisor = create!
|
14
|
+
@supervisor
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a new supervisor
|
18
|
+
# Returns [registry,supervisor]
|
19
|
+
def create!
|
20
|
+
registry = Celluloid::Registry.new
|
21
|
+
[registry, run!(registry)]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Destroy the registered supervisor
|
25
|
+
def terminate!
|
26
|
+
if(supervisor)
|
27
|
+
begin
|
28
|
+
supervisor.terminate
|
29
|
+
rescue Celluloid::DeadActorError
|
30
|
+
end
|
31
|
+
@supervisor = nil
|
32
|
+
@registry = nil
|
33
|
+
end
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# name:: Name of source
|
40
|
+
# Return source
|
41
|
+
def [](name)
|
42
|
+
instance = @registry[name]
|
43
|
+
unless(instance)
|
44
|
+
if(member = @members.detect{|m| m && m.name.to_s == name.to_s})
|
45
|
+
Celluloid::Logger.warn "Found missing actor in member list. Attempting to restart manually."
|
46
|
+
restart_actor(member.actor, true)
|
47
|
+
instance = @registry[name]
|
48
|
+
unless(instance)
|
49
|
+
Celluloid::Logger.error "Actor restart failed to make it available in the registry! (#{name})"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
instance
|
54
|
+
end
|
55
|
+
|
3
56
|
end
|
4
57
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
|
3
|
+
module Carnivore
|
4
|
+
module Utils
|
5
|
+
|
6
|
+
module Logging
|
7
|
+
|
8
|
+
# Define base logging types
|
9
|
+
%w(debug info warn error).each do |key|
|
10
|
+
define_method(key) do |string|
|
11
|
+
log(key, string)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Log message
|
16
|
+
def log(*args)
|
17
|
+
if(args.empty?)
|
18
|
+
Celluloid::Logger
|
19
|
+
else
|
20
|
+
severity, string = args
|
21
|
+
Celluloid::Logger.send(severity.to_sym, "#{self}: #{string}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Carnivore
|
2
|
+
module Utils
|
3
|
+
# Registry used for preventing duplicate message processing
|
4
|
+
class MessageRegistry
|
5
|
+
def initialize
|
6
|
+
@store = []
|
7
|
+
@size = 100
|
8
|
+
end
|
9
|
+
|
10
|
+
# message:: Carnivore::Message
|
11
|
+
# Returns true if message has not been processed
|
12
|
+
def valid?(message)
|
13
|
+
checksum = sha(message)
|
14
|
+
found = @store.include?(checksum)
|
15
|
+
unless(found)
|
16
|
+
push(checksum)
|
17
|
+
end
|
18
|
+
!found
|
19
|
+
end
|
20
|
+
|
21
|
+
# item:: checksum
|
22
|
+
# Pushes checksum into store
|
23
|
+
def push(item)
|
24
|
+
@store.push(item)
|
25
|
+
if(@store.size > @size)
|
26
|
+
@store.shift
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# thing:: Instance
|
32
|
+
# Return checksum for give instance
|
33
|
+
def sha(thing)
|
34
|
+
unless(thing.is_a?(String))
|
35
|
+
thing = MultiJson.dump(thing)
|
36
|
+
end
|
37
|
+
(Digest::SHA512.new << thing).hexdigest
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Carnivore
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
module Params
|
5
|
+
|
6
|
+
# hash:: Hash
|
7
|
+
# Symbolize keys in hash
|
8
|
+
def symbolize_hash(hash)
|
9
|
+
Hash[*(
|
10
|
+
hash.map do |k,v|
|
11
|
+
if(k.is_a?(String))
|
12
|
+
key = k.gsub(/(?<![A-Z])([A-Z])/, '_\1').sub(/^_/, '').downcase.to_sym
|
13
|
+
else
|
14
|
+
key = k
|
15
|
+
end
|
16
|
+
[
|
17
|
+
key,
|
18
|
+
v.is_a?(Hash) ? symbolize_hash(v) : v
|
19
|
+
]
|
20
|
+
end.flatten(1)
|
21
|
+
)]
|
22
|
+
end
|
23
|
+
|
24
|
+
# hash:: Hash
|
25
|
+
# args:: Symbols or strings
|
26
|
+
# Follow path in hash provided by args and return value or nil
|
27
|
+
# if path is not valid
|
28
|
+
def retrieve(hash, *args)
|
29
|
+
valids = [::Hash, hash.is_a?(Class) ? hash : hash.class]
|
30
|
+
args.flatten.inject(hash) do |memo, key|
|
31
|
+
break unless valids.detect{ |valid_type|
|
32
|
+
memo.is_a?(valid_type) || memo == valid_type
|
33
|
+
}
|
34
|
+
memo[key.to_s] || memo[key.to_sym] || break
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/lib/carnivore/utils.rb
CHANGED
@@ -1,47 +1,13 @@
|
|
1
|
-
require '
|
1
|
+
require 'carnivore'
|
2
2
|
|
3
3
|
module Carnivore
|
4
|
-
module Utils
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
if(k.is_a?(String))
|
11
|
-
key = k.gsub(/(?<![A-Z])([A-Z])/, '_\1').sub(/^_/, '').downcase.to_sym
|
12
|
-
else
|
13
|
-
key = k
|
14
|
-
end
|
15
|
-
[
|
16
|
-
key,
|
17
|
-
v.is_a?(Hash) ? symbolize_hash(v) : v
|
18
|
-
]
|
19
|
-
end.flatten(1)
|
20
|
-
)]
|
21
|
-
end
|
22
|
-
end
|
5
|
+
module Utils
|
6
|
+
autoload :Params, 'carnivore/utils/params'
|
7
|
+
autoload :Logging, 'carnivore/utils/logging'
|
8
|
+
autoload :MessageRegistry, 'carnivore/utils/message_registry'
|
23
9
|
|
24
10
|
extend Params
|
25
|
-
|
26
|
-
module Logging
|
27
|
-
|
28
|
-
%w(debug info warn error).each do |key|
|
29
|
-
define_method(key) do |string|
|
30
|
-
log(key, string)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def log(*args)
|
35
|
-
if(args.empty?)
|
36
|
-
Celluloid::Logger
|
37
|
-
else
|
38
|
-
severity, string = args
|
39
|
-
Celluloid::Logger.send(severity.to_sym, "#{self}: #{string}")
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
11
|
extend Logging
|
46
12
|
|
47
13
|
end
|
data/lib/carnivore/version.rb
CHANGED
data/lib/carnivore.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carnivore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-01-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: celluloid
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
@@ -28,7 +28,7 @@ dependencies:
|
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: mixlib-config
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: multi_json
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
@@ -66,29 +66,26 @@ extensions: []
|
|
66
66
|
extra_rdoc_files: []
|
67
67
|
files:
|
68
68
|
- lib/carnivore/callback.rb
|
69
|
+
- lib/carnivore/errors.rb
|
69
70
|
- lib/carnivore/supervisor.rb
|
70
71
|
- lib/carnivore/source/test.rb
|
71
72
|
- lib/carnivore/source.rb
|
72
73
|
- lib/carnivore/version.rb
|
73
74
|
- lib/carnivore/runner.rb
|
75
|
+
- lib/carnivore/spec_helper.rb
|
76
|
+
- lib/carnivore/utils/message_registry.rb
|
77
|
+
- lib/carnivore/utils/logging.rb
|
78
|
+
- lib/carnivore/utils/params.rb
|
79
|
+
- lib/carnivore/autoloader.rb
|
74
80
|
- lib/carnivore/config.rb
|
75
81
|
- lib/carnivore/message.rb
|
82
|
+
- lib/carnivore/source_container.rb
|
76
83
|
- lib/carnivore/utils.rb
|
77
84
|
- lib/carnivore/container.rb
|
78
85
|
- lib/carnivore.rb
|
79
|
-
-
|
80
|
-
- test/specs/source.rb
|
81
|
-
- test/specs/config.rb
|
82
|
-
- test/specs/message.rb
|
83
|
-
- test/specs/utils.rb
|
84
|
-
- test/specs/container.rb
|
85
|
-
- examples/test_http.rb
|
86
|
-
- examples/test_class.rb
|
87
|
-
- examples/test_block.rb
|
88
|
-
- Gemfile
|
86
|
+
- carnivore.gemspec
|
89
87
|
- README.md
|
90
88
|
- CHANGELOG.md
|
91
|
-
- carnivore.gemspec
|
92
89
|
homepage: https://github.com/carnivore-rb/carnivore
|
93
90
|
licenses: []
|
94
91
|
post_install_message:
|
data/Gemfile
DELETED
data/examples/test_block.rb
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
require 'carnivore'
|
2
|
-
|
3
|
-
Carnivore.configure do
|
4
|
-
s = Carnivore::Source.build(:type => :test, :args => {})
|
5
|
-
|
6
|
-
s.add_callback(:printer) do |message|
|
7
|
-
info "GOT MESSAGE: #{message[:message]} - source: #{message[:source]} - instance: #{self}"
|
8
|
-
end
|
9
|
-
|
10
|
-
end.start!
|
data/examples/test_class.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'carnivore'
|
2
|
-
|
3
|
-
class CustomCallback < Carnivore::Callback
|
4
|
-
|
5
|
-
self.workers = 5
|
6
|
-
|
7
|
-
def setup
|
8
|
-
info "Custom callback setup called!"
|
9
|
-
end
|
10
|
-
|
11
|
-
def execute(message)
|
12
|
-
info "GOT MESSAGE: #{message[:message]} - source: #{message[:source]} - instance: #{self}"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
Carnivore.configure do
|
17
|
-
s = Carnivore::Source.build(:type => :test, :args => {})
|
18
|
-
s.add_callback(:printer, CustomCallback)
|
19
|
-
|
20
|
-
end.start!
|
data/examples/test_http.rb
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
require 'carnivore'
|
2
|
-
|
3
|
-
Carnivore.configure do
|
4
|
-
s = Carnivore::Source.build(:type => :http, :args => {:bind => '0.0.0.0', :port => 3000})
|
5
|
-
|
6
|
-
s.add_callback(:printer) do |message|
|
7
|
-
info "GOT MESSAGE: #{message[:message][:body]} - path: #{message[:message][:request].url} - method: #{message[:message][:request].method} - source: #{message[:source]} - instance: #{self}"
|
8
|
-
end
|
9
|
-
|
10
|
-
end.start!
|
data/test/spec.rb
DELETED
data/test/specs/config.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'carnivore/config'
|
3
|
-
|
4
|
-
describe 'Carnivore::Config' do
|
5
|
-
describe 'Direct Configuration' do
|
6
|
-
it 'allows direct configuration set' do
|
7
|
-
Carnivore::Config[:direct] = true
|
8
|
-
Carnivore::Config[:direct].must_equal true
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'returns nil when configuration is undefined' do
|
12
|
-
Carnivore::Config[:missing].must_be_nil
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'raises exception when accessing nested keys that do not exist' do
|
16
|
-
-> { Carnivore::Config[:missing][:key] }.must_raise NoMethodError
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
describe '#get helper' do
|
22
|
-
it 'returns the nested configuration value' do
|
23
|
-
Carnivore::Config[:nested] = {:value => 'hello world'}
|
24
|
-
Carnivore::Config.get(:nested, :value).must_equal 'hello world'
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'returns `nil` when nested configuration does not exist' do
|
28
|
-
Carnivore::Config.get(:value, :does, :not, :exist).must_be_nil
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/test/specs/container.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'carnivore/container'
|
3
|
-
|
4
|
-
describe 'Carnivore::Container' do
|
5
|
-
it 'provides logging helpers' do
|
6
|
-
c = Carnivore::Container.new
|
7
|
-
Carnivore::Container.must_respond_to :log
|
8
|
-
c.must_respond_to :log
|
9
|
-
%w(debug info warn error).each do |key|
|
10
|
-
c.must_respond_to key
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
data/test/specs/message.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'carnivore/message'
|
3
|
-
|
4
|
-
describe 'Carnivore::Message' do
|
5
|
-
|
6
|
-
it 'requires a Source to be provided' do
|
7
|
-
-> { Carnivore::Message.new(:message => 'hi') }.must_raise ArgumentError
|
8
|
-
end
|
9
|
-
|
10
|
-
it 'provides argument access via `[]`' do
|
11
|
-
message = Carnivore::Message.new(:source => true, :message => 'hi')
|
12
|
-
message[:source].must_equal true
|
13
|
-
message[:message].must_equal 'hi'
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'provides direct argument hash access via `args`' do
|
17
|
-
message = Carnivore::Message.new(:source => true, :message => 'hi')
|
18
|
-
message.args.must_be_kind_of Hash
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'provides `confirm!` confirmation helper to Source' do
|
22
|
-
source = MiniTest::Mock.new
|
23
|
-
message = Carnivore::Message.new(:source => source, :message => 'hi')
|
24
|
-
source.expect(:confirm, true, [message])
|
25
|
-
message.confirm!
|
26
|
-
source.verify
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
data/test/specs/source.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'carnivore/source'
|
3
|
-
|
4
|
-
describe 'Carnivore::Source' do
|
5
|
-
describe 'Carnivore::Source::SourceContainer' do
|
6
|
-
before do
|
7
|
-
@src_ctn = Carnivore::Source::SourceContainer.new(:my_name, {:arg1 => true})
|
8
|
-
end
|
9
|
-
|
10
|
-
it 'should store name in `klass` attribute' do
|
11
|
-
@src_ctn.klass.must_equal :my_name
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'should store argument hash in `source_hash` attribute' do
|
15
|
-
@src_ctn.source_hash.must_equal :arg1 => true, :callbacks => {}
|
16
|
-
end
|
17
|
-
|
18
|
-
describe 'callback additions' do
|
19
|
-
before do
|
20
|
-
@block = lambda{ 'hi' }
|
21
|
-
@src_ctn.add_callback(:hi, &@block)
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'should store callback by name in `source_hash` under `:callbacks`' do
|
25
|
-
@src_ctn.source_hash.keys.must_include :callbacks
|
26
|
-
@src_ctn.source_hash[:callbacks].keys.must_include :hi
|
27
|
-
@src_ctn.source_hash[:callbacks].values.must_include @block
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe 'Custom source providers' do
|
33
|
-
|
34
|
-
before do
|
35
|
-
Carnivore::Source.provide(:meat_bag, 'carnivore-meat-bag/meat_bag')
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'gives expected require path if registered' do
|
39
|
-
Carnivore::Source.require_path(:meat_bag).must_equal 'carnivore-meat-bag/meat_bag'
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'gives `nil` require path if not registered' do
|
43
|
-
Carnivore::Source.require_path(:test).must_be_nil
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
describe 'Source registration' do
|
48
|
-
before do
|
49
|
-
@inst = Object.new
|
50
|
-
Carnivore::Source.register(:my_source, @inst)
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'provides list of registered sources' do
|
54
|
-
Carnivore::Source.sources.must_include @inst
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'allows accessing registered source by name' do
|
58
|
-
Carnivore::Source.source(:my_source).must_equal @inst
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe 'Processing with base Source instance' do
|
63
|
-
it 'should raise an exception' do
|
64
|
-
-> {
|
65
|
-
Carnivore::Source.new(:auto_process => false).process
|
66
|
-
}.must_raise NoMethodError
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe 'Source test instance' do
|
71
|
-
describe 'with no name argument' do
|
72
|
-
before do
|
73
|
-
require 'carnivore/source/test'
|
74
|
-
@source = Carnivore::Source::Test.new
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'should generate name if none provided' do
|
78
|
-
@source.name.wont_be :empty?
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
data/test/specs/utils.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'carnivore/utils'
|
3
|
-
|
4
|
-
describe 'Carnivore::Utils' do
|
5
|
-
describe 'Carnivore::Utils::Logging' do
|
6
|
-
before do
|
7
|
-
@obj = Object.new
|
8
|
-
@obj.extend(Carnivore::Utils::Logging)
|
9
|
-
Celluloid.logger.level = 0
|
10
|
-
end
|
11
|
-
|
12
|
-
after do
|
13
|
-
Celluloid.logger.level = 4
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'adds logging methods' do
|
17
|
-
%w(debug info warn error).each do |key|
|
18
|
-
@obj.must_respond_to(key)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'includes object information in logging output' do
|
23
|
-
out, err = capture_subprocess_io do
|
24
|
-
@obj.info 'hello world'
|
25
|
-
end
|
26
|
-
err.must_match %r{I,\s*\[.*?\]\s*INFO\s*--\s*:\s*#{Regexp.escape(@obj.inspect)}:\s*hello world\n}
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe 'Carnivore::Utils::Params' do
|
31
|
-
before do
|
32
|
-
@obj = Object.new
|
33
|
-
@obj.extend(Carnivore::Utils::Params)
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'adds `symbolize_hash` method' do
|
37
|
-
@obj.must_respond_to :symbolize_hash
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'converts hash keys to symbols' do
|
41
|
-
h = {'string' => {'another' => {'OneHere' => 'more'}}}
|
42
|
-
converted = @obj.symbolize_hash(h)
|
43
|
-
converted.keys.first.must_equal :string
|
44
|
-
converted[:string][:another].keys.first.must_equal :one_here
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|