serf 0.11.0 → 0.12.0

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.
Files changed (42) hide show
  1. data/Gemfile +1 -0
  2. data/Guardfile +1 -2
  3. data/README.md +131 -15
  4. data/example/components/logger.serf +7 -0
  5. data/example/serfs/create_widget.serf +24 -0
  6. data/lib/serf/builder.rb +7 -6
  7. data/lib/serf/loader.rb +16 -0
  8. data/lib/serf/loader/loader.rb +103 -0
  9. data/lib/serf/loader/registry.rb +86 -0
  10. data/lib/serf/middleware/error_handler.rb +4 -4
  11. data/lib/serf/middleware/parcel_freezer.rb +3 -6
  12. data/lib/serf/middleware/parcel_masher.rb +3 -6
  13. data/lib/serf/middleware/policy_checker.rb +3 -5
  14. data/lib/serf/middleware/request_timer.rb +47 -0
  15. data/lib/serf/middleware/uuid_tagger.rb +3 -5
  16. data/lib/serf/parcel_builder.rb +3 -6
  17. data/lib/serf/serfer.rb +5 -7
  18. data/lib/serf/util/uuidable.rb +3 -6
  19. data/lib/serf/version.rb +1 -1
  20. data/serf.gemspec +1 -0
  21. data/spec/serf/builder_spec.rb +4 -5
  22. data/spec/serf/errors/policy_failure_spec.rb +1 -1
  23. data/spec/serf/loader/loader_spec.rb +73 -0
  24. data/spec/serf/loader/registry_spec.rb +62 -0
  25. data/spec/serf/loader_spec.rb +57 -0
  26. data/spec/serf/middleware/error_handler_spec.rb +2 -2
  27. data/spec/serf/middleware/parcel_freezer_spec.rb +2 -2
  28. data/spec/serf/middleware/parcel_masher_spec.rb +5 -5
  29. data/spec/serf/middleware/policy_checker_spec.rb +3 -3
  30. data/spec/serf/middleware/request_timer_spec.rb +43 -0
  31. data/spec/serf/middleware/uuid_tagger_spec.rb +4 -4
  32. data/spec/serf/parcel_builder_spec.rb +7 -7
  33. data/spec/serf/serfer_spec.rb +4 -4
  34. data/spec/serf/util/error_handling_spec.rb +3 -3
  35. data/spec/serf/util/null_object_spec.rb +4 -4
  36. data/spec/serf/util/protected_call_spec.rb +4 -4
  37. data/spec/serf/util/uuidable_spec.rb +15 -15
  38. data/spec/support/factories.rb +7 -0
  39. metadata +34 -9
  40. data/lib/serf/util/options_extraction.rb +0 -117
  41. data/spec/serf/util/options_extraction_spec.rb +0 -62
  42. data/spec/support/options_extraction_wrapper.rb +0 -10
@@ -1,4 +1,5 @@
1
1
  require 'hashie'
2
+ require 'optser'
2
3
 
3
4
  require 'serf/parcel_builder'
4
5
  require 'serf/util/error_handling'
@@ -13,7 +14,6 @@ module Middleware
13
14
  #
14
15
  class ErrorHandler
15
16
  include Serf::Util::ErrorHandling
16
- include Serf::Util::OptionsExtraction
17
17
 
18
18
  attr_reader :app
19
19
  attr_reader :parcel_builder
@@ -23,12 +23,12 @@ module Middleware
23
23
  # @param app the app
24
24
  #
25
25
  def initialize(app, *args)
26
- extract_options! args
26
+ opts = Optser.extract_options! args
27
27
  @app = app
28
28
 
29
29
  # Tunable knobs
30
- @parcel_builder = opts(:parcel_builder) { Serf::ParcelBuilder.new }
31
- @uuidable = opts(:uuidable) { Serf::Util::Uuidable.new }
30
+ @parcel_builder = opts.get(:parcel_builder) { Serf::ParcelBuilder.new }
31
+ @uuidable = opts.get(:uuidable) { Serf::Util::Uuidable.new }
32
32
  end
33
33
 
34
34
  def call(parcel)
@@ -1,6 +1,5 @@
1
1
  require 'ice_nine'
2
-
3
- require 'serf/util/options_extraction'
2
+ require 'optser'
4
3
 
5
4
  module Serf
6
5
  module Middleware
@@ -9,8 +8,6 @@ module Middleware
9
8
  # Middleware to add uuids to the headers of the parcel hash.
10
9
  #
11
10
  class ParcelFreezer
12
- include Serf::Util::OptionsExtraction
13
-
14
11
  attr_reader :app
15
12
  attr_reader :freezer
16
13
 
@@ -18,9 +15,9 @@ module Middleware
18
15
  # @param app the app
