carnivore 0.1.6 → 0.1.8

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # v0.1.8
2
+ * Clean up requires
3
+ * Register sources by name for easy lookup
4
+ * Fix hash symbolizer to not symbolize non-strings
5
+ * Add confirmation helper method to message
6
+ * Start including tests
7
+
1
8
  # v0.1.6
2
9
  * Do not register callback if no worker is created
3
10
 
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'minitest'
4
+
3
5
  gemspec
data/README.md CHANGED
@@ -19,7 +19,7 @@ stuff gets done. Super simple!
19
19
  Carnivore.configure do
20
20
  src = Source.build(:type => :test, :args => {})
21
21
  src.add_callback(:print_message) do |msg|
22
- puts "Received message: #{message}"
22
+ puts "Received message: #{msg}"
23
23
  end
24
24
  end.start!
25
25
  ```
@@ -70,7 +70,7 @@ Carnivore.configure do
70
70
  src = Source.build(:type => :test, :args => {})
71
71
  src.add_callback(:print_message) do |msg|
72
72
  my_inst.be_awesome!
73
- puts "Received message: #{message}"
73
+ puts "Received message: #{msg}"
74
74
  end
75
75
  end.start!
76
76
  ```
@@ -83,7 +83,7 @@ Carnivore.configure do
83
83
  src.add_callback(:print_message) do |msg|
84
84
  my_inst = AwesomeSauce.new
85
85
  my_inst.be_awesome!
86
- puts "Received message: #{message}"
86
+ puts "Received message: #{msg}"
87
87
  end
88
88
  end.start!
89
89
  ```
@@ -103,7 +103,7 @@ Carnivore.configure do
103
103
  @my_inst = AwesomeSauce.new
104
104
  end
105
105
  @my_inst.be_awesome!
106
- puts "Received message: #{message}"
106
+ puts "Received message: #{msg}"
107
107
  end
108
108
  end.start!
109
109
  ```
Binary file
@@ -4,6 +4,7 @@ module Carnivore
4
4
  class Callback
5
5
 
6
6
  class << self
7
+ # Define number of workers to create
7
8
  attr_accessor :workers
8
9
  end
9
10
 
@@ -21,17 +22,24 @@ module Carnivore
21
22
  setup
22
23
  end
23
24
 
25
+ # Used by custom callback classes for setup
24
26
  def setup
25
27
  end
26
28
 
29
+ # Provide nice output when printed
27
30
  def inspect
28
31
  "callback<#{self.name}:#{self.object_id}>"
29
32
  end
33
+ alias_method :to_s, :inspect
30
34
 
35
+ # message:: Carnivore::Message
36
+ # Return true if message should be handled by this callback
31
37
  def valid?(message)
32
38
  true
33
39
  end
34
40
 
41
+ # message:: Carnivore::Message
42
+ # Pass message to registered callbacks
35
43
  def call(message)
36
44
  if(valid?(message))
37
45
  execute(message)
@@ -8,12 +8,16 @@ module Carnivore
8
8
 
9
9
  class << self
10
10
 
11
+ # args:: configuration hash
12
+ # Merge provided args into configuration
11
13
  def configure(args)
12
14
  build(args[:config_path]) if args[:config_path]
13
15
  self.merge!(args)
14
16
  self
15
17
  end
16
18
 
19
+ # path_or_hash:: Path to JSON file or configuration Hash
20
+ # Populates the configuration
17
21
  def build(path_or_hash)
18
22
  if(path_or_hash.is_a?(Hash))
19
23
  conf = path_or_hash
@@ -31,6 +35,14 @@ module Carnivore
31
35
  self
32
36
  end
33
37
 
38
+ # ary: keys into a hash
39
+ # Returns value if exists or nil
40
+ # Example:
41
+ # Config.build(:my_app => {:port => 30})
42
+ # Config.get(:my_app, :port) => 30
43
+ # Config.get(:my_app, :host) => nil
44
+ # Config.get(:other_app, :port) => nil
45
+ # Config.get(:my_app, :mail, :server) => nil
34
46
  def get(*ary)
35
47
  ary.flatten.inject(self) do |memo, key|
36
48
  memo[key.to_s] || memo[key.to_sym] || break
@@ -1,9 +1,10 @@
1
+ require 'carnivore/utils'
1
2
  require 'celluloid/logger'
