sprout 0.7.219-i686-darwin10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprout might be problematic. Click here for more details.
- data/MIT-LICENSE +20 -0
- data/TODO +12 -0
- data/bin/sprout +128 -0
- data/doc/Bundle +14 -0
- data/doc/Generator +35 -0
- data/doc/Library +63 -0
- data/doc/Task +21 -0
- data/doc/Tool +20 -0
- data/lib/platform.rb +109 -0
- data/lib/progress_bar.rb +354 -0
- data/lib/sprout.rb +496 -0
- data/lib/sprout/archive_unpacker.rb +225 -0
- data/lib/sprout/builder.rb +44 -0
- data/lib/sprout/commands/generate.rb +9 -0
- data/lib/sprout/dynamic_accessors.rb +34 -0
- data/lib/sprout/general_tasks.rb +6 -0
- data/lib/sprout/generator.rb +7 -0
- data/lib/sprout/generator/base_mixins.rb +186 -0
- data/lib/sprout/generator/named_base.rb +227 -0
- data/lib/sprout/log.rb +46 -0
- data/lib/sprout/process_runner.rb +111 -0
- data/lib/sprout/project_model.rb +278 -0
- data/lib/sprout/remote_file_loader.rb +71 -0
- data/lib/sprout/remote_file_target.rb +151 -0
- data/lib/sprout/simple_resolver.rb +88 -0
- data/lib/sprout/tasks/erb_resolver.rb +118 -0
- data/lib/sprout/tasks/gem_wrap_task.rb +200 -0
- data/lib/sprout/tasks/git_task.rb +134 -0
- data/lib/sprout/tasks/library_task.rb +118 -0
- data/lib/sprout/tasks/sftp_task.rb +248 -0
- data/lib/sprout/tasks/ssh_task.rb +153 -0
- data/lib/sprout/tasks/tool_task.rb +793 -0
- data/lib/sprout/tasks/zip_task.rb +158 -0
- data/lib/sprout/template_resolver.rb +207 -0
- data/lib/sprout/user.rb +383 -0
- data/lib/sprout/version.rb +12 -0
- data/lib/sprout/version_file.rb +89 -0
- data/lib/sprout/zip_util.rb +61 -0
- data/rakefile.rb +150 -0
- data/samples/gem_wrap/rakefile.rb +17 -0
- metadata +174 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
|
2
|
+
module Sprout
|
3
|
+
class ArchiveUnpackerError < StandardError #:nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
# Unpack downloaded files from a variety of common archive types.
|
7
|
+
# This library should efficiently extract archived files
|
8
|
+
# on OS X, Win XP, Vista, DOS, Cygwin, and Linux.
|
9
|
+
#
|
10
|
+
# It will attempt to infer the archive type by standard mime-type file
|
11
|
+
# extensions, but if there is a file with no extension, the unpack_archive
|
12
|
+
# method can be provided with an @archive_type symbol argument that is one
|
13
|
+
# of the following values:
|
14
|
+
# :exe
|
15
|
+
# :zip
|
16
|
+
# :targz
|
17
|
+
# :gzip
|
18
|
+
# :swc
|
19
|
+
# :rb
|
20
|
+
# :dmg
|
21
|
+
class ArchiveUnpacker #:nodoc:
|
22
|
+
include Archive::Tar
|
23
|
+
|
24
|
+
def unpack_archive(file_name, dir, force=false, archive_type=nil)
|
25
|
+
archive_type ||= inferred_archive_type(file_name)
|
26
|
+
suffix = suffix_for_archive_type(archive_type)
|
27
|
+
|
28
|
+
unpacked = unpacked_file_name(file_name, dir, suffix)
|
29
|
+
if(File.exists?(unpacked) && force)
|
30
|
+
FileUtils.rm_rf(unpacked)
|
31
|
+
end
|
32
|
+
|
33
|
+
if(!File.exists?(unpacked))
|
34
|
+
case archive_type.to_s
|
35
|
+
when 'zip'
|
36
|
+
unpack_zip(file_name, dir)
|
37
|
+
when 'targz'
|
38
|
+
unpack_targz(file_name, dir)
|
39
|
+
when 'dmg'
|
40
|
+
unpack_dmg(file_name, dir)
|
41
|
+
when 'exe'
|
42
|
+
FileUtils.mkdir_p(dir)
|
43
|
+
File.mv(file_name, dir)
|
44
|
+
when 'swc' || 'rb'
|
45
|
+
return
|
46
|
+
else
|
47
|
+
raise ArchiveUnpackerError.new("ArchiveUnpacker does not know how to unpack files of type: #{archive_type} for file_name: #{file_name}")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def unpack_zip(zip_file, dir)
|
53
|
+
# Avoid the rubyzip Segmentation Fault bug
|
54
|
+
# at least on os x...
|
55
|
+
if(RUBY_PLATFORM =~ /darwin/)
|
56
|
+
# Unzipping on OS X
|
57
|
+
FileUtils.makedirs(dir)
|
58
|
+
zip_dir = File.expand_path(File.dirname(zip_file))
|
59
|
+
zip_name = File.basename(zip_file)
|
60
|
+
output = File.expand_path(dir)
|
61
|
+
# puts ">> zip_dir: #{zip_dir} zip_name: #{zip_name} output: #{output}"
|
62
|
+
%x(cd #{zip_dir};unzip #{zip_name} -d #{output})
|
63
|
+
else
|
64
|
+
retries = 0
|
65
|
+
begin
|
66
|
+
retries += 1
|
67
|
+
Zip::ZipFile::open(zip_file) do |zf|
|
68
|
+
zf.each do |e|
|
69
|
+
fpath = File.join(dir, e.name)
|
70
|
+
FileUtils.mkdir_p(File.dirname(fpath))
|
71
|
+
# Disgusting, Gross Hack to fix DOS/Ruby Bug
|
72
|
+
# That makes the zip library throw a ZipDestinationFileExistsError
|
73
|
+
# When the zip archive includes two files whose names
|
74
|
+
# differ only by extension.
|
75
|
+
# This bug actually appears in the File.exists? implementation
|
76
|
+
# throwing false positives!
|
77
|
+
# If you're going to use this code, be sure you extract
|
78
|
+
# into a new, empty directory as existing files will be
|
79
|
+
# clobbered...
|
80
|
+
begin
|
81
|
+
if(File.exists?(fpath) && !File.directory?(fpath))
|
82
|
+
hackpath = fpath + 'hack'
|
83
|
+
zf.extract(e, hackpath)
|
84
|
+
File.copy(hackpath, fpath)
|
85
|
+
File.delete(hackpath)
|
86
|
+
else
|
87
|
+
zf.extract(e, fpath)
|
88
|
+
end
|
89
|
+
rescue NotImplementedError => ni_err
|
90
|
+
puts "[WARNING] #{ni_err} for: #{e}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
rescue StandardError => err
|
95
|
+
FileUtils.rm_rf(dir)
|
96
|
+
if(retries < 3)
|
97
|
+
FileUtils.makedirs(dir)
|
98
|
+
retry
|
99
|
+
end
|
100
|
+
raise err
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def unpacked_file_name(file, dir, suffix=nil)
|
106
|
+
basename = File.basename(file, suffix)
|
107
|
+
path = File.expand_path(dir)
|
108
|
+
return File.join(path, basename)
|
109
|
+
end
|
110
|
+
|
111
|
+
def unpack_targz(tgz_file, dir)
|
112
|
+
if(!File.exists?(dir))
|
113
|
+
FileUtils.makedirs(dir)
|
114
|
+
end
|
115
|
+
tar = Zlib::GzipReader.new(File.open(tgz_file, 'rb'))
|
116
|
+
Minitar.unpack(tar, dir)
|
117
|
+
|
118
|
+
# Recurse and unpack gzipped children (Adobe did this double
|
119
|
+
# gzip with the Linux FlashPlayer for some reason)
|
120
|
+
Dir.glob("#{dir}/**/*.tar.gz").each do |child|
|
121
|
+
if(child != tgz_file)
|
122
|
+
unpack_targz(child, File.dirname(child))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
# This is actually not unpacking the FlashPlayer
|
129
|
+
# Binary file as expected...
|
130
|
+
# OSX is treated the player binary as if it is
|
131
|
+
# a regular text file, but if it is copied manually,
|
132
|
+
# the command works fine!?
|
133
|
+
def unpack_dmg(dmg_file, dir)
|
134
|
+
# 1) Mount the dmg in place
|
135
|
+
# 2) Recursively Copy its contents to asproject_home
|
136
|
+
# 3) Unmount the dmg
|
137
|
+
if(mounted_path.nil?)
|
138
|
+
raise StandardError.new('DMG file downloaded, but the RemoteFileTask needs a mounted_path in order to mount it')
|
139
|
+
end
|
140
|
+
|
141
|
+
if(!File.exists?(full_mounted_path))
|
142
|
+
system("hdiutil mount #{dmg_file}")
|
143
|
+
end
|
144
|
+
|
145
|
+
begin
|
146
|
+
mounted_target = File.join(full_mounted_path, extracted_file)
|
147
|
+
|
148
|
+
# Copy the DMG contents using system copy rather than ruby utils
|
149
|
+
# Because OS X does something special with .app files that the
|
150
|
+
# Ruby FileUtils and File classes break...
|
151
|
+
from = mounted_target
|
152
|
+
# from = File.join(full_mounted_path, extracted_file)
|
153
|
+
to = File.join(@user.downloads, @name.to_s, extracted_file)
|
154
|
+
FileUtils.makedirs(File.dirname(to))
|
155
|
+
|
156
|
+
if(File.exists?(from))
|
157
|
+
`ditto '#{from}' '#{to}'`
|
158
|
+
end
|
159
|
+
rescue StandardError => e
|
160
|
+
if(File.exists?(full_mounted_path))
|
161
|
+
system("hdiutil unmount -force \"#{full_mounted_path}\"")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def suffix_for_archive_type(type)
|
167
|
+
if(type == :targz)
|
168
|
+
return '.tar.gz'
|
169
|
+
else
|
170
|
+
return ".#{type.to_s}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def inferred_archive_type(file_name)
|
175
|
+
if is_zip?(file_name)
|
176
|
+
return :zip
|
177
|
+
elsif is_targz?(file_name)
|
178
|
+
return :targz
|
179
|
+
elsif is_gzip?(file_name)
|
180
|
+
return :gz
|
181
|
+
elsif is_swc?(file_name)
|
182
|
+
return :swc
|
183
|
+
elsif is_rb?(file_name)
|
184
|
+
return :rb
|
185
|
+
elsif is_dmg?(file_name)
|
186
|
+
return :dmg
|
187
|
+
elsif is_exe?(file_name)
|
188
|
+
return :exe
|
189
|
+
else
|
190
|
+
return nil
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
def is_zip?(file)
|
196
|
+
return (file.split('.').pop == 'zip')
|
197
|
+
end
|
198
|
+
|
199
|
+
def is_targz?(file)
|
200
|
+
parts = file.split('.')
|
201
|
+
part = parts.pop
|
202
|
+
return (part == 'tgz' || part == 'gz' && parts.pop == 'tar')
|
203
|
+
end
|
204
|
+
|
205
|
+
def is_gzip?(file)
|
206
|
+
return (file.split('.').pop == 'gz')
|
207
|
+
end
|
208
|
+
|
209
|
+
def is_swc?(file)
|
210
|
+
return (file.split('.').pop == 'swc')
|
211
|
+
end
|
212
|
+
|
213
|
+
def is_rb?(file)
|
214
|
+
return (file.split('.').pop == 'rb')
|
215
|
+
end
|
216
|
+
|
217
|
+
def is_dmg?(file)
|
218
|
+
return (file.split('.').pop == 'dmg')
|
219
|
+
end
|
220
|
+
|
221
|
+
def is_exe?(file)
|
222
|
+
return (file.split('.').pop == 'exe')
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
module Sprout
|
3
|
+
|
4
|
+
class BuilderError < StandardError #:nodoc:
|
5
|
+
end
|
6
|
+
|
7
|
+
# accepts a destination path and a sprout specification
|
8
|
+
# and will download and unpack the platform-specific
|
9
|
+
# archives that are identified in the spec
|
10
|
+
class Builder # :nodoc:
|
11
|
+
|
12
|
+
def self.build(file_targets_yaml, destination)
|
13
|
+
data = nil
|
14
|
+
|
15
|
+
File.open(file_targets_yaml, 'r') do |f|
|
16
|
+
data = f.read
|
17
|
+
end
|
18
|
+
|
19
|
+
targets = YAML.load(data)
|
20
|
+
targets.each do |target|
|
21
|
+
# iterate over the provided RemoteFileTargets until we
|
22
|
+
# encounter one that is appropriate for our platform,
|
23
|
+
# or one that claims to be universal.
|
24
|
+
# When authoring a sprout.spec for libraries or tools,
|
25
|
+
# put the most specific RemoteFileTargets first, then
|
26
|
+
# universals to catch unexpected platforms.
|
27
|
+
if(target.platform == platform || target.platform == 'universal')
|
28
|
+
target.install_path = FileUtils.mkdir_p(destination)
|
29
|
+
target.resolve
|
30
|
+
return target
|
31
|
+
end
|
32
|
+
end
|
33
|
+
raise BuilderError.new("Sprout::Builder.build failed, unsupported platform or unexpected yaml")
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def self.platform
|
39
|
+
@@platform ||= User.new.platform.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
# DynamicAccessors provides support for
|
3
|
+
# simply setting and getting values of any kind from
|
4
|
+
# any object that includes it.
|
5
|
+
#
|
6
|
+
# require 'dynamic_accessors'
|
7
|
+
#
|
8
|
+
# class Foo
|
9
|
+
# include DynamicAccessors
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# foo = Foo.new
|
13
|
+
# foo.bar = "Hello World"
|
14
|
+
# foo.friends = ['a', 'b', 'c']
|
15
|
+
# puts "foo.bar: #{foo.bar}"
|
16
|
+
# puts "foo.friends: #{foo.friends.join(', ')}"
|
17
|
+
|
18
|
+
module DynamicAccessors
|
19
|
+
|
20
|
+
def initialize(*params)
|
21
|
+
super
|
22
|
+
@missing_params_hash = Hash.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(method, *params, &block)
|
26
|
+
if(method.to_s.match(/=$/))
|
27
|
+
method = method.to_s.gsub('=', '').to_sym
|
28
|
+
return @missing_params_hash[method] = params.shift
|
29
|
+
else
|
30
|
+
return @missing_params_hash[method] if @missing_params_hash.keys.collect(&:to_sym).include?(method)
|
31
|
+
end
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
|
2
|
+
class Gem::SourceIndex
|
3
|
+
|
4
|
+
# This feature seems to be getting deprecated from the latest
|
5
|
+
# gems releases (1.3.x).
|
6
|
+
# We actually need it and don't want the nagging - so,
|
7
|
+
# copied sources from RubyGems SVN trunk, and mixing in...
|
8
|
+
# :-(
|
9
|
+
# There should be a more stable integration point with RubyGems
|
10
|
+
# Any ideas or contributions are welcome!
|
11
|
+
def sprout_search(gem_pattern, platform_only = false)
|
12
|
+
version_requirement = nil
|
13
|
+
only_platform = false
|
14
|
+
|
15
|
+
# unless Gem::Dependency === gem_pattern
|
16
|
+
# warn "#{Gem.location_of_caller.join ':'}:Warning: Gem::SourceIndex#search support for #{gem_pattern.class} patterns is deprecated"
|
17
|
+
# end
|
18
|
+
|
19
|
+
case gem_pattern
|
20
|
+
when Regexp then
|
21
|
+
version_requirement = platform_only || Gem::Requirement.default
|
22
|
+
when Gem::Dependency then
|
23
|
+
only_platform = platform_only
|
24
|
+
version_requirement = gem_pattern.version_requirements
|
25
|
+
gem_pattern = if Regexp === gem_pattern.name then
|
26
|
+
gem_pattern.name
|
27
|
+
elsif gem_pattern.name.empty? then
|
28
|
+
//
|
29
|
+
else
|
30
|
+
/^#{Regexp.escape gem_pattern.name}$/
|
31
|
+
end
|
32
|
+
else
|
33
|
+
version_requirement = platform_only || Gem::Requirement.default
|
34
|
+
gem_pattern = /#{gem_pattern}/i
|
35
|
+
end
|
36
|
+
|
37
|
+
unless Gem::Requirement === version_requirement then
|
38
|
+
version_requirement = Gem::Requirement.create version_requirement
|
39
|
+
end
|
40
|
+
|
41
|
+
specs = @gems.values.select do |spec|
|
42
|
+
spec.name =~ gem_pattern and
|
43
|
+
version_requirement.satisfied_by? spec.version
|
44
|
+
end
|
45
|
+
|
46
|
+
if only_platform then
|
47
|
+
specs = specs.select do |spec|
|
48
|
+
Gem::Platform.match spec.platform
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
begin
|
53
|
+
specs.sort_by { |s| s.sort_obj }
|
54
|
+
rescue NoMethodError => e
|
55
|
+
puts "It looks like your RubyGems installation is not compatible with this version of Sprouts.\n\nTo update, run:\ngem update --system\n\n"
|
56
|
+
raise e
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module RubiGen # :nodoc:[all]
|
62
|
+
|
63
|
+
class Base # :nodoc:[all]
|
64
|
+
|
65
|
+
def initialize(runtime_args, runtime_options = {})
|
66
|
+
@args = runtime_args
|
67
|
+
parse!(@args, runtime_options)
|
68
|
+
|
69
|
+
# Derive source and destination paths.
|
70
|
+
@source_root = options[:source] || File.join(spec.path, 'templates')
|
71
|
+
|
72
|
+
if options[:destination]
|
73
|
+
@destination_root = options[:destination]
|
74
|
+
elsif defined? Sprout::Sprout.project_path
|
75
|
+
@destination_root = Sprout::Sprout.project_path
|
76
|
+
else
|
77
|
+
@destination_root = Dir.pwd
|
78
|
+
end
|
79
|
+
|
80
|
+
# Silence the logger if requested.
|
81
|
+
logger.quiet = options[:quiet]
|
82
|
+
|
83
|
+
# Raise usage error if help is requested.
|
84
|
+
usage if options[:help]
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
# GemGeneratorSource hits the mines to quarry for generators. The latest versions
|
90
|
+
# of gems named sprout-#{sprout_name}-bundle are selected.
|
91
|
+
class GemGeneratorSource < AbstractGemSource # :nodoc:[all]
|
92
|
+
|
93
|
+
def initialize(name=nil)
|
94
|
+
super()
|
95
|
+
@sprout_name = name
|
96
|
+
end
|
97
|
+
|
98
|
+
# Yield latest versions of generator gems.
|
99
|
+
def each
|
100
|
+
Gem::cache.sprout_search(/sprout-*#{@sprout_name}-bundle$/).inject({}) do |latest, gem|
|
101
|
+
hem = latest[gem.name]
|
102
|
+
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
|
103
|
+
latest
|
104
|
+
end.values.each do |gem|
|
105
|
+
yield Spec.new(gem.name.sub(/sprout-*#{@sprout_name}-bundle$/, ''), gem.full_gem_path, label)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def each_sprout
|
110
|
+
Gem::cache.sprout_search(/^sprout-.*/).inject({}) do |latest, gem|
|
111
|
+
hem = latest[gem.name]
|
112
|
+
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
|
113
|
+
latest
|
114
|
+
end.values.each do |gem|
|
115
|
+
yield Spec.new(gem.name, gem.full_gem_path, label)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# GemPathSource looks for generators within any RubyGem's
|
121
|
+
# /sprout/generators/<generator_name>/<generator_name>_generator.rb file.
|
122
|
+
# It will only include generators from sprouts whose name includes
|
123
|
+
# #{sprout_name}-bundle
|
124
|
+
class GemPathSource < AbstractGemSource # :nodoc:[all]
|
125
|
+
|
126
|
+
def initialize(name=nil)
|
127
|
+
super()
|
128
|
+
@sprout_name = name
|
129
|
+
end
|
130
|
+
|
131
|
+
# Yield each generator within generator subdirectories.
|
132
|
+
def each
|
133
|
+
generator_full_paths.each do |generator|
|
134
|
+
yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
def generator_full_paths
|
140
|
+
@generator_full_paths ||=
|
141
|
+
Gem::cache.inject({}) do |latest, name_gem|
|
142
|
+
name, gem = name_gem
|
143
|
+
hem = latest[gem.name]
|
144
|
+
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
|
145
|
+
latest
|
146
|
+
end.values.inject([]) do |mem, gem|
|
147
|
+
Dir[gem.full_gem_path + '/lib/sprout/**/generators/**/*_generator.rb'].each do |generator|
|
148
|
+
if(@sprout_name && gem.name.match(/sprout-#{@sprout_name}-bundle/))
|
149
|
+
mem << generator
|
150
|
+
end
|
151
|
+
end
|
152
|
+
mem
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module Lookup # :nodoc:[all]
|
158
|
+
module ClassMethods # :nodoc:[all]
|
159
|
+
|
160
|
+
def use_sprout_sources!(sprout_name, project_path=nil)
|
161
|
+
reset_sources
|
162
|
+
|
163
|
+
# Project-specific generator paths
|
164
|
+
if project_path
|
165
|
+
sources << PathSource.new(:project, "#{project_path}/generators")
|
166
|
+
sources << PathSource.new(:script, "#{project_path}/script/generators")
|
167
|
+
sources << PathSource.new(:vendor, "#{project_path}/vendor/generators")
|
168
|
+
end
|
169
|
+
|
170
|
+
# System-wide generator paths
|
171
|
+
system_path = "#{Sprout::Sprout.sprout_cache}/generators/#{sprout_name}"
|
172
|
+
if(File.exists?(system_path))
|
173
|
+
sources << PathSource.new(:system, system_path)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Gem generators will collect all
|
177
|
+
# rubygems that end with -bundle or -generators
|
178
|
+
if(Object.const_defined?(:Gem))
|
179
|
+
sources << GemGeneratorSource.new(sprout_name)
|
180
|
+
sources << GemPathSource.new(sprout_name)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|