zack 0.3.2 → 0.3.3
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/History.txt +12 -0
- data/README +1 -1
- data/Rakefile +1 -1
- data/example/client.rb +2 -1
- data/example/server.rb +1 -2
- data/lib/zack.rb +0 -3
- data/lib/zack/client.rb +60 -9
- data/lib/zack/server.rb +97 -11
- data/spec/acceptance/exception_handling_spec.rb +78 -0
- data/spec/{integration → acceptance}/regression_spec.rb +9 -8
- data/spec/{integration → acceptance}/server_runner_spec.rb +11 -0
- data/spec/lib/zack/server_spec.rb +4 -3
- data/spec/spec_helper.rb +2 -0
- metadata +21 -22
- data/example/pubsub.rb +0 -35
- data/lib/zack/target.rb +0 -68
- data/lib/zack/transparent_proxy.rb +0 -22
    
        data/History.txt
    CHANGED
    
    | @@ -1,3 +1,15 @@ | |
| 1 | 
            +
            = 0.3.3 / 29Nov2011
         | 
| 2 | 
            +
              + Updates to the cod rewrite, this eliminates the dependency on 
         | 
| 3 | 
            +
                beanstalk-client.
         | 
| 4 | 
            +
                
         | 
| 5 | 
            +
              + Works and is designed only for beanstalk now. This allows usage of some
         | 
| 6 | 
            +
                advanced queuing features. 
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              + Breaking change: Default server is now 'localhost:11300', not a server on
         | 
| 9 | 
            +
                the LAN we might not even own.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              + Breaking change: Exception block now has two arguments. 
         | 
| 12 | 
            +
             | 
| 1 13 | 
             
            = 0.3.2 / 31Aug2011
         | 
| 2 14 |  | 
| 3 15 | 
             
              . gemspec fix
         | 
    
        data/README
    CHANGED
    
    
    
        data/Rakefile
    CHANGED
    
    
    
        data/example/client.rb
    CHANGED
    
    
    
        data/example/server.rb
    CHANGED
    
    
    
        data/lib/zack.rb
    CHANGED
    
    
    
        data/lib/zack/client.rb
    CHANGED
    
    | @@ -12,21 +12,19 @@ module Zack | |
| 12 12 | 
             
                # :server :: beanstalkd server location url
         | 
| 13 13 | 
             
                # :only :: ignores all messages not in this hash
         | 
| 14 14 | 
             
                # :with_answer :: these messages wait for an answer from the service
         | 
| 15 | 
            +
                # :timeout :: How long to wait for an answer
         | 
| 15 16 | 
             
                #
         | 
| 16 17 | 
             
                def initialize(tube_name, opts={})
         | 
| 17 | 
            -
                  server = opts[:server] || 'beanstalk:11300'
         | 
| 18 18 | 
             
                  # Only respond to these messages
         | 
| 19 19 | 
             
                  @only        = opts[:only] || proc { true }
         | 
| 20 20 | 
             
                  # These have answers (wait for the server to answer)
         | 
| 21 21 | 
             
                  @with_answer = opts[:with_answer] || []
         | 
| 22 | 
            +
                  @timeout     = opts[:timeout]
         | 
| 23 | 
            +
                  
         | 
| 24 | 
            +
                  @tube_name  = tube_name
         | 
| 25 | 
            +
                  @server     = opts[:server] || 'localhost:11300'
         | 
| 22 26 |  | 
| 23 | 
            -
                   | 
| 24 | 
            -
                  unless @with_answer.empty?
         | 
| 25 | 
            -
                    @incoming = Cod.beanstalk(server, 
         | 
| 26 | 
            -
                      UniqueName.new(tube_name))
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
                
         | 
| 29 | 
            -
                  @service = Cod::Client.new(@outgoing, @incoming, 1)
         | 
| 27 | 
            +
                  connect
         | 
| 30 28 | 
             
                end
         | 
| 31 29 |  | 
| 32 30 | 
             
                def respond_to?(msg)
         | 
| @@ -37,6 +35,59 @@ module Zack | |
| 37 35 | 
             
                  @with_answer.include?(sym.to_sym)
         | 
| 38 36 | 
             
                end
         | 
| 39 37 |  | 
| 40 | 
            -
                 | 
| 38 | 
            +
                def method_missing(sym, *args, &block)
         | 
| 39 | 
            +
                  super unless respond_to?(sym)
         | 
| 40 | 
            +
             
         | 
