require 'socket' require 'open-uri' require 'date' require 'timeout' module Stella # A motley collection of methods that Stella loves to call! module Utils extend self include Socket::Constants # Return the external IP address (the one seen by the internet) def external_ip_address ip = nil begin %w{solutious.heroku.com/ip}.each do |sponge| ipstr = Net::HTTP.get(URI.parse("http://#{sponge}")) || '' ip = /([0-9]{1,3}\.){3}[0-9]{1,3}/.match(ipstr).to_s break if ip && !ip.empty? end rescue SocketError, Errno::ETIMEDOUT => ex Stella.le "Connection Error. Check your internets!" end ip end # Return the local IP address which receives external traffic # from: http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/ # NOTE: This does not open a connection to the IP address. def internal_ip_address # turn off reverse DNS resolution temporarily orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true ip = UDPSocket.open {|s| s.connect('75.101.137.7', 1); s.addr.last } # Solutious IP ip ensure Socket.do_not_reverse_lookup = orig end # require a glob of files. # * +path+ is a list of path elements which is sent to File.join # and then to Dir.glob. The list of files found are sent to require. # Nothing is returned but LoadError exceptions are caught. The message # is printed to STDERR and the program exits with 7. def require_glob(*path) begin Dir.glob(File.join(*path.flatten)).each do |path| require path end rescue LoadError => ex puts "Error: #{ex.message}" exit 7 end end # require a library from the vendor directory. # The vendor directory should be organized such # that +name+ and +version+ can be used to create # the path to the library. # # e.g. # # vendor/httpclient-2.1.5.2/httpclient # def require_vendor(name, version) $: << File.join(LIB_HOME, '..', 'vendor', "#{name}-#{version}") require name end # Checks whether something is listening to a socket. # * +host+ A hostname # * +port+ The port to check # * +wait+ The number of seconds to wait for before timing out. # # Returns true if +host+ allows a socket connection on +port+. # Returns false if one of the following exceptions is raised: # Errno::EAFNOSUPPORT, Errno::ECONNREFUSED, SocketError, Timeout::Error # def service_available?(host, port, wait=3) if Stella.sysinfo.vm == :java begin iadd = Java::InetSocketAddress.new host, port socket = Java::Socket.new socket.connect iadd, wait * 1000 # milliseconds success = !socket.isClosed && socket.isConnected rescue NativeException => ex puts ex.message, ex.backtrace if Stella.debug? false end else begin status = Timeout::timeout(wait) do socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( port, host ) socket.connect( sockaddr ) end true rescue Errno::EAFNOSUPPORT, Errno::ECONNREFUSED, SocketError, Timeout::Error => ex puts ex.class, ex.message, ex.backtrace if Stella.debug? false end end end # A basic file writer def write_to_file(filename, content, mode, chmod=600) mode = (mode == :append) ? 'a' : 'w' f = File.open(filename,mode) f.puts content f.close return unless Stella.sysinfo.os == :unix raise "Provided chmod is not a Fixnum (#{chmod})" unless chmod.is_a?(Fixnum) File.chmod(chmod, filename) end # # Generates a string of random alphanumeric characters. # * +len+ is the length, an Integer. Default: 8 # * +safe+ in safe-mode, ambiguous characters are removed (default: true): # i l o 1 0 def strand( len=8, safe=true ) chars = ("a".."z").to_a + ("0".."9").to_a chars.delete_if { |v| %w(i l o 1 0).member?(v) } if safe str = "" 1.upto(len) { |i| str << chars[rand(chars.size-1)] } str end # Returns +str+ with the leading indentation removed. # Stolen from http://github.com/mynyml/unindent/ because it was better. def noindent(str) indent = str.split($/).each {|line| !line.strip.empty? }.map {|line| line.index(/[^\s]/) }.compact.min str.gsub(/^[[:blank:]]{#{indent}}/, '') end end end