amqp 0.7.0.pre → 0.7.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.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/CHANGELOG +8 -2
- data/CONTRIBUTORS +22 -0
- data/Gemfile +3 -3
- data/README.md +20 -11
- data/Rakefile +30 -6
- data/amqp.gemspec +1 -1
- data/bin/cleanify.rb +50 -0
- data/examples/amqp/simple.rb +6 -4
- data/examples/mq/ack.rb +8 -6
- data/examples/mq/automatic_binding_for_default_direct_exchange.rb +65 -0
- data/examples/mq/callbacks.rb +9 -1
- data/examples/mq/clock.rb +17 -17
- data/examples/mq/hashtable.rb +19 -10
- data/examples/mq/internal.rb +13 -11
- data/examples/mq/logger.rb +38 -36
- data/examples/mq/multiclock.rb +16 -7
- data/examples/mq/pingpong.rb +16 -7
- data/examples/mq/pop.rb +8 -6
- data/examples/mq/primes-simple.rb +2 -0
- data/examples/mq/primes.rb +7 -5
- data/examples/mq/stocks.rb +14 -5
- data/lib/amqp.rb +12 -8
- data/lib/amqp/buffer.rb +35 -158
- data/lib/amqp/client.rb +34 -22
- data/lib/amqp/frame.rb +8 -64
- data/lib/amqp/protocol.rb +21 -70
- data/lib/amqp/server.rb +11 -9
- data/lib/amqp/spec.rb +8 -6
- data/lib/amqp/version.rb +2 -0
- data/lib/ext/blankslate.rb +3 -1
- data/lib/ext/em.rb +2 -0
- data/lib/ext/emfork.rb +13 -11
- data/lib/mq.rb +253 -156
- data/lib/mq/collection.rb +6 -88
- data/lib/mq/exchange.rb +70 -13
- data/lib/mq/header.rb +12 -6
- data/lib/mq/logger.rb +9 -7
- data/lib/mq/queue.rb +42 -30
- data/lib/mq/rpc.rb +6 -4
- data/protocol/codegen.rb +20 -18
- data/research/api.rb +10 -46
- data/research/primes-forked.rb +9 -7
- data/research/primes-processes.rb +74 -72
- data/research/primes-threaded.rb +9 -7
- data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +61 -0
- data/spec/mq_helper.rb +70 -0
- data/spec/spec_helper.rb +84 -29
- data/spec/unit/amqp/buffer_spec.rb +178 -0
- data/spec/unit/amqp/client_spec.rb +472 -0
- data/spec/unit/amqp/frame_spec.rb +60 -0
- data/spec/unit/amqp/misc_spec.rb +123 -0
- data/spec/unit/amqp/protocol_spec.rb +53 -0
- data/spec/unit/mq/channel_close_spec.rb +15 -0
- data/spec/unit/mq/collection_spec.rb +129 -0
- data/spec/unit/mq/exchange_declaration_spec.rb +524 -0
- data/spec/unit/mq/misc_spec.rb +228 -0
- data/spec/unit/mq/mq_basic_spec.rb +39 -0
- data/spec/unit/mq/queue_declaration_spec.rb +97 -0
- data/spec/unit/mq/queue_spec.rb +71 -0
- metadata +33 -21
- data/Gemfile.lock +0 -16
- data/old/README +0 -30
- data/old/Rakefile +0 -12
- data/old/amqp-0.8.json +0 -606
- data/old/amqp_spec.rb +0 -796
- data/old/amqpc.rb +0 -695
- data/old/codegen.rb +0 -148
- data/spec/channel_close_spec.rb +0 -13
- data/spec/sync_async_spec.rb +0 -52
    
        data/lib/mq.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            #:main: README
         | 
| 2 4 | 
             
            #
         | 
| 3 5 |  | 
| @@ -17,6 +19,18 @@ class MQ | |
| 17 19 |  | 
| 18 20 | 
             
              # Raised whenever an illegal operation is attempted.
         | 
| 19 21 | 
             
              class Error < StandardError; end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              class IncompatibleOptionsError < Error
         | 
| 24 | 
            +
                def initialize(name, opts_1, opts_2)
         | 
| 25 | 
            +
                  super("There is already an instance called #{name} with options #{opts_1.inspect}, you can't define the same instance with different options (#{opts_2.inspect})!")
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              class ChannelClosedError < Error
         | 
| 30 | 
            +
                def initialize(instance)
         | 
| 31 | 
            +
                  super("The channel #{instance.channel} was closed, you can't use it anymore!")
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 20 34 | 
             
            end
         | 
| 21 35 |  | 
| 22 36 | 
             
            # The top-level class for building AMQP clients. This class contains several
         | 
| @@ -116,9 +130,20 @@ end | |
| 116 130 | 
             
            #  ["every 2 seconds", :received, Tue Jan 06 22:46:20 -0600 2009]
         | 