| 41 | 
            +
                  raise ArgumentError, "Can't call methods remotely with a block" if block
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  if has_answer?(sym)
         | 
| 44 | 
            +
                    with_timeout do
         | 
| 45 | 
            +
                      return service.call([sym, args])
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  else
         | 
| 48 | 
            +
                    service.notify [sym, args]
         | 
| 49 | 
            +
                    return nil
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                rescue Timeout::Error => ex
         | 
| 52 | 
            +
                  raise Zack::ServiceTimeout, "The service took too long to answer (>#{@timeout || 1}s)."
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
                
         | 
| 55 | 
            +
                def close
         | 
| 56 | 
            +
                  @service.close
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              private 
         | 
| 59 | 
            +
                def with_timeout
         | 
| 60 | 
            +
                  if @timeout
         | 
| 61 | 
            +
                    begin
         | 
| 62 | 
            +
                      timeout(@timeout) do
         | 
| 63 | 
            +
                        yield
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
                    rescue Timeout::Error 
         | 
| 66 | 
            +
                      # The timeout might have occurred at any place at all. This means
         | 
| 67 | 
            +
                      # that the connection is probably in an invalid state at this point. 
         | 
| 68 | 
            +
                      reconnect
         | 
| 69 | 
            +
                      raise Zack::ServiceTimeout, 
         | 
| 70 | 
            +
                        "The server took longer than #{@timeout} seconds to respond."
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  else
         | 
| 73 | 
            +
                    yield
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
                
         | 
| 77 | 
            +
                def reconnect
         | 
| 78 | 
            +
                  @service.close
         | 
| 79 | 
            +
                  connect
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
                def connect
         | 
| 82 | 
            +
                  @outgoing = Cod.beanstalk(@tube_name, @server)
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  unless @with_answer.empty?
         | 
| 85 | 
            +
                    @incoming = Cod.beanstalk(
         | 
| 86 | 
            +
                      UniqueName.new(@tube_name), 
         | 
| 87 | 
            +
                      @server)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                
         | 
| 90 | 
            +
                  @service = @outgoing.client(@incoming)
         | 
| 91 | 
            +
                end
         | 
| 41 92 | 
             
              end
         | 
| 42 93 | 
             
            end
         | 
    
        data/lib/zack/server.rb
    CHANGED
    
    | @@ -2,22 +2,108 @@ | |
| 2 2 | 
             
            module Zack
         | 
| 3 3 | 
             
              # Server side for RPC calls. 
         | 
| 4 4 | 
             
              #
         | 
| 5 | 
            -
              class Server | 
| 5 | 
            +
              class Server
         | 
| 6 6 | 
             
                attr_reader :service
         | 
| 7 | 
            -
             | 
| 7 | 
            +
                attr_reader :factory
         | 
| 8 | 
            +
                attr_reader :server
         | 
| 9 | 
            +
                
         | 
| 10 | 
            +
                # Initializes a zack server. To specify which class should be the target
         | 
| 11 | 
            +
                # of the RPC call, you must either give the :factory or the :simple
         | 
| 12 | 
            +
                # argument. 
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # :simple expects a class. This class will be constructed each time a
         | 
| 15 | 
            +
                # request is made. Then the method will be called on the class. 
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # :factory expects a callable (a block or something that has #call) and is
         | 
| 18 | 
            +
                # passed the control object for the request (see Cod for an explanation of
         | 
| 19 | 
            +
                # this). You can chose to ignore the control and just use the block to
         | 
| 20 | 
            +
                # produce an object that is linked to the rest of your program. Or you can
         | 
| 21 | 
            +
                # link to the rest of the program and the control at the same time. 
         | 
| 22 | 
            +
                #
         | 
| 23 | 
            +
                # Note that in any case, one object instance _per call_ is created. This
         | 
| 24 | 
            +
                # is to discourage creating stateful servers. If you still want to do
         | 
| 25 | 
            +
                # that, well you will just have to code around the limitation, now won't
         | 
| 26 | 
            +
                # you. 
         | 
| 27 | 
            +
                #
         | 
| 8 28 | 
             
                def initialize(tube_name, opts={})
         | 
| 9 | 
            -
                   | 
| 10 | 
            -
             | 
| 11 | 
            -
                   | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 29 | 
            +
                  @server = opts[:server]
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  if opts.has_key? :factory
         | 
| 32 | 
            +
                    @factory = opts[:factory]
         | 
| 33 | 
            +
                  elsif opts.has_key? :simple
         | 
