serf 0.11.0 → 0.12.0

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