caset 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,211 @@
1
+ #coding:utf-8
2
+ require 'yaml'
3
+
4
+ =begin
5
+ INPUT: PYCODE.py + ABSTRACT.yml
6
+ OUTPUT: tests/PYTEST
7
+ =end
8
+
9
+ =begin # ABSTRACT TEMPLATE
10
+ --- # MODULE_NAME
11
+
12
+ # [basic header]
13
+ path: THE_FOLDER_PATH_OF_MODULE_SOURCE
14
+ module: MODULE_NAME
15
+ file:
16
+ FILENAME: # FILENAME INCLUDE ".py"
17
+
18
+ # [import]
19
+ import:
20
+ - REFMOD_NAME # => import REFMOD_NAME
21
+ - [REFMOD_NAME, SUB_REFMOD_NAME] # => from REFMOD_NAME import SUB_REFMOD_NAME
22
+ - [REFMOD_NAME, SUB_REFMOD_NAME, ALIAS_NAME] # => from REFMOD_NAME import SUB_REFMOD_NAME as ALIAS_NAME
23
+
24
+ # [external variables and functions]
25
+ varc:
26
+ - VAR_GLOBAL
27
+ - [VAR_GLOBAL, MODE]
28
+ - [VAR_GLOBAL, MODE, OG_VAR]
29
+ - [VAR_GLOBAL, MODE, OG_VAR, TYPE] # => [VAR_NAME|WRAPPER_NAME, null, null, TYPE|null]
30
+ func:
31
+ - [[EXTN_FUN_NAME, ARGV...], [REF_VAR_NAME|REF_FUN_NAME...], [RETN_VAL...]] # => [[MUST, OPTIONAL...], [OPTIONAL...], [OPTIONAL...]]
32
+ # no need to complete REF_STATEMENT and RETN_STATEMENT
33
+
34
+ # [two way to note vars and funs in class]
35
+ # one: only one class
36
+ CLASS:
37
+ BODY
38
+ # two: two or more classes
39
+ CLASSES:
40
+ - BODY
41
+
42
+ # [body of class]
43
+ CLASS:
44
+ name: CLASS_NAME
45
+ base: PARENT_CLASS_NAME
46
+
47
+ varc:
48
+ - CLASS_VAR
49
+ - [CLASS_VAR, MODE]
50
+ - [CLASS_VAR, MODE, OG_VAR]
51
+ - [CLASS_VAR, MODE, OG_VAR, TYPE] # => [VAR_NAME|WRAPPER_NAME, c|null, null, TYPE|null]
52
+ func:
53
+ - [[CLS_FUN_NAME]]
54
+ - [[CLS_FUN_NAME, ARGV...]]
55
+ - [[CLS_FUN_NAME, ARGV...], [REF_VAR_NAME|REF_FUN_NAME...]]
56
+ - [[CLS_FUN_NAME, ARGV...], [REF_VAR_NAME|REF_FUN_NAME...], [RETN_VAL...]] # => [[MUST, OPTIONAL...], [OPTIONAL...], [OPTIONAL...]]
57
+ # no need to complete REF_STATEMENT and RETN_STATEMENT
58
+
59
+ attr:
60
+ - INS_VAR
61
+ - [INS_VAR, MODE]
62
+ - [INS_VAR, MODE, OG_VAR]
63
+ - [INS_VAR, MODE, OG_VAR, TYPE] # => [VAR_NAME|WRAPPER_NAME, r|w|rw|i|null, ORIGINAL_VAR_NAME|null, TYPE|null]
64
+ fun:
65
+ - [[INS_FUN_NAME]]
66
+ - [[INS_FUN_NAME, ARGV...]]
67
+ - [[INS_FUN_NAME, ARGV...], [REF_VAR_NAME|REF_FUN_NAME...]]
68
+ - [[INS_FUN_NAME, ARGV...], [REF_VAR_NAME|REF_FUN_NAME...], [RETN_VAL...]] # => [[MUST, OPTIONAL...], [OPTIONAL...], [OPTIONAL...]]
69
+ # no need to complete REF_STATEMENT and RETN_STATEMENT
70
+ =end
71
+
72
+
73
+ =begin # EXAMPLE
74
+ --- # SERVICE_A
75
+ path: ~/workspace/dev/server/service_a
76
+ module: service_a
77
+
78
+ file:
79
+ service_a.py:
80
+ import:
81
+ - [__future__, absolute_import]
82
+ - sys
83
+ - [server.base_template, base_service, Service]
84
+ - [server.service_b, ServiceB]
85
+
86
+ classes: # or class and the value is only one item of hash
87
+ - name: ServiceA
88
+ base: Service
89
+ attr:
90
+ - [result, rw, dict, ResultA] # attr as r|w|rw methods
91
+ - [tmp, null, null]
92
+ fun:
93
+ - [[__init__],[tmp]]
94
+ - [[tmp_cal, tmp_val],[tmp, result], [result,size(result)]]
95
+ varc:
96
+ - a
97
+ - [b, null, null, str]
98
+ - c
99
+ func:
100
+ - [[tmp_zzZ, a,b,c]]
101
+
102
+ varc:
103
+ - d
104
+ - [e, null, null]
105
+ - [f, null, null, int]
106
+ func:
107
+ - [[tmp_ooO, d, e, f]]
108
+
109
+ =end
110
+
111
+
112
+ module Caset
113
+ module Pytest
114
+ module_function
115
+
116
+ def gen option
117
+ path, head, level = option[:path], option[:head], option[:level]
118
+ doc = YAML.load(File.read(path))
119
+ ohead = "#{doc['path']}/#{doc['module'] ? doc['module']+"/" : ''}"
120
+ pkgroute = head+ohead.split(head)[-1].gsub('/','.')
121
+ `mkdir -p tests/#{path.sub('.yml','')}`
122
+
123
+ doc['file'].each do|filename, abstract|
124
+ inner_fun_set = []
125
+ classes = (abstract['classes'] || [abstract['class']] || []).compact
126
+ classes.each do|klass|
127
+ inner_funcs,inner_funs = [], []
128
+
129
+ vars = klass['varc'] || []
130
+ vars.each do|var|
131
+ varname,varattr = var.instance_of?(Array) ? var[0..1] : [var,nil]
132
+ handle = level.include?(:strict) ? "raise Exception(\"No Implement\")" : ""
133
+ handle = level.include?(:fail) ? "assert False" : ""
134
+ tips = level.include?(:pending) ? "\"TODO: #{varname}#{varattr ? " :#{varattr}" : varattr}\"" : ""
135
+ adaptr = level.include?(:pending) ? " @pytest.mark.unfinished\n" : ""
136
+ inner_funcs << "#{adaptr} def test_#{varname}(self):\n #{handle}#{tips.empty? ? tips : "\n "+tips}\n"
137
+ end
138
+
139
+ funcs = klass['func'] || []
140
+ funcs.each do|func|
141
+ sign = func[0]
142
+ next if sign[0]=='__init__'
143
+ handle = level.include?(:strict) ? "raise Exception(\"No Implement\")" : ""
144
+ handle = level.include?(:fail) ? "assert False" : ""
145
+ tips = level.include?(:pending) ? "\"TODO: #{sign[0]}(#{sign[1..-1].join(', ')})\"" : ""
146
+ adaptr = level.include?(:pending) ? " @pytest.mark.unfinished\n" : ""
147
+ inner_funcs << "#{adaptr} def test_#{sign[0]}(self):\n #{handle}#{tips.empty? ? tips : "\n "+tips}\n"
148
+ end
149
+
150
+ vars = klass['attr'] || []
151
+ vars.each do|var|
152
+ varname,varattr = var.instance_of?(Array) ? var[0..1] : [var,nil]
153
+ handle = level.include?(:strict) ? "raise Exception(\"No Implement\")" : ""
154
+ handle = level.include?(:fail) ? "assert False" : ""
155
+ tips = level.include?(:pending) ? "\"TODO: #{varname}#{varattr ? " :#{varattr}" : varattr}\"" : ""
156
+ adaptr = level.include?(:pending) ? " @pytest.mark.unfinished\n" : ""
157
+ inner_funs << "#{adaptr} def test_#{varname}(self):\n #{handle}#{tips.empty? ? tips : "\n "+tips}\n"
158
+ end
159
+
160
+ funs = klass['fun'] || []
161
+ funs.each do|fun|
162
+ sign = fun[0]
163
+ next if sign[0]=='__init__'
164
+ handle = level.include?(:strict) ? "raise Exception(\"No Implement\")" : ""
165
+ handle = level.include?(:fail) ? "assert False" : ""
166
+ tips = level.include?(:pending) ? "\"TODO: #{sign[0]}(#{sign[1..-1].join(', ')})\"" : ""
167
+ adaptr = level.include?(:pending) ? " @pytest.mark.unfinished\n" : ""
168
+ inner_funs << "#{adaptr} def test_#{sign[0]}(self):\n #{handle}#{tips.empty? ? tips : "\n "+tips}\n"
169
+ end
170
+
171
+ unless inner_funcs.empty?
172
+ inner_funcs.unshift " def teardown_class(self):\n \"TODO: TEARDOWN CLASSMETHOD\"\n"
173
+ inner_funcs.unshift " def setup_class(self):\n \"TODO: SETUP CLASSMETHOD\"\n"
174
+ end
175
+ unless inner_funs.empty?
176
+ inner_funs.unshift " def teardown_method(self):\n \"TODO: TEARDOWN METHOD\"\n"
177
+ inner_funs.unshift " def setup_method(self):\n \"TODO: SETUP METHOD\"\n"
178
+ end
179
+
180
+ inner_tmp = inner_funcs + inner_funs
181
+ inner_tmp.unshift "class #{klass['name']}Test():" unless inner_tmp.empty?
182
+ inner_fun_set += inner_tmp
183
+ end
184
+
185
+ outer_funcs = []
186
+ funcs = abstract['func'] || []
187
+ funcs.each do|func|
188
+ sign = func[0]
189
+ handle = level.include?(:strict) ? "raise Exception(\"No Implement\")" : ""
190
+ handle = level.include?(:fail) ? "assert False" : ""
191
+ tips = level.include?(:pending) ? "\"TODO: #{sign[0]}(#{sign[1..-1].join(', ')})\"" : ""
192
+ adaptr = level.include?(:pending) ? "@pytest.mark.unfinished\n" : ""
193
+ outer_funcs << "#{adaptr}def test_#{sign[0]}():\n #{handle}#{tips.empty? ? tips : "\n "+tips}\n"
194
+ end
195
+
196
+ unless outer_funcs.empty?
197
+ outer_funcs.unshift "def teardown_function():\n \"TODO: TEARDOWN FUNCTION\"\n"
198
+ outer_funcs.unshift "def setup_function():\n \"TODO: SETUP FUNCTION\"\n"
199
+ end
200
+
201
+ content = (inner_fun_set + outer_funcs)
202
+ unless content.empty?
203
+ content.unshift "# import sys\n# sys.path.append(\".\")\n# PACKAGE ROUTE: #{pkgroute}#{filename[0..-4]}\n"
204
+ content.unshift "#coding:utf-8\nimport pytest\n"
205
+ testpath = "tests/#{path.sub('.yml','')}/test_#{filename}"
206
+ File.write testpath, content.join("\n") unless File.exist?(testpath)
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,36 @@
1
+ #coding:utf-8
2
+
3
+ # Usage:
4
+ # Spec.gen(ARGV,template)
5
+
6
+ module Spec
7
+ THE_SPEC_TEMPLATE = %Q{#coding:utf-8
8
+ $LOAD_PATH << "spec"
9
+
10
+ describe ((name)) do
11
+ context "..." do
12
+ before(:each) do
13
+
14
+ end
15
+
16
+ it "..." do
17
+ pending
18
+ end
19
+
20
+ after(:each) do
21
+
22
+ end
23
+ end
24
+ end}
25
+
26
+ def self.gen names,template=THE_SPEC_TEMPLATE
27
+ Dir.mkdir("spec") unless File.exist?("spec")
28
+ ["middleware","asset","support"].each do|subdir|
29
+ Dir.mkdir("spec/#{subdir}") unless File.exist?("spec/#{subdir}")
30
+ end
31
+ File.write("spec/spec_helper.rb","#coding:utf-8\n\n") unless File.exist?("spec/spec_helper.rb")
32
+ names.each do|name|
33
+ File.write("spec/#{name}_spec.rb",template.gsub("((name))",name)) unless File.exist?("spec/#{name}_spec.rb")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Caset
2
+ VERSION = "1.0.0"
3
+ end
data/lib/caset.rb ADDED
@@ -0,0 +1,272 @@
1
+ #coding:utf-8
2
+ require "caset/version"
3
+ require 'caset/gen_caset'
4
+ require 'caset/gen_bdd'
5
+ require 'caset/gen_spec'
6
+ require 'caset/gen_pytest'
7
+
8
+ module Caset
9
+ class Error < StandardError; end
10
+
11
+ #### 基础管道 ####
12
+ def self.construct
13
+ @__caset_register__ = { before_each: [], after_each: [], before_all: [], after_all: [], steps: {}, story: [] }
14
+ @__caset_report__ = []
15
+ @__caset_document__ = []
16
+ @__caset_indent__ = ""
17
+ end
18
+
19
+ def self.register
20
+ @__caset_register__
21
+ end
22
+
23
+ def self.report
24
+ @__caset_report__
25
+ end
26
+
27
+ def self.document
28
+ @__caset_document__
29
+ end
30
+
31
+ def self.indent
32
+ @__caset_indent__
33
+ end
34
+
35
+ def msg context
36
+ if context.instance_of?(Array)
37
+ context.each do|pile|
38
+ Caset.report << Caset.indent+pile.to_s
39
+ end
40
+ else
41
+ Caset.report << Caset.indent+context.to_s
42
+ end
43
+ Caset.report[-1]
44
+ end
45
+ alias :记录 :msg
46
+
47
+ def mark context
48
+ if context.instance_of?(Array)
49
+ context.each do|pile|
50
+ Caset.document << Caset.indent+pile.to_s
51
+ end
52
+ else
53
+ Caset.document << Caset.indent+context.to_s
54
+ end
55
+ Caset.document[-1]
56
+ end
57
+ alias :标记 :mark
58
+
59
+ def pending reason=nil
60
+ mark "pending"+(reason ? ":#{reason}" : '')
61
+ msg "pending"+(reason ? ":#{reason}" : '')
62
+ end
63
+
64
+ def 待定 原因=nil
65
+ mark "待定"+(原因 ? ":#{原因}" : '')
66
+ msg "待定"+(原因 ? ":#{原因}" : '')
67
+ end
68
+
69
+ def star context
70
+ mark "* #{context}"
71
+ end
72
+ alias :星 :star
73
+
74
+ def plus context
75
+ mark "+ #{context}"
76
+ end
77
+ alias :加 :plus
78
+
79
+ def minus context
80
+ mark "- #{context}"
81
+ end
82
+ alias :减 :minus
83
+
84
+ def Give context
85
+ mark "Give #{context}"
86
+ end
87
+
88
+ def When context
89
+ mark "When #{context}"
90
+ end
91
+
92
+ def Then context
93
+ mark "Then #{context}"
94
+ end
95
+
96
+ def And context
97
+ mark " #{context}"
98
+ end
99
+
100
+ def 如果 上下文
101
+ mark "如果#{上下文}"
102
+ end
103
+
104
+ def 当 上下文
105
+ mark "当#{上下文}"
106
+ end
107
+
108
+ def 那么 上下文
109
+ mark "那么#{上下文}"
110
+ end
111
+
112
+ def 而且 上下文
113
+ mark "而且#{上下文}"
114
+ end
115
+
116
+ def self.indent= indent
117
+ @__caset_indent__ = indent
118
+ end
119
+
120
+ #### 设定结构 ####
121
+ def scenario description,options=[],&block
122
+ # registration
123
+ Caset.construct
124
+ unless options.include?(:bm_story) or options.include?(:bm_step)
125
+ block.call # all blocks regist
126
+ else
127
+ require 'benchmark'
128
+ end
129
+ Benchmark.bm{|x| @_bm_story_ = x;block.call } if options.include?(:bm_story)
130
+ Benchmark.bm{|x| @_bm_step_ = x;block.call } if options.include?(:bm_step)
131
+
132
+ # execution
133
+ msg "====#{description}===="
134
+ mark "====#{description}===="
135
+
136
+ Caset.indent = " " # for before-all
137
+ msg "== before all ==" unless Caset.register[:before_all].empty?
138
+ mark "preconfig:" unless Caset.register[:before_all].empty?
139
+ Caset.register[:before_all].each do|before_all_transaction|
140
+ Caset.indent = " " # for inner before-all
141
+ before_all_transaction.call
142
+ end
143
+
144
+ Caset.register[:story].each do|stories|
145
+ description,story = stories
146
+ run_case(description,story)
147
+ end
148
+
149
+ Caset.indent = " " # for after-all
150
+ msg "== after all ==" unless Caset.register[:after_all].empty?
151
+ mark "postconfig:" unless Caset.register[:after_all].empty?
152
+ Caset.register[:after_all].each do|after_all_transaction|
153
+ Caset.indent = " " # for inner after-all
154
+ after_all_transaction.call
155
+ end
156
+
157
+ Caset.indent = "" # for scenario
158
+ msg "==== end scenario ===="
159
+ mark "==== end scenario ===="
160
+ end
161
+
162
+ def before options=[:each],&block
163
+ if options.include?(:each)
164
+ Caset.register[:before_each] << block
165
+ elsif options.include?(:all)
166
+ Caset.register[:before_all] << block
167
+ else
168
+ # :off
169
+ end
170
+ end
171
+
172
+ def after options=[:each],&block
173
+ if options.include?(:each)
174
+ Caset.register[:after_each] << block
175
+ elsif options.include?(:all)
176
+ Caset.register[:after_all] << block
177
+ else
178
+ # :off
179
+ end
180
+ end
181
+
182
+ def usecase description,options=[],&block
183
+ unless options.include?(:off)
184
+ Caset.register[:story] << [description,block]
185
+ end
186
+ end
187
+ alias :ustory :usecase
188
+
189
+ def ustep id, description=nil,options=[], &block
190
+ unless options.include?(:off)
191
+ Caset.register[:step] ||= {}
192
+ Caset.register[:step][id] = block
193
+ end
194
+ end
195
+
196
+ #### 控制结构 ####
197
+ def run_step(id, description=nil, *args)
198
+ Caset.indent = " "
199
+ mark "#{id}.#{description}"
200
+ if Caset.register[:step][id]
201
+ msg "<#{id}>#{description}"
202
+ Caset.indent = " "
203
+
204
+ unless @_bm_step_
205
+ Caset.register[:step][id].call(*args)
206
+ else
207
+ @_bm_step_.report(id.to_s+"."+description.to_s){Caset.register[:step][id].call(*args)}
208
+ end
209
+
210
+ else
211
+ msg "<Warning>You are trying to call a step(#{id}) yet defined!"
212
+ end
213
+ Caset.indent = " "
214
+ end
215
+
216
+ # steps = [step,...]
217
+ # step = {:id=>id, :desc=>description, :args=>args}
218
+ def run_steps(steps)
219
+ steps.each do|step|
220
+ run_step step[:id],step[:desc],*(step[:args]||[])
221
+ end
222
+ end
223
+
224
+ def run_case description,block
225
+ Caset.indent = " " # for before-each
226
+ msg "== before_each ==" unless Caset.register[:before_each].empty?
227
+ mark "prepare:" unless Caset.register[:before_each].empty?
228
+ Caset.indent = " " # for inner before-each
229
+ Caset.register[:before_each].each do|before_each_transaction|
230
+ before_each_transaction.call
231
+ end
232
+
233
+ Caset.indent = " " # for ustory
234
+ msg "#{description}"
235
+ mark "[#{description}]"
236
+ Caset.indent = " " # for inner ustory
237
+ unless @_bm_story_
238
+ block.call
239
+ else
240
+ @_bm_story_.report(description){block.call}
241
+ end
242
+
243
+ Caset.indent = " " # for after-each
244
+ msg "== after each ==" unless Caset.register[:after_each].empty?
245
+ mark "posthandle:" unless Caset.register[:after_each].empty?
246
+ Caset.indent = " " # for inner after-each
247
+ Caset.register[:after_each].each do|after_each_transaction|
248
+ after_each_transaction.call
249
+ end
250
+
251
+ Caset.indent = " " # return to ustory level
252
+ end
253
+ end
254
+
255
+ class Numeric
256
+ def is title,left='(',right=')'
257
+ Caset.document
258
+ Caset.document << "#{Caset.indent}#{left}#{self}#{right}#{title}\n"
259
+ end
260
+
261
+ def 是 标题,左='(',右=')';is 标题,左,右;end
262
+ end
263
+
264
+ class String
265
+ def is title,left='(',right=')'
266
+ Caset.document
267
+ Caset.document << "#{Caset.indent}#{left}#{self}#{right}#{title}\n"
268
+ end
269
+
270
+ def 是 标题,左='(',右=')';is 标题,左,右;end
271
+ end
272
+
@@ -0,0 +1,121 @@
1
+ #coding:utf-8
2
+ require 'csv'
3
+
4
+ #------------------------------------------------------------------------------
5
+ # CSV 表格读写与渲染
6
+ #------------------------------------------------------------------------------
7
+ module CasetDown
8
+ module_function
9
+
10
+ # 读取 CSV 文件并转换为二维表。
11
+ #
12
+ # 编码策略(对齐 SimTab.读取csv):优先按 UTF-8 解析(自动剥离 BOM),
13
+ # 内容不是合法 UTF-8 时回退按 GBK 转码,兼容历史报表。
14
+ # 支持表头解析(format == :csv 时解析表头)。
15
+ #
16
+ # @param path [String] CSV 文件路径
17
+ # @param format [Symbol] 解析模式,:raw 或 :csv(默认 :raw)
18
+ # @return [Object] 绑定表头的二维数组对象(含 绑定表头 方法)
19
+ def loadcsv path,format=:raw
20
+ raw = File.binread(path)
21
+ raw = raw.sub(/\A\xEF\xBB\xBF/n, '') # 剥离 UTF-8 BOM
22
+ csv = raw.dup.force_encoding('UTF-8')
23
+ csv = raw.dup.force_encoding('GBK').encode('UTF-8') unless csv.valid_encoding?
24
+ content = CSV.parse(csv, headers:(format==:csv))
25
+ table = content.绑定表头 content[0]
26
+ return table
27
+ end
28
+
29
+ # 将二维表转换为 Markdown 表格字符串。
30
+ #
31
+ # 自动生成 |---|---| 分隔行。
32
+ #
33
+ # @param table [Array<Array>] 二维表数据
34
+ # @return [String] Markdown 格式表格字符串
35
+ # @example
36
+ # CasetDown.maketab([["a", "b"], ["1", "2"]])
37
+ # # => "|a|b|\n|---|---|\n|1|2|"
38
+ def maketab table
39
+ printab = table.map{|row|"|"+row.join("|")+"|"}
40
+ printab.insert 1, Array.new(table[0].size, '|---').join+"|"
41
+ return printab.join("\n")
42
+ end
43
+
44
+ # 渲染文档中的表格,支持 CSV 读写与表格替换回文档。
45
+ #
46
+ # 两种工作模式:
47
+ # 1. 将文档中的 table 写入 CSV 文件(:wtab)
48
+ # 2. 将代码块加载的 CSV 表格替换回文档(:rtab)
49
+ #
50
+ # @param cds [Hash] 解析结果
51
+ # @param option [Hash] 控制选项
52
+ # @option option [String] :stream 覆盖原始文档文本
53
+ # @option option [Array<Symbol>] :opt 渲染标志
54
+ # - :wtab 将文档中的 table 写入 CSV 文件
55
+ # - :rtab 将代码块加载的 CSV 替换回文档
56
+ # - :rendfile 保存为带时间戳的新文件
57
+ # @return [Hash] 渲染结果
58
+ # - :text [String] 处理后的文档文本
59
+ # - :data [String, nil] Ruby 脚本尾部数据(endata 格式)
60
+ # - :fact [String, nil] Erlang 脚本尾部数据(fact 函数子句)
61
+ # @note 表格名前需要从 ((TableName)) 格式段落提取
62
+ # @note 数据尾部使用局部变量累积:cml 会同一进程内反复调用本方法,
63
+ # 若沿用全局变量累积,重复渲染会使 __END__ 数据段与 erlang fact
64
+ # 子句叠加(同名 fact 子句 erlang 首个匹配优先,旧数据会遮蔽新数据)
65
+ def render_table cds, option={ stream: nil, opt: []} # empty, :wtab, :rtab, :rendfile
66
+ endata = {}
67
+ # write table to csv file
68
+ docs = CasetDown::Check.all(cds)
69
+ docs.each_with_index do|doc,index|
70
+ if doc[:name]=='table'
71
+ table_name = ( index-1 >= 0 && docs[index-1][:name]=='p' && docs[index-1][:text].strip[0..1]=='((' && docs[index-1][:text].strip[-2..-1]=='))' ) ? docs[index-1][:text][2..-3].strip : 'Anonymous'
72
+ table_body = [doc[:head]]+doc[:body]
73
+ endata["[table@endata ~]$ruby #{table_name}"] = table_body.to_s
74
+ CSV.open(table_name+".csv","w")do|writer|
75
+ table_body.each do|record|
76
+ writer << record
77
+ end
78
+ end if option[:opt].include?(:wtab)
79
+ end
80
+ end
81
+
82
+ # load table from csv file
83
+ result = {}
84
+ codtab = docs.select{|doc|doc[:name]=='code'}
85
+ codtab.each do|code|
86
+ interpreter, namespace, config, target, flow, segment, input, output, rows, cols = magic_head code
87
+ next unless output.to_s[-4..-1]=='.csv'
88
+ tab = loadcsv(output)
89
+ seltab = rows.inject([tab[0]]) do|seltab,row|
90
+ seltab += [tab[row]] if row.is_a?(Integer)
91
+ seltab += tab[row] if row.is_a?(Range)
92
+ seltab
93
+ end
94
+ seltab.绑定表头 seltab[0]
95
+ newtab = seltab.选择(*cols)
96
+ result[output] = maketab(newtab)
97
+ endata["[table@endata ~]$ruby #{output}"] = newtab.to_s
98
+ end
99
+
100
+ scr_tail, erl_tail = nil, ''
101
+ endata.each do|head, table|
102
+ (scr_tail ||= "__END__") << "\n#{head}\n#{table}\n"
103
+ erl_tail << "\n\n"+ToErlang.fact(head.split('$ruby ')[1], ToErlang.table(eval(table)))
104
+ end
105
+ origin_text = option[:stream] || cds[:doc].text
106
+ result.each do|output, newtab|
107
+ wrapped = "((#{output}))\n\n#{newtab}"
108
+ origins = CasetDown.search_output(origin_text, output)
109
+ origins.each do|origin|
110
+ origin_text = origin_text.gsub(origin, wrapped)
111
+ end
112
+ end if option[:opt].include?(:rtab)
113
+
114
+ if option[:opt].include?(:rendfile)
115
+ pazz = cds[:path].split('.')
116
+ pazz.insert(-2,Time.new.strftime("%Y%m%d%H%M%S"))
117
+ File.write pazz.join('.'), origin_text
118
+ end
119
+ return { text: origin_text, data: scr_tail, fact: (erl_tail.empty? ? nil : erl_tail)}
120
+ end
121
+ end