| 34 | 
            +
                    klass = opts[:simple]
         | 
| 35 | 
            +
                    @factory = lambda { |ctl| klass.new }
         | 
| 36 | 
            +
                  else
         | 
| 37 | 
            +
                    raise ArgumentError, "Either :factory or :simple argument must be given." 
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             
         | 
| 40 | 
            +
                  channel = Cod.beanstalk(tube_name, server)
         | 
| 41 | 
            +
                  @service = channel.service
         | 
| 42 | 
            +
                end 
         | 
| 43 | 
            +
               
         | 
| 15 44 | 
             
                # Handles exactly one request. 
         | 
| 16 45 | 
             
                #
         | 
| 17 | 
            -
                def handle_request
         | 
| 18 | 
            -
                  service.one { |(sym, args)| | 
| 19 | 
            -
                     | 
| 46 | 
            +
                def handle_request(exception_handler=nil)
         | 
| 47 | 
            +
                  service.one { |(sym, args), control|
         | 
| 48 | 
            +
                    exception_handling(exception_handler, control) do
         | 
| 49 | 
            +
                      # p [sym, args]
         | 
| 50 | 
            +
                      process_request(control, sym, args)
         | 
| 51 | 
            +
                    end
         | 
| 20 52 | 
             
                  }
         | 
| 21 53 | 
             
                end
         | 
| 54 | 
            +
                
         | 
| 55 | 
            +
                # Processes exactly one request, but doesn't define how the request gets
         | 
| 56 | 
            +
                # here. 
         | 
| 57 | 
            +
                #
         | 
| 58 | 
            +
                def process_request(control, sym, args)
         | 
| 59 | 
            +
                  instance = factory.call(control)
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  instance.send(sym, *args)
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                # Runs the server and keeps running until the world ends (or the process,
         | 
| 65 | 
            +
                # whichever comes first). If you pass a non-nil messages argument, the
         | 
| 66 | 
            +
                # server will process that many messages and then quit. (Maybe you will
         | 
| 67 | 
            +
                # want to respawn the server from time to time?)
         | 
| 68 | 
            +
                #
         | 
| 69 | 
            +
                # Any exception that is raised inside the RPC code will be passed to the
         | 
| 70 | 
            +
                # exception_handler block: 
         | 
| 71 | 
            +
                # 
         | 
| 72 | 
            +
                #   server.run do |exception, control|
         | 
| 73 | 
            +
                #     # control is the service control object from cod. You can exercise
         | 
| 74 | 
            +
                #     # fine grained message control using this. 
         | 
| 75 | 
            +
                #     log.fatal exception
         | 
| 76 | 
            +
                #   end
         | 
| 77 | 
            +
                #
         | 
| 78 | 
            +
                # If you don't reraise exceptions from the exception handler block, they
         | 
| 79 | 
            +
                # will be caught and the server will stay running. 
         | 
| 80 | 
            +
                #
         | 
| 81 | 
            +
                def run(messages=nil, &exception_handler)
         | 
| 82 | 
            +
                  loop do
         | 
| 83 | 
            +
                    handle_request(exception_handler)
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                    if messages
         | 
| 86 | 
            +
                      messages -= 1
         | 
| 87 | 
            +
                      break if messages <= 0
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              private
         | 
| 93 | 
            +
                # Defines how the server handles exception. 
         | 
| 94 | 
            +
                #
         | 
| 95 | 
            +
                def exception_handling(exception_handler, control)
         | 
| 96 | 
            +
                  begin
         | 
| 97 | 
            +
                    yield
         | 
| 98 | 
            +
                  rescue => exception
         | 
| 99 | 
            +
                    # If we have an exception handler, it gets handed all the exceptions. 
         | 
| 100 | 
            +
                    # No exceptions stop the operation. 
         | 
| 101 | 
            +
                    if exception_handler
         | 
| 102 | 
            +
                      exception_handler.call(exception, control)
         | 
| 103 | 
            +
                    else
         | 
| 104 | 
            +
                      raise
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
                end
         | 
| 22 108 | 
             
              end
         | 
| 23 109 | 
             
            end
         | 
| @@ -0,0 +1,78 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "Exception handling" do
         | 
| 4 | 
            +
              # Clear old messages (test isolation)
         | 
| 5 | 
            +
              before(:each) { 
         | 
| 6 | 
            +
                conn = Beanstalk::Connection.new(BEANSTALK_CONNECTION, 'exceptions')
         | 
| 7 | 
            +
                while conn.peek_ready
         | 
| 8 | 
            +
                  conn.reserve.delete
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
                conn.close
         | 
| 11 | 
            +
              }
         | 
| 12 | 
            +
              
         | 
| 13 | 
            +
              SimpleService = Struct.new(:control, :retry_log) do
         | 
| 14 | 
            +
                # Used for the exception block specs
         | 
| 15 | 
            +
                def do_something
         | 
| 16 | 
            +
                  fail
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                # Used for the handler specs
         | 
| 20 | 
            +
                def retry_message
         | 
| 21 | 
            +
                  retries = (retry_log[control.msg_id] += 1)
         | 
| 22 | 
            +
                  if retries < 2
         | 
| 23 | 
            +
                    control.retry
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
              
         | 
| 28 | 
            +
              let!(:retry_log) { Hash.new(0) }
         | 
| 29 | 
            +
              
         | 
| 30 | 
            +
              let(:client) { Zack::Client.new('exceptions', 
         | 
| 31 | 
            +
                server: BEANSTALK_CONNECTION) }
         | 
| 32 | 
            +
              let(:server) { Zack::Server.new('exceptions', 
         | 
| 33 | 
            +
                server: BEANSTALK_CONNECTION, 
         | 
| 34 | 
            +
                factory: proc { |c| SimpleService.new(c, retry_log) }) }
         | 
| 35 | 
            +
              
         | 
| 36 | 
            +
              describe 'block given to #run' do
         | 
| 37 | 
            +
                it "can retry the message" do
         | 
| 38 | 
            +
                  client.do_something
         | 
| 39 | 
            +
                  
         | 
| 40 | 
            +
                  retried = false
         | 
| 41 | 
            +
                  server.run(2) do |exception, control|
         | 
| 42 | 
            +
                    retried ? control.delete : control.retry
         | 
| 43 | 
            +
                    retried = true
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                        
         | 
| 46 | 
            +
                  retried.should == true
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
                it "can use msg_id to retry n times" do
         | 
| 49 | 
            +
                  client.do_something
         | 
| 50 | 
            +
                  client.do_something
         | 
| 51 | 
            +
                  
         | 
| 52 | 
            +
                  retry_per_msg = Hash.new(0)
         | 
| 53 | 
            +
                  
         | 
| 54 | 
            +
                  server.run(4) do |exception, control|
         | 
| 55 | 
            +
                    retries = (retry_per_msg[control.msg_id] += 1)
         | 
| 56 | 
            +
                    retries < 2 ? control.retry : control.delete
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                        
         | 
| 59 | 
            +
                  retry_per_msg.should have(2).messages
         | 
| 60 | 
            +
                  retry_per_msg.each do |msg_id, retries|
         | 
| 61 | 
            +
                    msg_id.should >0
         | 
| 62 | 
            +
                    retries.should == 2
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end 
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
              describe 'using the control parameter to the factory' do
         | 
| 67 | 
            +
                it "can retry the message from within the handler" do
         | 
| 68 | 
            +
                  client.retry_message
         | 
| 69 | 
            +
                  server.run(2)
         | 
| 70 | 
            +
                  
         | 
| 71 | 
            +
                  retry_log.should have(1).message
         | 
| 72 | 
            +
                  
         | 
| 73 | 
            +
                  msg_id, retries = retry_log.first
         | 
| 74 | 
            +
                  msg_id.should > 0
         | 
| 75 | 
            +
                  retries.should == 2
         | 
| 76 | 
            +
                end 
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
            end
         | 
| @@ -32,7 +32,8 @@ describe "Regression: " do | |
| 32 32 | 
             
                  'regression', 
         | 
| 33 33 | 
             
                  :timeout => timeout,
         | 
| 34 34 | 
             
                  :server => BEANSTALK_CONNECTION, 
         | 
| 35 | 
            -
                  :with_answer => with_answer)
         | 