| 117 131 | 
             
            #
         | 
| 118 132 | 
             
            class MQ
         | 
| 133 | 
            +
             | 
| 134 | 
            +
              #
         | 
| 135 | 
            +
              # Behaviors
         | 
| 136 | 
            +
              #
         | 
| 137 | 
            +
             | 
| 119 138 | 
             
              include AMQP
         | 
| 120 139 | 
             
              include EM::Deferrable
         | 
| 121 140 |  | 
| 141 | 
            +
             | 
| 142 | 
            +
             | 
| 143 | 
            +
              #
         | 
| 144 | 
            +
              # API
         | 
| 145 | 
            +
              #
         | 
| 146 | 
            +
             | 
| 122 147 | 
             
              # Returns a new channel. A channel is a bidirectional virtual
         | 
| 123 148 | 
             
              # connection between the client and the AMQP server. Elsewhere in the
         | 
| 124 149 | 
             
              # library the channel is referred to in parameter lists as +mq+.
         | 
| @@ -136,148 +161,28 @@ class MQ | |
| 136 161 | 
             
              #    channel = MQ.new AMQP::connect
         | 
| 137 162 | 
             
              #  end
         | 
| 138 163 | 
             
              #
         | 
| 139 | 
            -
              def initialize | 
| 140 | 
            -
                raise 'MQ can only be used from within EM.run{}' unless EM.reactor_running?
         | 
| 164 | 
            +
              def initialize(connection = nil)
         | 
| 165 | 
            +
                raise 'MQ can only be used from within EM.run {}' unless EM.reactor_running?
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                @_send_mutex = Mutex.new
         | 
| 168 | 
            +
                @get_queue_mutex = Mutex.new
         | 
| 141 169 |  | 
| 142 170 | 
             
                @connection = connection || AMQP.start
         | 
| 143 171 |  | 
| 144 | 
            -
                conn.callback{ |c|
         | 
| 172 | 
            +
                conn.callback { |c|
         | 
| 145 173 | 
             
                  @channel = c.add_channel(self)
         | 
| 146 174 | 
             
                  send Protocol::Channel::Open.new
         | 
| 147 175 | 
             
                }
         | 
| 148 176 | 
             
              end
         | 
| 149 | 
            -
              attr_reader :channel, :connection
         | 
| 150 | 
            -
             | 
| 151 | 
            -
              def check_content_completion
         | 
| 152 | 
            -
                if @body.length >= @header.size
         | 
| 153 | 
            -
                  @header.properties.update(@method.arguments)
         | 
| 154 | 
            -
                  @consumer.receive @header, @body if @consumer
         | 
| 155 | 
            -
                  @body = @header = @consumer = @method = nil
         | 
| 156 | 
            -
                end
         | 
| 157 | 
            -
              end
         | 
| 158 | 
            -
             | 
| 159 | 
            -
              # May raise a MQ::Error exception when the frame payload contains a
         | 
| 160 | 
            -
              # Protocol::Channel::Close object.
         | 
| 161 | 
            -
              #
         | 
| 162 | 
            -
              # This usually occurs when a client attempts to perform an illegal
         | 
| 163 | 
            -
              # operation. A short, and incomplete, list of potential illegal operations
         | 
| 164 | 
            -
              # follows:
         | 
| 165 | 
            -
              # * publish a message to a deleted exchange (NOT_FOUND)
         | 
| 166 | 
            -
              # * declare an exchange using the reserved 'amq.' naming structure (ACCESS_REFUSED)
         | 
| 167 | 
            -
              #
         | 
| 168 | 
            -
              def process_frame frame
         | 
| 169 | 
            -
                log :received, frame
         | 
| 170 | 
            -
             | 
| 171 | 
            -
                case frame
         | 
| 172 | 
            -
                when Frame::Header
         | 
| 173 | 
            -
                  @header = frame.payload
         | 
| 174 | 
            -
                  @body = ''
         | 
| 175 | 
            -
                  check_content_completion
         | 
| 176 | 
            -
             | 
| 177 | 
            -
                when Frame::Body
         | 
| 178 | 
            -
                  @body << frame.payload
         | 
| 179 | 
            -
                  check_content_completion
         | 
| 180 | 
            -
             | 
| 181 | 
            -
                when Frame::Method
         | 
| 182 | 
            -
                  case method = frame.payload
         | 
| 183 | 
            -
                  when Protocol::Channel::OpenOk
         | 
| 184 | 
            -
                    send Protocol::Access::Request.new(:realm => '/data',
         | 
| 185 | 
            -
                                                       :read => true,
         | 
| 186 | 
            -
                                                       :write => true,
         | 
| 187 | 
            -
                                                       :active => true,
         | 
| 188 | 
            -
                                                       :passive => true)
         | 
| 189 | 
            -
             | 
| 190 | 
            -
                  when Protocol::Access::RequestOk
         | 
| 191 | 
            -
                    @ticket = method.ticket
         | 
| 192 | 
            -
                    callback{
         | 
| 193 | 
            -
                      send Protocol::Channel::Close.new(:reply_code => 200,
         | 
| 194 | 
            -
                                                        :reply_text => 'bye',
         | 
| 195 | 
            -
                                                        :method_id => 0,
         | 
| 196 | 
            -
                                                        :class_id => 0)
         | 
| 197 | 
            -
                    } if @closing
         | 
| 198 | 
            -
                    succeed
         | 
| 199 | 
            -
             | 
| 200 | 
            -
                  when Protocol::Basic::CancelOk
         | 
| 201 | 
            -
                    if @consumer = consumers[ method.consumer_tag ]
         | 
| 202 | 
            -
                      @consumer.cancelled
         | 
| 203 | 
            -
                    else
         | 
| 204 | 
            -
                      MQ.error "Basic.CancelOk for invalid consumer tag: #{method.consumer_tag}"
         | 
| 205 | 
            -
                    end
         | 
| 206 177 |  | 
| 207 | 
            -
             | 
| 208 | 
            -
             | 
| 209 | 
            -
                    # be an empty string, then AMQP broker generated a random one.
         | 
| 210 | 
            -
                    exchanges = self.exchanges.select { |exchange| exchange.opts[:nowait].eql?(false) }
         | 
| 211 | 
            -
                    exchange  = exchanges.reverse.find { |exchange| exchange.status.eql?(:unfinished) }
         | 
| 212 | 
            -
                    exchange.receive_response method
         | 
| 213 | 
            -
             | 
| 214 | 
            -
                  when Protocol::Queue::DeclareOk
         | 
| 215 | 
            -
                    # We can't use queues[method.queue] because if the name would
         | 
| 216 | 
            -
                    # be an empty string, then AMQP broker generated a random one.
         | 
| 217 | 
            -
                    queues = self.queues.select { |queue| queue.opts[:nowait].eql?(false) }
         | 
| 218 | 
            -
                    queue  = queues.reverse.find { |queue| queue.status.eql?(:unfinished) }
         | 
| 219 | 
            -
                    queue.receive_status method
         | 
| 220 | 
            -
             | 
| 221 | 
            -
                  when Protocol::Queue::BindOk
         | 
| 222 | 
            -
                    # We can't use queues[method.queue] because if the name would
         | 
| 223 | 
            -
                    # be an empty string, then AMQP broker generated a random one.
         | 
| 224 | 
            -
                    queues = self.queues.select { |queue| queue.opts[:nowait].eql?(false) }
         | 
| 225 | 
            -
                    queue  = queues.reverse.find { |queue| queue.status.eql?(:unbound) }
         | 
| 226 | 
            -
                    queue.after_bind method
         | 
| 227 | 
            -
             | 
| 228 | 
            -
                  when Protocol::Basic::Deliver, Protocol::Basic::GetOk
         | 
| 229 | 
            -
                    @method = method
         | 
| 230 | 
            -
                    @header = nil
         | 
| 231 | 
            -
                    @body = ''
         | 
| 232 | 
            -
             | 
| 233 | 
            -
                    if method.is_a? Protocol::Basic::GetOk
         | 
| 234 | 
            -
                      @consumer = get_queue{|q| q.shift }
         | 
| 235 | 
            -
                      MQ.error "No pending Basic.GetOk requests" unless @consumer
         | 
| 236 | 
            -
                    else
         | 
| 237 | 
            -
                      @consumer = consumers[ method.consumer_tag ]
         | 
| 238 | 
            -
                      MQ.error "Basic.Deliver for invalid consumer tag: #{method.consumer_tag}" unless @consumer
         | 
| 239 | 
            -
                    end
         | 
| 240 | 
            -
             | 
| 241 | 
            -
                  when Protocol::Basic::GetEmpty
         | 
| 242 | 
            -
                    if @consumer = get_queue{|q| q.shift }
         | 
| 243 | 
            -
                      @consumer.receive nil, nil
         | 
| 244 | 
            -
                    else
         | 
| 245 | 
            -
                      MQ.error "Basic.GetEmpty for invalid consumer"
         | 
| 246 | 
            -
                    end
         | 
| 247 | 
            -
             | 
| 248 | 
            -
                  when Protocol::Channel::Close
         | 
| 249 | 
            -
                    raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]} on #{@channel}"
         | 
| 250 | 
            -
             | 
| 251 | 
            -
                  when Protocol::Channel::CloseOk
         | 
| 252 | 
            -
                    @on_close && @on_close.call(self)
         | 
| 253 | 
            -
             | 
| 254 | 
            -
                    @closing = false
         | 
| 255 | 
            -
                    conn.callback{ |c|
         | 
| 256 | 
            -
                      c.channels.delete @channel
         | 
| 257 | 
            -
                      c.close if c.channels.empty?
         | 
| 258 | 
            -
                    }
         | 
| 178 | 
            +
              attr_reader :channel, :connection, :status
         | 
| 179 | 
            +
              alias :conn :connection
         | 
| 259 180 |  | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 262 | 
            -
                      @consumer.confirm_subscribe
         | 
| 263 | 
            -
                    else
         | 
| 264 | 
            -
                      MQ.error "Basic.ConsumeOk for invalid consumer tag: #{method.consumer_tag}"
         | 
| 265 | 
            -
                    end
         | 
| 266 | 
            -
                  end
         | 
| 267 | 
            -
                end
         | 
| 181 | 
            +
              def closed?
         | 
| 182 | 
            +
                @status.eql?(:closed)
         | 
| 268 183 | 
             
              end
         | 
| 269 184 |  | 
| 270 | 
            -
             | 
| 271 | 
            -
                conn.callback{ |c|
         | 
| 272 | 
            -
                  (@_send_mutex ||= Mutex.new).synchronize do
         | 
| 273 | 
            -
                    args.each do |data|
         | 
| 274 | 
            -
                      data.ticket = @ticket if @ticket and data.respond_to? :ticket=
         | 
| 275 | 
            -
                      log :sending, data
         | 
| 276 | 
            -
                      c.send data, :channel => @channel
         | 
| 277 | 
            -
                    end
         | 
| 278 | 
            -
                  end
         | 
| 279 | 
            -
                }
         | 
| 280 | 
            -
              end
         | 
| 185 | 
            +
             | 
| 281 186 |  | 
| 282 187 | 
             
              # Defines, intializes and returns an Exchange to act as an ingress
         | 
| 283 188 | 
             
              # point for all published messages.
         | 
| @@ -352,8 +257,16 @@ class MQ | |
| 352 257 | 
             
              # * redeclare an already-declared exchange to a different type
         | 
| 353 258 | 
             
              # * :passive => true and the exchange does not exist (NOT_FOUND)
         | 
| 354 259 | 
             
              #
         | 
| 355 | 
            -
              def direct | 
| 356 | 
            -
                self.exchanges  | 
| 260 | 
            +
              def direct(name = 'amq.direct', opts = {}, &block)
         | 
| 261 | 
            +
                if exchange = self.exchanges.find { |exchange| exchange.name == name }
         | 
| 262 | 
            +
                  extended_opts = Exchange.add_default_options(:direct, name, opts, block)
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                  validate_parameters_match!(exchange, extended_opts)
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                  exchange
         | 
| 267 | 
            +
                else
         | 
| 268 | 
            +
                  self.exchanges << Exchange.new(self, :direct, name, opts, &block)
         | 
| 269 | 
            +
                end
         | 
| 357 270 | 
             
              end
         | 
| 358 271 |  | 
| 359 272 | 
             
              # Defines, intializes and returns an Exchange to act as an ingress
         | 
| @@ -438,8 +351,16 @@ class MQ | |
| 438 351 | 
             
              # * redeclare an already-declared exchange to a different type
         | 
| 439 352 | 
             
              # * :passive => true and the exchange does not exist (NOT_FOUND)
         | 
| 440 353 | 
             
              #
         | 
| 441 | 
            -
              def fanout | 
| 442 | 
            -
                self.exchanges  | 
| 354 | 
            +
              def fanout(name = 'amq.fanout', opts = {}, &block)
         | 
| 355 | 
            +
                if exchange = self.exchanges.find { |exchange| exchange.name == name }
         | 
| 356 | 
            +
                  extended_opts = Exchange.add_default_options(:fanout, name, opts, block)
         | 
| 357 | 
            +
             | 
| 358 | 
            +
                  validate_parameters_match!(exchange, extended_opts)
         | 
| 359 | 
            +
             | 
| 360 | 
            +
                  exchange
         | 
| 361 | 
            +
                else
         | 
| 362 | 
            +
                  self.exchanges << Exchange.new(self, :fanout, name, opts, &block)
         | 
| 363 | 
            +
                end
         | 
| 443 364 | 
             
              end
         | 
| 444 365 |  | 
| 445 366 | 
             
              # Defines, intializes and returns an Exchange to act as an ingress
         | 
| @@ -550,8 +471,16 @@ class MQ | |
| 550 471 | 
             
              # * redeclare an already-declared exchange to a different type
         | 
| 551 472 | 
             
              # * :passive => true and the exchange does not exist (NOT_FOUND)
         | 
| 552 473 | 
             
              #
         | 
| 553 | 
            -
              def topic | 
