async_sinatra 0.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.
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
+