dev 2.1.153 → 2.1.154

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/apps/git.rb +207 -208
  3. data/lib/apps/msbuild.rb +90 -90
  4. data/lib/apps/nbench.rb +2 -1
  5. data/lib/apps/nuget.rb +57 -59
  6. data/lib/apps/svn.rb +137 -143
  7. data/lib/apps/wix.rb +47 -50
  8. data/lib/apps/xcodebuild.rb +13 -11
  9. data/lib/apps/zip.rb +25 -25
  10. data/lib/apps.rb +3 -1
  11. data/lib/base/array.rb +66 -64
  12. data/lib/base/command.rb +237 -238
  13. data/lib/base/dir.rb +73 -76
  14. data/lib/base/environment.rb +94 -99
  15. data/lib/base/file.rb +35 -33
  16. data/lib/base/gemspec.rb +47 -45
  17. data/lib/base/giturl.rb +88 -90
  18. data/lib/base/hash.rb +20 -15
  19. data/lib/base/history.rb +36 -33
  20. data/lib/base/internet.rb +22 -20
  21. data/lib/base/project.rb +410 -423
  22. data/lib/base/projects.rb +231 -246
  23. data/lib/base/source.rb +22 -20
  24. data/lib/base/string.rb +6 -4
  25. data/lib/base/text.rb +16 -14
  26. data/lib/base/timeout.rb +29 -28
  27. data/lib/base/timer.rb +23 -19
  28. data/lib/base/version.rb +68 -72
  29. data/lib/base.rb +5 -3
  30. data/lib/commands.rb +47 -43
  31. data/lib/dev.config.rb +3 -2
  32. data/lib/dev.rb +65 -66
  33. data/lib/tasks/add.rb +34 -40
  34. data/lib/tasks/analyze.rb +17 -15
  35. data/lib/tasks/build.rb +101 -103
  36. data/lib/tasks/clean.rb +6 -4
  37. data/lib/tasks/clobber.rb +20 -18
  38. data/lib/tasks/commit.rb +42 -44
  39. data/lib/tasks/default.rb +41 -39
  40. data/lib/tasks/doc.rb +10 -8
  41. data/lib/tasks/info.rb +8 -7
  42. data/lib/tasks/package.rb +23 -20
  43. data/lib/tasks/publish.rb +20 -25
  44. data/lib/tasks/pull.rb +9 -9
  45. data/lib/tasks/push.rb +11 -13
  46. data/lib/tasks/setup.rb +180 -183
  47. data/lib/tasks/test.rb +121 -107
  48. data/lib/tasks/update.rb +13 -11
  49. data/lib/tasks.rb +38 -42
  50. metadata +7 -9
  51. data/bin/dev +0 -3
data/lib/base/command.rb CHANGED
@@ -1,17 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  puts DELIMITER if defined?(DEBUG)
2
4
  puts __FILE__ if defined?(DEBUG)
3
5
 
4
- require 'time'
5
- require 'open3'
6
- require_relative('timeout.rb')
7
- require_relative('timer.rb')
8
- require_relative('array.rb')
9
- require_relative('hash.rb')
10
- require_relative('string.rb')
11
- require_relative('environment.rb')
12
- require_relative('dir.rb')
13
- BUFFER_SIZE=1024 if(!defined?(BUFFER_SIZE))
14
-
6
+ require "time"
7
+ require "open3"
8
+ require_relative("timeout")
9
+ require_relative("timer")
10
+ require_relative("array")
11
+ require_relative("hash")
12
+ require_relative("string")
13
+ require_relative("environment")
14
+ require_relative("dir")
15
+ BUFFER_SIZE = 1024 unless defined?(BUFFER_SIZE)
16
+
15
17
  # = Command
16
18
  #
17
19
  # execution of system commands
@@ -33,43 +35,41 @@ BUFFER_SIZE=1024 if(!defined?(BUFFER_SIZE))
33
35
  # - :end_time
34
36
  #
35
37
  class Command < Hash
