rack-remote 1.0.1 → 1.1.0

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: 460fb84ecefd34385f80aa6ebc14421a818b844b
4
- data.tar.gz: 13e5f8a42aca5a612c4dc4e7576e2f6b454bd482
3
+ metadata.gz: 4fd96f00690a34791cdf740b05ceabb0b11c8e11
4
+ data.tar.gz: ad6625414ec22cc4bf3c2707c836703713f6642f
5
5
  SHA512:
6
- metadata.gz: 68ea6cc8f771d6435bc8f732b31568192f03c5fbc97bd18f2938da91fbaba99fa8732c051a0284525d0495335c6ebf77117d5e67f5d4155c8a35a9d2b4d900f4
7
- data.tar.gz: 9ee0fe1fbca68005eafb306f26d3527eebe29a92a1b9805583fb3ca57edab55a4be8b39d06fbc5f2849d9e3a431e6e86b538730e9e233b033169cc860b47f30f
6
+ metadata.gz: af407c05bacc948152cf7c67597239a0bdd62a083b1b3f1b20759cdbc22890b7e7ba89a53504bc960afb215a8b28e90bad20513c0d0bcb232693f813e5a13039
7
+ data.tar.gz: 5cf6ad86343dade6b2cb27ff8a191bb696efec7cbc189ef5781f4cf41ca9c402a33f0f02d4c1c7aa587d06d3bdbc616afdb79da69b573e2a09b7d3226cca9758
data/Gemfile CHANGED
@@ -8,6 +8,6 @@ gem 'coveralls'
8
8
  gem 'webmock', '~> 1.7'
9
9
  gem 'rack-test'
10
10
 
11
- # Specify your gem's dependencies in acfs.gemspec
11
+ # Specify your gem's dependencies in gemspec
12
12
  gemroot = File.dirname File.absolute_path __FILE__
13
13
  gemspec path: gemroot
@@ -3,12 +3,45 @@ require 'rack/request'
3
3
  require 'multi_json'
4
4
 
5
5
  module Rack
6
-
7
6
  # Rack::Remote is a Rack middleware for intercepting calls
8
7
  # and invoking remote calls. It can be used to call remote
9
8
  # function for test instructions in distributed systems.
10
9
  #
11
10
  class Remote
11
+ require 'rack/remote/railtie' if defined?(Rails)
12
+
13
+ class ChainedError < StandardError
14
+ attr_reader :cause
15
+
16
+ def initialize(*attrs)
17
+ if attrs.last.is_a?(Hash) && attrs.last[:cause].is_a?(Exception)
18
+ @cause = attrs.last.delete(:cause)
19
+ attrs.pop if attrs.last.empty?
20
+ end
21
+ super *attrs
22
+ end
23
+
24
+ def set_backtrace(trace)
25
+ trace.is_a?(Array) ? trace.map!(&:to_s) : trace = trace.to_s.split("\n")
26
+ trace.map! { |line| " #{line}" }
27
+ if cause
28
+ trace << "caused by #{cause.class.name}: #{cause.message}"
29
+ trace += cause.backtrace.map! { |line| " #{line}" }
30
+ end
31
+ super trace
32
+ end
33
+ end
34
+
35
+ class RemoteError < StandardError
36
+ def initialize(opts = {})
37
+ super "#{opts[:class]}: #{opts[:error]}"
38
+ set_backtrace opts[:backtrace]
39
+ end
40
+ end
41
+
42
+ class RemoteCallFailed < ChainedError
43
+ end
44
+
12
45
  def initialize(app)
13
46
  @app = app
14
47
  end
@@ -20,14 +53,24 @@ module Rack
20
53
  call = env['HTTP_X_RACK_REMOTE_CALL'].to_s
21
54
 
22
55
  if (cb = self.class.calls[call])
23
- response = cb.call(request.params, env, request)
24
- if response.is_a?(Array) && response.size == 3
25
- return response
26
- else
27
- [200, {'Content-Type' => 'application/json'}, StringIO.new(MultiJson.dump response) ]
56
+ begin
57
+ # First rewind request body before read
58
+ request.body.rewind
59
+
60
+ data = request.body.read
61
+ json = data.empty? ? {} : MultiJson.load(data)
62
+
63
+ response = cb.call(json, env, request)
64
+ if response.is_a?(Array) && response.size == 3
65
+ return response
66
+ else
67
+ [200, {'Content-Type' => 'application/json'}, StringIO.new(MultiJson.dump response) ]
68
+ end
69
+ rescue => err
70
+ [500, {'Content-Type' => 'application/json'}, StringIO.new(MultiJson.dump error: err.message, backtrace: err.backtrace, class: err.class.name) ]
28
71
  end
