langis 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|