raggi-async_sinatra 0.1.4

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 +17 -0
  4. data/lib/sinatra/async.rb +84 -0
  5. metadata +100 -0
data/README.rdoc ADDED
@@ -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
+ register 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.
data/Rakefile ADDED
@@ -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
data/examples/basic.ru ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rackup -Ilib:../lib -s thin
2
+ require 'sinatra/async'
3
+
4
+ class AsyncTest < Sinatra::Base
5
+ register Sinatra::Async
6
+
7
+ aget '/' do
8
+ body "hello async"
9
+ end
10
+
11
+ aget '/delay/:n' do |n|
12
+ EM.add_timer(n.to_i) { body { "delayed for #{n} seconds" } }
13
+ end
14
+
15
+ end
16
+
17
+ run AsyncTest.new
@@ -0,0 +1,84 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra #:nodoc:
4
+
5
+ # Normally Sinatra expects that the completion of a request is # determined
6
+ # by the block exiting, and returning a value for the body.
7
+ #
8
+ # In an async environment, we want to tell the webserver that we're not going
9
+ # to provide a response now, but some time in the future.
10
+ #
11
+ # The a* methods provide a method for doing this, by informing the server of
12
+ # our asynchronous intent, and then scheduling our action code (your block)
13
+ # as the next thing to be invoked by the server.
14
+ #
15
+ # This code can then do a number of things, including waiting (asynchronously)
16
+ # for external servers, services, and whatnot to complete. When ready to send
17
+ # the real response, simply setup your environment as you normally would for
18
+ # sinatra (using #content_type, #headers, etc). Finally, you complete your
19
+ # response body by calling the #body method. This will push your body into the
20
+ # response object, and call out to the server to actually send that data.
21
+ #
22
+ # == Example
23
+ # require 'sinatra/async'
24
+ #
25
+ # class AsyncTest < Sinatra::Base
26
+ # register Sinatra::Async
27
+ #
28
+ # aget '/' do
29
+ # body "hello async"
30
+ # end
31
+ #
32
+ # aget '/delay/:n' do |n|
33
+ # EM.add_timer(n.to_i) { body { "delayed for #{n} seconds" } }
34
+ # end
35
+ #
36
+ # end
37
+ module Async
38
+ # Similar to Sinatra::Base#get, but the block will be scheduled to run
39
+ # during the next tick of the EventMachine reactor. In the meantime,
40
+ # Thin will hold onto the client connection, awaiting a call to
41
+ # Async#body with the response.
42
+ def aget(path, opts={}, &block)
43
+ conditions = @conditions.dup
44
+ aroute('GET', path, opts, &block)
45
+
46
+ @conditions = conditions
47
+ aroute('HEAD', path, opts, &block)
48
+ end
49
+
50
+ # See #aget.
51
+ def aput(path, opts={}, &bk); aroute 'PUT', path, opts, &bk; end
52
+ # See #aget.
53
+ def apost(path, opts={}, &bk); aroute 'POST', path, opts, &bk; end
54
+ # See #aget.
55
+ def adelete(path, opts={}, &bk); aroute 'DELETE', path, opts, &bk; end
56
+ # See #aget.
57
+ def ahead(path, opts={}, &bk); aroute 'HEAD', path, opts, &bk; end
58
+
59
+ private
60
+ def aroute(*args, &block) #:nodoc:
61
+ self.send :route, *args do |*bargs|
62
+ mc = class << self; self; end
63
+ mc.send :define_method, :__async_callback, &block
64
+ EM.next_tick { send(:__async_callback, *bargs) }
65
+ throw :async
66
+ end
67
+ end
68
+
69
+ module Helpers
70
+ # Send the given body or block as the final response to the asynchronous
71
+ # request.
72
+ def body(*args, &blk)
73
+ super
74
+ request.env['async.callback'][
75
+ [response.status, response.headers, response.body]
76
+ ] if respond_to?(:__async_callback)
77
+ end
78
+ end
79
+
80
+ def self.registered(app) #:nodoc:
81
+ app.helpers Helpers
82
+ end
83
+ end
84
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: raggi-async_sinatra
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
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 -07: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
+ post_install_message:
71
+ rdoc_options:
72
+ - --line-numbers
73
+ - --inline-source
74
+ - --title
75
+ - Async Sinatra
76
+ - --main
77
+ - README.rdoc
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ version:
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ version:
92
+ requirements: []
93
+
94
+ rubyforge_project: libraggi
95
+ rubygems_version: 1.2.0
96
+ signing_key:
97
+ specification_version: 2
98
+ summary: Asynchronous response API for Sinatra and Thin
99
+ test_files: []
100
+