carrot_rpc 0.5.1 → 0.6.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: d9202e93b42d441f3781fef272d5957cfab7ea4a
4
- data.tar.gz: 91e6cc50e86d52b94693bacf40a45ba13a17c70d
3
+ metadata.gz: b377cb46e74db89d8b6bcd97d305a26a0964d539
4
+ data.tar.gz: 266101d5211ce7409438a958dc5f9629e20cd43e
5
5
  SHA512:
6
- metadata.gz: 6020b67ed40ebdee85b960f2254cdedceca9d93cc289e0c5900f7ab10f3ec9a77d5b9c8a64274c2530f94fd6263ef2703cc808398ea1819db23668dc1c6101fd
7
- data.tar.gz: b001fd637d344818920319b27b1ddc51dda764f5a2335d037864a6f36687eee030ee411c2238ee31324b015e1fe23fa0192322fe6f10f7691df663f30a8a5a8d
6
+ metadata.gz: 4b42e357b5212e94d321a9ef2448567f23d905330a931218046f5920a39c818dadf20f3ad940d20da7b63841bb0a9e97595b911da978ae78d6305fa12c1b8dd7
7
+ data.tar.gz: a23da7aca020a9b0dfc1e17a521aea69a2675db343a1065c152b628d72d90e04c47655c8a6f9b218480174e16bb282112ab6664decd22ad6ec7b0bde07a51b07
data/CHANGELOG.md CHANGED
@@ -40,6 +40,14 @@
40
40
  # Changelog
41
41
  All significant changes in the project are documented here.
42
42
 
