carrot_rpc 0.4.1 → 0.5.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: 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