freddy 0.4.2 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd30720991795db96e70f26190cf3a1b65e92417
4
- data.tar.gz: fc188e002812f0d38781bd9971a8dec0f27fab4e
3
+ metadata.gz: f1b843578b576679503e114c5e6bda050cd980eb
4
+ data.tar.gz: af256b6384fca7e9ee13fd4d9a5f8b3301a96d0d
5
5
  SHA512:
6
- metadata.gz: eeb84534acc65e95aa33cfdb4a7619af476cf5355192dacc1b078cde7702f8323b0370bd63036d29fd10a4550fb6c11a68922c93b14e517d2482712001c23f04
7
- data.tar.gz: fe31e8cdc06941aa5d3d5bb214802bae19e4d86bf11d40a6381c3eef0f9cfcf8296eaa100d72e2a501f9c8fd001e6ad02ee1b0afcf5f4f62e5c9eaea8a9253ec
6
+ metadata.gz: 3b3768d47cf8a14f2743ca9e391458cba6968165e6fc29e8f1112f6bd093dff34bb933e76155baa95711fcbc8da4a6b734cf982a094fe63ba50566a06683d196
7
+ data.tar.gz: 225aca279255a4f0d9643100f48ecc32b817f55b43fb16a120cf7fdf2b32c8b5bd0f649c9c333e0e38c1815de1c7d01b6159588413ce10099a224cf1c9289c9c
data/.gitignore CHANGED
@@ -1 +1,5 @@
1
1
  .bundle/
2
+ Gemfile.lock
3
+ pkg
4
+ .tags
5
+ .tags1
@@ -1,8 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.1.0
4
+ - jruby-9.0.0.0
4
5
  services:
5
6
  - rabbitmq
6
7
  before_script:
7
- - bundle
8
+ - gem install bundler
9
+ - bundle install
8
10
  script: bundle exec rspec
data/Gemfile CHANGED
@@ -5,4 +5,4 @@ group :test, :development do
5
5
  gem 'pry'
6
6
  end
7
7
 
8
- gemspec
8
+ gemspec
data/README.md CHANGED
@@ -126,11 +126,6 @@ The following operations are supported:
126
126
  * stop responding
127
127
  ```ruby
128
128
  responder_handler.cancel
129
- ```
130
-
131
- * join the current thread to the responder thread
132
- ```ruby
133
- responder_handler.join
134
129
  ```
135
130
 
136
131
  * delete the destination
@@ -149,11 +144,11 @@ The thread pool is shared between *tap_into* and *respond_to* callbacks and the
149
144
  The thread pool size can be configured by passing the configuration option *max_concurrency*.
150
145
 
151
146
 
152
- Note that while it is possible to use *deliver_with_response* inside a *respond_to* block,
147
+ Note that while it is possible to use *deliver_with_response* inside a *respond_to* block,
153
148
  it is not possible to use another *respond_to* block inside a different *respond_to* block.
154
149
 
155
150
 
156
- Note also that other configuration options for freddy users
151
+ Note also that other configuration options for freddy users
157
152
  such as pool sizes for DB connections need to match or exceed *max_concurrency*
158
153
  to avoid running out of resources.
159
154
 
@@ -3,8 +3,12 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "freddy"
7
- spec.version = '0.4.2'
6
+ if RUBY_PLATFORM == 'java'
7
+ spec.name = "freddy-jruby"
8
+ else
9
+ spec.name = "freddy"
10
+ end
11
+ spec.version = '0.4.3'
8
12
  spec.authors = ["Urmas Talimaa"]
9
13
  spec.email = ["urmas.talimaa@gmail.com"]
10
14
  spec.description = %q{Messaging API}
@@ -16,10 +20,15 @@ Gem::Specification.new do |spec|
16
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
21
  spec.require_paths = ["lib"]
18
22
 
19
- spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "bundler"
20
24
  spec.add_development_dependency "rake"
21
25
 
