serif 0.5.2 → 0.6
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.
- checksums.yaml +7 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +58 -21
- data/LICENSE +1 -1
- data/README.md +11 -13
- data/lib/serif.rb +3 -2
- data/lib/serif/admin_server.rb +10 -10
- data/lib/serif/commands.rb +2 -2
- data/lib/serif/config.rb +1 -1
- data/lib/serif/content_file.rb +9 -9
- data/lib/serif/draft.rb +1 -1
- data/lib/serif/errors.rb +1 -1
- data/lib/serif/markup_renderer.rb +28 -17
- data/lib/serif/post.rb +1 -1
- data/lib/serif/server.rb +1 -1
- data/lib/serif/site.rb +5 -29
- data/rakefile +3 -6
- data/serif.gemspec +9 -3
- data/statics/assets/js/attachment.js +3 -3
- data/statics/skeleton/_config.yml +1 -1
- data/statics/skeleton/_layouts/default.html +1 -1
- data/statics/skeleton/_templates/archive_page.html +1 -1
- data/statics/skeleton/_templates/post.html +1 -1
- data/statics/skeleton/archive.html +1 -1
- data/statics/skeleton/index.html +1 -1
- data/statics/templates/admin/bookmarks.liquid +1 -1
- data/statics/templates/admin/edit_draft.liquid +1 -1
- data/statics/templates/admin/index.liquid +1 -1
- data/statics/templates/admin/layout.liquid +6 -4
- data/statics/templates/admin/new_draft.liquid +1 -1
- metadata +123 -127
- data/test/commands_spec.rb +0 -77
- data/test/config_spec.rb +0 -55
- data/test/content_file_spec.rb +0 -113
- data/test/draft_spec.rb +0 -275
- data/test/file_digest_tag_spec.rb +0 -38
- data/test/filters_spec.rb +0 -90
- data/test/liquid_filter_date_extension_spec.rb +0 -15
- data/test/markup_renderer_spec.rb +0 -47
- data/test/post_spec.rb +0 -139
- data/test/site_dir/_config.yml +0 -18
- data/test/site_dir/_drafts/another-sample-draft +0 -3
- data/test/site_dir/_drafts/sample-draft +0 -3
- data/test/site_dir/_layouts/alt-layout.html +0 -3
- data/test/site_dir/_layouts/default.html +0 -8
- data/test/site_dir/_posts/2012-01-05-sample-post +0 -4
- data/test/site_dir/_posts/2013-01-01-second-post +0 -4
- data/test/site_dir/_posts/2013-03-07-post-with-custom-layout +0 -5
- data/test/site_dir/_posts/2399-01-01-penultimate-post +0 -4
- data/test/site_dir/_posts/2400-01-01-final-post +0 -4
- data/test/site_dir/_templates/archive_page.html +0 -9
- data/test/site_dir/_templates/post.html +0 -10
- data/test/site_dir/archive.html +0 -7
- data/test/site_dir/file-digest-test.html +0 -4
- data/test/site_dir/index.html +0 -9
- data/test/site_dir/page-alt-layout.html +0 -3
- data/test/site_dir/page-header-but-no-layout.html +0 -3
- data/test/site_dir/test-smarty-filter.html +0 -3
- data/test/site_dir/test-stylesheet.css +0 -3
- data/test/site_generation_spec.rb +0 -204
- data/test/site_spec.rb +0 -189
- data/test/test_helper.rb +0 -61
data/test/site_dir/index.html
DELETED
@@ -1,204 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
describe Serif::Site do
|
4
|
-
subject do
|
5
|
-
Serif::Site.new(testing_dir)
|
6
|
-
end
|
7
|
-
|
8
|
-
before(:each) do
|
9
|
-
FileUtils.rm_rf(testing_dir("_site"))
|
10
|
-
end
|
11
|
-
|
12
|
-
describe "site generation" do
|
13
|
-
it "raises PostConflictError if there are conflicts" do
|
14
|
-
# not nil, the value is unimportant
|
15
|
-
subject.stub(:conflicts) { [] }
|
16
|
-
expect { capture_stdout { subject.generate } }.to raise_error(Serif::PostConflictError)
|
17
|
-
end
|
18
|
-
|
19
|
-
it "uses the permalinks in the config file for site generation" do
|
20
|
-
capture_stdout { subject.generate }
|
21
|
-
File.exist?(testing_dir("_site/test-blog/sample-post.html")).should be_true
|
22
|
-
end
|
23
|
-
|
24
|
-
it "reads the layout header for a non-post file and uses the appropriate layout file" do
|
25
|
-
capture_stdout { subject.generate }
|
26
|
-
|
27
|
-
# check it actually got generated
|
28
|
-
File.exist?(testing_dir("_site/page-alt-layout.html")).should be_true
|
29
|
-
File.read("_site/page-alt-layout.html").lines.first.should =~ /<h1.+?>Alternate layout<\/h1>/
|
30
|
-
end
|
31
|
-
|
32
|
-
it "reads the layout header for a post file and uses the appropriate layout file" do
|
33
|
-
capture_stdout { subject.generate }
|
34
|
-
|
35
|
-
# check it actually got generated
|
36
|
-
File.exist?(testing_dir("_site/test-blog/post-with-custom-layout.html")).should be_true
|
37
|
-
File.read("_site/test-blog/post-with-custom-layout.html").lines.first.should =~ /<h1.+?>Alternate layout<\/h1>/
|
38
|
-
end
|
39
|
-
|
40
|
-
it "supports a smarty filter" do
|
41
|
-
capture_stdout { subject.generate }
|
42
|
-
File.read("_site/test-smarty-filter.html").should =~ /testing’s for a “heading’s” `with code` in it…/
|
43
|
-
end
|
44
|
-
|
45
|
-
it "correctly handles file_digest calls" do
|
46
|
-
capture_stdout { subject.generate }
|
47
|
-
|
48
|
-
File.read("_site/file-digest-test.html").strip.should == "f8390232f0c354a871f9ba0ed306163c\n.f8390232f0c354a871f9ba0ed306163c"
|
49
|
-
end
|
50
|
-
|
51
|
-
it "makes the previous and next posts available" do
|
52
|
-
capture_stdout { subject.generate }
|
53
|
-
|
54
|
-
contents = File.read("_site/test-blog/sample-post.html")
|
55
|
-
previous_title = contents[/^Previous post: .+?$/]
|
56
|
-
next_title = contents[/^Next post: .+?$/]
|
57
|
-
|
58
|
-
previous_title.should be_nil
|
59
|
-
next_title.should_not be_nil
|
60
|
-
next_title[/(?<=: ).+/].should == "Second post"
|
61
|
-
|
62
|
-
contents = File.read("_site/test-blog/final-post.html")
|
63
|
-
previous_title = contents[/Previous post: .+?$/]
|
64
|
-
next_title = contents[/Next post: .+?$/]
|
65
|
-
|
66
|
-
previous_title.should_not be_nil
|
67
|
-
next_title.should be_nil
|
68
|
-
previous_title[/(?<=: ).+/].should == "Penultimate post"
|
69
|
-
end
|
70
|
-
|
71
|
-
it "sets a draft_preview flag for preview urls" do
|
72
|
-
preview_flag_pattern = /draftpreviewflagexists/
|
73
|
-
|
74
|
-
capture_stdout { subject.generate }
|
75
|
-
|
76
|
-
d = Serif::Draft.from_slug(subject, "sample-draft")
|
77
|
-
preview_contents = File.read(testing_dir("_site/#{subject.private_url(d)}.html"))
|
78
|
-
(preview_contents =~ preview_flag_pattern).should be_true
|
79
|
-
|
80
|
-
# does not exist on live published pages
|
81
|
-
(File.read(testing_dir("_site/test-blog/second-post.html")) =~ preview_flag_pattern).should be_false
|
82
|
-
end
|
83
|
-
|
84
|
-
it "sets a post_page flag for regular posts" do
|
85
|
-
capture_stdout { subject.generate }
|
86
|
-
d = Serif::Post.from_basename(subject, "2013-01-01-second-post")
|
87
|
-
d.should_not be_nil
|
88
|
-
contents = File.read(testing_dir("_site#{d.url}.html"))
|
89
|
-
|
90
|
-
# available to the post layout file
|
91
|
-
(contents =~ /post_page flag set for template/).should be_true
|
92
|
-
|
93
|
-
# available in the layout file itself
|
94
|
-
(contents =~ /post_page flag set for layout/).should be_true
|
95
|
-
|
96
|
-
# not set for regular pages
|
97
|
-
(File.read(testing_dir("_site/index.html")) =~ /post_page flag set for template/).should be_false
|
98
|
-
(File.read(testing_dir("_site/index.html")) =~ /post_page flag set for layout/).should be_false
|
99
|
-
|
100
|
-
# not set for drafts
|
101
|
-
d = Serif::Draft.from_slug(subject, "sample-draft")
|
102
|
-
preview_contents = File.read(testing_dir("_site/#{subject.private_url(d)}.html"))
|
103
|
-
(preview_contents =~ /post_page flag set for template/).should be_false
|
104
|
-
(preview_contents =~ /post_page flag set for layout/).should be_false
|
105
|
-
end
|
106
|
-
|
107
|
-
it "creates draft preview files" do
|
108
|
-
capture_stdout { subject.generate }
|
109
|
-
|
110
|
-
Dir.exist?(testing_dir("_site/drafts")).should be_true
|
111
|
-
Dir[File.join(testing_dir("_site/drafts/*"))].size.should == subject.drafts.size
|
112
|
-
|
113
|
-
Dir.exist?(testing_dir("_site/drafts/sample-draft")).should be_true
|
114
|
-
Dir[File.join(testing_dir("_site/drafts/sample-draft"), "*.html")].size.should == 1
|
115
|
-
|
116
|
-
d = Serif::Draft.from_slug(subject, "sample-draft")
|
117
|
-
subject.private_url(d).should_not be_nil
|
118
|
-
|
119
|
-
# absolute paths
|
120
|
-
(subject.private_url(d) =~ /\A\/drafts\/#{d.slug}\/.*\z/).should be_true
|
121
|
-
|
122
|
-
# 60 characters long (30 bytes as hex chars)
|
123
|
-
(subject.private_url(d) =~ /\A\/drafts\/#{d.slug}\/[a-z0-9]{60}\z/).should be_true
|
124
|
-
|
125
|
-
# does not create more than one
|
126
|
-
capture_stdout { subject.generate }
|
127
|
-
Dir[File.join(testing_dir("_site/drafts/sample-draft"), "*.html")].size.should == 1
|
128
|
-
end
|
129
|
-
|
130
|
-
context "for posts with an update: now header" do
|
131
|
-
around :each do |example|
|
132
|
-
begin
|
133
|
-
d = Serif::Draft.new(subject)
|
134
|
-
d.slug = "post-to-be-auto-updated"
|
135
|
-
d.title = "Testing title"
|
136
|
-
d.save("# some content")
|
137
|
-
d.publish!
|
138
|
-
|
139
|
-
@temporary_post = Serif::Post.new(subject, d.path)
|
140
|
-
@temporary_post.autoupdate = true
|
141
|
-
@temporary_post.save
|
142
|
-
|
143
|
-
example.run
|
144
|
-
ensure
|
145
|
-
FileUtils.rm(@temporary_post.path)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
it "sets the updated header to the current time" do
|
150
|
-
t = Time.now + 30
|
151
|
-
Timecop.freeze(t) do
|
152
|
-
capture_stdout { subject.generate }
|
153
|
-
Serif::Post.from_basename(subject, @temporary_post.basename).updated.to_i.should == t.to_i
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
context "for drafts with a publish: now header" do
|
159
|
-
before :each do
|
160
|
-
@time = Time.utc(2012, 12, 21, 15, 30, 00)
|
161
|
-
|
162
|
-
draft = Serif::Draft.new(subject)
|
163
|
-
draft.slug = "post-to-be-published-on-generate"
|
164
|
-
draft.title = "Some draft title"
|
165
|
-
draft.autopublish = true
|
166
|
-
draft.save("some content")
|
167
|
-
|
168
|
-
@post = Serif::Draft.from_slug(subject, draft.slug)
|
169
|
-
@post.should_not be_nil
|
170
|
-
|
171
|
-
# verifies that the header has actually been written to the file, since
|
172
|
-
# we round-trip the save and load.
|
173
|
-
@post.autopublish?.should be_true
|
174
|
-
|
175
|
-
# Site#generate creates a backup of the site directory in /tmp
|
176
|
-
# and uses a timestamp, which is now fixed across all tests,
|
177
|
-
# so we have to remove it first.
|
178
|
-
FileUtils.rm_rf("/tmp/_site.2012-12-21-15-30-00")
|
179
|
-
|
180
|
-
Timecop.freeze(@time)
|
181
|
-
end
|
182
|
-
|
183
|
-
after :each do
|
184
|
-
Timecop.return
|
185
|
-
|
186
|
-
# the generate processes creates its own set of instances, and we're
|
187
|
-
# publishing a draft marked as autopublish, so our @post instance
|
188
|
-
# has a #path value which is for the draft, not for the newly published
|
189
|
-
# post. thus, we need to clobber.
|
190
|
-
FileUtils.rm(*Dir[testing_dir("_posts/*-#{@post.slug}")])
|
191
|
-
end
|
192
|
-
|
193
|
-
it "places the file in the published posts folder" do
|
194
|
-
capture_stdout { subject.generate }
|
195
|
-
File.exist?(testing_dir("_site/test-blog/#{@post.slug}.html")).should be_true
|
196
|
-
end
|
197
|
-
|
198
|
-
it "marks the creation time as the current time" do
|
199
|
-
capture_stdout { subject.generate }
|
200
|
-
subject.posts.find { |p| p.slug == @post.slug }.created.to_i.should == @time.to_i
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
data/test/site_spec.rb
DELETED
@@ -1,189 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
describe Serif::Site do
|
4
|
-
subject do
|
5
|
-
Serif::Site.new(testing_dir)
|
6
|
-
end
|
7
|
-
|
8
|
-
describe "#conflicts" do
|
9
|
-
context "with no arguments" do
|
10
|
-
it "is nil if there are no conflicts" do
|
11
|
-
subject.conflicts.should be_nil
|
12
|
-
end
|
13
|
-
|
14
|
-
it "is a map of url => conflicts_array if there are conflicts" do
|
15
|
-
d = Serif::Draft.new(subject)
|
16
|
-
conflicting_post = subject.posts.first
|
17
|
-
d.slug = conflicting_post.slug
|
18
|
-
d.title = "Anything you like"
|
19
|
-
d.save("# Some content")
|
20
|
-
|
21
|
-
# need this to be true
|
22
|
-
d.url.should == conflicting_post.url
|
23
|
-
|
24
|
-
begin
|
25
|
-
conflicts = subject.conflicts
|
26
|
-
conflicts.should_not be_nil
|
27
|
-
conflicts.class.should == Hash
|
28
|
-
conflicts.size.should == 1
|
29
|
-
conflicts.keys.should == [conflicting_post.url]
|
30
|
-
conflicts[conflicting_post.url].size.should == 2
|
31
|
-
ensure
|
32
|
-
FileUtils.rm(d.path)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context "with an argument given" do
|
38
|
-
it "is nil if there are no conflicts" do
|
39
|
-
subject.conflicts(subject.drafts.sample).should be_nil
|
40
|
-
subject.conflicts(subject.posts.sample).should be_nil
|
41
|
-
|
42
|
-
d = Serif::Draft.new(subject)
|
43
|
-
subject.conflicts(d).should be_nil
|
44
|
-
end
|
45
|
-
|
46
|
-
it "is an array of conflicting content if there are conflicts" do
|
47
|
-
d = Serif::Draft.new(subject)
|
48
|
-
conflicting_post = subject.posts.first
|
49
|
-
d.slug = conflicting_post.slug
|
50
|
-
d.title = "Anything you like"
|
51
|
-
d.save("# Some content")
|
52
|
-
|
53
|
-
# need this to be true
|
54
|
-
d.url.should == conflicting_post.url
|
55
|
-
|
56
|
-
begin
|
57
|
-
conflicts = subject.conflicts(d)
|
58
|
-
conflicts.should_not be_nil
|
59
|
-
conflicts.class.should == Array
|
60
|
-
conflicts.size.should == 2
|
61
|
-
conflicts.each do |e|
|
62
|
-
e.url.should == conflicting_post.url
|
63
|
-
end
|
64
|
-
ensure
|
65
|
-
FileUtils.rm(d.path)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
describe "#source_directory" do
|
72
|
-
it "should be sane" do
|
73
|
-
subject.directory.should == File.join(File.dirname(__FILE__), "site_dir")
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
describe "#posts" do
|
78
|
-
it "is the number of posts in the site" do
|
79
|
-
subject.posts.length.should == 5
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
describe "#drafts" do
|
84
|
-
it "is the number of drafts in the site" do
|
85
|
-
subject.drafts.length.should == 2
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe "#private_url" do
|
90
|
-
it "returns nil for a draft without an existing file" do
|
91
|
-
d = double("")
|
92
|
-
d.stub(:slug) { "foo" }
|
93
|
-
subject.private_url(d).should be_nil
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
describe "#latest_update_time" do
|
98
|
-
it "is the latest time that a post was updated" do
|
99
|
-
subject.latest_update_time.should == Serif::Post.all(subject).max_by { |p| p.updated }.updated
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
describe "#site_path" do
|
104
|
-
it "should be relative, not absolute" do
|
105
|
-
p = Pathname.new(subject.site_path("foo"))
|
106
|
-
p.relative?.should be_true
|
107
|
-
p.absolute?.should be_false
|
108
|
-
end
|
109
|
-
|
110
|
-
it "takes a string and prepends _site to that path" do
|
111
|
-
%w[a b c d e f].each do |e|
|
112
|
-
subject.site_path(e).should == "_site/#{e}"
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
describe "#config" do
|
118
|
-
it "is a Serif::Config instance" do
|
119
|
-
subject.config.class.should == Serif::Config
|
120
|
-
end
|
121
|
-
|
122
|
-
it "should have the permalink format available" do
|
123
|
-
subject.config.permalink.should_not be_nil
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
describe "#archives" do
|
128
|
-
it "contains posts given in reverse chronological order" do
|
129
|
-
archives = subject.archives
|
130
|
-
archives[:posts].each_cons(2) do |a, b|
|
131
|
-
(a.created >= b.created).should be_true
|
132
|
-
end
|
133
|
-
|
134
|
-
archives[:years].each do |year|
|
135
|
-
year[:posts].each_cons(2) do |a, b|
|
136
|
-
(a.created >= b.created).should be_true
|
137
|
-
end
|
138
|
-
|
139
|
-
year[:months].each do |month|
|
140
|
-
month[:posts].each_cons(2) do |a, b|
|
141
|
-
(a.created >= b.created).should be_true
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
describe "#to_liquid" do
|
149
|
-
it "uses the value of #archives without modification" do
|
150
|
-
subject.should_receive(:archives).once
|
151
|
-
subject.to_liquid
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
describe "#archive_url_for_date" do
|
156
|
-
it "uses the archive URL format from the config to construct an archive URL string" do
|
157
|
-
date = Date.parse("2012-01-02")
|
158
|
-
subject.archive_url_for_date(date).should == "/test-archive/2012/01"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
describe "#bypass?" do
|
163
|
-
it "is false if the filename has a .html extension" do
|
164
|
-
subject.bypass?("foo.html").should be_false
|
165
|
-
end
|
166
|
-
|
167
|
-
it "is false if the filename has an .xml extension" do
|
168
|
-
subject.bypass?("foo.xml").should be_false
|
169
|
-
end
|
170
|
-
|
171
|
-
it "is true if the filename is neither xml nor html by extension" do
|
172
|
-
subject.bypass?("foo.css").should be_true
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
describe "#tmp_path" do
|
177
|
-
it "takes a string and prepends tmp/_site to that path" do
|
178
|
-
%w[a b c d].each do |e|
|
179
|
-
subject.tmp_path(e).should == "tmp/_site/#{e}"
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
it "should be relative, not absolute" do
|
184
|
-
p = Pathname.new(subject.tmp_path("foo"))
|
185
|
-
p.absolute?.should be_false
|
186
|
-
p.relative?.should be_true
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require "simplecov"
|
2
|
-
|
3
|
-
# if we're running on Travis, use Coveralls, otherwise
|
4
|
-
# let us generate SimpleCov output as normal.
|
5
|
-
if ENV["CI"]
|
6
|
-
require "coveralls"
|
7
|
-
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
8
|
-
end
|
9
|
-
|
10
|
-
SimpleCov.start do
|
11
|
-
add_filter "/test/"
|
12
|
-
end
|
13
|
-
|
14
|
-
# run tests in production mode so that file digests are enabled
|
15
|
-
ENV["ENV"] = "production"
|
16
|
-
|
17
|
-
# workaround checking. here before loading our application to ensure
|
18
|
-
# we aren't testing against our own monkeypatches.
|
19
|
-
|
20
|
-
describe "date 'now' patch" do
|
21
|
-
# if this test fails, the monkey match on StandardFilters#date can be removed
|
22
|
-
it "is necessary" do
|
23
|
-
liquid_filter = Object.new
|
24
|
-
liquid_filter.extend(Liquid::StandardFilters)
|
25
|
-
(liquid_filter.date_orig("now", "%Y") rescue "").should_not == Time.now.year.to_s
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "curly quote patch" do
|
30
|
-
# if this test fails, the workaround for the "markdown" filter can be removed
|
31
|
-
it "is necessary" do
|
32
|
-
renderer = Redcarpet::Markdown.new(Serif::MarkupRenderer)
|
33
|
-
renderer.render("something's here").should_not include("something’s here")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
require "serif"
|
38
|
-
require "serif/commands"
|
39
|
-
require "fileutils"
|
40
|
-
require "pathname"
|
41
|
-
require "time"
|
42
|
-
require "date"
|
43
|
-
require "timecop"
|
44
|
-
|
45
|
-
def testing_dir(path = nil)
|
46
|
-
full_path = File.join(File.dirname(__FILE__), "site_dir")
|
47
|
-
|
48
|
-
path ? File.join(full_path, path) : full_path
|
49
|
-
end
|
50
|
-
|
51
|
-
def capture_stdout
|
52
|
-
begin
|
53
|
-
$orig_stdout = $stdout
|
54
|
-
$stdout = StringIO.new
|
55
|
-
yield
|
56
|
-
$stdout.rewind
|
57
|
-
return $stdout.string
|
58
|
-
ensure
|
59
|
-
$stdout = $orig_stdout
|
60
|
-
end
|
61
|
-
end
|