29
72
  else
30
- [404, {'Content-Type' => 'application/json'}, StringIO.new('{"error":"remote call not defined"}') ]
73
+ [404, {'Content-Type' => 'application/json'}, StringIO.new(MultiJson.dump error: 'remote call not defined', calls: call, list: self.class.calls.keys) ]
31
74
  end
32
75
  end
33
76
 
@@ -88,13 +131,23 @@ module Rack
88
131
  end
89
132
 
90
133
  request['X-Rack-Remote-Call'] = call.to_s
91
- request.form_data = params
134
+ request['Content-Type'] = 'application/json'
135
+ request.body = MultiJson.dump(params)
92
136
 
93
137
  response = http.request request
138
+ if response.code.to_i == 500 and response['Content-Type'] == 'application/json'
139
+ json = MultiJson.load(response.body)
140
+
141
+ if json['error'] && json['backtrace'] && json['class']
142
+ remote_error = RemoteError.new class: json['class'], error: json['error'], backtrace: json['backtrace']
143
+ raise Rack::Remote::RemoteCallFailed.new("Remote call returned error code #{response.code}", cause: remote_error)
144
+ end
145
+ end
146
+
94
147
  raise StandardError, "Rack Remote Error Response: #{response.code}: #{response.body}" if response.code.to_i != 200
95
148
 
96
149
  if response['Content-Type'] == 'application/json'
97
- MultiJson.load response.body
150
+ response.body.empty? ? {} : MultiJson.load(response.body)
98
151
  else
99
152
  response.body
100
153
  end
@@ -0,0 +1,7 @@
1
+ class Rack::Remote
2
+ class Railtie < ::Rails::Railtie
3
+ initializer 'rack-remote.middleware' do |app|
4
+ app.config.middleware.use Rack::Remote unless Rails.env.production?
5
+ end
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Remote
3
- VERSION = '1.0.1'
3
+ VERSION = '1.1.0'
4
4
  end
5
5
  end
@@ -13,7 +13,7 @@ describe Rack::Remote do
13
13
  before { Rack::Remote.register :factory_girl, &block }
14
14
 
15
15
  context 'with intercept call' do
16
- let(:request) { -> { get '/', {}, {'HTTP_X_RACK_REMOTE_CALL' => 'factory_girl'} }}
16
+ let(:request) { -> { post '/', {}, {'HTTP_X_RACK_REMOTE_CALL' => 'factory_girl'} }}
17
17
 
18
18
  it 'should invoke registered call' do
19
19
  expect(block).to receive(:call)
@@ -27,7 +27,7 @@ describe Rack::Remote do
27
27
  end
28
28
 
29
29
  context 'with non-rack-remote call' do
30
- let(:request) { -> { get '/' }}
30
+ let(:request) { -> { post '/' }}
31
31
 
32
32
  it 'should delegate request to inner app' do
33
33
  expect(inner_app).to receive(:call).and_call_original
@@ -74,6 +74,12 @@ describe Rack::Remote do
74
74
  ret = Rack::Remote.invoke :users, :factory_girl, param1: 'val1'
75
75
  expect(ret).to eq({'id' => 1})
76
76
  end
77
+
78
+ it 'should invoke remote call (2)' do
79
+ expect(block).to receive(:call).with({ 'param1' => ['val1', {'abc' => 'cde'}] }, kind_of(Hash), kind_of(Rack::Request)).and_return({id: 1})
80
+ ret = Rack::Remote.invoke :users, :factory_girl, param1: ['val1', {abc: :cde}]
81
+ expect(ret).to eq({'id' => 1})
82
+ end
77
83
  end
78
84
  end
79
85
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-remote
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Graichen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-01 00:00:00.000000000 Z
11
+ date: 2013-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -68,6 +68,7 @@ files:
68
68
  - README.md
69
69
  - Rakefile
70
70
  - lib/rack/remote.rb
71
+ - lib/rack/remote/railtie.rb
71
72
  - lib/rack/remote/version.rb
72
73
  - rack-remote.gemspec
73
74
  - spec/rack/remote_spec.rb
@@ -99,3 +100,4 @@ summary: Small request intercepting rack middleware to invoke remote calls over
99
100
  test_files:
100
101
  - spec/rack/remote_spec.rb
101
102
  - spec/spec_helper.rb
103
+ has_rdoc: