freddy 0.4.2 → 0.4.3

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