22
- spec.add_dependency "bunny", "1.6.3"
26
+ if RUBY_PLATFORM == 'java'
27
+ spec.add_dependency 'march_hare', '~> 2.12.0'
28
+ else
29
+ spec.add_dependency "bunny", "2.2.0"
30
+ end
31
+
23
32
  spec.add_dependency "symbolizer"
24
33
  spec.add_dependency "hamster", "~> 1.0.1.pre.rc3"
25
34
  spec.add_dependency "thread", "~> 0.2"
@@ -1,8 +1,14 @@
1
- require 'bunny'
1
+ if RUBY_PLATFORM == 'java'
2
+ require 'march_hare'
3
+ else
4
+ require 'bunny'
5
+ end
6
+
2
7
  require 'json'
3
8
  require 'symbolizer'
4
9
  require 'thread/pool'
5
10
 
11
+ require_relative 'freddy/adaptive_queue'
6
12
  require_relative 'freddy/consumer'
7
13
  require_relative 'freddy/producer'
8
14
  require_relative 'freddy/request'
@@ -64,18 +70,23 @@ class Freddy
64
70
  end
65
71
  end
66
72
 
67
- def self.build(logger = Logger.new(STDOUT), bunny_config)
68
- bunny = Bunny.new(bunny_config)
69
- bunny.start
73
+ def self.build(logger = Logger.new(STDOUT), config)
74
+ if RUBY_PLATFORM == 'java'
75
+ connection = MarchHare.connect(config)
76
+ else
77
+ connection = Bunny.new(config)
78
+ connection.start
79
+ connection
80
+ end
70
81
 
71
- channel = bunny.create_channel
72
- new(channel, logger, bunny_config.fetch(:max_concurrency, 4))
82
+ new(connection, logger, config.fetch(:max_concurrency, 4))
73
83
  end
74
84
 
75
85
  attr_reader :channel, :consumer, :producer, :request
76
86
 
77
- def initialize(channel, logger, max_concurrency)
78
- @channel = channel
87
+ def initialize(connection, logger, max_concurrency)
88
+ @connection = connection
89
+ @channel = connection.create_channel
79
90
  @consume_thread_pool = Thread.pool(max_concurrency)
80
91
  @consumer = Consumer.new channel, logger, @consume_thread_pool
81
92
  @producer = Producer.new channel, logger
@@ -106,4 +117,8 @@ class Freddy
106
117
  timeout: timeout, delete_on_timeout: delete_on_timeout
107
118
  }
108
119
  end
120
+
121
+ def close
122
+ @connection.close
123
+ end
109
124
  end
@@ -0,0 +1,34 @@
1
+ class Freddy
2
+ class AdaptiveQueue
3
+ def initialize(queue)
4
+ @queue = queue
5
+ end
6
+
7
+ def subscribe(&block)
8
+ if hare?
9
+ @queue.subscribe do |meta, payload|
10
+ block.call(payload, Delivery.new(meta, meta.routing_key))
11
+ end
12
+ else
13
+ @queue.subscribe do |info, properties, payload|
14
+ block.call(payload, Delivery.new(properties, info.routing_key))
15
+ end
16
+ end
17
+ end
18
+
19
+ def bind(*args)
20
+ @queue.bind(*args)
21
+ self
22
+ end
23
+
24
+ def name
25
+ @queue.name
26
+ end
27
+
28
+ private
29
+
30
+ def hare?
31
+ RUBY_PLATFORM == 'java'
32
+ end
33
+ end
34
+ end
@@ -30,10 +30,10 @@ class Freddy
30
30
  end
31
31
 
32
32
  def tap_into(pattern, &block)
33
- queue = @channel.queue("", exclusive: true).bind(@topic_exchange, routing_key: pattern)
34
- consumer = queue.subscribe do |delivery_info, properties, payload|
33
+ queue = create_queue('', exclusive: true).bind(@topic_exchange, routing_key: pattern)
34
+ consumer = queue.subscribe do |payload, delivery|
35
35
  @consume_thread_pool.process do
36
- block.call parse_payload(payload), delivery_info.routing_key
36
+ block.call parse_payload(payload), delivery.routing_key
37
37
  end
38
38
  end
39
39
  @logger.debug "Tapping into messages that match #{pattern}"
@@ -43,11 +43,11 @@ class Freddy
43
43
  private
44
44
 
45
45
  def consume_using_pool(queue, options, pool, &block)
46
- consumer = queue.subscribe options do |delivery_info, properties, payload|
46
+ consumer = queue.subscribe do |payload, delivery|
47
47
  pool.process do
48
48
  parsed_payload = parse_payload(payload)
49
- log_receive_event(queue.name, parsed_payload, properties[:correlation_id])
50
- block.call parsed_payload, Delivery.new(delivery_info, properties)
49
+ log_receive_event(queue.name, parsed_payload, delivery.correlation_id)
50
+ block.call parsed_payload, delivery
51
51
  end
52
52
  end
53
53
  @logger.debug "Consuming messages on #{queue.name}"
@@ -62,8 +62,8 @@ class Freddy
62
62
  end
63
63
  end
64
64
 
65
- def create_queue(destination)
66
- @channel.queue(destination)
65
+ def create_queue(destination, options={})
66
+ AdaptiveQueue.new(@channel.queue(destination, options))
67
67
  end
68
68
 
69
69
  def log_receive_event(queue_name, payload, correlation_id)
@@ -1,10 +1,14 @@
1
1
  class Freddy
2
2
  class Delivery
3
- attr_reader :info, :properties
3
+ attr_reader :metadata, :routing_key
4
4
 
5
- def initialize(info, properties)
6
- @info = info
7
- @properties = properties
5
+ def initialize(metadata, routing_key)
6
+ @metadata = metadata
7
+ @routing_key = routing_key
8
+ end
9
+
10
+ def correlation_id
11
+ @metadata.correlation_id
8
12
  end
9
13
  end
10
14
  end
@@ -4,17 +4,16 @@ class Freddy
4
4
 
5
5
  def initialize(adapter, delivery)
6
6
  @adapter = adapter
7
- @properties = delivery.properties
8
- @destination = @properties[:destination]
9
- @correlation_id = @properties[:correlation_id]
7
+ @metadata = delivery.metadata
8
+ @correlation_id = @metadata.correlation_id
10
9
  end
11
10
 
12
11
  def success(response = nil)
13
- @adapter.success(@properties[:reply_to], response)
12
+ @adapter.success(@metadata.reply_to, response)
14
13
  end
15
14
 
16
15
  def error(error = {error: "Couldn't process message"})
17
- @adapter.error(@properties[:reply_to], error)
16
+ @adapter.error(@metadata.reply_to, error)
18
17
  end
19
18
  end
20
19
  end
@@ -5,7 +5,8 @@ class Freddy
5
5
  end
6
6
 
7
7
  class StandardMessageHandler
8
- def initialize(producer, logger)
8
+ def initialize(producer, destination, logger)
9
+ @destination = destination
9
10
  @producer = producer
10
11
  @logger = logger
11
12
  end
@@ -13,9 +14,8 @@ class Freddy
13
14
  def handle_message(payload, msg_handler, &block)
14
15
  block.call payload, msg_handler
15
16
  rescue Exception => e
16
- destination = msg_handler.destination
17
- @logger.error "Exception occured while processing message from #{destination}: #{Freddy.format_exception(e)}"
18
- Freddy.notify_exception(e, destination: destination)
17
+ @logger.error "Exception occured while processing message from #{Freddy.format_exception(e)}"
18
+ Freddy.notify_exception(e, destination: @destination)
19
19
  end
20
20
 
21
21
  def success(*)
@@ -28,9 +28,10 @@ class Freddy
28
28
  end
29
29
 
30
30
  class RequestHandler
31
- def initialize(producer, logger)
31
+ def initialize(producer, destination, logger)
32
32
  @producer = producer
33
33
  @logger = logger
34
+ @destination = destination
34
35
  end
35
36
 
36
37
  def handle_message(payload, msg_handler, &block)
@@ -44,7 +45,7 @@ class Freddy
44
45
  end
45
46
  rescue Exception => e
46
47
  @logger.error "Exception occured while handling the request with correlation_id #{@correlation_id}: #{Freddy.format_exception(e)}"
47
- Freddy.notify_exception(e, destination: msg_handler.destination, correlation_id: @correlation_id)
48
+ Freddy.notify_exception(e, correlation_id: @correlation_id, destination: @destination)
48
49
  end
49
50
 
50
51
  def success(reply_to, response)
@@ -3,6 +3,8 @@ require 'json'
3
3
 
4
4
  class Freddy
5
5
  class Producer
6
+ OnReturnNotImplemented = Class.new(NoMethodError)
7
+
6
8
  CONTENT_TYPE = 'application/json'.freeze
7
9
 
8
10
  def initialize(channel, logger)
@@ -21,8 +23,20 @@ class Freddy
21
23
  @exchange.publish json_payload, properties.dup
22
24
  end
23
25
 
24
- def on_return(*args, &block)
25
- @exchange.on_return(*args, &block)
26
+ def on_return(&block)
27
+ if @exchange.respond_to? :on_return # Bunny
28
+ @exchange.on_return do |return_info, properties, content|
29
+ block.call(return_info[:reply_code], properties[:correlation_id])
30
+ end
31
+ elsif @channel.respond_to? :on_return # Hare
32
+ @channel.on_return do |reply_code, _, exchange_name, _, properties|
33
+ if exchange_name != Freddy::FREDDY_TOPIC_EXCHANGE_NAME
34
+ block.call(reply_code, properties.correlation_id)
35
+ end
36
+ end
37
+ else
38
+ raise OnReturnNotImplemented.new "AMQP implementation doesn't implement on_return"
39
+ end
26
40
  end
27
41
  end
28
42
  end
@@ -23,9 +23,9 @@ class Freddy
23
23
  @request_map = Hamster.mutable_hash
24
24
  @request_manager = RequestManager.new @request_map, @logger
25
25
 
26
- @producer.on_return do |return_info, properties, content|
27
- if return_info[:reply_code] == NO_ROUTE
28
- @request_manager.no_route(properties[:correlation_id])
26
+ @producer.on_return do |reply_code, correlation_id|
27
+ if reply_code == NO_ROUTE
28
+ @request_manager.no_route(correlation_id)
29
29
  end
30
30
  end
31
31
 
@@ -68,9 +68,8 @@ class Freddy
68
68
 
69
69
  ensure_response_queue_exists
70
70
  @logger.info "Listening for requests on #{destination}"
71
-
72
71
  responder_handler = @consumer.consume destination do |payload, delivery|
73
- handler = MessageHandlers.for_type(delivery.properties[:type]).new(@producer, @logger)
72
+ handler = MessageHandlers.for_type(delivery.metadata.type).new(@producer, destination, @logger)
74
73
 
75
74
  msg_handler = MessageHandler.new(handler, delivery)
76
75
  handler.handle_message payload, msg_handler, &block
@@ -81,11 +80,11 @@ class Freddy
81
80
  private
82
81
 
83
82
  def create_response_queue
84
- @channel.queue("", exclusive: true)
83
+ AdaptiveQueue.new @channel.queue("", exclusive: true)
85
84
  end
86
85
 
87
86
  def handle_response(payload, delivery)
88
- correlation_id = delivery.properties[:correlation_id]
87
+ correlation_id = delivery.metadata.correlation_id
89
88
  request = @request_map[correlation_id]
90
89
  if request
91
90
  @logger.debug "Got response for request to #{request[:destination]} with correlation_id #{correlation_id}"
@@ -17,14 +17,5 @@ class Freddy
17
17
  def destroy_destination
18
18
  @consumer.queue.delete
19
19
  end
20
-
21
- def join
22
- @channel.work_pool.join
23
- end
24
-
25
- def shutdown
26
- @channel.work_pool.shutdown
27
- end
28
-
29
20
  end
30
21
  end
@@ -14,7 +14,7 @@ class Freddy
14
14
 
15
15
  if @response[:error] == 'RequestTimeout'
16
16
  raise TimeoutError.new(@response)
17
- elsif !@delivery || @delivery.properties[:type] == 'error'
17
+ elsif !@delivery || @delivery.metadata.type == 'error'
18
18
  raise InvalidRequestError.new(@response)
19
19
  else
20
20
  @response
@@ -8,6 +8,8 @@ describe Freddy::Consumer do
8
8
 
9
9
  let(:consumer) { freddy.consumer }
10
10
 
11
+ after { freddy.close }
12
+
11
13
  it 'raises exception when no consumer is provided' do
12
14
  expect { consumer.consume destination }.to raise_error described_class::EmptyConsumer
13
15
  end
@@ -7,6 +7,8 @@ describe Freddy do
7
7
  let(:destination2) { random_destination }
8
8
  let(:payload) { {pay: 'load'} }
9
9
 
10
+ after { freddy.close }
11
+
10
12
  def respond_to(&block)
11
13
  freddy.respond_to(destination, &block)
12
14
  end
@@ -4,8 +4,8 @@ describe Freddy::MessageHandler do
4
4
  subject(:handler) { described_class.new(adapter, delivery) }
5
5
 
6
6
  let(:adapter) { double }
7
- let(:delivery) { double(properties: properties) }
8
- let(:properties) { {reply_to: reply_to} }
7
+ let(:delivery) { double(metadata: metadata) }
8
+ let(:metadata) { double(reply_to: reply_to, correlation_id: 'abc') }
9
9
 
10
10
  let(:reply_to) { double }
11
11
 
@@ -8,12 +8,14 @@ describe Freddy::Request do
8
8
 
9
9
  let(:request) { freddy.request }
10
10
 
11
+ after { freddy.close }
12
+
11
13
  it 'raises empty responder exception when responding without callback' do
12
14
  expect {@responder = request.respond_to destination }.to raise_error described_class::EmptyResponder
13
15
  end
14
16
 
15
17
  context 'requesting from multiple threads' do
16
- let(:nr_of_threads) { 10 }
18
+ let(:nr_of_threads) { 50 }
17
19
 
18
20
  before do
19
21
  freddy.respond_to 'thread-queue' do |payload, msg_handler|
@@ -22,15 +24,16 @@ describe Freddy::Request do
22
24
  end
23
25
 
24
26
  it 'handles multiple threads' do
25
- msg_counter = 0
26
- nr_of_threads.times.map do
27
+ require 'hamster/experimental/mutable_set'
28
+ msg_counter = Hamster.mutable_set
29
+ nr_of_threads.times.map do |index|
27
30
  Thread.new do
28
31
  response = freddy.deliver_with_response 'thread-queue', payload
29
- msg_counter += 1
32
+ msg_counter << index
30
33
  expect(response).to eq(payload)
31
34
  end
32
35
  end.each(&:join)
33
- expect(msg_counter).to eq(nr_of_threads)
36
+ expect(msg_counter.count).to eq(nr_of_threads)
34
37
  end
35
38
 
36
39
  end
@@ -6,6 +6,8 @@ describe Freddy::ResponderHandler do
6
6
  let(:destination) { random_destination }
7
7
  let(:payload) { {pay: 'load'} }
8
8
 
9
+ after { freddy.close }
10
+
9
11
  it 'can cancel listening for messages' do
10
12
  consumer_handler = freddy.respond_to destination do
11
13
  @messages_count ||= 0
@@ -17,17 +19,4 @@ describe Freddy::ResponderHandler do
17
19
 
18
20
  expect(@messages_count).to eq 1
19
21
  end
20
-
21
- it 'can join the thread to the consumer' do
22
- consumer_handler = freddy.respond_to destination do
23
- end
24
- unreachable = true
25
- Thread.new do
26
- consumer_handler.join
27
- unreachable = false
28
- end
29
- wait_for { unreachable }
30
- expect(unreachable).to be(true)
31
- end
32
-
33
22
  end
@@ -5,6 +5,8 @@ describe 'Concurrency' do
5
5
  let(:freddy2) { Freddy.build(logger, config) }
6
6
  let(:freddy3) { Freddy.build(logger, config) }
7
7
 
8
+ after { [freddy1, freddy2, freddy3].each(&:close) }
9
+
8
10
  it 'supports multiple requests in #respond_to' do
9
11
  freddy1.respond_to 'Concurrency1' do |payload, msg_handler|
10
12
  begin
@@ -10,6 +10,8 @@ describe 'Logging' do
10
10
  let(:destination) { random_destination }
11
11
  let(:payload) { {pay: 'load'} }
12
12
 
13
+ after { [freddy1, freddy2].each(&:close) }
14
+
13
15
  before do
14
16
  freddy1.respond_to destination do |payload, msg_handler|
15
17
  msg_handler.success
@@ -1,21 +1,16 @@
1
1
  require 'pry'
2
2
  require 'securerandom'
3
3
  require 'freddy'
4
+ require 'logger'
5
+
6
+ Thread.abort_on_exception = true
4
7
 
5
8
  RSpec.configure do |config|
6
- config.treat_symbols_as_metadata_keys_with_true_values = true
7
9
  config.run_all_when_everything_filtered = true
8
10
  config.filter_run :focus
9
11
  config.order = 'random'
10
12
  end
11
13
 
12
- class Freddy::Consumer
13
- def create_queue(queue_name)
14
- #want to auto_delete queues while testing
15
- @channel.queue(queue_name, auto_delete: true)
16
- end
17
- end
18
-
19
14
  def random_destination
20
15
  SecureRandom.hex
21
16
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freddy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Urmas Talimaa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-15 00:00:00.000000000 Z
11
+ date: 2015-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 1.6.3
47
+ version: 2.2.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 1.6.3
54
+ version: 2.2.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: symbolizer
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,12 +108,12 @@ files:
108
108
  - ".ruby-version"
109
109
  - ".travis.yml"
110
110
  - Gemfile
111
- - Gemfile.lock
112
111
  - LICENCE.txt
113
112
  - README.md
114
113
  - Rakefile
115
114
  - freddy.gemspec
116
115
  - lib/freddy.rb
116
+ - lib/freddy/adaptive_queue.rb
117
117
  - lib/freddy/consumer.rb
118
118
  - lib/freddy/delivery.rb
119
119
  - lib/freddy/message_handler.rb
@@ -1,54 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- freddy (0.4.2)
5
- bunny (= 1.6.3)
6
- hamster (~> 1.0.1.pre.rc3)
7
- symbolizer
8
- thread (~> 0.2)
9
-
10
- GEM
11
- remote: https://rubygems.org/
12
- specs:
13
- amq-protocol (1.9.2)
14
- atomic (1.1.99)
15
- bunny (1.6.3)
16
- amq-protocol (>= 1.9.2)
17
- coderay (1.1.0)
18
- diff-lcs (1.2.5)
19
- hamster (1.0.1.pre.rc3)
20
- atomic (~> 1.1)
21
- method_source (0.8.2)
22
- pry (0.10.1)
23
- coderay (~> 1.1.0)
24
- method_source (~> 0.8.1)
25
- slop (~> 3.4)
26
- rake (10.3.2)
27
- rspec (3.1.0)
28
- rspec-core (~> 3.1.0)
29
- rspec-expectations (~> 3.1.0)
30
- rspec-mocks (~> 3.1.0)
31
- rspec-core (3.1.7)
32
- rspec-support (~> 3.1.0)
33
- rspec-expectations (3.1.2)
34
- diff-lcs (>= 1.2.0, < 2.0)
35
- rspec-support (~> 3.1.0)
36
- rspec-mocks (3.1.3)
37
- rspec-support (~> 3.1.0)
38
- rspec-support (3.1.2)
39
- slop (3.6.0)
40
- symbolizer (0.0.1)
41
- thread (0.2.2)
42
-
43
- PLATFORMS
44
- ruby
45
-
46
- DEPENDENCIES
47
- bundler (~> 1.3)
48
- freddy!
49
- pry
50
- rake
51
- rspec
52
-
53
- BUNDLED WITH
54
- 1.10.6