2
3
 
3
4
  module Carnivore
4
5
  class Container < Module
5
6
 
6
- include Celluloid::Logger
7
+ include Carnivore::Utils::Logging
7
8
 
8
9
  class << self
9
10
  def log
@@ -1,16 +1,25 @@
1
+ require 'carnivore/source'
2
+
1
3
  module Carnivore
2
4
  class Message
3
5
 
4
6
  attr_reader :args
5
7
 
6
8
  def initialize(args={})
7
- @args = args
9
+ unless(args[:source])
10
+ raise ArgumentError.new("A valid `Carnivore::Source` must be provided via `:source`")
11
+ end
12
+ @args = args.dup
8
13
  end
9
14
 
10
15
  def [](k)
11
16
  @args[k.to_sym] || @args[k.to_s]
12
17
  end
13
18
 
19
+ def confirm!
20
+ self[:source].confirm(self)
21
+ end
22
+
14
23
  def inspect
15
24
  "<Carnivore::Message[#{self.object_id}] @args=#{args}>"
16
25
  end
@@ -1,4 +1,5 @@
1
1
  require 'celluloid'
2
+ require 'carnivore/config'
2
3
  require 'carnivore/source'
3
4
  require 'carnivore/container'
4
5
 
@@ -69,25 +69,39 @@ module Carnivore
69
69
  msgs.map{|m| pre_process(m) }
70
70
  end
71
71
 
72
- def transmit(message, dest=nil)
73
- case dest
74
- when Numeric
75
- queue = @queues[dest]
76
- when String, Symbol
77
- queue = @queues[dest.to_s] || @queues.detect{|q| q.include?(dest.to_s)}
78
- else
79
- queue = @queues.first
80
- end
72
+ def transmit(message, original=nil)
73
+ queue = determine_queue(original)
81
74
  @fog.send_message(queue, message)
82
75
  end
83
76
 
84
77
  def confirm(message)
85
- debug "Source<#{name}> Confirming message<#{message.object_id}>"
86
- @fog.delete_message(message['SourceQueue'], message['ReceiptHandle'])
78
+ queue = determine_queue(message)
79
+ debug "Source<#{name}> Confirming message<#{message}> on Queue<#{queue}>"
80
+ m = message.is_a?(Message) ? message[:message] : message
81
+ @fog.delete_message(queue, m['ReceiptHandle'])
87
82
  end
88
83
 
89
84
  private
90
85
 
86
+ def determine_queue(obj)
87
+ queue = nil
88
+ if(obj)
89
+ if(obj.is_a?(Message))
90
+ queue = obj[:message]['SourceQueue']
91
+ else
92
+ case obj
93
+ when Numeric
94
+ queue = @queues[dest]
95
+ when String, Symbol
96
+ queue = @queues[dest.to_s] || queues.detect{|q| q.end_with?(dest.to_s)}
97
+ when Hash
98
+ queue = obj['SourceQueue']
99
+ end
100
+ end
101
+ end
102
+ queue || queues.first
103
+ end
104
+
91
105
  def queues
92
106
  if(@processable_queues)
93
107
  @queues.map do |k,v|
@@ -10,7 +10,7 @@ module Carnivore
10
10
  end
11
11
 
12
12
  def connect(*args)
13
- puts 'Test connect called'
13
+ info 'Test connect called'
14
14
  end
15
15
 
16
16
  def receive(*args)
@@ -23,7 +23,7 @@ module Carnivore
23
23
  end
24
24
 
25
25
  def transmit(message)
26
- puts "Transmit requested: #{message}"
26
+ info "Transmit requested: #{message}"
27
27
  end
28
28
 
29
29
  end
@@ -1,21 +1,27 @@
1
+ require 'celluloid'
1
2
  require 'carnivore/utils'
2
3
  require 'carnivore/callback'
3
4
  require 'carnivore/message'
4
5
 
5
6
  module Carnivore
6
- class Source < Celluloid::SupervisionGroup
7
+ class Source
7
8
 
8
9
  class SourceContainer
9
10
 
10
11
  attr_reader :klass
11
12
  attr_reader :source_hash
12
13
 
14
+ # class_name:: Name of source class
15
+ # args:: argument hash to pass to source instance
13
16
  def initialize(class_name, args={})