| 554 | 
            -
                self.exchanges  | 
| 474 | 
            +
              def topic(name = 'amq.topic', opts = {}, &block)
         | 
| 475 | 
            +
                if exchange = self.exchanges.find { |exchange| exchange.name == name }
         | 
| 476 | 
            +
                  extended_opts = Exchange.add_default_options(:topic, name, opts, block)
         | 
| 477 | 
            +
             | 
| 478 | 
            +
                  validate_parameters_match!(exchange, extended_opts)
         | 
| 479 | 
            +
             | 
| 480 | 
            +
                  exchange
         | 
| 481 | 
            +
                else
         | 
| 482 | 
            +
                  self.exchanges << Exchange.new(self, :topic, name, opts, &block)
         | 
| 483 | 
            +
                end
         | 
| 555 484 | 
             
              end
         | 
| 556 485 |  | 
| 557 486 | 
             
              # Defines, intializes and returns an Exchange to act as an ingress
         | 
| @@ -630,8 +559,16 @@ class MQ | |
| 630 559 | 
             
              # * redeclare an already-declared exchange to a different type
         | 
| 631 560 | 
             
              # * :passive => true and the exchange does not exist (NOT_FOUND)
         | 
| 632 561 | 
             
              # * using a value other than "any" or "all" for "x-match"
         | 
| 633 | 
            -
              def headers | 
| 634 | 
            -
                self.exchanges  | 
| 562 | 
            +
              def headers(name = 'amq.match', opts = {}, &block)
         | 
| 563 | 
            +
                if exchange = self.exchanges.find { |exchange| exchange.name == name }
         | 
| 564 | 
            +
                  extended_opts = Exchange.add_default_options(:headers, name, opts, block)
         | 
| 565 | 
            +
             | 
| 566 | 
            +
                  validate_parameters_match!(exchange, extended_opts)
         | 
| 567 | 
            +
             | 
| 568 | 
            +
                  exchange
         | 
| 569 | 
            +
                else
         | 
| 570 | 
            +
                  self.exchanges << Exchange.new(self, :headers, name, opts, &block)
         | 
| 571 | 
            +
                end
         | 
| 635 572 | 
             
              end
         | 
| 636 573 |  | 
| 637 574 | 
             
              # Queues store and forward messages.  Queues can be configured in the server
         | 
| @@ -647,8 +584,8 @@ class MQ | |
| 647 584 | 
             
              #
         | 
| 648 585 | 
             
              # == Options
         | 
| 649 586 | 
             
              # * :passive => true | false (default false)
         | 
| 650 | 
            -
              # If set, the server will not create the  | 
| 651 | 
            -
              # already exist. The client can use this to check whether  | 
| 587 | 
            +
              # If set, the server will not create the queue if it does not
         | 
| 588 | 
            +
              # already exist. The client can use this to check whether the queue
         | 
| 652 589 | 
             
              # exists without modifying  the server state.
         | 
| 653 590 | 
             
              #
         | 
| 654 591 | 
             
              # * :durable => true | false (default false)
         | 
| @@ -687,7 +624,7 @@ class MQ | |
| 687 624 | 
             
              #
         | 
| 688 625 | 
             
              # The server waits for a short period of time before
         | 
| 689 626 | 
             
              # determining the queue is unused to give time to the client code
         | 
| 690 | 
            -
              # to bind  | 
| 627 | 
            +
              # to bind a queue to it.
         | 
| 691 628 | 
             
              #
         | 
| 692 629 | 
             
              # If the queue has been previously declared, this option is ignored
         | 
| 693 630 | 
             
              # on subsequent declarations.
         | 
| @@ -700,11 +637,19 @@ class MQ | |
| 700 637 | 
             
              # not wait for a reply method.  If the server could not complete the
         | 
| 701 638 | 
             
              # method it will raise a channel or connection exception.
         | 
| 702 639 | 
             
              #
         | 
| 703 | 
            -
              def queue | 
| 704 | 
            -
                self.queues  | 
| 640 | 
            +
              def queue(name, opts = {}, &block)
         | 
| 641 | 
            +
                if queue = self.queues.find { |queue| queue.name == name }
         | 
| 642 | 
            +
                  extended_opts = Queue.add_default_options(name, opts, block)
         | 
| 643 | 
            +
             | 
| 644 | 
            +
                  validate_parameters_match!(queue, extended_opts)
         | 
| 645 | 
            +
             | 
| 646 | 
            +
                  queue
         | 
| 647 | 
            +
                else
         | 
| 648 | 
            +
                  self.queues << Queue.new(self, name, opts, &block)
         | 
| 649 | 
            +
                end
         | 
| 705 650 | 
             
              end
         | 
| 706 651 |  | 
| 707 | 
            -
              def queue! | 