19
16
  #
20
17
  def initialize(app, *args)
21
- extract_options! args
18
+ opts = Optser.extract_options! args
22
19
  @app = app
23
- @freezer = opts :freezer, IceNine
20
+ @freezer = opts.get :freezer, IceNine
24
21
  end
25
22
 
26
23
  ##
@@ -1,6 +1,5 @@
1
1
  require 'hashie'
2
-
3
- require 'serf/util/options_extraction'
2
+ require 'optser'
4
3
 
5
4
  module Serf
6
5
  module Middleware
@@ -9,8 +8,6 @@ module Middleware
9
8
  # Middleware to add uuids to the headers of the parcel hash.
10
9
  #
11
10
  class ParcelMasher
12
- include Serf::Util::OptionsExtraction
13
-
14
11
  attr_reader :app
15
12
  attr_reader :masher_class
16
13
 
@@ -18,9 +15,9 @@ module Middleware
18
15
  # @param app the app
19
16
  #
20
17
  def initialize(app, *args)
21
- extract_options! args
18
+ opts = Optser.extract_options! args
22
19
  @app = app
23
- @masher_class = opts :masher_class, Hashie::Mash
20
+ @masher_class = opts.get :masher_class, Hashie::Mash
24
21
  end
25
22
 
26
23
  ##
@@ -1,18 +1,16 @@
1
- require 'serf/util/options_extraction'
1
+ require 'optser'
2
2
 
3
3
  module Serf
4
4
  module Middleware
5
5
 
6
6
  class PolicyChecker
7
- include Serf::Util::OptionsExtraction
8
-
9
7
  attr_reader :app
10
8
  attr_reader :policy_chain
11
9
 
12
10
  def initialize(app, *args)
13
- extract_options! args
11
+ opts = Optser.extract_options! args
14
12
  @app = app
15
- @policy_chain = opts :policy_chain, []
13
+ @policy_chain = opts.get :policy_chain, []
16
14
  end
17
15
 
18
16
  ##
@@ -0,0 +1,47 @@
1
+ require 'optser'
2
+
3
+ module Serf
4
+ module Middleware
5
+
6
+ class RequestTimer
7
+ attr_reader :app
8
+ attr_reader :timer
9
+
10
+ def initialize(app, *args)
11
+ opts = Optser.extract_options! args
12
+ @app = app
13
+ @timer = opts.get :timer, Serf::Middleware::RequestTimer::Timer
14
+ end
15
+
16
+ def call(parcel)
17
+ t = timer.start
18
+ response_parcel = app.call parcel
19
+ response_parcel.headers[:elapsed_time] = t.mark
20
+ return response_parcel
21
+ end
22
+
23
+ class Timer
24
+ attr_reader :start_time
25
+
26
+ class << self
27
+ alias_method :start, :new
28
+ end
29
+
30
+ def initialize
31
+ @start_time = now
32
+ end
33
+
34
+ def mark
35
+ (now - @start_time).to_i
36
+ end
37
+
38
+ def now
39
+ Time.now.to_f * 1_000_000
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -1,6 +1,6 @@
1
1
  require 'hashie'
2
+ require 'optser'
2
3
 
3
- require 'serf/util/options_extraction'
4
4
  require 'serf/util/uuidable'
5
5
 
6
6
  module Serf
@@ -10,8 +10,6 @@ module Middleware
10
10
  # Middleware to add uuids to the headers of the parcel hash.
11
11
  #
12
12
  class UuidTagger
13
- include Serf::Util::OptionsExtraction
14
-
15
13
  attr_reader :app
16
14
  attr_reader :uuidable
17
15
 
@@ -19,9 +17,9 @@ module Middleware
19
17
  # @param app the app
20
18
  #
21
19
  def initialize(app, *args)
22
- extract_options! args
20
+ opts = Optser.extract_options! args
23
21
  @app = app
24
- @uuidable = opts(:uuidable) { Serf::Util::Uuidable.new }
22
+ @uuidable = opts.get(:uuidable) { Serf::Util::Uuidable.new }
25
23
  end
26
24
 
27
25
  def call(parcel)
@@ -1,6 +1,5 @@
1
1
  require 'hashie'
2
-
3
- require 'serf/util/options_extraction'
2
+ require 'optser'
4
3
 
5
4
  module Serf
6
5
 
@@ -8,14 +7,12 @@ module Serf
8
7
  # Builds Parcels as Hashie::Mash objects with headers and messages.
9
8
  #
10
9
  class ParcelBuilder
11
- include Serf::Util::OptionsExtraction
12
-
13
10
  attr_reader :mash_class
14
11
 
15
12
  def initialize(*args)
16
- extract_options! args
13
+ opts = Optser.extract_options! args
17
14
 
18
- @mash_class = opts :mash_class, Hashie::Mash
15
+ @mash_class = opts.get :mash_class, Hashie::Mash
19
16
  end
20
17
 
21
18
  def build(headers=nil, message=nil)
data/lib/serf/serfer.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'hashie'
2
+ require 'optser'
2
3
 
3
4
  require 'serf/parcel_builder'
4
- require 'serf/util/options_extraction'
5
5
  require 'serf/util/uuidable'
6
6
 
7
7
  module Serf
@@ -10,21 +10,19 @@ module Serf
10
10
  # Class to drive the Interactor execution.
11
11
  #
12
12
  class Serfer
13
- include Serf::Util::OptionsExtraction
14
-
15
13
  attr_reader :interactor
16
14
  attr_reader :parcel_builder
17
15
  attr_reader :uuidable
18
16
 
19
17
  def initialize(interactor, *args)
20
- extract_options! args
18
+ opts = Optser.extract_options! args
21
19
 
22
20
  # How to and when to handle requests
23
21
  @interactor = interactor
24
22
 
25
23
  # Tunable knobs
26
- @parcel_builder = opts(:parcel_builder) { Serf::ParcelBuilder.new }
27
- @uuidable = opts(:uuidable) { Serf::Util::Uuidable.new }
24
+ @parcel_builder = opts.get(:parcel_builder) { Serf::ParcelBuilder.new }
25
+ @uuidable = opts.get(:uuidable) { Serf::Util::Uuidable.new }
28
26
  end
29
27
 
30
28
  ##
@@ -35,7 +33,7 @@ module Serf
35
33
  message = parcel[:message]
36
34
 
37
35
  # 1. Execute interactor
38
- response_message, response_kind = interactor.call message
36
+ response_kind, response_message = interactor.call message
39
37
 
40
38
  # 2. Create the response headers
41
39
  response_headers = uuidable.create_uuids headers
@@ -1,9 +1,8 @@
1
1
  require 'base64'
2
2
  require 'hashie'
3
+ require 'optser'
3
4
  require 'uuidtools'
4
5
 
5
- require 'serf/util/options_extraction'
6
-
7
6
  module Serf
8
7
  module Util
9
8
 
@@ -14,13 +13,11 @@ module Util
14
13
  # base64 encoded UUIDs without trailing '='.
15
14
  #
16
15
  class Uuidable
17
- include Serf::Util::OptionsExtraction
18
-
19
16
  attr_reader :uuid_tool
20
17
 
21
18
  def initialize(*args)
22
- extract_options! args
23
- @uuid_tool = opts :uuid_tool, UUIDTools::UUID
19
+ opts = Optser.extract_options! args
20
+ @uuid_tool = opts.get :uuid_tool, UUIDTools::UUID
24
21
  end
25
22
 
26
23
  ##
data/lib/serf/version.rb CHANGED
@@ -2,7 +2,7 @@ module Serf
2
2
 
3
3
  module Version
4
4
  MAJOR = 0
5
- MINOR = 11
5
+ MINOR = 12
6
6
  PATCH = 0
7
7
  BUILD = nil
8
8
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join '.'
data/serf.gemspec CHANGED
@@ -24,5 +24,6 @@ Gem::Specification.new do |gem|
24
24
 
25
25
  gem.add_runtime_dependency('hashie', ['>= 1.2.0'])
26
26
  gem.add_runtime_dependency('ice_nine', ['>= 0.4.0'])
27
+ gem.add_runtime_dependency('optser', ['>= 0.1.0'])
27
28
  gem.add_runtime_dependency('uuidtools', ['>= 2.1.3'])
28
29
  end
@@ -11,7 +11,7 @@ describe Serf::Builder do
11
11
  }
12
12
  let(:app) {
13
13
  lambda { |parcel|
14
- return parcel, response_kind
14
+ return response_kind, parcel
15
15
  }
16
16
  }
