langis 0.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.
- data/Gemfile +14 -0
- data/LICENSE +202 -0
- data/NOTICE +4 -0
- data/README.md +533 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/generators/langis_config_generator.rb +7 -0
- data/generators/templates/langis_config.rb +44 -0
- data/lib/langis.rb +60 -0
- data/lib/langis/dsl.rb +346 -0
- data/lib/langis/engine.rb +146 -0
- data/lib/langis/middleware.rb +135 -0
- data/lib/langis/rackish.rb +118 -0
- data/lib/langis/sinks.rb +138 -0
- data/spec/langis/dsl_spec.rb +301 -0
- data/spec/langis/engine_spec.rb +168 -0
- data/spec/langis/middleware_spec.rb +196 -0
- data/spec/langis/rackish_spec.rb +33 -0
- data/spec/langis/sinks/delayed_job_sink_spec.rb +227 -0
- data/spec/langis/sinks/redis_resque_sink_spec.rb +232 -0
- data/spec/redis.conf +132 -0
- data/spec/spec_helper.rb +8 -0
- metadata +171 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
module Langis
|
2
|
+
module Engine
|
3
|
+
|
4
|
+
##
|
5
|
+
# Instances of this class are executed when a message is pushed to
|
6
|
+
# the intake's EventMachine::Channel that it subscribed to. Its
|
7
|
+
# primary function is to safely execute the Langis sink
|
8
|
+
# (Rackish application) that it has been tasked to manage. To do
|
9
|
+
# this safely with performance considerations, it enqueues a
|
10
|
+
# Proc to be handled by the EvenMachine's deferred thread pool and
|
11
|
+
# protects the thread pool by wrapping the sink call with a rescue block.
|
12
|
+
# Any caught errors will result in an error message with the caught
|
13
|
+
# exception pushed into the given EventMachine error channel. All
|
14
|
+
# successful completions will push the returned Rackish result to the
|
15
|
+
# EventMachine success channel.
|
16
|
+
class EventMachineRunner
|
17
|
+
|
18
|
+
##
|
19
|
+
#
|
20
|
+
# @param [#call] app The Rackish app to execute.
|
21
|
+
# @param [Hash] options ({})
|
22
|
+
# @option options [EventMachine::Channel] :success_channel (nil) The
|
23
|
+
# EventMachine::Channel instance to push the Rackish app's return
|
24
|
+
# results to. This happens when there are no errors raised.
|
25
|
+
# @option options [EventMachine::Channel] :error_channel (nil) The
|
26
|
+
# EventMachine::Channel instance to push error messages to when the
|
27
|
+
# runner catches an exception during the execution of the Rackish app.
|
28
|
+
# @option options [Object] :evm (EventMachine) Specify a different
|
29
|
+
# class/module to use when executing deferred calls. Mainly useful
|
30
|
+
# for unit testing, or if you want to run the app directly in
|
31
|
+
# the main thread instead of the deferred thread pool.
|
32
|
+
def initialize(app, options={})
|
33
|
+
@app = app
|
34
|
+
@success_channel = options[:success_channel]
|
35
|
+
@error_channel = options[:error_channel]
|
36
|
+
@evm = options[:evm] || EventMachine
|
37
|
+
@intake_name = options[:intake_name]
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# The method that is called in the EventMachine's main reactor thread
|
42
|
+
# whose job is to enqueue the main app code to be run by EventMachine's
|
43
|
+
# deferred thread pool.
|
44
|
+
#
|
45
|
+
# This method sets up the Rackish environment hash that is passed
|
46
|
+
# as the main input to the Rackish app; it sets up the Proc that will
|
47
|
+
# be actually executed in by the deferred thread pool. The proc
|
48
|
+
# protects the thread pool from exceptions, and pushes respective
|
49
|
+
# error and success results to given EventMachine channels.
|
50
|
+
#
|
51
|
+
# @param [Object] message The message that is getting pushed through
|
52
|
+
# the Langis pipes to their eventual handlers.
|
53
|
+
def call(message)
|
54
|
+
# Assign local variables to the proper apps, etc for readability.
|
55
|
+
app = @app
|
56
|
+
success_channel = @success_channel
|
57
|
+
error_channel = @error_channel
|
58
|
+
intake_name = @intake_name
|
59
|
+
# Enqueue the proc to be run in by the deferred thread pool.
|
60
|
+
@evm.defer(proc do
|
61
|
+
# Create the base environment that is understood by the Rackish apps.
|
62
|
+
env = {}
|
63
|
+
env[MESSAGE_TYPE_KEY] = message.message_type.to_s if(
|
64
|
+
message.respond_to? :message_type)
|
65
|
+
env[MESSAGE_KEY] = message
|
66
|
+
env[INTAKE_KEY] = intake_name
|
67
|
+
# Actually run the Rackish app, protected by a rescue block.
|
68
|
+
# Push the results to their respective channels when finished.
|
69
|
+
begin
|
70
|
+
results = app.call env
|
71
|
+
success_channel.push(results) if success_channel
|
72
|
+
rescue => e
|
73
|
+
# It was an error, so we have to create a Rackish response array.
|
74
|
+
# We push a SERVER_ERROR status along with an enhanced
|
75
|
+
# headers section: the exception and original message.
|
76
|
+
error_channel.push([
|
77
|
+
SERVER_ERROR,
|
78
|
+
env.merge({ X_EXCEPTION => e}),
|
79
|
+
['']]) if error_channel
|
80
|
+
end
|
81
|
+
end)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# And EventMachine based implementation of a Langis Engine. Its sole
|
87
|
+
# job is to take a pumped message into an intake and broadcast the same
|
88
|
+
# message to all of the intake's registered sinks. In essense these
|
89
|
+
# engines need to execute the sinks handler methods for each message.
|
90
|
+
#
|
91
|
+
# This class leverages EventMachine's features to easily do efficient
|
92
|
+
# publishing to the subscribers, and uses the EventMachineRunner
|
93
|
+
# to do the actual code execution.
|
94
|
+
#
|
95
|
+
# @see EventMachineRunner
|
96
|
+
class EventMachineEngine
|
97
|
+
|
98
|
+
##
|
99
|
+
# @param [Hash{String=>Array<#call>}] intakes The mapping of intake
|
100
|
+
# names to the list of Rackish applications (sinks) that subscribed
|
101
|
+
# to the given intake.
|
102
|
+
# @option options [Object] :evm (EventMachine) Specify a different
|
103
|
+
# class/module to use when executing deferred calls. Mainly useful
|
104
|
+
# for unit testing, or if you want to run the app directly in
|
105
|
+
# the main thread instead of the deferred thread pool.
|
106
|
+
# @option options [Class] :evm_channel (EventMachine::Channel) The
|
107
|
+
# channel class to instantiate as the underlying pub-sub engine. This
|
108
|
+
# is useful for unittesting, or if you want to implement a
|
109
|
+
# non-EventMachine pub-sub mechanism.
|
110
|
+
def initialize(intakes, options={})
|
111
|
+
evm_channel_class = options[:evm_channel] || EventMachine::Channel
|
112
|
+
@intake_channels = {}
|
113
|
+
intakes.each do |intake_name, apps|
|
114
|
+
runner_options = {
|
115
|
+
:success_channel => options[:success_channel],
|
116
|
+
:error_channel => options[:error_channel],
|
117
|
+
:evm => options[:evm],
|
118
|
+
:intake_name => intake_name
|
119
|
+
}
|
120
|
+
@intake_channels[intake_name] = channel = evm_channel_class.new
|
121
|
+
apps.each do |app|
|
122
|
+
channel.subscribe EventMachineRunner.new(app, runner_options)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Publishes a message into the Langis publish-subscribe bus.
|
129
|
+
#
|
130
|
+
# @overload pump(message)
|
131
|
+
# Publishes the message to the :default intake.
|
132
|
+
# @param [Object] message The message to publish.
|
133
|
+
# @overload pump(message, ...)
|
134
|
+
# Publishes the message to the given list of intakes.
|
135
|
+
# @param [Object] message The message to publish.
|
136
|
+
# @param [#to_s] ... Publish the message to these listed intakes
|
137
|
+
def pump(message, *intakes)
|
138
|
+
intakes.unshift :default if intakes.empty?
|
139
|
+
intakes.each do |name|
|
140
|
+
channel = @intake_channels[name.to_s]
|
141
|
+
channel.push(message) if channel
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Langis
|
2
|
+
module Middleware
|
3
|
+
|
4
|
+
##
|
5
|
+
# Middleware class that modifies the Rackish input environment by
|
6
|
+
# transforming a specific value in the environment before passing
|
7
|
+
# it along to the rest of the Rackish application chain.
|
8
|
+
#
|
9
|
+
# Some useful applications of this transform:
|
10
|
+
# * Serialization or Deserialization of input data.
|
11
|
+
# * Filter or modify the message; to explicitly whitelist the list of
|
12
|
+
# properties in the message that may be exposed to a service or
|
13
|
+
# third party.
|
14
|
+
class EnvFieldTransform
|
15
|
+
|
16
|
+
##
|
17
|
+
#
|
18
|
+
# @param app The Rackish Application for which this instance is acting as
|
19
|
+
# middleware.
|
20
|
+
# @option options [String] :key (Langis::MESSAGE_KEY) The hash key
|
21
|
+
# of the Rackish environment whose value is the object that we
|
22
|
+
# want to transform (transformation object).
|
23
|
+
# @option options [Symbol,String] :to_method (:to_json) The
|
24
|
+
# transformation object that will respond to the invokation of this
|
25
|
+
# method name. The return value of that method will replace the
|
26
|
+
# original transformation object in the Rackish environment as
|
27
|
+
# the environment is passed on to the rest of the Rackish app chain.
|
28
|
+
# @option options [Array,Object] :to_args ([]) The parameter or
|
29
|
+
# list of parameters to pass to the transformation method.
|
30
|
+
def initialize(app, options={})
|
31
|
+
@app = app
|
32
|
+
@to_method = options[:to_method] || :to_json
|
33
|
+
@to_args = options[:to_args] || []
|
34
|
+
@to_args = [@to_args] unless @to_args.is_a? Array
|
35
|
+
@key = options[:key] || MESSAGE_KEY
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Executes the object transformation, and invokes the rest of the
|
40
|
+
# Rackish app chain.
|
41
|
+
#
|
42
|
+
# @param [Hash] env The input Rackish Environment.
|
43
|
+
# @return [Array<Integer,Hash,#each>] The return of the proxied Rackish
|
44
|
+
# application chain.
|
45
|
+
def call(env)
|
46
|
+
item = env[@key].send @to_method, *@to_args
|
47
|
+
return @app.call env.merge({ @key => item })
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Middleware that adds an Array of values to the Rackish Environment
|
53
|
+
# input. This array of values is created by calling callables using the
|
54
|
+
# said Rackish Environment as input, and from static strings.
|
55
|
+
#
|
56
|
+
# The following example creates an Array of size two, and places it
|
57
|
+
# into the Rackish Environment key identified by 'my_key'. The first
|
58
|
+
# item in the Array is the static string, "Hello World". The second value
|
59
|
+
# is whatever was in the Rackish Environment under the key, "name".
|
60
|
+
#
|
61
|
+
# use Parameterizer,
|
62
|
+
# 'Hello World',
|
63
|
+
# lambda { |env| env['name'] },
|
64
|
+
# :env_key => 'my_key'
|
65
|
+
#
|
66
|
+
class Parameterizer
|
67
|
+
|
68
|
+
##
|
69
|
+
# @param [#call] app The next link in the Rackish Application chain.
|
70
|
+
# @param [String,#call] *args The list of new parameters that the
|
71
|
+
# Parameterizer middleware creates. String values are used as is,
|
72
|
+
# and callable objects are executed with the input Rackish Environment
|
73
|
+
# as the first parameter.
|
74
|
+
# @option options [String] :env_key (::Langis::MESSAGE_KEY)
|
75
|
+
def initialize(app, *args)
|
76
|
+
@app = app
|
77
|
+
@options = args.last.kind_of?(Hash) ? args.pop : {}
|
78
|
+
@args = args
|
79
|
+
@env_key = @options[:env_key] || MESSAGE_KEY
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# The main method of the Parameterizer middleware.
|
84
|
+
#
|
85
|
+
# @param [Hash] env The input Rackish Environment.
|
86
|
+
def call(env={})
|
87
|
+
new_args = @args.map do |value|
|
88
|
+
value.respond_to?(:call) ? value.call(env) : value
|
89
|
+
end
|
90
|
+
new_env = {}.update(env)
|
91
|
+
new_env[@env_key] = new_args
|
92
|
+
return @app.call new_env
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Middleware to only continue execution of the Rackish application chain
|
98
|
+
# if the input environment's Langis::MESSAGE_TYPE_KEY is set to a value
|
99
|
+
# that has been whitelisted.
|
100
|
+
class MessageTypeFilter
|
101
|
+
|
102
|
+
##
|
103
|
+
#
|
104
|
+
# @param app The Rackish application chain to front.
|
105
|
+
# @param [#to_s] ... The whitelist of message types to allow pass.
|
106
|
+
def initialize(app, *args)
|
107
|
+
@app = app
|
108
|
+
@message_types = args.map { |message_type| message_type.to_s }
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Executes the filtering, and invokes the rest of the Rackish app chain
|
113
|
+
# if the message type is allowed.
|
114
|
+
#
|
115
|
+
# @param [Hash] env The Rackish input environment.
|
116
|
+
# @return [Array<Integer,Hash,#each>] The return of the proxied Rackish
|
117
|
+
# application chain, or an OK with the filter reason.
|
118
|
+
# @see Langis::X_FILTERED_BY
|
119
|
+
# @see Langis::X_FILTERED_TYPE
|
120
|
+
def call(env)
|
121
|
+
if @message_types.include? env[MESSAGE_TYPE_KEY]
|
122
|
+
return @app.call(env)
|
123
|
+
else
|
124
|
+
return [
|
125
|
+
OK,
|
126
|
+
{
|
127
|
+
X_FILTERED_BY => self.class.to_s,
|
128
|
+
X_FILTERED_TYPE => env[MESSAGE_TYPE_KEY].class
|
129
|
+
},
|
130
|
+
['']]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Langis
|
2
|
+
module Rackish
|
3
|
+
|
4
|
+
##
|
5
|
+
# Error raised when RackishJob is asked to run an unregistered Rackish
|
6
|
+
# Application.
|
7
|
+
#
|
8
|
+
# @see RackishJob
|
9
|
+
class NotFoundError < LangisError
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# RackishJob is a dual DelayedJob-Resque job that is used to execute
|
14
|
+
# Rackish applications, or Rack applications that are robust against
|
15
|
+
# non-conformant "Rack Environments", in the background.
|
16
|
+
# Rackish Applications are created and registered with this RackishJob
|
17
|
+
# class. Each registration is associated with an app_key that is well
|
18
|
+
# known to any component that wants to execute that particular Rackish
|
19
|
+
# Application. Client components then queue up this job class with
|
20
|
+
# the app_key and the input hash for that application.
|
21
|
+
#
|
22
|
+
# Notes
|
23
|
+
# * This class does not provide a compliant Rack specified environment
|
24
|
+
# to the Rackish applications it calls. Prepend middleware that
|
25
|
+
# provides such an environment to the application chain if required.
|
26
|
+
#
|
27
|
+
# For example, to queue up a RackishJob using DelayedJob:
|
28
|
+
# Delayed::Job.enqueue Langis::Rackish::RackishJob.new(
|
29
|
+
# 'my_app',
|
30
|
+
# {
|
31
|
+
# 'input_key' => 'value'
|
32
|
+
# })
|
33
|
+
#
|
34
|
+
# For example, to queue up a RackishJob using Resque:
|
35
|
+
# Resque.enqueue(
|
36
|
+
# Langis::Rackish::RackishJob,
|
37
|
+
# 'my_app',
|
38
|
+
# {
|
39
|
+
# 'input_key' => 'value'
|
40
|
+
# })
|
41
|
+
#
|
42
|
+
# The my_app job may be registered in the worker process as follows:
|
43
|
+
# Langis::Rackish::RackishJob.register_rackish_app(
|
44
|
+
# 'my_app',
|
45
|
+
# lambda { |env|
|
46
|
+
# # Do something
|
47
|
+
# })
|
48
|
+
#
|
49
|
+
class RackishJob < Struct.new(:app_key, :env)
|
50
|
+
class << self
|
51
|
+
|
52
|
+
##
|
53
|
+
# Registers a Rackish Application under a given name so it can be
|
54
|
+
# executed by the RackishJob class via the DelayedJob or Resque
|
55
|
+
# background job libraries.
|
56
|
+
#
|
57
|
+
# For example, the following can be found in a Rails initializer.
|
58
|
+
# my_app = Rack::Builder.app do
|
59
|
+
# run MyApp
|
60
|
+
# end
|
61
|
+
# RackishJob.register 'my_app', my_app
|
62
|
+
#
|
63
|
+
# @param [String] app_key The name used to lookup which Rackish
|
64
|
+
# application to call.
|
65
|
+
# @param [#call] app The Rackish Application to call for the requested
|
66
|
+
# app_key.
|
67
|
+
# @return [#call] The Rackish Application passed in is returned back.
|
68
|
+
def register_rackish_app(app_key, app)
|
69
|
+
@apps ||= {}
|
70
|
+
@apps[app_key.to_s] = app
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Acts as the Resque starting point.
|
75
|
+
#
|
76
|
+
# For example, the following can be used to execute the 'my_app'
|
77
|
+
# Rackish application using Resque from an ActiveRecord callback:
|
78
|
+
# def after_create(record)
|
79
|
+
# Resque.enqueue RackishJob, 'my_app', { 'my.data' => record.id }
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# @param [String] app_key The registered application's name that
|
83
|
+
# is to be called with the given env input.
|
84
|
+
# @param [Hash] env The Rackish input environment. This is the input
|
85
|
+
# that should be relevant to the called app. There is no guarantee
|
86
|
+
# that this environment hash is a fully compliant Rack environment.
|
87
|
+
# @raise [RackishAppNotFoundError] Signals that the given app_key
|
88
|
+
# was not registered with RackishJob. See DelayedJob and Resque
|
89
|
+
# documentation to understand how to ignore or handle raised
|
90
|
+
# exceptions for retry.
|
91
|
+
def perform(app_key=nil, env={})
|
92
|
+
app = @apps[app_key]
|
93
|
+
if app.respond_to? :call
|
94
|
+
app.call env
|
95
|
+
else
|
96
|
+
raise NotFoundError.new "#{app_key} not found"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Acts as the DelayedJob starting point. All this does is relays the
|
103
|
+
# call to the Resque starting point.
|
104
|
+
#
|
105
|
+
# For example, the following can be used to execute the 'my_app'
|
106
|
+
# Rackish application using DelayedJob from an ActiveRecord callback:
|
107
|
+
# def after_create(record)
|
108
|
+
# Delayed::Job.enqueue(
|
109
|
+
# RackishJob.new('my_app', { 'my.data' => record.id }))
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# @see RackishJob.perform
|
113
|
+
def perform
|
114
|
+
self.class.perform(app_key.to_s, env || {})
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/langis/sinks.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
module Langis
|
2
|
+
|
3
|
+
##
|
4
|
+
# Predefined sinks, destinations for a message pumped into the Langis Engines.
|
5
|
+
module Sinks
|
6
|
+
|
7
|
+
##
|
8
|
+
# The header key whose value is the DelayedJob enqueue result.
|
9
|
+
DELAYED_JOB_RESULT_KEY = 'langis.sink.delayed_job.result'
|
10
|
+
|
11
|
+
##
|
12
|
+
# Module function that creates the endpoint Rackish app that will
|
13
|
+
# enqueue a new instance of a DelayedJob job class with instantiation
|
14
|
+
# parameters extracted from the Rackish input environment.
|
15
|
+
#
|
16
|
+
# @param [Class] job_class The DelayedJob job class to enqueue.
|
17
|
+
# @option options [String] :env_key (Langis::MESSAGE_KEY) The Rackish
|
18
|
+
# input environment key whose value is passed to the given job_class
|
19
|
+
# constructor. If the value of this key is an array, then the elements
|
20
|
+
# of that array are passed as though they were individually specified.
|
21
|
+
# @option options [Integer] :priority (0) DelayedJob priority to be used
|
22
|
+
# for all jobs enqueued with this sink.
|
23
|
+
# @option options [Time] :run_at (nil) DelayedJob run_at to be used for
|
24
|
+
# all jobs enqueued with this sink.
|
25
|
+
# @option options [Symbol] :transform (nil) method to call on the object
|
26
|
+
# passed in via :env_key if env_key responds to it. The returned Array's
|
27
|
+
# elements becomes the initializer argument(s) of the delayed job.
|
28
|
+
# If the return value is anything but an array, then that value is
|
29
|
+
# passed on; even explicit nils are passed on to the job_class#new.
|
30
|
+
# Note that DelayedJob serializes these parameters in Yaml.
|
31
|
+
# @option options [Array] :transform_args ([]) arguments to pass to
|
32
|
+
# the transform call.
|
33
|
+
# @return [Array<Integer,Hash,#each>] A simple OK return with the header
|
34
|
+
# hash that contains the delayed job enqueue result.
|
35
|
+
def delayed_job(job_class, options={})
|
36
|
+
priority = options[:priority] || 0
|
37
|
+
run_at = options[:run_at]
|
38
|
+
env_key = options[:env_key] || MESSAGE_KEY
|
39
|
+
xform = options[:transform]
|
40
|
+
xform_args = options[:transform_args] || []
|
41
|
+
xform_args = [xform_args] unless xform_args.is_a? Array
|
42
|
+
lambda { |env|
|
43
|
+
message = env[env_key]
|
44
|
+
if xform.is_a? Symbol and message.respond_to? xform
|
45
|
+
args = message.send(xform, *xform_args)
|
46
|
+
else
|
47
|
+
args = message
|
48
|
+
end
|
49
|
+
args = [args] unless args.is_a? Array
|
50
|
+
result = Delayed::Job.enqueue job_class.new(*args), priority, run_at
|
51
|
+
return [OK, { DELAYED_JOB_RESULT_KEY => result }, ['']]
|
52
|
+
}
|
53
|
+
end
|
54
|
+
module_function :delayed_job
|
55
|
+
|
56
|
+
##
|
57
|
+
# The header key whose value is the Redis rpush result.
|
58
|
+
REDIS_RESULT_KEY = 'langis.sink.redis.result'
|
59
|
+
|
60
|
+
##
|
61
|
+
# Module function that creates the endpoint Rackish app that will
|
62
|
+
# rpush an input environment's value into a list stored in a Redis
|
63
|
+
# database.
|
64
|
+
#
|
65
|
+
# @param [Object] connection The redis database connection.
|
66
|
+
# @param [String] key The index key of the list in the Redis database.
|
67
|
+
# @option options [String] :env_key (Langis::MESSAGE_KEY) The Rackish
|
68
|
+
# input environment key whose value is pushed onto the end of the
|
69
|
+
# Redis key's list.
|
70
|
+
# @option options [Symbol] :transform (nil) method to call on the object
|
71
|
+
# passed in via :env_key if env_key responds to it. The returned value
|
72
|
+
# is saved (after a #to_s by the Redis client) to the Redis database.
|
73
|
+
# @option options [Array] :transform_args ([]) arguments to pass to
|
74
|
+
# the transform call.
|
75
|
+
# @return [Array<Integer,Hash,#each>] A simple OK return with the header
|
76
|
+
# hash that contains the Redis#rpush result.
|
77
|
+
def redis(connection, key, options={})
|
78
|
+
env_key = options[:env_key] || MESSAGE_KEY
|
79
|
+
xform = options[:transform]
|
80
|
+
xform_args = options[:transform_args] || []
|
81
|
+
xform_args = [xform_args] unless xform_args.is_a? Array
|
82
|
+
lambda { |env|
|
83
|
+
message = env[env_key]
|
84
|
+
if xform.is_a? Symbol and message.respond_to? xform
|
85
|
+
message = message.send(xform, *xform_args)
|
86
|
+
end
|
87
|
+
result = connection.rpush key, message
|
88
|
+
return [OK, { REDIS_RESULT_KEY => result }, ['']]
|
89
|
+
}
|
90
|
+
end
|
91
|
+
module_function :redis
|
92
|
+
|
93
|
+
##
|
94
|
+
# The header key whose value is the Resque enqueue result.
|
95
|
+
RESQUE_RESULT_KEY = 'langis.sink.resque.result'
|
96
|
+
|
97
|
+
##
|
98
|
+
# Module function that creates the endpoint Rackish app that will
|
99
|
+
# rpush an input environment's value into a list stored in a Redis
|
100
|
+
# database.
|
101
|
+
#
|
102
|
+
# @param [Class] job_class The Resque job class for which we want
|
103
|
+
# to enqueue the message.
|
104
|
+
# @option options [String] :env_key (Langis::MESSAGE_KEY) The Rackish
|
105
|
+
# input environment key whose value is passed as the input arguments
|
106
|
+
# to the actual execution of the Resque job. The found value can be
|
107
|
+
# an Array, in which case the elements will be used as the execution
|
108
|
+
# parameters of the given job.
|
109
|
+
# @option options [Symbol] :transform (nil) method to call on the object
|
110
|
+
# passed in via :env_key if env_key responds to it. The returned Array's
|
111
|
+
# elements becomes the perform argument(s) of the Resque job.
|
112
|
+
# If the return value is anything but an array, then that value is
|
113
|
+
# passed on; even explicit nils are passed as the perform argument.
|
114
|
+
# Note that these Resque arguments are serialized via #to_json
|
115
|
+
# @option options [Array] :transform_args ([]) arguments to pass to
|
116
|
+
# the transform call.
|
117
|
+
# @return [Array<Integer,Hash,#each>] A simple OK return with the header
|
118
|
+
# hash that contains the Resque enqueue result.
|
119
|
+
def resque(job_class, options={})
|
120
|
+
env_key = options[:env_key] || MESSAGE_KEY
|
121
|
+
xform = options[:transform]
|
122
|
+
xform_args = options[:transform_args] || []
|
123
|
+
xform_args = [xform_args] unless xform_args.is_a? Array
|
124
|
+
lambda { |env|
|
125
|
+
message = env[env_key]
|
126
|
+
if xform.is_a? Symbol and message.respond_to? xform
|
127
|
+
args = message.send(xform, *xform_args)
|
128
|
+
else
|
129
|
+
args = message
|
130
|
+
end
|
131
|
+
args = [args] unless args.is_a? Array
|
132
|
+
result = Resque.enqueue job_class, *args
|
133
|
+
return [OK, { RESQUE_RESULT_KEY => result }, ['']]
|
134
|
+
}
|
135
|
+
end
|
136
|
+
module_function :resque
|
137
|
+
end
|
138
|
+
end
|