async_sinatra 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README.rdoc +68 -0
  2. data/Rakefile +96 -0
  3. data/examples/basic.ru +18 -0
  4. data/lib/sinatra/async.rb +86 -0
  5. metadata +102 -0
@@ -0,0 +1,68 @@
1
+ = async_sinatra (for Thin)
2
+ by James Tucker
3
+ http://ra66i.org
4
+ http://github.com/raggi/async_sinatra
5
+ http://libraggi.rubyforge.org/async_sinatra
6
+
7
+ == DESCRIPTION:
8
+
9
+ A Sinatra plugin to provide convenience whilst performing asynchronous
10
+ responses inside of the Sinatra framework running under the Thin webserver.
11
+
12
+ To properly utilise this package, some knowledge of EventMachine and/or
13
+ asynchronous patterns is recommended.
14
+
15
+ == SYNOPSIS:
16
+
17
+ A quick example:
18
+
19
+ require 'sinatra/async'
20
+
21
+ class AsyncTest < Sinatra::Base
22
+ include Sinatra::Async
23
+
24
+ aget '/' do
25
+ body "hello async"
26
+ end
27
+
28
+ aget '/delay/:n' do |n|
29
+ EM.add_timer(n.to_i) { body { "delayed for #{n} seconds" } }
30
+ end
31
+
32
+ end
33
+
34
+ See Sinatra::Async for more details.
35
+
36
+ == REQUIREMENTS:
37
+
38
+ * Sinatra (>= 0.9)
39
+ * Thin (>= 1.2)
40
+
41
+ == INSTALL:
42
+
43
+ * gem install async_sinatra
44
+
45
+ == LICENSE:
46
+
47
+ (The MIT License)
48
+
49
+ Copyright (c) 2009 James Tucker
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining
52
+ a copy of this software and associated documentation files (the
53
+ 'Software'), to deal in the Software without restriction, including
54
+ without limitation the rights to use, copy, modify, merge, publish,
55
+ distribute, sublicense, and/or sell copies of the Software, and to
56
+ permit persons to whom the Software is furnished to do so, subject to
57
+ the following conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
65
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
66
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
67
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
68
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/clean'
3
+
4
+ task :default => :spec
5
+
6
+ def spec(file = Dir['*.gemspec'].first)
7
+ @spec ||=
8
+ begin
9
+ require 'rubygems/specification'
10
+ Thread.abort_on_exception = true
11
+ data = File.read(file)
12
+ spec = nil
13
+ Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
14
+ spec.instance_variable_set(:@filename, file)
15
+ def spec.filename; @filename; end
16
+ spec
17
+ end
18
+ end
19
+
20
+ def manifest; @manifest ||= `git ls-files`.split("\n").reject{|s|s=~/\.gemspec$|\.gitignore$/}; end
21
+
22
+ require 'rake/gempackagetask'
23
+ def gem_task; @gem_task ||= Rake::GemPackageTask.new(spec); end
24
+ gem_task.define
25
+ Rake::Task[:clobber].enhance [:clobber_package]
26
+
27
+ require 'rake/testtask'
28
+ Rake::TestTask.new(:spec) do |t|
29
+ t.test_files = spec.test_files
30
+ t.ruby_opts = ['-rubygems'] if defined? Gem
31
+ t.warning = true
32
+ end unless spec.test_files.empty?
33
+
34
+ require 'rake/rdoctask'
35
+ df = begin; require 'rdoc/generator/darkfish'; true; rescue LoadError; end
36
+ rdtask = Rake::RDocTask.new do |rd|
37
+ rd.title = spec.name
38
+ rd.main = spec.extra_rdoc_files.first
39
+ lib_rexp = spec.require_paths.map { |p| Regexp.escape p }.join('|')
40
+ rd.rdoc_files.include(*manifest.grep(/^(?:#{lib_rexp})/))
41
+ rd.rdoc_files.include(*spec.extra_rdoc_files)
42
+ rd.template = 'darkfish' if df
43
+ end
44
+
45
+ Rake::Task[:clobber].enhance [:clobber_rdoc]
46
+
47
+ require 'yaml'
48
+ require 'rake/contrib/sshpublisher'
49
+ desc "Publish rdoc to rubyforge"
50
+ task :publish => rdtask.name do
51
+ rf_cfg = File.expand_path '~/.rubyforge/user-config.yml'
52
+ host = "#{YAML.load_file(rf_cfg)['username']}@rubyforge.org"
53
+ remote_dir = "/var/www/gforge-projects/#{spec.rubyforge_project}/#{spec.name}/"
54
+ Rake::SshDirPublisher.new(host, remote_dir, rdtask.rdoc_dir).upload
55
+ end
56
+
57
+ desc 'Generate and open documentation'
58
+ task :docs => :rdoc do
59
+ path = rdtask.send :rdoc_target
60
+ case RUBY_PLATFORM
61
+ when /darwin/ ; sh "open #{path}"
62
+ when /mswin|mingw/ ; sh "start #{path}"
63
+ else
64
+ sh "firefox #{path}"
65
+ end
66
+ end
67
+
68
+ desc "Regenerate gemspec"
69
+ task :gemspec => spec.filename
70
+
71
+ task spec.filename do
72
+ spec.files = manifest
73
+ spec.test_files = manifest.grep(/(?:spec|test)\/*.rb/)
74
+ open(spec.filename, 'w') { |w| w.write spec.to_ruby }
75
+ end
76
+
77
+ desc "Bump version from #{spec.version} to #{spec.version.to_s.succ}"
78
+ task :bump do
79
+ spec.version = spec.version.to_s.succ
80
+ end
81
+
82
+ desc "Tag version #{spec.version}"
83
+ task :tag do
84
+ tagged = Dir.new('.git/refs/tags').entries.include? spec.version.to_s
85
+ if tagged
86
+ warn "Tag #{spec.version} already exists"
87
+ else
88
+ # TODO release message in tag message
89
+ sh "git tag #{spec.version}"
90
+ end
91
+ end
92
+
93
+ desc "Release #{gem_task.gem_file} to rubyforge"
94
+ task :release => [:tag, :gem, :publish] do |t|
95
+ sh "rubyforge add_release #{spec.rubyforge_project} #{spec.name} #{spec.version} #{gem_task.package_dir}/#{gem_task.gem_file}"
96
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env rackup -Ilib:../lib -s thin
2
+ require 'sinatra'
3
+ require 'sinatra/async'
4
+
5
+ class AsyncTest < Sinatra::Base
6
+ include Sinatra::Async
7
+
8
+ aget '/' do
9
+ body "hello async"
10
+ end
11
+
12
+ aget '/delay/:n' do |n|
13
+ EM.add_timer(n.to_i) { body { "delayed for #{n} seconds" } }
14
+ end
15
+
16
+ end
17
+
18
+ run AsyncTest.new
@@ -0,0 +1,86 @@
1
+ require 'sinatra'
2
+
3
+ module Sinatra #:nodoc:
4
+
5
+ # See Async::ClassMethods for the DSL
6
+ #
7
+ # Normally Sinatra::Base expects that the completion of a request is
8
+ # determined by the block exiting, and returning a value for the body.
9
+ #
10
+ # In an async environment, we want to tell the webserver that we're not going
11
+ # to provide a response now, but some time in the future.
12
+ #
13
+ # The a* methods provide a method for doing this, by informing the server of
14
+ # our asynchronous intent, and then scheduling our action code (your block)
15
+ # as the next thing to be invoked by the server.
16
+ #
17
+ # This code can then do a number of things, including waiting (asynchronously)
18
+ # for external servers, services, and whatnot to complete. When ready to send
19
+ # the real response, simply setup your environment as you normally would for
20
+ # sinatra (using #content_type, #headers, etc). Finally, you complete your
21
+ # response body by calling the #body method. This will push your body into the
22
+ # response object, and call out to the server to actually send that data.
23
+ #
24
+ # == Example
25
+ # require 'sinatra/async'
26
+ #
27
+ # class AsyncTest < Sinatra::Base
28
+ # include Sinatra::Async
29
+ #
30
+ # aget '/' do
31
+ # body "hello async"
32
+ # end
33
+ #
34
+ # aget '/delay/:n' do |n|
35
+ # EM.add_timer(n.to_i) { body { "delayed for #{n} seconds" } }
36
+ # end
37
+ #
38
+ # end
39
+ module Async
40
+ module ClassMethods
41
+ # Similar to Sinatra::Base#get, but the block will be scheduled to run
42
+ # during the next tick of the EventMachine reactor. In the meantime,
43
+ # Thin will hold onto the client connection, awaiting a call to
44
+ # Async#body with the response.
45
+ def aget(path, opts={}, &block)
46
+ conditions = @conditions.dup
47
+ aroute('GET', path, opts, &block)
48
+
49
+ @conditions = conditions
50
+ aroute('HEAD', path, opts, &block)
51
+ end
52
+
53
+ # See #aget.
54
+ def aput(path, opts={}, &bk); aroute 'PUT', path, opts, &bk; end
55
+ # See #aget.
56
+ def apost(path, opts={}, &bk); aroute 'POST', path, opts, &bk; end
57
+ # See #aget.
58
+ def adelete(path, opts={}, &bk); aroute 'DELETE', path, opts, &bk; end
59
+ # See #aget.
60
+ def ahead(path, opts={}, &bk); aroute 'HEAD', path, opts, &bk; end
61
+
62
+ private
63
+ def aroute(*args, &block) #:nodoc:
64
+ self.send :route, *args do |*bargs|
65
+ mc = class << self; self; end
66
+ mc.send :define_method, :__async_callback, &block
67
+ EM.next_tick { send(:__async_callback, *bargs) }
68
+ throw :async
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.included(klass) #:nodoc:
74
+ klass.extend ClassMethods
75
+ end
76
+
77
+ # Send the given body or block as the final response to the asynchronous
78
+ # request.
79
+ def body(*args, &blk)
80
+ super
81
+ request.env['async.callback'][
82
+ [response.status, response.headers, response.body]
83
+ ] if respond_to?(:__async_callback)
84
+ end
85
+ end
86
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: async_sinatra
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - James Tucker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-24 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sinatra
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: thin
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rdoc
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.4.1
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rake
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.3
54
+ version:
55
+ description: Asynchronous response API for Sinatra and Thin
56
+ email: raggi@rubyforge.org
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README.rdoc
63
+ files:
64
+ - README.rdoc
65
+ - Rakefile
66
+ - examples/basic.ru
67
+ - lib/sinatra/async.rb
68
+ has_rdoc: true
69
+ homepage: http://libraggi.rubyforge.org/async_sinatra
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options:
74
+ - --line-numbers
75
+ - --inline-source
76
+ - --title
77
+ - Async Sinatra
78
+ - --main
79
+ - README.rdoc
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ version:
94
+ requirements: []
95
+
96
+ rubyforge_project: libraggi
97
+ rubygems_version: 1.3.1
98
+ signing_key:
99
+ specification_version: 2
100
+ summary: Asynchronous response API for Sinatra and Thin
101
+ test_files: []
102
+