rjack-tarpit 2.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +71 -0
- data/Manifest.txt +45 -0
- data/NOTICE.txt +2 -0
- data/README.rdoc +124 -0
- data/Rakefile +21 -0
- data/lib/rjack-tarpit.rb +40 -0
- data/lib/rjack-tarpit/base.rb +22 -0
- data/lib/rjack-tarpit/base_strategy.rb +175 -0
- data/lib/rjack-tarpit/clean.rb +43 -0
- data/lib/rjack-tarpit/doc.rb +67 -0
- data/lib/rjack-tarpit/gem.rb +119 -0
- data/lib/rjack-tarpit/git.rb +45 -0
- data/lib/rjack-tarpit/line_match.rb +134 -0
- data/lib/rjack-tarpit/readme_parser.rb +74 -0
- data/lib/rjack-tarpit/spec.rb +314 -0
- data/lib/rjack-tarpit/test.rb +110 -0
- data/lib/rjack-tarpit/util.rb +37 -0
- data/test/jproject/History.rdoc +2 -0
- data/test/jproject/Manifest.static +10 -0
- data/test/jproject/Manifest.txt +11 -0
- data/test/jproject/NOTICE.txt +2 -0
- data/test/jproject/README.rdoc +24 -0
- data/test/jproject/Rakefile +7 -0
- data/test/jproject/jproject.gemspec +15 -0
- data/test/jproject/lib/jproject.rb +11 -0
- data/test/jproject/lib/jproject/base.rb +3 -0
- data/test/jproject/pom.xml +75 -0
- data/test/jproject/test/setup.rb +7 -0
- data/test/jproject/test/test_jproject.rb +14 -0
- data/test/setup.rb +7 -0
- data/test/test_projects.rb +71 -0
- data/test/test_readme_parser.rb +97 -0
- data/test/test_tarpit.rb +33 -0
- data/test/zookeeper/History.rdoc +2 -0
- data/test/zookeeper/Manifest.static +10 -0
- data/test/zookeeper/Manifest.txt +13 -0
- data/test/zookeeper/README.rdoc +23 -0
- data/test/zookeeper/Rakefile +7 -0
- data/test/zookeeper/assembly.xml +15 -0
- data/test/zookeeper/lib/rjack-zookeeper.rb +29 -0
- data/test/zookeeper/lib/rjack-zookeeper/base.rb +23 -0
- data/test/zookeeper/pom.xml +73 -0
- data/test/zookeeper/rjack-zookeeper.gemspec +18 -0
- data/test/zookeeper/test/setup.rb +17 -0
- data/test/zookeeper/test/test_zookeeper.rb +14 -0
- metadata +186 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2012 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
module RJack::TarPit
|
18
|
+
|
19
|
+
module CleanTaskDefiner
|
20
|
+
|
21
|
+
# An array of file patterns to delete on clean.
|
22
|
+
attr_accessor :clean_globs
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
super
|
26
|
+
|
27
|
+
@clean_globs ||= %w[ .source_index **/*~ **/.*~ ]
|
28
|
+
|
29
|
+
add_define_hook( :define_clean_tasks )
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_clean_tasks
|
33
|
+
desc 'Clean up (common backup file patterns)'
|
34
|
+
task :clean do
|
35
|
+
globs = clean_globs + [ 'pkg', rdoc_dir ]
|
36
|
+
files = globs.map { |p| Dir[ p ] }.flatten
|
37
|
+
rm_rf( files, :verbose => true ) unless files.empty?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2012 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
module RJack::TarPit
|
18
|
+
|
19
|
+
module DocTaskDefiner
|
20
|
+
|
21
|
+
# Local destination directory for RDoc generated files.
|
22
|
+
# (default: doc)
|
23
|
+
attr_accessor :rdoc_dir
|
24
|
+
|
25
|
+
# Remote destinations array for publish_rdoc. (default: [])
|
26
|
+
attr_accessor :rdoc_destinations
|
27
|
+
|
28
|
+
# Rsync flags for publish_rdoc. (default: %w[ -a -u -i ])
|
29
|
+
attr_accessor :publish_rdoc_rsync_flags
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
super
|
33
|
+
|
34
|
+
@rdoc_dir = 'doc'
|
35
|
+
@rdoc_destinations = []
|
36
|
+
@publish_rdoc_rsync_flags = %w[ -a -u -i ]
|
37
|
+
|
38
|
+
add_define_hook( :define_doc_tasks )
|
39
|
+
end
|
40
|
+
|
41
|
+
def define_doc_tasks
|
42
|
+
require 'rdoc/task'
|
43
|
+
|
44
|
+
RDoc::Task.new( :rdoc ) do |t|
|
45
|
+
t.rdoc_dir = rdoc_dir
|
46
|
+
t.rdoc_files += spec.require_paths
|
47
|
+
t.rdoc_files += spec.extra_rdoc_files
|
48
|
+
t.options = spec.rdoc_options
|
49
|
+
end
|
50
|
+
|
51
|
+
unless rdoc_destinations.empty?
|
52
|
+
desc "Publish rdoc to #{ rdoc_destinations.join( ', ' ) }"
|
53
|
+
task :publish_rdoc => [ :docs ] do
|
54
|
+
rdoc_destinations.each do |dest|
|
55
|
+
sh( *[ 'rsync', publish_rdoc_rsync_flags,
|
56
|
+
rdoc_dir + '/', dest ].flatten )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "Alias rdoc task"
|
62
|
+
task :docs => [ :rdoc ]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2012 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'rubygems/package_task'
|
18
|
+
|
19
|
+
module RJack::TarPit
|
20
|
+
|
21
|
+
module GemTaskDefiner
|
22
|
+
|
23
|
+
# Should package create a tarball? (default: false)
|
24
|
+
attr_accessor :need_tar
|
25
|
+
|
26
|
+
# Should package create a zipfile? (default: false)
|
27
|
+
attr_accessor :need_zip
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
super
|
31
|
+
|
32
|
+
@need_tar = false
|
33
|
+
@need_zip = false
|
34
|
+
|
35
|
+
add_define_hook( :define_gem_tasks )
|
36
|
+
end
|
37
|
+
|
38
|
+
def define_gem_tasks
|
39
|
+
|
40
|
+
Gem::PackageTask.new( spec ) do |pkg|
|
41
|
+
pkg.need_tar = @need_tar
|
42
|
+
pkg.need_zip = @need_zip
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'Dump plain ruby Gem::Specification'
|
46
|
+
task :debug_gem do
|
47
|
+
puts spec.to_ruby
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "gem push (gemcutter)"
|
51
|
+
task :push => [ :gem ] do
|
52
|
+
require 'rubygems'
|
53
|
+
require 'rubygems/command_manager'
|
54
|
+
cm = Gem::CommandManager.instance
|
55
|
+
cm.run( gem_config( 'push', '-V', gem_file ) )
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "gem(+maven) install"
|
59
|
+
task :install => [ :gem ] do
|
60
|
+
require 'rubygems'
|
61
|
+
require 'rubygems/command_manager'
|
62
|
+
cm = Gem::CommandManager.instance
|
63
|
+
begin
|
64
|
+
cm.run( gem_config( 'install', '--local', '-V', gem_file ) )
|
65
|
+
rescue Gem::SystemExitException => x
|
66
|
+
raise "Install failed (#{x.exit_code})" if x.exit_code != 0
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "gem install missing/all dev dependencies"
|
71
|
+
task( :install_deps, :force ) do |t,args|
|
72
|
+
require 'rubygems'
|
73
|
+
require 'rubygems/command_manager'
|
74
|
+
force = ( args[:force] == 'force' )
|
75
|
+
( @spec.extra_deps + @spec.extra_dev_deps ).each do |dep|
|
76
|
+
if force
|
77
|
+
gem_install_dep( dep )
|
78
|
+
else
|
79
|
+
begin
|
80
|
+
gem( *dep )
|
81
|
+
rescue Gem::LoadError => e
|
82
|
+
puts "Gem dep: " + e.to_s
|
83
|
+
gem_install_dep( dep )
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def gem_install_dep( dep )
|
91
|
+
puts "Install: " + dep.inspect
|
92
|
+
cm = Gem::CommandManager.instance
|
93
|
+
c = [ 'install', '--remote', '-V', dep.first ]
|
94
|
+
c += dep[1..-1].map { |r| [ '-v', r ] }.flatten
|
95
|
+
cm.run( gem_config( *c ) )
|
96
|
+
rescue Gem::SystemExitException => x
|
97
|
+
raise "Install failed (#{x.exit_code})" if x.exit_code != 0
|
98
|
+
end
|
99
|
+
|
100
|
+
def gem_file
|
101
|
+
parts = [ spec.name, spec.version ]
|
102
|
+
p = spec.platform
|
103
|
+
parts << 'java' if p.respond_to?( :os ) && p.os == 'java'
|
104
|
+
|
105
|
+
"pkg/#{ parts.join( '-' ) }.gem"
|
106
|
+
end
|
107
|
+
|
108
|
+
def gem_config( command, *args )
|
109
|
+
cargs = [ 'gem', command ].map do |cmd|
|
110
|
+
conf = Gem.configuration[ cmd ]
|
111
|
+
conf.is_a?( String ) ? conf.split( ' ' ) : Array( conf )
|
112
|
+
end
|
113
|
+
cargs.flatten!
|
114
|
+
[ command ] + cargs + args
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2012 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
module RJack::TarPit
|
18
|
+
|
19
|
+
module GitTaskDefiner
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
|
24
|
+
add_define_hook( :define_git_tasks )
|
25
|
+
end
|
26
|
+
|
27
|
+
# Define git based :tag task
|
28
|
+
def define_git_tasks
|
29
|
+
desc "git tag current version"
|
30
|
+
task :tag do
|
31
|
+
tag = [ spec.name, spec.version ].join( '-' )
|
32
|
+
dname = Rake.original_dir
|
33
|
+
dname = '.' if Dir.getwd == dname
|
34
|
+
delta = `git status --porcelain -- #{dname} 2>&1`.split(/^/)
|
35
|
+
if delta.length > 0
|
36
|
+
puts delta
|
37
|
+
raise "Commit these changes before tagging"
|
38
|
+
end
|
39
|
+
sh %{git tag -s -f -m "tag [#{tag}]" "#{tag}"}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2012 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
module RJack::TarPit
|
18
|
+
|
19
|
+
module LineMatchTaskDefiner
|
20
|
+
|
21
|
+
# Regexp to use for first line of History file with version, date
|
22
|
+
attr_accessor :history_regexp
|
23
|
+
|
24
|
+
# Regexp to use for a valid/releasable date on History
|
25
|
+
attr_accessor :history_date_regexp
|
26
|
+
|
27
|
+
# Proc returning regexp given gem version as parameter, to use to
|
28
|
+
# validate the version of the latest history entry.
|
29
|
+
attr_accessor :history_version_regexp
|
30
|
+
|
31
|
+
# Array of "init" files to check for gem version references in
|
32
|
+
# (default: [ init/<spec.name> ], if exists)
|
33
|
+
attr_accessor :init_files
|
34
|
+
|
35
|
+
# Proc given spec and returning regexp matching gem line to find in
|
36
|
+
# init_files. (default: /^gem.+<spec.name>/)
|
37
|
+
attr_accessor :init_line_regexp
|
38
|
+
|
39
|
+
# Proc returning regexp given gem version as parameter, to use to
|
40
|
+
# validate the gem version of gem line in init_files.
|
41
|
+
# (default: /= <spec.version>/)
|
42
|
+
attr_accessor :init_version_regexp
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
super
|
46
|
+
|
47
|
+
@history_regexp = /^==/
|
48
|
+
@history_date_regexp = /\([0-9\-]+\)$/
|
49
|
+
@history_version_regexp = Proc.new { |v| / #{v} / }
|
50
|
+
|
51
|
+
@init_files = :default
|
52
|
+
@init_line_regexp = Proc.new { |s| /^gem.+#{s.name}/ }
|
53
|
+
@init_version_regexp = Proc.new { |v| /= #{v}/ }
|
54
|
+
|
55
|
+
add_define_hook( :define_line_match_tasks )
|
56
|
+
end
|
57
|
+
|
58
|
+
def define_line_match_tasks
|
59
|
+
|
60
|
+
if spec.history_file && history_regexp
|
61
|
+
desc "Check that #{spec.history_file} has latest version"
|
62
|
+
task :check_history_version do
|
63
|
+
test_line_match( spec.history_file,
|
64
|
+
history_regexp,
|
65
|
+
history_version_regexp.call( spec.version ) )
|
66
|
+
end
|
67
|
+
[ :gem, :tag, :push ].each { |t| task t => :check_history_version }
|
68
|
+
end
|
69
|
+
|
70
|
+
if spec.history_file && history_date_regexp
|
71
|
+
desc "Check that #{spec.history_file} has a date for release"
|
72
|
+
task :check_history_date do
|
73
|
+
test_line_match( spec.history_file,
|
74
|
+
history_regexp,
|
75
|
+
history_date_regexp )
|
76
|
+
end
|
77
|
+
[ :tag, :push ].each { |t| task t => :check_history_date }
|
78
|
+
end
|
79
|
+
|
80
|
+
if init_files == :default
|
81
|
+
init_files = [ File.join( 'init', spec.name ) ].
|
82
|
+
select { |f| File.exist?( f ) }
|
83
|
+
end
|
84
|
+
|
85
|
+
init_files.each do |inf|
|
86
|
+
desc "Check that #{init_files.join(", ")} has latest version"
|
87
|
+
task :check_init_version do
|
88
|
+
test_line_match( inf,
|
89
|
+
init_line_regexp.call( spec ),
|
90
|
+
init_version_regexp.call( spec.version ) )
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
unless init_files.empty?
|
95
|
+
[ :gem, :tag ].each { |t| task t => :check_init_version }
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
# Test that all specified files have at least one line matching
|
101
|
+
# line_regex, and that first line additionally matches (optional)
|
102
|
+
# pass_line_regex.
|
103
|
+
# ==== Parameters
|
104
|
+
# files<Array(~)>:: List of files to test
|
105
|
+
# line_regex<Regexp>:: Test first matching line
|
106
|
+
# pass_line_regex:: Further test on match line (default: match all)
|
107
|
+
# ==== Raises
|
108
|
+
# RuntimeError:: on test failure.
|
109
|
+
def test_line_match( files, line_regex, pass_line_regex = // )
|
110
|
+
Array( files ).each do |mfile|
|
111
|
+
found = false
|
112
|
+
open( mfile ) do |mf|
|
113
|
+
num = 0
|
114
|
+
mf.each do |line|
|
115
|
+
num += 1
|
116
|
+
if line =~ line_regex
|
117
|
+
found = true
|
118
|
+
unless line =~ pass_line_regex
|
119
|
+
raise( "%s:%d: %s !~ %s" %
|
120
|
+
[ mfile, num, line.strip, pass_line_regex.inspect ] )
|
121
|
+
end
|
122
|
+
break
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
unless found
|
127
|
+
raise "#{mfile}: #{line_regex.inspect} not found"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2012 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'rjack-tarpit/base'
|
18
|
+
|
19
|
+
module RJack::TarPit
|
20
|
+
|
21
|
+
# Helper mixin for deriving default spec properties from the README
|
22
|
+
# (rdoc,txt) file
|
23
|
+
module ReadmeParser
|
24
|
+
|
25
|
+
# Parse the given README file, setting properties homepage,
|
26
|
+
# summary, and description on self if possible.
|
27
|
+
def parse_readme( file )
|
28
|
+
|
29
|
+
in_desc = false
|
30
|
+
desc = ""
|
31
|
+
|
32
|
+
readme_file_open( file ) do |fin|
|
33
|
+
fin.each do |line|
|
34
|
+
if homepage.nil? && line =~ /^\s*\*\s*(http\S+)\s*$/
|
35
|
+
self.homepage = $1
|
36
|
+
elsif line =~ /^=/ # section header
|
37
|
+
in_desc = ( line =~ /^=+\s*Description\s*$/i )
|
38
|
+
# Stop at new section if we already have a description
|
39
|
+
break unless desc.empty?
|
40
|
+
elsif in_desc
|
41
|
+
# Stop if empty line after description, or bullet (*) list
|
42
|
+
break if ( !desc.empty? && line =~ /^\s*$/ ) || line =~ /^\s*\*/
|
43
|
+
desc << line
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc = desc.
|
49
|
+
gsub( /\s+/, ' ' ). #Simplify whitespace
|
50
|
+
gsub( /\{([^\}]+)\}\[[^\]]*\]/, '\1' ). # Replace rdoc link \w anchor
|
51
|
+
gsub( /(\S)\[\s*http:[^\]]*\]/, '\1' ). # Replace bare rdoc links
|
52
|
+
strip.
|
53
|
+
sub( /:$/, '.' ) # Replace a final ':', like when term. at list
|
54
|
+
|
55
|
+
# Summary is first sentence if we find one, or entire desc otherwise
|
56
|
+
if desc =~ /^(.+[!?:.])\s/
|
57
|
+
self.summary = $1.strip
|
58
|
+
self.description = desc # Use remainder for description
|
59
|
+
else
|
60
|
+
self.summary = desc
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Open, and test hook
|
68
|
+
def readme_file_open( file, &block )
|
69
|
+
File.open( file, 'r', &block )
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|