porcupine 0.1.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 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: