migr8 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +356 -0
- data/Rakefile +133 -0
- data/bin/migr8.rb +14 -0
- data/lib/migr8.rb +2645 -0
- data/migr8.gemspec +57 -0
- data/setup.rb +1585 -0
- data/test/Application_test.rb +148 -0
- data/test/Migration_test.rb +261 -0
- data/test/Util_test.rb +873 -0
- data/test/helpers.rb +93 -0
- data/test/oktest.rb +1537 -0
- data/test/run_all.rb +8 -0
- metadata +71 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'oktest'
|
4
|
+
require 'migr8'
|
5
|
+
require 'stringio'
|
6
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'helpers')
|
7
|
+
|
8
|
+
|
9
|
+
Oktest.scope do
|
10
|
+
|
11
|
+
|
12
|
+
topic Migr8::Application do
|
13
|
+
|
14
|
+
klass = Migr8::Application
|
15
|
+
|
16
|
+
|
17
|
+
topic '.run()' do
|
18
|
+
|
19
|
+
fixture :app do
|
20
|
+
Migr8::Application.new
|
21
|
+
end
|
22
|
+
|
23
|
+
spec "[!dcggy] sets Migr8::DEBUG=true when '-d' or '--debug' specified." do |app|
|
24
|
+
[
|
25
|
+
["-D"],
|
26
|
+
["--debug"],
|
27
|
+
].each do |args|
|
28
|
+
at_exit { Migr8.DEBUG = false }
|
29
|
+
sout, serr = Dummy.new.stdouterr do
|
30
|
+
Migr8::DEBUG = false
|
31
|
+
ok {Migr8::DEBUG} == false
|
32
|
+
status = app.run(args)
|
33
|
+
ok {Migr8::DEBUG} == true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
spec "[!ktlay] prints help message and exit when '-h' or '--help' specified." do |app|
|
39
|
+
[
|
40
|
+
["-h", "foo"],
|
41
|
+
["--help", "foo"],
|
42
|
+
].each do |args|
|
43
|
+
sout, serr = Dummy.new.stdouterr do
|
44
|
+
status = app.run(args)
|
45
|
+
ok {status} == 0
|
46
|
+
ok {args} == ["foo"]
|
47
|
+
end
|
48
|
+
expected = <<END
|
49
|
+
#{File.basename($0)} -- database schema version management tool
|
50
|
+
|
51
|
+
Usage: #{File.basename($0)} [global-options] [action [options] [...]]
|
52
|
+
-h, --help : show help
|
53
|
+
-v, --version : show version
|
54
|
+
-D, --debug : not remove sql file ('migr8/tmp.sql') for debug
|
55
|
+
|
56
|
+
Actions: (default: status)
|
57
|
+
readme : !!READ ME AT FIRST!!
|
58
|
+
help [action] : show help message of action, or list action names
|
59
|
+
init : create necessary files and a table
|
60
|
+
hist : list history of versions
|
61
|
+
new : create new migration file and open it by $MIGR8_EDITOR
|
62
|
+
show [version] : show migration file with expanding variables
|
63
|
+
edit [version] : open migration file by $MIGR8_EDITOR
|
64
|
+
status : show status
|
65
|
+
up : apply next migration
|
66
|
+
down : unapply current migration
|
67
|
+
redo : do migration down, and up it again
|
68
|
+
apply version ... : apply specified migrations
|
69
|
+
unapply version ... : unapply specified migrations
|
70
|
+
delete version ... : delete unapplied migration file
|
71
|
+
|
72
|
+
(ATTENTION!! Run '#{File.basename($0)} readme' at first if you don't know #{File.basename($0)} well.)
|
73
|
+
|
74
|
+
END
|
75
|
+
ok {sout} == expected
|
76
|
+
ok {serr} == ""
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
spec "[!n0ubh] prints version string and exit when '-v' or '--version' specified." do |app|
|
81
|
+
[
|
82
|
+
["-v", "foo", "bar"],
|
83
|
+
["--version", "foo", "bar"],
|
84
|
+
].each do |args|
|
85
|
+
sout, serr = Dummy.new.stdouterr do
|
86
|
+
status = app.run(args)
|
87
|
+
ok {status} == 0
|
88
|
+
ok {args} == ["foo", "bar"]
|
89
|
+
end
|
90
|
+
expected = "#{Migr8::RELEASE}\n"
|
91
|
+
ok {sout} == expected
|
92
|
+
ok {serr} == ""
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
spec "[!saisg] returns 0 as status code when succeeded." do |app|
|
97
|
+
[
|
98
|
+
#["foo", "bar"],
|
99
|
+
[],
|
100
|
+
].each do |args|
|
101
|
+
sout, serr = Dummy.new.stdouterr do
|
102
|
+
status = app.run(args)
|
103
|
+
ok {status} == 0
|
104
|
+
ok {args} == [] # ["foo", "bar"]
|
105
|
+
end
|
106
|
+
ok {sout}.NOT == ""
|
107
|
+
ok {serr} == ""
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
topic '.main()' do
|
115
|
+
|
116
|
+
spec "[!cy0yo] uses ARGV when args is not passed." do
|
117
|
+
bkup = ARGV.dup
|
118
|
+
ARGV[0..-1] = ["-h", "-v", "foo", "bar"]
|
119
|
+
Dummy.new.stdouterr do
|
120
|
+
klass.main()
|
121
|
+
ok {ARGV} == ["foo", "bar"]
|
122
|
+
end
|
123
|
+
ARGV[0..-1] = bkup
|
124
|
+
end
|
125
|
+
|
126
|
+
spec "[!t0udo] returns status code (0: ok, 1: error)." do
|
127
|
+
Dummy.new.stdouterr do
|
128
|
+
status = klass.main(["-hv"])
|
129
|
+
ok {status} == 0
|
130
|
+
status = klass.main(["-hx"])
|
131
|
+
ok {status} == 1
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
spec "[!maomq] command-option error is cached and not raised." do
|
136
|
+
Dummy.new.stdouterr do
|
137
|
+
pr = proc { klass.main(["-hx"]) }
|
138
|
+
ok {pr}.NOT.raise?(Migr8::Util::CommandOptionError)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'oktest'
|
4
|
+
require 'migr8'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
|
8
|
+
Oktest.scope do
|
9
|
+
|
10
|
+
|
11
|
+
topic Migr8::Migration do
|
12
|
+
|
13
|
+
klass = Migr8::Migration
|
14
|
+
|
15
|
+
|
16
|
+
topic '#initalize()' do
|
17
|
+
|
18
|
+
spec "[!y4dy3] takes version, author, and desc arguments." do
|
19
|
+
mig = klass.new('abcd1234', 'user1', 'desc1')
|
20
|
+
ok {mig.version} == 'abcd1234'
|
21
|
+
ok {mig.author} == 'user1'
|
22
|
+
ok {mig.desc} == 'desc1'
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
topic '#applied?' do
|
29
|
+
|
30
|
+
spec "[!ebzct] returns false when @applied_at is nil, else true." do
|
31
|
+
mig = klass.new()
|
32
|
+
ok {mig.applied?} == false
|
33
|
+
mig.applied_at = '2013-01-01 12:34:56'
|
34
|
+
ok {mig.applied?} == true
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
topic '#up_script' do
|
41
|
+
|
42
|
+
spec "[!200k7] returns @up_script if it is set." do
|
43
|
+
mig = klass.new()
|
44
|
+
mig.up_script = "xxx"
|
45
|
+
ok {mig.up_script} == "xxx"
|
46
|
+
end
|
47
|
+
|
48
|
+
spec "[!cfp34] returns nil when 'up' is not set." do
|
49
|
+
mig = klass.new()
|
50
|
+
mig.up = nil
|
51
|
+
ok {mig.up_script} == nil
|
52
|
+
end
|
53
|
+
|
54
|
+
spec "[!6gaxb] returns 'up' string expanding vars in it." do
|
55
|
+
original = <<END
|
56
|
+
create table ${table} (
|
57
|
+
id serial primary key;
|
58
|
+
name varchar(255) not null;
|
59
|
+
);
|
60
|
+
create index ${table}_${column}_idx on ${table}(${column});
|
61
|
+
END
|
62
|
+
expanded = <<END
|
63
|
+
create table sample1 (
|
64
|
+
id serial primary key;
|
65
|
+
name varchar(255) not null;
|
66
|
+
);
|
67
|
+
create index sample1_name_idx on sample1(name);
|
68
|
+
END
|
69
|
+
mig = klass.new
|
70
|
+
mig.up = original
|
71
|
+
mig.vars = {'table'=>'sample1', 'column'=>'name'}
|
72
|
+
ok {mig.up_script} == expanded
|
73
|
+
end
|
74
|
+
|
75
|
+
spec "[!jeomg] renders 'up' script as eRuby template." do
|
76
|
+
original = <<END
|
77
|
+
insert into ${table}(${column}) values
|
78
|
+
<% comma = " " %>
|
79
|
+
<% for name in %w[Haruhi Mikuru Yuki] %>
|
80
|
+
<%= comma %>('<%= name %>')
|
81
|
+
<% comma = ", " %>
|
82
|
+
<% end %>
|
83
|
+
;
|
84
|
+
END
|
85
|
+
expanded = <<END
|
86
|
+
insert into users(name) values
|
87
|
+
('Haruhi')
|
88
|
+
, ('Mikuru')
|
89
|
+
, ('Yuki')
|
90
|
+
;
|
91
|
+
END
|
92
|
+
mig = klass.new
|
93
|
+
mig.up = original
|
94
|
+
mig.vars = {'table'=>'users', 'column'=>'name'}
|
95
|
+
ok {mig.up_script} == expanded
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
topic '#down_script' do
|
102
|
+
|
103
|
+
spec "[!27n2l] returns @down_script if it is set." do
|
104
|
+
mig = klass.new
|
105
|
+
mig.down_script = "xxx"
|
106
|
+
ok {mig.down_script} == "xxx"
|
107
|
+
end
|
108
|
+
|
109
|
+
spec "[!e45s1] returns nil when 'down' is not set." do
|
110
|
+
mig = klass.new
|
111
|
+
mig.down = nil
|
112
|
+
ok {mig.down_script} == nil
|
113
|
+
end
|
114
|
+
|
115
|
+
spec "[!0q3nq] returns 'down' string expanding vars in it." do
|
116
|
+
original = <<END
|
117
|
+
drop index ${table}_${column}_idx;
|
118
|
+
drop table ${table};
|
119
|
+
END
|
120
|
+
expanded = <<END
|
121
|
+
drop index sample1_name_idx;
|
122
|
+
drop table sample1;
|
123
|
+
END
|
124
|
+
mig = klass.new
|
125
|
+
mig.down = original
|
126
|
+
mig.vars = {'table'=>'sample1', 'column'=>'name'};
|
127
|
+
ok {mig.down_script} == expanded
|
128
|
+
end
|
129
|
+
|
130
|
+
spec "[!kpwut] renders 'up' script as eRuby template." do
|
131
|
+
original = <<END
|
132
|
+
<% for name in %w[Haruhi Mikuru Yuki] %>
|
133
|
+
delete from ${table} where ${column} = '<%= name %>';
|
134
|
+
<% end %>
|
135
|
+
END
|
136
|
+
expanded = <<END
|
137
|
+
delete from users where name = 'Haruhi';
|
138
|
+
delete from users where name = 'Mikuru';
|
139
|
+
delete from users where name = 'Yuki';
|
140
|
+
END
|
141
|
+
mig = klass.new
|
142
|
+
mig.down = original
|
143
|
+
mig.vars = {'table'=>'users', 'column'=>'name'}
|
144
|
+
ok {mig.down_script} == expanded
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
topic '#_render()' do
|
151
|
+
|
152
|
+
spec "[!1w3ov] renders string with 'vars' as context variables." do
|
153
|
+
mig = klass.new
|
154
|
+
mig.vars = {'table'=>'users', 'columns'=>['id', 'name']}
|
155
|
+
src = "@table: <%=@table.inspect%>; @columns: <%=@columns.inspect%>;"
|
156
|
+
output = mig.__send__(:_render, src)
|
157
|
+
ok {output} == '@table: "users"; @columns: ["id", "name"];'
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
topic '#applied_at_or()' do
|
164
|
+
|
165
|
+
spec "[!zazux] returns default arugment when not applied." do
|
166
|
+
mig = klass.new
|
167
|
+
ok {mig.applied?} == false
|
168
|
+
ok {mig.applied_at_or('(not applied)')} == '(not applied)'
|
169
|
+
end
|
170
|
+
|
171
|
+
spec "[!fxb4y] returns @applied_at without msec." do
|
172
|
+
mig = klass.new
|
173
|
+
mig.applied_at = '2013-01-01 12:34:56.789'
|
174
|
+
ok {mig.applied?} == true
|
175
|
+
ok {mig.applied_at_or('(not applied)')} == '2013-01-01 12:34:56'
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
topic '#filepath' do
|
182
|
+
|
183
|
+
spec "[!l9t5k] returns nil when version is not set." do
|
184
|
+
mig = klass.new
|
185
|
+
mig.version = nil
|
186
|
+
ok {mig.filepath} == nil
|
187
|
+
end
|
188
|
+
|
189
|
+
spec "[!p0d9q] returns filepath of migration file." do
|
190
|
+
mig = klass.new
|
191
|
+
mig.version = 'abcd1234'
|
192
|
+
ok {mig.filepath} == 'migr8/migrations/abcd1234.yaml'
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
topic '.load_from()' do
|
199
|
+
|
200
|
+
fixture :mig_filepath do
|
201
|
+
content = <<END
|
202
|
+
# -*- coding: utf-8 -*-
|
203
|
+
version: wxyz7890
|
204
|
+
author: haruhi
|
205
|
+
desc: test migration '#1'
|
206
|
+
vars:
|
207
|
+
- table: members
|
208
|
+
- column: name
|
209
|
+
- index: ${table}_${column}_idx
|
210
|
+
|
211
|
+
up: |
|
212
|
+
cteate table ${table}(
|
213
|
+
id serial primary key,
|
214
|
+
name varchar(255) not null
|
215
|
+
);
|
216
|
+
create index ${index} on ${table}(${name});
|
217
|
+
|
218
|
+
down: |
|
219
|
+
drop table ${table};
|
220
|
+
END
|
221
|
+
fpath = "tmp.#{rand().to_s[2..5]}.yaml"
|
222
|
+
File.open(fpath, 'w') {|f| f.write(content) }
|
223
|
+
at_end { File.unlink(fpath) }
|
224
|
+
fpath
|
225
|
+
end
|
226
|
+
|
227
|
+
spec "[!fbea5] loads data from file and returns migration object." do |mig_filepath|
|
228
|
+
mig = klass.load_from(mig_filepath)
|
229
|
+
ok {mig}.is_a?(Migr8::Migration)
|
230
|
+
ok {mig.version} == 'wxyz7890'
|
231
|
+
ok {mig.author} == 'haruhi'
|
232
|
+
ok {mig.desc} == 'test migration \'#1\''
|
233
|
+
end
|
234
|
+
|
235
|
+
spec "[!sv21s] expands values of 'vars'." do |mig_filepath|
|
236
|
+
mig = klass.load_from(mig_filepath)
|
237
|
+
ok {mig.vars} == {'table'=>'members', 'column'=>'name',
|
238
|
+
'index'=>'members_name_idx'}
|
239
|
+
end
|
240
|
+
|
241
|
+
spec "[!32ns3] not expand both 'up' and 'down'." do |mig_filepath|
|
242
|
+
mig = klass.load_from(mig_filepath)
|
243
|
+
ok {mig.up} == <<END
|
244
|
+
cteate table ${table}(
|
245
|
+
id serial primary key,
|
246
|
+
name varchar(255) not null
|
247
|
+
);
|
248
|
+
create index ${index} on ${table}(${name});
|
249
|
+
END
|
250
|
+
ok {mig.down} == <<END
|
251
|
+
drop table ${table};
|
252
|
+
END
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
end
|
data/test/Util_test.rb
ADDED
@@ -0,0 +1,873 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'oktest'
|
4
|
+
require 'migr8'
|
5
|
+
|
6
|
+
|
7
|
+
Oktest.scope do
|
8
|
+
|
9
|
+
|
10
|
+
topic Migr8::Util::CommandOptionDefinition do
|
11
|
+
|
12
|
+
klass = Migr8::Util::CommandOptionDefinition
|
13
|
+
errclass = Migr8::Util::CommandOptionDefinitionError
|
14
|
+
|
15
|
+
|
16
|
+
topic '.new()' do
|
17
|
+
|
18
|
+
spec "parses definition string of short and long options without arg." do
|
19
|
+
[
|
20
|
+
"-h, --help: show help",
|
21
|
+
"-h,--help: show help",
|
22
|
+
" -h, --help : show help ",
|
23
|
+
].each do |defstr|
|
24
|
+
x = klass.new(defstr)
|
25
|
+
ok {x.short} == 'h'
|
26
|
+
ok {x.long} == 'help'
|
27
|
+
ok {x.name} == 'help'
|
28
|
+
ok {x.arg} == nil
|
29
|
+
ok {x.desc} == 'show help'
|
30
|
+
ok {x.arg_required} == false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
spec "parses definition string of short and long options with required arg." do
|
35
|
+
[
|
36
|
+
"-a, --action=name: action name.",
|
37
|
+
"-a,--action=name: action name.",
|
38
|
+
" -a, --action=name : action name. ",
|
39
|
+
].each do |defstr|
|
40
|
+
x = klass.new(defstr)
|
41
|
+
ok {x.short} == 'a'
|
42
|
+
ok {x.long} == 'action'
|
43
|
+
ok {x.name} == 'action'
|
44
|
+
ok {x.arg} == 'name'
|
45
|
+
ok {x.desc} == 'action name.'
|
46
|
+
ok {x.arg_required} == true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
spec "parses definition string of short and long options with optional arg." do
|
51
|
+
[
|
52
|
+
"-i, --indent[=N]: indent depth (default 2).",
|
53
|
+
"-i,--indent[=N]: indent depth (default 2).",
|
54
|
+
" -i, --indent[=N] : indent depth (default 2). ",
|
55
|
+
].each do |defstr|
|
56
|
+
x = klass.new(defstr)
|
57
|
+
ok {x.short} == 'i'
|
58
|
+
ok {x.long} == 'indent'
|
59
|
+
ok {x.name} == 'indent'
|
60
|
+
ok {x.arg} == 'N'
|
61
|
+
ok {x.desc} == 'indent depth (default 2).'
|
62
|
+
ok {x.arg_required} == nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
spec "parses definition string of short-only options without arg." do
|
67
|
+
[
|
68
|
+
"-q: be quiet",
|
69
|
+
" -q : be quiet ",
|
70
|
+
].each do |defstr|
|
71
|
+
x = klass.new(defstr)
|
72
|
+
ok {x.short} == 'q'
|
73
|
+
ok {x.long} == nil
|
74
|
+
ok {x.name} == 'q'
|
75
|
+
ok {x.arg} == nil
|
76
|
+
ok {x.desc} == 'be quiet'
|
77
|
+
ok {x.arg_required} == false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
spec "parses definition string of short-only options with required arg." do
|
82
|
+
[
|
83
|
+
"-a name: action name.",
|
84
|
+
" -a name : action name. ",
|
85
|
+
].each do |defstr|
|
86
|
+
x = klass.new(defstr)
|
87
|
+
ok {x.short} == 'a'
|
88
|
+
ok {x.long} == nil
|
89
|
+
ok {x.name} == 'a'
|
90
|
+
ok {x.arg} == 'name'
|
91
|
+
ok {x.desc} == 'action name.'
|
92
|
+
ok {x.arg_required} == true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
spec "parses definition string of short-only options with optional arg." do
|
97
|
+
[
|
98
|
+
"-i[N]: indent depth (default 2).",
|
99
|
+
" -i[N] : indent depth (default 2). ",
|
100
|
+
].each do |defstr|
|
101
|
+
x = klass.new(defstr)
|
102
|
+
ok {x.short} == 'i'
|
103
|
+
ok {x.long} == nil
|
104
|
+
ok {x.name} == 'i'
|
105
|
+
ok {x.arg} == 'N'
|
106
|
+
ok {x.desc} == 'indent depth (default 2).'
|
107
|
+
ok {x.arg_required} == nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
spec "parses definition string of long-only options without arg." do
|
112
|
+
[
|
113
|
+
"--verbose: be verbose",
|
114
|
+
" --verbose : be verbose ",
|
115
|
+
].each do |defstr|
|
116
|
+
x = klass.new(defstr)
|
117
|
+
ok {x.short} == nil
|
118
|
+
ok {x.long} == 'verbose'
|
119
|
+
ok {x.name} == 'verbose'
|
120
|
+
ok {x.arg} == nil
|
121
|
+
ok {x.desc} == 'be verbose'
|
122
|
+
ok {x.arg_required} == false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
spec "parses definition string of long-only options with required arg." do
|
127
|
+
[
|
128
|
+
"--action=name: action name.",
|
129
|
+
" --action=name : action name. ",
|
130
|
+
].each do |defstr|
|
131
|
+
x = klass.new(defstr)
|
132
|
+
ok {x.short} == nil
|
133
|
+
ok {x.long} == 'action'
|
134
|
+
ok {x.name} == 'action'
|
135
|
+
ok {x.arg} == 'name'
|
136
|
+
ok {x.desc} == 'action name.'
|
137
|
+
ok {x.arg_required} == true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
spec "parses definition string of long-only options with optional arg." do
|
142
|
+
[
|
143
|
+
"--indent[=N]: indent depth (default 2).",
|
144
|
+
" --indent[=N] : indent depth (default 2). ",
|
145
|
+
].each do |defstr|
|
146
|
+
x = klass.new(defstr)
|
147
|
+
ok {x.short} == nil
|
148
|
+
ok {x.long} == 'indent'
|
149
|
+
ok {x.name} == 'indent'
|
150
|
+
ok {x.arg} == 'N'
|
151
|
+
ok {x.desc} == 'indent depth (default 2).'
|
152
|
+
ok {x.arg_required} == nil
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
spec "detects '#name' notation to override option name." do
|
157
|
+
#
|
158
|
+
[
|
159
|
+
"-h #usage : show usage.",
|
160
|
+
].each do |defstr|
|
161
|
+
x = klass.new(defstr)
|
162
|
+
ok {x.short} == 'h'
|
163
|
+
ok {x.long} == nil
|
164
|
+
ok {x.name} == 'usage'
|
165
|
+
ok {x.arg} == nil
|
166
|
+
ok {x.desc} == 'show usage.'
|
167
|
+
ok {x.arg_required} == false
|
168
|
+
end
|
169
|
+
#
|
170
|
+
[
|
171
|
+
"-h, --help #usage : show usage.",
|
172
|
+
].each do |defstr|
|
173
|
+
x = klass.new(defstr)
|
174
|
+
ok {x.short} == 'h'
|
175
|
+
ok {x.long} == 'help'
|
176
|
+
ok {x.name} == 'usage'
|
177
|
+
ok {x.arg} == nil
|
178
|
+
ok {x.desc} == 'show usage.'
|
179
|
+
ok {x.arg_required} == false
|
180
|
+
end
|
181
|
+
#
|
182
|
+
[
|
183
|
+
"--help #usage : show usage.",
|
184
|
+
].each do |defstr|
|
185
|
+
x = klass.new(defstr)
|
186
|
+
ok {x.short} == nil
|
187
|
+
ok {x.long} == 'help'
|
188
|
+
ok {x.name} == 'usage'
|
189
|
+
ok {x.arg} == nil
|
190
|
+
ok {x.desc} == 'show usage.'
|
191
|
+
ok {x.arg_required} == false
|
192
|
+
end
|
193
|
+
#
|
194
|
+
[
|
195
|
+
"-a name #command : action name.",
|
196
|
+
].each do |defstr|
|
197
|
+
x = klass.new(defstr)
|
198
|
+
ok {x.short} == 'a'
|
199
|
+
ok {x.long} == nil
|
200
|
+
ok {x.name} == 'command'
|
201
|
+
ok {x.arg} == 'name'
|
202
|
+
ok {x.desc} == 'action name.'
|
203
|
+
ok {x.arg_required} == true
|
204
|
+
end
|
205
|
+
#
|
206
|
+
[
|
207
|
+
"--action=name #command : action name.",
|
208
|
+
].each do |defstr|
|
209
|
+
x = klass.new(defstr)
|
210
|
+
ok {x.short} == nil
|
211
|
+
ok {x.long} == 'action'
|
212
|
+
ok {x.name} == 'command'
|
213
|
+
ok {x.arg} == 'name'
|
214
|
+
ok {x.desc} == 'action name.'
|
215
|
+
ok {x.arg_required} == true
|
216
|
+
end
|
217
|
+
#
|
218
|
+
[
|
219
|
+
"-i, --indent[=N] #width : indent width (default 2).",
|
220
|
+
].each do |defstr|
|
221
|
+
x = klass.new(defstr)
|
222
|
+
ok {x.short} == 'i'
|
223
|
+
ok {x.long} == 'indent'
|
224
|
+
ok {x.name} == 'width'
|
225
|
+
ok {x.arg} == 'N'
|
226
|
+
ok {x.desc} == 'indent width (default 2).'
|
227
|
+
ok {x.arg_required} == nil
|
228
|
+
end
|
229
|
+
#
|
230
|
+
[
|
231
|
+
"-i[N] #width : indent width (default 2).",
|
232
|
+
].each do |defstr|
|
233
|
+
x = klass.new(defstr)
|
234
|
+
ok {x.short} == 'i'
|
235
|
+
ok {x.long} == nil
|
236
|
+
ok {x.name} == 'width'
|
237
|
+
ok {x.arg} == 'N'
|
238
|
+
ok {x.desc} == 'indent width (default 2).'
|
239
|
+
ok {x.arg_required} == nil
|
240
|
+
end
|
241
|
+
#
|
242
|
+
[
|
243
|
+
"--indent[=N] #width : indent width (default 2).",
|
244
|
+
].each do |defstr|
|
245
|
+
x = klass.new(defstr)
|
246
|
+
ok {x.short} == nil
|
247
|
+
ok {x.long} == 'indent'
|
248
|
+
ok {x.name} == 'width'
|
249
|
+
ok {x.arg} == 'N'
|
250
|
+
ok {x.desc} == 'indent width (default 2).'
|
251
|
+
ok {x.arg_required} == nil
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
spec "raises error when failed to parse definition string." do
|
256
|
+
pr = proc { klass.new("-h, --help:show help") }
|
257
|
+
ok {pr}.raise?(errclass, "'-h, --help:show help': invalid definition.")
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
topic '#usage()' do
|
264
|
+
|
265
|
+
spec "[!xd9do] returns option usage with specified width." do
|
266
|
+
optdef = klass.new("-h, --help: show help")
|
267
|
+
ok {optdef.usage(15)} == "-h, --help : show help"
|
268
|
+
ok {optdef.usage()} == "-h, --help : show help"
|
269
|
+
optdef = klass.new("-h: show help")
|
270
|
+
ok {optdef.usage()} == "-h : show help"
|
271
|
+
optdef = klass.new("--help: show help")
|
272
|
+
ok {optdef.usage()} == "--help : show help"
|
273
|
+
#
|
274
|
+
optdef = klass.new("-a, --action=name: action name")
|
275
|
+
ok {optdef.usage(15)} == "-a, --action=name: action name"
|
276
|
+
ok {optdef.usage()} == "-a, --action=name : action name"
|
277
|
+
optdef = klass.new("-a name: action name")
|
278
|
+
ok {optdef.usage()} == "-a name : action name"
|
279
|
+
optdef = klass.new("--action=name: action name")
|
280
|
+
ok {optdef.usage()} == "--action=name : action name"
|
281
|
+
#
|
282
|
+
optdef = klass.new("-i, --indent[=N]: indent width")
|
283
|
+
ok {optdef.usage(15)} == "-i, --indent[=N]: indent width"
|
284
|
+
ok {optdef.usage()} == "-i, --indent[=N] : indent width"
|
285
|
+
optdef = klass.new("-i[N]: indent width")
|
286
|
+
ok {optdef.usage()} == "-i[N] : indent width"
|
287
|
+
optdef = klass.new("--indent[=N]: indent width")
|
288
|
+
ok {optdef.usage()} == "--indent[=N] : indent width"
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
topic Migr8::Util::CommandOptionParser do
|
297
|
+
|
298
|
+
klass = Migr8::Util::CommandOptionParser
|
299
|
+
errclass = Migr8::Util::CommandOptionError
|
300
|
+
|
301
|
+
|
302
|
+
topic '#add()' do
|
303
|
+
|
304
|
+
spec "[!tm89j] parses definition string and adds optdef object." do
|
305
|
+
parser = klass.new
|
306
|
+
parser.add("-h, --help: show help")
|
307
|
+
parser.add("-a, --action=name: action name.")
|
308
|
+
ok {parser.optdefs.length} == 2
|
309
|
+
ok {parser.optdefs[0].short} == 'h'
|
310
|
+
ok {parser.optdefs[0].long} == 'help'
|
311
|
+
ok {parser.optdefs[0].arg} == nil
|
312
|
+
ok {parser.optdefs[1].short} == 'a'
|
313
|
+
ok {parser.optdefs[1].long} == 'action'
|
314
|
+
ok {parser.optdefs[1].arg} == 'name'
|
315
|
+
end
|
316
|
+
|
317
|
+
spec "[!00kvl] returns self." do
|
318
|
+
parser = klass.new
|
319
|
+
ret = parser.add("-h, --help: show help")
|
320
|
+
ok {ret}.same?(parser)
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
topic '#parse()' do
|
327
|
+
|
328
|
+
fixture :parser do
|
329
|
+
parser = klass.new
|
330
|
+
parser.add("-h, --help : show help")
|
331
|
+
parser.add("-V #version : show version")
|
332
|
+
parser.add("-a, --action=name : action name")
|
333
|
+
parser.add("-i, --indent[=N] : indent width (default N=2)")
|
334
|
+
parser
|
335
|
+
end
|
336
|
+
|
337
|
+
spec "returns options parsed." do |parser|
|
338
|
+
args = "-hVi4 -a print foo bar".split(' ')
|
339
|
+
options = parser.parse(args)
|
340
|
+
ok {options} == {'help'=>true, 'version'=>true, 'indent'=>4, 'action'=>'print'}
|
341
|
+
ok {args} == ["foo", "bar"]
|
342
|
+
end
|
343
|
+
|
344
|
+
spec "parses short options." do |parser|
|
345
|
+
# short options
|
346
|
+
args = "-hVi4 -a print foo bar".split(' ')
|
347
|
+
options = parser.parse(args)
|
348
|
+
ok {options} == {'help'=>true, 'version'=>true, 'indent'=>4, 'action'=>'print'}
|
349
|
+
ok {args} == ["foo", "bar"]
|
350
|
+
#
|
351
|
+
args = "-hi foo bar".split(' ')
|
352
|
+
options = parser.parse(args)
|
353
|
+
ok {options} == {'help'=>true, 'indent'=>true}
|
354
|
+
ok {args} == ["foo", "bar"]
|
355
|
+
end
|
356
|
+
|
357
|
+
spec "parses long options." do |parser|
|
358
|
+
# long options
|
359
|
+
args = "--help --action=print --indent=4 foo bar".split(' ')
|
360
|
+
options = parser.parse(args)
|
361
|
+
ok {options} == {'help'=>true, 'indent'=>4, 'action'=>'print'}
|
362
|
+
ok {args} == ["foo", "bar"]
|
363
|
+
#
|
364
|
+
args = "--indent foo bar".split(' ')
|
365
|
+
options = parser.parse(args)
|
366
|
+
ok {options} == {'indent'=>true}
|
367
|
+
ok {args} == ["foo", "bar"]
|
368
|
+
end
|
369
|
+
|
370
|
+
spec "[!2jo9d] stops to parse options when '--' found." do |parser|
|
371
|
+
args = ["-h", "--", "-V", "--action=print", "foo", "bar"]
|
372
|
+
options = parser.parse(args)
|
373
|
+
ok {options} == {'help'=>true}
|
374
|
+
ok {args} == ["-V", "--action=print", "foo", "bar"]
|
375
|
+
end
|
376
|
+
|
377
|
+
spec "[!7pa2x] raises error when invalid long option." do |parser|
|
378
|
+
pr1 = proc { parser.parse(["--help?", "aaa"]) }
|
379
|
+
ok {pr1}.raise?(errclass, "--help?: invalid option format.")
|
380
|
+
pr2 = proc { parser.parse(["---help", "aaa"]) }
|
381
|
+
ok {pr2}.raise?(errclass, "---help: invalid option format.")
|
382
|
+
end
|
383
|
+
|
384
|
+
spec "[!sj0cv] raises error when unknown long option." do |parser|
|
385
|
+
pr = proc { parser.parse(["--foobar", "aaa"]) }
|
386
|
+
ok {pr}.raise?(errclass, "--foobar: unknown option.")
|
387
|
+
end
|
388
|
+
|
389
|
+
spec "[!a7qxw] raises error when argument required but not provided." do |parser|
|
390
|
+
pr = proc { parser.parse(["--action", "foo", "bar"]) }
|
391
|
+
ok {pr}.raise?(errclass, "--action: argument required.")
|
392
|
+
end
|
393
|
+
|
394
|
+
spec "[!8eu9s] raises error when option takes no argument but provided." do |parser|
|
395
|
+
pr = proc { parser.parse(["--help=true", "foo", "bar"]) }
|
396
|
+
ok {pr}.raise?(errclass, "--help=true: unexpected argument.")
|
397
|
+
end
|
398
|
+
|
399
|
+
case_when "[!1l2dn] when argname is 'N'..." do
|
400
|
+
|
401
|
+
spec "[!cfjp3] raises error when argval is not an integer." do |parser|
|
402
|
+
parser.add("-n, --num=N: number")
|
403
|
+
pr = proc { parser.parse(["--num=314"]) }
|
404
|
+
ok {pr}.NOT.raise?(Exception)
|
405
|
+
pr = proc { parser.parse(["--num=3.14"]) }
|
406
|
+
ok {pr}.raise?(errclass, "--num=3.14: integer expected.")
|
407
|
+
#
|
408
|
+
pr = proc { parser.parse(["--indent=4"]) }
|
409
|
+
ok {pr}.NOT.raise?(Exception)
|
410
|
+
pr = proc { parser.parse(["--indent=4i"]) }
|
411
|
+
ok {pr}.raise?(errclass, "--indent=4i: integer expected.")
|
412
|
+
end
|
413
|
+
|
414
|
+
spec "[!18p1g] raises error when argval <= 0." do |parser|
|
415
|
+
parser.add("-n, --num=N: number")
|
416
|
+
opts = parser.parse(["--num=9"])
|
417
|
+
ok {opts['num']} == 9
|
418
|
+
#
|
419
|
+
pr = proc { parser.parse(["--num=-9"]) }
|
420
|
+
ok {pr}.raise?(errclass, "--num=-9: positive value expected.")
|
421
|
+
pr = proc { parser.parse(["--num=0"]) }
|
422
|
+
ok {pr}.raise?(errclass, "--num=0: positive value expected.")
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
spec "[!dtbdd] uses option name instead of long name when option name specified." do |parser|
|
428
|
+
pr = proc { parser.parse(["--help=true", "foo", "bar"]) }
|
429
|
+
ok {pr}.raise?(errclass, "--help=true: unexpected argument.")
|
430
|
+
end
|
431
|
+
|
432
|
+
spec "[!7mp75] sets true as value when argument is not provided." do |parser|
|
433
|
+
args = ["--help", "foo", "bar"]
|
434
|
+
options = parser.parse(args)
|
435
|
+
ok {options} == {'help'=>true}
|
436
|
+
ok {args} == ["foo", "bar"]
|
437
|
+
end
|
438
|
+
|
439
|
+
spec "[!8aaj0] raises error when unknown short option provided." do |parser|
|
440
|
+
args = ["-hxV", "foo"]
|
441
|
+
pr = proc { parser.parse(args) }
|
442
|
+
ok {pr}.raise?(errclass, "-x: unknown option.")
|
443
|
+
end
|
444
|
+
|
445
|
+
case_when "[!mnwxw] when short option takes no argument..." do
|
446
|
+
|
447
|
+
spec "[!8atm1] sets true as value." do |parser|
|
448
|
+
args = ["-hV", "foo", "bar"]
|
449
|
+
options = parser.parse(args)
|
450
|
+
ok {options} == {'help'=>true, 'version'=>true}
|
451
|
+
ok {args} == ["foo", "bar"]
|
452
|
+
end
|
453
|
+
|
454
|
+
end
|
455
|
+
|
456
|
+
case_when "[!l5mee] when short option takes required argument..." do
|
457
|
+
|
458
|
+
spec "[!crvxx] uses following string as argument." do |parser|
|
459
|
+
[
|
460
|
+
["-aprint", "foo", "bar"],
|
461
|
+
["-a", "print", "foo", "bar"],
|
462
|
+
].each do |args|
|
463
|
+
options = parser.parse(args)
|
464
|
+
ok {options} == {'action'=>'print'}
|
465
|
+
ok {args} == ["foo", "bar"]
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
spec "[!7t6l3] raises error when no argument provided." do |parser|
|
470
|
+
pr = proc { parser.parse(["-a"]) }
|
471
|
+
ok {pr}.raise?(errclass, "-a: argument required.")
|
472
|
+
end
|
473
|
+
|
474
|
+
case_when "[!h3gt8] when argname is 'N'..." do
|
475
|
+
|
476
|
+
spec "[!yzr2p] argument must be an integer." do |parser|
|
477
|
+
parser.add("-n, --num=N: number.")
|
478
|
+
pr = proc { parser.parse(["-n", "314", "hoo"]) }
|
479
|
+
ok {pr}.NOT.raise?(Exception)
|
480
|
+
#
|
481
|
+
pr = proc { opts = parser.parse(["-n", "3.14", "hoo"]) }
|
482
|
+
ok {pr}.raise?(errclass, "-n 3.14: integer expected.")
|
483
|
+
end
|
484
|
+
|
485
|
+
spec "[!mcwu7] argument must be positive value." do |parser|
|
486
|
+
parser.add("-n, --num=N: number.")
|
487
|
+
opts = parser.parse(["-n", "9"])
|
488
|
+
ok {opts['num']} == 9
|
489
|
+
#
|
490
|
+
pr = proc { parser.parse(["-n", "-9"]) }
|
491
|
+
ok {pr}.raise?(errclass, "-n -9: positive value expected.")
|
492
|
+
pr = proc { parser.parse(["-n", "0"]) }
|
493
|
+
ok {pr}.raise?(errclass, "-n 0: positive value expected.")
|
494
|
+
end
|
495
|
+
|
496
|
+
end
|
497
|
+
|
498
|
+
end
|
499
|
+
|
500
|
+
case_when "[!pl97z] when short option takes optional argument..." do
|
501
|
+
|
502
|
+
spec "[!4k3zy] uses following string as argument if provided." do |parser|
|
503
|
+
args = ["-hi4", "foo", "bar"]
|
504
|
+
options = parser.parse(args)
|
505
|
+
ok {options} == {'help'=>true, 'indent'=>4}
|
506
|
+
ok {args} == ["foo", "bar"]
|
507
|
+
end
|
508
|
+
|
509
|
+
spec "[!9k2ip] uses true as argument value if not provided." do |parser|
|
510
|
+
args = ["-hi", "foo", "bar"]
|
511
|
+
options = parser.parse(args)
|
512
|
+
ok {options} == {'help'=>true, 'indent'=>true}
|
513
|
+
ok {args} == ["foo", "bar"]
|
514
|
+
end
|
515
|
+
|
516
|
+
case_when "[!lk761] when argname is 'N'..." do
|
517
|
+
|
518
|
+
spec "[!6oy04] argument must be an integer." do |parser|
|
519
|
+
parser.add("-n, --num[=N]: number.")
|
520
|
+
pr = proc { parser.parse(["-n314", "hoo"]) }
|
521
|
+
ok {pr}.NOT.raise?(Exception)
|
522
|
+
#
|
523
|
+
pr = proc { parser.parse(["-n3.14", "hoo"]) }
|
524
|
+
ok {pr}.raise?(errclass, "-n3.14: integer expected.")
|
525
|
+
end
|
526
|
+
|
527
|
+
spec "[!nc3av] argument must be positive value." do |parser|
|
528
|
+
parser.add("-n, --num[=N]: number")
|
529
|
+
opts = parser.parse(["-n9"])
|
530
|
+
ok {opts['num']} == 9
|
531
|
+
#
|
532
|
+
pr = proc { opts = parser.parse(["-n-9"]) }
|
533
|
+
ok {pr}.raise?(errclass, "-n-9: positive value expected.")
|
534
|
+
pr = proc { opts = parser.parse(["-n0"]) }
|
535
|
+
ok {pr}.raise?(errclass, "-n0: positive value expected.")
|
536
|
+
end
|
537
|
+
|
538
|
+
end
|
539
|
+
|
540
|
+
end
|
541
|
+
|
542
|
+
spec "[!35eof] returns parsed options." do |parser|
|
543
|
+
[
|
544
|
+
["-hVi4", "--action=print", "foo", "bar"],
|
545
|
+
["--indent=4", "-hVa", "print", "foo", "bar"],
|
546
|
+
].each do |args|
|
547
|
+
options = parser.parse(args)
|
548
|
+
ok {options} == {'help'=>true, 'version'=>true, 'indent'=>4, 'action'=>'print'}
|
549
|
+
ok {args} == ["foo", "bar"]
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
end
|
554
|
+
|
555
|
+
|
556
|
+
topic '#usage()' do
|
557
|
+
|
558
|
+
spec "[!w9v9c] returns usage string of all options." do
|
559
|
+
parser = klass.new
|
560
|
+
parser.add("-h, --help : show help")
|
561
|
+
parser.add("-V #version : show version")
|
562
|
+
parser.add("-a, --action=name : action name")
|
563
|
+
parser.add("-i, --indent[=N] : indent width (default N=2)")
|
564
|
+
#
|
565
|
+
expected = <<'END'
|
566
|
+
-h, --help : show help
|
567
|
+
-V : show version
|
568
|
+
-a, --action=name : action name
|
569
|
+
-i, --indent[=N] : indent width (default N=2)
|
570
|
+
END
|
571
|
+
ok {parser.usage()} == expected
|
572
|
+
#
|
573
|
+
expected = <<'END'
|
574
|
+
-h, --help : show help
|
575
|
+
-V : show version
|
576
|
+
-a, --action=name: action name
|
577
|
+
-i, --indent[=N]: indent width (default N=2)
|
578
|
+
END
|
579
|
+
ok {parser.usage(16)} == expected
|
580
|
+
end
|
581
|
+
|
582
|
+
spec "[!i0uvr] adds indent when specified." do
|
583
|
+
parser = klass.new
|
584
|
+
parser.add("-h, --help : show help")
|
585
|
+
parser.add("--quiet : be quiet")
|
586
|
+
#
|
587
|
+
expected = <<'END'
|
588
|
+
-h, --help : show help
|
589
|
+
--quiet : be quiet
|
590
|
+
END
|
591
|
+
ok {parser.usage(nil, ' ')} == expected
|
592
|
+
end
|
593
|
+
|
594
|
+
spec "[!lbjai] skips options when desc is empty." do
|
595
|
+
parser = klass.new
|
596
|
+
parser.add("-h, --help : show help")
|
597
|
+
parser.add("-D, --debug[=N] : ")
|
598
|
+
parser.add("--quiet : be quiet")
|
599
|
+
#
|
600
|
+
expected = <<'END'
|
601
|
+
-h, --help : show help
|
602
|
+
--quiet : be quiet
|
603
|
+
END
|
604
|
+
ok {parser.usage()} == expected
|
605
|
+
end
|
606
|
+
|
607
|
+
|
608
|
+
end
|
609
|
+
|
610
|
+
|
611
|
+
end
|
612
|
+
|
613
|
+
|
614
|
+
topic Migr8::Util::Template do
|
615
|
+
|
616
|
+
tmpl_class = Migr8::Util::Template
|
617
|
+
|
618
|
+
|
619
|
+
topic '#initialize()' do
|
620
|
+
|
621
|
+
spec "[!6z4kp] converts input string into ruby code." do
|
622
|
+
input = <<'END'
|
623
|
+
<p>
|
624
|
+
<% x = 10 %>
|
625
|
+
<%= x %>
|
626
|
+
<%== x %>
|
627
|
+
</p>
|
628
|
+
END
|
629
|
+
expected = <<'END'
|
630
|
+
_buf = ''; _buf << %q`<p>
|
631
|
+
`; x = 10
|
632
|
+
; _buf << %q` `; _buf << (escape( x )).to_s; _buf << %q`
|
633
|
+
`; _buf << %q` `; _buf << ( x ).to_s; _buf << %q`
|
634
|
+
`; _buf << %q`</p>
|
635
|
+
`;
|
636
|
+
_buf.to_s
|
637
|
+
END
|
638
|
+
tmpl = tmpl_class.new(input)
|
639
|
+
ok {tmpl.src} == expected
|
640
|
+
end
|
641
|
+
|
642
|
+
end
|
643
|
+
|
644
|
+
topic '#convert()' do
|
645
|
+
|
646
|
+
spec "[!118pw] converts template string into ruby code." do
|
647
|
+
input = <<'END'
|
648
|
+
<div>
|
649
|
+
<% for i in 1..3 do %>
|
650
|
+
<%== i %>
|
651
|
+
<% end %>
|
652
|
+
</div>
|
653
|
+
END
|
654
|
+
expected = <<'END'
|
655
|
+
_buf = ''; _buf << %q`<div>
|
656
|
+
`; for i in 1..3 do
|
657
|
+
; _buf << %q` `; _buf << ( i ).to_s; _buf << %q`
|
658
|
+
`; end
|
659
|
+
; _buf << %q`</div>
|
660
|
+
`;
|
661
|
+
_buf.to_s
|
662
|
+
END
|
663
|
+
tmpl = tmpl_class.new()
|
664
|
+
actual = tmpl.convert(input)
|
665
|
+
ok {actual} == expected
|
666
|
+
end
|
667
|
+
|
668
|
+
spec "[!7ht59] escapes '`' and '\\' characters." do
|
669
|
+
input = <<'END'
|
670
|
+
output = `ls`
|
671
|
+
newline = "\n"
|
672
|
+
END
|
673
|
+
expected = <<'END'
|
674
|
+
_buf = ''; _buf << %q`output = \`ls\`
|
675
|
+
newline = "\\n"
|
676
|
+
`;
|
677
|
+
_buf.to_s
|
678
|
+
END
|
679
|
+
tmpl = tmpl_class.new()
|
680
|
+
actual = tmpl.convert(input)
|
681
|
+
ok {actual} == expected
|
682
|
+
end
|
683
|
+
|
684
|
+
spec "[!u93y5] wraps expression by 'escape()' when <%= %>." do
|
685
|
+
input = "val=<%= val %>"
|
686
|
+
expected = <<'END'
|
687
|
+
_buf = ''; _buf << %q`val=`; _buf << (escape( val )).to_s;
|
688
|
+
_buf.to_s
|
689
|
+
END
|
690
|
+
tmpl = tmpl_class.new()
|
691
|
+
ok {tmpl.convert(input)} == expected
|
692
|
+
end
|
693
|
+
|
694
|
+
spec "[!auj95] leave expression as it is when <%== %>." do
|
695
|
+
input = "val=<%== val %>"
|
696
|
+
expected = <<'END'
|
697
|
+
_buf = ''; _buf << %q`val=`; _buf << ( val ).to_s;
|
698
|
+
_buf.to_s
|
699
|
+
END
|
700
|
+
tmpl = tmpl_class.new()
|
701
|
+
ok {tmpl.convert(input)} == expected
|
702
|
+
end
|
703
|
+
|
704
|
+
spec "[!b10ns] generates ruby code correctly even when no embedded code." do
|
705
|
+
input = <<'END'
|
706
|
+
create table (users) (
|
707
|
+
id serial primary key,
|
708
|
+
name varchar(255) not null
|
709
|
+
);
|
710
|
+
END
|
711
|
+
expected = <<'END'
|
712
|
+
_buf = ''; _buf << %q`create table (users) (
|
713
|
+
id serial primary key,
|
714
|
+
name varchar(255) not null
|
715
|
+
);
|
716
|
+
`;
|
717
|
+
_buf.to_s
|
718
|
+
END
|
719
|
+
tmpl = tmpl_class.new()
|
720
|
+
actual = tmpl.convert(input)
|
721
|
+
ok {actual} == expected
|
722
|
+
end
|
723
|
+
|
724
|
+
end
|
725
|
+
|
726
|
+
|
727
|
+
topic '#render()' do
|
728
|
+
|
729
|
+
spec "[!48pfc] returns rendered string." do
|
730
|
+
input = <<'END'
|
731
|
+
<p>
|
732
|
+
<% for item in ['Haruhi', 'Mikuru', 'Yuki'] %>
|
733
|
+
<%= item %>
|
734
|
+
<% end %>
|
735
|
+
</p>
|
736
|
+
END
|
737
|
+
expected = <<'END'
|
738
|
+
<p>
|
739
|
+
Haruhi
|
740
|
+
Mikuru
|
741
|
+
Yuki
|
742
|
+
</p>
|
743
|
+
END
|
744
|
+
tmpl = tmpl_class.new(input)
|
745
|
+
actual = tmpl.render()
|
746
|
+
ok {actual} == expected
|
747
|
+
end
|
748
|
+
|
749
|
+
spec "[!umsfx] takes hash object as context variables." do
|
750
|
+
input = <<'END'
|
751
|
+
<p>
|
752
|
+
<% for item in @members %>
|
753
|
+
<%= item %>
|
754
|
+
<% end %>
|
755
|
+
</p>
|
756
|
+
END
|
757
|
+
expected = <<'END'
|
758
|
+
<p>
|
759
|
+
Haruhi
|
760
|
+
Mikuru
|
761
|
+
Yuki
|
762
|
+
</p>
|
763
|
+
END
|
764
|
+
context = {
|
765
|
+
:members=>['Haruhi', 'Mikuru', 'Yuki'],
|
766
|
+
}
|
767
|
+
tmpl = tmpl_class.new(input)
|
768
|
+
actual = tmpl.render(context)
|
769
|
+
ok {actual} == expected
|
770
|
+
end
|
771
|
+
|
772
|
+
spec "[!p0po0] context argument can be null." do
|
773
|
+
input = "Hello"
|
774
|
+
context = nil
|
775
|
+
tmpl = tmpl_class.new(input)
|
776
|
+
actual = nil
|
777
|
+
pr = proc { actual = tmpl.render(nil) }
|
778
|
+
ok {pr}.NOT.raise?()
|
779
|
+
ok {actual} == "Hello"
|
780
|
+
end
|
781
|
+
|
782
|
+
spec "[!1i0v8] escapes \"'\" into \"''\" when '<%= %>', and not when '<%== %>'." do
|
783
|
+
input = <<'END'
|
784
|
+
<% for item in @items %>
|
785
|
+
<%= item %>
|
786
|
+
<%== item %>
|
787
|
+
<% end %>
|
788
|
+
END
|
789
|
+
expected = <<'END'
|
790
|
+
Rock''n Roll
|
791
|
+
Rock'n Roll
|
792
|
+
xx''yy''''zz''''''
|
793
|
+
xx'yy''zz'''
|
794
|
+
END
|
795
|
+
items = ["Rock'n Roll", "xx'yy''zz'''"]
|
796
|
+
tmpl = tmpl_class.new(input)
|
797
|
+
ok {tmpl.render({:items=>items})} == expected
|
798
|
+
end
|
799
|
+
|
800
|
+
end
|
801
|
+
|
802
|
+
|
803
|
+
end
|
804
|
+
|
805
|
+
|
806
|
+
topic Migr8::Util::TemplateContext do
|
807
|
+
klass = Migr8::Util::TemplateContext
|
808
|
+
|
809
|
+
|
810
|
+
topic '#initialize()' do
|
811
|
+
|
812
|
+
spec "[!p69q1] takes vars and sets them into instance variables." do
|
813
|
+
ctx = klass.new({'title'=>'hello', 'members'=>%w[Haruhi Mikuru Yuki]})
|
814
|
+
ok {ctx.instance_variable_get('@title')} == 'hello'
|
815
|
+
ok {ctx.instance_variable_get('@members')} == ['Haruhi', 'Mikuru', 'Yuki']
|
816
|
+
end
|
817
|
+
|
818
|
+
spec "[!p853f] do nothing when vars is nil." do
|
819
|
+
pr = proc { klass.new(nil) }
|
820
|
+
ok {pr}.NOT.raise?(Exception)
|
821
|
+
end
|
822
|
+
|
823
|
+
end
|
824
|
+
|
825
|
+
|
826
|
+
topic '#escape()' do
|
827
|
+
|
828
|
+
spec "[!f3yy9] escapes \"'\" into \"''\" for default." do
|
829
|
+
$MIGR8_DBMS = nil
|
830
|
+
ctx = klass.new()
|
831
|
+
ok {ctx.escape("lock'n roll!")} == "lock''n roll!"
|
832
|
+
ok {ctx.escape("aa'bb''cc'''")} == "aa''bb''''cc''''''"
|
833
|
+
end
|
834
|
+
|
835
|
+
spec "[!to5kz] converts any value into string." do
|
836
|
+
$MIGR8_DBMS = nil
|
837
|
+
ctx = klass.new()
|
838
|
+
ok {ctx.escape(1)} == "1"
|
839
|
+
ok {ctx.escape(nil)} == ""
|
840
|
+
ok {ctx.escape(true)} == "true"
|
841
|
+
ok {ctx.escape(false)} == "false"
|
842
|
+
end
|
843
|
+
|
844
|
+
spec "[!6v5yq] escapes "'" into "\\'" when on MySQL dbms." do
|
845
|
+
at_exit { $MIGR8_DBMS = nil }
|
846
|
+
#
|
847
|
+
$MIGR8_DBMS = Migr8::DBMS::MySQL.new('mysql -q -u user1 example1')
|
848
|
+
ctx = klass.new()
|
849
|
+
ok {ctx.escape("aa'bb''cc'''")} == "aa\\'bb\\'\\'cc\\'\\'\\'"
|
850
|
+
ok {ctx.escape(123)} == "123"
|
851
|
+
ok {ctx.escape(nil)} == ""
|
852
|
+
#
|
853
|
+
$MIGR8_DBMS = Migr8::DBMS::SQLite3.new('sqlite3 example1.db')
|
854
|
+
ctx = klass.new()
|
855
|
+
ok {ctx.escape("aa'bb''cc'''")} == "aa''bb''''cc''''''"
|
856
|
+
ok {ctx.escape(123)} == "123"
|
857
|
+
ok {ctx.escape(nil)} == ""
|
858
|
+
#
|
859
|
+
$MIGR8_DBMS = Migr8::DBMS::PostgreSQL.new('psql -q -U user1 example1')
|
860
|
+
ctx = klass.new()
|
861
|
+
ok {ctx.escape("aa'bb''cc'''")} == "aa''bb''''cc''''''"
|
862
|
+
ok {ctx.escape(123)} == "123"
|
863
|
+
ok {ctx.escape(nil)} == ""
|
864
|
+
end
|
865
|
+
|
866
|
+
|
867
|
+
end
|
868
|
+
|
869
|
+
|
870
|
+
end
|
871
|
+
|
872
|
+
|
873
|
+
end
|