36
- def initialize command
37
- self[:input] = ''
38
- self[:timeout] = 0
39
- self[:directory] = ''
40
- self[:exit_code] = 0
41
- self[:output] = ''
42
- self[:error] = ''
43
- self[:machine] = ''
44
- self[:user] = ''
45
- self[:start_time] = nil
46
- self[:end_time] = nil
47
-
48
- if(command.kind_of?(String))
49
- self[:input] = command
50
- end
51
-
52
- if(command.kind_of?(Hash))
53
- command.each{|k,v|self[k.to_sym]=v}
54
- self[:start_time]=Time.parse(self[:start_time]) if(self.has_key?(:start_time) && !self[:start_time].nil?)
55
- self[:end_time]=Time.parse(self[:end_time]) if(self.has_key?(:end_time) && !self[:end_time].nil?)
38
+ def initialize(command)
39
+ self[:input] = ""
40
+ self[:timeout] = 0
41
+ self[:directory] = ""
42
+ self[:exit_code] = 0
43
+ self[:output] = ""
44
+ self[:error] = ""
45
+ self[:machine] = ""
46
+ self[:user] = ""
47
+ self[:start_time] = nil
48
+ self[:end_time] = nil
49
+
50
+ self[:input] = command if command.is_a?(String)
51
+
52
+ if command.is_a?(Hash)
53
+ command.each { |k, v| self[k.to_sym] = v }
54
+ self[:start_time] = Time.parse(self[:start_time]) if key?(:start_time) && !self[:start_time].nil?
55
+ self[:end_time] = Time.parse(self[:end_time]) if key?(:end_time) && !self[:end_time].nil?
56
56
  end
57
- end
57
+ end
58
58
 
59
- def save filename
60
- File.open(filename,'w'){|f|f.write(to_json)}
59
+ def save(filename)
60
+ File.open(filename, "w") { |f| f.write(to_json) }
61
61
  end
62
62
 
63
- def open filename=''
64
- @filename=filename if filename.length > 0
65
- self.clear
66
- JSON.parse(IO.read(@filename)).each{|k,v| self[k.to_sym]=v}
67
- self[:start_time]=Time.parse(self[:start_time]) if(self.has_key?(:start_time))
68
- self[:end_time]=Time.parse(self[:end_time]) if(self.has_key?(:end_time))
63
+ def open(filename = "")
64
+ @filename = filename if filename.length.positive?
65
+ clear
66
+ JSON.parse(IO.read(@filename)).each { |k, v| self[k.to_sym] = v }
67
+ self[:start_time] = Time.parse(self[:start_time]) if key?(:start_time)
68
+ self[:end_time] = Time.parse(self[:end_time]) if key?(:end_time)
69
69
  end
70
70
 
71
71
  def quiet?
72
- (self.has_key?(:quiet) && self[:quiet])
72
+ (key?(:quiet) && self[:quiet])
73
73
  end
74
74
 
75
75
  def exit_code
@@ -79,255 +79,254 @@ class Command < Hash
79
79
  def output
80
80
  self[:output]
81
81
  end
82
+
82
83
  def error
83
84
  self[:error]
84
85
  end
86
+
85
87
  def self.executes?(command)
86
- cmd = Command.new({ :input => command, :quiet => true,:ignore_failure => true})
88
+ cmd = Command.new({ input: command, quiet: true, ignore_failure: true })
87
89
  cmd.execute
88
- if(cmd[:exit_code] == 0)
90
+ if (cmd[:exit_code]).zero?
89
91
  true
90
92
  else
91
93
  false
92
94
  end
93
95
  end
94
96
 
95
- # todo: add log of execution
96
- def execute value=nil
97
+ # TODO: add log of execution
98
+ def execute(value = nil)
99
+ puts (self[:input]).to_s if defined?(DEBUG)
97
100
 
98
- puts "#{self[:input]}" if defined?(DEBUG)
101
+ value.each { |k, v| self[k] = v } if !value.nil? && value.is_a?(Hash)
99
102
 
100
- if(!value.nil? && value.is_a?(Hash))
101
- value.each{|k,v|self[k]=v}
102
- end
103
+ pwd = Dir.pwd
104
+ self[:directory] = pwd if !key?(:directory) || self[:directory].length.zero?
103
105
 
104
- pwd=Dir.pwd
105
- self[:directory] = pwd if(!self.has_key?(:directory) || self[:directory].length==0)
106
-
107
- if(self[:timeout] > 0)
108
- puts "#{self[:input]} (#{self[:directory]}) timeout #{self[:timeout].to_s}" if(!quiet?)
106
+ if (self[:timeout]).positive?
107
+ puts "#{self[:input]} (#{self[:directory]}) timeout #{self[:timeout]}" unless quiet?
109
108
  else
