baby_erubis 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|