| 35 | 
            +
                  :with_answer => with_answer).tap { |client| 
         | 
| 36 | 
            +
                    self.class.after(:each) { client.close } }
         | 
| 36 37 | 
             
              end
         | 
| 37 38 |  | 
| 38 39 | 
             
              def print_stats
         | 
| @@ -50,7 +51,7 @@ describe "Regression: " do | |
| 50 51 | 
             
                connection.close
         | 
| 51 52 | 
             
              end
         | 
| 52 53 |  | 
| 53 | 
            -
              describe " | 
| 54 | 
            +
              describe "long running message, followed by a short running one (bug)" do
         | 
| 54 55 | 
             
                class Regression1Server
         | 
| 55 56 | 
             
                  def reader; 42; end
         | 
| 56 57 | 
             
                  def long_running; sleep 0.1 end
         | 
| @@ -98,11 +99,11 @@ describe "Regression: " do | |
| 98 99 | 
             
                end
         | 
| 99 100 |  | 
| 100 101 | 
             
                it "should timeout a blocking call" do
         | 
| 101 | 
            -
                   | 
| 102 | 
            -
                     | 
| 102 | 
            +
                  expect {
         | 
| 103 | 
            +
                    expect {
         | 
| 103 104 | 
             
                      client.crash_and_burn
         | 
| 104 | 
            -
                    }. | 
| 105 | 
            -
                  }. | 