110
- puts "#{self[:input]} (#{self[:directory]})" if(!quiet?)
109
+ puts "#{self[:input]} (#{self[:directory]})" unless quiet?
111
110
  end
112
111
 
113
- self[:machine] = Command.machine
114
- self[:user] = Command.user
112
+ self[:machine] = Command.machine
113
+ self[:user] = Command.user
115
114
 
116
- self[:start_time]=Time.now
117
- timer=Timer.new
115
+ self[:start_time] = Time.now
116
+ timer = Timer.new
118
117
 
119
118
  Dir.chdir(self[:directory]) do
120
- if self[:input].include?('<%') && self[:input].include?('%>')
121
- ruby = self[:input].gsub("<%","").gsub("%>","")
122
-
123
- begin
124
- self[:output]=eval(ruby)
125
- rescue
126
- self[:exit_code]=1
127
- self[:error]="unable to eval(#{ruby})"
128
- end
129
-
130
- self[:elapsed] = timer.elapsed_str
131
- self[:end_time] = Time.now
132
- else
133
- begin
134
- if(self[:timeout] <= 0)
135
- self[:output],self[:error],status= Open3.capture3(self[:input])
136
- self[:exit_code]=status.to_i
137
- self[:elapsed] = timer.elapsed_str
138
- self[:end_time] = Time.now
119
+ if self[:input].include?("<%") && self[:input].include?("%>")
120
+ ruby = self[:input].gsub("<%", "").gsub("%>", "")
121
+
122
+ begin
123
+ self[:output] = eval(ruby)
124
+ rescue StandardError
125
+ self[:exit_code] = 1
126
+ self[:error] = "unable to eval(#{ruby})"
127
+ end
128
+
129
+ self[:elapsed] = timer.elapsed_str
130
+ self[:end_time] = Time.now
131
+ else
132
+ begin
133
+ if self[:timeout] <= 0
134
+ self[:output], self[:error], status = Open3.capture3(self[:input])
135
+ self[:exit_code] = status.to_i
136
+ self[:elapsed] = timer.elapsed_str
137
+ self[:end_time] = Time.now
139
138
  else
140
- require_relative 'timeout.rb'
141
- result=run_with_timeout(self[:directory],self[:input], self[:timeout],2)
142
- self[:output]=result[0]
143
- self[:exit_code]=result[1]
139
+ require_relative "timeout"
140
+ result = run_with_timeout(self[:directory], self[:input], self[:timeout], 2)
141
+ self[:output] = result[0]
142
+ self[:exit_code] = result[1]
144
143
  self[:elapsed] = timer.elapsed_str
145
144
  self[:end_time] = Time.now
146
-
147
- if(timer.elapsed >= self[:timeout])
148
- self[:exit_code]=1
149
- self[:error] = self[:error] + "timed out"
145
+
146
+ if timer.elapsed >= self[:timeout]
147
+ self[:exit_code] = 1
148
+ self[:error] = "#{self[:error]}timed out"
150
149
  end
151
150
  end
152
- rescue Exception => e
153
- self[:elapsed] = timer.elapsed_str
154
- self[:end_time] = Time.now
155
- self[:error] = "Exception: " + e.to_s
156
- self[:exit_code]=1
157
- end
158
- end
151
+ rescue Exception => e
152
+ self[:elapsed] = timer.elapsed_str
153
+ self[:end_time] = Time.now
154
+ self[:error] = "Exception: #{e}"
155
+ self[:exit_code] = 1
156
+ end
157
+ end
159
158
  end
160
-
161
159
 
162
- if(self[:exit_code] != 0)
163
- if(!quiet?)
164
- puts "exit_code=#{self[:exit_code]}"
165
- puts self[:output]
166
- puts self[:error]
160
+ if self[:exit_code] != 0
161
+ unless quiet?
162
+ puts "exit_code=#{self[:exit_code]}"
163
+ puts self[:output]
164
+ puts self[:error]
165
+ end
166
+ if !key?(:ignore_failure) || !self[:ignore_failure]
167
+ raise "#{self[:input]} failed\n#{self[:output]}\n#{self[:error]}"
167
168
  end
