threadless 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/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
+