ruby_llm 1.6.3 → 1.7.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.
- checksums.yaml +4 -4
- data/README.md +6 -3
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +115 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +10 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +30 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
- data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +4 -4
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +8 -7
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +43 -0
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +6 -5
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +10 -4
- data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -3
- data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +3 -0
- data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/install_generator.rb +129 -33
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +137 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb +160 -0
- data/lib/ruby_llm/active_record/acts_as.rb +112 -319
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +398 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +336 -0
- data/lib/ruby_llm/active_record/message_methods.rb +72 -0
- data/lib/ruby_llm/active_record/model_methods.rb +84 -0
- data/lib/ruby_llm/aliases.json +58 -13
- data/lib/ruby_llm/attachment.rb +20 -0
- data/lib/ruby_llm/chat.rb +8 -7
- data/lib/ruby_llm/configuration.rb +9 -0
- data/lib/ruby_llm/connection.rb +4 -4
- data/lib/ruby_llm/model/info.rb +12 -0
- data/lib/ruby_llm/models.json +3579 -2029
- data/lib/ruby_llm/models.rb +51 -22
- data/lib/ruby_llm/provider.rb +3 -3
- data/lib/ruby_llm/providers/anthropic/chat.rb +2 -2
- data/lib/ruby_llm/providers/anthropic/media.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/tools.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/chat.rb +2 -2
- data/lib/ruby_llm/providers/bedrock/models.rb +19 -1
- data/lib/ruby_llm/providers/gemini/chat.rb +53 -25
- data/lib/ruby_llm/providers/gemini/media.rb +1 -1
- data/lib/ruby_llm/providers/gpustack/chat.rb +11 -0
- data/lib/ruby_llm/providers/gpustack/media.rb +45 -0
- data/lib/ruby_llm/providers/gpustack/models.rb +44 -8
- data/lib/ruby_llm/providers/gpustack.rb +1 -0
- data/lib/ruby_llm/providers/ollama/media.rb +2 -6
- data/lib/ruby_llm/providers/ollama/models.rb +36 -0
- data/lib/ruby_llm/providers/ollama.rb +1 -0
- data/lib/ruby_llm/providers/openai/chat.rb +1 -1
- data/lib/ruby_llm/providers/openai/media.rb +4 -4
- data/lib/ruby_llm/providers/openai/tools.rb +11 -6
- data/lib/ruby_llm/providers/openai.rb +2 -2
- data/lib/ruby_llm/providers/vertexai/chat.rb +14 -0
- data/lib/ruby_llm/providers/vertexai/embeddings.rb +32 -0
- data/lib/ruby_llm/providers/vertexai/models.rb +130 -0
- data/lib/ruby_llm/providers/vertexai/streaming.rb +14 -0
- data/lib/ruby_llm/providers/vertexai.rb +55 -0
- data/lib/ruby_llm/railtie.rb +20 -3
- data/lib/ruby_llm/streaming.rb +1 -1
- data/lib/ruby_llm/utils.rb +5 -9
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +4 -3
- data/lib/tasks/models.rake +525 -0
- data/lib/tasks/release.rake +37 -2
- data/lib/tasks/ruby_llm.rake +15 -0
- data/lib/tasks/vcr.rake +2 -2
- metadata +37 -5
- data/lib/generators/ruby_llm/install/templates/INSTALL_INFO.md.tt +0 -108
- data/lib/tasks/aliases.rake +0 -205
- data/lib/tasks/models_docs.rake +0 -214
- data/lib/tasks/models_update.rake +0 -108
@@ -6,369 +6,162 @@ module RubyLLM
|
|
6
6
|
module ActsAs
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
# When ActsAs is included, ensure models are loaded from database
|
10
|
+
def self.included(base)
|
11
|
+
super
|
12
|
+
# Monkey-patch Models to use database when ActsAs is active
|
13
|
+
RubyLLM::Models.class_eval do
|
14
|
+
def load_models
|
15
|
+
read_from_database
|
16
|
+
rescue StandardError => e
|
17
|
+
RubyLLM.logger.debug "Failed to load models from database: #{e.message}, falling back to JSON"
|
18
|
+
read_from_json
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
inverse_of: :chat,
|
20
|
-
dependent: :destroy
|
21
|
+
def load_from_database!
|
22
|
+
@models = read_from_database
|
23
|
+
end
|
21
24
|
|
22
|
-
|
25
|
+
def read_from_database
|
26
|
+
model_class = RubyLLM.config.model_registry_class
|
27
|
+
model_class = model_class.constantize if model_class.is_a?(String)
|
28
|
+
model_class.all.map(&:to_llm)
|
29
|
+
end
|
23
30
|
end
|
31
|
+
end
|
24
32
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
touch_chat: false)
|
30
|
-
include MessageMethods
|
31
|
-
|
32
|
-
@chat_class = chat_class.to_s
|
33
|
-
@chat_foreign_key = chat_foreign_key || ActiveSupport::Inflector.foreign_key(@chat_class)
|
33
|
+
class_methods do # rubocop:disable Metrics/BlockLength
|
34
|
+
def acts_as_chat(messages: :messages, message_class: nil,
|
35
|
+
model: :model, model_class: nil)
|
36
|
+
include RubyLLM::ActiveRecord::ChatMethods
|
34
37
|
|
35
|
-
|
36
|
-
@tool_call_foreign_key = tool_call_foreign_key || ActiveSupport::Inflector.foreign_key(@tool_call_class)
|
38
|
+
class_attribute :messages_association_name, :model_association_name, :message_class, :model_class
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
touch: touch_chat
|
40
|
+
self.messages_association_name = messages
|
41
|
+
self.model_association_name = model
|
42
|
+
self.message_class = (message_class || messages.to_s.classify).to_s
|
43
|
+
self.model_class = (model_class || model.to_s.classify).to_s
|
43
44
|
|
44
|
-
has_many
|
45
|
-
|
45
|
+
has_many messages,
|
46
|
+
-> { order(created_at: :asc) },
|
47
|
+
class_name: self.message_class,
|
46
48
|
dependent: :destroy
|
47
49
|
|
48
|
-
belongs_to
|
49
|
-
class_name:
|
50
|
-
|
51
|
-
optional: true,
|
52
|
-
inverse_of: :result
|
53
|
-
|
54
|
-
delegate :tool_call?, :tool_result?, :tool_results, to: :to_llm
|
55
|
-
end
|
56
|
-
|
57
|
-
def acts_as_tool_call(message_class: 'Message', message_foreign_key: nil, result_foreign_key: nil)
|
58
|
-
@message_class = message_class.to_s
|
59
|
-
@message_foreign_key = message_foreign_key || ActiveSupport::Inflector.foreign_key(@message_class)
|
60
|
-
@result_foreign_key = result_foreign_key || ActiveSupport::Inflector.foreign_key(name)
|
61
|
-
|
62
|
-
belongs_to :message,
|
63
|
-
class_name: @message_class,
|
64
|
-
foreign_key: @message_foreign_key,
|
65
|
-
inverse_of: :tool_calls
|
66
|
-
|
67
|
-
has_one :result,
|
68
|
-
class_name: @message_class,
|
69
|
-
foreign_key: @result_foreign_key,
|
70
|
-
inverse_of: :parent_tool_call,
|
71
|
-
dependent: :nullify
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Methods mixed into chat models.
|
77
|
-
module ChatMethods
|
78
|
-
extend ActiveSupport::Concern
|
79
|
-
|
80
|
-
class_methods do
|
81
|
-
attr_reader :tool_call_class
|
82
|
-
end
|
50
|
+
belongs_to model,
|
51
|
+
class_name: self.model_class,
|
52
|
+
optional: true
|
83
53
|
|
84
|
-
|
85
|
-
@chat ||= if context
|
86
|
-
context.chat(model: model_id)
|
87
|
-
else
|
88
|
-
RubyLLM.chat(model: model_id)
|
89
|
-
end
|
90
|
-
@chat.reset_messages!
|
54
|
+
delegate :add_message, to: :to_llm
|
91
55
|
|
92
|
-
|
93
|
-
|
94
|
-
|
56
|
+
define_method :messages_association do
|
57
|
+
send(messages_association_name)
|
58
|
+
end
|
95
59
|
|
96
|
-
|
97
|
-
|
60
|
+
define_method :model_association do
|
61
|
+
send(model_association_name)
|
62
|
+
end
|
98
63
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
messages.create!(role: :system, content: instructions)
|
64
|
+
define_method :'model_association=' do |value|
|
65
|
+
send("#{model_association_name}=", value)
|
66
|
+
end
|
103
67
|
end
|
104
|
-
to_llm.with_instructions(instructions)
|
105
|
-
self
|
106
|
-
end
|
107
|
-
|
108
|
-
def with_tool(...)
|
109
|
-
to_llm.with_tool(...)
|
110
|
-
self
|
111
|
-
end
|
112
|
-
|
113
|
-
def with_tools(...)
|
114
|
-
to_llm.with_tools(...)
|
115
|
-
self
|
116
|
-
end
|
117
|
-
|
118
|
-
def with_model(...)
|
119
|
-
update(model_id: to_llm.with_model(...).model.id)
|
120
|
-
self
|
121
|
-
end
|
122
|
-
|
123
|
-
def with_temperature(...)
|
124
|
-
to_llm.with_temperature(...)
|
125
|
-
self
|
126
|
-
end
|
127
68
|
|
128
|
-
|
129
|
-
|
130
|
-
self
|
131
|
-
end
|
69
|
+
def acts_as_model(chats: :chats, chat_class: nil)
|
70
|
+
include RubyLLM::ActiveRecord::ModelMethods
|
132
71
|
|
133
|
-
|
134
|
-
to_llm.with_params(...)
|
135
|
-
self
|
136
|
-
end
|
137
|
-
|
138
|
-
def with_headers(...)
|
139
|
-
to_llm.with_headers(...)
|
140
|
-
self
|
141
|
-
end
|
72
|
+
class_attribute :chats_association_name, :chat_class
|
142
73
|
|
143
|
-
|
144
|
-
|
145
|
-
self
|
146
|
-
end
|
147
|
-
|
148
|
-
def on_new_message(&block)
|
149
|
-
to_llm
|
150
|
-
|
151
|
-
existing_callback = @chat.instance_variable_get(:@on)[:new_message]
|
152
|
-
|
153
|
-
@chat.on_new_message do
|
154
|
-
existing_callback&.call
|
155
|
-
block&.call
|
156
|
-
end
|
157
|
-
self
|
158
|
-
end
|
74
|
+
self.chats_association_name = chats
|
75
|
+
self.chat_class = (chat_class || chats.to_s.classify).to_s
|
159
76
|
|
160
|
-
|
161
|
-
|
77
|
+
validates :model_id, presence: true, uniqueness: { scope: :provider }
|
78
|
+
validates :provider, presence: true
|
79
|
+
validates :name, presence: true
|
162
80
|
|
163
|
-
|
81
|
+
has_many chats, class_name: self.chat_class
|
164
82
|
|
165
|
-
|
166
|
-
|
167
|
-
|
83
|
+
define_method :chats_association do
|
84
|
+
send(chats_association_name)
|
85
|
+
end
|
168
86
|
end
|
169
|
-
self
|
170
|
-
end
|
171
87
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
88
|
+
def acts_as_message(chat: :chat, chat_class: nil, touch_chat: false, # rubocop:disable Metrics/ParameterLists
|
89
|
+
tool_calls: :tool_calls, tool_call_class: nil,
|
90
|
+
model: :model, model_class: nil)
|
91
|
+
include RubyLLM::ActiveRecord::MessageMethods
|
176
92
|
|
177
|
-
|
178
|
-
|
179
|
-
self
|
180
|
-
end
|
93
|
+
class_attribute :chat_association_name, :tool_calls_association_name, :model_association_name,
|
94
|
+
:chat_class, :tool_call_class, :model_class
|
181
95
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
def ask(message, with: nil, &)
|
189
|
-
create_user_message(message, with:)
|
190
|
-
complete(&)
|
191
|
-
end
|
96
|
+
self.chat_association_name = chat
|
97
|
+
self.tool_calls_association_name = tool_calls
|
98
|
+
self.model_association_name = model
|
99
|
+
self.chat_class = (chat_class || chat.to_s.classify).to_s
|
100
|
+
self.tool_call_class = (tool_call_class || tool_calls.to_s.classify).to_s
|
101
|
+
self.model_class = (model_class || model.to_s.classify).to_s
|
192
102
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
to_llm.complete(...)
|
197
|
-
rescue RubyLLM::Error => e
|
198
|
-
cleanup_failed_messages if @message&.persisted? && @message.content.blank?
|
199
|
-
cleanup_orphaned_tool_results
|
200
|
-
raise e
|
201
|
-
end
|
202
|
-
|
203
|
-
private
|
204
|
-
|
205
|
-
def cleanup_failed_messages
|
206
|
-
RubyLLM.logger.debug "RubyLLM: API call failed, destroying message: #{@message.id}"
|
207
|
-
@message.destroy
|
208
|
-
end
|
103
|
+
belongs_to chat,
|
104
|
+
class_name: self.chat_class,
|
105
|
+
touch: touch_chat
|
209
106
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
last = messages.order(:id).last
|
107
|
+
has_many tool_calls,
|
108
|
+
class_name: self.tool_call_class,
|
109
|
+
dependent: :destroy
|
214
110
|
|
215
|
-
|
111
|
+
belongs_to :parent_tool_call,
|
112
|
+
class_name: self.tool_call_class,
|
113
|
+
foreign_key: ActiveSupport::Inflector.foreign_key(tool_calls.to_s.singularize),
|
114
|
+
optional: true
|
216
115
|
|
217
|
-
|
218
|
-
|
219
|
-
|
116
|
+
has_many :tool_results,
|
117
|
+
through: tool_calls,
|
118
|
+
source: :result,
|
119
|
+
class_name: name
|
220
120
|
|
221
|
-
|
222
|
-
|
121
|
+
belongs_to model,
|
122
|
+
class_name: self.model_class,
|
123
|
+
optional: true
|
223
124
|
|
224
|
-
|
225
|
-
@chat.on_end_message { |msg| persist_message_completion(msg) }
|
125
|
+
delegate :tool_call?, :tool_result?, to: :to_llm
|
226
126
|
|
227
|
-
|
228
|
-
|
229
|
-
|
127
|
+
define_method :chat_association do
|
128
|
+
send(chat_association_name)
|
129
|
+
end
|
230
130
|
|
231
|
-
|
232
|
-
|
233
|
-
|
131
|
+
define_method :tool_calls_association do
|
132
|
+
send(tool_calls_association_name)
|
133
|
+
end
|
234
134
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
tool_call_id = find_tool_call_id(message.tool_call_id) if message.tool_call_id
|
239
|
-
|
240
|
-
transaction do
|
241
|
-
content = message.content
|
242
|
-
content = content.to_json if content.is_a?(Hash) || content.is_a?(Array)
|
243
|
-
|
244
|
-
@message.update!(
|
245
|
-
role: message.role,
|
246
|
-
content: content,
|
247
|
-
model_id: message.model_id,
|
248
|
-
input_tokens: message.input_tokens,
|
249
|
-
output_tokens: message.output_tokens
|
250
|
-
)
|
251
|
-
@message.write_attribute(@message.class.tool_call_foreign_key, tool_call_id) if tool_call_id
|
252
|
-
@message.save!
|
253
|
-
persist_tool_calls(message.tool_calls) if message.tool_calls.present?
|
135
|
+
define_method :model_association do
|
136
|
+
send(model_association_name)
|
137
|
+
end
|
254
138
|
end
|
255
|
-
end
|
256
139
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
attributes[:tool_call_id] = attributes.delete(:id)
|
261
|
-
@message.tool_calls.create!(**attributes)
|
262
|
-
end
|
263
|
-
end
|
140
|
+
def acts_as_tool_call(message: :message, message_class: nil,
|
141
|
+
result: :result, result_class: nil)
|
142
|
+
class_attribute :message_association_name, :result_association_name, :message_class, :result_class
|
264
143
|
|
265
|
-
|
266
|
-
|
267
|
-
|
144
|
+
self.message_association_name = message
|
145
|
+
self.result_association_name = result
|
146
|
+
self.message_class = (message_class || message.to_s.classify).to_s
|
147
|
+
self.result_class = (result_class || self.message_class).to_s
|
268
148
|
|
269
|
-
|
270
|
-
|
149
|
+
belongs_to message,
|
150
|
+
class_name: self.message_class
|
271
151
|
|
272
|
-
|
273
|
-
|
274
|
-
|
152
|
+
has_one result,
|
153
|
+
class_name: self.result_class,
|
154
|
+
dependent: :nullify
|
275
155
|
|
276
|
-
|
277
|
-
|
278
|
-
case attachment
|
279
|
-
when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob
|
280
|
-
attachment
|
281
|
-
when ActiveStorage::Attached::One, ActiveStorage::Attached::Many
|
282
|
-
attachment.blobs
|
283
|
-
when Hash
|
284
|
-
attachment.values.map { |v| prepare_for_active_storage(v) }
|
285
|
-
else
|
286
|
-
convert_to_active_storage_format(attachment)
|
156
|
+
define_method :message_association do
|
157
|
+
send(message_association_name)
|
287
158
|
end
|
288
|
-
end.flatten.compact
|
289
|
-
end
|
290
|
-
|
291
|
-
def convert_to_active_storage_format(source)
|
292
|
-
return if source.blank?
|
293
159
|
|
294
|
-
|
295
|
-
|
296
|
-
{
|
297
|
-
io: StringIO.new(attachment.content),
|
298
|
-
filename: attachment.filename,
|
299
|
-
content_type: attachment.mime_type
|
300
|
-
}
|
301
|
-
rescue StandardError => e
|
302
|
-
RubyLLM.logger.warn "Failed to process attachment #{source}: #{e.message}"
|
303
|
-
nil
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
# Methods mixed into message models.
|
308
|
-
module MessageMethods
|
309
|
-
extend ActiveSupport::Concern
|
310
|
-
|
311
|
-
class_methods do
|
312
|
-
attr_reader :chat_class, :tool_call_class, :chat_foreign_key, :tool_call_foreign_key
|
313
|
-
end
|
314
|
-
|
315
|
-
def to_llm
|
316
|
-
RubyLLM::Message.new(
|
317
|
-
role: role.to_sym,
|
318
|
-
content: extract_content,
|
319
|
-
tool_calls: extract_tool_calls,
|
320
|
-
tool_call_id: extract_tool_call_id,
|
321
|
-
input_tokens: input_tokens,
|
322
|
-
output_tokens: output_tokens,
|
323
|
-
model_id: model_id
|
324
|
-
)
|
325
|
-
end
|
326
|
-
|
327
|
-
private
|
328
|
-
|
329
|
-
def extract_tool_calls
|
330
|
-
tool_calls.to_h do |tool_call|
|
331
|
-
[
|
332
|
-
tool_call.tool_call_id,
|
333
|
-
RubyLLM::ToolCall.new(
|
334
|
-
id: tool_call.tool_call_id,
|
335
|
-
name: tool_call.name,
|
336
|
-
arguments: tool_call.arguments
|
337
|
-
)
|
338
|
-
]
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
def extract_tool_call_id
|
343
|
-
parent_tool_call&.tool_call_id
|
344
|
-
end
|
345
|
-
|
346
|
-
def extract_content
|
347
|
-
return content unless respond_to?(:attachments) && attachments.attached?
|
348
|
-
|
349
|
-
RubyLLM::Content.new(content).tap do |content_obj|
|
350
|
-
@_tempfiles = []
|
351
|
-
|
352
|
-
attachments.each do |attachment|
|
353
|
-
tempfile = download_attachment(attachment)
|
354
|
-
content_obj.add_attachment(tempfile, filename: attachment.filename.to_s)
|
160
|
+
define_method :result_association do
|
161
|
+
send(result_association_name)
|
355
162
|
end
|
356
163
|
end
|
357
164
|
end
|
358
|
-
|
359
|
-
def download_attachment(attachment)
|
360
|
-
ext = File.extname(attachment.filename.to_s)
|
361
|
-
basename = File.basename(attachment.filename.to_s, ext)
|
362
|
-
tempfile = Tempfile.new([basename, ext])
|
363
|
-
tempfile.binmode
|
364
|
-
|
365
|
-
attachment.download { |chunk| tempfile.write(chunk) }
|
366
|
-
|
367
|
-
tempfile.flush
|
368
|
-
tempfile.rewind
|
369
|
-
@_tempfiles << tempfile
|
370
|
-
tempfile
|
371
|
-
end
|
372
165
|
end
|
373
166
|
end
|
374
167
|
end
|