crampy 0.15.3
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/bin/cramp +17 -0
- data/lib/cramp/abstract.rb +104 -0
- data/lib/cramp/action.rb +134 -0
- data/lib/cramp/body.rb +48 -0
- data/lib/cramp/callbacks.rb +92 -0
- data/lib/cramp/exception_handler.rb +357 -0
- data/lib/cramp/fiber_pool.rb +47 -0
- data/lib/cramp/generators/application.rb +97 -0
- data/lib/cramp/generators/templates/application/Gemfile +32 -0
- data/lib/cramp/generators/templates/application/app/actions/home_action.rb +11 -0
- data/lib/cramp/generators/templates/application/application.rb +36 -0
- data/lib/cramp/generators/templates/application/config/database.yml +6 -0
- data/lib/cramp/generators/templates/application/config/routes.rb +4 -0
- data/lib/cramp/generators/templates/application/config.ru +25 -0
- data/lib/cramp/keep_connection_alive.rb +19 -0
- data/lib/cramp/long_polling.rb +6 -0
- data/lib/cramp/periodic_timer.rb +56 -0
- data/lib/cramp/rendering.rb +11 -0
- data/lib/cramp/sse.rb +5 -0
- data/lib/cramp/test_case.rb +58 -0
- data/lib/cramp/version.rb +3 -0
- data/lib/cramp/websocket.rb +13 -0
- data/lib/cramp.rb +47 -0
- data/lib/crampy.rb +4 -0
- data/lib/vendor/fiber_pool.rb +85 -0
- metadata +130 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Cramp
|
2
|
+
module PeriodicTimer
|
3
|
+
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
# was class_inheritable_accessor
|
8
|
+
class_attribute :periodic_timers, :instance_reader => false
|
9
|
+
self.periodic_timers ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def periodic_timer(method, options = {})
|
14
|
+
self.periodic_timers << [method, options]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(*)
|
19
|
+
super
|
20
|
+
@timers = []
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def continue
|
26
|
+
super
|
27
|
+
start_periodic_timers
|
28
|
+
end
|
29
|
+
|
30
|
+
def init_async_body
|
31
|
+
super
|
32
|
+
|
33
|
+
if self.class.periodic_timers.any?
|
34
|
+
@body.callback { stop_periodic_timers }
|
35
|
+
@body.errback { stop_periodic_timers }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def start_periodic_timers
|
40
|
+
self.class.periodic_timers.each do |method, options|
|
41
|
+
@timers << EventMachine::PeriodicTimer.new(options[:every] || 1) { timer_method_wrapper(method) unless @finished }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_periodic_timers
|
46
|
+
@timers.each {|t| t.cancel }
|
47
|
+
end
|
48
|
+
|
49
|
+
def timer_method_wrapper(method)
|
50
|
+
send(method)
|
51
|
+
rescue StandardError, LoadError, SyntaxError => exception
|
52
|
+
handle_exception(exception)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/lib/cramp/sse.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/test_case'
|
3
|
+
|
4
|
+
module Cramp
|
5
|
+
class TestCase < ::ActiveSupport::TestCase
|
6
|
+
|
7
|
+
setup :create_request
|
8
|
+
|
9
|
+
def create_request
|
10
|
+
@request = Rack::MockRequest.new(app)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(path, options = {}, headers = {}, &block)
|
14
|
+
callback = options.delete(:callback) || block
|
15
|
+
headers = headers.merge('async.callback' => callback)
|
16
|
+
|
17
|
+
EM.run do
|
18
|
+
catch(:async) { @request.get(path, headers) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_body(path, options = {}, headers = {}, &block)
|
23
|
+
callback = options.delete(:callback) || block
|
24
|
+
response_callback = proc {|response| response[-1].each {|chunk| callback.call(chunk) } }
|
25
|
+
headers = headers.merge('async.callback' => response_callback)
|
26
|
+
|
27
|
+
EM.run do
|
28
|
+
catch(:async) { @request.get(path, headers) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_body_chunks(path, options = {}, headers = {}, &block)
|
33
|
+
callback = options.delete(:callback) || block
|
34
|
+
count = options.delete(:count) || 1
|
35
|
+
|
36
|
+
stopping = false
|
37
|
+
chunks = []
|
38
|
+
|
39
|
+
get_body(path, options, headers) do |body_chunk|
|
40
|
+
chunks << body_chunk unless stopping
|
41
|
+
|
42
|
+
if chunks.count >= count
|
43
|
+
stopping = true
|
44
|
+
callback.call(chunks) if callback
|
45
|
+
EM.next_tick { EM.stop }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def app
|
51
|
+
raise "Please define a method called 'app' returning an async Rack Application"
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
EM.stop
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Cramp
|
2
|
+
class Websocket < Action
|
3
|
+
self.transport = :websocket
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def backend=(backend)
|
7
|
+
raise "Websocket backend #{backend} is unknown" unless [:thin, :rainbows].include?(backend.to_sym)
|
8
|
+
Faye::WebSocket.load_adapter(backend.to_s)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
data/lib/cramp.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
# this shoud be called before reactor start, otherwise EM will silently ignore it
|
3
|
+
EM.epoll
|
4
|
+
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext/class/attribute'
|
7
|
+
#require 'active_support/core_ext/class/inheritable_attributes'
|
8
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
9
|
+
require 'active_support/core_ext/module/aliasing'
|
10
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
11
|
+
require 'active_support/core_ext/kernel/reporting'
|
12
|
+
require 'active_support/concern'
|
13
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
14
|
+
require 'active_support/core_ext/hash/except'
|
15
|
+
require 'active_support/buffered_logger'
|
16
|
+
|
17
|
+
require 'rack'
|
18
|
+
require 'faye/websocket'
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'fiber'
|
22
|
+
require File.join(File.dirname(__FILE__), 'vendor/fiber_pool')
|
23
|
+
rescue LoadError
|
24
|
+
# No fibers available!
|
25
|
+
end
|
26
|
+
|
27
|
+
module Cramp
|
28
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
29
|
+
require "#{Cramp::ROOT}/cramp/version"
|
30
|
+
|
31
|
+
mattr_accessor :logger
|
32
|
+
|
33
|
+
autoload :Action, "cramp/action"
|
34
|
+
autoload :Websocket, "cramp/websocket"
|
35
|
+
autoload :WebsocketExtension, "cramp/websocket/extension"
|
36
|
+
autoload :Protocol10FrameParser, "cramp/websocket/protocol10_frame_parser"
|
37
|
+
autoload :SSE, "cramp/sse"
|
38
|
+
autoload :LongPolling, "cramp/long_polling"
|
39
|
+
autoload :Body, "cramp/body"
|
40
|
+
autoload :PeriodicTimer, "cramp/periodic_timer"
|
41
|
+
autoload :KeepConnectionAlive, "cramp/keep_connection_alive"
|
42
|
+
autoload :Abstract, "cramp/abstract"
|
43
|
+
autoload :Callbacks, "cramp/callbacks"
|
44
|
+
autoload :FiberPool, "cramp/fiber_pool"
|
45
|
+
autoload :ExceptionHandler, "cramp/exception_handler"
|
46
|
+
autoload :TestCase, "cramp/test_case"
|
47
|
+
end
|
data/lib/crampy.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
|
2
|
+
# Copyright:: Copyright (c) 2008 eSpace, Inc.
|
3
|
+
# License:: Distributes under the same terms as Ruby
|
4
|
+
|
5
|
+
require 'fiber'
|
6
|
+
|
7
|
+
class Fiber
|
8
|
+
|
9
|
+
#Attribute Reference--Returns the value of a fiber-local variable, using
|
10
|
+
#either a symbol or a string name. If the specified variable does not exist,
|
11
|
+
#returns nil.
|
12
|
+
def [](key)
|
13
|
+
local_fiber_variables[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
#Attribute Assignment--Sets or creates the value of a fiber-local variable,
|
17
|
+
#using either a symbol or a string. See also Fiber#[].
|
18
|
+
def []=(key,value)
|
19
|
+
local_fiber_variables[key] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def local_fiber_variables
|
25
|
+
@local_fiber_variables ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class FiberPool
|
30
|
+
|
31
|
+
# gives access to the currently free fibers
|
32
|
+
attr_reader :fibers
|
33
|
+
attr_reader :busy_fibers
|
34
|
+
|
35
|
+
# Code can register a proc with this FiberPool to be called
|
36
|
+
# every time a Fiber is finished. Good for releasing resources
|
37
|
+
# like ActiveRecord database connections.
|
38
|
+
attr_accessor :generic_callbacks
|
39
|
+
|
40
|
+
# Prepare a list of fibers that are able to run different blocks of code
|
41
|
+
# every time. Once a fiber is done with its block, it attempts to fetch
|
42
|
+
# another one from the queue
|
43
|
+
def initialize(count = 100)
|
44
|
+
@fibers,@busy_fibers,@queue,@generic_callbacks = [],{},[],[]
|
45
|
+
count.times do |i|
|
46
|
+
fiber = Fiber.new do |block|
|
47
|
+
loop do
|
48
|
+
block.call
|
49
|
+
|
50
|
+
# callbacks are called in a reverse order, much like c++ destructor
|
51
|
+
Fiber.current[:callbacks].pop.call while Fiber.current[:callbacks].length > 0
|
52
|
+
generic_callbacks.each do |cb|
|
53
|
+
cb.call
|
54
|
+
end
|
55
|
+
|
56
|
+
if @queue.any?
|
57
|
+
block = @queue.shift
|
58
|
+
else
|
59
|
+
@busy_fibers.delete(Fiber.current.object_id)
|
60
|
+
@fibers.unshift Fiber.current
|
61
|
+
block = Fiber.yield
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
fiber[:callbacks] = []
|
67
|
+
fiber[:em_keys] = []
|
68
|
+
@fibers << fiber
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# If there is an available fiber use it, otherwise, leave it to linger
|
73
|
+
# in a queue
|
74
|
+
def spawn(&block)
|
75
|
+
if fiber = @fibers.shift
|
76
|
+
fiber[:callbacks] = []
|
77
|
+
@busy_fibers[fiber.object_id] = fiber
|
78
|
+
fiber.resume(block)
|
79
|
+
else
|
80
|
+
@queue << block
|
81
|
+
end
|
82
|
+
self # we are keen on hiding our queue
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crampy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.15.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pratik Naik
|
9
|
+
- Vasily Fedoseyev
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-06-12 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: &70186850572180 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70186850572180
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rack
|
28
|
+
requirement: &70186850570900 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *70186850570900
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: eventmachine
|
39
|
+
requirement: &70186850569540 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.0.0.beta.3
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70186850569540
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: faye-websocket
|
50
|
+
requirement: &70186850567380 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.3'
|
56
|
+
type: :runtime
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70186850567380
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: thor
|
61
|
+
requirement: &70186850565720 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ~>
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0.14'
|
67
|
+
type: :runtime
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *70186850565720
|
70
|
+
description: Crampy is a fork of Cramp, a framework for developing asynchronous web
|
71
|
+
applications.
|
72
|
+
email:
|
73
|
+
- vasilyfedoseyev@gmail.com
|
74
|
+
executables:
|
75
|
+
- cramp
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- MIT-LICENSE
|
80
|
+
- lib/cramp/abstract.rb
|
81
|
+
- lib/cramp/action.rb
|
82
|
+
- lib/cramp/body.rb
|
83
|
+
- lib/cramp/callbacks.rb
|
84
|
+
- lib/cramp/exception_handler.rb
|
85
|
+
- lib/cramp/fiber_pool.rb
|
86
|
+
- lib/cramp/generators/application.rb
|
87
|
+
- lib/cramp/generators/templates/application/app/actions/home_action.rb
|
88
|
+
- lib/cramp/generators/templates/application/application.rb
|
89
|
+
- lib/cramp/generators/templates/application/config/database.yml
|
90
|
+
- lib/cramp/generators/templates/application/config/routes.rb
|
91
|
+
- lib/cramp/generators/templates/application/config.ru
|
92
|
+
- lib/cramp/generators/templates/application/Gemfile
|
93
|
+
- lib/cramp/keep_connection_alive.rb
|
94
|
+
- lib/cramp/long_polling.rb
|
95
|
+
- lib/cramp/periodic_timer.rb
|
96
|
+
- lib/cramp/rendering.rb
|
97
|
+
- lib/cramp/sse.rb
|
98
|
+
- lib/cramp/test_case.rb
|
99
|
+
- lib/cramp/version.rb
|
100
|
+
- lib/cramp/websocket.rb
|
101
|
+
- lib/cramp.rb
|
102
|
+
- lib/crampy.rb
|
103
|
+
- lib/vendor/fiber_pool.rb
|
104
|
+
- bin/cramp
|
105
|
+
homepage: https://github.com/Vasfed/cramp
|
106
|
+
licenses: []
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ! '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 1.8.15
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: Asynchronous web framework.
|
129
|
+
test_files: []
|
130
|
+
has_rdoc: false
|