migr8 0.4.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/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
|