asir_resque 1.1.3

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.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ example/*.out
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color -f d
@@ -0,0 +1,4 @@
1
+ 2012-12-04 Kurt A. Stephens <ks.github@kurtstephens.com>
2
+
3
+ * v1.1.3: New version.
4
+ * Resque: Moved Resque support to gem asir_resque.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in asir_resque.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Kurt Stephens
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # asir_resque
2
+
3
+ ASIR Transport for Resque
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'asir_resque'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install asir_resque
18
+
19
+ ## Usage
20
+
21
+ See examples/
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,34 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ gem 'rspec'
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc "Run specs"
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
9
+ # Put spec opts in a file named .rspec in root
10
+ end
11
+
12
+ desc "Generate code coverage"
13
+ RSpec::Core::RakeTask.new(:coverage) do |t|
14
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
15
+ t.rcov = true
16
+ t.rcov_opts = ['--exclude', 'spec']
17
+ end
18
+
19
+ ######################################################################
20
+
21
+ desc "Default => :test"
22
+ task :default => :test
23
+
24
+ desc "Run all tests"
25
+ task :test => [ :spec ]
26
+
27
+ desc "Run examples."
28
+ task :example do
29
+ ENV["ASIR_EXAMPLE_SILENT"]="1"
30
+ Dir["example/ex[0-9]*.rb"].each do | rb |
31
+ sh %Q{ruby -I example -I lib #{rb}}
32
+ end
33
+ ENV.delete("ASIR_EXAMPLE_SILENT")
34
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'asir_resque/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+
8
+ gem.name = "asir_resque"
9
+ gem.version = AsirResque::VERSION
10
+ gem.authors = ["Kurt Stephens"]
11
+ gem.email = ["ks.github@kurtstephens.com"]
12
+ gem.description = %q{Resque transport for ASIR}
13
+ gem.summary = %q{Resque transport for ASIR}
14
+ gem.homepage = "http://github.com/kstephens/abstracting_services_in_ruby"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency "asir", "~> 1.1.0"
22
+ gem.add_dependency "redis", "~> 3.0.2"
23
+ gem.add_dependency "resque", "~> 1.23.0"
24
+
25
+ gem.add_development_dependency 'rake', '>= 0.9.0'
26
+ gem.add_development_dependency 'rspec', '~> 2.12.0'
27
+ gem.add_development_dependency 'simplecov', '>= 0.1'
28
+ end
@@ -0,0 +1,40 @@
1
+ #!/bin/sh
2
+ set -x
3
+ dir="$(cd "$(dirname $0)" && /bin/pwd)"
4
+ PATH="$dir/../bin:$PATH"
5
+ export RUBYLIB="$dir/../example:$dir/../lib"
6
+ asir="asir verbose=9 config_rb=$dir/config/asir_config.rb"
7
+ args="$*"
8
+ args="${args:-ALL}"
9
+ # set -e
10
+
11
+ #############################
12
+
13
+ case "$args"
14
+ in
15
+ *resque*|*ALL*)
16
+
17
+ $asir start resque conduit
18
+ sleep 1
19
+ if $asir alive resque conduit; then
20
+ echo "resque conduit alive"
21
+ fi
22
+ $asir start resque worker
23
+ sleep 1
24
+ $asir pid resque worker
25
+ if $asir alive resque worker; then
26
+ echo "resque worker alive"
27
+ fi
28
+
29
+ ruby "$dir/asir_control_client_resque.rb"
30
+ sleep 1
31
+ $asir stop resque worker
32
+ sleep 1
33
+ $asir stop resque conduit
34
+
35
+ ;;
36
+ esac
37
+
38
+ #############################
39
+
40
+ exit 0
@@ -0,0 +1,16 @@
1
+ require 'example_helper'
2
+ gem 'resque'
3
+ require 'asir/transport/resque'
4
+ require 'asir/coder/marshal'
5
+ begin
6
+ Email.asir.transport = t =
7
+ ASIR::Transport::Resque.new
8
+ t.one_way = true
9
+ t.encoder = ASIR::Coder::Marshal.new
10
+ pr Email.asir.send_email(:pdf_invoice,
11
+ :to => "user@email.com",
12
+ :customer => @customer)
13
+ ensure
14
+ t.close rescue nil
15
+ end
16
+
@@ -0,0 +1,59 @@
1
+ # Used by asir/bin/asir.
2
+ # Configures asir worker transport and error logging.
3
+ # asir object is bound to ASIR::Environment instance.
4
+
5
+ $stderr.puts "asir.phase = #{asir.phase.inspect}" if asir.verbose >= 1
6
+ case asir.phase
7
+ when :configure
8
+ # NOTHING
9
+ true
10
+ when :environment
11
+ require 'rubygems'
12
+
13
+ require 'asir'
14
+ require 'asir/transport/file'
15
+ require 'asir/coder/marshal'
16
+ require 'asir/coder/yaml'
17
+
18
+ $:.unshift File.expand_path('..')
19
+ require 'example_helper'
20
+ require 'sample_service'
21
+ require 'unsafe_service'
22
+ when :start
23
+ # NOTHING
24
+ true
25
+ when :transport
26
+ # Compose with Marshal for final coding.
27
+ coder = ASIR::Coder::Marshal.new
28
+
29
+ # Logger for worker-side Exceptions.
30
+ error_log_file = asir.log_file.sub(/\.log$/, '-error.log')
31
+ error_transport =
32
+ ASIR::Transport::File.new(:file => error_log_file,
33
+ :mode => 'a+',
34
+ :perms => 0666)
35
+ error_transport.encoder = ASIR::Coder::Yaml.new
36
+
37
+ # Setup requested Transport.
38
+ case asir.adjective
39
+ when :resque
40
+ gem 'resque'
41
+ require 'asir/transport/resque'
42
+ transport = ASIR::Transport::Resque.new
43
+ else
44
+ raise "Cannot configure Transport for #{asir.adjective}"
45
+ end
46
+
47
+ transport.encoder = coder
48
+ transport._logger = STDERR
49
+ transport._log_enabled = true
50
+ # transport.verbose = 3
51
+ transport.on_exception =
52
+ lambda { | transport, exc, phase, message, result |
53
+ error_transport.send_request(message)
54
+ }
55
+
56
+ transport
57
+ else
58
+ $stderr.puts "Warning: unhandled asir.phase: #{asir.phase.inspect}"
59
+ end
@@ -0,0 +1,41 @@
1
+ # !SLIDE :capture_code_output true
2
+ # One-way Resque service.
3
+
4
+ require 'rubygems'
5
+ gem 'resque'
6
+ require 'example_helper'
7
+ require 'asir/transport/resque'
8
+ begin
9
+ t = ASIR::Transport::Resque.new(:port => 31925,
10
+ :encoder => ASIR::Coder::Marshal.new)
11
+ # Control throttling of Resque::Worker inside ASIR::Transport::Resque
12
+ t.throttle = {
13
+ # :verbose => true,
14
+ :min_sleep => 0.0,
15
+ :max_sleep => 2.0,
16
+ :inc_sleep => 0.1,
17
+ :mul_sleep => 1.5,
18
+ :rand_sleep => 0.1,
19
+ }
20
+ t.start_conduit!; sleep 1
21
+ server_process do
22
+ t.prepare_server!
23
+ t.run_server!
24
+ end
25
+ UnsafeService.asir.transport = t
26
+ pr UnsafeService.asir.do_it(":ok")
27
+ rescue ::Exception => err
28
+ $stderr.puts "### #{$$}: ERROR: #{err.inspect}\n #{err.backtrace * "\n "}"
29
+ raise
30
+ ensure
31
+ sleep 5
32
+ t.close rescue nil; sleep 1; server_kill
33
+ t.stop_conduit!
34
+ end
35
+
36
+ # !SLIDE END
37
+ # EXPECT: : client process
38
+ # EXPECT: : server process
39
+ # EXPECT: UnsafeService.do_it => :ok
40
+ # EXPECT: : pr: nil
41
+ # EXPECT!: ERROR
@@ -0,0 +1,77 @@
1
+ # Sample client support
2
+ #
3
+ require 'rubygems'
4
+ case RUBY_PLATFORM
5
+ when /java/i
6
+ gem 'spoon'; require 'spoon'
7
+ end
8
+
9
+ $: << File.expand_path("../../lib", __FILE__)
10
+ gem 'asir'
11
+
12
+ require 'asir'
13
+ require 'asir/coder/chain'
14
+ require 'asir/coder/marshal'
15
+ ASIR::Log.enabled = true unless ENV['ASIR_EXAMPLE_SILENT']
16
+ require 'unsafe_service'
17
+
18
+ require 'pp'
19
+
20
+ @customer = 123
21
+
22
+ class ::Object
23
+
24
+ def pr result
25
+ $stdout.puts "*** #{$$}: pr: #{PP.pp(result, '')}"
26
+ end
27
+
28
+ def server_process &blk
29
+ # $stderr.puts " at #{__FILE__}:#{__LINE__}"
30
+ case RUBY_PLATFORM
31
+ when /java/i
32
+ # JRuby cannot fork.
33
+ # So we must prevent spawn a new jruby and
34
+ # instruct it to only run the server blk, and not
35
+ # the subsequent client code.
36
+ # In other words, we cannot rely on how Process.fork
37
+ # terminates within the block.
38
+ if ENV['ASIR_JRUBY_SPAWNED']
39
+ $stderr.puts " spawned server at #{__FILE__}:#{__LINE__}"
40
+ puts "*** #{$$}: server process"; $stdout.flush
41
+ yield
42
+ Process.exit!(0)
43
+ # dont do client, client is our parent process.
44
+ else
45
+ $stderr.puts " spawning at #{__FILE__}:#{__LINE__}"
46
+ ENV['ASIR_JRUBY_SPAWNED'] = "1"
47
+ cmd = "ruby -I #{File.dirname(__FILE__)} -I #{File.expand_path('../../lib', __FILE__)} #{$0} #{ARGV * ' '}"
48
+ $stderr.puts " cmd = #{cmd}"
49
+ $server_pid = Spoon.spawnp(cmd)
50
+ ENV.delete('ASIR_JRUBY_SPAWNED')
51
+ $stderr.puts " spawned #{$server_pid} at #{__FILE__}:#{__LINE__}"
52
+ end
53
+ else
54
+ # $stderr.puts " at #{__FILE__}:#{__LINE__}"
55
+ $server_pid = Process.fork do
56
+ puts "*** #{$$}: server process"; $stdout.flush
57
+ yield
58
+ end
59
+ end
60
+ sleep 1 # wait for server to be ready.
61
+ return false # do client.
62
+ end
63
+
64
+ def server_kill
65
+ if $server_pid
66
+ Process.kill 9, $server_pid
67
+ Process.waitpid($server_pid)
68
+ end
69
+ rescue Errno::ESRCH
70
+ ensure
71
+ $server_pid = nil
72
+ end
73
+
74
+ end
75
+
76
+ puts "*** #{$$}: client process"; $stdout.flush
77
+
@@ -0,0 +1,12 @@
1
+ require 'asir'
2
+
3
+ module UnsafeService
4
+ include ASIR::Client
5
+ def self.do_it(expr)
6
+ result = eval(expr)
7
+ puts "#{$$}: UnsafeService.do_it(#{expr}) #{result.inspect}"
8
+ $stderr.puts "#{$$}: UnsafeService.do_it => #{result.inspect}"
9
+ result
10
+ end
11
+ end
12
+
@@ -0,0 +1,246 @@
1
+ require 'asir/transport/connection_oriented'
2
+ require 'resque'
3
+ require 'asir/poll_throttle'
4
+
5
+ module ASIR
6
+ class Transport
7
+ # !SLIDE
8
+ # Resque Transport
9
+ class Resque < ConnectionOriented
10
+ include PollThrottle
11
+
12
+ attr_accessor :queues, :queue, :namespace, :throttle
13
+
14
+ def initialize *args
15
+ @port_default = 6379
16
+ @scheme_default = 'redis'.freeze
17
+ super
18
+ self.one_way = true
19
+ end
20
+
21
+ # !SLIDE
22
+ # Resque client.
23
+ def _client_connect!
24
+ # $stderr.puts " #{$$} #{self} _client_connect!"
25
+ resque_connect!
26
+ rescue ::Exception => exc
27
+ raise exc.class, "#{self.class} #{uri}: #{exc.message}", exc.backtrace
28
+ end
29
+
30
+ # !SLIDE
31
+ # Resque server (worker).
32
+ def _server!
33
+ # $stderr.puts " #{$$} #{self} _server!"
34
+ resque_connect!
35
+ resque_worker
36
+ rescue ::Exception => exc
37
+ raise exc.class, "#{self.class} #{uri}: #{exc.message}", exc.backtrace
38
+ end
39
+
40
+ def _receive_result message, opaque_result
41
+ return nil if one_way || message.one_way
42
+ super
43
+ end
44
+
45
+ def _send_result message, result, result_payload, stream, message_state
46
+ return nil if one_way || message.one_way
47
+ super
48
+ end
49
+
50
+ def _send_message message, message_payload
51
+ stream.with_stream! do | io | # Force connect
52
+ queue = message[:resque_queue] || message[:queue] || self.queue
53
+ $stderr.puts " #{$$} #{self} _send_message #{message_payload.inspect} to queue=#{queue.inspect} as #{self.class} :process_job" if @verbose >= 2
54
+ # Invokes Transport::Resque.perform(metadata, payload)
55
+ metadata = message[:resque_metadata] || message.description
56
+ ::Resque.enqueue_to(queue, self.class, metadata, message_payload)
57
+ end
58
+ end
59
+
60
+ def queues
61
+ @queues ||=
62
+ (
63
+ x = nil
64
+ x = path if @uri
65
+ x ||= ""
66
+ root, x = x.split('/')
67
+ x ||= ""
68
+ x = x.split(/(\s+|\s*,\s*)/)
69
+ x.each(&:freeze)
70
+ x.freeze
71
+ )
72
+ end
73
+
74
+ # Defaults to [ 'asir' ].
75
+ def queues_
76
+ @queues_ ||=
77
+ queues.empty? ? [ DEFAULT_QUEUE ] : queues.freeze
78
+ end
79
+
80
+ # Defaults to 'asir'.
81
+ def queue
82
+ @queue ||= queues_.first || DEFAULT_QUEUE
83
+ end
84
+
85
+ # Defaults to 'asir'.
86
+ def namespace_
87
+ @namespace_ ||= namespace || DEFAULT_QUEUE
88
+ end
89
+
90
+ DEFAULT_QUEUE = 'asir'.freeze
91
+
92
+ def _server_accept_connection! server
93
+ [ server, server ]
94
+ end
95
+
96
+ # Resque is message-oriented, process only one message per "connection".
97
+ def stream_eof? stream
98
+ false
99
+ end
100
+
101
+ # Nothing to be closed for Resque.
102
+ def _server_close_connection! in_stream, out_stream
103
+ # NOTHING
104
+ end
105
+
106
+ def serve_stream_message! in_stream, out_stream # ignored
107
+ save = ::Thread.current[:asir_transport_resque_instance]
108
+ ::Thread.current[:asir_transport_resque_instance] = self
109
+ poll_throttle throttle do
110
+ $stderr.puts " #{$$} #{self} serve_stream_message!: resque_worker = #{resque_worker} on queues #{resque_worker.queues.inspect}" if @verbose >= 3
111
+ if job = resque_worker.reserve
112
+ $stderr.puts " #{$$} #{self} serve_stream_message! job=#{job.class}:#{job.inspect}" if @verbose >= 2
113
+ resque_worker.process(job)
114
+ end
115
+ job
116
+ end
117
+ self
118
+ ensure
119
+ ::Thread.current[:asir_transport_resque_instance] = save
120
+ end
121
+
122
+ # Class method entry point from Resque::Job.perform.
123
+ def self.perform metadata, payload = nil
124
+ payload ||= metadata # old calling signature (just payload).
125
+ # $stderr.puts " #{self} process_job payload=#{payload.inspect}"
126
+ t = ::Thread.current[:asir_transport_resque_instance]
127
+ # Pass payload as in_stream; _receive_message will return it.
128
+ t.serve_message! payload, nil
129
+ end
130
+
131
+ def _receive_message payload, additional_data # is actual payload
132
+ # $stderr.puts " #{$$} #{self} _receive_message payload=#{payload.inspect}"
133
+ [ payload, nil ]
134
+ end
135
+
136
+ ####################################
137
+
138
+ def resque_uri
139
+ @resque_uri ||=
140
+ (
141
+ unless scheme == 'redis'
142
+ raise ArgumentError, "Invalid resque URI: #{uri.inspect}"
143
+ end
144
+ _uri
145
+ )
146
+ end
147
+
148
+ def resque_connect!
149
+ @redis_config = {
150
+ :host => host,
151
+ :port => port,
152
+ :thread_safe => true,
153
+ }
154
+ @redis =
155
+ ::Redis.new(@redis_config)
156
+ if namespace_
157
+ ::Resque.redis =
158
+ @redis =
159
+ ::Redis::Namespace.new(namespace_, :redis => @redis)
160
+ ::Resque.redis.namespace = namespace_
161
+ else
162
+ ::Resque.redis = @redis
163
+ end
164
+ # $stderr.puts " *** #{$$} #{self} resque_connect! #{@redis.inspect}"
165
+ @redis
166
+ end
167
+
168
+ def resque_disconnect!
169
+ ::Resque.redis = nil
170
+ end
171
+
172
+ def resque_worker
173
+ @resque_worker ||= ::Resque::Worker.new(*queues_)
174
+ end
175
+
176
+ def server_on_start!
177
+ # prune_dead_workers expects processes to have "resque " in the name.
178
+ @save_progname ||= $0.dup
179
+ $0 = "resque #{$0}"
180
+ if worker = resque_worker
181
+ worker.prune_dead_workers
182
+ worker.register_worker
183
+ end
184
+ self
185
+ end
186
+
187
+ def server_on_stop!
188
+ $0 = @save_progname if @save_progname
189
+ if worker = @resque_worker
190
+ worker.unregister_worker
191
+ end
192
+ self
193
+ rescue Redis::CannotConnectError
194
+ # This error is not actionable since server
195
+ # is stopping.
196
+ nil
197
+ end
198
+
199
+ #########################################
200
+
201
+ @@redis_server_version = nil
202
+ def redis_server_version
203
+ @@redis_server_version ||=
204
+ begin
205
+ case v = `redis-server --version`
206
+ when /v=([.0-9]+)/ # 3.x
207
+ v = $1
208
+ when / version ([.0-9]+)/ # 2.x
209
+ v = $1
210
+ else
211
+ v = 'UNKNOWN'
212
+ end
213
+ v
214
+ end
215
+ end
216
+
217
+ def _start_conduit!
218
+ @redis_dir ||= "/tmp"
219
+ @redis_conf ||= "#{@redis_dir}/asir-redis-#{port}.conf"
220
+ @redis_log ||= "#{@redis_dir}/asir-redis-#{port}.log"
221
+ @redis_cmd = [ 'redis-server' ]
222
+ case redis_server_version
223
+ when /^2\.4/
224
+ ::File.open(@redis_conf, "w+") do | out |
225
+ out.puts "daemonize no"
226
+ out.puts "port #{port}"
227
+ out.puts "loglevel warning"
228
+ out.puts "logfile #{@redis_log}"
229
+ end
230
+ @redis_cmd << @redis_conf
231
+ else
232
+ @redis_cmd <<
233
+ '--port' << port <<
234
+ '--loglevel' << 'warning' <<
235
+ '--logfile' << @redis_log
236
+ end
237
+ @redis_cmd.map! { | x | x.to_s }
238
+ # $stderr.puts " redis_cmd = #{@redis_cmd * ' '}" if @verbose >= 1
239
+ exec *@redis_cmd
240
+ end
241
+ end
242
+ # !SLIDE END
243
+ end # class
244
+ end # module
245
+
246
+
@@ -0,0 +1,8 @@
1
+ require "asir_resque/version"
2
+
3
+ module AsirResque
4
+ end
5
+
6
+ gem 'asir'
7
+ require 'asir'
8
+ require 'asir/transport/resque'
@@ -0,0 +1,3 @@
1
+ module AsirResque
2
+ VERSION = "1.1.3"
3
+ end
@@ -0,0 +1,88 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ $:.unshift File.expand_path('../../example', __FILE__)
4
+
5
+ describe "ASIR Example" do
6
+ attr_accessor :file, :expects
7
+
8
+ before(:each) do
9
+ @expects = [ ]
10
+ end
11
+
12
+ after(:each) do
13
+ @file.should_not == nil
14
+ File.open(@file) do | fh |
15
+ until fh.eof?
16
+ line = fh.readline
17
+ line.chomp!
18
+ case
19
+ when line.sub!(/^\s*#\s*EXPECT\/:\s*/, '')
20
+ expect Regexp.new(line)
21
+ when line.sub!(/^\s*#\s*EXPECT!\/:\s*/, '')
22
+ expect Regexp.new(line), :'!~'
23
+ when line.sub!(/^\s*#\s*EXPECT:\s*/, '')
24
+ expect Regexp.new(Regexp.escape(line))
25
+ when line.sub!(/^\s*#\s*EXPECT!:\s*/, '')
26
+ expect Regexp.new(Regexp.escape(line)), :'!~'
27
+ end
28
+ end
29
+ end
30
+ @output, @exit_code = run_file!(@file)
31
+ @exit_code.should == 0
32
+ @expects.empty?.should_not == true
33
+ @expects.each do | rx, mode |
34
+ $stderr.puts " Checking #{mode} #{rx.inspect}" if ENV['SPEC_VERBOSE']
35
+ case mode
36
+ when :'=~'
37
+ @output.should =~ rx
38
+ when :'!~'
39
+ @output.should_not =~ rx
40
+ else
41
+ raise ArgumentError
42
+ end
43
+ end
44
+ end
45
+
46
+ def run_file! file, output = StringIO.new('')
47
+ progname_save, stdout_save, stderr_save = $0, $stdout, $stderr
48
+ exc = system_exit = nil; exit_code = 0
49
+ begin
50
+ if true
51
+ cmd = "ASIR_EXAMPLE_SILENT=1 ruby -I example -I lib #{file}"
52
+ $stderr.puts "\n Running #{cmd}:" if ENV['SPEC_VERBOSE']
53
+ output = `#{cmd} 2>&1 | tee #{file}.out`
54
+ else
55
+ $stderr.puts "\n Loading #{file}:" if ENV['SPEC_VERBOSE']
56
+ $stdout.puts "*** #{$$}: client process"; $stdout.flush
57
+ $stdout = $stderr = output
58
+ $0 = file
59
+ Kernel.load(file, true)
60
+ output = output.string if StringIO === output
61
+ end
62
+ rescue ::SystemExit => system_exit
63
+ exit_code = 1 # ???
64
+ rescue ::Exception => exc
65
+ exit_code = -1
66
+ end
67
+ [ output, exit_code ]
68
+ ensure
69
+ $0, $stdout, $stderr = progname_save, stdout_save, stderr_save
70
+ $stderr.write output if ENV['SPEC_VERBOSE']
71
+ if exc
72
+ stderr_save.puts "ERROR: #{file}: #{exc.inspect}\n#{exc.backtrace * "\n"}"
73
+ raise exc
74
+ end
75
+ end
76
+
77
+ def expect rx, mode = :'=~'
78
+ @expects << [ rx, mode ]
79
+ end
80
+
81
+ Dir['example/**/ex[0-9]*.rb'].sort.each do | file |
82
+ title = File.open(file) { | fh | fh.read(4096) }
83
+ title = title =~ /#\s+!SLIDE[^\n]*\n\s*#\s*([^\n]+)/ && $1
84
+ it "#{file} - #{title}" do
85
+ @file = file
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,210 @@
1
+ require 'rubygems'
2
+ gem 'redis'
3
+ gem 'resque'
4
+ require 'asir'
5
+ require 'asir/transport/resque'
6
+ require 'asir/coder/marshal'
7
+ require 'timeout'
8
+
9
+ describe "ASIR::Transport::Resque" do
10
+ it "should be able to start/stop redis" do
11
+ with_cleanup! do
12
+ create_transport!
13
+ pid = Process.fork do
14
+ # exec "false" # simulate failure to start.
15
+ transport._start_conduit!
16
+ end
17
+ sleep 1
18
+ Process.kill('TERM', pid)
19
+ sleep 1
20
+ wpid, status = nil, nil
21
+ Timeout.timeout(5) do
22
+ wpid, status = Process.waitpid2(pid, Process::WUNTRACED)
23
+ end
24
+ # puts status.inspect
25
+ wpid.should == pid
26
+ status.stopsig.should == nil
27
+ status.termsig.should == nil
28
+ status.exited?.should == true
29
+ status.exitstatus.should == 0
30
+ status.success?.should == true
31
+ end
32
+ end
33
+
34
+ it "should process and stop! gracefully" do
35
+ with_cleanup! do
36
+ create_transport!
37
+ start_conduit!; sleep 1
38
+ start_client!
39
+
40
+ message_count = 0
41
+ transport.after_receive_message = lambda do | t, message |
42
+ message_count += 1
43
+ $stderr.write ">#{message_count}" if verbose
44
+ if message_count >= 5
45
+ t.stop!
46
+ end
47
+ end
48
+ Timeout.timeout(20) do
49
+ start_server!
50
+ end
51
+
52
+ message_count.should == 5
53
+ exceptions.should == [ ]
54
+ end
55
+ end
56
+
57
+ it "should bubble up Redis::CannotConnectError if redis is not running" do
58
+ with_cleanup! do
59
+ create_transport!
60
+ message_count = 0
61
+ lambda do
62
+ transport.after_receive_message = lambda do | t, message |
63
+ message_count += 1
64
+ $stderr.write ">#{message_count}" if verbose
65
+ if message_count >= 10
66
+ t.stop!
67
+ end
68
+ end
69
+ Timeout.timeout(20) do
70
+ start_server!
71
+ end
72
+ end.should raise_error(Redis::CannotConnectError)
73
+ message_count.should == 0
74
+ exceptions.should == [ ]
75
+ end
76
+ end
77
+
78
+ it "should bubble up dropped connection error" do
79
+ with_cleanup! do
80
+ create_transport!
81
+ start_conduit!; sleep 1
82
+ start_client!
83
+
84
+ # Server should have errors.
85
+ message_count = 0
86
+ lambda do
87
+ transport.after_receive_message = lambda do | t, message |
88
+ message_count += 1
89
+ $stderr.write ">#{message_count}" if verbose
90
+ if message_count >= 5
91
+ stop_client!
92
+ stop_conduit! :signal => 9
93
+ end
94
+ if message_count >= 10
95
+ t.stop!
96
+ end
97
+ end
98
+ Timeout.timeout(20) do
99
+ start_server!
100
+ end
101
+ raise "start_server! exited"
102
+ end.should raise_error(Redis::CannotConnectError)
103
+ message_count.should == 5
104
+ exceptions.should == [ ]
105
+ end
106
+ end
107
+
108
+ attr_accessor :transport, :target, :exceptions, :verbose
109
+
110
+ before :each do
111
+ @target = ASIR::Test::ResqueTarget.new
112
+ @exceptions = [ ]
113
+ @verbose = (ENV['ASIR_TEST_VERBOSE'] || 0).to_i > 0
114
+ end
115
+
116
+ def with_cleanup!
117
+ yield
118
+ ensure
119
+ stop_client!
120
+ stop_server!
121
+ stop_conduit!
122
+ end
123
+
124
+ def create_transport!
125
+ @uri = "redis://localhost:23456"
126
+ @transport = ASIR::Transport::Resque.new(:uri => @uri)
127
+ transport.encoder = ASIR::Coder::Marshal.new
128
+ if verbose
129
+ transport._logger = $stderr
130
+ transport._log_enabled = true
131
+ end
132
+ ASIR::Client::Proxy.config_callbacks[target.class] = lambda do | proxy |
133
+ proxy.transport = transport
134
+ end
135
+ end
136
+
137
+ def start_conduit!
138
+ transport.verbose = 0
139
+ transport.start_conduit!
140
+ transport.verbose = 0
141
+ end
142
+
143
+ def stop_conduit! opts = nil
144
+ transport.verbose = 0
145
+ transport.stop_conduit! opts
146
+ transport.verbose = 0
147
+ end
148
+
149
+ def start_client! &blk
150
+ @client_pid = Process.fork do
151
+ # transport.verbose = 3
152
+ i = 0
153
+ loop do
154
+ i += 1
155
+ $stderr.write "<#{i}" if verbose
156
+ target.asir.eval! "2 + 2"
157
+ sleep 0.25
158
+ end
159
+ end
160
+ end
161
+
162
+ def stop_client!
163
+ if @client_pid
164
+ Process.kill 9, @client_pid
165
+ end
166
+ ensure
167
+ @client_pid = nil
168
+ end
169
+
170
+ def start_server!
171
+ transport.verbose = 0
172
+ transport.prepare_server!
173
+ transport.on_exception = lambda do | t, exc, kind, message, result |
174
+ @exceptions << exc
175
+ $stderr.puts " on_exception: #{kind.inspect}: #{exc.inspect}\n #{exc.backtrace * "\n "}"
176
+ end
177
+ transport.throttle = {
178
+ :min_sleep => 0.01,
179
+ :max_sleep => 2,
180
+ :inc_sleep => 0.1,
181
+ :mul_sleep => 1.25,
182
+ # :verbose => true,
183
+ }
184
+ transport.run_server!
185
+ transport.verbose = 0
186
+ end
187
+
188
+ def stop_server!
189
+ transport.stop!
190
+ end
191
+ end
192
+
193
+ module ASIR
194
+ module Test
195
+ class TestError < ::Exception; end
196
+ class ResqueTarget
197
+ include ASIR::Client
198
+ def raise_error! msg
199
+ raise TestError, msg
200
+ end
201
+ def eval! expr
202
+ result = eval expr
203
+ # $stderr.puts " #{self} eval!(#{expr.inspect}) => #{result.inspect}"
204
+ result
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ gem 'asir'
5
+ require 'asir'
6
+
7
+ module ASIR
8
+ module Test
9
+ class TestError < ::Exception; end
10
+ class TestObject
11
+ include ASIR::Client
12
+ attr_accessor :spec, :arg, :cls, :msg, :transport, :message
13
+ def initialize spec
14
+ @spec = spec
15
+ end
16
+
17
+ def get_data!
18
+ @transport = ASIR::Transport.current
19
+ @message = @transport && @transport.message
20
+ end
21
+
22
+ def return_argument arg
23
+ get_data!
24
+ @arg = arg
25
+ arg
26
+ end
27
+
28
+ def raise_exception! cls, msg
29
+ get_data!
30
+ @cls = cls
31
+ @msg = msg
32
+ raise cls, msg
33
+ end
34
+ end
35
+ end
36
+ end
37
+
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asir_resque
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kurt Stephens
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: asir
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: redis
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.0.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: resque
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.23.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.23.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.9.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.9.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 2.12.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 2.12.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: simplecov
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0.1'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0.1'
110
+ description: Resque transport for ASIR
111
+ email:
112
+ - ks.github@kurtstephens.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .rspec
119
+ - ChangeLog
120
+ - Gemfile
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - asir_resque.gemspec
125
+ - example/asir_control.sh
126
+ - example/asir_control_client_resque.rb
127
+ - example/config/asir_config.rb
128
+ - example/ex01.rb
129
+ - example/example_helper.rb
130
+ - example/unsafe_service.rb
131
+ - lib/asir/transport/resque.rb
132
+ - lib/asir_resque.rb
133
+ - lib/asir_resque/version.rb
134
+ - spec/example_spec.rb
135
+ - spec/resque_spec.rb
136
+ - spec/spec_helper.rb
137
+ homepage: http://github.com/kstephens/abstracting_services_in_ruby
138
+ licenses: []
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ segments:
150
+ - 0
151
+ hash: 2014414063490366303
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ segments:
159
+ - 0
160
+ hash: 2014414063490366303
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 1.8.24
164
+ signing_key:
165
+ specification_version: 3
166
+ summary: Resque transport for ASIR
167
+ test_files:
168
+ - spec/example_spec.rb
169
+ - spec/resque_spec.rb
170
+ - spec/spec_helper.rb