| 105 | 
            +
                    }.not_to take_long
         | 
| 106 | 
            +
                  }.to raise_error(Zack::ServiceTimeout)
         | 
| 106 107 | 
             
                end
         | 
| 107 108 | 
             
              end
         | 
| 108 109 | 
             
              describe "server that takes a long time, timeout in client stops the operation" do
         | 
| @@ -119,7 +120,7 @@ describe "Regression: " do | |
| 119 120 | 
             
                it "should pass a sanity check" do
         | 
| 120 121 | 
             
                  client.long_running(0, 42).should == 42
         | 
| 121 122 | 
             
                end 
         | 
| 122 | 
            -
                context " | 
| 123 | 
            +
                context "message ordering" do
         | 
| 123 124 | 
             
                  before(:each) {
         | 
| 124 125 | 
             
                    begin
         | 
| 125 126 | 
             
                      client.long_running(1.1, 10)
         | 
| @@ -127,7 +128,7 @@ describe "Regression: " do | |
| 127 128 | 
             
                    end
         | 
| 128 129 | 
             
                  }
         | 
| 129 130 |  | 
| 130 | 
            -
                  it "should  | 
| 131 | 
            +
                  it "should be preserved" do
         | 
| 131 132 | 
             
                    client.long_running(0, 42).should == 42
         | 
| 132 133 | 
             
                  end
         | 
| 133 134 | 
             
                end
         | 
| @@ -10,6 +10,17 @@ describe "Server#run" do | |
| 10 10 | 
             
                end
         | 
| 11 11 | 
             
              end
         | 
| 12 12 |  | 
| 13 | 
            +
              # Isolates the specs by clearing the 'server_run' tube before use.
         | 
| 14 | 
            +
              before(:each) { 
         | 
| 15 | 
            +
                conn = Beanstalk::Connection.new(BEANSTALK_CONNECTION, 'server_run')
         | 
| 16 | 
            +
                
         | 
| 17 | 
            +
                while conn.peek_ready
         | 
| 18 | 
            +
                  conn.reserve.delete
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
                
         | 
| 21 | 
            +
                conn.close
         | 
| 22 | 
            +
              }
         | 
| 23 | 
            +
              
         | 
