carrot_rpc 0.4.1 → 0.5.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: b572d5d0139ae2c92283f32b320f917135f44dfd
4
- data.tar.gz: 26e9cf234ef9c0f34c2856735424e2ffa43aa9b7
3
+ metadata.gz: 7021e60c18d67242d796d0a7989d0b6ba411bd2f
4
+ data.tar.gz: 2a857193768aafda74326c94339132e71d30ba65
5
5
  SHA512:
6
- metadata.gz: db094e71f16fed9739c59444b294121db35813ccab080569779da8c63593f3d2732007fceb4efd955542a988896514c3c2b5dac06a5a51aa4005321140eec46a
7
- data.tar.gz: 9f3a627db5a911aa7a8171ff0acf0671624d7c832f6de2420448493d3e19f4b6b05912e53511b0fed3b76535637f4f43755b602bc9475f9f5233e24557632d3f
6
+ metadata.gz: d244349b6f223b3bc3104075a766084054d7e15aab8d419c7e2a0c12c8cc63ac9b2554366ad56975dff3972463d40867aaa1a931c608a661695e25f6c358168f
7
+ data.tar.gz: 3f74c94e6af796112817eb92de1401767e837fe06ee4d9356473cffabdecb8ce8e896a16f86c3c74d007cd3f1fab899c8bbcca9a8f0f4f020031a6f35f60f248
data/CHANGELOG.md CHANGED
@@ -34,6 +34,36 @@
34
34
  # Changelog
35
35
  All significant changes in the project are documented here.
36
36
 
