porcupine 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .idea
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ jruby
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in porcupine.gemspec
4
+ gemspec
data/Jarfile ADDED
@@ -0,0 +1,3 @@
1
+ jar 'com.netflix.rxjava:rxjava-jruby:0.10.1'
2
+ jar 'com.netflix.hystrix:hystrix-core:1.3.2'
3
+ jar 'org.slf4j:slf4j-simple:1.7.5'
data/Jarfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ ---
2
+ version: 0.7.5
3
+ groups:
4
+ default:
5
+ dependencies:
6
+ - com.google.code.findbugs:jsr305:jar:2.0.0
7
+ - com.netflix.archaius:archaius-core:jar:0.4.1
8
+ - com.netflix.hystrix:hystrix-core:jar:1.3.2
9
+ - com.netflix.rxjava:rxjava-core:jar:0.10.1
10
+ - com.netflix.rxjava:rxjava-jruby:jar:0.10.1
11
+ - commons-configuration:commons-configuration:jar:1.8
12
+ - commons-lang:commons-lang:jar:2.6
13
+ - commons-logging:commons-logging:jar:1.1.1
14
+ - org.slf4j:slf4j-api:jar:1.7.5
15
+ - org.slf4j:slf4j-simple:jar:1.7.5
16
+ artifacts:
17
+ - jar:com.netflix.rxjava:rxjava-jruby:jar:0.10.1:
18
+ transitive:
19
+ com.netflix.rxjava:rxjava-core:jar:0.10.1: {}
20
+ - jar:com.netflix.hystrix:hystrix-core:jar:1.3.2:
21
+ transitive:
22
+ com.google.code.findbugs:jsr305:jar:2.0.0: {}
23
+ com.netflix.archaius:archaius-core:jar:0.4.1:
24
+ commons-configuration:commons-configuration:jar:1.8:
25
+ commons-logging:commons-logging:jar:1.1.1: {}
26
+ commons-lang:commons-lang:jar:2.6: {}
27
+ - jar:org.slf4j:slf4j-simple:jar:1.7.5:
28
+ transitive:
29
+ org.slf4j:slf4j-api:jar:1.7.5: {}
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mike Ragalie
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # Porcupine
2
+
3
+ Porcupine is a JRuby wrapper for Netflix's [Hystrix library](https://github.com/Netflix/Hystrix).
4
+ It simplifies instantiating HystrixCommands and throws Ruby exceptions for most things that can
5
+ go wrong (execution failures, timeouts, short circuits, no threads in the thread pool).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'porcupine'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install porcupine
20
+
21
+ ## Usage
22
+
23
+ Instantiate a Porcupine object and give it a name, a group name (optional), a timeout (optional) and a block:
24
+
25
+ ```ruby
26
+ porcupine = Porcupine.new("Sleeping Beauty", "Disney Characters", 10_000) { sleep(50) }
27
+ ```
28
+
29
+ You can also provide a [Setter](https://github.com/Netflix/Hystrix/wiki/Configuration) directly:
30
+
31
+ ```ruby
32
+ setter = com.netflix.hystrix.HystrixCommand::Setter.withGroupKey(com.netflix.hystrix.HystrixCommandGroupKey::Factory.asKey("Disney Characters"))
33
+ .andCommandKey(com.netflix.hystrix.HystrixCommandKey::Factory.asKey("Sleeping Beauty"))
34
+ porcupine = Porcupine.new(setter) { sleep(50) }
35
+ ```
36
+
37
+ Then either you can request a result immediately:
38
+
39
+ ```ruby
40
+ porcupine.execute
41
+ ```
42
+
43
+ Or retrieve a Future and get the result later:
44
+
45
+ ```ruby
46
+ future = porcupine.queue
47
+ future.get # Blocks on the result
48
+ ```
49
+
50
+ Subscribing to an event is also supported:
51
+
52
+ ```ruby
53
+ observer = porcupine.observe
54
+ observer.subscribe { puts "done!" } # Will puts "done!" when finished
55
+ ```
56
+
57
+ If you provide an onError function, then the function will receive exceptions raised:
58
+
59
+ ```ruby
60
+ observer = porcupine.observe
61
+ observer.subscribe("onNext" => lambda {}, "onError" => lambda {|exception| puts exception}) # Will puts any exception
62
+ ```
63
+
64
+ ## Contributing
65
+
66
+ 1. Fork it
67
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
68
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
69
+ 4. Push to the branch (`git push origin my-new-feature`)
70
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ task :default => :prepare
2
+
3
+ task :prepare do
4
+ require 'lock_jar'
5
+
6
+ # get jarfile relative the gem dir
7
+ lockfile = File.expand_path( "../Jarfile.lock", __FILE__ )
8
+
9
+ LockJar.install( :lockfile => lockfile )
10
+ end
@@ -0,0 +1,6 @@
1
+ class Porcupine
2
+ class FailedExecutionError < RuntimeError; end
3
+ class RejectedError < RuntimeError; end
4
+ class ShortCircuitError < RuntimeError; end
5
+ class TimeoutError < RuntimeError; end
6
+ end
@@ -0,0 +1,12 @@
1
+ require "delegate"
2
+
3
+ class Porcupine
4
+ class Future < SimpleDelegator
5
+ def get(*args)
6
+ result = __getobj__.get(*args)
7
+ raise result if result.is_a?(Exception)
8
+
9
+ result
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ require "delegate"
2
+
3
+ class Porcupine
4
+ class Observable < SimpleDelegator
5
+ def subscribe(*args, &block)
6
+ raise ArgumentError unless block_given? || (args.first && args.first["onNext"])
7
+
8
+ on_next = if block_given?
9
+ block
10
+ else
11
+ args.first["onNext"]
12
+ end
13
+
14
+ on_error = args.first && args.first["onError"]
15
+
16
+ wrapped = lambda do |value_or_exception|
17
+ if value_or_exception.is_a?(Exception)
18
+ on_error && on_error.call(value_or_exception)
19
+ else
20
+ on_next.call(value_or_exception)
21
+ end
22
+ end
23
+
24
+ __getobj__.subscribe("onNext" => wrapped, "onError" => on_error)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ class Porcupine < com.netflix.hystrix.HystrixCommand
2
+ java_import com.netflix.hystrix.HystrixCommand::Setter
3
+ java_import com.netflix.hystrix.HystrixCommandKey
4
+ java_import com.netflix.hystrix.HystrixCommandGroupKey
5
+ java_import com.netflix.hystrix.HystrixCommandProperties
6
+
7
+ DEFAULT_TIMEOUT = 10_000
8
+ DEFAULT_GROUP = "default"
9
+
10
+ attr_reader :block
11
+
12
+ def initialize(name_or_setter, group=DEFAULT_GROUP, timeout=DEFAULT_TIMEOUT, &block)
13
+ @block = block
14
+
15
+ setter = name_or_setter if name_or_setter.is_a?(com.netflix.hystrix.HystrixCommand::Setter)
16
+ setter ||= Setter.withGroupKey(HystrixCommandGroupKey::Factory.asKey(group))
17
+ .andCommandKey(HystrixCommandKey::Factory.asKey(name_or_setter))
18
+ .andCommandPropertiesDefaults(HystrixCommandProperties::Setter().withExecutionIsolationThreadTimeoutInMilliseconds(timeout))
19
+
20
+ super(setter)
21
+ end
22
+
23
+ # Only catch Java exceptions since we already handle most exceptions in Observable#get
24
+ def execute
25
+ queue.get
26
+ rescue Java::JavaLang::Throwable => e
27
+ raise decomposeException(e)
28
+ end
29
+
30
+ def observe
31
+ Observable.new(super)
32
+ end
33
+
34
+ # Only wrap the outer-most call; otherwise Java gets angry because the class of the
35
+ # returned object won't match the signature when the calls recurse
36
+ def toObservable(*args)
37
+ result = super
38
+
39
+ unless caller.first.match(/toObservable/) || caller.first.match(/observe/)
40
+ result = Observable.new(result)
41
+ end
42
+
43
+ result
44
+ end
45
+
46
+ def queue
47
+ Future.new(super)
48
+ end
49
+
50
+ def run
51
+ block.call
52
+ end
53
+
54
+ def getFallback
55
+ return getFailedExecutionException.getException if isFailedExecution
56
+ return RejectedError.new if isResponseRejected
57
+ return ShortCircuitError.new if isResponseShortCircuited
58
+ return TimeoutError.new if isResponseTimedOut
59
+ RuntimeError.new
60
+ end
61
+ end
data/lib/porcupine.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "lock_jar"
2
+
3
+ lockfile = File.expand_path( "../../Jarfile.lock", __FILE__ )
4
+ LockJar.load(lockfile)
5
+
6
+ require "porcupine/porcupine"
7
+ require "porcupine/exceptions"
8
+ require "porcupine/future"
9
+ require "porcupine/observable"
data/porcupine.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "porcupine"
7
+ spec.version = "0.1.0"
8
+ spec.authors = ["Mike Ragalie"]
9
+ spec.email = ["ragalie@gmail.com"]
10
+ spec.summary = "JRuby wrapper for Hystrix"
11
+ spec.homepage = "https://github.com/ragalie/porcupine"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.extensions = ["Rakefile"]
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "lock_jar"
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec", "~> 2.0"
25
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe Porcupine::Future do
4
+ let(:object) { double("future") }
5
+ subject(:future) { described_class.new(object) }
6
+
7
+ it "is a SimpleDelegator" do
8
+ future.should be_a(SimpleDelegator)
9
+ end
10
+
11
+ describe "#get" do
12
+ before { object.stub(:get) { "banana" } }
13
+
14
+ it "returns the future value" do
15
+ future.get.should == "banana"
16
+ end
17
+
18
+ it "raises if the value is an exception" do
19
+ exception = RuntimeError.new
20
+ object.stub(:get) { exception }
21
+ expect { future.get }.to raise_error(exception)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+
3
+ describe Porcupine::Observable do
4
+ let(:object) { double("observable", :subscribe => nil) }
5
+ subject(:observable) { described_class.new(object) }
6
+
7
+ it "is a SimpleDelegator" do
8
+ observable.should be_a(SimpleDelegator)
9
+ end
10
+
11
+ describe "#subscribe" do
12
+ let(:on_next) { lambda {|v| "pumpkin" } }
13
+ let(:on_error) { lambda {|v| "apples" } }
14
+ let(:arguments) { observable.subscribe("onNext" => on_next, "onError" => on_error) }
15
+
16
+ describe "arguments" do
17
+ it "accepts a block" do
18
+ observable.subscribe {}
19
+ end
20
+
21
+ it "accepts an onNext parameter" do
22
+ observable.subscribe("onNext" => lambda {})
23
+ end
24
+
25
+ it "raises otherwise" do
26
+ expect { observable.subscribe("cat!") }.to raise_error(ArgumentError)
27
+ end
28
+ end
29
+
30
+ it "calls subscribe with a block" do
31
+ object.stub(:subscribe) {|arg| arg}
32
+ observable.subscribe(&on_next)
33
+ arguments["onNext"].call(1).should == "pumpkin"
34
+ end
35
+
36
+ it "calls subscribe with the provided onNext method" do
37
+ object.stub(:subscribe) {|arg| arg}
38
+ arguments["onNext"].call(1).should == "pumpkin"
39
+ end
40
+
41
+ it "calls subscribe with the provided onError method" do
42
+ object.stub(:subscribe) {|arg| arg}
43
+ arguments["onError"].should == on_error
44
+ end
45
+
46
+ describe "onNext method" do
47
+ before { object.stub(:subscribe) {|arg| arg} }
48
+
49
+ it "calls the success method" do
50
+ wrapped = arguments["onNext"]
51
+
52
+ on_next.should_receive(:call).with("pony")
53
+ wrapped.call("pony")
54
+ end
55
+
56
+ it "calls the error method if the value is an exception" do
57
+ wrapped = arguments["onNext"]
58
+
59
+ exception = RuntimeError.new
60
+ on_error.should_receive(:call).with(exception)
61
+ wrapped.call(exception)
62
+ end
63
+
64
+ it "does nothing if there's no on_error" do
65
+ arguments = observable.subscribe(&on_next)
66
+ wrapped = arguments["onNext"]
67
+
68
+ exception = RuntimeError.new
69
+ on_error.should_not_receive(:call)
70
+ on_next.should_not_receive(:call)
71
+ wrapped.call(exception)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,138 @@
1
+ require "spec_helper"
2
+
3
+ describe Porcupine do
4
+ let(:block) { lambda {} }
5
+
6
+ subject(:porcupine) do
7
+ described_class.new("test", &block)
8
+ end
9
+
10
+ after do
11
+ com.netflix.hystrix.Hystrix.reset
12
+ end
13
+
14
+ describe "#initialize" do
15
+ it "handles a name, group, timeout and block" do
16
+ porcupine = described_class.new("blah", "blah_group", 1_000, &block)
17
+ porcupine.should be_a(com.netflix.hystrix.HystrixCommand)
18
+ porcupine.getCommandKey.name.should == "blah"
19
+ porcupine.getCommandGroup.name.should == "blah_group"
20
+ porcupine.getProperties.executionIsolationThreadTimeoutInMilliseconds.get.should == 1_000
21
+ porcupine.block.should == block
22
+ end
23
+
24
+ it "handles a setter" do
25
+ setter = com.netflix.hystrix.HystrixCommand::Setter.withGroupKey(com.netflix.hystrix.HystrixCommandGroupKey::Factory.asKey("blah2_group"))
26
+ .andCommandKey(com.netflix.hystrix.HystrixCommandKey::Factory.asKey("blah2"))
27
+
28
+ porcupine = described_class.new(setter, &block)
29
+ porcupine.should be_a(com.netflix.hystrix.HystrixCommand)
30
+ porcupine.getCommandKey.name.should == "blah2"
31
+ porcupine.getCommandGroup.name.should == "blah2_group"
32
+ porcupine.block.should == block
33
+ end
34
+
35
+ it "uses the defaults if none are provided" do
36
+ porcupine = described_class.new("blah3", &block)
37
+ porcupine.getCommandKey.name.should == "blah3"
38
+ porcupine.getCommandGroup.name.should == "default"
39
+ porcupine.getProperties.executionIsolationThreadTimeoutInMilliseconds.get.should == 10_000
40
+ porcupine.block.should == block
41
+ end
42
+ end
43
+
44
+ describe "#execute" do
45
+ let(:future) { double("future") }
46
+
47
+ before { porcupine.stub(:queue) { future } }
48
+
49
+ it "calls queue.get" do
50
+ porcupine.should_receive(:queue) { future }
51
+ future.should_receive(:get)
52
+
53
+ porcupine.execute
54
+ end
55
+
56
+ it "lets Ruby exceptions propogate" do
57
+ future.stub(:get).and_raise(RuntimeError)
58
+ porcupine.should_not_receive(:decomposeException)
59
+
60
+ expect { porcupine.execute }.to raise_error(RuntimeError)
61
+ end
62
+
63
+ it "catches Java exceptions and runs them through #decomposeException" do
64
+ exception = java.lang.Throwable.new
65
+ future.stub(:get).and_raise(exception)
66
+ porcupine.should_receive(:decomposeException).with(exception) { java.lang.Exception.new }
67
+
68
+ expect { porcupine.execute }.to raise_error(Java::JavaLang::Exception)
69
+ end
70
+ end
71
+
72
+ describe "#observe" do
73
+ it "wraps the result in an Observable" do
74
+ observable = porcupine.observe
75
+ observable.should be_a(Porcupine::Observable)
76
+ observable.__getobj__.should be_a(Java::Rx::Observable)
77
+ end
78
+ end
79
+
80
+ describe "#toObservable" do
81
+ it "wraps the result in an Observable with no args" do
82
+ observable = porcupine.toObservable
83
+ observable.should be_a(Porcupine::Observable)
84
+ observable.__getobj__.should be_a(Java::Rx::Observable)
85
+ end
86
+
87
+ it "wraps the result in an Observable with one arg" do
88
+ observable = porcupine.toObservable(Java::RxConcurrency::Schedulers.threadPoolForComputation)
89
+ observable.should be_a(Porcupine::Observable)
90
+ observable.__getobj__.should be_a(Java::Rx::Observable)
91
+ end
92
+ end
93
+
94
+ describe "#queue" do
95
+ it "wraps the result in a Future" do
96
+ future = porcupine.queue
97
+ future.should be_a(Porcupine::Future)
98
+ future.__getobj__.class.ancestors.should include(Java::JavaUtilConcurrent::Future)
99
+ future.cancel(true)
100
+ end
101
+ end
102
+
103
+ describe "#run" do
104
+ it "calls the block" do
105
+ block.should_receive(:call)
106
+ porcupine.run
107
+ end
108
+ end
109
+
110
+ describe "#getFallback" do
111
+ it "returns the failure exception if the block failed" do
112
+ exception = Exception.new
113
+
114
+ porcupine.stub(:isFailedExecution) { true }
115
+ porcupine.stub_chain(:getFailedExecutionException, :getException) { exception }
116
+ porcupine.getFallback.should == exception
117
+ end
118
+
119
+ it "returns a RejectedError if the response was rejected" do
120
+ porcupine.stub(:isResponseRejected) { true }
121
+ porcupine.getFallback.should be_a(Porcupine::RejectedError)
122
+ end
123
+
124
+ it "returns a ShortCircuitError if the response was short circuited" do
125
+ porcupine.stub(:isResponseShortCircuited) { true }
126
+ porcupine.getFallback.should be_a(Porcupine::ShortCircuitError)
127
+ end
128
+
129
+ it "returns a TimeoutError if the response timed out" do
130
+ porcupine.stub(:isResponseTimedOut) { true }
131
+ porcupine.getFallback.should be_a(Porcupine::TimeoutError)
132
+ end
133
+
134
+ it "returns a RuntimeError otherwise" do
135
+ porcupine.getFallback.should be_a(RuntimeError)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1 @@
1
+ require "porcupine"
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: porcupine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mike Ragalie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: lock_jar
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ none: false
22
+ requirement: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ none: false
28
+ prerelease: false
29
+ type: :runtime
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: '1.3'
37
+ none: false
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: '1.3'
43
+ none: false
44
+ prerelease: false
45
+ type: :development
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ none: false
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ none: false
60
+ prerelease: false
61
+ type: :development
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ none: false
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ version: '2.0'
75
+ none: false
76
+ prerelease: false
77
+ type: :development
78
+ description:
79
+ email:
80
+ - ragalie@gmail.com
81
+ executables: []
82
+ extensions:
83
+ - Rakefile
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - .ruby-version
88
+ - Gemfile
89
+ - Jarfile
90
+ - Jarfile.lock
91
+ - LICENSE.txt
92
+ - README.md
93
+ - Rakefile
94
+ - lib/porcupine.rb
95
+ - lib/porcupine/exceptions.rb
96
+ - lib/porcupine/future.rb
97
+ - lib/porcupine/observable.rb
98
+ - lib/porcupine/porcupine.rb
99
+ - porcupine.gemspec
100
+ - spec/future_spec.rb
101
+ - spec/observable_spec.rb
102
+ - spec/porcupine_spec.rb
103
+ - spec/spec_helper.rb
104
+ homepage: https://github.com/ragalie/porcupine
105
+ licenses:
106
+ - MIT
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ none: false
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ none: false
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.24
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: JRuby wrapper for Hystrix
129
+ test_files:
130
+ - spec/future_spec.rb
131
+ - spec/observable_spec.rb
132
+ - spec/porcupine_spec.rb
133
+ - spec/spec_helper.rb
134
+ has_rdoc: