carnivore 0.1.6 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
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!