lyber-core 0.9.6.2

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.
@@ -0,0 +1,14 @@
1
+ require 'dlss_service'
2
+ require 'dor/suri_service'
3
+ require 'dor/workflow_service'
4
+ require 'dor/base'
5
+ require 'lyber_core/connection'
6
+ require 'lyber_core/destroyer'
7
+ require 'lyber_core/log'
8
+ require 'lyber_core/robots/robot'
9
+ require 'lyber_core/robots/workflow'
10
+ require 'lyber_core/robots/workspace'
11
+ require 'lyber_core/robots/work_queue'
12
+ require 'lyber_core/robots/work_item'
13
+ require 'lyber_core/exceptions/empty_queue'
14
+
@@ -0,0 +1,97 @@
1
+ require 'net/https'
2
+ require 'uri'
3
+ require 'cgi'
4
+
5
+ module LyberCore
6
+ class Connection
7
+ def Connection.get_https_connection(url)
8
+ LyberCore::Log.debug("Establishing connection to #{url.host} on port #{url.port}")
9
+ https = Net::HTTP.new(url.host, url.port)
10
+ if(url.scheme == 'https')
11
+ https.use_ssl = true
12
+ LyberCore::Log.debug("Using SSL")
13
+ https.cert = OpenSSL::X509::Certificate.new( File.read(LyberCore::CERT_FILE) )
14
+ LyberCore::Log.debug("Using cert file #{LyberCore::CERT_FILE}")
15
+ https.key = OpenSSL::PKey::RSA.new( File.read(LyberCore::KEY_FILE), LyberCore::KEY_PASS )
16
+ LyberCore::Log.debug("Using key file #{LyberCore::KEY_FILE} with pass #{LyberCore::KEY_PASS}")
17
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE
18
+ LyberCore::Log.debug("https.verify_mode = #{https.verify_mode} (should eql #{OpenSSL::SSL::VERIFY_NONE})")
19
+ end
20
+ https
21
+ end
22
+
23
+ # Returns body of the HTTP response, or passes the response to the block if it's passed in
24
+ #
25
+ # == Required Parameters
26
+ # - <b>full_url</b> - A string containing the full url to the resource you're trying to connect to
27
+ # - <b>method</b> - Recognizes the following symbols which correspond to an HTTP verb. The convenience methods take care of this
28
+ # :get for HTTP GET
29
+ # :post for HTTP POST
30
+ # :put for HTTP PUT
31
+ # - <b>body</b> The body of your request. Can be nil if you don't have one.
32
+ #
33
+ # == Options
34
+ # - <b>:auth_user</b> for basic HTTP authentication. :auth_user and :auth_password must both be set if using authentication
35
+ # - <b>:auth_password</b> for basic HTTP authentication. :auth_user and :auth_password must both be set if using authentication
36
+ # - <b>:content_type</b> if not passed in as an option, then it is set to 'application/xml'
37
+ #
38
+ # == Block
39
+ # By default, this method returns the body of the response, Net::HTTPResponse.body . If you want to work with the Net::HTTPResponse
40
+ # object, you can pass in a block, and the response will be passed to it.
41
+ #
42
+ # == Exceptions
43
+ # Any exceptions thrown while trying to connect should be handled by the caller
44
+ def Connection.connect(full_url, method, body, options = {}, &block)
45
+ url = URI.parse(full_url)
46
+ case method
47
+ when :get
48
+ req = Net::HTTP::Get.new(url.request_uri)
49
+ when :post
50
+ req = Net::HTTP::Post.new(url.request_uri)
51
+ when :put
52
+ req = Net::HTTP::Put.new(url.request_uri)
53
+ end
54
+ req.body = body unless(body.nil?)
55
+ if(options.include?(:content_type))
56
+ req.content_type = options[:content_type]
57
+ else
58
+ req.content_type = 'application/xml'
59
+ end
60
+
61
+ if(options.include?(:auth_user))
62
+ req.basic_auth options[:auth_user], options[:auth_password]
63
+ end
64
+
65
+ res = Connection.get_https_connection(url).start {|http| http.request(req) }
66
+ case res
67
+ when Net::HTTPSuccess
68
+ if(block_given?)
69
+ block.call(res)
70
+ else
71
+ return res.body
72
+ end
73
+ else
74
+ raise res.error!
75
+ end
76
+
77
+ end
78
+ end
79
+
80
+
81
+ # Convenience method for performing an HTTP GET using Connection.connect
82
+ def Connection.get(full_url, options = {}, &b)
83
+ Connection.connect(full_url, :get, nil, options, &b)
84
+ end
85
+
86
+ # Convenience method for performing an HTTP POST using Connection.connect
87
+ def Connection.post(full_url, body, options = {}, &b)
88
+ Connection.connect(full_url, :post, body, options, &b)
89
+ end
90
+
91
+ # Convenience method for performing an HTTP PUT using Connection.connect
92
+ def Connection.put(full_url, body, options = {}, &b)
93
+ Connection.connect(full_url, :put, body, options, &b)
94
+ end
95
+
96
+
97
+ end
@@ -0,0 +1,74 @@
1
+ module LyberCore
2
+
3
+ class LyberCore::Destroyer
4
+
5
+ require 'rubygems'
6
+ require 'active-fedora'
7
+ require 'open-uri'
8
+
9
+ attr_reader :repository
10
+ attr_reader :workflow
11
+ attr_reader :registration_robot
12
+ attr_reader :druid_list
13
+ attr_reader :current_druid
14
+
15
+ # Given a repository, a workflow and the name of a robot that registers objects in fedora
16
+ # we can generate a list of all the druids that were created by that robot
17
+ def initialize(repository, workflow, registration_robot)
18
+ @repository = repository
19
+ @workflow = workflow
20
+ @registration_robot = registration_robot
21
+ @druid_list = self.get_druid_list
22
+ end
23
+
24
+ def get_druid_list
25
+ begin
26
+ druid_list = []
27
+ url_string = "#{WORKFLOW_URI}/workflow_queue?repository=#{@repository}&workflow=#{@workflow}&completed=#{@registration_robot}"
28
+ LyberCore::Log.info("Fetching druids from #{url_string}")
29
+ doc = Nokogiri::XML(open(url_string))
30
+ doc.xpath("//objects/object/@id").each do |id|
31
+ druid_list << id.to_s
32
+ end
33
+ return druid_list
34
+ rescue Exception => e
35
+ raise e, "Couldn't fetch druid list from #{url_string}"
36
+ end
37
+ end
38
+
39
+ # Iterate through the druids in @druid_list and delete each of them from FEDORA
40
+ def delete_druids
41
+ begin
42
+ connect_to_fedora
43
+ @druid_list.each do |druid|
44
+ @current_druid = druid
45
+ LyberCore::Log.info("Deleting #{@current_druid}")
46
+ begin
47
+ obj = ActiveFedora::Base.load_instance(@current_druid)
48
+ obj.delete
49
+ rescue ActiveFedora::ObjectNotFoundError
50
+ LyberCore::Log.info("#{@current_druid} does not exist in this repository")
51
+ end
52
+ end
53
+ rescue Exception => e
54
+ raise e
55
+ end
56
+ end
57
+
58
+ def connect_to_fedora
59
+ begin
60
+ if @repository == "dor"
61
+ repo_url = FEDORA_URI
62
+ elsif @repository == "sdr"
63
+ repo_url = SEDORA_URI
64
+ else
65
+ raise "I don't know how to connect to repository #{@repository}"
66
+ end
67
+ LyberCore::Log.info("connecting to #{repo_url}")
68
+ Fedora::Repository.register(repo_url)
69
+ rescue Errno::ECONNREFUSED => e
70
+ raise e, "Can't connect to Fedora at url #{repo_url}"
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,9 @@
1
+ require 'dor_service'
2
+
3
+ module LyberCore
4
+ module Exceptions
5
+ class EmptyQueue < RuntimeError
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,105 @@
1
+
2
+ module LyberCore
3
+
4
+ class LyberCore::Log
5
+ require 'logger'
6
+
7
+ # Default values
8
+ DEFAULT_LOGFILE = "/tmp/lybercore_log.log"
9
+ DEFAULT_LOG_LEVEL = Logger::INFO
10
+ DEFAULT_FORMATTER = proc{|s,t,p,m|"%5s [%s] (%s) %s :: %s\n" % [s,
11
+ t.strftime("%Y-%m-%d %H:%M:%S"), $$, p, m]}
12
+
13
+ # Initial state
14
+ @@logfile = DEFAULT_LOGFILE
15
+ @@log ||= Logger.new(@@logfile)
16
+ # $stdout.reopen(@@logfile)
17
+ # $stderr.reopen(@@logfile)
18
+ @@log.level = DEFAULT_LOG_LEVEL
19
+ @@log.formatter = DEFAULT_FORMATTER
20
+
21
+ # Restore LyberCore::Log to its default state
22
+ def Log.restore_defaults
23
+ @@log.level = DEFAULT_LOG_LEVEL
24
+ Log.set_logfile(DEFAULT_LOGFILE)
25
+ @@log.formatter = DEFAULT_FORMATTER
26
+ end
27
+
28
+ # The current location of the logfile
29
+ def Log.logfile
30
+ return @@logfile
31
+ end
32
+
33
+
34
+
35
+ # Accepts a filename as an argument, and checks to see whether that file can be
36
+ # opened for writing. If it can be opened, it closes the existing Logger object
37
+ # and re-opens it with the new logfile location. It raises an exception if it
38
+ # cannot write to the specified logfile.
39
+ def Log.set_logfile(new_logfile)
40
+ begin
41
+ current_log_level = @@log.level
42
+ current_formatter = @@log.formatter
43
+ @@log = Logger.new(new_logfile)
44
+ @@logfile = new_logfile
45
+ @@log.level = current_log_level
46
+ @@log.formatter = current_formatter
47
+ rescue Exception => e
48
+ raise e, "Couldn't initialize logfile #{new_logfile}: #{e.backtrace}"
49
+ end
50
+
51
+ end
52
+
53
+ # Set the log level.
54
+ # See http://ruby-doc.org/core/classes/Logger.html for more info.
55
+ # Possible values are:
56
+ # Logger::FATAL (4): an unhandleable error that results in a program crash
57
+ # Logger::ERROR (3): a handleable error condition
58
+ # Logger::WARN (2): a warning
59
+ # Logger::INFO (1): generic (useful) information about system operation
60
+ # Logger::DEBUG (0): low-level information for developers
61
+ def Log.set_level(loglevel)
62
+ begin
63
+ if [0,1,2,3,4].contains? loglevel
64
+ @@log.level = loglevel
65
+ @@log.debug "Setting LyberCore::Log.level to #{loglevel}"
66
+ else
67
+ @@log.warn "I received an invalid option for log level. I expected a number between 0 and 4 but I got #{loglevel}"
68
+ @@log.warn "I'm setting the loglevel to 0 (debug) because you seem to be having trouble."
69
+ @@log.level = 0
70
+ end
71
+ rescue Exception => e
72
+ raise e, "Couldn't set log level: #{e.backtrace}"
73
+ end
74
+ end
75
+
76
+ # Return the current log level
77
+ def Log.level
78
+ @@log.level
79
+ end
80
+
81
+ def Log.fatal(msg)
82
+ @@log.add(Logger::FATAL) { msg }
83
+ end
84
+
85
+ def Log.error(msg)
86
+ @@log.add(Logger::ERROR) { msg }
87
+ end
88
+
89
+ def Log.warn(msg)
90
+ @@log.add(Logger::WARN) { msg }
91
+ end
92
+
93
+ def Log.info(msg)
94
+ @@log.add(Logger::INFO) { msg }
95
+ end
96
+
97
+ def Log.debug(msg)
98
+ @@log.add(Logger::DEBUG) { msg }
99
+ end
100
+
101
+
102
+ end
103
+
104
+
105
+ end
@@ -0,0 +1,126 @@
1
+ require 'rake/tasklib'
2
+ require 'pony'
3
+
4
+ module LyberCore
5
+
6
+ # A rake task that will tag, build and publish your gem to the DLSS gemserver
7
+ #
8
+ # == Usage
9
+ # Include the following two lines in your <tt>Rakefile</tt>:
10
+ # require 'lyber_core/rake/dlss_release'
11
+ # LyberCore::DlssRelease.new
12
+ #
13
+ # To build and release the gem, run the following AFTER pushing your code to the git repository on AFS:
14
+ # rake dlss_release
15
+ #
16
+ # == Requirements
17
+ # - You need <b>ONE</b> <tt>your-gemname.gemspec</tt> file in the root of your project. See the example in <tt>lyber-core.gemspec</tt> or look at http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/
18
+ # - Inside the <tt>Gem::Specification</tt> block of the <tt>your-gemname.gemspec</tt> file, you must set the version number. For example
19
+ # Gem::Specification.new do |s|
20
+ # s.name = "your-gemname"
21
+ # ...
22
+ # s.version = "0.9.6"
23
+ # ...
24
+ # end
25
+ # - You need to have access to the account on the DLSS gemserver, usually by adding your sunetid to the .k5login. You also need git installed.
26
+ class DlssRelease < Rake::TaskLib
27
+ # Name of the rake task. Defaults to :dlss_release
28
+ attr_accessor :name
29
+
30
+ # Name of the gem
31
+ attr_accessor :gemname
32
+
33
+ # Version of the gem being released
34
+ attr_accessor :version
35
+
36
+ def initialize(name=:dlss_release)
37
+ @name = name
38
+ @gemname = determine_gemname
39
+ yield self if block_given?
40
+ raise "Gemname must be set" if @gemname.nil?
41
+ define
42
+ end
43
+
44
+ # Tries to parse the gemname from the prefix of the <tt>your-gemname.gemspec</tt> file.
45
+ def determine_gemname
46
+ files = Dir.glob("*.gemspec")
47
+ raise "Unable to find project gemspec file.\nEither it doesn't exist or there are too many files ending in .gemspec" if(files.size != 1)
48
+
49
+ files.first =~ /(.*)\.gemspec/
50
+ $1
51
+ end
52
+
53
+ def send_release_announcement
54
+ Pony.mail(:to => 'dlss-developers@lists.stanford.edu',
55
+ :from => 'dlss-developers@lists.stanford.edu',
56
+ :subject => "Release #{@version} of the #{@gemname} gem",
57
+ :body => "The #{@gemname}-#{@version}.gem has been released to the DLSS gemserver",
58
+ :via => :smtp,
59
+ :via_options => {
60
+ :address => 'smtp.stanford.edu',
61
+ :port => '25',
62
+ :enable_starttls_auto => true,
63
+ :authentication => :plain, # :plain, :login, :cram_md5, no auth by default
64
+ :domain => "localhost.localdomain" # the HELO domain provided by the client to the server
65
+ }
66
+ )
67
+ end
68
+
69
+ def define
70
+ desc "Tag, build, and release DLSS specified gem"
71
+ task @name do
72
+ IO.foreach(File.join(Rake.original_dir, "#{@gemname}.gemspec")) do |line|
73
+ if(line =~ /\.version.*=.*"(.*)"/)
74
+ @version = $1
75
+ break
76
+ end
77
+ end
78
+
79
+ if(@version.nil?)
80
+ raise "Unable to find version number in #{@gemname}.gemspec"
81
+ end
82
+ created_gem = "#{@gemname}-#{@version}.gem"
83
+
84
+ puts "Make sure:"
85
+ puts " 1) Version #{@version} of #{@gemname} has not been tagged and released previously"
86
+ puts " 2) All of the tests pass"
87
+ puts " 3) You've pushed the code to the git repository on AFS (usually 'git push origin master')"
88
+ puts "Type 'yes' to continue if all of these statements are true"
89
+
90
+ resp = STDIN.gets.chomp
91
+ unless(resp =~ /yes/ )
92
+ raise "Please complete the prerequisites"
93
+ end
94
+
95
+ puts "Releasing version #{@version} of the #{@gemname} gem"
96
+
97
+ begin
98
+ tags = `git tag`.split("\n")
99
+ raise "tag v#{@version} already exists. Change the version number in the #{@gemname}.gemspec file" if tags.include?("v#{@version}")
100
+
101
+ puts "...Tagging release"
102
+ sh "git tag -a v#{@version} -m 'Gem version #{@version}'"
103
+ sh "git push origin --tags"
104
+
105
+ puts "...Building gem"
106
+ sh "gem build #{@gemname}.gemspec"
107
+
108
+ puts "...Publishing gem to sulair-rails-dev DLSS gemserver"
109
+ sh "scp #{created_gem} webteam@sulair-rails-dev.stanford.edu:/var/www/html/gems"
110
+ sh "ssh webteam@sulair-rails-dev.stanford.edu gem generate_index -d /var/www/html"
111
+
112
+ puts "Done!!!!! A local copy of the gem is in the pkg directory"
113
+ FileUtils.mkdir("pkg") unless File.exists?("pkg")
114
+ FileUtils.mv("#{created_gem}", "pkg")
115
+ rescue Exception => e
116
+ FileUtils.rm("#{created_gem}") if(File.exists?("#{created_gem}"))
117
+ raise e
118
+ end
119
+
120
+ puts "Sending release announcement to DLSS developers list"
121
+ send_release_announcement
122
+
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,214 @@
1
+ # == Usage
2
+ # ruby_cl_skeleton [options] source_file
3
+ #
4
+ # For help use: ruby_cl_skeleton -h
5
+
6
+ module LyberCore
7
+ module Robots
8
+ require 'optparse'
9
+ require 'ostruct'
10
+
11
+ # ===== Usage
12
+ # User defined robots should derive from this class and override the #process_item method
13
+ class Robot
14
+ attr_accessor :workflow_name
15
+ attr_accessor :workflow_step
16
+
17
+ # A LyberCore::Robots::Workflow object
18
+ attr_accessor :workflow
19
+ attr_accessor :collection_name
20
+ attr_accessor :workspace
21
+ attr_accessor :args
22
+ attr_accessor :options
23
+
24
+
25
+ # Available options
26
+ # - :collection_name - The collection this workflow should work with.
27
+ # Defined as a subdirectory within ROBOT_ROOT/config/workflows/your_workflow/your_collection
28
+ # - :workspace - Full path of where to find content for a particular workflow
29
+ # - :logfile - Where to write log messages
30
+ # - :loglevel - Level of logging from 0 - 4 where 0 = DEBUG and 4 = FATAL
31
+ def initialize(workflow_name, workflow_step, args = {})
32
+ @workflow_name = workflow_name
33
+ @workflow_step = workflow_step
34
+ @collection_name = args[:collection_name]
35
+ @opts = args
36
+
37
+ if args[:logfile]
38
+ LyberCore::Log.set_logfile(args[:logfile])
39
+ else
40
+ FileUtils.mkdir(File.join(ROBOT_ROOT, 'log')) unless(File.exists?(File.join(ROBOT_ROOT, 'log')))
41
+ robot_logfile = File.join(ROBOT_ROOT,'log',workflow_step+'.log')
42
+ LyberCore::Log.set_logfile(robot_logfile)
43
+ end
44
+
45
+ LyberCore::Log.set_level(args[:loglevel]) if args[:loglevel]
46
+
47
+ # Set defaults
48
+ @options = OpenStruct.new
49
+ self.parse_options
50
+ self.create_workflow
51
+ self.set_workspace
52
+ end
53
+
54
+ # Some workflows require a directory where their content lives
55
+ # If a robot is invoked with a :workspace => true option, its @workspace
56
+ # should be set from the value in
57
+ def set_workspace
58
+ if(@opts[:workspace])
59
+ @workspace = LyberCore::Robots::Workspace.new(@workflow_name, @collection_name)
60
+ LyberCore::Log.debug("workspace = #{workspace.inspect}")
61
+ end
62
+ end
63
+
64
+ # Create the workflow at instantiation, not when we start running the robot.
65
+ # That way we can do better error checking and ensure that everything is going
66
+ # to run okay before we actually start things.
67
+ def create_workflow
68
+
69
+ unless defined?(WORKFLOW_URI)
70
+ LyberCore::Log.fatal "FATAL: WORKFLOW_URI is not defined"
71
+ LyberCore::Log.fatal "Usually this is a value like 'http://lyberservices-dev.stanford.edu/workflow'"
72
+ LyberCore::Log.fatal "Usually you load it by setting ROBOT_ENVIRONMENT when you invoke your robot"
73
+ raise "WORKFLOW_URI is not set! Do you need to set your ROBOT_ENVIRONMENT value?"
74
+ end
75
+ LyberCore::Log.debug("About to instatiate a Workflow object
76
+ -- LyberCore::Robots::Workflow.new(#{@workflow_name},#{collection_name}")
77
+ @workflow = LyberCore::Robots::Workflow.new(@workflow_name, {:logger => @logger, :collection_name => @collection_name})
78
+
79
+ end
80
+
81
+ # == Create a new workflow
82
+ def start()
83
+
84
+ begin
85
+ LyberCore::Log.debug("Starting robot...")
86
+
87
+ queue = @workflow.queue(@workflow_step)
88
+
89
+ # If we have arguments, parse out the parts that indicate druids
90
+ if(@options.file or @options.druid)
91
+ queue.enqueue_druids(get_druid_list)
92
+ else
93
+ queue.enqueue_workstep_waiting()
94
+ end
95
+ process_queue(queue)
96
+ # TODO: Implement a FatalError class
97
+ # rescue LyberCore::Exceptions::FatalError => e
98
+ # LyberCore::Log.fatal("e.msg")
99
+ # exit
100
+ rescue LyberCore::Exceptions::EmptyQueue
101
+ LyberCore::Log.info("Empty queue -- no objects to process")
102
+ rescue Exception => e
103
+ LyberCore::Log.error(e.msg)
104
+ LyberCore::Log.error(e.backtrace.join("\n"))
105
+ end
106
+ end
107
+
108
+ # Generate a list of druids to process
109
+ def get_druid_list
110
+
111
+ druid_list = Array.new
112
+
113
+ # append any druids passed explicitly
114
+ if(@options.druid)
115
+ druid_list << @options.druid
116
+ end
117
+
118
+ # identifier list is in a file
119
+ if (@options.file && File.exist?(@options.file))
120
+ File.open(@options.file) do |file|
121
+ file.each_line do |line|
122
+ druid = line.strip
123
+ if (druid.length > 0)
124
+ druid_list << druid
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ return druid_list
131
+ end
132
+
133
+ def process_queue(queue)
134
+ while work_item = queue.next_item do
135
+ begin
136
+ #call overridden method
137
+ process_item(work_item)
138
+ work_item.set_success
139
+ rescue Exception => e
140
+ # LyberCore::Log.error("Encountered exception processing #{work_item.druid}: #{e.to_s}")
141
+ # LyberCore::Log.debug("Encountered exception processing #{work_item.druid}: #{e.backtrace.join("\n")}")
142
+ work_item.set_error(e)
143
+ end
144
+ end
145
+ queue.print_stats()
146
+ end
147
+
148
+ # Override this method in your robot instance. The method in this base class will throw an exception if it is not overriden.
149
+ def process_item(work_item)
150
+ #to be overridden by child classes
151
+ raise 'You must implement this method in your subclass'
152
+ end
153
+
154
+ # ###########################
155
+ # command line option parsing
156
+
157
+ def parse_options
158
+
159
+ options = {}
160
+
161
+ o = OptionParser.new do |opts|
162
+ opts.banner = "Usage: example.rb [options]"
163
+ opts.separator ""
164
+
165
+ opts.on("-d DRUID", "--druid DRUID", "Pass in a druid to process") do |d|
166
+ @options.druid = d
167
+ end
168
+
169
+ opts.on("-f", "--file FILE", "Pass in a file of druids to process") do |f|
170
+ @options.file = f
171
+ end
172
+
173
+ end
174
+
175
+ # Parse the command line options and ignore anything not specified above
176
+ begin
177
+ o.parse!
178
+ rescue OptionParser::InvalidOption => e
179
+ LyberCore::Log.debug("e.inspect")
180
+ rescue OptionParser::ParseError => e
181
+ LyberCore::Log.error("Couldn't parse options: #{e.backtrace}")
182
+ raise e
183
+ end
184
+
185
+ end
186
+
187
+ # def output_options
188
+ # puts "Options:\n"
189
+ #
190
+ # @options.marshal_dump.each do |name, val|
191
+ # puts " #{name} = #{val}"
192
+ # end
193
+ # end
194
+ #
195
+ # def output_help
196
+ # output_version
197
+ # RDoc::usage() #exits app
198
+ # end
199
+ #
200
+ # def output_usage
201
+ # RDoc::usage('usage') # gets usage from comments above
202
+ # end
203
+ #
204
+ # def output_version
205
+ # puts "#{File.basename(__FILE__)} version #{VERSION}"
206
+ # end
207
+
208
+ # ##################################
209
+ # end of command line option parsing
210
+ # ##################################
211
+
212
+ end # end of class
213
+ end # end of Robots module
214
+ end # end of LyberCore module