168
- if(!self.has_key?(:ignore_failure) || !self[:ignore_failure])
169
- raise "#{self[:input]} failed\n#{self[:output]}\n#{self[:error]}"
170
- end
171
169
  end
172
- self
173
- end
170
+ self
171
+ end
174
172
 
175
- def self.machine
176
- if !ENV['COMPUTERNAME'].nil?
177
- return ENV['COMPUTERNAME']
178
- end
173
+ def self.machine
174
+ return ENV["COMPUTERNAME"] unless ENV["COMPUTERNAME"].nil?
179
175
 
180
- machine = `hostname`
181
- machine = machine.split('.')[0] if machine.include?('.')
182
- return machine.strip
183
- end
176
+ machine = `hostname`
177
+ machine = machine.split(".")[0] if machine.include?(".")
178
+ machine.strip
179
+ end
184
180
 
185
- def self.user
186
- ENV['USER'].nil? ? ENV['USERNAME'] : ENV['USER']
187
- end
188
-
189
- def self.home
190
- ["USERPROFILE","HOME"].each {|v|
191
- return ENV[v].gsub('\\','/') unless ENV[v].nil?
192
- }
193
- dir="~"
194
- dir=ENV["HOME"] unless ENV["HOME"].nil?
195
- dir=ENV["USERPROFILE"].gsub('\\','/') unless ENV["USERPROFILE"].nil?
196
- return dir
197
- end
181
+ def self.user
182
+ ENV["USER"].nil? ? ENV["USERNAME"] : ENV["USER"]
183
+ end
198
184
 
199
- def self.dev_root
200
- ["DEV_HOME","DEV_ROOT"].each {|v|
201
- return ENV[v].gsub('\\','/') unless ENV[v].nil?
202
- }
203
- dir=home
204
- return dir
185
+ def self.home
186
+ %w[USERPROFILE HOME].each do |v|
187
+ return ENV[v].gsub('\\', "/") unless ENV[v].nil?
205
188
  end
189
+ dir = "~"
190
+ dir = ENV["HOME"] unless ENV["HOME"].nil?
191
+ dir = ENV["USERPROFILE"].gsub('\\', "/") unless ENV["USERPROFILE"].nil?
192
+ dir
193
+ end
206
194
 
207
- def self.execute command, working_directory=''
208
- cmd = Command.new({ :input => command, :quiet => true}) if command.kind_of?(String)
209
- cmd[:directory] = working_directory if command.kind_of?(String)
210
- cmd = command if command.kind_of?(Command)
211
- cmd = Command.new(command) if command.kind_of?(Hash)
212
- cmd.execute
213
- cmd[:exit_code]
214
- cmd
195
+ def self.dev_root
196
+ %w[DEV_HOME DEV_ROOT].each do |v|
197
+ return ENV[v].gsub('\\', "/") unless ENV[v].nil?
215
198
  end
199
+ home
200
+ end
216
201
 
217
- def self.exit_code command
218
- cmd = Command.new(command)
219
- cmd[:ignore_failure]=true
220
- cmd[:quiet]=true
221
- cmd.execute
222
- cmd[:exit_code]
223
- end
202
+ def self.execute(command, working_directory = "")
203
+ cmd = Command.new({ input: command, quiet: true }) if command.is_a?(String)
204
+ cmd[:directory] = working_directory if command.is_a?(String)
205
+ cmd = command if command.is_a?(Command)
206
+ cmd = Command.new(command) if command.is_a?(Hash)
207
+ cmd.execute
208
+ cmd[:exit_code]
209
+ cmd
210
+ end
224
211
 
225
- def self.output command
226
- cmd = Command.new(command)
227
- cmd[:ignore_failure]=true
228
- cmd[:quiet]=true
229
- cmd.execute
230
- cmd[:output]
231
- end
212
+ def self.exit_code(command)
213
+ cmd = Command.new(command)
214
+ cmd[:ignore_failure] = true
215
+ cmd[:quiet] = true
216
+ cmd.execute
217
+ cmd[:exit_code]
218
+ end
219
+
220
+ def self.output(command)
221
+ cmd = Command.new(command)
222
+ cmd[:ignore_failure] = true
223
+ cmd[:quiet] = true
224
+ cmd.execute
225
+ cmd[:output]
226
+ end
227
+
228
+ def self.error(command)
229
+ cmd = Command.new(command)
230
+ cmd[:ignore_failure] = true
231
+ cmd[:quiet] = true
232
+ cmd.execute
233
+ cmd[:error]
234
+ end
232
235
 