| 652 | 
            +
              def queue!(name, opts = {}, &block)
         | 
| 708 653 | 
             
                self.queues.add! Queue.new(self, name, opts, &block)
         | 
| 709 654 | 
             
              end
         | 
| 710 655 |  | 
| @@ -744,7 +689,7 @@ class MQ | |
| 744 689 | 
             
              #    end
         | 
| 745 690 | 
             
              #  end
         | 
| 746 691 | 
             
              #
         | 
| 747 | 
            -
              def rpc | 
| 692 | 
            +
              def rpc(name, obj = nil)
         | 
| 748 693 | 
             
                rpcs[name] ||= RPC.new(self, name, obj)
         | 
| 749 694 | 
             
              end
         | 
| 750 695 |  | 
| @@ -772,7 +717,9 @@ class MQ | |
| 772 717 |  | 
| 773 718 | 
             
              def prefetch(size)
         | 
| 774 719 | 
             
                @prefetch_size = size
         | 
| 720 | 
            +
             | 
| 775 721 | 
             
                send Protocol::Basic::Qos.new(:prefetch_size => 0, :prefetch_count => size, :global => false)
         | 
| 722 | 
            +
             | 
| 776 723 | 
             
                self
         | 
| 777 724 | 
             
              end
         | 
| 778 725 |  | 
| @@ -784,7 +731,7 @@ class MQ | |
| 784 731 | 
             
              # If this flag is true, the server will attempt to requeue the message, potentially then
         | 
| 785 732 | 
             
              # delivering it to an alternative subscriber.
         | 
| 786 733 | 
             
              #
         | 
| 787 | 
            -
              def recover | 
| 734 | 
            +
              def recover(requeue = false)
         | 
| 788 735 | 
             
                send Protocol::Basic::Recover.new(:requeue => requeue)
         | 
| 789 736 | 
             
                self
         | 
| 790 737 | 
             
              end
         | 
| @@ -805,7 +752,7 @@ class MQ | |
| 805 752 |  | 
| 806 753 | 
             
              def get_queue
         | 
| 807 754 | 
             
                if block_given?
         | 
| 808 | 
            -
                   | 
| 755 | 
            +
                  @get_queue_mutex.synchronize {
         | 
| 809 756 | 
             
                    yield( @get_queue ||= [] )
         | 
| 810 757 | 
             
                  }
         | 
| 811 758 | 
             
                end
         | 
| @@ -833,26 +780,176 @@ class MQ | |
| 833 780 | 
             
                @consumers = {}
         | 
| 834 781 |  | 
| 835 782 | 
             
                exs = @exchanges
         | 
| 836 | 
            -
                @exchanges =  | 
