class BinScript
require ‘active_support/core_ext/class/attribute.rb’
Constants
- PARAMETER_TYPES
Allowed parameter types. Equivalence aliases with GetoptLong constants.
- VERSION
Attributes
Place for store exit status.
Place for logger object
Public Class Methods
# File lib/bin_script/bin_script.rb, line 88 def bin_name script_name.gsub('_script', '') end
Prepare class name from filename parts
# File lib/bin_script/bin_script.rb, line 120 def calculate_script_class_filename(parts) files = [] # Collect from disk, needed class files parts.each do |p| off = "app/{bin,bins,models,script,scripts}/**/#{p}_script.rb" files += Dir[File.join(RailsStub.root, off)] off = "script/**/#{p}_script.rb" files += Dir[File.join(RailsStub.root, off)] end files.reverse end
Prepare class name from filename parts
# File lib/bin_script/bin_script.rb, line 109 def calculate_script_class_name(parts) # Calculate class name and paths from source script filename parts if(parts.length > 1) class_name = parts.map{|p| p.camelize}.join('::') + parts.first.camelize else class_name = parts.first.camelize end class_name += "Script" end
Prepare ARGV parameters as hash
# File lib/bin_script/bin_script.rb, line 174 def get_argv_values values = {} o = GetoptLong.new(*get_getoptlong_params) o.quiet = true # Don't write arg error to STDERR o.each { |opt, arg| values[opt[1..-1].to_sym] = arg } values end
Get parameter by key
# File lib/bin_script/bin_script.rb, line 77 def get_parameter(key) param = @parameters.find{|p| p[:key] == key || (p[:alias].present? && p[:alias].include?(key))} raise "Can't find parameter with key #{key.inspect} for class #{self.inspect}!" if param.nil? param end
Initialize script
# File lib/bin_script/bin_script.rb, line 229 def initialize begin @source_argv = ARGV.dup @overrided_parameters = {} @params_values = (RailsStub.env == 'test' ? {} : self.class.get_argv_values) # Create logger if logging enabled if self.class.enable_logging @logger = XLogger.new(:file => log_filename, :dont_touch_rails_logger => (RailsStub.env == 'test')) @logger.level = self.class.log_level end rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument, GetoptLong::NeedlessArgument => e usage_exit e.message end end
Parse script filename. Extract important path parts
# File lib/bin_script/bin_script.rb, line 93 def parse_script_file_name(filename) result = {} # Prepare important parts of source script filename parts = filename.split(File::SEPARATOR) parts = parts[parts.index('bin')+1..-1] parts.map!{|p| File.basename(p).split('.').first} result[:parts] = parts result[:class] = calculate_script_class_name(parts) result[:files] = calculate_script_class_filename(parts) result end
Remove parameter
# File lib/bin_script/bin_script.rb, line 167 def remove_parameter(key) new = [] @parameters.each { |p| new << p if p[:key] != key } @parameters = new end
Run script detected by the filename of source script file
# File lib/bin_script/bin_script.rb, line 136 def run_script(filename = $0) cfg = parse_script_file_name(Pathname.new(filename).realpath.to_s) cfg[:files].each { |f| require f } # Create instance and call run! for script class klass = cfg[:class].constantize script = klass.new script.run! # Exit with specified exit status exit script.exit_status || 0 end
Prepare readable script name
# File lib/bin_script/bin_script.rb, line 84 def script_name self.to_s.underscore.gsub('/','__') end
Prepare usage message
# File lib/bin_script/bin_script.rb, line 183 def usage(message = nil) usage_msg = '' usage_msg += "Error: #{message}\n\n" unless message.nil? usage_msg += "Use: ./bin/#{bin_name}.rb [OPTIONS]\n\n" usage_msg += "\"#{self.description}\"\n\n" if message.nil? && self.description.present? usage_msg += "Availible options:\n\n" @parameters.each do |param| arg = case param[:type] when :required then " v " when :optional then "[v]" when :noarg then " " end usage_msg += " #{prefix_key param[:key]}#{arg} #{param[:description]}\n" end usage_msg += "\n" usage_msg end
Private Class Methods
Prepare parameters in Getoptlong lib format
# File lib/bin_script/bin_script.rb, line 205 def get_getoptlong_params result = [] @parameters.each do |param| cfg = [prefix_key(param[:key])] param[:alias].each{|als| cfg << prefix_key(als) } unless param[:alias].blank? cfg << PARAMETER_TYPES[param[:type]] result << cfg end result end
Prepare argument name with short or long prefix
# File lib/bin_script/bin_script.rb, line 217 def prefix_key(key) key = key.to_s (key.length > 1 ? "--" : "-") + key end
Prepare string with exception details
# File lib/bin_script/bin_script.rb, line 401 def self.prepare_exception_message(e) <<-EXCEPTION Exception happend Type: #{e.class.inspect} Error occurs: #{e.message} Backtrace: #{e.backtrace.join("\n")} EXCEPTION end
Public Instance Methods
# File lib/bin_script/bin_script.rb, line 246 def check_required_params self.class.parameters.each do |param| if param[:type] == :required && @params_values.has_key?(param[:key]) if @params_values[param[:key]].nil? error "Param #{param[:key]} require value, but not present" usage_exit end end end end
Dummy for do! method
# File lib/bin_script/bin_script.rb, line 331 def do!; end
Prepare filename of log file
# File lib/bin_script/bin_script.rb, line 363 def lock_filename params(:L).blank? ? File.join(RailsStub.root, 'locks', "#{self.class.script_name}.lock") : params(:L) end
Prepare filename of log file
# File lib/bin_script/bin_script.rb, line 368 def log_filename params(:l).blank? ? File.join(RailsStub.root, 'log', "#{self.class.script_name}#{log_filename_time_part}.log") : params(:l) end
Override one or more parameters for testing purposes
# File lib/bin_script/bin_script.rb, line 334 def override_parameters(args) if args.is_a?(Symbol) override_parameter(self.class.get_parameter(args)) elsif args.is_a?(Hash) args.each{|key, value| override_parameter(self.class.get_parameter(key), value)} else raise "Parameter should be Symbol or Hash" end end
Return parameter value by key
# File lib/bin_script/bin_script.rb, line 345 def params(key) param = self.class.get_parameter(key) # Use dafault key (if call by alias) key = param[:key] case param[:type] when :noarg return (@overrided_parameters.has_key?(key) && @overrided_parameters[key]) || !@params_values[key].nil? when :optional return @overrided_parameters[key] || @params_values[key] || param[:default] when :required value = @overrided_parameters[key] || @params_values[key] || param[:default] return value end end
# File lib/bin_script/bin_script.rb, line 223 def puts(*arg) return if self.class.disable_puts_for_tests && RailsStub.env == 'test' Kernel.puts(*arg) end
Create lock file, call script code and unlock file even if error happend.
# File lib/bin_script/bin_script.rb, line 258 def run! # Print usage and exit if asked usage_exit if params(:h) check_required_params # Create and check lock file if enabled if self.class.enable_locking @lock = LockFile.new(lock_filename) @lock.quiet = true # Don't write errors to STDERR if(@lock.lock) msg = "--- Try start. Buy lock file '#{@lock.path}' already open in exclusive mode. Exit! ---" # puts msg # puts is not good idea, because cron will mail it, but this is not error warn msg exit end end begin # Log important info and call script job info "" log_params = {:env => RailsStub.env, :log_level => (self.class.enable_logging ? @logger.level : nil), :lock_file => (self.class.enable_locking ? @lock.path : nil)} info "> Script #{self.class.script_name} started! (#{log_params.inspect})" info "- Parameters: #{@params_values.inspect}" start = Time.now # Инкрементируем счетчик запусков этого скрипта inc_counter("#{self.class.script_name}_times") do! duration = Time.now - start info "< Script #{self.class.script_name} finished! (#{"%.4f" % duration.to_f} sec)" info "Exit status: #{@exit_status}" if @exit_status # Инкрементируем время работы э inc_counter("#{self.class.script_name}_long", duration) # Log benchmarker info if it's not empty log_benchmarker_data rescue Exception => e # Print error info if it's not test env or exit exit_mes = (e.class == SystemExit) || (e.class == Interrupt) || (e.class == SignalException) || (RailsStub.env == 'test') unless exit_mes msg = self.class.prepare_exception_message(e) puts "\n" + msg fatal msg notify_about_error(e) else error "Get exit message! #{e.message}" end # Инкрементируем счетчик ошибок этого скрипта inc_counter("#{self.class.script_name}_raised") ensure # Unlock lock file @lock.unlock if self.class.enable_locking && @lock end end
Print usage message and exit
# File lib/bin_script/bin_script.rb, line 324 def usage_exit(message = nil) error "Exit with error message: #{message}" if message.present? Kernel.puts(self.class.usage(message)) exit end
Private Instance Methods
# File lib/bin_script/bin_script.rb, line 410 def inc_counter(id, counter = 1) # stub end
Print benchmarker statistic to log if its not empty
# File lib/bin_script/bin_script.rb, line 393 def log_benchmarker_data benchmark_data = {} #benchmark_get_data return if benchmark_data.empty? info "Benchmarker data:" info benchmark_data.to_yaml end
Current time logname part.
# File lib/bin_script/bin_script.rb, line 375 def log_filename_time_part Time.now.strftime(self.class.date_log_postfix) end
# File lib/bin_script/bin_script.rb, line 414 def notify_about_error(ex) # stub end
Override value for one parameter
# File lib/bin_script/bin_script.rb, line 380 def override_parameter(param, value = nil) value = case param[:type] when :noarg true when :optional value.to_s when :required value end @overrided_parameters[param[:key]] = value end