14
17
  @klass = class_name
15
18
  @source_hash = args || {}
16
19
  @source_hash[:callbacks] = {}
17
20
  end
18
21
 
22
+ # name:: Name of callback
23
+ # klass:: Class of callback (optional)
24
+ # Add a callback to a source via Class or block
19
25
  def add_callback(name, klass=nil, &block)
20
26
  @source_hash[:callbacks][name] = klass || block
21
27
  end
@@ -23,29 +29,58 @@ module Carnivore
23
29
 
24
30
  class << self
25
31
 
32
+ # args:: Hash
33
+ # :type -> Source type
34
+ # :args -> arguments for `Source` instance
35
+ # Builds a source container of `:type`
26
36
  def build(args={})
27
37
  [:args, :type].each do |key|
28
38
  unless(args.has_key?(key))
29
39
  raise ArgumentError.new "Missing required parameter `:#{key}`"
30
40
  end
31
41
  end
32
- require "carnivore/source/#{args[:type]}"
42
+ require Source.require_path(args[:type]) || "carnivore/source/#{args[:type]}"
33
43
  klass = args[:type].to_s.split('_').map(&:capitalize).join
34
44
  klass = Source.const_get(klass)
35
45
  args[:args][:name] ||= Celluloid.uuid
36
46
  inst = SourceContainer.new(klass, args[:args])
37
- register(inst)
47
+ register(args[:args][:name], inst)
38
48
  inst
39
49
  end
40
50
 
41
- def register(inst)
42
- @sources ||= []
43
- @sources << inst
51
+ def provide(type, require_path)
52
+ @source_klass ||= {}
53
+ @source_klass[type.to_sym] = require_path
44
54
  true
45
55
  end
46
56
 
57
+ def require_path(type)
58
+ @source_klass ||= {}
59
+ @source_klass[type.to_sym]
60
+ end
61
+
62
+ # name:: Name of source
63
+ # inst:: SourceContainer
64
+ # Register the container
65
+ def register(name, inst)
66
+ @sources ||= {}
67
+ @sources[name.to_sym] = inst
68
+ true
69
+ end
70
+
71
+ # name:: Name of registered source
72
+ # Return source container
73
+ def source(name)
74
+ if(@sources && @sources[name.to_sym])
75
+ @sources[name.to_sym]
76
+ else
77
+ raise KeyError.new("Requested named source is not registered: #{name}")
78
+ end
79
+ end
80
+
81
+ # Registered containers
47
82
  def sources
48
- @sources || []
83
+ @sources ? @sources.values : []
49
84
  end
50
85
  end
51
86
 
@@ -61,10 +96,10 @@ module Carnivore
61
96
  def initialize(args={})
62
97
  @callbacks = []
63
98
  @callback_names = {}
64
- @auto_process = true
99
+ @auto_process = args.fetch(:auto_process, true)
100
+ @auto_confirm = !!args[:auto_confirm]
65
101
  @callback_supervisor = Celluloid::SupervisionGroup.run!
66
102
  @name = args[:name] || Celluloid.uuid
67
- @auto_confirm = !!args[:auto_confirm]
68
103
  if(args[:callbacks])
69
104
  args[:callbacks].each do |name, block|
70
105
  add_callback(name, block)
@@ -102,14 +137,16 @@ module Carnivore
102
137
  raise NoMethodError.new('Abstract method not valid for runtime')
103
138
  end
104
139
 
105
- def transmit(message)
140
+ def transmit(message, original_message, args={})
106
141
  raise NoMethodError.new('Abstract method not valid for runtime')
107
142
  end
108
143
 
144
+ def confirm(message)
145
+ debug 'No custom confirm declared'
146
+ end
147
+
109
148
  def terminate
110
- if(@callback_supervisor)
111
- @callback_supervisor.actors.map(&:terminate)
112
- end
149
+ @callback_supervisor.finalize
113
150
  end
114
151
 
115
152
  def add_callback(name, block_or_class)
@@ -1,3 +1,5 @@
1
+ require 'celluloid'
2
+
1
3
  module Carnivore
2
4
  module Utils
3
5
 
@@ -5,8 +7,13 @@ module Carnivore
5
7
  def symbolize_hash(hash)
