threadless 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest ADDED
@@ -0,0 +1,4 @@
1
+ README.rdoc
2
+ Rakefile
3
+ lib/threadless.rb
4
+ Manifest
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ We all know that rails has a rocky history regarding threads, to say the least.
2
+ Sadly, that seems also include the Rails port of one of my favourite Merb features: [run_later](http://github.com/mattmatt/run_later).
3
+
4
+ Basically run_later takes a block, turns it into a Proc, and sends it to a worker threads for later execution. That way the request processing is not hindered in any way. Say, you want to send a "you have just signed-up" email to your users: this is a perfect solution: easy to use, lightweight (you don't need extra middle ware), and semi reliable (no fallback when your system breaks down).
5
+
6
+ However, I ran into few problems using mattmatt's solution in, at least, development mode:
7
+
8
+ - the app regularly ran into class (un)loading issues, and
9
+ - Rails' mysql adapter apparently didn't disposed of used database connections, refusing new connections
10
+
11
+ While all of the above can be explained as some of the quirks of the development environment it didn't increase the "trust level" into that solution (and I have to point out here, that markmark's code looks pretty good to me, and that these problems more likely arise from the somewhat idiosyncratic behaviour of Rails towards threads)
12
+
13
+ That made me thinking: why use threads in the first place? After all, what we need is a defined point during request processing that gives us a handle to yield of some piece of code, and which occurs after the request's response has been sent back to the client. And, yes, thanks to metal, I found one.
14
+
15
+ So here is a solution. It is not as feature complete as markmark's, tests are still missing, and it blocks the server process until the run_later block is finished. (But you have more than one server process running, haven't you?)
16
+
17
+ Someone out there wants to help move that into a regular plugin?
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('threadless', '0.1.0') do |p|
6
+ p.description = "run_later without threads"
7
+ p.url = "http://github.com/pboy/threadless"
8
+ p.author = "pboy"
9
+ p.email = "eno-pboy@open-lab.org"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.development_dependencies = []
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/lib/threadless.rb ADDED
@@ -0,0 +1,85 @@
1
+ #
2
+ # A single threaded runlater implementation
3
+ class Threadless
4
+ ENV_KEY = "rack.threadless.run_later"
5
+
6
+ module InstanceMethods
7
+ def threadless
8
+ @threadless ||= Adapter.new(request)
9
+ end
10
+ end
11
+
12
+ #
13
+ # The adapter; use it as
14
+ #
15
+ # threadless.run_later do ... end
16
+ #
17
+ # from within a controller or a view.
18
+ #
19
+ class Adapter
20
+ def initialize(request)
21
+ @request = request
22
+ end
23
+
24
+ def run_later(run_later = true)
25
+ if run_later
26
+ @request.env[ENV_KEY] ||= []
27
+ @request.env[ENV_KEY].push Proc.new
28
+ else
29
+ yield
30
+ end
31
+ end
32
+ end
33
+
34
+ #
35
+ # This replaces the metal body piece. It intercepts all messages and
36
+ # adjusts "close" in such a way that close closes the original body,
37
+ # and then executes all the procs that are passed into the c'tor.
38
+ class Executor
39
+ def method_missing(sym, *args, &block)
40
+ @body.__send__ sym, *args, &block
41
+ end
42
+
43
+ def initialize(body, procs)
44
+ @body, @procs = body, procs
45
+ end
46
+
47
+ def close
48
+ return if @closed
49
+ @closed = true
50
+
51
+ @body.close if @body.respond_to?(:close)
52
+
53
+ @procs.each do |proc|
54
+ proc.call
55
+ end
56
+ end
57
+ end
58
+
59
+ #
60
+ # The metal interface: initializing
61
+ def initialize(app)
62
+ @app = app
63
+ end
64
+
65
+ #
66
+ # The metal interface: call
67
+ def call(env)
68
+ status, headers, body = *@app.call(env)
69
+
70
+ body = Executor.new(body, env[ENV_KEY]) if env[ENV_KEY]
71
+
72
+ [ status, headers, body ]
73
+ end
74
+ end
75
+
76
+ #
77
+ # -- activate instance methods and middleware, on Rails
78
+
79
+ if defined?(ActionController)
80
+ ActionController::Base.send(:include, Threadless::InstanceMethods)
81
+ ActionController::Base.send(:helper, Threadless::InstanceMethods)
82
+
83
+ middleware = ActionController::MiddlewareStack::Middleware.new(self)
84
+ ActionController::Dispatcher.middleware.push middleware
85
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{threadless}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["pboy"]
9
+ s.date = %q{2009-12-12}
10
+ s.description = %q{run_later without threads}
11
+ s.email = %q{eno-pboy@open-lab.org}
12
+ s.extra_rdoc_files = ["README.rdoc", "lib/threadless.rb"]
13
+ s.files = ["README.rdoc", "Rakefile", "lib/threadless.rb", "Manifest", "threadless.gemspec"]
14
+ s.homepage = %q{http://github.com/pboy/threadless}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Threadless", "--main", "README.mdown"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{threadless}
18
+ s.rubygems_version = %q{1.3.5}
19
+ s.summary = %q{run_later without threads}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: threadless
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - pboy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-12 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: run_later without threads
17
+ email: eno-pboy@open-lab.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - lib/threadless.rb
25
+ files:
26
+ - README.rdoc
27
+ - Rakefile
28
+ - lib/threadless.rb
29
+ - Manifest
30
+ - threadless.gemspec
31
+ has_rdoc: true
32
+ homepage: http://github.com/pboy/threadless
33
+ licenses: []
34
+
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --line-numbers
38
+ - --inline-source
39
+ - --title
40
+ - Threadless
41
+ - --main
42
+ - README.mdown
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "1.2"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: threadless
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: run_later without threads
64
+ test_files: []
65
+