ramaze 2010.06.18 → 2011.01
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/.gitignore +1 -0
- data/MANIFEST +9 -16
- data/README.md +37 -30
- data/Rakefile +5 -1
- data/TODO.md +19 -0
- data/doc/AUTHORS +5 -1
- data/doc/CHANGELOG +3553 -3272
- data/doc/tutorial/todolist.html +1512 -1512
- data/examples/app/blog/app.rb +2 -0
- data/examples/app/todolist/controller/init.rb +1 -2
- data/examples/app/wiktacular/mkd/main/2007-07-20_19-21-12.mkd +1 -1
- data/examples/app/wiktacular/mkd/main/2007-07-20_19-23-10.mkd +1 -1
- data/examples/app/wiktacular/mkd/main/2007-07-20_19-45-07.mkd +1 -1
- data/examples/app/wiktacular/mkd/main/current.mkd +1 -1
- data/examples/app/wiktacular/mkd/testing/2007-07-20_16-43-46.mkd +1 -1
- data/examples/app/wiktacular/mkd/testing/2007-07-20_19-43-50.mkd +2 -2
- data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-08.mkd +16 -16
- data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-54.mkd +16 -16
- data/examples/app/wiktacular/mkd/testing/current.mkd +16 -16
- data/lib/proto/model/init.rb +1 -1
- data/lib/proto/public/js/jquery.js +2034 -1095
- data/lib/proto/start.rb +2 -0
- data/lib/proto/view/index.xhtml +3 -3
- data/lib/ramaze.rb +1 -2
- data/lib/ramaze/cache.rb +1 -0
- data/lib/ramaze/cache/sequel.rb +131 -37
- data/lib/ramaze/controller.rb +1 -0
- data/lib/ramaze/gestalt.rb +75 -46
- data/lib/ramaze/helper.rb +1 -0
- data/lib/ramaze/helper/auth.rb +38 -4
- data/lib/ramaze/helper/blue_form.rb +498 -78
- data/lib/ramaze/helper/cache.rb +2 -2
- data/lib/ramaze/helper/csrf.rb +225 -0
- data/lib/ramaze/helper/erector.rb +67 -9
- data/lib/ramaze/helper/flash.rb +4 -2
- data/lib/ramaze/helper/gestalt.rb +2 -0
- data/lib/ramaze/helper/gravatar.rb +1 -1
- data/lib/ramaze/helper/localize.rb +4 -0
- data/lib/ramaze/helper/send_file.rb +30 -0
- data/lib/ramaze/helper/thread.rb +5 -0
- data/lib/ramaze/helper/user.rb +4 -3
- data/lib/ramaze/helper/xhtml.rb +87 -8
- data/lib/ramaze/log.rb +13 -0
- data/lib/ramaze/log/analogger.rb +15 -5
- data/lib/ramaze/log/growl.rb +28 -13
- data/lib/ramaze/log/hub.rb +12 -4
- data/lib/ramaze/log/informer.rb +28 -11
- data/lib/ramaze/log/knotify.rb +7 -2
- data/lib/ramaze/log/logger.rb +12 -4
- data/lib/ramaze/log/logging.rb +40 -14
- data/lib/ramaze/log/rotatinginformer.rb +47 -23
- data/lib/ramaze/log/syslog.rb +37 -31
- data/lib/ramaze/log/xosd.rb +7 -4
- data/lib/ramaze/middleware_compiler.rb +2 -2
- data/lib/ramaze/snippets/fiber.rb +63 -63
- data/lib/ramaze/snippets/ramaze/lru_hash.rb +1 -1
- data/lib/ramaze/tool/bin.rb +1 -1
- data/lib/ramaze/version.rb +1 -1
- data/lib/ramaze/view.rb +4 -4
- data/lib/ramaze/view/erector.rb +88 -13
- data/ramaze.gemspec +65 -65
- data/spec/ramaze/bin/ramaze.rb +1 -1
- data/spec/ramaze/cache/localmemcache.rb +20 -12
- data/spec/ramaze/cache/sequel.rb +19 -19
- data/spec/ramaze/helper/blue_form.rb +549 -257
- data/spec/ramaze/helper/csrf.rb +109 -0
- data/spec/ramaze/helper/httpdigest.rb +31 -29
- data/spec/ramaze/helper/user.rb +1 -1
- data/spec/ramaze/helper/xhtml.rb +17 -0
- data/spec/ramaze/log/growl.rb +34 -0
- data/spec/ramaze/log/informer.rb +1 -0
- data/spec/ramaze/view/erector.rb +49 -71
- data/spec/ramaze/view/erector/external_view.erector +5 -0
- data/spec/ramaze/view/erector/index.erector +5 -0
- data/spec/ramaze/view/erector/layout.erector +13 -3
- data/spec/ramaze/view/erector/tables.erector +23 -0
- data/spec/ramaze/view/erector/view.erector +6 -0
- data/tasks/git.rake +2 -2
- metadata +133 -176
- data/examples/helpers/form_with_sequel.rb +0 -24
- data/examples/helpers/nitro_form.rb +0 -23
- data/lib/ramaze/helper/form.rb +0 -133
- data/lib/ramaze/helper/nitroform.rb +0 -14
- data/lib/ramaze/helper/pager.rb +0 -367
- data/lib/ramaze/helper/partial.rb +0 -100
- data/lib/ramaze/helper/sequel.rb +0 -55
- data/lib/ramaze/helper/sequel_form.rb +0 -284
- data/lib/vendor/etag.rb +0 -22
- data/spec/ramaze/helper/form.rb +0 -360
- data/spec/ramaze/helper/pager.rb +0 -96
- data/spec/ramaze/helper/sequel_form.rb +0 -94
- data/spec/ramaze/view/erector/external.erector +0 -1
- data/spec/ramaze/view/erector/invoke_helper_method.erector +0 -1
- data/spec/ramaze/view/erector/strict_xhtml.erector +0 -3
- data/spec/ramaze/view/erector/sum.erector +0 -1
data/lib/ramaze/log/logging.rb
CHANGED
@@ -3,8 +3,10 @@
|
|
3
3
|
|
4
4
|
module Ramaze
|
5
5
|
|
6
|
+
##
|
6
7
|
# This module provides a basic skeleton for your own loggers to be compatible.
|
7
|
-
#
|
8
|
+
#
|
9
|
+
# @example
|
8
10
|
#
|
9
11
|
# class MyLogger
|
10
12
|
# include Logging
|
@@ -13,14 +15,19 @@ module Ramaze
|
|
13
15
|
# p tag => args
|
14
16
|
# end
|
15
17
|
# end
|
16
|
-
|
18
|
+
#
|
17
19
|
module Logging
|
18
20
|
|
21
|
+
##
|
19
22
|
# Takes the tag (:warn|:debug|:error|:info) and the name of a method to be
|
20
23
|
# called upon elements of msgs that don't respond to :to_str
|
21
24
|
# Goes on and sends the tag and transformed messages each to the #log method.
|
22
25
|
# If you include this module you have to define #log or it will raise.
|
23
|
-
|
26
|
+
#
|
27
|
+
# @param [String] tag The level of the log message.
|
28
|
+
# @param [String] method
|
29
|
+
# @param [Array] msgs The data that should be logged.
|
30
|
+
#
|
24
31
|
def tag_log(tag, meth, *msgs)
|
25
32
|
msgs.each do |msg|
|
26
33
|
string = (msg.respond_to?(:to_str) ? msg : msg.send(meth))
|
@@ -28,35 +35,50 @@ module Ramaze
|
|
28
35
|
end
|
29
36
|
end
|
30
37
|
|
38
|
+
##
|
31
39
|
# Converts everything given to strings and passes them on with :info
|
32
|
-
|
40
|
+
#
|
41
|
+
# @param [Array] objects An array of objects that need to be converted to strings.
|
42
|
+
#
|
33
43
|
def info(*objects)
|
34
44
|
tag_log(:info, :to_s, *objects)
|
35
45
|
end
|
36
46
|
|
47
|
+
##
|
37
48
|
# Converts everything given to strings and passes them on with :warn
|
38
|
-
|
49
|
+
#
|
50
|
+
# @param [Array] objects An array of objects that need to be converted to strings.
|
51
|
+
#
|
39
52
|
def warn(*objects)
|
40
53
|
tag_log(:warn, :to_s, *objects)
|
41
54
|
end
|
42
55
|
|
43
|
-
|
44
|
-
|
56
|
+
##
|
57
|
+
# Inspects objects if they are no strings. Tag is :debug
|
58
|
+
#
|
59
|
+
# @param [Array] objects An array of objects that will be inspected.
|
60
|
+
#
|
45
61
|
def debug(*objects)
|
46
62
|
tag_log(:debug, :inspect, *objects)
|
47
63
|
end
|
48
64
|
|
49
|
-
|
50
|
-
|
65
|
+
##
|
66
|
+
# Inspects objects if they are no strings. Tag is :dev
|
67
|
+
#
|
68
|
+
# @param [Array] objects An array of objects that will be inspected.
|
69
|
+
#
|
51
70
|
def dev(*objects)
|
52
71
|
tag_log(:dev, :inspect, *objects)
|
53
72
|
end
|
54
73
|
|
55
74
|
alias << debug
|
56
75
|
|
76
|
+
##
|
57
77
|
# Takes either an Exception or just a String, formats backtraces to be a bit
|
58
78
|
# more readable and passes all of this on to tag_log :error
|
59
|
-
|
79
|
+
#
|
80
|
+
# @param [Object] ex The exception that was raised.
|
81
|
+
#
|
60
82
|
def error(ex)
|
61
83
|
if ex.respond_to?(:exception)
|
62
84
|
message = ex.backtrace
|
@@ -68,13 +90,17 @@ module Ramaze
|
|
68
90
|
tag_log(:error, :to_s, *message)
|
69
91
|
end
|
70
92
|
|
71
|
-
|
72
|
-
|
93
|
+
##
|
94
|
+
# Nothing.
|
95
|
+
#
|
96
|
+
# THINK: Is this really required? It doesn't do anything anyway.
|
97
|
+
#
|
73
98
|
def shutdown
|
74
99
|
end
|
75
100
|
|
76
|
-
|
77
|
-
|
101
|
+
##
|
102
|
+
# Stub for WEBrick
|
103
|
+
#
|
78
104
|
def debug?
|
79
105
|
false
|
80
106
|
end
|
@@ -2,8 +2,11 @@ module Ramaze
|
|
2
2
|
|
3
3
|
module Logger
|
4
4
|
|
5
|
+
##
|
5
6
|
# A customized logger (based on Informer) that creates multiple log files based on time
|
6
|
-
|
7
|
+
#
|
8
|
+
# TODO: This class isn't fully documented and could use a few improvements.
|
9
|
+
#
|
7
10
|
class RotatingInformer
|
8
11
|
include Innate::Traited
|
9
12
|
include Logging
|
@@ -16,7 +19,8 @@ module Ramaze
|
|
16
19
|
|
17
20
|
# This is how the final output is arranged.
|
18
21
|
trait :format => "[%time] %prefix %text"
|
19
|
-
|
22
|
+
|
23
|
+
##
|
20
24
|
# Create a new instance of RotatingInformer.
|
21
25
|
#
|
22
26
|
# base_dir is the directory where all log files will be stored
|
@@ -29,21 +33,24 @@ module Ramaze
|
|
29
33
|
# that the log receives. The array may contain
|
30
34
|
# any or all of the symbols :debug, :error, :info and/or :warn
|
31
35
|
#
|
32
|
-
#
|
36
|
+
# @example
|
37
|
+
#
|
38
|
+
# # Creates logs in directory called logs. The generated filenames will be in the form YYYY-MM-DD.log
|
33
39
|
# RotatingInformer.new('logs')
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# # Creates logs in directory called logs. The generated filenames will be in the form YYYY-MM.txt
|
37
43
|
# RotatingInformer.new('logs', '%Y-%m.txt')
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
44
|
+
#
|
45
|
+
#
|
46
|
+
# # Creates logs in directory called logs. The generated filenames will be in the form YYYY-MM.txt.
|
47
|
+
# # Only errors will be logged to the files.
|
41
48
|
# RotatingInformer.new('logs', '%Y-%m.txt', [:error])
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
49
|
+
#
|
50
|
+
# @param [String] base_dir The base directory for all the log files.
|
51
|
+
# @param [String] time_format The time format for all log files.
|
52
|
+
# @param [Array] log_levels Array containing the type of messages to log.
|
53
|
+
#
|
47
54
|
def initialize(base_dir, time_format = '%Y-%m-%d.log', log_levels = [:debug, :error, :info, :warn])
|
48
55
|
# Verify and set base directory
|
49
56
|
send :base_dir=, base_dir, true
|
@@ -55,6 +62,7 @@ module Ramaze
|
|
55
62
|
@in_shutdown = false
|
56
63
|
end
|
57
64
|
|
65
|
+
##
|
58
66
|
# Set the base directory for log files
|
59
67
|
#
|
60
68
|
# If this method is called with the raise_exception
|
@@ -63,8 +71,11 @@ module Ramaze
|
|
63
71
|
#
|
64
72
|
# If raise_exception is set to false, the method will just
|
65
73
|
# silently fail if the specified directory does not exist
|
66
|
-
# or is unwritable
|
67
|
-
|
74
|
+
# or is unwritable.
|
75
|
+
#
|
76
|
+
# @param [String] directory The base directory specified by the developer.
|
77
|
+
# @param [Bool] raise_exception Boolean that indicates if an exception should be raised if the base directory doesn't exist.
|
78
|
+
#
|
68
79
|
def base_dir=(directory, raise_exception = false)
|
69
80
|
# Expand directory path
|
70
81
|
base_dir = File.expand_path(directory)
|
@@ -86,8 +97,9 @@ module Ramaze
|
|
86
97
|
end
|
87
98
|
end
|
88
99
|
|
100
|
+
##
|
89
101
|
# Close the file we log to if it isn't closed already.
|
90
|
-
|
102
|
+
#
|
91
103
|
def shutdown
|
92
104
|
if @out.respond_to?(:close)
|
93
105
|
unless @in_shutdown
|
@@ -99,8 +111,12 @@ module Ramaze
|
|
99
111
|
end
|
100
112
|
end
|
101
113
|
|
114
|
+
##
|
102
115
|
# Integration to Logging.
|
103
|
-
|
116
|
+
#
|
117
|
+
# @param [String] tag The type of message we're logging.
|
118
|
+
# @param [Array] messages An array of messages to log.
|
119
|
+
#
|
104
120
|
def log tag, *messages
|
105
121
|
|
106
122
|
return unless @log_levels.include?(tag)
|
@@ -119,9 +135,14 @@ module Ramaze
|
|
119
135
|
@out.flush if @out.respond_to?(:flush)
|
120
136
|
end
|
121
137
|
|
138
|
+
##
|
122
139
|
# Takes the prefix (tag), text and timestamp and applies it to
|
123
140
|
# the :format trait.
|
124
|
-
|
141
|
+
#
|
142
|
+
# @param [String] prefix
|
143
|
+
# @param [String] text
|
144
|
+
# @param [Integer] time
|
145
|
+
#
|
125
146
|
def log_interpolate prefix, text, time = timestamp
|
126
147
|
message = class_trait[:format].dup
|
127
148
|
|
@@ -131,27 +152,30 @@ module Ramaze
|
|
131
152
|
message
|
132
153
|
end
|
133
154
|
|
155
|
+
##
|
134
156
|
# This uses timestamp trait or a date in the format of
|
135
157
|
# %Y-%m-%d %H:%M:%S
|
136
158
|
# # => "2007-01-19 21:09:32"
|
137
|
-
|
159
|
+
#
|
138
160
|
def timestamp
|
139
161
|
mask = class_trait[:timestamp]
|
140
162
|
Time.now.strftime(mask || "%Y-%m-%d %H:%M:%S")
|
141
163
|
end
|
142
164
|
|
143
|
-
|
144
|
-
|
165
|
+
##
|
166
|
+
# Is @out closed?
|
167
|
+
#
|
145
168
|
def closed?
|
146
169
|
@out.respond_to?(:closed?) && @out.closed?
|
147
170
|
end
|
148
171
|
|
149
172
|
private
|
150
173
|
|
174
|
+
##
|
151
175
|
# Checks whether current filename is still valid.
|
152
176
|
# If not, update the current log to point at the new
|
153
177
|
# filename
|
154
|
-
|
178
|
+
#
|
155
179
|
def update_current_log
|
156
180
|
out = File.join(@base_dir, Time.now.strftime(@time_format))
|
157
181
|
if @out.nil? || @out.path != out
|
data/lib/ramaze/log/syslog.rb
CHANGED
@@ -13,39 +13,45 @@ module Syslog
|
|
13
13
|
end
|
14
14
|
|
15
15
|
module Ramaze
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
module Logger
|
17
|
+
##
|
18
|
+
# Logger class for writing to syslog. It is a *very* thin wrapper
|
19
|
+
# around the Syslog library.
|
20
|
+
#
|
21
|
+
class Syslog
|
22
|
+
include Logging
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
##
|
25
|
+
# Open the syslog library, if it is allready open, we reopen it using the
|
26
|
+
# new argument list. The argument list is passed on to the Syslog library
|
27
|
+
# so please check that, and man syslog for detailed information.
|
28
|
+
# There are 3 parameters:
|
29
|
+
#
|
30
|
+
# ident: The identification used in the log file, defaults to $0
|
31
|
+
# options: defaults to Syslog::LOG_PID | Syslog::LOG_CONS
|
32
|
+
# facility: defaults to Syslog::LOG_USER
|
33
|
+
#
|
34
|
+
def initialize( *args )
|
35
|
+
::Syslog.close if ::Syslog.opened?
|
36
|
+
::Syslog.open( *args )
|
37
|
+
end
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
##
|
40
|
+
# Just sends all messages received to ::Syslog
|
41
|
+
# We simply return if the log was closed for some reason, this behavior
|
42
|
+
# was copied from Informer. We do not handle levels here. This will
|
43
|
+
# be done by te syslog daemon based on it's configuration.
|
44
|
+
def log(tag, *messages)
|
45
|
+
return if !::Syslog.opened?
|
46
|
+
::Syslog.send(tag, *messages)
|
47
|
+
end
|
44
48
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
##
|
50
|
+
# Has to call the modules singleton-method.
|
51
|
+
#
|
52
|
+
def inspect
|
53
|
+
::Syslog.inspect
|
54
|
+
end
|
50
55
|
end
|
56
|
+
end
|
51
57
|
end
|
data/lib/ramaze/log/xosd.rb
CHANGED
@@ -8,11 +8,12 @@ require 'thread'
|
|
8
8
|
module Ramaze
|
9
9
|
module Logger
|
10
10
|
|
11
|
+
##
|
11
12
|
# Informer for the XOSD notification system for X11.
|
12
13
|
#
|
13
14
|
# You can install the ruby-bindings with:
|
14
15
|
# gem install xosd.
|
15
|
-
|
16
|
+
#
|
16
17
|
class Xosd < ::Xosd
|
17
18
|
attr_accessor :options
|
18
19
|
|
@@ -43,11 +44,12 @@ module Ramaze
|
|
43
44
|
# Here new messages are pushed to eventually displaying them.
|
44
45
|
QUEUE = Queue.new
|
45
46
|
|
47
|
+
##
|
46
48
|
# Create a new instance, valid options are in DEFAULT.
|
47
49
|
# In the background a new thread will be running that checks the QUEUE
|
48
50
|
# and processes all messages that are being sent to it.
|
49
51
|
# This is done to make output nicer and readable.
|
50
|
-
|
52
|
+
#
|
51
53
|
def initialize(options = {})
|
52
54
|
@options = DEFAULT.merge(options)
|
53
55
|
|
@@ -79,8 +81,9 @@ module Ramaze
|
|
79
81
|
end
|
80
82
|
end
|
81
83
|
|
82
|
-
|
83
|
-
|
84
|
+
##
|
85
|
+
# Pushes all messages it gets on the QUEUE for further processing.
|
86
|
+
#
|
84
87
|
def log(tag, *messages)
|
85
88
|
messages.each do |message|
|
86
89
|
QUEUE << [tag, message]
|
@@ -2,12 +2,12 @@ module Ramaze
|
|
2
2
|
class MiddlewareCompiler < Innate::MiddlewareCompiler
|
3
3
|
def static(path)
|
4
4
|
require 'rack/contrib'
|
5
|
-
Rack::ETag.new(Rack::ConditionalGet.new(Rack::File.new(path)))
|
5
|
+
Rack::ETag.new(Rack::ConditionalGet.new(Rack::File.new(path)), 'public')
|
6
6
|
end
|
7
7
|
|
8
8
|
def directory(path)
|
9
9
|
require 'rack/contrib'
|
10
|
-
Rack::ETag.new(Rack::ConditionalGet.new(Rack::Directory.new(path)))
|
10
|
+
Rack::ETag.new(Rack::ConditionalGet.new(Rack::Directory.new(path)), 'public')
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -1,63 +1,63 @@
|
|
1
|
-
unless defined? Fiber
|
2
|
-
require 'thread'
|
3
|
-
|
4
|
-
class FiberError < StandardError; end
|
5
|
-
|
6
|
-
class Fiber
|
7
|
-
def initialize
|
8
|
-
raise ArgumentError, 'new Fiber requires a block' unless block_given?
|
9
|
-
|
10
|
-
@yield = Queue.new
|
11
|
-
@resume = Queue.new
|
12
|
-
|
13
|
-
@thread = Thread.new{ @yield.push [*yield(*wait)] }
|
14
|
-
@thread.abort_on_exception = true
|
15
|
-
@thread[:fiber] = self
|
16
|
-
end
|
17
|
-
attr_reader :yield, :thread
|
18
|
-
|
19
|
-
def resume *args
|
20
|
-
raise FiberError, 'dead fiber called' unless @thread.alive?
|
21
|
-
@resume.push(args)
|
22
|
-
result = @yield.pop
|
23
|
-
result.size > 1 ? result : result.first
|
24
|
-
end
|
25
|
-
|
26
|
-
def wait
|
27
|
-
@resume.pop
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.yield *args
|
31
|
-
raise FiberError, "can't yield from root fiber" unless fiber = Thread.current[:fiber]
|
32
|
-
fiber.yield.push(args)
|
33
|
-
result = fiber.wait
|
34
|
-
result.size > 1 ? result : result.first
|
35
|
-
end
|
36
|
-
|
37
|
-
def inspect
|
38
|
-
"#<#{self.class}:0x#{self.object_id.to_s(16)}>"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
if __FILE__ == $0
|
44
|
-
f = Fiber.new{ puts 'hi'; p Fiber.yield(1); puts 'bye'; :done }
|
45
|
-
p f.resume
|
46
|
-
p f.resume(2)
|
47
|
-
end
|
48
|
-
|
49
|
-
__END__
|
50
|
-
|
51
|
-
$ ruby fbr.rb
|
52
|
-
hi
|
53
|
-
1
|
54
|
-
2
|
55
|
-
bye
|
56
|
-
:done
|
57
|
-
|
58
|
-
$ ruby1.9 fbr.rb
|
59
|
-
hi
|
60
|
-
1
|
61
|
-
2
|
62
|
-
bye
|
63
|
-
:done
|
1
|
+
unless defined? Fiber
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
class FiberError < StandardError; end
|
5
|
+
|
6
|
+
class Fiber
|
7
|
+
def initialize
|
8
|
+
raise ArgumentError, 'new Fiber requires a block' unless block_given?
|
9
|
+
|
10
|
+
@yield = Queue.new
|
11
|
+
@resume = Queue.new
|
12
|
+
|
13
|
+
@thread = Thread.new{ @yield.push [*yield(*wait)] }
|
14
|
+
@thread.abort_on_exception = true
|
15
|
+
@thread[:fiber] = self
|
16
|
+
end
|
17
|
+
attr_reader :yield, :thread
|
18
|
+
|
19
|
+
def resume *args
|
20
|
+
raise FiberError, 'dead fiber called' unless @thread.alive?
|
21
|
+
@resume.push(args)
|
22
|
+
result = @yield.pop
|
23
|
+
result.size > 1 ? result : result.first
|
24
|
+
end
|
25
|
+
|
26
|
+
def wait
|
27
|
+
@resume.pop
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.yield *args
|
31
|
+
raise FiberError, "can't yield from root fiber" unless fiber = Thread.current[:fiber]
|
32
|
+
fiber.yield.push(args)
|
33
|
+
result = fiber.wait
|
34
|
+
result.size > 1 ? result : result.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect
|
38
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if __FILE__ == $0
|
44
|
+
f = Fiber.new{ puts 'hi'; p Fiber.yield(1); puts 'bye'; :done }
|
45
|
+
p f.resume
|
46
|
+
p f.resume(2)
|
47
|
+
end
|
48
|
+
|
49
|
+
__END__
|
50
|
+
|
51
|
+
$ ruby fbr.rb
|
52
|
+
hi
|
53
|
+
1
|
54
|
+
2
|
55
|
+
bye
|
56
|
+
:done
|
57
|
+
|
58
|
+
$ ruby1.9 fbr.rb
|
59
|
+
hi
|
60
|
+
1
|
61
|
+
2
|
62
|
+
bye
|
63
|
+
:done
|