6
8
  Hash[*(
7
9
  hash.map do |k,v|
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
8
15
  [
9
- k.gsub(/(?<![A-Z])([A-Z])/, '_\1').sub(/^_/, '').downcase.to_sym,
16
+ key,
10
17
  v.is_a?(Hash) ? symbolize_hash(v) : v
11
18
  ]
12
19
  end.flatten(1)
@@ -1,5 +1,5 @@
1
1
  module Carnivore
2
2
  class Version < Gem::Version
3
3
  end
4
- VERSION = Version.new('0.1.6')
4
+ VERSION = Version.new('0.1.8')
5
5
  end
data/test/spec.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'celluloid'
2
+ Celluloid.logger.level = 4
3
+
4
+ Dir.glob(File.join(File.expand_path(File.dirname(__FILE__)), 'specs/*.rb')).each do |path|
5
+ require path
6
+ end
7
+
8
+ MiniTest::Spec.before do
9
+ Celluloid.shutdown
10
+ Celluloid.boot
11
+ end
@@ -0,0 +1,31 @@
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
@@ -0,0 +1,13 @@
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
@@ -0,0 +1,29 @@
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
@@ -0,0 +1,82 @@
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
@@ -0,0 +1,47 @@
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
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.1.6
4
+ version: 0.1.8
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: 2013-09-30 00:00:00.000000000 Z
12
+ date: 2013-10-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -111,13 +111,19 @@ files:
111
111
  - lib/carnivore/utils.rb
112
112
  - lib/carnivore/container.rb
113
113
  - lib/carnivore.rb
114
+ - test/spec.rb
115
+ - test/specs/source.rb
116
+ - test/specs/config.rb
117
+ - test/specs/message.rb
118
+ - test/specs/utils.rb
119
+ - test/specs/container.rb
114
120
  - examples/test_http.rb
115
121
  - examples/test_class.rb
116
122
  - examples/test_block.rb
117
123
  - Gemfile
118
124
  - README.md
125
+ - carnivore-0.1.6.gem
119
126
  - CHANGELOG.md
120
- - Gemfile.lock
121
127
  - carnivore.gemspec
122
128
  homepage: https://github.com/heavywater/carnivore
123
129
  licenses: []
data/Gemfile.lock DELETED
@@ -1,64 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- carnivore (0.1.0)
5
- bunny
6
- celluloid
7
- fog
8
- mixlib-config
9
- reel
10
-
11
- GEM
12
- remote: https://rubygems.org/
13
- specs:
14
- builder (3.2.2)
15
- bunny (0.8.0)
16
- celluloid (0.15.1)
17
- timers (~> 1.1.0)
18
- celluloid-io (0.15.0)
19
- celluloid (>= 0.15.0)
20
- nio4r (>= 0.5.0)
21
- certified (0.1.1)
22
- excon (0.25.3)
23
- fog (1.15.0)
24
- builder
25
- excon (~> 0.25.0)
26
- formatador (~> 0.2.0)
27
- mime-types
28
- multi_json (~> 1.0)
29
- net-scp (~> 1.1)
30
- net-ssh (>= 2.1.3)
31
- nokogiri (~> 1.5)
32
- ruby-hmac
33
- formatador (0.2.4)
34
- http (0.4.0)
35
- certified
36
- http_parser.rb
37
- http_parser.rb (0.5.3)
38
- mime-types (1.25)
39
- mini_portile (0.5.1)
40
- mixlib-config (1.1.2)
41
- multi_json (1.8.0)
42
- net-scp (1.1.2)
43
- net-ssh (>= 2.6.5)
44
- net-ssh (2.6.8)
45
- nio4r (0.5.0)
46
- nokogiri (1.6.0)
47
- mini_portile (~> 0.5.0)
48
- rack (1.5.2)
49
- reel (0.3.0)
50
- celluloid-io (>= 0.8.0)
51
- http (>= 0.2.0)
52
- http_parser.rb (>= 0.5.3)
53
- rack (>= 1.4.0)
54
- websocket_parser (>= 0.1.0)
55
- ruby-hmac (0.4.0)
56
- timers (1.1.0)
57
- websocket_parser (0.1.4)
58
- http
59
-
60
- PLATFORMS
61
- ruby
62
-
63
- DEPENDENCIES
64
- carnivore!