43
+ ## v0.6.0
44
+ ### Enhancements
45
+ * [#34](https://github.com/C-S-D/carrot_rpc/pull/34) - `--server_test_mode` options for `carrot_rpc` sets `CarrotRpc.configuration.server_test_mode` to `true`. When `server_test_mode` is true, `_test` is appended to the queue name used by `CarrotRpc::RpcServer` and `CarrotRpc::RpcClient`, so that tests don't use the same queue as production or development. - [@shamil614](https://github.com/C-S-D/carrot_rpc/pull/34)
46
+ * [#36](https://github.com/C-S-D/carrot_rpc/pull/36) - Request in thread-local variable, so it can be used for client request - [@KronicDeth](https://github.com/KronicDeth)
47
+ * `carrot_rpc --thread-request VARIABLE` allows the request payload to be put in a Thread-local `VARIABLE, so that client that are invoked during an RPC server request can use parts of the request, most importantly, parts of "meta" in their own requests. This is needed to pass along ownership information for db_connection in Ecto 2.0.
48
+ * Tag the log with the correlation_id, as it can be filtered for then.
49
+ * Clarify whether a request or response is being published or received, so that server vs client logging can be distinguished.
50
+
43
51
  ## v0.5.1
44
52
  ### Bug Fixes
45
53
  * [#31](https://github.com/C-S-D/carrot_rpc/pull/31) - If the server does not respond to a method in the `request_message`, then return a "Method not found" JSONRPC 2.0 error instead of the server crashing with `NoMethodError` exception. - [KronicDeth)(https://github.com/KronicDeth)
data/README.md CHANGED
@@ -53,18 +53,19 @@ By typing in `carrot_rpc -h` you will see all the command line options:
53
53
  Usage: server [options]
54
54
 
55
55
  Process options:
56
- -d, --daemonize run daemonized in the background (default: false)
57
- --pidfile PIDFILE the pid filename
58
- -s, --runloop_sleep VALUE Configurable sleep time in the runloop
59
- --autoload_rails VALUE loads rails env by default. Uses Rails Logger by default.
60
- --logfile VALUE relative path and name for Log file. Overrides Rails logger.
61
- --loglevel VALUE levels of loggin: DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
62
- --rabbitmq_url VALUE connection string to RabbitMQ 'amqp://user:pass@host:10000/vhost'
56
+ -d, --daemonize run daemonized in the background (default: false)
57
+ --pidfile PIDFILE the pid filename
58
+ -s, --runloop_sleep VALUE Configurable sleep time in the runloop
59
+ --autoload_rails VALUE loads rails env by default. Uses Rails Logger by default.
60
+ --logfile VALUE relative path and name for Log file. Overrides Rails logger.
61
+ --loglevel VALUE levels of loggin: DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
62
+ --rabbitmq_url VALUE connection string to RabbitMQ 'amqp://user:pass@host:10000/vhost'
63
+ --thread-request VARIABLE Copies the current request into VARIABLE Thread local variable, where it can be retrieved with `Thread.thread_variable_get(<VARIABLE>)`
63
64
 
64
65
  Ruby options:
65
- -I, --include PATH an additional $LOAD_PATH
66
- --debug set $DEBUG to true
67
- --warn enable warnings
66
+ -I, --include PATH an additional $LOAD_PATH
67
+ --debug set $DEBUG to true
68
+ --warn enable warnings
68
69
 
69
70
  Common options:
70
71
  -h, --help
@@ -95,9 +96,10 @@ CarrotRpc.configure do |config|
95
96
  config.rpc_client_response_key_format = :underscore
96
97
 
97
98
  # Don't use. Server implementation only. The values below are set via CLI:
99
+ # config.logfile = nil
98
100
  # config.pidfile = nil
99
101
  # config.runloop_sleep = 0
100
- # config.logfile = nil
102
+ # config.thread_request_variable = nil
101
103
  end
102
104
  ```
103
105
 
@@ -182,6 +184,33 @@ car_client = CarClient.new(config)
182
184
  ```
183
185
  By duplicating the `Configuration` instance you can override the global configuration and pass a custom configuration to the RpcClient instance.
184
186
 
187
+ #### Using request threading in clients
188
+
189
+ If you run the server with `--thread-request VARIABLE`, you can retrieve that variable in the `before_request` callback, such as to pass along important meta data:
190
+
191
+ ```sh
192
+ carrot_rpc --thread-request carrot_rpc_server_request_message
193
+ ```
194
+
195
+ Example Client: `app/clients/profile_client.rb`
196
+ ```
197
+ # Allows calls to other RPC server's profile resource.
198
+ class ProfileClient < CarrotRpc::RpcClient
199
+ before_request ->(params){
200
+ request_message = Thread.current.thread_variable_get(:carrot_rpc_server_request_message)
201
+
202
+ if request_message
203
+ meta = params[:meta] || {}
204
+ # beam is test meta data specific to Elixir
205
+ meta = meta.merge(beam: request_message[:params][:meta][:beam])
206
+ params.merge(meta: meta)
207
+ else
208
+ params
209
+ end
210
+ }
211
+ end
212
+ ```
213
+
185
214
  ### Support for JSONAPI::Resources
186
215
  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.
187
216
 
@@ -38,6 +38,12 @@ module CarrotRpc::CLI
38
38
  CarrotRpc.configuration.runloop_sleep = value
39
39
  end
40
40
 
41
+ stm_msg = "runs servers with '_test' appended to queue names." \
42
+ "Set Rails Rack env vars to 'test' when used in conjunction with '--autoload_rails'"
43
+ option_parser.on(" ", "--server_test_mode", stm_msg) do
44
+ CarrotRpc.configuration.server_test_mode = true
45
+ end
46
+
41
47
  option_parser.on(
42
48
  " ",
43
49
  "--autoload_rails value",
@@ -67,6 +73,15 @@ module CarrotRpc::CLI
67
73
  ) do |value|
68
74
  CarrotRpc.configuration.bunny = Bunny.new(value)
69
75
  end
76
+
77
+ option_parser.on(
78
+ " ",
79
+ "--thread-request VARIABLE",
80
+ "Copies the current request into VARIABLE Thread local variable, " \
81
+ "where it can be retrieved with `Thread.thread_variable_get(<VARIABLE>)`"
82
+ ) do |variable|
83
+ CarrotRpc.configuration.thread_request_variable = variable
84
+ end
70
85
  end
71
86
 
72
87
  def self.add_ruby_options(option_parser)
@@ -22,4 +22,13 @@ module CarrotRpc::ClientServer
22
22
  "queue_name(new_name) :: new_name or queue_name() :: current_name are the only ways to call queue_name"
23
23
  end
24
24
  end
25
+
26
+ def test_queue_name(name, append_name = false)
27
+ return name unless append_name
28
+ if name
29
+ "#{name}_test"
30
+ else
31
+ fail CarrotRpc::Exception::InvalidQueueName
32
+ end
33
+ end
25
34
  end
@@ -1,7 +1,8 @@
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, :rpc_client_timeout, :rpc_client_response_key_format, :rpc_client_request_key_format
4
+ :before_request, :rpc_client_timeout, :rpc_client_response_key_format, :rpc_client_request_key_format,
5
+ :server_test_mode, :client_test_mode, :thread_request_variable
5
6
 
6
7
  # logfile - set logger to a file. overrides rails logger.
7
8
 
@@ -18,5 +19,8 @@ class CarrotRpc::Configuration
18
19
  @rpc_client_timeout = 5
19
20
  @rpc_client_response_key_format = :none
20
21
  @rpc_client_request_key_format = :none
22
+ @client_test_mode = false
23
+ @server_test_mode = false
24
+ @thread_request_variable = nil
21
25
  end # rubocop:enable Metrics/MethodLength
22
26
  end
@@ -2,4 +2,5 @@
2
2
  module CarrotRpc::Exception
3
3
  # Exception to be raised when the client timesout waiting for a response.
4
4
  class RpcClientTimeout < StandardError; end
5
+ class InvalidQueueName < StandardError; end
5
6
  end
@@ -0,0 +1,23 @@
1
+ # Used to format keys
2
+ module CarrotRpc::Format
3
+ using CarrotRpc::HashExtensions
4
+
5
+ # Logic to process the renaming of keys in a hash.
6
+ # @param format [Symbol] :dasherize changes keys that have "_" to "-"
7
+ # @param format [Symbol] :underscore changes keys that have "-" to "_"
8
+ # @param format [Symbol] :skip, will not rename the keys
9
+ # @param data [Hash] data structure to be transformed
10
+ # @return [Hash] the transformed data
11
+ def self.keys(format, data)
12
+ case format
13
+ when :dasherize
14
+ data.rename_keys("_", "-")
15
+ when :underscore
16
+ data.rename_keys("-", "_")
17
+ when :none
18
+ data
19
+ else
20
+ data
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ # Publishes properly formatted replies to consumed requests
2
+ module CarrotRpc::Reply
3
+ private
4
+
5
+ def reply(properties:, response_message:)
6
+ payload = response_message.to_json
7
+
8
+ logger.debug "Publishing response: #{payload}"
9
+
10
+ @exchange.publish payload,
11
+ correlation_id: properties.correlation_id,
12
+ routing_key: properties.reply_to
13
+ end
14
+
15
+ # See http://www.jsonrpc.org/specification#error_object
16
+ def reply_error(error, properties:, request_message:)
17
+ response_message = { error: error, id: request_message[:id], jsonrpc: "2.0" }
18
+
19
+ reply properties: properties,
20
+ response_message: response_message
21
+ end
22
+
23
+ def reply_method_not_found(method:, properties:, request_message:)
24
+ error = CarrotRpc::Error.new code: CarrotRpc::Error::Code::METHOD_NOT_FOUND,
25
+ data: {
26
+ method: method
27
+ },
28
+ message: "Method not found"
29
+ logger.error(error)
30
+
31
+ reply_error error.serialized_message,
32
+ properties: properties,
33
+ request_message: request_message
34
+ end
35
+
36
+ # See http://www.jsonrpc.org/specification#response_object
37
+ def reply_result(result, properties:, request_message:)
38
+ if result && result.is_a?(Hash) && result["errors"]
39
+ reply_result_with_errors(result, properties: properties, request_message: request_message)
40
+ else
41
+ reply_result_without_errors(result, properties: properties, request_message: request_message)
42
+ end
43
+ end
44
+
45
+ def reply_result_with_errors(result, properties:, request_message:)
46
+ scrubbed_result = result.merge(
47
+ "errors" => CarrotRpc::Scrub.errors(result.fetch("errors"))
48
+ )
49
+ reply_error({ code: 422, data: scrubbed_result, message: "JSONAPI error" },
50
+ properties: properties,
51
+ request_message: request_message)
52
+ end
53
+
54
+ def reply_result_without_errors(result, properties:, request_message:)
55
+ response_message = { id: request_message[:id], jsonrpc: "2.0", result: result }
56
+
57
+ reply properties: properties,
58
+ response_message: response_message
59
+ end
60
+ end
@@ -4,8 +4,6 @@ require "securerandom"
4
4
  # Let's define a naming convention here for subclasses becuase I don't want to write a Confluence doc.
5
5
  # All subclasses should have the following naming convention: <Name>RpcConsumer ex: PostRpcConsumer
6
6
  class CarrotRpc::RpcClient
7
- using CarrotRpc::HashExtensions
8
-
9
7
  attr_reader :channel, :server_queue, :logger
10
8
 
11
9
  extend CarrotRpc::ClientServer
@@ -21,25 +19,6 @@ class CarrotRpc::RpcClient
21
19
  end
22
20
  end
23
21
 
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
22
  # Use defaults for application level connection to RabbitMQ.
44
23
  #
45
24
  # @example pass custom {Configuration} class as an argument to override.
@@ -59,8 +38,9 @@ class CarrotRpc::RpcClient
59
38
  # Create a new channel on each request because the channel should be closed after each request.
60
39
  @channel = @config.bunny.create_channel
61
40
 
41
+ queue_name = self.class.test_queue_name(self.class.queue_name, @config.client_test_mode)
62
42
  # auto_delete => false keeps the queue around until RabbitMQ restarts or explicitly deleted
63
- @server_queue = @channel.queue(self.class.queue_name, auto_delete: false)
43
+ @server_queue = @channel.queue(queue_name, auto_delete: false)
64
44
 
65
45
  # Setup a direct exchange.
66
46
  @exchange = @channel.default_exchange
@@ -76,12 +56,8 @@ class CarrotRpc::RpcClient
76
56
 
77
57
  # setup subscribe block to Service
78
58
  # block => false is a non blocking IO option.
79
- @reply_queue.subscribe(block: false) do |_delivery_info, properties, payload|
80
- response = JSON.parse(payload).with_indifferent_access
81
-
82
- result = parse_response(response)
83
- result = response_key_formatter(result).with_indifferent_access if result.is_a? Hash
84
- @results[properties[:correlation_id]].push(result)
59
+ @reply_queue.subscribe(block: false) do |delivery_info, properties, payload|
60
+ consume(delivery_info, properties, payload)
85
61
  end
86
62
  end
87
63
 
@@ -97,9 +73,11 @@ class CarrotRpc::RpcClient
97
73
  start
98
74
  subscribe
99
75
  correlation_id = SecureRandom.uuid
100
- params = self.class.before_request.call(params) if self.class.before_request
101
- publish(correlation_id: correlation_id, method: remote_method, params: request_key_formatter(params))
102
- wait_for_result(correlation_id)
76
+ logger.with_correlation_id(correlation_id) do
77
+ params = self.class.before_request.call(params) if self.class.before_request
78
+ publish(correlation_id: correlation_id, method: remote_method, params: request_key_formatter(params))
79
+ wait_for_result(correlation_id)
80
+ end
103
81
  end
104
82
 
105
83
  def wait_for_result(correlation_id)
@@ -120,14 +98,14 @@ class CarrotRpc::RpcClient
120
98
  # @param payload [Hash] response data received from the remote server.
121
99
  # @return [Hash] formatted data structure.
122
100
  def response_key_formatter(payload)
123
- self.class.format_keys @config.rpc_client_response_key_format, payload
101
+ CarrotRpc::Format.keys @config.rpc_client_response_key_format, payload
124
102
  end
125
103
 
126
104
  # Formats keys in the request data.
127
105
  # @param payload [Hash] request data to be sent to the remote server.
128
106
  # @return [Hash] formatted data structure.
129
107
  def request_key_formatter(params)
130
- self.class.format_keys @config.rpc_client_request_key_format, params
108
+ CarrotRpc::Format.keys @config.rpc_client_request_key_format, params
131
109
  end
132
110
 
133
111
  # A @reply_queue is deleted when the channel is closed.
@@ -138,10 +116,7 @@ class CarrotRpc::RpcClient
138
116
  params: params,
139
117
  method: method
140
118
  )
141
- # Reply To => make sure the service knows where to send it's response.
142
- # Correlation ID => identify the results that belong to the unique call made
143
- @exchange.publish(message.to_json, routing_key: @server_queue.name, correlation_id: correlation_id,
144
- reply_to: @reply_queue.name)
119
+ publish_payload(message.to_json, correlation_id: correlation_id)
145
120
  end
146
121
 
147
122
  def message(correlation_id:, method:, params:)
@@ -155,6 +130,18 @@ class CarrotRpc::RpcClient
155
130
 
156
131
  private
157
132
 
133
+ def consume(_delivery_info, properties, payload)
134
+ logger.with_correlation_id(properties[:correlation_id]) do
135
+ logger.debug "Receiving response: #{payload}"
136
+
137
+ response = JSON.parse(payload).with_indifferent_access
138
+
139
+ result = parse_response(response)
140
+ result = response_key_formatter(result).with_indifferent_access if result.is_a? Hash
141
+ @results[properties[:correlation_id]].push(result)
142
+ end
143
+ end
144
+
158
145
  # Logic to find the data from the RPC response.
159
146
  # @param [Hash] response from rpc call
160
147
  # @return [Hash,nil]
@@ -169,4 +156,14 @@ class CarrotRpc::RpcClient
169
156
  response
170
157
  end
171
158
  end
159
+
160
+ def publish_payload(payload, correlation_id:)
161
+ # Reply To => make sure the service knows where to send it's response.
162
+ # Correlation ID => identify the results that belong to the unique call made
163
+ logger.debug "Publishing request: #{payload}"
164
+ @exchange.publish payload,
165
+ correlation_id: correlation_id,
166
+ reply_to: @reply_queue.name,
167
+ routing_key: @server_queue.name
168
+ end
172
169
  end
@@ -4,19 +4,22 @@ class CarrotRpc::RpcServer
4
4
 
5
5
  using CarrotRpc::HashExtensions
6
6
 
7
- attr_reader :channel, :server_queue, :logger
7
+ attr_reader :channel, :server_queue, :logger, :thread_request_variable
8
8
  # method_reciver => object that receives the method. can be a class or anything responding to send
9
9
 
10
10
  extend CarrotRpc::ClientServer
11
+ include CarrotRpc::Reply
11
12
 
12
13
  # Documentation advises not to share a channel connection. Create new channel for each server instance.
13
14
  def initialize(config: nil, block: true)
14
15
  # create a channel and exchange that both client and server know about
15
16
  config ||= CarrotRpc.configuration
17
+ @thread_request_variable = config.thread_request_variable
16
18
  @channel = config.bunny.create_channel
17
19
  @logger = config.logger
18
20
  @block = block
19
- @server_queue = @channel.queue(self.class.queue_name)
21
+ queue_name = self.class.test_queue_name(self.class.queue_name, config.server_test_mode)
22
+ @server_queue = @channel.queue(queue_name)
20
23
  @exchange = @channel.default_exchange
21
24
  end
22
25
 
@@ -24,28 +27,21 @@ class CarrotRpc::RpcServer
24
27
  # method => object that receives the method. can be a class or anything responding to send
25
28
  def start
26
29
  # subscribe is like a callback
27
- @server_queue.subscribe(block: @block) do |_delivery_info, properties, payload|
28
- logger.debug "Receiving message: #{payload}"
29
-
30
- request_message = JSON.parse(payload).with_indifferent_access
31
-
32
- process_request(request_message, properties: properties)
30
+ @server_queue.subscribe(block: @block) do |delivery_info, properties, payload|
31
+ consume(delivery_info, properties, payload)
33
32
  end
34
33
  end
35
34
 
36
35
  def process_request(request_message, properties:)
37
- method = request_message[:method]
38
-
39
- handler = if respond_to? method
40
- :call_found_method
41
- else
42
- :reply_method_not_found
43
- end
44
-
45
- send handler,
46
- method: method,
47
- properties: properties,
48
- request_message: request_message
36
+ maybe_thread_request(request_message: request_message) do
37
+ method = request_message[:method]
38
+ handler = method_handler(method)
39
+
40
+ send handler,
41
+ method: method,
42
+ properties: properties,
43
+ request_message: request_message
44
+ end
49
45
  end
50
46
 
51
47
  private
@@ -64,73 +60,48 @@ class CarrotRpc::RpcServer
64
60
  request_message: request_message
65
61
  end
66
62
 
67
- def reply(properties:, response_message:)
68
- @exchange.publish response_message.to_json,
69
- correlation_id: properties.correlation_id,
70
- routing_key: properties.reply_to
71
- end
72
-
73
- # See http://www.jsonrpc.org/specification#error_object
74
- def reply_error(error, properties:, request_message:)
75
- response_message = { error: error, id: request_message[:id], jsonrpc: "2.0" }
63
+ def consume(_delivery_info, properties, payload)
64
+ logger.tagged("server", "queue=#{server_queue.name}", "correlation_id=#{properties[:correlation_id]}") do
65
+ logger.debug "Receiving request: #{payload}"
76
66
 
77
- logger.debug "Publish error: #{error} to #{response_message}"
67
+ # rubocop:disable Lint/RescueException
68
+ begin
69
+ request_message = JSON.parse(payload).with_indifferent_access
78
70
 
79
- reply properties: properties,
80
- response_message: response_message
81
- end
82
-
83
- def reply_method_not_found(method:, properties:, request_message:)
84
- error = CarrotRpc::Error.new code: CarrotRpc::Error::Code::METHOD_NOT_FOUND,
85
- data: {
86
- method: method
87
- },
88
- message: "Method not found"
89
- logger.error(error)
90
-
91
- reply_error error.serialized_message,
92
- properties: properties,
93
- request_message: request_message
71
+ process_request(request_message, properties: properties)
72
+ rescue Exception => exception
73
+ logger.error(exception)
74
+ end
75
+ # rubocop:enable Lint/RescueException
76
+ end
94
77
  end
95
78
 
96
- # See http://www.jsonrpc.org/specification#response_object
97
- def reply_result(result, properties:, request_message:)
98
- if result && result.is_a?(Hash) && result["errors"]
99
- reply_result_with_errors(result, properties: properties, request_message: request_message)
79
+ def maybe_thread_request(request_message:)
80
+ if thread_request_variable
81
+ thread_request(request_message: request_message, &Proc.new)
100
82
  else
101
- reply_result_without_errors(result, properties: properties, request_message: request_message)
83
+ yield
102
84
  end
103
85
  end
104
86
 
105
- def reply_result_with_errors(result, properties:, request_message:)
106
- scrubbed_result = result.merge(
107
- "errors" => scrub_errors(result.fetch("errors"))
108
- )
109
- reply_error({ code: 422, data: scrubbed_result, message: "JSONAPI error" },
110
- properties: properties,
111
- request_message: request_message)
87
+ def method_handler(method)
88
+ if respond_to? method
89
+ :call_found_method
90
+ else
91
+ :reply_method_not_found
92
+ end
112
93
  end
113
94
 
114
- def reply_result_without_errors(result, properties:, request_message:)
115
- response_message = { id: request_message[:id], jsonrpc: "2.0", result: result }
95
+ def thread_request(request_message:)
96
+ logger.debug "Threading request (#{request_message.inspect}) " \
97
+ "in Thread.current.thread_variable_get(#{thread_request_variable.inspect})"
116
98
 
117
- logger.debug "Publishing result: #{result} to #{response_message}"
99
+ Thread.current.thread_variable_set(thread_request_variable, request_message)
118
100
 
119
- reply properties: properties,
120
- response_message: response_message
121
- end
122
-
123
- # Removes `nil` values as JSONAPI spec expects unset keys not to be transmitted
124
- def scrub_error(error)
125
- error.reject { |_, value|
126
- value.nil?
127
- }
128
- end
129
-
130
- # Removes `nil` values as JSONAPI spec expects unset keys not to be transmitted
131
- def scrub_errors(errors)
132
- errors.map { |error|
133
- scrub_error(error)
134
- }
101
+ begin
102
+ yield
103
+ ensure
104
+ Thread.current.thread_variable_set(thread_request_variable, nil)
105
+ end
135
106
  end
136
107
  end
@@ -0,0 +1,16 @@
1
+ # Removes `nil` valued keys from nested `Hash`es.
2
+ module CarrotRpc::Scrub
3
+ # Removes `nil` values as JSONAPI spec expects unset keys not to be transmitted
4
+ def self.error(error)
5
+ error.reject { |_, value|
6
+ value.nil?
7
+ }
8
+ end
9
+
10
+ # Removes `nil` values as JSONAPI spec expects unset keys not to be transmitted
11
+ def self.errors(errors)
12
+ errors.map { |error|
13
+ error(error)
14
+ }
15
+ end
16
+ end
@@ -19,7 +19,7 @@ module CarrotRpc::ServerRunner::AutoloadRails
19
19
 
20
20
  if File.exist?(rails_path)
21
21
  logger.info "Rails app found at: #{rails_path}"
22
- ENV["RACK_ENV"] ||= ENV["RAILS_ENV"] || "development"
22
+ set_rack_rails_env
23
23
  require rails_path
24
24
  ::Rails.application.eager_load!
25
25
  true
@@ -38,4 +38,17 @@ module CarrotRpc::ServerRunner::AutoloadRails
38
38
  load_root(root, logger: logger)
39
39
  end
40
40
  end
41
+
42
+ # Set Rails/Rack env vars to test when server test mode is enabled.
43
+ #
44
+ # @return [Void]
45
+ def self.set_rack_rails_env
46
+ if CarrotRpc.configuration.server_test_mode
47
+ env_mode = "test"
48
+ ENV["RACK_ENV"] = ENV["RAILS_ENV"] = env_mode
49
+ else
50
+ env_mode = "development"
51
+ ENV["RACK_ENV"] ||= ENV["RAILS_ENV"] ||= env_mode
52
+ end
53
+ end
41
54
  end
@@ -81,7 +81,8 @@ class CarrotRpc::ServerRunner
81
81
  server_klass_name = file.to_s.split("/").last.gsub(".rb", "").camelize
82
82
  server_klass = server_klass_name.constantize
83
83
 
84
- logger.info "Starting #{server_klass}..."
84
+ stm = CarrotRpc.configuration.server_test_mode
85
+ logger.info "Starting: #{server_klass} | Rails/Rack ENV: #{ENV['RAILS_ENV']} | Server Test Mode: #{stm}"
85
86
 
86
87
  server = server_klass.new(block: false)
87
88
  server.start
@@ -21,4 +21,10 @@ class CarrotRpc::TaggedLog
21
21
  logger.tagged(tags) { logger.send(level, msg || block.call) }
22
22
  end
23
23
  end
24
+
25
+ delegate :tagged, to: :logger
26
+
27
+ def with_correlation_id(correlation_id, &block)
28
+ tagged("correlation_id=#{correlation_id}", &block)
29
+ end
24
30
  end
@@ -1,3 +1,3 @@
1
1
  module CarrotRpc
2
- VERSION = "0.5.1".freeze
2
+ VERSION = "0.6.0".freeze
3
3
  end
data/lib/carrot_rpc.rb CHANGED
@@ -23,10 +23,13 @@ module CarrotRpc
23
23
  autoload :Configuration
24
24
  autoload :Error
25
25
  autoload :Exception
26
+ autoload :Format
26
27
  autoload :HashExtensions
28
+ autoload :Reply
27
29
  autoload :RpcClient
28
30
  autoload :RpcServer
29
31
  autoload :ServerRunner
32
+ autoload :Scrub
30
33
  autoload :TaggedLog
31
34
 
32
35
  class << self
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.5.1
4
+ version: 0.6.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-05-17 00:00:00.000000000 Z
12
+ date: 2016-07-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -158,11 +158,14 @@ files:
158
158
  - lib/carrot_rpc/error.rb
159
159
  - lib/carrot_rpc/error/code.rb
160
160
  - lib/carrot_rpc/exception.rb
161
+ - lib/carrot_rpc/format.rb
161
162
  - lib/carrot_rpc/hash_extensions.rb
163
+ - lib/carrot_rpc/reply.rb
162
164
  - lib/carrot_rpc/rpc_client.rb
163
165
  - lib/carrot_rpc/rpc_server.rb
164
166
  - lib/carrot_rpc/rpc_server/jsonapi_resources.rb
165
167
  - lib/carrot_rpc/rpc_server/jsonapi_resources/actions.rb
168
+ - lib/carrot_rpc/scrub.rb
166
169
  - lib/carrot_rpc/server_runner.rb
167
170
  - lib/carrot_rpc/server_runner/autoload_rails.rb
168
171
  - lib/carrot_rpc/server_runner/logger.rb