jackal 0.1.0 → 0.1.2
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.
- data/CHANGELOG.md +5 -0
- data/README.md +87 -1
- data/bin/jackal +0 -0
- data/bin/jackal-test +5 -0
- data/jackal.gemspec +1 -0
- data/lib/jackal.rb +1 -0
- data/lib/jackal/callback.rb +56 -14
- data/lib/jackal/cli.rb +1 -0
- data/lib/jackal/error.rb +1 -0
- data/lib/jackal/loader.rb +24 -22
- data/lib/jackal/utils.rb +38 -0
- data/lib/jackal/utils/spec.rb +10 -0
- data/lib/jackal/utils/spec/callback_local.rb +24 -0
- data/lib/jackal/utils/spec/helpers.rb +49 -0
- data/lib/jackal/utils/spec/loader.rb +14 -0
- data/lib/jackal/utils/spec/runner.rb +11 -0
- data/lib/jackal/version.rb +1 -1
- metadata +10 -2
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,89 @@
|
|
1
1
|
# Jackal
|
2
2
|
|
3
|
-
Run your carnivores
|
3
|
+
Run your carnivores.
|
4
|
+
|
5
|
+
## Configuration
|
6
|
+
|
7
|
+
```json
|
8
|
+
{
|
9
|
+
"jackal": {
|
10
|
+
"require": [
|
11
|
+
"carnivore-http",
|
12
|
+
"fubar-helloer"
|
13
|
+
]
|
14
|
+
},
|
15
|
+
"fubar": {
|
16
|
+
"helloer": {
|
17
|
+
"sources": {
|
18
|
+
"input": {
|
19
|
+
...
|
20
|
+
},
|
21
|
+
"output": {
|
22
|
+
...
|
23
|
+
},
|
24
|
+
"error": {
|
25
|
+
...
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"callbacks": [
|
29
|
+
"Fubar::Helloer::SayHello"
|
30
|
+
],
|
31
|
+
"config": {
|
32
|
+
"output_prefix": "Received message: "
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
* `jackal` provides subsystem configuration
|
40
|
+
* `require` libraries to load at startup
|
41
|
+
* `fubar` configuration of components (snake cased top level module)
|
42
|
+
* `helloer` configuration of specific component (snake cased second level module)
|
43
|
+
* `sources` configuration for carnivore sources
|
44
|
+
* `callbacks` callback class names to initialize and attach to input source
|
45
|
+
* `config` configuration hash used by callbacks
|
46
|
+
|
47
|
+
## Jackal Callbacks
|
48
|
+
|
49
|
+
Jackal callbacks are subclassed Carnivore callbacks adding a bit more structure. The
|
50
|
+
general implementation of a Jackal callback:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
module Fubar
|
54
|
+
module Helloer
|
55
|
+
class SayHello < Jackal::Callback
|
56
|
+
|
57
|
+
def valid?(message)
|
58
|
+
super do |payload|
|
59
|
+
payload.get(:data, :helloer, :output)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute(message)
|
64
|
+
failure_wrap(message) do |payload|
|
65
|
+
info config[:output_prefix] + payload.get(:data, :helloer, :output)
|
66
|
+
job_completed(:helloer, payload, message)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
## Testing
|
76
|
+
|
77
|
+
Jackal provides test helpers building upon the helpers provided by
|
78
|
+
Carnivore.
|
79
|
+
|
80
|
+
### jackal-test
|
81
|
+
|
82
|
+
This executable will load minitest and auto run all files matched
|
83
|
+
by the glob: `test/specs/*.rb`.
|
84
|
+
|
85
|
+
## Info
|
86
|
+
|
87
|
+
* Repository: https://github.com/carnivore-rb/jackal
|
88
|
+
* Carnivore: https://github.com/carnivore-rb/carnivore
|
89
|
+
* IRC: Freenode @ #carnivore
|
data/bin/jackal
CHANGED
File without changes
|
data/bin/jackal-test
ADDED
data/jackal.gemspec
CHANGED
data/lib/jackal.rb
CHANGED
data/lib/jackal/callback.rb
CHANGED
@@ -1,25 +1,45 @@
|
|
1
1
|
require 'jackal'
|
2
2
|
|
3
3
|
module Jackal
|
4
|
+
# Jackal customized callback
|
4
5
|
class Callback < Carnivore::Callback
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
include Utils::Payload
|
8
|
+
|
9
|
+
# @return [Array] key path in configuration
|
10
|
+
def config_path
|
11
|
+
self.class.name.split('::')[0,2].map do |string|
|
12
|
+
string.gsub(/(?<![A-Z])([A-Z])/, '_\1').sub(/^_/, '').downcase
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [String] prefix of source for this callback
|
17
|
+
def source_prefix
|
18
|
+
config_path.join('_')
|
8
19
|
end
|
9
20
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
21
|
+
# @return [Hash] configuration
|
22
|
+
def config
|
23
|
+
Carnviore::Config.get(*config_path.push(:config)) || Smash.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validity of message
|
27
|
+
#
|
28
|
+
# @param message [Carnivore::Message]
|
29
|
+
# @return [TrueClass, FalseClass]
|
30
|
+
def valid?(message)
|
31
|
+
m = unpack(message)
|
32
|
+
block_given? ? yield(m) : true
|
15
33
|
end
|
16
34
|
|
17
|
-
# message:: Original message
|
18
35
|
# Executes block and catches unexpected exceptions if encountered
|
36
|
+
#
|
37
|
+
# @param message [Carnivore::Message]
|
38
|
+
# @return [Object]
|
19
39
|
def failure_wrap(message)
|
20
40
|
abort 'Failure wrap requires block for execution' unless block_given?
|
21
41
|
begin
|
22
|
-
payload = message
|
42
|
+
payload = unpack(message)
|
23
43
|
yield payload
|
24
44
|
rescue => e
|
25
45
|
error "!!! Unexpected failure encountered -> #{e.class}: #{e}"
|
@@ -28,29 +48,51 @@ module Jackal
|
|
28
48
|
end
|
29
49
|
end
|
30
50
|
|
51
|
+
# Send payload to error handler
|
52
|
+
#
|
53
|
+
# @param payload [Hash]
|
54
|
+
# @param message [Carnivore::Message]
|
55
|
+
# @param reason [String]
|
31
56
|
def failed(payload, message, reason='No reason provided')
|
32
57
|
error "Processing of #{message} failed! Reason: #{reason}"
|
33
58
|
message.confirm!
|
34
|
-
destination = "#{
|
59
|
+
destination = "#{source_prefix}_error"
|
35
60
|
source = Carnivore::Supervisor.supervisor[destination]
|
36
61
|
error "Sending #{message} to error handler: #{source}"
|
37
62
|
source.transmit(payload)
|
38
63
|
end
|
39
64
|
|
65
|
+
# Mark payload complete and forward
|
66
|
+
#
|
67
|
+
# @param payload [Hash]
|
68
|
+
# @param message [Carnivore::Message]
|
40
69
|
def completed(payload, message)
|
41
70
|
message.confirm!
|
42
71
|
info "Processing of #{message} complete on this callback"
|
43
72
|
forward(payload)
|
44
73
|
end
|
45
74
|
|
75
|
+
# Forward payload to output source
|
76
|
+
#
|
77
|
+
# @param payload [Hash]
|
46
78
|
def forward(payload)
|
47
|
-
destination = "#{
|
79
|
+
destination = "#{source_prefix}_output"
|
48
80
|
source = Carnivore::Supervisor.supervisor[destination]
|
49
|
-
|
50
|
-
|
51
|
-
|
81
|
+
if(source)
|
82
|
+
info "Forwarding payload to output destination... (#{source})"
|
83
|
+
debug "Forwarded payload: #{payload.inspect}"
|
84
|
+
source.transmit(payload)
|
85
|
+
else
|
86
|
+
warn "No destination source found for generated source path: #{destination}"
|
87
|
+
info "Processing of message #{message} has completed. Message now discarded."
|
88
|
+
end
|
52
89
|
end
|
53
90
|
|
91
|
+
# Mark job as completed
|
92
|
+
#
|
93
|
+
# @param name [String]
|
94
|
+
# @param payload [Hash]
|
95
|
+
# @param message [Carnivore::Message]
|
54
96
|
def job_completed(name, payload, message)
|
55
97
|
info "Processing of message #{message} has completed within this component #{name}"
|
56
98
|
message.confirm!
|
data/lib/jackal/cli.rb
CHANGED
data/lib/jackal/error.rb
CHANGED
data/lib/jackal/loader.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'carnivore'
|
2
2
|
require 'jackal'
|
3
3
|
|
4
|
-
|
5
|
-
cli.
|
6
|
-
|
4
|
+
unless(ENV['JACKAL_TESTING_MODE'])
|
5
|
+
cli = Jackal::Cli.new
|
6
|
+
cli.parse_options
|
7
|
+
Carnivore::Config.configure(cli.config)
|
8
|
+
end
|
9
|
+
|
7
10
|
Carnivore::Config.auto_symbolize(true)
|
8
11
|
|
9
12
|
(Carnivore::Config.get(:jackal, :require) || []).each do |path|
|
@@ -11,26 +14,25 @@ Carnivore::Config.auto_symbolize(true)
|
|
11
14
|
end
|
12
15
|
|
13
16
|
begin
|
14
|
-
Carnivore::Utils.symbolize_hash(Carnivore::Config.hash_dup).each do |
|
15
|
-
next
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
17
|
+
Carnivore::Utils.symbolize_hash(Carnivore::Config.hash_dup).each do |namespace, args|
|
18
|
+
next unless args.is_a?(Hash)
|
19
|
+
args.each do |key, opts|
|
20
|
+
next unless opts.is_a?(Hash) && opts[:sources]
|
21
|
+
Carnivore::Utils.info "Processing: #{opts.inspect}"
|
22
|
+
Carnivore.configure do
|
23
|
+
opts.fetch(:sources, {}).each do |kind, source_args|
|
24
|
+
source = Carnivore::Source.build(
|
25
|
+
:type => source_args[:type].to_sym,
|
26
|
+
:args => source_args.fetch(:args, {}).merge(:name => "#{namespace}_#{key}_#{kind}")
|
27
|
+
)
|
28
|
+
Carnivore::Utils.info "Initialized new source: #{namespace}_#{key}_#{kind}"
|
29
|
+
if(kind == :input)
|
30
|
+
opts.fetch(:callbacks, []).each do |klass_name|
|
31
|
+
klass = klass_name.split('::').inject(Object) do |memo, name|
|
32
|
+
memo.const_get(name)
|
33
|
+
end
|
34
|
+
source.add_callback(klass_name, klass)
|
32
35
|
end
|
33
|
-
source.add_callback(klass_name, klass)
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
data/lib/jackal/utils.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'jackal'
|
2
|
+
|
3
|
+
module Jackal
|
4
|
+
# Helper utilities
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
autoload :Spec, 'jackal/utils/spec'
|
8
|
+
|
9
|
+
# Module utilities
|
10
|
+
module Payload
|
11
|
+
|
12
|
+
# Generate a new payload
|
13
|
+
#
|
14
|
+
# @param name [String]
|
15
|
+
# @param payload [Hash]
|
16
|
+
# @return [Smash]
|
17
|
+
def new_payload(name, payload)
|
18
|
+
Smash.new(
|
19
|
+
:name => name,
|
20
|
+
:id => Celluloid.uuid,
|
21
|
+
:data => payload
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Extract payload from message
|
26
|
+
#
|
27
|
+
# @param message [Carnivore::Message]
|
28
|
+
# @return [Smash]
|
29
|
+
def unpack(message)
|
30
|
+
Smash.new(message[:message])
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
extend Payload
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'jackal'
|
2
|
+
|
3
|
+
module Jackal
|
4
|
+
module Utils
|
5
|
+
module Spec
|
6
|
+
# Callback helper module for isolated testing
|
7
|
+
module CallbackLocal
|
8
|
+
|
9
|
+
# @return [Array] forwarded payloads
|
10
|
+
def forwarded
|
11
|
+
@forwarded ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Force payload into local store
|
15
|
+
#
|
16
|
+
# @param payload [Hash]
|
17
|
+
def forward(payload)
|
18
|
+
@forwarded << payload
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'carnivore/spec_helper'
|
3
|
+
|
4
|
+
Celluloid.logger.level = 0 if ENV['DEBUG']
|
5
|
+
|
6
|
+
# Default source setup higher than base carivore default
|
7
|
+
unless(ENV['CARNIVORE_SOURCE_SETUP'])
|
8
|
+
ENV['CARNIVORE_SOURCE_SETUP'] = '0.5'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Pass any jackal specific wait settings down to carnivore
|
12
|
+
ENV.each do |key, value|
|
13
|
+
if(key.start_with?('JACKAL_SOURCE_'))
|
14
|
+
carnivore_key = key.sub('JACKAL_SOURCE', 'CARNIVORE_SOURCE')
|
15
|
+
ENV[carnivore_key] = value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def payload_for(style, args={})
|
20
|
+
file = "#{style}.json"
|
21
|
+
path = [File.join(Dir.pwd, 'test/specs/payloads'), File.join(File.dirname(__FILE__), 'payloads')].map do |dir|
|
22
|
+
if(File.exists?(full_path = File.join(dir, file)))
|
23
|
+
full_path
|
24
|
+
end
|
25
|
+
end.compact.first
|
26
|
+
if(path)
|
27
|
+
if(args[:raw])
|
28
|
+
MultiJson.load(File.read(path))
|
29
|
+
else
|
30
|
+
if(args[:nest])
|
31
|
+
Jackal::Utils.new_payload(:test, args[:nest] => MultiJson.load(File.read(path)))
|
32
|
+
else
|
33
|
+
Jackal::Utils.new_payload(:test, File.read(path))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
else
|
37
|
+
raise "Requested payload path for test does not exist: #{File.expand_path(path)}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_setup(config)
|
42
|
+
path = File.join(Dir.pwd, 'test/specs/config', "#{config}.json")
|
43
|
+
Carnivore::Config.configure(:config_path => path)
|
44
|
+
runner = Thread.new do
|
45
|
+
require 'jackal/loader'
|
46
|
+
end
|
47
|
+
source_wait(:setup)
|
48
|
+
runner
|
49
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'carnivore/config'
|
2
|
+
|
3
|
+
ENV['JACKAL_TESTING_MODE'] = 'true'
|
4
|
+
|
5
|
+
path = File.join(Dir.pwd, 'test')
|
6
|
+
|
7
|
+
if(File.directory?(path))
|
8
|
+
if(File.exists?(spec_file = File.join(path, 'spec.rb')))
|
9
|
+
require spec_file
|
10
|
+
end
|
11
|
+
require 'jackal/utils/spec/runner'
|
12
|
+
else
|
13
|
+
raise "No test directory found: #{path}"
|
14
|
+
end
|
data/lib/jackal/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jackal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-05-
|
12
|
+
date: 2014-05-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: carnivore
|
@@ -47,14 +47,21 @@ description: Message processing helper
|
|
47
47
|
email: code@chrisroberts.org
|
48
48
|
executables:
|
49
49
|
- jackal
|
50
|
+
- jackal-test
|
50
51
|
extensions: []
|
51
52
|
extra_rdoc_files: []
|
52
53
|
files:
|
53
54
|
- lib/jackal/callback.rb
|
54
55
|
- lib/jackal/version.rb
|
56
|
+
- lib/jackal/utils/spec/callback_local.rb
|
57
|
+
- lib/jackal/utils/spec/runner.rb
|
58
|
+
- lib/jackal/utils/spec/helpers.rb
|
59
|
+
- lib/jackal/utils/spec/loader.rb
|
60
|
+
- lib/jackal/utils/spec.rb
|
55
61
|
- lib/jackal/cli.rb
|
56
62
|
- lib/jackal/loader.rb
|
57
63
|
- lib/jackal/error.rb
|
64
|
+
- lib/jackal/utils.rb
|
58
65
|
- lib/jackal.rb
|
59
66
|
- jackal.gemspec
|
60
67
|
- README.md
|
@@ -62,6 +69,7 @@ files:
|
|
62
69
|
- CONTRIBUTING.md
|
63
70
|
- LICENSE
|
64
71
|
- bin/jackal
|
72
|
+
- bin/jackal-test
|
65
73
|
homepage: https://github.com/carnivore-rb/jackal
|
66
74
|
licenses:
|
67
75
|
- Apache 2.0
|