docbook_files 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -4
- data/Gemfile.lock +9 -4
- data/History.txt +10 -0
- data/LICENSE +22 -0
- data/README.md +30 -18
- data/Rakefile +15 -1
- data/lib/docbook_files/app.rb +112 -79
- data/lib/docbook_files/docbook.rb +2 -2
- data/lib/docbook_files/file_data.rb +125 -83
- data/lib/docbook_files/file_ref.rb +98 -0
- data/lib/docbook_files/file_ref_types.rb +20 -0
- data/spec/docbook_files/app_spec.rb +69 -15
- data/spec/docbook_files/docbook_spec.rb +1 -1
- data/spec/docbook_files/file_data_spec.rb +59 -49
- data/spec/docbook_files/file_ref_spec.rb +97 -0
- data/version.txt +1 -1
- metadata +49 -16
data/Gemfile
CHANGED
@@ -3,14 +3,22 @@ source "http://rubygems.org"
|
|
3
3
|
gem "libxml-ruby", :require => 'xml'
|
4
4
|
gem "term-ansicolor"
|
5
5
|
gem "wand"
|
6
|
-
gem "
|
6
|
+
gem "zucker"
|
7
|
+
gem "json", :platforms => :ruby_18
|
8
|
+
gem "win32console", :platforms => :mingw
|
7
9
|
|
8
10
|
group :development do
|
9
11
|
gem "bones"
|
10
|
-
gem 'turn'
|
11
12
|
gem 'rspec'
|
12
13
|
gem 'guard'
|
13
|
-
gem 'rb-fsevent'
|
14
|
-
gem 'growl_notify'
|
15
14
|
gem 'guard-rspec'
|
16
15
|
end
|
16
|
+
group :darwin do
|
17
|
+
gem 'rb-fsevent', :require => false
|
18
|
+
gem 'growl_notify', :require => false
|
19
|
+
end
|
20
|
+
group :linux do
|
21
|
+
gem 'rb-inotify', :require => false
|
22
|
+
gem 'libnotify', :require => false
|
23
|
+
end
|
24
|
+
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
ansi (1.3.0)
|
5
4
|
bones (3.7.1)
|
6
5
|
little-plugger (>= 1.1.2)
|
7
6
|
loquacious (>= 1.8.1)
|
8
7
|
rake (>= 0.8.7)
|
9
8
|
diff-lcs (1.1.3)
|
9
|
+
ffi (1.0.9)
|
10
10
|
growl_notify (0.0.3)
|
11
11
|
rb-appscript
|
12
12
|
guard (0.8.4)
|
@@ -14,6 +14,7 @@ GEM
|
|
14
14
|
guard-rspec (0.5.0)
|
15
15
|
guard (>= 0.8.4)
|
16
16
|
json (1.6.1)
|
17
|
+
libnotify (0.5.7)
|
17
18
|
libxml-ruby (2.2.2)
|
18
19
|
little-plugger (1.1.2)
|
19
20
|
loquacious (1.9.0)
|
@@ -21,6 +22,8 @@ GEM
|
|
21
22
|
rake (0.9.2)
|
22
23
|
rb-appscript (0.6.1)
|
23
24
|
rb-fsevent (0.4.3.1)
|
25
|
+
rb-inotify (0.8.8)
|
26
|
+
ffi (>= 0.5.0)
|
24
27
|
rspec (2.6.0)
|
25
28
|
rspec-core (~> 2.6.0)
|
26
29
|
rspec-expectations (~> 2.6.0)
|
@@ -32,11 +35,10 @@ GEM
|
|
32
35
|
safe_shell (1.0.1)
|
33
36
|
term-ansicolor (1.0.7)
|
34
37
|
thor (0.14.6)
|
35
|
-
turn (0.8.3)
|
36
|
-
ansi
|
37
38
|
wand (0.4)
|
38
39
|
mime-types
|
39
40
|
safe_shell (~> 1.0.0)
|
41
|
+
zucker (11)
|
40
42
|
|
41
43
|
PLATFORMS
|
42
44
|
ruby
|
@@ -47,9 +49,12 @@ DEPENDENCIES
|
|
47
49
|
guard
|
48
50
|
guard-rspec
|
49
51
|
json
|
52
|
+
libnotify
|
50
53
|
libxml-ruby
|
51
54
|
rb-fsevent
|
55
|
+
rb-inotify
|
52
56
|
rspec
|
53
57
|
term-ansicolor
|
54
|
-
turn
|
55
58
|
wand
|
59
|
+
win32console
|
60
|
+
zucker
|
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 0.5.0 / 2011-10-19
|
2
|
+
|
3
|
+
* A file can now manage multiple inclusions and/or references.
|
4
|
+
* The YAML and JSON output contains now two sections, hierarchy and details, just like the terminal output.
|
5
|
+
* The details contain new fields for multiple references: includes, included_by and references, referenced_by
|
6
|
+
|
7
|
+
* Minor changes
|
8
|
+
** Removed JSON dependency. It is used when available.
|
9
|
+
** Color support is now optional on Windows
|
10
|
+
|
1
11
|
== 0.4.0 / 2011-10-14
|
2
12
|
|
3
13
|
* Added JSON and YAML output formats
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Rainer Volz
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -29,29 +29,41 @@ If you don't like the screen output or want to integrate docbook_file into a cer
|
|
29
29
|
|
30
30
|
docbook_files --outputformat=yaml myproject.xml
|
31
31
|
|
32
|
-
The result is printed to STDOUT. The structure returned is equivalent to the normal terminal output, except that you always get the details.
|
33
|
-
|
34
|
-
*
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
*
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
32
|
+
The result is printed to STDOUT. The structure returned is equivalent to the normal terminal output, except that you always get the details.
|
33
|
+
|
34
|
+
* hierarchy - an array of entries for each step in the file hierarchy
|
35
|
+
** type - file type (main, inc-luded, or ref-erenced)
|
36
|
+
** name - file name
|
37
|
+
** path - path relative to the main file
|
38
|
+
** status - error status: 0 = ok, 1 = file not found, 2 = processing error (see error_string)
|
39
|
+
** size - file size in bytes
|
40
|
+
** level - the level in the file hierarchy, starting with 0
|
41
|
+
|
42
|
+
* details - an array of entries for each file used in the hierarchy
|
43
|
+
** name - file name
|
44
|
+
** path - path relative to the main file
|
45
|
+
** status - error status: 0 = ok, 1 = file not found, 2 = processing error (see error_string)
|
46
|
+
** error_string - contains an error message, if status > 0
|
47
|
+
** namespace - XML namespace, if applicable
|
48
|
+
** version - XML version attribute, if applicable
|
49
|
+
** docbook - true for DocBook 5 files, else false
|
50
|
+
** tag - start tag for XML files (chapter, book, article ...)
|
51
|
+
** ts - file modification time
|
52
|
+
** size - file size in byte
|
53
|
+
** checksum - SHA1 checksum
|
54
|
+
** mime - MIME type
|
55
|
+
** includes - files that are included by this file, an array of file names
|
56
|
+
** included_by - files that include this file, an array of file names
|
57
|
+
** references - files that are referenced by this file, an array of file names
|
58
|
+
** referenced_by - files that reference this file, an array of file names
|
59
|
+
|
50
60
|
|
51
61
|
Requirements
|
52
62
|
------------
|
53
63
|
|
54
64
|
* libxml2
|
65
|
+
* json (optional, if you want JSON output on Ruby 1.8)
|
66
|
+
* win32console (optional, if you want color support on MS Windows)
|
55
67
|
|
56
68
|
Install
|
57
69
|
-------
|
data/Rakefile
CHANGED
@@ -21,5 +21,19 @@ Bones {
|
|
21
21
|
depend_on 'libxml-ruby'
|
22
22
|
depend_on 'term-ansicolor'
|
23
23
|
depend_on 'wand'
|
24
|
-
depend_on '
|
24
|
+
depend_on 'zucker'
|
25
|
+
depend_on 'rspec', :development => true
|
26
|
+
gem.extras[:license] = 'MIT'
|
27
|
+
gem.extras[:post_install_message] = <<-POST_INSTALL_MSG
|
28
|
+
|
29
|
+
Please note:
|
30
|
+
|
31
|
+
- docbook_files uses color to mark problematic files.
|
32
|
+
On Windows, you should additionally install the gem 'win32console'
|
33
|
+
to enable color output in the terminal.
|
34
|
+
|
35
|
+
- JSON output is optional for Ruby 1.8. Please install the gem 'json'
|
36
|
+
if you are running Ruby 1.8 and want JSON output.
|
37
|
+
|
38
|
+
POST_INSTALL_MSG
|
25
39
|
}
|
data/lib/docbook_files/app.rb
CHANGED
@@ -2,21 +2,48 @@
|
|
2
2
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'yaml'
|
5
|
-
require '
|
6
|
-
require 'term/ansicolor'
|
5
|
+
require 'zucker/env'
|
7
6
|
|
8
|
-
|
9
|
-
|
7
|
+
# Windows (RubyInstaller) needs the additional gem.
|
8
|
+
# If not present create dummies for the color routines.
|
9
|
+
if OS.windows?
|
10
|
+
begin
|
11
|
+
require 'win32console'
|
12
|
+
require 'term/ansicolor'
|
13
|
+
class String
|
14
|
+
include Term::ANSIColor
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
class String
|
18
|
+
def red; self; end
|
19
|
+
def green; self; end
|
20
|
+
def magenta; self; end
|
21
|
+
def bold; self; end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
else
|
25
|
+
require 'term/ansicolor'
|
26
|
+
class String
|
27
|
+
include Term::ANSIColor
|
28
|
+
end
|
10
29
|
end
|
11
30
|
|
12
31
|
module DocbookFiles
|
13
32
|
|
14
33
|
# Create a new instance of App, and run the +docbook_files+ application given
|
15
|
-
# the command line _args_.
|
34
|
+
# the command line _args_. Check also for JSON availability.
|
16
35
|
#
|
17
36
|
def self.run( args = nil )
|
18
37
|
args ||= ARGV.dup.map! { |v| v.dup }
|
19
|
-
|
38
|
+
opts = {}
|
39
|
+
# For Windows and/or Ruby 1.8
|
40
|
+
begin
|
41
|
+
require 'json'
|
42
|
+
opts[:json_available] = true
|
43
|
+
rescue LoadError
|
44
|
+
opts[:json_available] = false
|
45
|
+
end
|
46
|
+
::DocbookFiles::App.new(opts).run args
|
20
47
|
end
|
21
48
|
|
22
49
|
##
|
@@ -28,6 +55,14 @@ module DocbookFiles
|
|
28
55
|
# * 2 - processing error
|
29
56
|
#
|
30
57
|
class App
|
58
|
+
|
59
|
+
# Replacement for empty values
|
60
|
+
EMPTYVAL = '-'
|
61
|
+
|
62
|
+
# Replacement for monstrously large files sizes
|
63
|
+
XXL_SIZE = "XXL"
|
64
|
+
|
65
|
+
# Help banner
|
31
66
|
@@banner = <<EOB
|
32
67
|
docbook_files, Version #{DocbookFiles::VERSION}
|
33
68
|
|
@@ -37,6 +72,7 @@ Files with problems (not found, invalid ...) are marked red.
|
|
37
72
|
Usage: docbook_files [options] <DOCBOOK-FILE>
|
38
73
|
EOB
|
39
74
|
|
75
|
+
# Initialize options and reset the FileData storage
|
40
76
|
def initialize(opts = {})
|
41
77
|
opts[:stdout] ||= $stdout
|
42
78
|
opts[:stderr] ||= $stderr
|
@@ -45,22 +81,28 @@ EOB
|
|
45
81
|
@stderr = opts[:stderr]
|
46
82
|
@opts[:output_format] ||= :screen
|
47
83
|
@opts[:details] ||= false
|
48
|
-
@
|
49
|
-
|
84
|
+
@opts[:json_available] ||= opts[:json_available]
|
85
|
+
@props = [:name, :path, :status, :size]
|
86
|
+
FileData.reset()
|
50
87
|
end
|
51
88
|
|
52
89
|
def run(args)
|
53
90
|
opts = OptionParser.new
|
54
91
|
opts.on('--details','List file details') {|val| @opts[:details] = true}
|
55
|
-
opts.on('--outputformat=yaml|json',['
|
92
|
+
opts.on('--outputformat=yaml|json',['yaml','json'],
|
56
93
|
'Return the result in YAML or JSON format') {|format|
|
57
94
|
case
|
58
95
|
when format == 'yaml'
|
59
96
|
@opts[:output_format] = :yaml
|
60
97
|
when format == 'json'
|
61
|
-
@opts[:
|
98
|
+
if @opts[:json_available]
|
99
|
+
@opts[:output_format] = :json
|
100
|
+
else
|
101
|
+
@stderr.puts "Error: JSON not available. Please install the json gem first."
|
102
|
+
exit 1
|
103
|
+
end
|
62
104
|
else
|
63
|
-
@stderr.puts "
|
105
|
+
@stderr.puts "Error: Unknown output format #{format}."
|
64
106
|
end
|
65
107
|
}
|
66
108
|
opts.banner = @@banner
|
@@ -83,6 +125,7 @@ EOB
|
|
83
125
|
begin
|
84
126
|
dbf = DocbookFiles::Docbook.new(rest[0])
|
85
127
|
table = dbf.list_as_table(@props)
|
128
|
+
files = FileData.files
|
86
129
|
rescue => exc
|
87
130
|
@stderr.puts "Something unexpected happend while docbook_files was running ..."
|
88
131
|
@stderr.puts exc.inspect.red
|
@@ -91,27 +134,29 @@ EOB
|
|
91
134
|
unless table.nil?
|
92
135
|
case @opts[:output_format]
|
93
136
|
when :json
|
94
|
-
|
95
|
-
|
96
|
-
t[:full_name] = relative2main(t[:full_name], mpath)
|
97
|
-
t
|
98
|
-
}
|
99
|
-
@stdout.puts ntable.to_json
|
137
|
+
out = {:hierarchy => table, :details => files2table(files)}
|
138
|
+
@stdout.puts out.to_json
|
100
139
|
when :yaml
|
101
|
-
|
102
|
-
|
103
|
-
t[:full_name] = relative2main(t[:full_name], mpath)
|
104
|
-
t
|
105
|
-
}
|
106
|
-
YAML.dump(ntable,@stdout)
|
140
|
+
out = {:hierarchy => table, :details => files2table(files)}
|
141
|
+
YAML.dump(out,@stdout)
|
107
142
|
else
|
108
|
-
|
143
|
+
output_hierarchy(table)
|
144
|
+
output_details(files) if @opts[:details]
|
109
145
|
end
|
110
146
|
end
|
111
147
|
end
|
112
148
|
|
113
|
-
#
|
114
|
-
def
|
149
|
+
# Transform the files into something YAML/JSON can handle
|
150
|
+
def files2table(files)
|
151
|
+
files.map { |f|
|
152
|
+
f.to_hash([:name, :path, :status, :error_string, :namespace,
|
153
|
+
:version, :docbook, :tag, :ts, :size, :checksum,
|
154
|
+
:mime, :includes, :included_by, :references, :referenced_by])
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
# Terminal output for file hierarchy
|
159
|
+
def output_hierarchy(table)
|
115
160
|
output_string = "%3d %-60s %4s %10s"
|
116
161
|
@stdout.puts
|
117
162
|
@stdout.puts 'File Hierarchy'.bold
|
@@ -122,7 +167,7 @@ EOB
|
|
122
167
|
sum_xml_err = 0
|
123
168
|
table.each do |t|
|
124
169
|
output = output_string % [t[:level],
|
125
|
-
format_name(t[:level],t[:
|
170
|
+
format_name(t[:level],t[:path]),
|
126
171
|
t[:type].to_s,
|
127
172
|
format_size(t[:size])]
|
128
173
|
sum_size += t[:size]
|
@@ -145,36 +190,44 @@ EOB
|
|
145
190
|
summary += " #{sum_xml_err} file(s) with errors.".red
|
146
191
|
end
|
147
192
|
@stdout.puts summary
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
193
|
+
end
|
194
|
+
|
195
|
+
# Print the FileData representation to the terminal
|
196
|
+
def output_details(files)
|
197
|
+
@stdout.puts
|
198
|
+
@stdout.puts "Details".bold
|
199
|
+
files.each do |t|
|
200
|
+
fname = t.path
|
201
|
+
@stdout.puts "File: %s" % [((t.status == FileData::STATUS_OK) ? fname : fname.red)]
|
202
|
+
@stdout.puts "Includes: %s" % [format_fds(t.includes)] unless t.includes.empty?
|
203
|
+
@stdout.puts "Included by: %s" % [format_fds(t.included_by)] unless t.included_by.empty?
|
204
|
+
@stdout.puts "References: %s" % [format_fds(t.references)] unless t.references.empty?
|
205
|
+
@stdout.puts "Referenced by: %s" % [format_fds(t.referenced_by)] unless t.referenced_by.empty?
|
206
|
+
unless t.status == FileData::STATUS_NOT_FOUND
|
207
|
+
# show that part only if file exists
|
208
|
+
@stdout.puts "Size: %s (%d)" % [format_size(t.size),t.size]
|
209
|
+
if (t.docbook)
|
210
|
+
@stdout.puts "Type: DocBook, Version #{t.version}, Tag: #{t.tag}"
|
158
211
|
else
|
159
|
-
@stdout.puts "
|
160
|
-
end
|
161
|
-
unless t[:status] == FileData::STATUS_NOT_FOUND
|
162
|
-
# show that part only if file exists
|
163
|
-
@stdout.puts "Size: %s (%d)" % [format_size(t[:size]),t[:size]]
|
164
|
-
if (t[:docbook])
|
165
|
-
@stdout.puts "Type: DocBook, Version #{t[:version]}, Tag: #{t[:tag]}"
|
166
|
-
else
|
167
|
-
@stdout.puts "MIME: #{val_s(t[:mime])}"
|
168
|
-
end
|
169
|
-
@stdout.puts "Timestamp: %s" % [t[:ts]]
|
170
|
-
@stdout.puts "Checksum: %s" % [t[:checksum]]
|
212
|
+
@stdout.puts "MIME: #{val_s(t.mime)}"
|
171
213
|
end
|
172
|
-
@stdout.puts "
|
173
|
-
@stdout.puts
|
214
|
+
@stdout.puts "Timestamp: %s" % [t.ts]
|
215
|
+
@stdout.puts "SHA1: %s" % [t.checksum]
|
174
216
|
end
|
217
|
+
@stdout.puts "Error: %s" % [t.error_string.to_s.red] unless (t.error_string.nil?)
|
218
|
+
@stdout.puts
|
175
219
|
end
|
176
220
|
end
|
177
221
|
|
222
|
+
|
223
|
+
# Format a list of FileDatas
|
224
|
+
def format_fds(fds)
|
225
|
+
if (fds.nil? || fds.empty?)
|
226
|
+
EMPTYVAL
|
227
|
+
else
|
228
|
+
fds.map {|p| p.path}.join ','
|
229
|
+
end
|
230
|
+
end
|
178
231
|
|
179
232
|
# Format the filename to indicate the level in the hierarchy.
|
180
233
|
# Indentation = two spaces per level.
|
@@ -183,9 +236,8 @@ EOB
|
|
183
236
|
# relative part of the path is shown, else the full path.
|
184
237
|
# If the resulting string is too long for display it is shortened.
|
185
238
|
#
|
186
|
-
def format_name(level, full_name
|
187
|
-
|
188
|
-
lnname = ' '*level+nname
|
239
|
+
def format_name(level, full_name)
|
240
|
+
lnname = ' '*level+full_name
|
189
241
|
if (lnname.length > 60)
|
190
242
|
lnname[0..3]+'...'+lnname[-54,lnname.length-1]
|
191
243
|
else
|
@@ -193,18 +245,6 @@ EOB
|
|
193
245
|
end
|
194
246
|
end
|
195
247
|
|
196
|
-
# Try to find the path of _file_name_ that is relative to the _main file_.
|
197
|
-
# If there is no common part return the _file_name_.
|
198
|
-
def relative2main(file_name,main_name)
|
199
|
-
main_dir = File.dirname(main_name)
|
200
|
-
md = file_name.match("^#{main_dir}/")
|
201
|
-
if md.nil?
|
202
|
-
file_name
|
203
|
-
else
|
204
|
-
md.post_match
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
248
|
# :stopdoc:
|
209
249
|
KB = 1024
|
210
250
|
MB = 1048576
|
@@ -217,7 +257,7 @@ EOB
|
|
217
257
|
# Sizes >= 1PB will return 'XXL'
|
218
258
|
def format_size(sz)
|
219
259
|
if (emptyval?(sz))
|
220
|
-
|
260
|
+
EMPTYVAL
|
221
261
|
else
|
222
262
|
case
|
223
263
|
when sz < KB then "#{sz}B"
|
@@ -226,18 +266,14 @@ EOB
|
|
226
266
|
when sz >= GB && sz < TB then "#{sz/GB}GB"
|
227
267
|
when sz >= TB && sz < PB then "#{sz/TB}TB"
|
228
268
|
else
|
229
|
-
|
269
|
+
XXL_SIZE
|
230
270
|
end
|
231
271
|
end
|
232
272
|
end
|
233
273
|
|
234
|
-
# Return a string for the value, '
|
274
|
+
# Return a string for the value, '-' if there is none.
|
235
275
|
def val_s(val)
|
236
|
-
|
237
|
-
'-'
|
238
|
-
else
|
239
|
-
val.to_s
|
240
|
-
end
|
276
|
+
emptyval?(val) ? EMPTYVAL : val.to_s
|
241
277
|
end
|
242
278
|
|
243
279
|
# Check whether the value is nil or empty.
|
@@ -245,12 +281,9 @@ EOB
|
|
245
281
|
if val.nil?
|
246
282
|
true
|
247
283
|
else
|
248
|
-
|
249
|
-
val.empty?
|
250
|
-
else
|
251
|
-
false
|
252
|
-
end
|
284
|
+
(val.class == String) ? val.empty? : false
|
253
285
|
end
|
254
286
|
end
|
287
|
+
|
255
288
|
end
|
256
289
|
end
|