| 13 24 | 
             
              let(:server) { Zack::Server.new(
         | 
| 14 25 | 
             
                'server_run', 
         | 
| 15 26 | 
             
                :simple => RunServerRun,
         | 
| @@ -10,6 +10,7 @@ describe Zack::Server do | |
| 10 10 | 
             
              end
         | 
| 11 11 | 
             
              context "instance" do
         | 
| 12 12 | 
             
                let(:implementation) { flexmock(:implementation) }
         | 
| 13 | 
            +
                let(:control)        { flexmock(:control) }
         | 
| 13 14 |  | 
| 14 15 | 
             
                # Replacing the Cod::Service with a mock
         | 
| 15 16 | 
             
                let(:service) { flexmock(:service) }
         | 
| @@ -18,7 +19,7 @@ describe Zack::Server do | |
| 18 19 | 
             
                context "with a factory" do
         | 
| 19 20 | 
             
                  # A small factory that always returns instance.
         | 
| 20 21 | 
             
                  class ImplFactory < Struct.new(:instance)
         | 
| 21 | 
            -
                    def call
         | 
| 22 | 
            +
                    def call(control)
         | 
| 22 23 | 
             
                      instance
         | 
| 23 24 | 
             
                    end
         | 
| 24 25 | 
             
                  end
         | 
| @@ -37,7 +38,7 @@ describe Zack::Server do | |
| 37 38 |  | 
| 38 39 | 
             
                      it "should call the right message on implementation" do
         | 
| 39 40 | 
             
                        service.should_receive(:one).
         | 
| 40 | 
            -
                           | 
| 41 | 
            +
                          yields([:foobar, [123, '123', :a123]], control)
         | 
| 41 42 |  | 
| 42 43 | 
             
                        implementation.
         | 
| 43 44 | 
             
                          should_receive(:foobar).
         | 
| @@ -64,7 +65,7 @@ describe Zack::Server do | |
| 64 65 |  | 
| 65 66 | 
             
                      it "should call the right message on implementation" do
         | 
| 66 67 | 
             
                        service.should_receive(:one).
         | 
| 67 | 
            -
                           | 
| 68 | 
            +
                          yields([:foobar, [123, '123', :a123]], control)
         | 
| 68 69 |  | 
| 69 70 | 
             
                        implementation.
         | 
| 70 71 | 
             
                          should_receive(:foobar).
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: zack
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.3. | 
| 4 | 
            +
              version: 0.3.3
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -10,42 +10,41 @@ authors: | |
| 10 10 | 
             
            autorequire: 
         | 
| 11 11 | 
             
            bindir: bin
         | 
| 12 12 | 
             
            cert_chain: []
         | 
| 13 | 
            -
            date: 2011- | 
| 14 | 
            -
            default_executable: 
         | 
| 13 | 
            +
            date: 2011-11-29 00:00:00.000000000Z
         | 
| 15 14 | 
             
            dependencies:
         | 
| 16 15 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 17 16 | 
             
              name: cod
         | 
| 18 | 
            -
              requirement: & | 
| 17 | 
            +
              requirement: &70152213379200 !ruby/object:Gem::Requirement
         | 
| 19 18 | 
             
                none: false
         | 
| 20 19 | 
             
                requirements:
         | 
| 21 20 | 
             
                - - ~>
         | 
| 22 21 | 
             
                  - !ruby/object:Gem::Version
         | 
| 23 | 
            -
                    version: '0. | 
| 22 | 
            +
                    version: '0.4'
         | 
| 24 23 | 
             
              type: :runtime
         | 
| 25 24 | 
             
              prerelease: false
         | 
| 26 | 
            -
              version_requirements: * | 
| 25 | 
            +
              version_requirements: *70152213379200
         | 
| 27 26 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            -
              name:  | 
| 29 | 
            -
              requirement: & | 
| 27 | 
            +
              name: uuid
         | 
| 28 | 
            +
              requirement: &70152213378720 !ruby/object:Gem::Requirement
         | 
| 30 29 | 
             
                none: false
         | 
| 31 30 | 
             
                requirements:
         | 
| 32 31 | 
             
                - - ~>
         | 
| 33 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 34 | 
            -
                    version: ' | 
| 33 | 
            +
                    version: '2.3'
         | 
| 35 34 | 
             
              type: :runtime
         | 
| 36 35 | 
             
              prerelease: false
         | 
| 37 | 
            -
              version_requirements: * | 
| 36 | 
            +
              version_requirements: *70152213378720
         | 
| 38 37 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 39 | 
            -
              name:  | 
| 40 | 
            -
              requirement: & | 
| 38 | 
            +
              name: beanstalk-client
         | 
| 39 | 
            +
              requirement: &70152213378240 !ruby/object:Gem::Requirement
         | 
| 41 40 | 
             
                none: false
         | 
| 42 41 | 
             
                requirements:
         | 
| 43 42 | 
             
                - - ~>
         | 
| 44 43 | 
             
                  - !ruby/object:Gem::Version
         | 
| 45 | 
            -
                    version: ' | 
| 46 | 
            -
              type: : | 
| 44 | 
            +
                    version: '1.0'
         | 
| 45 | 
            +
              type: :development
         | 
| 47 46 | 
             
              prerelease: false
         | 
| 48 | 
            -
              version_requirements: * | 
| 47 | 
            +
              version_requirements: *70152213378240
         | 
| 49 48 | 
             
            description: 
         | 
| 50 49 | 
             
            email:
         | 
| 51 50 | 
             
            - kaspar.schiess@absurd.li
         | 
| @@ -61,19 +60,16 @@ files: | |
| 61 60 | 
             
            - README
         | 
| 62 61 | 
             
            - lib/zack/client.rb
         | 
| 63 62 | 
             
            - lib/zack/server.rb
         | 
| 64 | 
            -
            - lib/zack/target.rb
         | 
| 65 | 
            -
            - lib/zack/transparent_proxy.rb
         | 
| 66 63 | 
             
            - lib/zack/unique_name.rb
         | 
| 67 64 | 
             
            - lib/zack.rb
         | 
| 68 | 
            -
            - spec/ | 
| 69 | 
            -
            - spec/ | 
| 65 | 
            +
            - spec/acceptance/exception_handling_spec.rb
         | 
| 66 | 
            +
            - spec/acceptance/regression_spec.rb
         | 
| 67 | 
            +
            - spec/acceptance/server_runner_spec.rb
         | 
| 70 68 | 
             
            - spec/lib/zack/client_spec.rb
         | 
| 71 69 | 
             
            - spec/lib/zack/server_spec.rb
         | 
| 72 70 | 
             
            - spec/spec_helper.rb
         | 
| 73 71 | 
             
            - example/client.rb
         | 
| 74 | 
            -
            - example/pubsub.rb
         | 
| 75 72 | 
             
            - example/server.rb
         | 
| 76 | 
            -
            has_rdoc: true
         | 
| 77 73 | 
             
            homepage: http://github.com/kschiess/zack
         | 
| 78 74 | 
             
            licenses: []
         | 
| 79 75 | 
             
            post_install_message: 
         | 
| @@ -88,6 +84,9 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 88 84 | 
             
              - - ! '>='
         | 
| 89 85 | 
             
                - !ruby/object:Gem::Version
         | 
| 90 86 | 
             
                  version: '0'
         | 
| 87 | 
            +
                  segments:
         | 
| 88 | 
            +
                  - 0
         | 
| 89 | 
            +
                  hash: 2009193566488392526
         | 
| 91 90 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 92 91 | 
             
              none: false
         | 
| 93 92 | 
             
              requirements:
         | 
| @@ -96,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 96 95 | 
             
                  version: '0'
         | 
| 97 96 | 
             
            requirements: []
         | 
| 98 97 | 
             
            rubyforge_project: 
         | 
| 99 | 
            -
            rubygems_version: 1. | 
| 98 | 
            +
            rubygems_version: 1.8.10
         | 
| 100 99 | 
             
            signing_key: 
         | 
| 101 100 | 
             
            specification_version: 3
         | 
| 102 101 | 
             
            summary: Ruby RPC calls via Cod
         | 
    
        data/example/pubsub.rb
    DELETED
    
    | @@ -1,35 +0,0 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            class Handler
         | 
| 3 | 
            -
              def foo
         | 
| 4 | 
            -
                puts "Foo was called."
         | 
| 5 | 
            -
              end
         | 
| 6 | 
            -
              def bar
         | 
| 7 | 
            -
                Process.pid
         | 
| 8 | 
            -
              end
         | 
| 9 | 
            -
              def shutdown
         | 
| 10 | 
            -
                exit 0
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
            end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
            source = Zack::Notifier.new(
         | 
| 15 | 
            -
              'football', 
         | 
| 16 | 
            -
              server: 'localhost:11300', 
         | 
| 17 | 
            -
              with_answer: [:bar])
         | 
| 18 | 
            -
              
         | 
| 19 | 
            -
            %w(foo bar).each do |filter|
         | 
| 20 | 
            -
              fork do
         | 
| 21 | 
            -
                handler = Zack::Listener.new(
         | 
| 22 | 
            -
                  'football', 
         | 
| 23 | 
            -
                  simple: Handler,
         | 
| 24 | 
            -
                  server: 'localhost:11300')
         | 
| 25 | 
            -
                  
         | 
| 26 | 
            -
                handler.run
         | 
| 27 | 
            -
              end
         | 
| 28 | 
            -
            end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
            source.foo
         | 
| 31 | 
            -
            p source.bar
         | 
| 32 | 
            -
             | 
| 33 | 
            -
            source.shutdown
         | 
| 34 | 
            -
             | 
| 35 | 
            -
            Process.waitall
         | 
    
        data/lib/zack/target.rb
    DELETED
    
    | @@ -1,68 +0,0 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            module Zack
         | 
| 3 | 
            -
              # Abstract base class for everything that is an RPC target. This implements
         | 
| 4 | 
            -
              # some common mechanisms like a run loop, exception handling and argument
         | 
| 5 | 
            -
              # handling. 
         | 
| 6 | 
            -
              #
         | 
| 7 | 
            -
              class Target
         | 
| 8 | 
            -
                attr_reader :factory
         | 
| 9 | 
            -
                attr_reader :server
         | 
| 10 | 
            -
                
         | 
| 11 | 
            -
                # Initializes #factory and #server.
         | 
| 12 | 
            -
                #
         | 
| 13 | 
            -
                def initialize(tube_name, opts={})
         | 
| 14 | 
            -
                  @server = opts[:server] || 'beanstalk:11300'
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                  if opts.has_key? :factory
         | 
| 17 | 
            -
                    @factory = opts[:factory]
         | 
| 18 | 
            -
                  elsif opts.has_key? :simple
         | 
| 19 | 
            -
                    klass = opts[:simple]
         | 
| 20 | 
            -
                    @factory = lambda { klass.new }
         | 
| 21 | 
            -
                  else
         | 
| 22 | 
            -
                    raise ArgumentError, "Either :factory or :simple argument must be given." 
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
                end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                # Processes exactly one request, but doesn't define how the request gets
         | 
| 27 | 
            -
                # here. 
         | 
| 28 | 
            -
                #
         | 
| 29 | 
            -
                def process_request(sym, args)
         | 
| 30 | 
            -
                  instance = factory.call
         | 
| 31 | 
            -
                  
         | 
| 32 | 
            -
                  instance.send(sym, *args)
         | 
| 33 | 
            -
                end
         | 
| 34 | 
            -
                
         | 
| 35 | 
            -
                # Handles one request. This is specific to implementors. 
         | 
| 36 | 
            -
                #
         | 
| 37 | 
            -
                def handle_request
         | 
| 38 | 
            -
                  raise NotImplementedError, 
         | 
| 39 | 
            -
                    "Abstract base class doesn't implement #handle_request."
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                # Runs the server and keeps running until the world ends (or the process, 
         | 
| 43 | 
            -
                # whichever comes first).
         | 
| 44 | 
            -
                #
         | 
| 45 | 
            -
                def run(&block)
         | 
| 46 | 
            -
                  loop do
         | 
| 47 | 
            -
                    exception_handling(block) do
         | 
| 48 | 
            -
                      handle_request
         | 
| 49 | 
            -
                    end
         | 
| 50 | 
            -
                  end
         | 
| 51 | 
            -
                end
         | 
| 52 | 
            -
             | 
| 53 | 
            -
              private
         | 
| 54 | 
            -
                # Defines how the server handles exception. 
         | 
| 55 | 
            -
                #
         | 
| 56 | 
            -
                def exception_handling(exception_handler)
         | 
| 57 | 
            -
                  if exception_handler
         | 
| 58 | 
            -
                    begin
         | 
| 59 | 
            -
                      yield
         | 
| 60 | 
            -
                    rescue => exception
         | 
| 61 | 
            -
                      exception_handler.call(exception)
         | 
| 62 | 
            -
                    end
         | 
| 63 | 
            -
                  else
         | 
| 64 | 
            -
                    yield
         | 
| 65 | 
            -
                  end
         | 
| 66 | 
            -
                end
         | 
| 67 | 
            -
              end
         | 
| 68 | 
            -
            end
         | 
| @@ -1,22 +0,0 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            # A method missing implementation that will use respond_to? to see wether a 
         | 
| 3 | 
            -
            # message should be answered. If yes, it delegates the message to service, 
         | 
| 4 | 
            -
            # which is supposed to return one of Cods RPC client primitives. Depending on
         | 
| 5 | 
            -
            # the value of has_answer?(symbol), either #call or #notify is used. 
         | 
| 6 | 
            -
            #
         | 
| 7 | 
            -
            module Zack::TransparentProxy
         | 
| 8 | 
            -
              def method_missing(sym, *args, &block)
         | 
| 9 | 
            -
                super unless respond_to?(sym)
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                raise ArgumentError, "Can't call methods remotely with a block" if block
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                if has_answer?(sym)
         | 
| 14 | 
            -
                  return service.call([sym, args])
         | 
| 15 | 
            -
                else
         | 
| 16 | 
            -
                  service.notify [sym, args]
         | 
| 17 | 
            -
                  return nil
         | 
| 18 | 
            -
                end
         | 
| 19 | 
            -
              rescue Cod::Channel::TimeoutError
         | 
| 20 | 
            -
                raise Zack::ServiceTimeout, "No response from server in the allowed time."
         | 
| 21 | 
            -
              end
         | 
| 22 | 
            -
            end
         |