bluemark-smallcage 0.1.3
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/History.txt +48 -0
- data/License.txt +20 -0
- data/README.txt +1 -0
- data/Rakefile +140 -0
- data/bin/smc +16 -0
- data/lib/smallcage.rb +18 -0
- data/lib/smallcage/application.rb +164 -0
- data/lib/smallcage/commands/auto.rb +158 -0
- data/lib/smallcage/commands/base.rb +19 -0
- data/lib/smallcage/commands/clean.rb +32 -0
- data/lib/smallcage/commands/export.rb +41 -0
- data/lib/smallcage/commands/import.rb +217 -0
- data/lib/smallcage/commands/manifest.rb +39 -0
- data/lib/smallcage/commands/server.rb +15 -0
- data/lib/smallcage/commands/update.rb +121 -0
- data/lib/smallcage/document_path.rb +46 -0
- data/lib/smallcage/erb_base.rb +16 -0
- data/lib/smallcage/http_server.rb +66 -0
- data/lib/smallcage/loader.rb +278 -0
- data/lib/smallcage/misc.rb +13 -0
- data/lib/smallcage/renderer.rb +19 -0
- data/lib/smallcage/resources/Manifest.erb +19 -0
- data/lib/smallcage/resources/auto.html +119 -0
- data/lib/smallcage/runner.rb +51 -0
- data/lib/smallcage/version.rb +9 -0
- data/project/base/_smc/helpers/base_helper.rb +41 -0
- data/project/base/_smc/helpers/site_helper.rb +5 -0
- data/project/base/_smc/templates/default.rhtml +5 -0
- data/project/base/_smc/templates/footer.rhtml +0 -0
- data/project/base/_smc/templates/header.rhtml +0 -0
- data/project/bluecloth/_smc/helpers/blue_cloth_helper.rb +10 -0
- data/project/bluecloth/_smc/templates/markdown.rhtml +5 -0
- data/project/lang/_smc/helpers/lang_helper.rb +19 -0
- data/project/lang/_smc/templates/other_lang.rhtml +6 -0
- data/project/news/_smc/helpers/news_helper.rb +36 -0
- data/project/nkf/_smc/filters/filters.yml +3 -0
- data/project/nkf/_smc/filters/nkf_filter.rb +15 -0
- data/project/rake/_smc/Rakefile +68 -0
- data/project/redcloth/_smc/helpers/red_cloth_helper.rb +10 -0
- data/project/redcloth/_smc/templates/textile.rhtml +5 -0
- data/project/relpath/_smc/filters/filters.yml +2 -0
- data/project/relpath/_smc/filters/relpath_filter.rb +13 -0
- data/project/standard/_dir.smc +2 -0
- data/project/standard/_smc/helpers/base_helper.rb +34 -0
- data/project/standard/_smc/helpers/menu_helper.rb +23 -0
- data/project/standard/_smc/templates/default.rhtml +5 -0
- data/project/standard/_smc/templates/footer.rhtml +13 -0
- data/project/standard/_smc/templates/header.rhtml +37 -0
- data/project/standard/_smc/templates/menu.rhtml +6 -0
- data/project/standard/_smc/templates/redirect.rhtml +13 -0
- data/project/standard/_smc/templates/sidebar.rhtml +7 -0
- data/project/standard/_smc/templates/topic_path.rhtml +6 -0
- data/project/standard/common/css/default.css +145 -0
- data/project/standard/common/css/print.css +0 -0
- data/project/standard/index.html.smc +3 -0
- data/project/standard/sample/_dir.smc +1 -0
- data/project/standard/sample/index.html.smc +7 -0
- data/project/standard/sample/redirect.html.smc +2 -0
- data/project/standard/sample/sub/_dir.smc +1 -0
- data/project/standard/sample/sub/contents.html.smc +3 -0
- data/project/standard/sample/sub/index.html.smc +7 -0
- data/spec/data/htdocs1/_dir.smc +0 -0
- data/spec/data/htdocs1/a/b/c/index.html.smc +6 -0
- data/spec/data/htdocs2/_smc/templates/dummy.rhtml +0 -0
- data/spec/data/htdocs2/a/b/c/test.html +1 -0
- data/spec/data/htdocs2/a/b/test.html +1 -0
- data/spec/data/htdocs2/a/test.html.smc +2 -0
- data/spec/document_path_spec.rb +42 -0
- data/spec/export_spec.rb +45 -0
- data/spec/import_spec.rb +20 -0
- data/spec/loader_spec.rb +55 -0
- data/spec/manifest_spec.rb +39 -0
- data/spec/misc_spec.rb +25 -0
- data/spec/smallcage_spec.rb +40 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/test/test_helper.rb +2 -0
- data/test/test_smallcage.rb +11 -0
- metadata +193 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module SmallCage::Commands
|
|
2
|
+
class Clean
|
|
3
|
+
def self.execute(opts)
|
|
4
|
+
self.new(opts).execute
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def initialize(opts)
|
|
8
|
+
@opts = opts
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def execute
|
|
12
|
+
target = Pathname.new(@opts[:path])
|
|
13
|
+
unless target.exist?
|
|
14
|
+
raise "target directory or file does not exist.: " + target.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
loader = SmallCage::Loader.new(target)
|
|
18
|
+
loader.each_smc_obj do |obj|
|
|
19
|
+
if obj["path"].exist?
|
|
20
|
+
obj["path"].delete
|
|
21
|
+
puts "D " + obj["uri"] unless @opts[:quiet]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
tmpdir = loader.root + "./_smc/tmp"
|
|
26
|
+
if tmpdir.exist?
|
|
27
|
+
FileUtils.rm_r(tmpdir)
|
|
28
|
+
puts "D /_smc/tmp" unless @opts[:quiet]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module SmallCage::Commands
|
|
2
|
+
class Export < SmallCage::Commands::Base
|
|
3
|
+
def execute
|
|
4
|
+
target = Pathname.new(@opts[:path])
|
|
5
|
+
unless target.exist?
|
|
6
|
+
raise target.to_s + " does not exist."
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
loader = SmallCage::Loader.new(target)
|
|
10
|
+
root = loader.root
|
|
11
|
+
|
|
12
|
+
if @opts[:out].nil?
|
|
13
|
+
out = root + ("./_smc/tmp/export/" + Time.now.strftime("%Y%m%d%H%M%S"))
|
|
14
|
+
else
|
|
15
|
+
out = Pathname.new(@opts[:out])
|
|
16
|
+
end
|
|
17
|
+
if out.exist?
|
|
18
|
+
raise out.to_s + " already exist."
|
|
19
|
+
end
|
|
20
|
+
FileUtils.makedirs(out)
|
|
21
|
+
out = out.realpath
|
|
22
|
+
|
|
23
|
+
# TODO create empty directories
|
|
24
|
+
loader.each_not_smc_file do |docpath|
|
|
25
|
+
dir = Pathname.new(docpath.uri).parent
|
|
26
|
+
outdir = out + ("." + dir.to_s)
|
|
27
|
+
FileUtils.makedirs(outdir)
|
|
28
|
+
FileUtils.cp(docpath.path, outdir)
|
|
29
|
+
puts "A " + docpath.uri unless quiet?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
unless quiet?
|
|
33
|
+
puts ""
|
|
34
|
+
puts "All contents exported to:"
|
|
35
|
+
puts " #{out.to_s}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
module SmallCage::Commands
|
|
2
|
+
class Import
|
|
3
|
+
def self.execute(opts)
|
|
4
|
+
self.new(opts).execute
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def initialize(opts)
|
|
8
|
+
@opts = opts
|
|
9
|
+
if @opts[:from] == "default"
|
|
10
|
+
@opts[:from] = "base,standard"
|
|
11
|
+
end
|
|
12
|
+
@project_dir = Pathname.new(__FILE__) + "../../../../project"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def qp(str = "")
|
|
16
|
+
print str unless @opts[:quiet]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def qps(str = "")
|
|
20
|
+
puts str unless @opts[:quiet]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def execute
|
|
24
|
+
@dest = Pathname.new(@opts[:to])
|
|
25
|
+
|
|
26
|
+
Dir.mkdir(@dest) unless @dest.exist?
|
|
27
|
+
return unless @dest.directory?
|
|
28
|
+
|
|
29
|
+
from = @opts[:from].split(/,/)
|
|
30
|
+
from.each do |f|
|
|
31
|
+
qps
|
|
32
|
+
qps "Import: #{f}"
|
|
33
|
+
if f =~ %r{^https?://}
|
|
34
|
+
import_external(f)
|
|
35
|
+
elsif f =~ %r{^\w+$}
|
|
36
|
+
import(f)
|
|
37
|
+
else
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def import(from)
|
|
43
|
+
d = @project_dir + from
|
|
44
|
+
return unless d.directory?
|
|
45
|
+
@entries = local_entries(d)
|
|
46
|
+
unless @opts[:quiet]
|
|
47
|
+
return unless confirm_entries
|
|
48
|
+
end
|
|
49
|
+
import_entries
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def import_external
|
|
53
|
+
@entries = external_entries
|
|
54
|
+
unless @opts[:quiet]
|
|
55
|
+
return unless confirm_entries
|
|
56
|
+
end
|
|
57
|
+
import_entries
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def import_entries
|
|
61
|
+
failed = []
|
|
62
|
+
@entries.each do |e|
|
|
63
|
+
if e.overwrite?
|
|
64
|
+
qps "M /" + e.path
|
|
65
|
+
elsif ! e.exist?
|
|
66
|
+
qps "A /" + e.path
|
|
67
|
+
elsif e.to.directory?
|
|
68
|
+
# nothing
|
|
69
|
+
else
|
|
70
|
+
qps "? /" + e.path
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
e.import
|
|
75
|
+
rescue
|
|
76
|
+
failed << e
|
|
77
|
+
qps "F /" + e.path
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
unless failed.empty?
|
|
82
|
+
qps "FAILED:"
|
|
83
|
+
failed.each do |e|
|
|
84
|
+
qps " " + e.path
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
private :import_entries
|
|
89
|
+
|
|
90
|
+
def local_entries(d)
|
|
91
|
+
result = []
|
|
92
|
+
Dir.chdir(d) do
|
|
93
|
+
Dir.glob("**/*") do |f|
|
|
94
|
+
e = ImportEntry.new
|
|
95
|
+
e.path = f
|
|
96
|
+
e.from = d + f
|
|
97
|
+
e.to = @dest + f
|
|
98
|
+
result << e
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
return result
|
|
102
|
+
end
|
|
103
|
+
private :local_entries
|
|
104
|
+
|
|
105
|
+
def external_entries(uri)
|
|
106
|
+
if uri !~ %r{/$}
|
|
107
|
+
uri += "/"
|
|
108
|
+
end
|
|
109
|
+
mfuri = uri + "Manifest.html"
|
|
110
|
+
|
|
111
|
+
source = open(mfuri) {|io| io.read }
|
|
112
|
+
result = []
|
|
113
|
+
|
|
114
|
+
files = source.scan(%r{<li><a href="(./[^"]+)">(./[^<]+)</a></li>})
|
|
115
|
+
files.each do |f|
|
|
116
|
+
raise "illegal path:#{f[0]},#{f[1]}" if f[0] != f[1]
|
|
117
|
+
raise "illegal path:#{f[0]}" if f[0] =~ %r{/\.\.}
|
|
118
|
+
path = f[0]
|
|
119
|
+
e = ImportEntry.new
|
|
120
|
+
e.path = path
|
|
121
|
+
e.from = uri + path
|
|
122
|
+
e.to = @dest + path
|
|
123
|
+
result << e
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
return result
|
|
127
|
+
end
|
|
128
|
+
private :external_entries
|
|
129
|
+
|
|
130
|
+
def confirm_entries
|
|
131
|
+
overwrite = []
|
|
132
|
+
|
|
133
|
+
qps "Create:"
|
|
134
|
+
@entries.each do |e|
|
|
135
|
+
if e.overwrite?
|
|
136
|
+
overwrite << e
|
|
137
|
+
elsif ! e.exist?
|
|
138
|
+
qps " /" + e.path
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
qps
|
|
142
|
+
|
|
143
|
+
unless overwrite.empty?
|
|
144
|
+
qps "Overwrite:"
|
|
145
|
+
overwrite.each do |e|
|
|
146
|
+
qps " /" + e.path
|
|
147
|
+
end
|
|
148
|
+
qps
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
return y_or_n("Import these files?[yN]: ", false)
|
|
152
|
+
end
|
|
153
|
+
private :confirm_entries
|
|
154
|
+
|
|
155
|
+
def y_or_n(prompt, default)
|
|
156
|
+
loop do
|
|
157
|
+
print prompt
|
|
158
|
+
yn = STDIN.gets.strip
|
|
159
|
+
if yn =~ /^(y|yes)$/i
|
|
160
|
+
return true
|
|
161
|
+
elsif yn =~ /^(n|no)$/i
|
|
162
|
+
return false
|
|
163
|
+
elsif yn == ""
|
|
164
|
+
return default
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
private :y_or_n
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ImportEntry
|
|
172
|
+
attr_accessor :path, :from, :to
|
|
173
|
+
|
|
174
|
+
def import
|
|
175
|
+
if external?
|
|
176
|
+
copy_external
|
|
177
|
+
else
|
|
178
|
+
copy_local
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def external?
|
|
183
|
+
from =~ %r{^https?://}
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def exist?
|
|
187
|
+
to.exist?
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def overwrite?
|
|
191
|
+
to.file?
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def copy_local
|
|
195
|
+
if from.directory?
|
|
196
|
+
FileUtils.makedirs(to)
|
|
197
|
+
else
|
|
198
|
+
FileUtils.makedirs(to.parent)
|
|
199
|
+
FileUtils.copy(from, to)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
private :copy_local
|
|
203
|
+
|
|
204
|
+
def copy_external
|
|
205
|
+
if from =~ %r{/$}
|
|
206
|
+
FileUtils.makedirs(to)
|
|
207
|
+
else
|
|
208
|
+
FileUtils.makedirs(to.parent)
|
|
209
|
+
s = open(from) {|io| io.read }
|
|
210
|
+
open(to, "w") {|io| io << s }
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
private :copy_external
|
|
214
|
+
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module SmallCage::Commands
|
|
2
|
+
class Manifest
|
|
3
|
+
def self.execute(opts)
|
|
4
|
+
self.new(opts).execute
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def initialize(opts)
|
|
8
|
+
@opts = opts
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def execute
|
|
12
|
+
entries = []
|
|
13
|
+
root = Pathname.new(@opts[:path])
|
|
14
|
+
Dir.chdir(root) do
|
|
15
|
+
Dir.glob("**/*") do |f|
|
|
16
|
+
entries << f
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
tmp = []
|
|
21
|
+
entries.each do |f|
|
|
22
|
+
path = root + f
|
|
23
|
+
f = "./" + f
|
|
24
|
+
if path.directory?
|
|
25
|
+
f = f + "/"
|
|
26
|
+
end
|
|
27
|
+
next if path.basename.to_s == "Manifest.html"
|
|
28
|
+
tmp << f
|
|
29
|
+
end
|
|
30
|
+
entries = tmp
|
|
31
|
+
|
|
32
|
+
template = File.dirname(__FILE__) + "/../resources/Manifest.erb"
|
|
33
|
+
source = ERB.new(File.read(template), nil, "-").result(binding)
|
|
34
|
+
open(root + "Manifest.html", "w") do |io|
|
|
35
|
+
io << source
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SmallCage::Commands
|
|
2
|
+
class Server
|
|
3
|
+
def self.execute(opts)
|
|
4
|
+
document_root = opts[:path]
|
|
5
|
+
port = opts[:port]
|
|
6
|
+
|
|
7
|
+
server = SmallCage::HTTPServer.new(document_root, port)
|
|
8
|
+
|
|
9
|
+
sighandler = Proc.new {|signal| server.shutdown}
|
|
10
|
+
SmallCage::Application.add_signal_handler(["INT", "TERM"], sighandler)
|
|
11
|
+
|
|
12
|
+
server.start
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module SmallCage::Commands
|
|
2
|
+
class Update
|
|
3
|
+
def self.execute(opts)
|
|
4
|
+
self.new(opts).execute
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def initialize(opts)
|
|
8
|
+
@opts = opts
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def execute
|
|
12
|
+
target = Pathname.new(@opts[:path])
|
|
13
|
+
unless target.exist?
|
|
14
|
+
raise "target directory or file does not exist.: " + target.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
@loader = SmallCage::Loader.new(target)
|
|
18
|
+
@renderer = SmallCage::Renderer.new(@loader)
|
|
19
|
+
|
|
20
|
+
urilist = render_smc_files
|
|
21
|
+
if list_file.exist?
|
|
22
|
+
urilist = delete_expired_files(urilist)
|
|
23
|
+
end
|
|
24
|
+
save_list(urilist)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render_smc_files
|
|
28
|
+
urilist = []
|
|
29
|
+
@loader.each_smc_obj do |obj|
|
|
30
|
+
urilist << obj["uri"].smc
|
|
31
|
+
mark = obj["path"].exist? ? "U " : "A "
|
|
32
|
+
render_smc_obj(obj)
|
|
33
|
+
puts mark + obj["uri"] unless @opts[:quiet]
|
|
34
|
+
end
|
|
35
|
+
return urilist
|
|
36
|
+
end
|
|
37
|
+
private :render_smc_files
|
|
38
|
+
|
|
39
|
+
def render_smc_obj(obj)
|
|
40
|
+
result = @renderer.render(obj["template"], obj)
|
|
41
|
+
result = after_rendering_filters(obj, result)
|
|
42
|
+
output_result(obj, result)
|
|
43
|
+
end
|
|
44
|
+
private :render_smc_obj
|
|
45
|
+
|
|
46
|
+
def delete_expired_files(urilist)
|
|
47
|
+
old_urilist = load_list
|
|
48
|
+
root = @loader.root
|
|
49
|
+
target_uri = SmallCage::DocumentPath.new(root, @loader.target).uri
|
|
50
|
+
|
|
51
|
+
if @loader.target.file?
|
|
52
|
+
old_urilist << target_uri unless old_urilist.include?(target_uri)
|
|
53
|
+
return old_urilist
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
target_uris = []
|
|
57
|
+
not_target_uris = []
|
|
58
|
+
old_urilist.each do |uri|
|
|
59
|
+
if uri.index(target_uri) == 0
|
|
60
|
+
target_uris << uri
|
|
61
|
+
else
|
|
62
|
+
not_target_uris << uri
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
deletelist = target_uris - urilist
|
|
67
|
+
|
|
68
|
+
deletelist.each do |uri|
|
|
69
|
+
delfile = SmallCage::DocumentPath.new(root, root + ("." + uri)).outfile
|
|
70
|
+
next unless delfile.path.file?
|
|
71
|
+
|
|
72
|
+
File.delete(delfile.path)
|
|
73
|
+
puts "D #{delfile.uri}" unless @opts[:quiet]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return (not_target_uris + urilist).sort
|
|
77
|
+
end
|
|
78
|
+
private :delete_expired_files
|
|
79
|
+
|
|
80
|
+
def after_rendering_filters(obj, result)
|
|
81
|
+
filters = @loader.filters("after_rendering_filters")
|
|
82
|
+
filters.each do |f|
|
|
83
|
+
result = f.after_rendering_filter(obj, result)
|
|
84
|
+
end
|
|
85
|
+
return result
|
|
86
|
+
end
|
|
87
|
+
private :after_rendering_filters
|
|
88
|
+
|
|
89
|
+
def save_list(urilist)
|
|
90
|
+
f = list_file
|
|
91
|
+
FileUtils.makedirs(f.parent)
|
|
92
|
+
open(f, "w") do |io|
|
|
93
|
+
io << "version: " + SmallCage::VERSION::STRING + "\n"
|
|
94
|
+
urilist.each do |u|
|
|
95
|
+
io << u + "\n"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
private :save_list
|
|
100
|
+
|
|
101
|
+
def load_list
|
|
102
|
+
txt = File.read(list_file)
|
|
103
|
+
result = txt.split(/\n/)
|
|
104
|
+
result.shift
|
|
105
|
+
return result
|
|
106
|
+
end
|
|
107
|
+
private :load_list
|
|
108
|
+
|
|
109
|
+
def list_file
|
|
110
|
+
@loader.root + "./_smc/tmp/list.txt"
|
|
111
|
+
end
|
|
112
|
+
private :list_file
|
|
113
|
+
|
|
114
|
+
def output_result(obj, str)
|
|
115
|
+
open(obj["path"], "w") do |io|
|
|
116
|
+
io << str
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
private :output_result
|
|
120
|
+
end
|
|
121
|
+
end
|