233
- def self.error command
234
- cmd = Command.new(command)
235
- cmd[:ignore_failure]=true
236
- cmd[:quiet]=true
237
- cmd.execute
238
- cmd[:error]
236
+ def getFormattedTimeSpan(timespan)
237
+ result = ""
238
+ seconds = timespan.round
239
+ if seconds > 99
240
+ minutes = (seconds / 60).round
241
+ result = "#{minutes}m"
242
+ else
243
+ result = "#{seconds}s" # 99s
239
244
  end
245
+ result.fix(3)
246
+ end
240
247
 
241
- def getFormattedTimeSpan timespan
242
- result=''
243
- seconds = timespan.round
244
- if(seconds > 99)
245
- minutes=(seconds/60).round
246
- result="#{minutes}m"
247
- else
248
- result="#{seconds}s" # 99s
249
- end
250
- result.fix(3)
248
+ def summary(include_directory = false)
249
+ duration = ""
250
+ duration = getFormattedTimeSpan(self[:end_time] - self[:start_time])
251
+ if Environment.default.colorize?
252
+ require "ansi/code"
253
+ cduration = ANSI.reset + duration
254
+ # code=ANSI.green + '+ ' + ANSI.reset
255
+ # code=ANSI.red + '- ' + ANSI.reset if exit_code != 0
256
+ cinput = ANSI.reset + self[:input] + ANSI.reset
257
+ cinput = ANSI.red + self[:input] + ANSI.reset if exit_code != 0
258
+ cdirectory = ""
259
+ cdirectory = "(#{self[:directory]})" if include_directory
260
+ " #{cduration} #{cinput} #{cdirectory}"
261
+ else
262
+ code = " "
263
+ code = "X" if exit_code != 0
264
+ sdirectory = ""
265
+ sdirectory = "(#{self[:directory]})" if include_directory
266
+ "#{code} #{duration} #{self[:input]} #{sdirectory}"
251
267
  end
268
+ end
252
269
 
253
- def summary include_directory=false
254
- duration=""
255
- duration=getFormattedTimeSpan(self[:end_time]-self[:start_time])
256
- if(Environment.default.colorize?)
257
- require 'ansi/code'
258
- cduration = ANSI.reset + duration
259
- #code=ANSI.green + '+ ' + ANSI.reset
260
- #code=ANSI.red + '- ' + ANSI.reset if exit_code != 0
261
- cinput = ANSI.reset + self[:input] + ANSI.reset
262
- cinput = ANSI.red + self[:input] + ANSI.reset if exit_code != 0
263
- cdirectory = ''
264
- cdirectory = "(#{self[:directory]})" if include_directory
265
- " #{cduration} #{cinput} #{cdirectory}"
266
- else
267
- code=' '
268
- code='X' if exit_code != 0
269
- sdirectory = ''
270
- sdirectory = "(#{self[:directory]})" if include_directory
271
- "#{code} #{duration} #{self[:input]} #{sdirectory}"
272
- end
270
+ def format_property(name, value)
271
+ if Environment.default.colorize?
272
+ require "ansi/code"
273
+ "#{name}: " + ANSI.yellow + ANSI.bright + value.to_s.strip + ANSI.reset
274
+ else
275
+ "#{name}: #{value}"
273
276
  end
277
+ end
274
278
 
