rack-remote 1.0.1 → 1.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.
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: