rabal 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +13 -0
- data/README.PLUGIN +8 -8
- data/Rakefile +79 -0
- data/lib/rabal.rb +26 -28
- data/lib/rabal/application.rb +53 -15
- data/lib/rabal/directory_tree.rb +3 -3
- data/lib/rabal/error.rb +2 -0
- data/lib/rabal/file_tree.rb +9 -4
- data/lib/rabal/gemspec.rb +50 -0
- data/lib/rabal/logger.rb +15 -6
- data/lib/rabal/plugin.rb +9 -5
- data/lib/rabal/plugin/core.rb +4 -3
- data/lib/rabal/plugin/ext.rb +17 -0
- data/lib/rabal/plugin/foundation.rb +21 -10
- data/lib/rabal/plugin/license.rb +8 -13
- data/lib/rabal/plugin/rubyforge.rb +22 -0
- data/lib/rabal/plugin/spec.rb +6 -0
- data/lib/rabal/plugin/test.rb +7 -0
- data/lib/rabal/plugin_tree.rb +1 -2
- data/lib/rabal/project_tree.rb +7 -2
- data/lib/rabal/specification.rb +128 -0
- data/lib/rabal/tree.rb +19 -5
- data/lib/rabal/usage.rb +8 -5
- data/lib/rabal/util.rb +2 -2
- data/lib/rabal/version.rb +6 -3
- data/resources/trees/bin/rabal.project +3 -3
- data/resources/trees/core/{CHANGES → CHANGES.erb} +1 -1
- data/resources/trees/core/{README → README.erb} +1 -1
- data/resources/trees/core/Rakefile.erb +67 -0
- data/resources/trees/core/lib/rabal.project.rb.erb +0 -2
- data/resources/trees/core/lib/rabal.project/gemspec.rb.erb +51 -0
- data/resources/trees/core/lib/rabal.project/specification.rb.erb +128 -0
- data/resources/trees/core/lib/rabal.project/version.rb.erb +2 -1
- data/resources/trees/ext/ext/rabal.project/ext/mkrf_conf.rb.erb +7 -0
- data/resources/trees/ext/tasks/extension.rb.erb +15 -0
- data/resources/trees/rubyforge/tasks/rubyforge.rb.erb +105 -0
- data/resources/trees/spec/{rabal.project_spec.rb.erb → spec/rabal.project_spec.rb.erb} +0 -0
- data/resources/trees/spec/{spec_helper.rb.erb → spec/spec_helper.rb.erb} +0 -0
- data/resources/trees/spec/tasks/rspec.rb.erb +20 -0
- data/resources/trees/test/tasks/testunit.rb.erb +14 -0
- data/resources/trees/test/{rabal.project_test.rb.erb → test/rabal.project_test.rb.erb} +1 -1
- data/resources/trees/test/{test_helper.rb.erb → test/test_helper.rb.erb} +1 -1
- data/spec/application_spec.rb +47 -0
- data/spec/bin_plugin_spec.rb +1 -1
- data/spec/core_plugin_spec.rb +10 -3
- data/spec/license_plugin_spec.rb +10 -8
- data/spec/plugin_tree_spec.rb +4 -4
- data/spec/spec_plugin_spec.rb +1 -1
- data/spec/test_plugin_spec.rb +1 -1
- data/spec/version_spec.rb +1 -1
- metadata +73 -24
- data/resources/trees/core/Rakefile +0 -0
data/lib/rabal/usage.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require '
|
1
|
+
require 'rabal'
|
2
|
+
|
2
3
|
module Rabal
|
3
4
|
|
4
5
|
# Rabal has unique Usage output requirements and as such the default
|
@@ -24,11 +25,13 @@ module Rabal
|
|
24
25
|
# to dump, or rename
|
25
26
|
def to_s
|
26
27
|
u = ::Main::Usage.new
|
27
|
-
# just transfer directly over these
|
28
|
-
%w[name synopsis
|
28
|
+
# just transfer directly over these chunks
|
29
|
+
%w[name synopsis].each do |chunk|
|
29
30
|
u[chunk.dup] = old_usage[chunk].to_s
|
30
31
|
end
|
31
32
|
|
33
|
+
u['description'] = ::Main::Util.columnize(old_usage['description'].to_s, :indent => 6, :width => 78)
|
34
|
+
|
32
35
|
arguments = app.main.parameters.select{|p| p.type == :argument}
|
33
36
|
global_options = app.main.parameters.select{|p| p.type == :option and app.global_option_names.include?(p.name) }
|
34
37
|
load_options = app.main.parameters.select{|p| p.type == :option and app.plugin_load_option_names.include?(p.name) }
|
@@ -47,7 +50,7 @@ module Rabal
|
|
47
50
|
app.plugin_manager.plugins.sort_by{|c,p| c}.each do |cat,plugins|
|
48
51
|
plugins.each do |key,plugin|
|
49
52
|
pre = " " + (plugin.use_always? ? "*" : " ")
|
50
|
-
s << option_format(pre,"#{plugin.use_name} (#{plugin.register_path})",plugin.description,
|
53
|
+
s << option_format(pre,"#{plugin.use_name} (#{plugin.register_path})",plugin.description,40,43,78)
|
51
54
|
s << "\n"
|
52
55
|
|
53
56
|
# create the module options for this one, if the
|
@@ -92,7 +95,7 @@ module Rabal
|
|
92
95
|
end
|
93
96
|
list.sort_by{|p| p.name}.collect do |p|
|
94
97
|
ps = ""
|
95
|
-
ps << option_format(pre,p.short_synopsis,p.description,
|
98
|
+
ps << option_format(pre,p.short_synopsis,p.description,42,45,78)
|
96
99
|
ps
|
97
100
|
end.join("\n")
|
98
101
|
end
|
data/lib/rabal/util.rb
CHANGED
data/lib/rabal/version.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
begin
|
4
|
-
require '<%=
|
4
|
+
require '<%= project_name %>'
|
5
5
|
rescue LoadError => le
|
6
6
|
$: << File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
|
7
|
-
require '<%=
|
7
|
+
require '<%= project_name %>'
|
8
8
|
end
|
9
9
|
|
10
|
-
puts "I am the killer app <%=
|
10
|
+
puts "I am the killer app <%= project_name %> at version #{<%= project_name.camelize %>::VERSION}!"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
$: << File.join(File.dirname(__FILE__),"lib")
|
7
|
+
require '<%= project_name.underscore %>'
|
8
|
+
|
9
|
+
# load all the extra tasks for the project
|
10
|
+
TASK_DIR = File.join(File.dirname(__FILE__),"tasks")
|
11
|
+
FileList[File.join(TASK_DIR,"*.rb")].each do |tasklib|
|
12
|
+
require "tasks/#{File.basename(tasklib)}"
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => "test:default"
|
16
|
+
|
17
|
+
#-----------------------------------------------------------------------
|
18
|
+
# Documentation
|
19
|
+
#-----------------------------------------------------------------------
|
20
|
+
namespace :doc do
|
21
|
+
|
22
|
+
# generating documentation locally
|
23
|
+
Rake::RDocTask.new do |rdoc|
|
24
|
+
rdoc.rdoc_dir = <%= project_name.camelize %>::SPEC.local_rdoc_dir
|
25
|
+
rdoc.options = <%= project_name.camelize %>::SPEC.rdoc_options
|
26
|
+
rdoc.rdoc_files = <%= project_name.camelize %>::SPEC.rdoc_files
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "View the RDoc documentation locally"
|
30
|
+
task :view => :rdoc do
|
31
|
+
show_files <%= project_name.camelize %>::SPEC.local_rdoc_dir
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
#-----------------------------------------------------------------------
|
37
|
+
# Packaging and Distribution
|
38
|
+
#-----------------------------------------------------------------------
|
39
|
+
namespace :dist do
|
40
|
+
|
41
|
+
GEM_SPEC = eval(<%= project_name.camelize %>:::SPEC.to_ruby)
|
42
|
+
|
43
|
+
Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
|
44
|
+
pkg.need_tar = <%= project_name.camelize %>::SPEC.need_tar
|
45
|
+
pkg.need_zip = <%= project_name.camelize %>::SPEC.need_zip
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Install as a gem"
|
49
|
+
task :install => [:clobber, :package] do
|
50
|
+
sh "sudo gem install pkg/#{<%= project_name.camelize %>::SPEC.full_name}.gem"
|
51
|
+
end
|
52
|
+
|
53
|
+
# uninstall the gem and all executables
|
54
|
+
desc "Uninstall gem"
|
55
|
+
task :uninstall do
|
56
|
+
sh "sudo gem uninstall #{<%= project_name.camelize %>::SPEC.name} -x"
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "dump gemspec"
|
60
|
+
task :gemspec do
|
61
|
+
puts <%= project_name.camelize %>::SPEC.to_ruby
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "reinstall gem"
|
65
|
+
task :reinstall => [:install, :uninstall]
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require '<%= project_name %>/specification'
|
3
|
+
require '<%= project_name %>/version'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
# The Gem Specification plus some extras for <%= project_name %>.
|
7
|
+
module <%= project_name.camelize %>
|
8
|
+
SPEC = <%= project_name.camelize %>::Specification.new do |spec|
|
9
|
+
spec.name = "<%= project_name %>"
|
10
|
+
spec.version = <%= project_name.camelize %>::VERSION
|
11
|
+
spec.rubyforge_project = "<%= project_name %>"
|
12
|
+
spec.author = "<%= root.author %>"
|
13
|
+
spec.email = "<%= root.email %>"
|
14
|
+
spec.homepage = "http://<%= project_name %>.rubyforge.org/"
|
15
|
+
|
16
|
+
spec.summary = "A Summary of <%= project_name %>."
|
17
|
+
spec.description = <<-DESC
|
18
|
+
A longer more detailed description of <%= project_name %>.
|
19
|
+
DESC
|
20
|
+
|
21
|
+
spec.extra_rdoc_files = FileList["[A-Z]*"]
|
22
|
+
spec.has_rdoc = true
|
23
|
+
spec.rdoc_main = "README"
|
24
|
+
spec.rdoc_options = [ "--line-numbers" , "--inline-source" ]
|
25
|
+
|
26
|
+
spec.test_files = FileList["spec/**/*.rb", "test/**/*.rb"]
|
27
|
+
spec.files = spec.test_files + spec.extra_rdoc_files +
|
28
|
+
FileList["lib/**/*.rb", "resources/**/*"]
|
29
|
+
<% if root.has_subtree?(["bin"]) then %>
|
30
|
+
spec.executable = spec.name
|
31
|
+
<% end %>
|
32
|
+
|
33
|
+
# add dependencies
|
34
|
+
# spec.add_dependency("somegem", ">= 0.4.2")
|
35
|
+
<% if root.has_subtree?(["ext"]) then %>
|
36
|
+
spec.add_dependency("mkrf")
|
37
|
+
spec.extensions << "ext/<%= project_name %>/ext/mkrf_conf.rb"
|
38
|
+
<% end %>
|
39
|
+
spec.platform = Gem::Platform::RUBY
|
40
|
+
|
41
|
+
spec.local_rdoc_dir = "doc/rdoc"
|
42
|
+
spec.remote_rdoc_dir = "#{spec.name}/rdoc"
|
43
|
+
spec.local_coverage_dir = "doc/coverage"
|
44
|
+
spec.remote_coverage_dir= "#{spec.name}/coverage"
|
45
|
+
|
46
|
+
spec.remote_site_dir = "#{spec.name}/"
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/specification'
|
3
|
+
require 'rake'
|
4
|
+
|
5
|
+
module <%= project_name.camelize %>
|
6
|
+
# Add some additional items to Gem::Specification
|
7
|
+
# A <%= project_name.camelize %>::Specification adds additional pieces of information the
|
8
|
+
# typical gem specification
|
9
|
+
class Specification
|
10
|
+
|
11
|
+
RUBYFORGE_ROOT = "/var/www/gforge-projects/"
|
12
|
+
|
13
|
+
# user that accesses remote site
|
14
|
+
attr_accessor :remote_user
|
15
|
+
|
16
|
+
# remote host, default 'rubyforge.org'
|
17
|
+
attr_accessor :remote_host
|
18
|
+
|
19
|
+
# name the rdoc main
|
20
|
+
attr_accessor :rdoc_main
|
21
|
+
|
22
|
+
# local directory in development holding the generated rdoc
|
23
|
+
# default 'doc'
|
24
|
+
attr_accessor :local_rdoc_dir
|
25
|
+
|
26
|
+
# remote directory for storing rdoc, default 'doc'
|
27
|
+
attr_accessor :remote_rdoc_dir
|
28
|
+
|
29
|
+
# local directory for coverage report
|
30
|
+
attr_accessor :local_coverage_dir
|
31
|
+
|
32
|
+
# remote directory for storing coverage reports
|
33
|
+
# This defaults to 'coverage'
|
34
|
+
attr_accessor :remote_coverage_dir
|
35
|
+
|
36
|
+
# local directory for generated website, default +site/public+
|
37
|
+
attr_accessor :local_site_dir
|
38
|
+
|
39
|
+
# remote directory relative to +remote_root+ for the website.
|
40
|
+
# website.
|
41
|
+
attr_accessor :remote_site_dir
|
42
|
+
|
43
|
+
# is a .tgz to be created?, default 'true'
|
44
|
+
attr_accessor :need_tar
|
45
|
+
|
46
|
+
# is a .zip to be created, default 'true'
|
47
|
+
attr_accessor :need_zip
|
48
|
+
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
@remote_user = nil
|
52
|
+
@remote_host = "rubyforge.org"
|
53
|
+
|
54
|
+
@rdoc_main = "README"
|
55
|
+
@local_rdoc_dir = "doc"
|
56
|
+
@remote_rdoc_dir = "doc"
|
57
|
+
@local_coverage_dir = "coverage"
|
58
|
+
@remote_coverage_dir = "coverage"
|
59
|
+
@local_site_dir = "site/public"
|
60
|
+
@remote_site_dir = "."
|
61
|
+
|
62
|
+
@need_tar = true
|
63
|
+
@need_zip = true
|
64
|
+
|
65
|
+
@spec = Gem::Specification.new
|
66
|
+
|
67
|
+
yield self if block_given?
|
68
|
+
|
69
|
+
# update rdoc options to take care of the rdoc_main if it is
|
70
|
+
# there, and add a default title if one is not given
|
71
|
+
if not @spec.rdoc_options.include?("--main") then
|
72
|
+
@spec.rdoc_options.concat(["--main", rdoc_main])
|
73
|
+
end
|
74
|
+
|
75
|
+
if not @spec.rdoc_options.include?("--title") then
|
76
|
+
@spec.rdoc_options.concat(["--title","'#{name} -- #{summary}'"])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# if this gets set then it overwrites what would be the
|
81
|
+
# rubyforge default. If rubyforge project is not set then use
|
82
|
+
# name. If rubyforge project and name are set, but they are
|
83
|
+
# different then assume that name is a subproject of the
|
84
|
+
# rubyforge project
|
85
|
+
def remote_root
|
86
|
+
if rubyforge_project.nil? or
|
87
|
+
rubyforge_project == name then
|
88
|
+
return RUBYFORGE_ROOT + "#{name}/"
|
89
|
+
else
|
90
|
+
return RUBYFORGE_ROOT + "#{rubyforge_project}/#{name}/"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# rdoc files is the same as what would be generated during gem
|
95
|
+
# installation. That is, everything in the require paths plus
|
96
|
+
# the rdoc_extra_files
|
97
|
+
#
|
98
|
+
def rdoc_files
|
99
|
+
flist = extra_rdoc_files.dup
|
100
|
+
@spec.require_paths.each do |rp|
|
101
|
+
flist << FileList["#{rp}/**/*.rb"]
|
102
|
+
end
|
103
|
+
flist.flatten.uniq
|
104
|
+
end
|
105
|
+
|
106
|
+
# calculate the remote directories
|
107
|
+
def remote_root_location
|
108
|
+
"#{remote_user}@#{remote_host}:#{remote_root}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def remote_rdoc_location
|
112
|
+
remote_root_location + @remote_rdoc_dir
|
113
|
+
end
|
114
|
+
|
115
|
+
def remote_coverage_location
|
116
|
+
remote_root_loation + @remote_coverage_dir
|
117
|
+
end
|
118
|
+
|
119
|
+
def remote_site_location
|
120
|
+
remote_root_location + @remote_site_dir
|
121
|
+
end
|
122
|
+
|
123
|
+
# we delegate any other calls to spec
|
124
|
+
def method_missing(method_id,*params,&block)
|
125
|
+
@spec.send method_id, *params, &block
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
namespace :extension do
|
2
|
+
desc "Build the extension(s)"
|
3
|
+
task :build do
|
4
|
+
<%= project_name.camelize %>::SPEC.extensions.each do |extension|
|
5
|
+
path = Pathname.new(extension)
|
6
|
+
parts = path.split
|
7
|
+
conf = parts.last
|
8
|
+
Dir.chdir(path.dirname) do |d|
|
9
|
+
ruby conf.to_s
|
10
|
+
sh "rake default"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'rubyforge'
|
2
|
+
|
3
|
+
#-----------------------------------------------------------------------
|
4
|
+
# Documentation - pushing documentation to rubyforge
|
5
|
+
#-----------------------------------------------------------------------
|
6
|
+
namespace :doc do
|
7
|
+
desc "Deploy the RDoc documentation to rubyforge"
|
8
|
+
task :deploy => :rerdoc do
|
9
|
+
sh "rsync -zav --delete #{<%= project_name.camelize %>::SPEC.local_rdoc_dir}/ #{<%= project_name.camelize %>::SPEC.rubyforge_rdoc_dest}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
#-----------------------------------------------------------------------
|
14
|
+
# Packaging and Distribution - push to rubyforge
|
15
|
+
#-----------------------------------------------------------------------
|
16
|
+
namespace :dist do
|
17
|
+
desc "Release files to rubyforge"
|
18
|
+
task :release => [:clean, :package] do
|
19
|
+
|
20
|
+
rubyforge = RubyForge.new
|
21
|
+
|
22
|
+
# make sure this release doesn't already exist
|
23
|
+
releases = rubyforge.autoconfig['release_ids']
|
24
|
+
if releases.has_key?(<%= project_name.camelize %>::SPEC.name) and releases[<%= project_name.camelize %>::SPEC.name][<%= project_name.camelize %>::VERSION] then
|
25
|
+
abort("Release #{<%= project_name.camelize %>::VERSION} already exists! Unable to release.")
|
26
|
+
end
|
27
|
+
|
28
|
+
config = rubyforge.userconfig
|
29
|
+
config["release_notes"] = <%= project_name.camelize %>::SPEC.description
|
30
|
+
config["release_changes"] = last_changeset
|
31
|
+
config["Prefomatted"] = true
|
32
|
+
|
33
|
+
|
34
|
+
puts "Uploading to rubyforge..."
|
35
|
+
files = FileList[File.join("pkg","#{<%= project_name.camelize %>::SPEC.name}-#{<%= project_name.camelize %>::VERSION}.*")].to_a
|
36
|
+
rubyforge.login
|
37
|
+
rubyforge.add_release(<%= project_name.camelize %>::SPEC.rubyforge_project, <%= project_name.camelize %>::SPEC.name, <%= project_name.camelize %>::VERSION, *files)
|
38
|
+
puts "done."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#-----------------------------------------------------------------------
|
43
|
+
# Announcements - Create an email text file, and post news to rubyforge
|
44
|
+
#-----------------------------------------------------------------------
|
45
|
+
def changes
|
46
|
+
change_file = File.expand_path(File.join(File.basename(__FILE__),"..","CHANGES"))
|
47
|
+
sections = File.read(change_file).split(/^(?===)/)
|
48
|
+
end
|
49
|
+
def last_changeset
|
50
|
+
changes[1]
|
51
|
+
end
|
52
|
+
|
53
|
+
def announcement
|
54
|
+
urls = " #{<%= project_name.camelize %>::SPEC.homepage}"
|
55
|
+
subject = "#{<%= project_name.camelize %>::SPEC.name} #{<%= project_name.camelize %>::VERSION} Released"
|
56
|
+
title = "#{<%= project_name.camelize %>::SPEC.name} version #{<%= project_name.camelize %>::VERSION} has been released."
|
57
|
+
body = <<BODY
|
58
|
+
#{<%= project_name.camelize %>::SPEC.description.rstrip}
|
59
|
+
|
60
|
+
{{ Changelog for Version #{<%= project_name.camelize %>::VERSION} }}
|
61
|
+
|
62
|
+
#{last_changeset.rstrip}
|
63
|
+
|
64
|
+
BODY
|
65
|
+
|
66
|
+
return subject, title, body, urls
|
67
|
+
end
|
68
|
+
|
69
|
+
namespace :announce do
|
70
|
+
desc "create email for ruby-talk"
|
71
|
+
task :email do
|
72
|
+
subject, title, body, urls = announcement
|
73
|
+
|
74
|
+
File.open("email.txt", "w") do |mail|
|
75
|
+
mail.puts "From: #{<%= project_name.camelize %>::SPEC.author} <#{<%= project_name.camelize %>::SPEC.email}>"
|
76
|
+
mail.puts "To: ruby-talk@ruby-lang.org"
|
77
|
+
mail.puts "Date: #{Time.now.rfc2822}"
|
78
|
+
mail.puts "Subject: [ANN] #{subject}"
|
79
|
+
mail.puts
|
80
|
+
mail.puts title
|
81
|
+
mail.puts
|
82
|
+
mail.puts urls
|
83
|
+
mail.puts
|
84
|
+
mail.puts body
|
85
|
+
mail.puts
|
86
|
+
mail.puts urls
|
87
|
+
end
|
88
|
+
puts "Created the following as email.txt:"
|
89
|
+
puts "-" * 72
|
90
|
+
puts File.read("email.txt")
|
91
|
+
puts "-" * 72
|
92
|
+
end
|
93
|
+
|
94
|
+
CLOBBER << "email.txt"
|
95
|
+
|
96
|
+
desc "Post news of #{<%= project_name.camelize %>::SPEC.name} to #{<%= project_name.camelize %>::SPEC.rubyforge_project} on rubyforge"
|
97
|
+
task :post_news do
|
98
|
+
subject, title, body, urls = announcement
|
99
|
+
rubyforge = RubyForge.new
|
100
|
+
rubyforge.login
|
101
|
+
rubyforge.post_news(<%= project_name.camelize %>::SPEC.rubyforge_project, subject, "#{title}\n\n#{urls}\n\n#{body}")
|
102
|
+
puts "Posted to rubyforge"
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|