baby_erubis 0.1.0 → 1.0.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/README.md +41 -6
- data/Rakefile +7 -1
- data/baby_erubis.gemspec +6 -3
- data/bin/baby_erubis +481 -0
- data/lib/baby_erubis.rb +8 -5
- data/test/context_test.rb +1 -1
- data/test/run_all.rb +1 -1
- data/test/script_test.rb +646 -0
- data/test/template_test.rb +26 -12
- metadata +7 -4
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
BabyErubis.rb
|
2
2
|
=============
|
3
3
|
|
4
|
-
$Release:
|
4
|
+
$Release: 1.0.0 $
|
5
5
|
|
6
6
|
BabyErubis is an yet another eRuby implementation, based on Erubis.
|
7
7
|
|
@@ -10,7 +10,7 @@ BabyErubis is an yet another eRuby implementation, based on Erubis.
|
|
10
10
|
* Supports HTML as well as plain text
|
11
11
|
* Accepts both template file and template string
|
12
12
|
|
13
|
-
BabyErubis
|
13
|
+
BabyErubis supports Ruby >= 1.8 and Rubinius >= 2.0.
|
14
14
|
|
15
15
|
|
16
16
|
|
@@ -41,6 +41,24 @@ Render template file:
|
|
41
41
|
print output
|
42
42
|
|
43
43
|
|
44
|
+
(Use `BabyErubis::Text` instead of `BabyErubis::Html` when rendering plain text.)
|
45
|
+
|
46
|
+
|
47
|
+
Command-line examples (see `baby_erubis.rb --help` for details):
|
48
|
+
|
49
|
+
## convert eRuby file into Ruby code
|
50
|
+
$ baby_erubis -x file.erb # text
|
51
|
+
$ baby_erubis -xH file.erb # html
|
52
|
+
$ baby_erubis -X file.erb # embedded code only
|
53
|
+
## render eRuby file with context data
|
54
|
+
$ baby_erubis -c '{items: [A, B, C]}' file.erb # YAML
|
55
|
+
$ baby_erubis -c '@items=["A","B","C"]' file.erb # Ruby
|
56
|
+
$ baby_erubis -f data.yaml file.erb # or -f *.json, *.rb
|
57
|
+
## debug eRuby file
|
58
|
+
$ baby_erubis -xH file.erb | ruby -wc # check syntax error
|
59
|
+
$ baby_erubis -XHNU file.erb # show embedded ruby code
|
60
|
+
|
61
|
+
|
44
62
|
|
45
63
|
Template Syntax
|
46
64
|
===============
|
@@ -54,7 +72,7 @@ Expression in `<%= ... %>` is escaped according to template class.
|
|
54
72
|
* `BabyErubis::Text` doesn't escape anything.
|
55
73
|
It justs converts expression into a string.
|
56
74
|
* `BabyErubis::Html` escapes html special characters.
|
57
|
-
It converts
|
75
|
+
It converts `< > & " '` into `< > & " '` respectively.
|
58
76
|
|
59
77
|
(Experimental) `<%- ... -%>` and `<%-= ... -%>` are handled same as
|
60
78
|
`<% ... %>` and `<%= ... %>` respectively.
|
@@ -92,7 +110,7 @@ Example:
|
|
92
110
|
end
|
93
111
|
|
94
112
|
def render()
|
95
|
-
return TEMPLATE.render(self)
|
113
|
+
return TEMPLATE.render(self) # use self as context object
|
96
114
|
end
|
97
115
|
|
98
116
|
end
|
@@ -105,8 +123,8 @@ Example:
|
|
105
123
|
String#freeze()
|
106
124
|
---------------
|
107
125
|
|
108
|
-
BabyErubis supports String#freeze() automatically when Ruby version >= 2.1.
|
109
|
-
And you can
|
126
|
+
BabyErubis supports String#freeze() automatically when on Ruby version >= 2.1.
|
127
|
+
And you can control whether to use freeze() or not.
|
110
128
|
|
111
129
|
template_str = <<'END'
|
112
130
|
<div>
|
@@ -292,9 +310,26 @@ Sample code:
|
|
292
310
|
|
293
311
|
|
294
312
|
|
313
|
+
Todo
|
314
|
+
====
|
315
|
+
|
316
|
+
* Support Rails syntax (= `<%= form_for do |f| %>`)
|
317
|
+
|
318
|
+
|
319
|
+
|
295
320
|
Changes
|
296
321
|
=======
|
297
322
|
|
323
|
+
|
324
|
+
Release 1.0.0 (2014-05-17)
|
325
|
+
--------------------------
|
326
|
+
|
327
|
+
* [enhance] Provides script file `bin/baby_erubis`.
|
328
|
+
* [enhance] Supports Ruby 1.8 and Rubinius 2.x.
|
329
|
+
* [change] Define 'BabyErubis::RELEASE'.
|
330
|
+
* [bugfix] 'Template#render()' creates context object when nil passed.
|
331
|
+
|
332
|
+
|
298
333
|
Release 0.1.0 (2014-05-06)
|
299
334
|
--------------------------
|
300
335
|
|
data/Rakefile
CHANGED
@@ -29,8 +29,14 @@ task :test do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
|
32
|
+
desc "remove *.rbc"
|
33
|
+
task :clean do
|
34
|
+
rm_f [Dir.glob("lib/**/*.rbc"), Dir.glob("test/**/*.rbc")]
|
35
|
+
end
|
36
|
+
|
37
|
+
|
32
38
|
desc "copy files into 'dist/#{RELEASE}'"
|
33
|
-
task :dist do
|
39
|
+
task :dist => :clean do
|
34
40
|
require_release_number()
|
35
41
|
spec_src = File.open('baby_erubis.gemspec') {|f| f.read }
|
36
42
|
spec = eval spec_src
|
data/baby_erubis.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
|
3
3
|
###
|
4
|
-
### $Release:
|
4
|
+
### $Release: 1.0.0 $
|
5
5
|
### $License: MIT License $
|
6
6
|
### $Copyright: copyright(c) 2014 kuwata-lab.com all rights reserved $
|
7
7
|
###
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.name = "baby_erubis"
|
14
14
|
s.author = "makoto kuwata"
|
15
15
|
s.email = "kwa(at)kuwata-lab.com"
|
16
|
-
s.version = "$Release:
|
16
|
+
s.version = "$Release: 1.0.0 $".split()[1]
|
17
17
|
s.platform = Gem::Platform::RUBY
|
18
18
|
s.homepage = "https://github.com/kwatch/BabyErubis/tree/ruby"
|
19
19
|
s.summary = "yet another eRuby implementation based on Erubis"
|
@@ -25,14 +25,17 @@ BabyErubis is an yet another eRuby implementation, based on Erubis.
|
|
25
25
|
* Accepts both template file and template string
|
26
26
|
* Easy to customize
|
27
27
|
|
28
|
-
BabyErubis support Ruby 1.9 or higher.
|
28
|
+
BabyErubis support Ruby 1.9 or higher, and will work on 1.8 very well.
|
29
29
|
END
|
30
30
|
|
31
31
|
## files
|
32
32
|
files = []
|
33
33
|
files += Dir.glob('lib/*.rb')
|
34
34
|
files += Dir.glob('test/*.rb')
|
35
|
+
files += ['bin/baby_erubis']
|
35
36
|
files += %w[README.md MIT-LICENSE setup.rb baby_erubis.gemspec Rakefile]
|
36
37
|
s.files = files
|
38
|
+
s.executables = ['baby_erubis']
|
39
|
+
s.bindir = 'bin'
|
37
40
|
s.test_file = 'test/run_all.rb'
|
38
41
|
end
|
data/bin/baby_erubis
ADDED
@@ -0,0 +1,481 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
###
|
5
|
+
### $Release: 1.0.0 $
|
6
|
+
### $Copyright: copyright(c) 2014 kuwata-lab.com all rights reserved $
|
7
|
+
### $License: MIT License $
|
8
|
+
###
|
9
|
+
|
10
|
+
|
11
|
+
#require 'yaml' # on-demand load
|
12
|
+
#require 'json' # on-demand load
|
13
|
+
require 'baby_erubis'
|
14
|
+
|
15
|
+
|
16
|
+
module Cmdopt
|
17
|
+
|
18
|
+
|
19
|
+
def self.new(*args)
|
20
|
+
return Parser.new(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.new_with(*optdef_strs)
|
24
|
+
parser = Parser.new
|
25
|
+
optdef_strs.each do |str|
|
26
|
+
parser.option(str)
|
27
|
+
end
|
28
|
+
return parser
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
class Schema
|
33
|
+
|
34
|
+
def initialize(optstr)
|
35
|
+
short = long = nil
|
36
|
+
case optstr
|
37
|
+
when /\A(?:<(\w+)>)? *-(\w) *, *--(\w+)(?:\[=(\S+)\]|=(\S+))?[ \t]*(?::[ \t]*(.*))?\z/
|
38
|
+
name, short, long, optarg, arg, desc = $1, $2, $3, $4, $5, $6
|
39
|
+
when /\A(?:<(\w+)>)? *-(\w)(?:\[(\S+)\]| +(\S+)?)?[ \t]*(?::[ \t]*(.*))?\z/
|
40
|
+
name, short, optarg, arg, desc = $1, $2, $3, $4, $5
|
41
|
+
when /\A(?:<(\w+)>)? *--(\w+)(?:\[=(\S+)\]|=(\S+))?[ \t]*(?::[ \t]*(.*))?\z/
|
42
|
+
name, long, optarg, arg, desc = $1, $2, $3, $4, $5
|
43
|
+
else
|
44
|
+
raise SchemaError.new("'#{optstr}': invalid option definition.")
|
45
|
+
end
|
46
|
+
@name = name
|
47
|
+
@short = short
|
48
|
+
@long = long
|
49
|
+
@arg = arg || optarg
|
50
|
+
@desc = desc
|
51
|
+
@arg_required = !! arg
|
52
|
+
@arg_optional = !! optarg
|
53
|
+
@validations = []
|
54
|
+
@action = nil
|
55
|
+
#
|
56
|
+
setup()
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader :name, :short, :long, :arg, :desc
|
60
|
+
|
61
|
+
def setup
|
62
|
+
if @arg == 'N' || @optarg == 'N'
|
63
|
+
_add_validation(proc {|arg|
|
64
|
+
"integer expected." unless arg =~ /\A\d+\z/
|
65
|
+
})
|
66
|
+
_set_action(proc {|options, arg|
|
67
|
+
options[canonical_name()] = arg == true ? arg : arg.to_i
|
68
|
+
})
|
69
|
+
end
|
70
|
+
end
|
71
|
+
private :setup
|
72
|
+
|
73
|
+
def arg_required?
|
74
|
+
@arg_required
|
75
|
+
end
|
76
|
+
|
77
|
+
def arg_optional?
|
78
|
+
@arg_optional
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate(optarg)
|
82
|
+
@validations.each do |block|
|
83
|
+
errmsg = block.call(optarg)
|
84
|
+
return errmsg if errmsg
|
85
|
+
end
|
86
|
+
return nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def run_action(options, optarg)
|
90
|
+
if @action
|
91
|
+
@action.call(options, optarg)
|
92
|
+
else
|
93
|
+
options[canonical_name()] = optarg
|
94
|
+
end
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def canonical_name
|
99
|
+
return @name || @long || @short
|
100
|
+
end
|
101
|
+
|
102
|
+
def _add_validation(block)
|
103
|
+
@validations << block
|
104
|
+
end
|
105
|
+
|
106
|
+
def _set_action(block)
|
107
|
+
@action = block
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_help_message(width)
|
111
|
+
if @short && @long
|
112
|
+
if @optarg ; s = "-#{@short}, --#{@long}[=#{@optarg}]"
|
113
|
+
elsif @arg ; s = "-#{@short}, --#{@long}=#{@arg}"
|
114
|
+
else ; s = "-#{@short}, --#{@long}"
|
115
|
+
end
|
116
|
+
elsif @short
|
117
|
+
if @optarg ; s = "-#{@short}[#{@optarg}]"
|
118
|
+
elsif @arg ; s = "-#{@short} #{@arg}"
|
119
|
+
else ; s = "-#{@short}"
|
120
|
+
end
|
121
|
+
elsif @long
|
122
|
+
if @optarg ; s = " --#{@long}[=#{@optarg}]"
|
123
|
+
elsif @arg ; s = " --#{@long}=#{@arg}"
|
124
|
+
else ; s = " --#{@long}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
s << ' ' * (width - s.length) if s.length < width
|
128
|
+
s << ': ' << @desc.to_s
|
129
|
+
s << "\n"
|
130
|
+
return s
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
class SchemaError < StandardError
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
class SchemaBuilder
|
141
|
+
|
142
|
+
def initialize(schema)
|
143
|
+
@schema = schema
|
144
|
+
end
|
145
|
+
|
146
|
+
def validation(&block)
|
147
|
+
@schema._add_validation(block)
|
148
|
+
return self
|
149
|
+
end
|
150
|
+
|
151
|
+
def action(&block)
|
152
|
+
@schema._set_action(block)
|
153
|
+
return self
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
class Parser
|
160
|
+
|
161
|
+
def initialize(cmdname=true)
|
162
|
+
cmdname = File.basename($0) if cmdname == true
|
163
|
+
@cmdname = cmdname
|
164
|
+
@schemas = []
|
165
|
+
end
|
166
|
+
|
167
|
+
def option(optstr)
|
168
|
+
schema = Schema.new(optstr)
|
169
|
+
@schemas << schema
|
170
|
+
return SchemaBuilder.new(schema)
|
171
|
+
end
|
172
|
+
|
173
|
+
def parse(argv)
|
174
|
+
options = {}
|
175
|
+
while argv[0] && argv[0] =~ /\A-/
|
176
|
+
optstr = argv.shift
|
177
|
+
if optstr == '--'
|
178
|
+
break
|
179
|
+
elsif optstr =~ /\A--(\w+)(?:=(.*))?/
|
180
|
+
optname = $1
|
181
|
+
optarg = $2 || true
|
182
|
+
schema = @schemas.find {|sch| sch.long == optname } or
|
183
|
+
raise error("--#{optname}: unknown option.")
|
184
|
+
if schema.arg_required? && optarg == true
|
185
|
+
raise error("--#{optname}: argument required.")
|
186
|
+
end
|
187
|
+
errmsg = schema.validate(optarg)
|
188
|
+
raise error("#{optstr}: #{errmsg}") if errmsg
|
189
|
+
schema.run_action(options, optarg)
|
190
|
+
else
|
191
|
+
i = 1
|
192
|
+
while i < optstr.length
|
193
|
+
optch = optstr[i, 1]
|
194
|
+
schema = @schemas.find {|sch| sch.short == optch } or
|
195
|
+
raise error("-#{optch}: unknown option.")
|
196
|
+
if schema.arg_required?
|
197
|
+
optarg = (i+1 < optstr.length) ? optstr[(i+1)..-1] : argv.shift or
|
198
|
+
raise error("-#{optch}: argument required.")
|
199
|
+
errmsg = schema.validate(optarg)
|
200
|
+
raise error("-#{optch} #{optarg}: #{errmsg}") if errmsg
|
201
|
+
i = optstr.length
|
202
|
+
elsif schema.arg_optional?
|
203
|
+
optarg = (i+1 < optstr.length) ? optstr[(i+1)..-1] : true
|
204
|
+
errmsg = optarg != true ? schema.validate(optarg) : nil
|
205
|
+
raise error("-#{optch}#{optarg}: #{errmsg}") if errmsg
|
206
|
+
i = optstr.length
|
207
|
+
else
|
208
|
+
optarg = true
|
209
|
+
i += 1
|
210
|
+
end
|
211
|
+
schema.run_action(options, optarg)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
return options
|
216
|
+
end
|
217
|
+
|
218
|
+
def help_message(width=30)
|
219
|
+
s = ""
|
220
|
+
@schemas.each do |schema|
|
221
|
+
s << " " << schema.to_help_message(width-2) if schema.desc
|
222
|
+
end
|
223
|
+
return s
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
def error(message)
|
229
|
+
message = "#{@cmdname}: #{message}" if @cmdname && @cmdname.length > 0
|
230
|
+
return ParseError.new(message)
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
class ParseError < StandardError
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
module BabyErubis
|
244
|
+
|
245
|
+
|
246
|
+
module HideTextEnhander
|
247
|
+
|
248
|
+
def build_text(text)
|
249
|
+
return text.to_s.gsub(/^.*\n/, "\n").gsub(/./, ' ')
|
250
|
+
end
|
251
|
+
alias _t build_text
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
class Main
|
260
|
+
|
261
|
+
def self.main(argv=ARGV)
|
262
|
+
begin
|
263
|
+
self.new.run(argv)
|
264
|
+
return 0
|
265
|
+
rescue Cmdopt::ParseError => ex
|
266
|
+
$stderr << ex.message << "\n"
|
267
|
+
return 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def initialize(cmdname=File.basename($0))
|
272
|
+
@cmdname = cmdname
|
273
|
+
end
|
274
|
+
|
275
|
+
def run(argv)
|
276
|
+
parser = build_parser()
|
277
|
+
options = parser.parse(argv)
|
278
|
+
if options['help']
|
279
|
+
$stdout << build_help_message(parser)
|
280
|
+
return
|
281
|
+
end
|
282
|
+
if options['version']
|
283
|
+
$stdout << BabyErubis::RELEASE << "\n"
|
284
|
+
return
|
285
|
+
end
|
286
|
+
#
|
287
|
+
klass = handle_format(options['format'] || (options['H'] ? 'html' : nil))
|
288
|
+
if options['X']
|
289
|
+
klass = Class.new(klass)
|
290
|
+
klass.class_eval { include BabyErubis::HideTextEnhander }
|
291
|
+
end
|
292
|
+
#
|
293
|
+
freeze = handle_freeze(options['freeze'])
|
294
|
+
tplopt = {:freeze=>freeze}
|
295
|
+
#
|
296
|
+
encoding = options['encoding'] || 'utf-8'
|
297
|
+
context = options['c']
|
298
|
+
datafile = options['f']
|
299
|
+
show_src = options['x'] || options['X']
|
300
|
+
(argv.empty? ? [nil] : argv).each do |filename|
|
301
|
+
if filename
|
302
|
+
template = klass.new(tplopt).from_file(filename, encoding)
|
303
|
+
else
|
304
|
+
template_str = $stdin.read()
|
305
|
+
template = klass.new(tplopt).from_str(template_str, '(stdin)')
|
306
|
+
end
|
307
|
+
if show_src
|
308
|
+
$stdout << edit_src(template.src, options['N'], options['U'], options['C'])
|
309
|
+
else
|
310
|
+
ctxobj = template.new_context({})
|
311
|
+
handle_datafile(datafile, encoding, ctxobj) if datafile
|
312
|
+
handle_context(context, encoding, ctxobj) if context
|
313
|
+
output = template.render(ctxobj)
|
314
|
+
$stdout << output
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
private
|
320
|
+
|
321
|
+
def build_parser
|
322
|
+
parser = Cmdopt.new(@cmdname)
|
323
|
+
parser.option("-h, --help : help")
|
324
|
+
parser.option("-v, --version : version")
|
325
|
+
parser.option("-x : show ruby code")
|
326
|
+
parser.option("-X : show ruby code only (no text part)")
|
327
|
+
parser.option("-N : numbering: add line numbers (for '-x/-X')")
|
328
|
+
parser.option("-U : unique: compress empty lines (for '-x/-X')")
|
329
|
+
parser.option("-C : compact: remove empty lines (for '-x/-X')")
|
330
|
+
parser.option("-c context : context string (yaml inline style or ruby code)")
|
331
|
+
parser.option("-f file : context data file (*.yaml, *.json, or *.rb)")
|
332
|
+
parser.option("-H : same as --format=html")
|
333
|
+
parser.option(" --format={text|html} : format (default: text)")\
|
334
|
+
.validation {|arg| "'text' or 'html' expected" if arg !~ /\A(text|html)\z/ }
|
335
|
+
parser.option(" --encoding=name : encoding (default: utf-8)")
|
336
|
+
parser.option(" --freeze={true|false} : use String#freeze() or not")\
|
337
|
+
.validation {|arg| "'true' or 'false' expected" if arg !~ /\A(true|false)\z/ }
|
338
|
+
parser.option("-D")
|
339
|
+
return parser
|
340
|
+
end
|
341
|
+
|
342
|
+
def build_help_message(parser)
|
343
|
+
s = "Usage: #{@cmdname} [..options..] [erubyfile]\n"
|
344
|
+
s << parser.help_message(30)
|
345
|
+
s << "\n"
|
346
|
+
s << <<"END"
|
347
|
+
Example:
|
348
|
+
## convert eRuby file into Ruby code
|
349
|
+
$ #{@cmdname} -x file.erb # text
|
350
|
+
$ #{@cmdname} -xH file.erb # html
|
351
|
+
$ #{@cmdname} -X file.erb # embedded code only
|
352
|
+
## render eRuby file with context data
|
353
|
+
$ #{@cmdname} -c '{items: [A, B, C]}' file.erb # YAML
|
354
|
+
$ #{@cmdname} -c '@items=["A","B","C"]' file.erb # Ruby
|
355
|
+
$ #{@cmdname} -f data.yaml file.erb # or -f *.json, *.rb
|
356
|
+
## debug eRuby file
|
357
|
+
$ #{@cmdname} -xH file.erb | ruby -wc # check syntax error
|
358
|
+
$ #{@cmdname} -XHNU file.erb # show embedded ruby code
|
359
|
+
END
|
360
|
+
return s
|
361
|
+
end
|
362
|
+
|
363
|
+
def handle_format(format)
|
364
|
+
case format
|
365
|
+
when nil ; return BabyErubis::Text
|
366
|
+
when 'text'; return BabyErubis::Text
|
367
|
+
when 'html'; return BabyErubis::Html
|
368
|
+
else
|
369
|
+
raise "** unreachable: format=#{format.inspect}"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def handle_freeze(freeze)
|
374
|
+
case freeze
|
375
|
+
when nil ; return nil
|
376
|
+
when 'true' ; return true
|
377
|
+
when 'false'; return false
|
378
|
+
else
|
379
|
+
raise "** unreachable: options['freeze']=#{options['freeze'].inspect}"
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def handle_context(context_str, encoding, context_obj)
|
384
|
+
return nil if context_str.nil? || context_str.empty?
|
385
|
+
if context_str =~ /\A\{/
|
386
|
+
kind = 'YAML'
|
387
|
+
require 'yaml'
|
388
|
+
dict = YAML.load(context_str) # raises Psych::SyntaxError
|
389
|
+
dict.is_a?(Hash) or
|
390
|
+
raise Cmdopt::ParseError.new("-c '#{context_str}': YAML mapping expected.")
|
391
|
+
dict.each {|k, v| context_obj.instance_variable_set("@#{k}", v) }
|
392
|
+
else
|
393
|
+
kind = 'Ruby'
|
394
|
+
_eval(context_str, context_obj) # raises SyntaxError
|
395
|
+
end
|
396
|
+
return context_obj
|
397
|
+
#rescue Psych::SyntaxError, SyntaxError => ex
|
398
|
+
rescue Exception => ex
|
399
|
+
errmsg = ex.to_s
|
400
|
+
case ex.class.name
|
401
|
+
when 'Psych::SyntaxError'; errmsg = errmsg.sub(/\(<unknown>\): /, '')
|
402
|
+
when 'SyntaxError'; errmsg = errmsg.sub(/\(eval\):\d: syntax error, /, '')
|
403
|
+
else
|
404
|
+
if ex.class == ArgumentError && errmsg =~ /^syntax error on line \d+, (col \d+)/
|
405
|
+
errmsg = $1 # for Rubinius
|
406
|
+
else
|
407
|
+
raise ex
|
408
|
+
end
|
409
|
+
end
|
410
|
+
raise Cmdopt::ParseError.new("-c '#{context_str}': #{kind} syntax error: (#{ex.class.name}) #{errmsg}")
|
411
|
+
end
|
412
|
+
|
413
|
+
def handle_datafile(datafile, encoding, context_obj)
|
414
|
+
return nil unless datafile
|
415
|
+
case datafile
|
416
|
+
when /\.ya?ml\z/
|
417
|
+
kind = 'YAML'
|
418
|
+
require 'yaml'
|
419
|
+
dict = File.open(datafile, "rb:utf-8") {|f| YAML.load(f) } # raises Psych::SyntaxError
|
420
|
+
dict.is_a?(Hash) or
|
421
|
+
raise Cmdopt::ParseError.new("-f #{datafile}: YAML mapping expected.")
|
422
|
+
dict.each {|k, v| context_obj.instance_variable_set("@#{k}", v) }
|
423
|
+
when /\.json\z/
|
424
|
+
kind = 'JSON'
|
425
|
+
require 'json'
|
426
|
+
json_str = File.open(datafile, "rb:utf-8") {|f| f.read() }
|
427
|
+
dict = JSON.load(json_str) # raises JSON::ParserError
|
428
|
+
dict.is_a?(Hash) or
|
429
|
+
raise Cmdopt::ParseError.new("-f #{datafile}: JSON object expected.")
|
430
|
+
dict.each {|k, v| context_obj.instance_variable_set("@#{k}", v) }
|
431
|
+
when /\.rb\z/
|
432
|
+
kind = 'Ruby'
|
433
|
+
context_str = File.open(datafile, "rb:utf-8") {|f| f.read() }
|
434
|
+
_eval(context_str, context_obj) # raises SyntaxError
|
435
|
+
else
|
436
|
+
raise Cmdopt::ParseError.new("-f #{datafile}: unknown suffix (expected '.yaml', '.json', or '.rb').")
|
437
|
+
end
|
438
|
+
return context_obj
|
439
|
+
rescue Errno::ENOENT => ex
|
440
|
+
raise Cmdopt::ParseError.new("-f #{datafile}: file not found.")
|
441
|
+
#rescue Psych::SyntaxError, JSON::ParserError, SyntaxError => ex
|
442
|
+
rescue Exception => ex
|
443
|
+
errmsg = ex.to_s
|
444
|
+
case ex.class.name
|
445
|
+
when 'Psych::SyntaxError'; errmsg = errmsg.sub(/\(<unknown>\): /, '')
|
446
|
+
when 'JSON::ParserError'; errmsg = errmsg.sub(/^(\d+): (.*) at .*\n?/, '\1: \2')
|
447
|
+
when 'SyntaxError'; errmsg = errmsg.sub(/\(eval\):\d: syntax error, /, '')
|
448
|
+
else
|
449
|
+
if ex.class == ArgumentError && errmsg =~ /^syntax error on line \d+, (col \d+)/
|
450
|
+
errmsg = $1 # for Rubinius
|
451
|
+
else
|
452
|
+
raise ex
|
453
|
+
end
|
454
|
+
end
|
455
|
+
raise Cmdopt::ParseError.new("-f #{datafile}: #{kind} syntax error: (#{ex.class.name}) #{errmsg}")
|
456
|
+
end
|
457
|
+
|
458
|
+
def _eval(_context_str, _context_obj)
|
459
|
+
_context_obj.instance_eval(_context_str)
|
460
|
+
end
|
461
|
+
|
462
|
+
def edit_src(src, numbering, unique, compact)
|
463
|
+
if numbering # -N
|
464
|
+
i = 0
|
465
|
+
src = src.gsub(/^/) { "%4d: " % (i+=1) }
|
466
|
+
src = src.gsub(/(^ *\d+:\s*\n)+/, "\n") if unique # -U
|
467
|
+
src = src.gsub(/^ *\d+:\s*\n/, '') if compact # -C
|
468
|
+
else
|
469
|
+
src = src.gsub(/(^\s*\n)+/, "\n") if unique # -U
|
470
|
+
src = src.gsub(/^\s*\n/, '') if compact # -C
|
471
|
+
end
|
472
|
+
return src
|
473
|
+
end
|
474
|
+
|
475
|
+
|
476
|
+
end
|
477
|
+
|
478
|
+
|
479
|
+
#if __FILE__ == $0
|
480
|
+
exit Main.main() unless defined? NOEXEC_SCRIPT
|
481
|
+
#end
|