17
17
  subject {
@@ -21,7 +21,7 @@ describe Serf::Builder do
21
21
  describe '#to_app' do
22
22
 
23
23
  it 'builds a callable app' do
24
- subject.to_app.should respond_to(:call)
24
+ expect(subject.to_app).to respond_to(:call)
25
25
  end
26
26
 
27
27
  end
@@ -32,9 +32,8 @@ describe Serf::Builder do
32
32
 
33
33
  it 'runs the app' do
34
34
  response = subject.to_app.call request_parcel
35
- puts response.to_hash
36
- response.message.should == request_parcel.message
37
- response.headers.kind.should == response_kind
35
+ expect(response.message).to eq(request_parcel.message)
36
+ expect(response.headers.kind).to eq(response_kind)
38
37
  end
39
38
 
40
39
  end
@@ -5,7 +5,7 @@ require 'serf/errors/policy_failure'
5
5
  describe Serf::Errors::PolicyFailure do
6
6
 
7
7
  it {
8
- should be_a_kind_of(RuntimeError)
8
+ expect(subject).to be_a_kind_of(RuntimeError)
9
9
  }
10
10
 
11
11
  end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ require 'serf/loader/loader'
4
+
5
+ describe Serf::Loader::Loader do
6
+ let(:root_library_path) {
7
+ File.join(File.dirname(__FILE__), '../../..')
8
+ }
9
+
10
+ context '#serfup Serf Map' do
11
+ let(:random_message) {
12
+ FactoryGirl.create :random_message
13
+ }
14
+ subject {
15
+ Serf::Loader::Loader.new.serfup(
16
+ globs: [
17
+ 'example/**/*.serf'
18
+ ],
19
+ serfs: [
20
+ 'subsystem/requests/create_widget'
21
+ ],
22
+ base_path: root_library_path,
23
+ env: {
24
+ success_message: random_message
25
+ })
26
+ }
27
+
28
+ it 'loads the Serfs into a frozen Serf Map' do
29
+ expect(subject.frozen?).to be_true
30
+ end
31
+
32
+ it 'loads our only serf into the map' do
33
+ expect(subject.size).to eq(1)
34
+ end
35
+
36
+ it 'gives us a proper callable serf' do
37
+ serf = subject['subsystem/requests/create_widget']
38
+ expect(subject).to_not be_nil
39
+
40
+ results = serf.call({})
41
+ expect(results).to_not be_nil
42
+ expect(results.headers.kind).to_not eq('serf/events/caught_error')
43
+ end
44
+
45
+ it 'returns nil on not found parcel kind' do
46
+ expect(subject[nil]).to be_nil
47
+ end
48
+
49
+ it 'passes in a good environment' do
50
+ serf = subject['subsystem/requests/create_widget']
51
+ results = serf.call({})
52
+ expect(results.headers.kind).to eq('subsystem/events/mywidget_created')
53
+ expect(results.message.success_message).to eq(random_message)
54
+ end
55
+ end
56
+
57
+ context '#serfup Serf Map with missing Serf' do
58
+ let(:serfup_config) {
59
+ Hashie::Mash.new(
60
+ globs: [],
61
+ serfs: [
62
+ 'subsystem/requests/create_widget'
63
+ ])
64
+ }
65
+
66
+ it 'raises an error' do
67
+ expect {
68
+ Serf::Loader::Loader.new.serfup serfup_config
69
+ }.to raise_error('Missing Serf: subsystem/requests/create_widget')
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ require 'serf/loader/registry'
4
+
5
+ describe Serf::Loader::Registry do
6
+ let(:random_message) {
7
+ FactoryGirl.create :random_message
8
+ }
9
+
10
+ context '#add' do
11
+ it 'registers a named block, but does not call it' do
12
+ uncalled_mock = double 'Uncalled Mock'
13
+ expect(subject.blocks.size).to eq(0)
14
+ expect(subject.values.size).to eq(0)
15
+ subject.add 'component_name' do
16
+ uncalled_mock
17
+ end
18
+ expect(subject.blocks.size).to eq(1)
19
+ expect(subject.values.size).to eq(0)
20
+ end
21
+ end
22
+
23
+ context '#[]' do
24
+ it 'evaluates a block on lookup' do
25
+ expect(subject.blocks.size).to eq(0)
26
+ expect(subject.values.size).to eq(0)
27
+ subject.add 'component_name' do
28
+ random_message
29
+ end
30
+ expect(subject['component_name']).to eq(random_message)
31
+ expect(subject.blocks.size).to eq(0)
32
+ expect(subject.values.size).to eq(1)
33
+ end
34
+
35
+ it 'returns memoized block value' do
36
+ call_once = double 'Callable'
37
+ call_once.should_receive(:call).once.and_return(random_message)
38
+
39
+ # Make the add to the registry
40
+ expect(subject.blocks.size).to eq(0)
41
+ expect(subject.values.size).to eq(0)
42
+ subject.add 'component_name' do
43
+ call_once.call
44
+ end
45
+
46
+ # First call
47
+ expect(subject['component_name']).to eq(random_message)
48
+ expect(subject.blocks.size).to eq(0)
49
+ expect(subject.values.size).to eq(1)
50
+
51
+ # This should be memoized
52
+ expect(subject['component_name']).to eq(random_message)
53
+ expect(subject.blocks.size).to eq(0)
54
+ expect(subject.values.size).to eq(1)
55
+ end
56
+
57
+ it 'returns nil on not found' do
58
+ expect(subject[random_message]).to be_nil
59
+ end
60
+ end
61
+
62
+ end