rubycut-sinatra-contrib 1.4.0
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/LICENSE +20 -0
- data/README.md +136 -0
- data/Rakefile +75 -0
- data/ideas.md +29 -0
- data/lib/sinatra/capture.rb +124 -0
- data/lib/sinatra/config_file.rb +167 -0
- data/lib/sinatra/content_for.rb +125 -0
- data/lib/sinatra/contrib.rb +39 -0
- data/lib/sinatra/contrib/all.rb +2 -0
- data/lib/sinatra/contrib/setup.rb +53 -0
- data/lib/sinatra/contrib/version.rb +17 -0
- data/lib/sinatra/cookies.rb +331 -0
- data/lib/sinatra/decompile.rb +120 -0
- data/lib/sinatra/engine_tracking.rb +96 -0
- data/lib/sinatra/extension.rb +95 -0
- data/lib/sinatra/json.rb +130 -0
- data/lib/sinatra/link_header.rb +132 -0
- data/lib/sinatra/multi_route.rb +87 -0
- data/lib/sinatra/namespace.rb +284 -0
- data/lib/sinatra/reloader.rb +394 -0
- data/lib/sinatra/respond_with.rb +249 -0
- data/lib/sinatra/streaming.rb +267 -0
- data/lib/sinatra/test_helpers.rb +87 -0
- data/sinatra-contrib.gemspec +127 -0
- data/spec/capture_spec.rb +93 -0
- data/spec/config_file/key_value.yml +6 -0
- data/spec/config_file/key_value.yml.erb +6 -0
- data/spec/config_file/key_value_override.yml +2 -0
- data/spec/config_file/missing_env.yml +4 -0
- data/spec/config_file/with_envs.yml +7 -0
- data/spec/config_file/with_nested_envs.yml +11 -0
- data/spec/config_file_spec.rb +63 -0
- data/spec/content_for/different_key.erb +1 -0
- data/spec/content_for/different_key.erubis +1 -0
- data/spec/content_for/different_key.haml +2 -0
- data/spec/content_for/different_key.slim +2 -0
- data/spec/content_for/layout.erb +1 -0
- data/spec/content_for/layout.erubis +1 -0
- data/spec/content_for/layout.haml +1 -0
- data/spec/content_for/layout.slim +1 -0
- data/spec/content_for/multiple_blocks.erb +4 -0
- data/spec/content_for/multiple_blocks.erubis +4 -0
- data/spec/content_for/multiple_blocks.haml +8 -0
- data/spec/content_for/multiple_blocks.slim +8 -0
- data/spec/content_for/multiple_yields.erb +3 -0
- data/spec/content_for/multiple_yields.erubis +3 -0
- data/spec/content_for/multiple_yields.haml +3 -0
- data/spec/content_for/multiple_yields.slim +3 -0
- data/spec/content_for/passes_values.erb +1 -0
- data/spec/content_for/passes_values.erubis +1 -0
- data/spec/content_for/passes_values.haml +1 -0
- data/spec/content_for/passes_values.slim +1 -0
- data/spec/content_for/same_key.erb +1 -0
- data/spec/content_for/same_key.erubis +1 -0
- data/spec/content_for/same_key.haml +2 -0
- data/spec/content_for/same_key.slim +2 -0
- data/spec/content_for/takes_values.erb +1 -0
- data/spec/content_for/takes_values.erubis +1 -0
- data/spec/content_for/takes_values.haml +3 -0
- data/spec/content_for/takes_values.slim +3 -0
- data/spec/content_for_spec.rb +213 -0
- data/spec/cookies_spec.rb +802 -0
- data/spec/decompile_spec.rb +44 -0
- data/spec/extension_spec.rb +33 -0
- data/spec/json_spec.rb +117 -0
- data/spec/link_header_spec.rb +100 -0
- data/spec/multi_route_spec.rb +60 -0
- data/spec/namespace/foo.erb +1 -0
- data/spec/namespace/nested/foo.erb +1 -0
- data/spec/namespace_spec.rb +676 -0
- data/spec/okjson.rb +581 -0
- data/spec/reloader/app.rb.erb +40 -0
- data/spec/reloader_spec.rb +441 -0
- data/spec/respond_with/bar.erb +1 -0
- data/spec/respond_with/bar.json.erb +1 -0
- data/spec/respond_with/foo.html.erb +1 -0
- data/spec/respond_with/not_html.sass +2 -0
- data/spec/respond_with_spec.rb +297 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/streaming_spec.rb +436 -0
- metadata +313 -0
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'sinatra/json'
|
2
|
+
require 'sinatra/base'
|
3
|
+
|
4
|
+
module Sinatra
|
5
|
+
#
|
6
|
+
# = Sinatra::RespondWith
|
7
|
+
#
|
8
|
+
# These extensions let Sinatra automatically choose what template to render or
|
9
|
+
# action to perform depending on the request's Accept header.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# # Without Sinatra::RespondWith
|
14
|
+
# get '/' do
|
15
|
+
# data = { :name => 'example' }
|
16
|
+
# request.accept.each do |type|
|
17
|
+
# case type
|
18
|
+
# when 'text/html'
|
19
|
+
# halt haml(:index, :locals => data)
|
20
|
+
# when 'text/json'
|
21
|
+
# halt data.to_json
|
22
|
+
# when 'application/atom+xml'
|
23
|
+
# halt nokogiri(:'index.atom', :locals => data)
|
24
|
+
# when 'application/xml', 'text/xml'
|
25
|
+
# halt nokogiri(:'index.xml', :locals => data)
|
26
|
+
# when 'text/plain'
|
27
|
+
# halt 'just an example'
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# error 406
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # With Sinatra::RespondWith
|
34
|
+
# get '/' do
|
35
|
+
# respond_with :index, :name => 'example' do |f|
|
36
|
+
# f.txt { 'just an example' }
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Both helper methods +respond_to+ and +respond_with+ let you define custom
|
41
|
+
# handlers like the one above for +text/plain+. +respond_with+ additionally
|
42
|
+
# takes a template name and/or an object to offer the following default
|
43
|
+
# behavior:
|
44
|
+
#
|
45
|
+
# * If a template name is given, search for a template called
|
46
|
+
# +name.format.engine+ (+index.xml.nokogiri+ in the above example).
|
47
|
+
# * If a template name is given, search for a templated called +name.engine+
|
48
|
+
# for engines known to result in the requested format (+index.haml+).
|
49
|
+
# * If a file extension associated with the mime type is known to Sinatra, and
|
50
|
+
# the object responds to +to_extension+, call that method and use the result
|
51
|
+
# (+data.to_json+).
|
52
|
+
#
|
53
|
+
# == Security
|
54
|
+
#
|
55
|
+
# Since methods are triggered based on client input, this can lead to security
|
56
|
+
# issues (but not as severe as those might appear in the first place: keep in
|
57
|
+
# mind that only known file extensions are used). You should limit
|
58
|
+
# the possible formats you serve.
|
59
|
+
#
|
60
|
+
# This is possible with the +provides+ condition:
|
61
|
+
#
|
62
|
+
# get '/', :provides => [:html, :json, :xml, :atom] do
|
63
|
+
# respond_with :index, :name => 'example'
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# However, since you have to set +provides+ for every route, this extension
|
67
|
+
# adds an app global (class method) `respond_to`, that lets you define content
|
68
|
+
# types for all routes:
|
69
|
+
#
|
70
|
+
# respond_to :html, :json, :xml, :atom
|
71
|
+
# get('/a') { respond_with :index, :name => 'a' }
|
72
|
+
# get('/b') { respond_with :index, :name => 'b' }
|
73
|
+
#
|
74
|
+
# == Custom Types
|
75
|
+
#
|
76
|
+
# Use the +on+ method for defining actions for custom types:
|
77
|
+
#
|
78
|
+
# get '/' do
|
79
|
+
# respond_to do |f|
|
80
|
+
# f.xml { nokogiri :index }
|
81
|
+
# f.on('application/custom') { custom_action }
|
82
|
+
# f.on('text/*') { data.to_s }
|
83
|
+
# f.on('*/*') { "matches everything" }
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# Definition order does not matter.
|
88
|
+
module RespondWith
|
89
|
+
class Format
|
90
|
+
def initialize(app)
|
91
|
+
@app, @map, @generic, @default = app, {}, {}, nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def on(type, &block)
|
95
|
+
@app.settings.mime_types(type).each do |mime|
|
96
|
+
case mime
|
97
|
+
when '*/*' then @default = block
|
98
|
+
when /^([^\/]+)\/\*$/ then @generic[$1] = block
|
99
|
+
else @map[mime] = block
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def finish
|
105
|
+
yield self if block_given?
|
106
|
+
mime_type = @app.content_type ||
|
107
|
+
@app.request.preferred_type(@map.keys) ||
|
108
|
+
@app.request.preferred_type ||
|
109
|
+
'text/html'
|
110
|
+
type = mime_type.split(/\s*;\s*/, 2).first
|
111
|
+
handlers = [@map[type], @generic[type[/^[^\/]+/]], @default].compact
|
112
|
+
handlers.each do |block|
|
113
|
+
if result = block.call(type)
|
114
|
+
@app.content_type mime_type
|
115
|
+
@app.halt result
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@app.halt 406
|
119
|
+
end
|
120
|
+
|
121
|
+
def method_missing(method, *args, &block)
|
122
|
+
return super if args.any? or block.nil? or not @app.mime_type(method)
|
123
|
+
on(method, &block)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
module Helpers
|
128
|
+
include Sinatra::JSON
|
129
|
+
|
130
|
+
def respond_with(template, object = nil, &block)
|
131
|
+
object, template = template, nil unless Symbol === template
|
132
|
+
format = Format.new(self)
|
133
|
+
format.on "*/*" do |type|
|
134
|
+
exts = settings.ext_map[type]
|
135
|
+
exts << :xml if type.end_with? '+xml'
|
136
|
+
if template
|
137
|
+
args = template_cache.fetch(type, template) { template_for(template, exts) }
|
138
|
+
if args.any?
|
139
|
+
locals = { :object => object }
|
140
|
+
locals.merge! object.to_hash if object.respond_to? :to_hash
|
141
|
+
|
142
|
+
renderer = args.first
|
143
|
+
options = args[1..-1] + [{:locals => locals}]
|
144
|
+
|
145
|
+
halt send(renderer, *options)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
if object
|
149
|
+
exts.each do |ext|
|
150
|
+
halt json(object) if ext == :json
|
151
|
+
next unless object.respond_to? method = "to_#{ext}"
|
152
|
+
halt(*object.send(method))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
false
|
156
|
+
end
|
157
|
+
format.finish(&block)
|
158
|
+
end
|
159
|
+
|
160
|
+
def respond_to(&block)
|
161
|
+
Format.new(self).finish(&block)
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def template_for(name, exts)
|
167
|
+
# in production this is cached, so don't worry too much about runtime
|
168
|
+
possible = []
|
169
|
+
settings.template_engines[:all].each do |engine|
|
170
|
+
exts.each { |ext| possible << [engine, "#{name}.#{ext}"] }
|
171
|
+
end
|
172
|
+
exts.each do |ext|
|
173
|
+
settings.template_engines[ext].each { |e| possible << [e, name] }
|
174
|
+
end
|
175
|
+
possible.each do |engine, template|
|
176
|
+
# not exactly like Tilt[engine], but does not trigger a require
|
177
|
+
klass = Tilt.mappings[Tilt.normalize(engine)].first
|
178
|
+
find_template(settings.views, template, klass) do |file|
|
179
|
+
next unless File.exist? file
|
180
|
+
return settings.rendering_method(engine) << template.to_sym
|
181
|
+
end
|
182
|
+
end
|
183
|
+
[] # nil or false would not be cached
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
attr_accessor :ext_map
|
188
|
+
|
189
|
+
def remap_extensions
|
190
|
+
ext_map.clear
|
191
|
+
Rack::Mime::MIME_TYPES.each { |e,t| ext_map[t] << e[1..-1].to_sym }
|
192
|
+
ext_map['text/javascript'] << 'js'
|
193
|
+
ext_map['text/xml'] << 'xml'
|
194
|
+
end
|
195
|
+
|
196
|
+
def mime_type(*)
|
197
|
+
result = super
|
198
|
+
remap_extensions
|
199
|
+
result
|
200
|
+
end
|
201
|
+
|
202
|
+
def respond_to(*formats)
|
203
|
+
if formats.any?
|
204
|
+
@respond_to ||= []
|
205
|
+
@respond_to.concat formats
|
206
|
+
elsif @respond_to.nil? and superclass.respond_to? :respond_to
|
207
|
+
superclass.respond_to
|
208
|
+
else
|
209
|
+
@respond_to
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def rendering_method(engine)
|
214
|
+
return [engine] if Sinatra::Templates.method_defined? engine
|
215
|
+
return [:mab] if engine.to_sym == :markaby
|
216
|
+
[:render, :engine]
|
217
|
+
end
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
def compile!(verb, path, block, options = {})
|
222
|
+
options[:provides] ||= respond_to if respond_to
|
223
|
+
super
|
224
|
+
end
|
225
|
+
|
226
|
+
ENGINES = {
|
227
|
+
:css => [:less, :sass, :scss],
|
228
|
+
:xml => [:builder, :nokogiri],
|
229
|
+
:js => [:coffee],
|
230
|
+
:json => [:yajl],
|
231
|
+
:html => [:erb, :erubis, :haml, :slim, :liquid, :radius, :mab, :markdown,
|
232
|
+
:textile, :rdoc],
|
233
|
+
:all => Sinatra::Templates.instance_methods.map(&:to_sym) + [:mab] -
|
234
|
+
[:find_template, :markaby]
|
235
|
+
}
|
236
|
+
|
237
|
+
ENGINES.default = []
|
238
|
+
|
239
|
+
def self.registered(base)
|
240
|
+
base.ext_map = Hash.new { |h,k| h[k] = [] }
|
241
|
+
base.set :template_engines, ENGINES.dup
|
242
|
+
base.remap_extensions
|
243
|
+
base.helpers Helpers
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
register RespondWith
|
248
|
+
Delegator.delegate :respond_to
|
249
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'backports'
|
4
|
+
|
5
|
+
module Sinatra
|
6
|
+
|
7
|
+
# = Sinatra::Streaming
|
8
|
+
#
|
9
|
+
# Sinatra 1.3 introduced the +stream+ helper. This addon improves the
|
10
|
+
# streaming API by making the stream object immitate an IO object, turning
|
11
|
+
# it into a real Deferrable and making the body play nicer with middleware
|
12
|
+
# unaware of streaming.
|
13
|
+
#
|
14
|
+
# == IO-like behavior
|
15
|
+
#
|
16
|
+
# This is useful when passing the stream object to a library expecting an
|
17
|
+
# IO or StringIO object.
|
18
|
+
#
|
19
|
+
# get '/' do
|
20
|
+
# stream do |out|
|
21
|
+
# out.puts "Hello World!", "How are you?"
|
22
|
+
# out.write "Written #{out.pos} bytes so far!\n"
|
23
|
+
# out.putc(65) unless out.closed?
|
24
|
+
# out.flush
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# == Proper Deferrable
|
29
|
+
#
|
30
|
+
# Handy when using EventMachine.
|
31
|
+
#
|
32
|
+
# list = []
|
33
|
+
#
|
34
|
+
# get '/' do
|
35
|
+
# stream(:keep_open) do |out|
|
36
|
+
# list << out
|
37
|
+
# out.callback { list.delete out }
|
38
|
+
# out.errback do
|
39
|
+
# logger.warn "lost connection"
|
40
|
+
# list.delete out
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# == Better Middleware Handling
|
46
|
+
#
|
47
|
+
# Blocks passed to #map! or #map will actually be applied when streaming
|
48
|
+
# takes place (as you might have suspected, #map! applies modifications
|
49
|
+
# to the current body, while #map creates a new one):
|
50
|
+
#
|
51
|
+
# class StupidMiddleware
|
52
|
+
# def initialize(app) @app = app end
|
53
|
+
#
|
54
|
+
# def call(env)
|
55
|
+
# status, headers, body = @app.call(env)
|
56
|
+
# body.map! { |e| e.upcase }
|
57
|
+
# [status, headers, body]
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# use StupidMiddleware
|
62
|
+
#
|
63
|
+
# get '/' do
|
64
|
+
# stream do |out|
|
65
|
+
# out.puts "still"
|
66
|
+
# sleep 1
|
67
|
+
# out.puts "streaming"
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# Even works if #each is used to generate an Enumerator:
|
72
|
+
#
|
73
|
+
# def call(env)
|
74
|
+
# status, headers, body = @app.call(env)
|
75
|
+
# body = body.each.map { |s| s.upcase }
|
76
|
+
# [status, headers, body]
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# Note that both examples violate the Rack specification.
|
80
|
+
#
|
81
|
+
# == Setup
|
82
|
+
#
|
83
|
+
# In a classic application:
|
84
|
+
#
|
85
|
+
# require "sinatra"
|
86
|
+
# require "sinatra/streaming"
|
87
|
+
#
|
88
|
+
# In a modular application:
|
89
|
+
#
|
90
|
+
# require "sinatra/base"
|
91
|
+
# require "sinatra/streaming"
|
92
|
+
#
|
93
|
+
# class MyApp < Sinatra::Base
|
94
|
+
# helpers Sinatra::Streaming
|
95
|
+
# end
|
96
|
+
module Streaming
|
97
|
+
def stream(*)
|
98
|
+
stream = super
|
99
|
+
stream.extend Stream
|
100
|
+
stream.app = self
|
101
|
+
env['async.close'].callback { stream.close } if env.key? 'async.close'
|
102
|
+
stream
|
103
|
+
end
|
104
|
+
|
105
|
+
module Stream
|
106
|
+
include EventMachine::Deferrable
|
107
|
+
|
108
|
+
attr_accessor :app, :lineno, :pos, :transformer, :closed
|
109
|
+
alias tell pos
|
110
|
+
alias closed? closed
|
111
|
+
|
112
|
+
def self.extended(obj)
|
113
|
+
obj.closed, obj.lineno, obj.pos = false, 0, 0
|
114
|
+
obj.callback { obj.closed = true }
|
115
|
+
obj.errback { obj.closed = true }
|
116
|
+
end
|
117
|
+
|
118
|
+
def <<(data)
|
119
|
+
raise IOError, 'not opened for writing' if closed?
|
120
|
+
data = data.to_s
|
121
|
+
data = @transformer[data] if @transformer
|
122
|
+
@pos += data.bytesize
|
123
|
+
super(data)
|
124
|
+
end
|
125
|
+
|
126
|
+
def each
|
127
|
+
# that way body.each.map { ... } works
|
128
|
+
return self unless block_given?
|
129
|
+
super
|
130
|
+
end
|
131
|
+
|
132
|
+
def map(&block)
|
133
|
+
# dup would not copy the mixin
|
134
|
+
clone.map!(&block)
|
135
|
+
end
|
136
|
+
|
137
|
+
def map!(&block)
|
138
|
+
if @transformer
|
139
|
+
inner, outer = @transformer, block
|
140
|
+
block = proc { |value| outer[inner[value]] }
|
141
|
+
end
|
142
|
+
@transformer = block
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def write(data)
|
147
|
+
self << data
|
148
|
+
data.to_s.bytesize
|
149
|
+
end
|
150
|
+
|
151
|
+
alias syswrite write
|
152
|
+
alias write_nonblock write
|
153
|
+
|
154
|
+
def print(*args)
|
155
|
+
args.each { |arg| self << arg }
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
159
|
+
def printf(format, *args)
|
160
|
+
print(format.to_s % args)
|
161
|
+
end
|
162
|
+
|
163
|
+
def putc(c)
|
164
|
+
print c.chr
|
165
|
+
end
|
166
|
+
|
167
|
+
def puts(*args)
|
168
|
+
args.each { |arg| self << "#{arg}\n" }
|
169
|
+
nil
|
170
|
+
end
|
171
|
+
|
172
|
+
def close
|
173
|
+
@scheduler.schedule { succeed }
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
|
177
|
+
def close_read
|
178
|
+
raise IOError, "closing non-duplex IO for reading"
|
179
|
+
end
|
180
|
+
|
181
|
+
def closed_read?
|
182
|
+
true
|
183
|
+
end
|
184
|
+
|
185
|
+
def closed_write?
|
186
|
+
closed?
|
187
|
+
end
|
188
|
+
|
189
|
+
def external_encoding
|
190
|
+
Encoding.find settings.default_encoding
|
191
|
+
rescue NameError
|
192
|
+
settings.default_encoding
|
193
|
+
end
|
194
|
+
|
195
|
+
def closed?
|
196
|
+
@closed
|
197
|
+
end
|
198
|
+
|
199
|
+
def settings
|
200
|
+
app.settings
|
201
|
+
end
|
202
|
+
|
203
|
+
def rewind
|
204
|
+
@pos = @lineno = 0
|
205
|
+
end
|
206
|
+
|
207
|
+
def not_open_for_reading(*)
|
208
|
+
raise IOError, "not opened for reading"
|
209
|
+
end
|
210
|
+
|
211
|
+
alias bytes not_open_for_reading
|
212
|
+
alias eof? not_open_for_reading
|
213
|
+
alias eof not_open_for_reading
|
214
|
+
alias getbyte not_open_for_reading
|
215
|
+
alias getc not_open_for_reading
|
216
|
+
alias gets not_open_for_reading
|
217
|
+
alias read not_open_for_reading
|
218
|
+
alias read_nonblock not_open_for_reading
|
219
|
+
alias readbyte not_open_for_reading
|
220
|
+
alias readchar not_open_for_reading
|
221
|
+
alias readline not_open_for_reading
|
222
|
+
alias readlines not_open_for_reading
|
223
|
+
alias readpartial not_open_for_reading
|
224
|
+
alias sysread not_open_for_reading
|
225
|
+
alias ungetbyte not_open_for_reading
|
226
|
+
alias ungetc not_open_for_reading
|
227
|
+
private :not_open_for_reading
|
228
|
+
|
229
|
+
def enum_not_open_for_reading(*)
|
230
|
+
not_open_for_reading if block_given?
|
231
|
+
enum_for(:not_open_for_reading)
|
232
|
+
end
|
233
|
+
|
234
|
+
alias chars enum_not_open_for_reading
|
235
|
+
alias each_line enum_not_open_for_reading
|
236
|
+
alias each_byte enum_not_open_for_reading
|
237
|
+
alias each_char enum_not_open_for_reading
|
238
|
+
alias lines enum_not_open_for_reading
|
239
|
+
undef enum_not_open_for_reading
|
240
|
+
|
241
|
+
def dummy(*) end
|
242
|
+
alias flush dummy
|
243
|
+
alias fsync dummy
|
244
|
+
alias internal_encoding dummy
|
245
|
+
alias pid dummy
|
246
|
+
undef dummy
|
247
|
+
|
248
|
+
def seek(*)
|
249
|
+
0
|
250
|
+
end
|
251
|
+
|
252
|
+
alias sysseek seek
|
253
|
+
|
254
|
+
def sync
|
255
|
+
true
|
256
|
+
end
|
257
|
+
|
258
|
+
def tty?
|
259
|
+
false
|
260
|
+
end
|
261
|
+
|
262
|
+
alias isatty tty?
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
helpers Streaming
|
267
|
+
end
|