td 0.7.5 → 0.8.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/ChangeLog CHANGED
@@ -1,4 +1,9 @@
1
1
 
2
+ == 2011-08-21 version 0.8.0
3
+
4
+ * Splits API libraries to td-client gem
5
+
6
+
2
7
  == 2011-08-18 version 0.7.5
3
8
 
4
9
  * set-schema: adds column instead of replacing all columns
@@ -1,7 +1,7 @@
1
1
 
2
2
  module TreasureData
3
3
 
4
- autoload :API, 'td/api'
4
+ autoload :API, 'td/client/api'
5
5
  autoload :Client, 'td/client'
6
6
  autoload :Database, 'td/client'
7
7
  autoload :Table, 'td/client'
data/lib/td/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module TreasureData
2
2
 
3
- VERSION = '0.7.5'
3
+ VERSION = '0.8.0'
4
4
 
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: td
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 63
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 7
9
- - 5
10
- version: 0.7.5
8
+ - 8
9
+ - 0
10
+ version: 0.8.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sadayuki Furuhashi
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-18 00:00:00 +09:00
18
+ date: 2011-08-21 00:00:00 +09:00
19
19
  default_executable: td
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -66,6 +66,22 @@ dependencies:
66
66
  version: 0.4.5
67
67
  type: :runtime
68
68
  version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: td-client
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 63
78
+ segments:
79
+ - 0
80
+ - 8
81
+ - 0
82
+ version: 0.8.0
83
+ type: :runtime
84
+ version_requirements: *id004
69
85
  description:
70
86
  email:
71
87
  executables:
@@ -76,8 +92,6 @@ extra_rdoc_files:
76
92
  - ChangeLog
77
93
  - README.rdoc
78
94
  files:
79
- - lib/td/api.rb
80
- - lib/td/client.rb
81
95
  - lib/td/command/account.rb
82
96
  - lib/td/command/common.rb
83
97
  - lib/td/command/database.rb
data/lib/td/api.rb DELETED
@@ -1,455 +0,0 @@
1
-
2
- module TreasureData
3
-
4
-
5
- class APIError < StandardError
6
- end
7
-
8
- class AuthError < APIError
9
- end
10
-
11
- class AlreadyExistsError < APIError
12
- end
13
-
14
- class NotFoundError < APIError
15
- end
16
-
17
-
18
- class API
19
- def initialize(apikey)
20
- require 'json'
21
- @apikey = apikey
22
- end
23
-
24
- # TODO error check & raise appropriate errors
25
-
26
- attr_reader :apikey
27
-
28
- def self.validate_database_name(name)
29
- name = name.to_s
30
- if name.empty?
31
- raise "Empty name is not allowed"
32
- end
33
- if name.length < 3 || 32 < name.length
34
- raise "Name must be 3 to 32 characters, got #{name.length} characters."
35
- end
36
- unless name =~ /^([a-z0-9_]+)$/
37
- raise "Name must consist only of alphabets, numbers, '_'."
38
- end
39
- name
40
- end
41
-
42
- def self.validate_table_name(name)
43
- validate_database_name(name)
44
- end
45
-
46
- def self.validate_column_name(name)
47
- name = name.to_s
48
- if name.empty?
49
- raise "Empty column name is not allowed"
50
- end
51
- if 32 < name.length
52
- raise "Column name must be to 32 characters, got #{name.length} characters."
53
- end
54
- unless name =~ /^([a-z0-9_]+)$/
55
- raise "Column name must consist only of alphabets, numbers, '_'."
56
- end
57
- end
58
-
59
- def self.normalize_type_name(name)
60
- case name
61
- when /int/i, /integer/i
62
- "int"
63
- when /long/i, /bigint/i
64
- "long"
65
- when /string/i
66
- "string"
67
- when /float/i
68
- "float"
69
- when /double/i
70
- "double"
71
- else
72
- raise "Type name must eather of int, long, string float or double"
73
- end
74
- end
75
-
76
- ####
77
- ## Database API
78
- ##
79
-
80
- # => [name:String]
81
- def list_databases
82
- code, body, res = get("/v3/database/list")
83
- if code != "200"
84
- raise_error("List databases failed", res)
85
- end
86
- # TODO format check
87
- js = JSON.load(body)
88
- names = js["databases"].map {|dbinfo| dbinfo['name'] }
89
- return names
90
- end
91
-
92
- # => true
93
- def delete_database(db)
94
- code, body, res = post("/v3/database/delete/#{e db}")
95
- if code != "200"
96
- raise_error("Delete database failed", res)
97
- end
98
- return true
99
- end
100
-
101
- # => true
102
- def create_database(db)
103
- code, body, res = post("/v3/database/create/#{e db}")
104
- if code != "200"
105
- raise_error("Create database failed", res)
106
- end
107
- return true
108
- end
109
-
110
-
111
- ####
112
- ## Table API
113
- ##
114
-
115
- # => {name:String => [type:Symbol, count:Integer]}
116
- def list_tables(db)
117
- code, body, res = get("/v3/table/list/#{e db}")
118
- if code != "200"
119
- raise_error("List tables failed", res)
120
- end
121
- # TODO format check
122
- js = JSON.load(body)
123
- result = {}
124
- js["tables"].map {|m|
125
- name = m['name']
126
- type = (m['type'] || '?').to_sym
127
- count = (m['count'] || 0).to_i # TODO?
128
- schema = JSON.parse(m['schema'] || '[]')
129
- result[name] = [type, schema, count]
130
- }
131
- return result
132
- end
133
-
134
- def create_log_or_item_table(db, table, type)
135
- code, body, res = post("/v3/table/create/#{e db}/#{e table}/#{type}")
136
- if code != "200"
137
- raise_error("Create #{type} table failed", res)
138
- end
139
- return true
140
- end
141
- private :create_log_or_item_table
142
-
143
- # => true
144
- def create_log_table(db, table)
145
- create_table(db, table, :log)
146
- end
147
-
148
- # => true
149
- def create_item_table(db, table)
150
- create_table(db, table, :item)
151
- end
152
-
153
- def create_table(db, table, type)
154
- schema = schema.to_s
155
- code, body, res = post("/v3/table/create/#{e db}/#{e table}/#{type}")
156
- if code != "200"
157
- raise_error("Create #{type} table failed", res)
158
- end
159
- return true
160
- end
161
- private :create_table
162
-
163
- # => true
164
- def update_schema(db, table, schema_json)
165
- code, body, res = post("/v3/table/update-schema/#{e db}/#{e table}", {'schema'=>schema_json})
166
- if code != "200"
167
- raise_error("Create schema table failed", res)
168
- end
169
- return true
170
- end
171
-
172
- # => type:Symbol
173
- def delete_table(db, table)
174
- code, body, res = post("/v3/table/delete/#{e db}/#{e table}")
175
- if code != "200"
176
- raise_error("Drop table failed", res)
177
- end
178
- # TODO format check
179
- js = JSON.load(body)
180
- type = (js['type'] || '?').to_sym
181
- return type
182
- end
183
-
184
-
185
- ####
186
- ## Job API
187
- ##
188
-
189
- # => [(jobId:String, type:Symbol, status:String, start_at:String, end_at:String)]
190
- def list_jobs(from=0, to=nil)
191
- params = {}
192
- params['from'] = from.to_s if from
193
- params['to'] = to.to_s if to
194
- code, body, res = get("/v3/job/list", params)
195
- if code != "200"
196
- raise_error("List jobs failed", res)
197
- end
198
- # TODO format check
199
- js = JSON.load(body)
200
- result = []
201
- js['jobs'].each {|m|
202
- job_id = m['job_id']
203
- type = (m['type'] || '?').to_sym
204
- status = m['status']
205
- query = m['query']
206
- start_at = m['start_at']
207
- end_at = m['end_at']
208
- result << [job_id, type, status, query, start_at, end_at]
209
- }
210
- return result
211
- end
212
-
213
- # => (type:Symbol, status:String, result:String, url:String)
214
- def show_job(job_id)
215
- code, body, res = get("/v3/job/show/#{e job_id}")
216
- if code != "200"
217
- raise_error("Show job failed", res)
218
- end
219
- # TODO format check
220
- js = JSON.load(body)
221
- # TODO debug
222
- type = (js['type'] || '?').to_sym # TODO
223
- query = js['query']
224
- status = js['status']
225
- debug = js['debug']
226
- url = js['url']
227
- start_at = js['start_at']
228
- end_at = js['end_at']
229
- return [type, query, status, url, debug, start_at, end_at]
230
- end
231
-
232
- def job_result(job_id)
233
- require 'msgpack'
234
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'})
235
- if code != "200"
236
- raise_error("Get job result failed", res)
237
- end
238
- result = []
239
- MessagePack::Unpacker.new.feed_each(body) {|row|
240
- result << row
241
- }
242
- return result
243
- end
244
-
245
- def job_result_format(job_id, format)
246
- # TODO chunked encoding
247
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format})
248
- if code != "200"
249
- raise_error("Get job result failed", res)
250
- end
251
- return body
252
- end
253
-
254
- def job_result_each(job_id, &block)
255
- # TODO chunked encoding
256
- require 'msgpack'
257
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'})
258
- if code != "200"
259
- raise_error("Get job result failed", res)
260
- end
261
- result = []
262
- MessagePack::Unpacker.new.feed_each(body) {|row|
263
- yield row
264
- }
265
- nil
266
- end
267
-
268
- def job_result_raw(job_id, format)
269
- code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format})
270
- if code != "200"
271
- raise_error("Get job result failed", res)
272
- end
273
- return body
274
- end
275
-
276
- # => jobId:String
277
- def hive_query(q, db=nil)
278
- code, body, res = post("/v3/job/issue/hive/#{e db}", {'query'=>q})
279
- if code != "200"
280
- raise_error("Query failed", res)
281
- end
282
- # TODO format check
283
- js = JSON.load(body)
284
- return js['job_id'].to_s
285
- end
286
-
287
-
288
- ####
289
- ## Import API
290
- ##
291
-
292
- # => time:Float
293
- def import(db, table, format, stream, stream_size=stream.lstat.size)
294
- code, body, res = put("/v3/table/import/#{e db}/#{e table}/#{format}", stream, stream_size)
295
- if code[0] != ?2
296
- raise_error("Import failed", res)
297
- end
298
- # TODO format check
299
- js = JSON.load(body)
300
- time = js['time'].to_f
301
- return time
302
- end
303
-
304
-
305
- ####
306
- ## User API
307
- ##
308
-
309
- # apikey:String
310
- def authenticate(user, password)
311
- code, body, res = post("/v3/user/authenticate", {'user'=>user, 'password'=>password})
312
- if code != "200"
313
- raise_error("Authentication failed", res)
314
- end
315
- # TODO format check
316
- js = JSON.load(body)
317
- apikey = js['apikey']
318
- return apikey
319
- end
320
-
321
- ####
322
- ## Server Status API
323
- ##
324
-
325
- # => status:String
326
- def server_status
327
- code, body, res = get('/v3/system/server_status')
328
- if code != "200"
329
- return "Server is down (#{code})"
330
- end
331
- # TODO format check
332
- js = JSON.load(body)
333
- status = js['status']
334
- return status
335
- end
336
-
337
- private
338
- host = 'api.treasure-data.com'
339
- port = 80
340
- if e = ENV['TD_API_SERVER']
341
- host, port_ = e.split(':',2)
342
- port_ = port_.to_i
343
- port = port_ if port_ != 0
344
- end
345
-
346
- HOST = host
347
- PORT = port
348
- USE_SSL = false
349
- BASE_URL = ''
350
-
351
- def get(url, params=nil)
352
- http, header = new_http
353
-
354
- path = BASE_URL + url
355
- if params && !params.empty?
356
- path << "?"+params.map {|k,v|
357
- "#{k}=#{e v}"
358
- }.join('&')
359
- end
360
-
361
- request = Net::HTTP::Get.new(path, header)
362
-
363
- response = http.request(request)
364
- return [response.code, response.body, response]
365
- end
366
-
367
- def post(url, params=nil)
368
- http, header = new_http
369
-
370
- path = BASE_URL + url
371
-
372
- request = Net::HTTP::Post.new(path, header)
373
- request.set_form_data(params) if params
374
-
375
- response = http.request(request)
376
- return [response.code, response.body, response]
377
- end
378
-
379
- def put(url, stream, stream_size)
380
- http, header = new_http
381
-
382
- path = BASE_URL + url
383
-
384
- header['Content-Type'] = 'application/octet-stream'
385
- header['Content-Length'] = stream_size.to_s
386
-
387
- request = Net::HTTP::Put.new(url, header)
388
- if request.respond_to?(:body_stream=)
389
- request.body_stream = stream
390
- else # Ruby 1.8
391
- request.body = stream.read
392
- end
393
-
394
- response = http.request(request)
395
- return [response.code, response.body, response]
396
- end
397
-
398
- def new_http
399
- require 'net/http'
400
- require 'time'
401
-
402
- http = Net::HTTP.new(HOST, PORT)
403
- if USE_SSL
404
- http.use_ssl = true
405
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
406
- store = OpenSSL::X509::Store.new
407
- http.cert_store = store
408
- end
409
-
410
- #http.read_timeout = options[:read_timeout]
411
-
412
- header = {}
413
- if @apikey
414
- header['Authorization'] = "TD1 #{apikey}"
415
- end
416
- header['Date'] = Time.now.rfc2822
417
-
418
- return http, header
419
- end
420
-
421
- def raise_error(msg, res)
422
- begin
423
- js = JSON.load(res.body)
424
- msg = js['message']
425
- error_code = js['error_code']
426
-
427
- if res.code == "404"
428
- raise NotFoundError, "#{error_code}: #{msg}"
429
- elsif res.code == "409"
430
- raise AlreadyExistsError, "#{error_code}: #{msg}"
431
- else
432
- raise APIError, "#{error_code}: #{msg}"
433
- end
434
-
435
- rescue
436
- if res.code == "404"
437
- raise NotFoundError, "#{msg}: #{res.body}"
438
- elsif res.code == "409"
439
- raise AlreadyExistsError, "#{msg}: #{res.body}"
440
- else
441
- raise APIError, "#{msg}: #{res.body}"
442
- end
443
- end
444
- # TODO error
445
- end
446
-
447
- def e(s)
448
- require 'cgi'
449
- CGI.escape(s.to_s)
450
- end
451
- end
452
-
453
-
454
- end
455
-
data/lib/td/client.rb DELETED
@@ -1,380 +0,0 @@
1
- require 'time'
2
- require 'td/api'
3
-
4
- module TreasureData
5
-
6
- class Client
7
- def self.authenticate(user, password)
8
- api = API.new(nil)
9
- apikey = api.authenticate(user, password)
10
- new(apikey)
11
- end
12
-
13
- def self.server_status
14
- api = API.new(nil)
15
- api.server_status
16
- end
17
-
18
- def initialize(apikey)
19
- @api = API.new(apikey)
20
- end
21
-
22
- attr_reader :api
23
-
24
- def apikey
25
- @api.apikey
26
- end
27
-
28
- def server_status
29
- @api.server_status
30
- end
31
-
32
- # => true
33
- def create_database(db_name)
34
- @api.create_database(db_name)
35
- end
36
-
37
- # => true
38
- def delete_database(db_name)
39
- @api.delete_database(db_name)
40
- end
41
-
42
- # => [Database]
43
- def databases
44
- names = @api.list_databases
45
- names.map {|db_name|
46
- Database.new(self, db_name)
47
- }
48
- end
49
-
50
- # => Database
51
- def database(db_name)
52
- names = @api.list_databases
53
- names.each {|n|
54
- if n == db_name
55
- return Database.new(self, db_name)
56
- end
57
- }
58
- raise NotFoundError, "Database '#{db_name}' does not exist"
59
- end
60
-
61
- # => true
62
- def create_log_table(db_name, table_name)
63
- @api.create_log_table(db_name, table_name)
64
- end
65
-
66
- # => true
67
- def create_item_table(db_name, table_name)
68
- @api.create_item_table(db_name, table_name)
69
- end
70
-
71
- # => true
72
- def update_schema(db_name, table_name, schema)
73
- @api.update_schema(db_name, table_name, schema.to_json)
74
- end
75
-
76
- # => type:Symbol
77
- def delete_table(db_name, table_name)
78
- @api.delete_table(db_name, table_name)
79
- end
80
-
81
- # => [Table]
82
- def tables(db_name)
83
- m = @api.list_tables(db_name)
84
- m.map {|table_name,(type,schema,count)|
85
- schema = Schema.new.from_json(schema)
86
- Table.new(self, db_name, table_name, type, schema, count)
87
- }
88
- end
89
-
90
- # => Table
91
- def table(db_name, table_name)
92
- tables(db_name).each {|t|
93
- if t.name == table_name
94
- return t
95
- end
96
- }
97
- raise NotFoundError, "Table '#{db_name}.#{table_name}' does not exist"
98
- end
99
-
100
- # => Job
101
- def query(db_name, q)
102
- job_id = @api.hive_query(q, db_name)
103
- Job.new(self, job_id, :hive, q) # TODO url
104
- end
105
-
106
- # => [Job=]
107
- def jobs(from=nil, to=nil)
108
- js = @api.list_jobs(from, to)
109
- js.map {|job_id,type,status,query,start_at,end_at|
110
- Job.new(self, job_id, type, query, status, nil, nil, start_at, end_at)
111
- }
112
- end
113
-
114
- # => Job
115
- def job(job_id)
116
- job_id = job_id.to_s
117
- type, query, status, url, debug, start_at, end_at = @api.show_job(job_id)
118
- Job.new(self, job_id, type, query, status, url, debug, start_at, end_at)
119
- end
120
-
121
- # => type:Symbol, url:String
122
- def job_status(job_id)
123
- type, query, status, url, debug, start_at, end_at = @api.show_job(job_id)
124
- return query, status, url, debug, start_at, end_at
125
- end
126
-
127
- # => result:[{column:String=>value:Object]
128
- def job_result(job_id)
129
- @api.job_result(job_id)
130
- end
131
-
132
- # => result:String
133
- def job_result_format(job_id, format)
134
- @api.job_result_format(job_id, format)
135
- end
136
-
137
- # => nil
138
- def job_result_each(job_id, &block)
139
- @api.job_result_each(job_id, &block)
140
- end
141
-
142
- # => time:Flaot
143
- def import(db_name, table_name, format, stream, stream_size=stream.lstat.size)
144
- @api.import(db_name, table_name, format, stream, stream_size)
145
- end
146
- end
147
-
148
-
149
- class Model
150
- def initialize(client)
151
- @client = client
152
- end
153
- end
154
-
155
- class Database < Model
156
- def initialize(client, db_name, tables=nil)
157
- super(client)
158
- @db_name = db_name
159
- @tables = tables
160
- end
161
-
162
- def name
163
- @db_name
164
- end
165
-
166
- def tables
167
- update_tables! unless @tables
168
- @tables
169
- end
170
-
171
- def create_log_table(name)
172
- @client.create_log_table(@db_name, name)
173
- end
174
-
175
- def create_item_table(name)
176
- @client.create_item_table(@db_name, name)
177
- end
178
-
179
- def table(table_name)
180
- @client.table(@db_name, table_name)
181
- end
182
-
183
- def delete
184
- @client.delete_database(@db_name)
185
- end
186
-
187
- def update_tables!
188
- @tables = @client.tables(@db_name)
189
- end
190
- end
191
-
192
- class Table < Model
193
- def initialize(client, db_name, table_name, type, schema, count)
194
- super(client)
195
- @db_name = db_name
196
- @table_name = table_name
197
- @type = type
198
- @schema = schema
199
- @count = count
200
- end
201
-
202
- attr_reader :type, :db_name, :table_name, :schema, :count
203
-
204
- alias database_name db_name
205
- alias name table_name
206
-
207
- def database
208
- @client.database(@db_name)
209
- end
210
-
211
- def identifier
212
- "#{@db_name}.#{@table_name}"
213
- end
214
-
215
- def delete
216
- @client.delete_table(@db_name, @table_name)
217
- end
218
- end
219
-
220
- class Schema
221
- class Field
222
- def initialize(name, type)
223
- @name = name
224
- @type = type
225
- end
226
- attr_reader :name
227
- attr_reader :type
228
- end
229
-
230
- def self.parse(cols)
231
- fields = cols.split(',').map {|col|
232
- name, type, *_ = col.split(':')
233
- Field.new(name, type)
234
- }
235
- Schema.new(fields)
236
- end
237
-
238
- def initialize(fields=[])
239
- @fields = fields
240
- end
241
-
242
- attr_reader :fields
243
-
244
- def add_field(name, type)
245
- @fields << Field.new(name, type)
246
- end
247
-
248
- def merge(schema)
249
- nf = @fields.dup
250
- schema.fields.each {|f|
251
- if i = nf.find_index {|sf| sf.name == f.name }
252
- nf[i] = f
253
- else
254
- nf << f
255
- end
256
- }
257
- Schema.new(nf)
258
- end
259
-
260
- def to_json(*args)
261
- @fields.map {|f| [f.name, f.type] }.to_json(*args)
262
- end
263
-
264
- def from_json(obj)
265
- @fields = obj.map {|f|
266
- Field.new(f[0], f[1])
267
- }
268
- self
269
- end
270
- end
271
-
272
- class Job < Model
273
- def initialize(client, job_id, type, query, status=nil, url=nil, debug=nil, start_at=nil, end_at=nil, result=nil)
274
- super(client)
275
- @job_id = job_id
276
- @type = type
277
- @url = url
278
- @query = query
279
- @status = status
280
- @debug = debug
281
- @start_at = start_at
282
- @end_at = end_at
283
- @result = result
284
- end
285
-
286
- attr_reader :job_id, :type
287
-
288
- def wait(timeout=nil)
289
- # TODO
290
- end
291
-
292
- def query
293
- update_status! unless @query
294
- @query
295
- end
296
-
297
- def status
298
- update_status! unless @status
299
- @status
300
- end
301
-
302
- def url
303
- update_status! unless @url
304
- @url
305
- end
306
-
307
- def debug
308
- update_status! unless @debug
309
- @debug
310
- end
311
-
312
- def start_at
313
- update_status! unless @start_at
314
- @start_at && !@start_at.empty? ? Time.parse(@start_at) : nil
315
- end
316
-
317
- def end_at
318
- update_status! unless @end_at
319
- @end_at && !@end_at.empty? ? Time.parse(@end_at) : nil
320
- end
321
-
322
- def result
323
- unless @result
324
- return nil unless finished?
325
- @result = @client.job_result(@job_id)
326
- end
327
- @result
328
- end
329
-
330
- def result_format(format)
331
- return nil unless finished?
332
- @client.job_result_format(@job_id, format)
333
- end
334
-
335
- def result_each(&block)
336
- if @result
337
- @result.each(&block)
338
- else
339
- @client.job_result_each(@job_id, &block)
340
- end
341
- nil
342
- end
343
-
344
- def finished?
345
- update_status! unless @status
346
- if @status == "success" || @status == "error"
347
- return true
348
- else
349
- return false
350
- end
351
- end
352
-
353
- def running?
354
- !finished?
355
- end
356
-
357
- def success?
358
- update_status! unless @status
359
- @status == "success"
360
- end
361
-
362
- def error?
363
- update_status! unless @status
364
- @status == "error"
365
- end
366
-
367
- def update_status!
368
- query, status, url, debug, start_at, end_at = @client.job_status(@job_id)
369
- @query = query
370
- @status = status
371
- @url = url
372
- @debug = debug
373
- @start_at = start_at
374
- @end_at = end_at
375
- self
376
- end
377
- end
378
-
379
- end
380
-