| 837 | 
            -
                exs.each{ | | 
| 783 | 
            +
                @exchanges = MQ::Collection.new
         | 
| 784 | 
            +
                exs.each { |e| e.reset } if exs
         | 
| 838 785 |  | 
| 839 786 | 
             
                qus = @queues
         | 
| 840 | 
            -
                @queues =  | 
| 841 | 
            -
                qus.each{ | | 
| 787 | 
            +
                @queues = MQ::Collection.new
         | 
| 788 | 
            +
                qus.each { |q| q.reset } if qus
         | 
| 842 789 |  | 
| 843 790 | 
             
                prefetch(@prefetch_size) if @prefetch_size
         | 
| 844 791 | 
             
              end
         | 
| 845 792 |  | 
| 793 | 
            +
             | 
| 794 | 
            +
              #
         | 
| 795 | 
            +
              # Implementation
         | 
| 796 | 
            +
              #
         | 
| 797 | 
            +
             | 
| 798 | 
            +
              # May raise a MQ::Error exception when the frame payload contains a
         | 
| 799 | 
            +
              # Protocol::Channel::Close object.
         | 
| 800 | 
            +
              #
         | 
| 801 | 
            +
              # This usually occurs when a client attempts to perform an illegal
         | 
| 802 | 
            +
              # operation. A short, and incomplete, list of potential illegal operations
         | 
| 803 | 
            +
              # follows:
         | 
| 804 | 
            +
              # * publish a message to a deleted exchange (NOT_FOUND)
         | 
| 805 | 
            +
              # * declare an exchange using the reserved 'amq.' naming structure (ACCESS_REFUSED)
         | 
| 806 | 
            +
              #
         | 
| 807 | 
            +
              def process_frame(frame)
         | 
| 808 | 
            +
                log :received, frame
         | 
| 809 | 
            +
             | 
| 810 | 
            +
                case frame
         | 
| 811 | 
            +
                when Frame::Header
         | 
| 812 | 
            +
                  @header = frame.payload
         | 
| 813 | 
            +
                  @body = ''
         | 
| 814 | 
            +
                  check_content_completion
         | 
| 815 | 
            +
             | 
| 816 | 
            +
                when Frame::Body
         | 
| 817 | 
            +
                  @body << frame.payload
         | 
| 818 | 
            +
                  check_content_completion
         | 
| 819 | 
            +
             | 
| 820 | 
            +
                when Frame::Method
         | 
| 821 | 
            +
                  case method = frame.payload
         | 
| 822 | 
            +
                  when Protocol::Channel::OpenOk
         | 
| 823 | 
            +
                    send Protocol::Access::Request.new(:realm => '/data',
         | 
| 824 | 
            +
                                                       :read => true,
         | 
| 825 | 
            +
                                                       :write => true,
         | 
| 826 | 
            +
                                                       :active => true,
         | 
| 827 | 
            +
                                                       :passive => true)
         | 
| 828 | 
            +
             | 
| 829 | 
            +
                  when Protocol::Access::RequestOk
         | 
| 830 | 
            +
                    @ticket = method.ticket
         | 
| 831 | 
            +
                    callback {
         | 
| 832 | 
            +
                      send Protocol::Channel::Close.new(:reply_code => 200,
         | 
| 833 | 
            +
                                                        :reply_text => 'bye',
         | 
| 834 | 
            +
                                                        :method_id => 0,
         | 
| 835 | 
            +
                                                        :class_id => 0)
         | 
| 836 | 
            +
                    } if @closing
         | 
| 837 | 
            +
                    succeed
         | 
| 838 | 
            +
             | 
| 839 | 
            +
                  when Protocol::Basic::CancelOk
         | 
| 840 | 
            +
                    if @consumer = consumers[ method.consumer_tag ]
         | 
| 841 | 
            +
                      @consumer.cancelled
         | 
| 842 | 
            +
                    else
         | 
| 843 | 
            +
                      MQ.error "Basic.CancelOk for invalid consumer tag: #{method.consumer_tag}"
         | 
| 844 | 
            +
                    end
         | 
| 845 | 
            +
             | 
| 846 | 
            +
                  when Protocol::Exchange::DeclareOk
         | 
| 847 | 
            +
                    # We can't use exchanges[method.exchange] because if the name would
         | 
| 848 | 
            +
                    # be an empty string, then AMQP broker generated a random one.
         | 
| 849 | 
            +
                    exchanges = self.exchanges.select { |exchange| exchange.opts[:nowait].eql?(false) }
         | 
| 850 | 
            +
                    exchange  = exchanges.reverse.find { |exchange| exchange.status.eql?(:unfinished) }
         | 
| 851 | 
            +
                    exchange.receive_response method
         | 
| 852 | 
            +
             | 
| 853 | 
            +
                  when Protocol::Queue::DeclareOk
         | 
| 854 | 
            +
                    # We can't use queues[method.queue] because if the name would
         | 
| 855 | 
            +
                    # be an empty string, then AMQP broker generated a random one.
         | 
| 856 | 
            +
                    queues = self.queues.select { |queue| queue.opts[:nowait].eql?(false) }
         | 
| 857 | 
            +
                    queue  = queues.reverse.find { |queue| queue.status.eql?(:unfinished) }
         | 
| 858 | 
            +
                    queue.receive_status method
         | 
| 859 | 
            +
             | 
| 860 | 
            +
                  when Protocol::Queue::BindOk
         | 
| 861 | 
            +
                    # We can't use queues[method.queue] because if the name would
         | 
| 862 | 
            +
                    # be an empty string, then AMQP broker generated a random one.
         | 
| 863 | 
            +
                    queues = self.queues.select { |queue| queue.sync_bind }
         | 
| 864 | 
            +
                    queue  = queues.reverse.find { |queue| queue.status.eql?(:unbound) }
         | 
| 865 | 
            +
                    queue.after_bind method
         | 
| 866 | 
            +
             | 
| 867 | 
            +
                  when Protocol::Basic::Deliver, Protocol::Basic::GetOk
         | 
| 868 | 
            +
                    @method = method
         | 
| 869 | 
            +
                    @header = nil
         | 
| 870 | 
            +
                    @body = ''
         | 
| 871 | 
            +
             | 
| 872 | 
            +
                    if method.is_a? Protocol::Basic::GetOk
         | 
| 873 | 
            +
                      @consumer = get_queue { |q| q.shift }
         | 
| 874 | 
            +
                      MQ.error "No pending Basic.GetOk requests" unless @consumer
         | 
| 875 | 
            +
                    else
         | 
| 876 | 
            +
                      @consumer = consumers[ method.consumer_tag ]
         | 
| 877 | 
            +
                      MQ.error "Basic.Deliver for invalid consumer tag: #{method.consumer_tag}" unless @consumer
         | 
| 878 | 
            +
                    end
         | 
| 879 | 
            +
             | 
| 880 | 
            +
                  when Protocol::Basic::GetEmpty
         | 
| 881 | 
            +
                    if @consumer = get_queue { |q| q.shift }
         | 
| 882 | 
            +
                      @consumer.receive nil, nil
         | 
| 883 | 
            +
                    else
         | 
| 884 | 
            +
                      MQ.error "Basic.GetEmpty for invalid consumer"
         | 
| 885 | 
            +
                    end
         | 
| 886 | 
            +
             | 
| 887 | 
            +
                  when Protocol::Channel::Close
         | 
| 888 | 
            +
                    @status = :closed
         | 
| 889 | 
            +
                    MQ.error "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]} on #{@channel}"
         | 
| 890 | 
            +
             | 
| 891 | 
            +
                  when Protocol::Channel::CloseOk
         | 
| 892 | 
            +
                    @status = :closed
         | 
| 893 | 
            +
                    @on_close && @on_close.call(self)
         | 
| 894 | 
            +
             | 
| 895 | 
            +
                    @closing = false
         | 
| 896 | 
            +
                    conn.callback { |c|
         | 
| 897 | 
            +
                      c.channels.delete @channel
         | 
| 898 | 
            +
                      c.close if c.channels.empty?
         | 
| 899 | 
            +
                    }
         | 
| 900 | 
            +
             | 
| 901 | 
            +
                  when Protocol::Basic::ConsumeOk
         | 
| 902 | 
            +
                    if @consumer = consumers[ method.consumer_tag ]
         | 
| 903 | 
            +
                      @consumer.confirm_subscribe
         | 
| 904 | 
            +
                    else
         | 
| 905 | 
            +
                      MQ.error "Basic.ConsumeOk for invalid consumer tag: #{method.consumer_tag}"
         | 
| 906 | 
            +
                    end
         | 
| 907 | 
            +
                  end
         | 
| 908 | 
            +
                end
         | 
| 909 | 
            +
              end # process_frame
         | 
| 910 | 
            +
             | 
| 911 | 
            +
             | 
| 912 | 
            +
              def send(*args)
         | 
| 913 | 
            +
                conn.callback { |c|
         | 
| 914 | 
            +
                  @_send_mutex.synchronize do
         | 
| 915 | 
            +
                    args.each do |data|
         | 
| 916 | 
            +
                      unless self.closed?
         | 
| 917 | 
            +
                        data.ticket = @ticket if @ticket and data.respond_to? :ticket=
         | 
| 918 | 
            +
                        log :sending, data
         | 
| 919 | 
            +
                        c.send data, :channel => @channel
         | 
| 920 | 
            +
                      else
         | 
| 921 | 
            +
                        unless data.class == AMQP::Protocol::Channel::CloseOk
         | 
| 922 | 
            +
                          raise ChannelClosedError.new(self)
         | 
| 923 | 
            +
                        end
         | 
| 924 | 
            +
                      end
         | 
| 925 | 
            +
                    end
         | 
| 926 | 
            +
                  end
         | 
| 927 | 
            +
                }
         | 
| 928 | 
            +
              end # send
         | 
| 929 | 
            +
             | 
| 930 | 
            +
             | 
| 931 | 
            +
              def check_content_completion
         | 
| 932 | 
            +
                if @body.length >= @header.size
         | 
| 933 | 
            +
                  @header.properties.update(@method.arguments)
         | 
| 934 | 
            +
                  @consumer.receive @header, @body if @consumer
         | 
| 935 | 
            +
                  @body = @header = @consumer = @method = nil
         | 
| 936 | 
            +
                end
         | 
| 937 | 
            +
              end # check_content_completion
         | 
| 938 | 
            +
             | 
| 939 | 
            +
             | 
| 846 940 | 
             
              private
         | 
| 847 941 |  | 
| 848 | 
            -
              def log | 
| 942 | 
            +
              def log(*args)
         | 
| 849 943 | 
             
                return unless MQ.logging
         | 
| 850 944 | 
             
                pp args
         | 
| 851 945 | 
             
                puts
         | 
| 852 | 
            -
              end
         | 
| 946 | 
            +
              end # log
         | 
| 853 947 |  | 
| 854 | 
            -
               | 
| 855 | 
            -
             | 
| 948 | 
            +
              def validate_parameters_match!(entity, parameters)
         | 
| 949 | 
            +
                unless entity.opts == parameters || parameters[:passive]
         | 
| 950 | 
            +
                  raise IncompatibleOptionsError.new(entity.name, entity.opts, parameters)
         | 
| 951 | 
            +
                end
         | 
| 952 | 
            +
              end # validate_parameters_match!(entity, parameters)
         | 
| 856 953 | 
             
            end
         | 
| 857 954 |  | 
| 858 955 | 
             
            #-- convenience wrapper (read: HACK) for thread-local MQ object
         |