275
- def format_property name,value
276
- if(Environment.default.colorize?)
277
- require 'ansi/code'
278
- return "#{name}: " + ANSI.yellow + ANSI.bright + value.to_s.strip + ANSI.reset
279
- else
280
- return "#{name}: #{value}"
281
- end
282
- end
283
-
284
- def info
285
- result=format_property('input'.fix(15),self[:input]) + "\n"
286
- result=result + format_property('directory'.fix(15),self[:directory]) + "\n"
287
- result=result + format_property('exit_code'.fix(15),self[:exit_code]) + "\n"
288
- result=result + format_property('duration'.fix(15),getFormattedTimeSpan(self[:end_time]-self[:start_time])) + "\n"
289
- output=['']
290
- output=self[:output].strip.split("\n") if !self[:output].nil?
291
- if(output.length <= 1)
292
- result=result + format_property('output'.fix(15),output[0]) + "\n"
293
- #result=result + format_property('output'.fix(15),'') + "\n" if(output.length==0)
294
- #result=result + format_property('output'.fix(15),output) + "\n" if(output.length==1)
295
- else
296
- result=result + format_property('output'.fix(15),'') + "\n"
297
- output.each{|line|
298
- result=result + ' '.fix(16) + line + "\n"
299
- }
279
+ def info
280
+ result = "#{format_property("input".fix(15), self[:input])}\n"
281
+ result = "#{result}#{format_property("directory".fix(15), self[:directory])}\n"
282
+ result = "#{result}#{format_property("exit_code".fix(15), self[:exit_code])}\n"
283
+ result = result + format_property("duration".fix(15),
284
+ getFormattedTimeSpan(self[:end_time] - self[:start_time])) + "\n"
285
+ output = [""]
286
+ output = self[:output].strip.split("\n") unless self[:output].nil?
287
+ if output.length <= 1
288
+ result = "#{result}#{format_property("output".fix(15), output[0])}\n"
289
+ # result=result + format_property('output'.fix(15),'') + "\n" if(output.length==0)
290
+ # result=result + format_property('output'.fix(15),output) + "\n" if(output.length==1)
291
+ else
292
+ result = "#{result}#{format_property("output".fix(15), "")}\n"
293
+ output.each do |line|
294
+ result = "#{result}#{" ".fix(16)}#{line}\n"
300
295
  end
301
- error=['']
302
- error=self[:error].strip.split("\n") if !self[:error].nil?
303
- if(error.length <= 1)
304
- result=result + format_property('error'.fix(15),error[0]) + "\n"
305
- #result=result + format_property('error'.fix(15),'') + "\n" if(error.length==0)
306
- #result=result + format_property('error'.fix(15),error) + "\n" if(error.length==1)
307
- else
308
- result=result + format_property('error'.fix(15),'') + "\n"
309
- error.each{|line|
310
- result=result + ' '.fix(16) + line + "\n"
311
- }
296
+ end
297
+ error = [""]
298
+ error = self[:error].strip.split("\n") unless self[:error].nil?
299
+ if error.length <= 1
300
+ result = "#{result}#{format_property("error".fix(15), error[0])}\n"
301
+ # result=result + format_property('error'.fix(15),'') + "\n" if(error.length==0)
302
+ # result=result + format_property('error'.fix(15),error) + "\n" if(error.length==1)
303
+ else
304
+ result = "#{result}#{format_property("error".fix(15), "")}\n"
305
+ error.each do |line|
306
+ result = "#{result}#{" ".fix(16)}#{line}\n"
312
307
  end
313
308
  end
309
+ end
314
310
 
315
- def to_html
316
- if self[:exit_code] == 0
311
+ def to_html
312
+ if (self[:exit_code]).zero?
317
313
  [
318
- '<div><table><tr><td width="20"></td><td><pre>',
319
- self[:input],
320
- '</pre></td></tr></table></div>'
314
+ '<div><table><tr><td width="20"></td><td><pre>',
315
+ self[:input],
316
+ "</pre></td></tr></table></div>",
321
317
  ].join
322
- else
318
+ else
323
319
  [
324
- '<div><table><tr><td width="20"></td><td><pre>',
325
- self[:input],
320
+ '<div><table><tr><td width="20"></td><td><pre>',
321
+ self[:input],
326
322
  '</pre><table><tr><td width="20"></td><td><table>',
327
- map { |k, v| ["<tr><td><strong>#{k}</strong></td>", v.respond_to?(:to_html) ? v.to_html : "<td><span><pre>#{v}</pre></span></td></tr>"] },
328
- '</table>',
329
- '</td></tr></table></td></tr></table></div>'
323
+ map do |k, v|
324
+ ["<tr><td><strong>#{k}</strong></td>",
325
+ v.respond_to?(:to_html) ? v.to_html : "<td><span><pre>#{v}</pre></span></td></tr>"]
326
+ end,
327
+ "</table>",
328
+ "</td></tr></table></td></tr></table></div>",
330
329
  ].join
331
- end
332
330
  end
333
- end
331
+ end
332
+ end