37
+ ## v.0.5.0
38
+ ### Enhancements
39
+ * [#25](https://github.com/C-S-D/carrot_rpc/pull/25) - [shamil614](https://github.com/shamil614)
40
+ * Timeout RpcClient requests when response is not received.
41
+ * Default timeout is 5 seconds.
42
+ * Timeout is configurable.
43
+ * [#27](https://github.com/C-S-D/carrot_rpc/pull/27) - [shamil614](https://github.com/shamil614)
44
+ * Simplify RpcClient usage.
45
+ * Each request which goes through `RpcClient.remote_request` ultimately needs to use a unique `reply_queue` on eqch request.
46
+ * By closing the channel and opening a new channel on each request we ensure that cleanup takes place by the deletion of the `reply_queue`.
47
+ * [#29](https://github.com/C-S-D/carrot_rpc/pull/29) - [shamil614](https://github.com/shamil614)
48
+ * Implementations of the RpcClient need to be flexible with the key formatter.
49
+ * Formatting can be set globally via `Configuration`, overridden via passing Configuration object upon initializing client, or redefine `response_key_formatter` `request_key_formatter` methods.
50
+
51
+ ### Incompatible Changes
52
+ * [#27](https://github.com/C-S-D/carrot_rpc/pull/27) - [shamil614](https://github.com/shamil614)
53
+ * Calling `rpc_client.start` and `rpc_client.channel.close` are no longer required when calling `rpc_client.remote_call` or the methods that call it (`index` `create`, etc).
54
+ * Calling `rpc_client.channel.close` after `rpc_client.remote_call` will cause an Exception to be raised as the channel is already closed.
55
+ * [#29](https://github.com/C-S-D/carrot_rpc/pull/29) - [shamil614](https://github.com/shamil614)
56
+ * Replaced hard coded key formatter in place of a configurable option.
57
+ * Need to set the following in config to maintain previous behavior
58
+ ```ruby
59
+ CarrotRpc.configure do |config|
60
+ # RpcServers expect the params to be dashed.
61
+ config.rpc_client_request_key_format = :dasherize
62
+ # In most cases the RpcClient instances use JSONAPI::Resource classes and the keys need to be transformed.
63
+ config.rpc_client_response_key_format = :underscore
64
+ end
65
+ ```
66
+
37
67
  ## v0.4.1
38
68
  ### Bug Fixes
39
69
  * [#23](https://githb.com/C-S-D/carrot_rpc/pull/23) - [shamil614](https://github.com/shamil614)
data/README.md CHANGED
@@ -86,6 +86,12 @@ CarrotRpc.configure do |config|
86
86
  config.logger = CarrotRpc::TaggedLog.new(logger: Rails.logger, tags: ["Carrot RPC Client"])
87
87
  # Set a Proc to allow manipulation of the params on the RpcClient before the request is sent.
88
88
  config.before_request = proc { |params| params.merge(foo: "bar") }
89
+ # Number of seconds to wait before a RPC Client request timesout. Default 5 seconds.
90
+ config.rpc_client_timeout = 5
91
+ # Formats hash keys to stringified and replaces "_" with "-". Default is `:none` for no formatting.
92
+ config.rpc_client_request_key_format = :dasherize
93
+ # Formats hash keys to stringified and replaces "-" with "_". Default is `:none` for no formatting.
94
+ config.rpc_client_response_key_format = :underscore
89
95
 
90
96
  # Don't use. Server implementation only. The values below are set via CLI:
91
97
  # config.pidfile = nil
@@ -160,14 +166,21 @@ class CarsController < ApplicationController
160
166
 
161
167
  def show
162
168
  car_client = CarClient.new
163
- car_client.start
164
169
  result = car_client.show({id: 1})
165
- # Good idea to clean up connections when finished.
166
- car_client.channel.close
167
170
  end
168
171
  end
169
172
  ```
170
173
 
174
+ One way to implement a RpcClient is to override the default configuration.
175
+ ```ruby
176
+ config = CarrotRPC.configuration.clone
177
+ # Now only this one object will format keys as dashes
178
+ config.rpc_client_response_key_format = :dasherize
179
+
180
+ car_client = CarClient.new(config)
181
+ ```
182
+ By duplicating the `Configuration` instance you can override the global configuration and pass a custom configuration to the RpcClient instance.
183
+
171
184
  ### Support for JSONAPI::Resources
172
185
  In the case that you're writing an application that uses the `jsonapi-resources` gem and you want the `RpcServer` to have the same functionality, then we got you covered. All you need to do is import a few modules. See [jsonapi-resources](https://github.com/cerebris/jsonapi-resources) for details on how to implement resources for your models.
173
186
 
data/carrot_rpc.gemspec CHANGED
@@ -44,6 +44,8 @@ Gem::Specification.new do |spec|
44
44
  spec.add_development_dependency "rspec"
45
45
  # Style-checker
46
46
  spec.add_development_dependency "rubocop", "~> 0.36.0"
47
+ # Documentation
48
+ spec.add_development_dependency "yard", "~> 0.8"
47
49
 
48
50
  spec.required_ruby_version = "~> 2.2"
49
51
  end
@@ -0,0 +1,34 @@
1
+ # Methods similar to rails controller actions for RpcClient
2
+ module CarrotRpc::ClientActions
3
+ # Convience method as a resource alias for index action.
4
+ # To customize, override the method in your class.
5
+ #
6
+ # @param params [Hash] the arguments for the method being called.
7
+ def index(params)
8
+ remote_call("index", params)
9
+ end
10
+
11
+ # Convience method as a resource alias for show action.
12
+ # To customize, override the method in your class.
13
+ #
14
+ # @param params [Hash] the arguments for the method being called.
15
+ def show(params)
16
+ remote_call("show", params)
17
+ end
18
+
19
+ # Convience method as a resource alias for create action.
20
+ # To customize, override the method in your class.
21
+ #
22
+ # @param params [Hash] the arguments for the method being called.
23
+ def create(params)
24
+ remote_call("create", params)
25
+ end
26
+
27
+ # Convience method as a resource alias for update action.
28
+ # To customize, override the method in your class.
29
+ #
30
+ # @param params [Hash] the arguments for the method being called.
31
+ def update(params)
32
+ remote_call("update", params)
33
+ end
34
+ end
@@ -1,11 +1,11 @@
1
1
  # Global configuration for {CarrotRpc}. Access with {CarrotRpc.configuration}.
2
2
  class CarrotRpc::Configuration
3
3
  attr_accessor :logger, :logfile, :loglevel, :daemonize, :pidfile, :runloop_sleep, :autoload_rails, :bunny,
4
- :before_request
4
+ :before_request, :rpc_client_timeout, :rpc_client_response_key_format, :rpc_client_request_key_format
5
5
 
6
6
  # logfile - set logger to a file. overrides rails logger.
7
7
 
8
- def initialize
8
+ def initialize # rubocop:disable Metrics/MethodLength
9
9
  @logfile = nil
10
10
  @loglevel = Logger::DEBUG
11
11
  @logger = nil
@@ -15,5 +15,8 @@ class CarrotRpc::Configuration
15
15
  @autoload_rails = true
16
16
  @bunny = nil
17
17
  @before_request = nil
18
- end
18
+ @rpc_client_timeout = 5
19
+ @rpc_client_response_key_format = :none
20
+ @rpc_client_request_key_format = :none
21
+ end # rubocop:enable Metrics/MethodLength
19
22
  end
@@ -1,4 +1,4 @@
1
- # An enum of predefined {RpcServer::Server#code}
1
+ # An enum of predefined error codes.
2
2
  module CarrotRpc::Error::Code
3
3
  # Internal JSON-RPC error.
4
4
  INTERNAL_ERROR = -32_603
@@ -0,0 +1,5 @@
1
+ # Various exceptions that can be invoked for CarrotRpc gem.
2
+ module CarrotRpc::Exception
3
+ # Exception to be raised when the client timesout waiting for a response.
4
+ class RpcClientTimeout < StandardError; end
5
+ end
@@ -9,6 +9,7 @@ class CarrotRpc::RpcClient
9
9
  attr_reader :channel, :server_queue, :logger
10
10
 
11
11
  extend CarrotRpc::ClientServer
12
+ include CarrotRpc::ClientActions
12
13
 
13
14
  def self.before_request(*proc)
14
15
  if proc.length == 0
@@ -20,12 +21,44 @@ class CarrotRpc::RpcClient
20
21
  end
21
22
  end
22
23
 
23
- # Use defaults for application level connection to RabbitMQ
24
- # All RPC data goes over the same queue. I think that's ok....
25
- def initialize(config: nil)
26
- config ||= CarrotRpc.configuration
27
- @channel = config.bunny.create_channel
28
- @logger = config.logger
24
+ # Logic to process the renaming of keys in a hash.
25
+ # @param format [Symbol] :dasherize changes keys that have "_" to "-"
26
+ # @param format [Symbol] :underscore changes keys that have "-" to "_"
27
+ # @param format [Symbol] :skip, will not rename the keys
28
+ # @param data [Hash] data structure to be transformed
29
+ # @return [Hash] the transformed data
30
+ def self.format_keys(format, data)
31
+ case format
32
+ when :dasherize
33
+ data.rename_keys("_", "-")
34
+ when :underscore
35
+ data.rename_keys("-", "_")
36
+ when :none
37
+ data
38
+ else
39
+ data
40
+ end
41
+ end
42
+
43
+ # Use defaults for application level connection to RabbitMQ.
44
+ #
45
+ # @example pass custom {Configuration} class as an argument to override.
46
+ # config = CarrotRpc::Configuration.new
47
+ # config.rpc_client_timeout = 10
48
+ # CarrotRpc::RpcClient.new(config)
49
+ def initialize(config = nil)
50
+ @config = config || CarrotRpc.configuration
51
+ @logger = @config.logger
52
+ end
53
+
54
+ # Starts the connection to listen for messages.
55
+ #
56
+ # All RpcClient requests go to the a single @server_queue
57
+ # Responses come back over a unique queue name.
58
+ def start
59
+ # Create a new channel on each request because the channel should be closed after each request.
60
+ @channel = @config.bunny.create_channel
61
+
29
62
  # auto_delete => false keeps the queue around until RabbitMQ restarts or explicitly deleted
30
63
  @server_queue = @channel.queue(self.class.queue_name, auto_delete: false)
31
64
 
@@ -33,8 +66,7 @@ class CarrotRpc::RpcClient
33
66
  @exchange = @channel.default_exchange
34
67
  end
35
68
 
36
- # Starts the connection to listen for messages.
37
- def start
69
+ def subscribe
38
70
  # Empty queue name ends up creating a randomly named queue by RabbitMQ
39
71
  # Exclusive => queue will be deleted when connection closes. Allows for automatic "cleanup".
40
72
  @reply_queue = @channel.queue("", exclusive: true)
@@ -45,9 +77,10 @@ class CarrotRpc::RpcClient
45
77
  # setup subscribe block to Service
46
78
  # block => false is a non blocking IO option.
47
79
  @reply_queue.subscribe(block: false) do |_delivery_info, properties, payload|
48
- response = JSON.parse(payload).rename_keys("-", "_").with_indifferent_access
80
+ response = JSON.parse(payload).with_indifferent_access
49
81
 
50
82
  result = parse_response(response)
83
+ result = response_key_formatter(result).with_indifferent_access if result.is_a? Hash
51
84
  @results[properties[:correlation_id]].push(result)
52
85
  end
53
86
  end
@@ -61,20 +94,44 @@ class CarrotRpc::RpcClient
61
94
  # @param params [Hash] the arguments for the method being called.
62
95
  # @return [Object] the result of the method call.
63
96
  def remote_call(remote_method, params)
97
+ start
98
+ subscribe
64
99
  correlation_id = SecureRandom.uuid
65
100
  params = self.class.before_request.call(params) if self.class.before_request
66
- publish(correlation_id: correlation_id, method: remote_method, params: params.rename_keys("_", "-"))
101
+ publish(correlation_id: correlation_id, method: remote_method, params: request_key_formatter(params))
67
102
  wait_for_result(correlation_id)
68
103
  end
69
104
 
70
105
  def wait_for_result(correlation_id)
71
- # `pop` is `Queue#pop`, so it is blocking on the receiving thread and this must happend before the `Hash.delete` or
72
- # the receiving thread won't be able to find the correlation_id in @results
73
- result = @results[correlation_id].pop
74
- @results.delete correlation_id # remove item from hash. prevents memory leak.
75
- result
106
+ # Should be good to timeout here because we're blocking in the main thread here.
107
+ Timeout.timeout(@config.rpc_client_timeout, CarrotRpc::Exception::RpcClientTimeout) do
108
+ # `pop` is `Queue#pop`, so it is blocking on the receiving thread
109
+ # and this must happend before the `Hash.delete` or
110
+ # the receiving thread won't be able to find the correlation_id in @results
111
+ result = @results[correlation_id].pop
112
+ @results.delete correlation_id # remove item from hash. prevents memory leak.
113
+ result
114
+ end
115
+ ensure
116
+ @channel.close
117
+ end
118
+
119
+ # Formats keys in the response data.
120
+ # @param payload [Hash] response data received from the remote server.
121
+ # @return [Hash] formatted data structure.
122
+ def response_key_formatter(payload)
123
+ self.class.format_keys @config.rpc_client_response_key_format, payload
124
+ end
125
+
126
+ # Formats keys in the request data.
127
+ # @param payload [Hash] request data to be sent to the remote server.
128
+ # @return [Hash] formatted data structure.
129
+ def request_key_formatter(params)
130
+ self.class.format_keys @config.rpc_client_request_key_format, params
76
131
  end
77
132
 
133
+ # A @reply_queue is deleted when the channel is closed.
134
+ # Closing the channel accounts for cleanup of the client @reply_queue.
78
135
  def publish(correlation_id:, method:, params:)
79
136
  message = message(
80
137
  correlation_id: correlation_id,
@@ -96,38 +153,6 @@ class CarrotRpc::RpcClient
96
153
  }
97
154
  end
98
155
 
99
- # Convience method as a resource alias for index action.
100
- # To customize, override the method in your class.
101
- #
102
- # @param params [Hash] the arguments for the method being called.
103
- def index(params)
104
- remote_call("index", params)
105
- end
106
-
107
- # Convience method as a resource alias for show action.
108
- # To customize, override the method in your class.
109
- #
110
- # @param params [Hash] the arguments for the method being called.
111
- def show(params)
112
- remote_call("show", params)
113
- end
114
-
115
- # Convience method as a resource alias for create action.
116
- # To customize, override the method in your class.
117
- #
118
- # @param params [Hash] the arguments for the method being called.
119
- def create(params)
120
- remote_call("create", params)
121
- end
122
-
123
- # Convience method as a resource alias for update action.
124
- # To customize, override the method in your class.
125
- #
126
- # @param params [Hash] the arguments for the method being called.
127
- def update(params)
128
- remote_call("update", params)
129
- end
130
-
131
156
  private
132
157
 
133
158
  # Logic to find the data from the RPC response.
@@ -75,7 +75,7 @@ class CarrotRpc::ServerRunner::Pid
75
75
 
76
76
  ## Instance Methods
77
77
 
78
- # Exits if {#status} indicates server is already running, otherwise deletes {#path}.
78
+ # Exits if status indicates server is already running, otherwise deletes {#path}.
79
79
  #
80
80
  # @return [void]
81
81
  def check
@@ -1,4 +1,4 @@
1
- # Traps all {the signals NAMES} that should be intercepted by a long-running background process.
1
+ # Traps all the signals {NAMES} that should be intercepted by a long-running background process.
2
2
  module CarrotRpc::ServerRunner::Signals
3
3
  # CONSTANTS
4
4
 
@@ -1,3 +1,3 @@
1
1
  module CarrotRpc
2
- VERSION = "0.4.1".freeze
2
+ VERSION = "0.5.0".freeze
3
3
  end
data/lib/carrot_rpc.rb CHANGED
@@ -18,9 +18,11 @@ module CarrotRpc
18
18
  extend ActiveSupport::Autoload
19
19
 
20
20
  autoload :CLI
21
+ autoload :ClientActions
21
22
  autoload :ClientServer
22
23
  autoload :Configuration
23
24
  autoload :Error
25
+ autoload :Exception
24
26
  autoload :HashExtensions
25
27
  autoload :RpcClient
26
28
  autoload :RpcServer
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carrot_rpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Hamilton
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-04-20 00:00:00.000000000 Z
12
+ date: 2016-05-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -109,6 +109,20 @@ dependencies:
109
109
  - - "~>"
110
110
  - !ruby/object:Gem::Version
111
111
  version: 0.36.0
112
+ - !ruby/object:Gem::Dependency
113
+ name: yard
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '0.8'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '0.8'
112
126
  description: Streamlined approach to setting up RPC over RabbitMQ.
113
127
  email:
114
128
  - shamil614@gmail.com
@@ -138,10 +152,12 @@ files:
138
152
  - circle.yml
139
153
  - lib/carrot_rpc.rb
140
154
  - lib/carrot_rpc/cli.rb
155
+ - lib/carrot_rpc/client_actions.rb
141
156
  - lib/carrot_rpc/client_server.rb
142
157
  - lib/carrot_rpc/configuration.rb
143
158
  - lib/carrot_rpc/error.rb
144
159
  - lib/carrot_rpc/error/code.rb
160
+ - lib/carrot_rpc/exception.rb
145
161
  - lib/carrot_rpc/hash_extensions.rb
146
162
  - lib/carrot_rpc/rpc_client.rb
147
163
  - lib/carrot_rpc/rpc_server.rb