Module: Kernel

Defined in:
lib/qualitysmith_extensions/kernel/die.rb,
lib/qualitysmith_extensions/object/default.rb,
lib/qualitysmith_extensions/kernel/backtrace.rb,
lib/qualitysmith_extensions/kernel/autoreload.rb,
lib/qualitysmith_extensions/kernel/trap_chain.rb,
lib/qualitysmith_extensions/kernel/require_all.rb,
lib/qualitysmith_extensions/global_variable_set.rb,
lib/qualitysmith_extensions/kernel/remove_const.rb,
lib/qualitysmith_extensions/kernel/require_once.rb,
lib/qualitysmith_extensions/module/remove_const.rb,
lib/qualitysmith_extensions/kernel/remove_module.rb,
lib/qualitysmith_extensions/kernel/capture_output.rb,
lib/qualitysmith_extensions/kernel/windows_platform.rb

Overview

Author

Tyler Rick

Copyright

Copyright © 2007 QualitySmith, Inc.

License

Ruby License

Submit to Facets?

No, too ugly and unreliable.

++

Instance Method Summary collapse

Instance Method Details

#autoreload(*args) ⇒ Object

Autoreload feature files.

Automatically reload, at regular intervals, any previously loaded features, and/or other files not already loaded, if they have been modified since the last interval check. A numeric parameter sets the reload interval in seconds and the file parameter can either be a glob string or an array of file paths. If a glob string, it is expanded only once on the initial method call. Supplying a boolean parameter of ‘false’ will force autreload to skip previously loaded features and only reload the specified files. Also keeps a “dirty” flag.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/qualitysmith_extensions/kernel/autoreload.rb', line 17

def autoreload( *args )

  check_interval=10
  include_features = true
  files = nil

  args.each do |arg|
    case arg
    when Numeric
      check_interval = arg
    when String
      files = Dir.glob( arg )
    when Array
      files = arg
    when TrueClass, FalseClass
      include_features = arg
    end
  end

  file_mtime = {}

  Thread.new(Time.now) do |start_time|
    loop do
      sleep check_interval

      if include_features
        feature_files = $LOADED_FEATURES.collect { |feature|
          $LOAD_PATH.each { |lp| file = File.join(lp, feature) }
        }.flatten
        p feature_files

        feature_files.each { |file|
          if File.exists?(file) and (mtime = File.stat(file).mtime) > (file_mtime[file] || start_time)
            $autoreload_dirty = true
            file_mtime[file] = mtime
            STDERR.puts "File '#{ file }' reloaded"
            begin
              load(file)
            rescue Exception => e
              STDERR.puts e.inspect
            end
          end
        }
      end

      if files
        files.each do |file|
          if File.exists?(file) and (mtime = File.stat(file).mtime) > (file_mtime[file] || start_time)
            $autoreload_dirty = true
            file_mtime[file] = mtime
            STDERR.puts "File '#{ file }' changed"
            begin
              load(file)
            rescue Exception => e
              STDERR.puts e.inspect
            end
          end
        end
      end

    end
  end

end

#autoreload_files(*args) ⇒ Object Also known as: autoreload_glob

Same as #autoreload, but does not include previously loaded features. This is equivalent to as adding a ‘false’ parameter to #autoreload.



86
87
88
# File 'lib/qualitysmith_extensions/kernel/autoreload.rb', line 86

def autoreload_files( *args )
  autoreload( false, *args )
end

#backtraceObject

Equivalent to calling caller(0)



13
14
15
16
# File 'lib/qualitysmith_extensions/kernel/backtrace.rb', line 13

def backtrace
  full_backtrace = caller(0)
  return full_backtrace[1..-1]    # We don't want this call to backtrace showing up in the backtrace, so skip top 1 frame.
end

#backtrace_using_exceptionObject

I thought I ran into some case where it didn’t work to use caller(0)…which prompted me to do it this way (raise and rescue an exception)…but now I can’t duplicate that problem, so I will deprecate this method.



25
26
27
28
29
30
31
32
# File 'lib/qualitysmith_extensions/kernel/backtrace.rb', line 25

def backtrace_using_exception
  begin
    raise "Where am I?"
  rescue Exception => exception
    full_backtrace = exception.backtrace
    return full_backtrace[1..-1]    # We don't want this call to backtrace showing up in the backtrace, so skip top 1 frame.
  end
end

#capture_output(output_streams = $stdout, &block) ⇒ Object

Captures the output (stdout by default) that block tries to generate and returns it as a string.

output = capture_output($stderr) { noisy_command }

output = capture_output([$stdout, $stderr]) do
  noisy_command
end

Note: If you specify more than one output stream, the entire results of each will be concatenated in the order you listed them, not necessarily in the order that you wrote to those streams.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/qualitysmith_extensions/kernel/capture_output.rb', line 35

def capture_output(output_streams = $stdout, &block)
  output_streams = [output_streams] unless output_streams.is_a? Array
  
  saved_output_streams = Dictionary.new
  output_streams.each do |output_stream|
    case output_stream.object_id
      when $stdout.object_id
        saved_output_streams[:$stdout] = $stdout
        $stdout = StringIO.new
      when $stderr.object_id
        saved_output_streams[:$stderr] = $stderr
        $stderr = StringIO.new
    end
  end

  what_they_tried_to_output = '' 
  begin
    yield
  rescue Exception
    raise
  ensure
    saved_output_streams.each do |name, output_stream|
      case name
        when :$stdout
          what_they_tried_to_output += $stdout.string
        when :$stderr
          what_they_tried_to_output += $stderr.string
      end

      # Restore the original output_stream that we saved.
      case name
        when :$stdout
          $stdout = saved_output_streams[:$stdout]
        when :$stderr
          $stderr = saved_output_streams[:$stderr]
      end
    end
  end
  return what_they_tried_to_output
end

#default!(object, default_value) ⇒ Object



9
10
11
12
13
14
15
16
# File 'lib/qualitysmith_extensions/object/default.rb', line 9

def default!(object, default_value)
  case object
  when NilClass
    #object.become default_value
    #object.replace default_value
  else
  end
end

#die(message, exit_code = 1) ⇒ Object



10
11
12
13
# File 'lib/qualitysmith_extensions/kernel/die.rb', line 10

def die(message, exit_code = 1)
  $stderr.puts message
  exit exit_code
end

#global_variable_get(var, options = {}) ⇒ Object

Gets the global variable var, which can either be a symbol or an actual global variable (use :match_object).

global_variable_get(:$a)
global_variable_get($a, :match_object => true)


12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/qualitysmith_extensions/global_variable_set.rb', line 12

def global_variable_get(var, options = {})
  if options.delete(:match_object)
    return global_variable_get(global_variable_name(var), options)
  else 
    if var.is_a? Symbol
      raise NameError.new("#{var} is not a valid global variable name") unless var.to_s[0..0] == '$'
      return eval("#{var}")
    else
      raise ArgumentError.new("var must be a symbol unless :match_object => true")
    end
  end
end

#global_variable_name(var) ⇒ Object

Looks up the name of global variable var, which must be an actual global variable.

global_variable_name($a)

Raises:

  • (ArgumentError)


27
28
29
30
31
32
33
34
35
36
37
# File 'lib/qualitysmith_extensions/global_variable_set.rb', line 27

def global_variable_name(var)
  global_variables.each do |test_var|
    #if eval(test_var).eql?(var)
    if eval(test_var).object_id == var.object_id
      #$stderr.puts "Checking #{test_var}. #{eval(test_var).inspect}" 
      #$stderr.puts "          #{$stdout.inspect}"
      return test_var.to_sym
    end
  end
  raise ArgumentError.new("The given object (#{var.inspect}) (#{var.object_id}) is not a valid global variable")
end

#global_variable_set(var, value, options = {}) ⇒ Object

Sets the global variable var, which can either be a symbol or an actual global variable (use :match_object).

global_variable_set(:$a, 'new')
global_variable_set($a, 'new', :match_object => true)
global_variable_set(:$a, "StringIO.new", :eval_string => true)


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/qualitysmith_extensions/global_variable_set.rb', line 43

def global_variable_set(var, value, options = {})
  #puts "global_variable_set(#{var}, #{value.inspect}, #{options.inspect}"
  if options.delete(:match_object)
    return global_variable_set(global_variable_name(var), value, options)
  else 
    if var.is_a? Symbol
      raise NameError.new("#{var} is not a valid global variable name") unless var.to_s[0..0] == '$'
      if options.delete(:eval_string)
        #puts("About to eval: #{var} = #{value}")
        eval("#{var} = #{value}")
      else
        marshalled_data = Marshal.dump(value)
        eval("#{var} = Marshal.load(%Q<#{marshalled_data}>)")
      end
      return var
    else
      raise ArgumentError.new("var must be a symbol unless :match_object => true")
    end
  end
end

#pretty_backtraceObject

Returns a human-readable backtrace



19
20
21
22
# File 'lib/qualitysmith_extensions/kernel/backtrace.rb', line 19

def pretty_backtrace
  "Backtrace:\n" +
    backtrace[1..-1].map{|frame| "* " + frame}.join("\n")
end

#remove_const(const_name) ⇒ Object

This is similar to the built-in Module#remove_const, but it is accessible from all “levels” (because it is defined in Kernel) and can handle hierarchy.

Makes it possible to write simply:

remove_const(A::B::C.name)

rather than having to think about which module the constant is actually defined in and calling remove_const on that module.

This is how you would otherwise have to do it:

A::B.send(:remove_const, :C)

const_name must be an object that responds to to_s.

const_name must be a fully qualified name. For example, this will not work as expected:

module Mod
  Foo = 'foo'
  remove_const(:Foo)
end

because it will try to remove ::Foo instead of Mod::Foo. Fortunately, however, this will work as expected:

module Mod
  Foo = 'foo'
  remove_const(Foo.name)
end

This method is partially inspired by Facets’ Kernel#constant method, which provided a more user-friendly alternative to const_get.

Raises:

  • (ArgumentError)


47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/qualitysmith_extensions/kernel/remove_const.rb', line 47

def remove_const(const_name)
  #require 'pp'
  #puts "remove_const(#{const_name})"
  raise ArgumentError unless const_name.respond_to?(:to_s)
  nesting = const_name.to_s.split(/::/).map(&:to_sym)
  if nesting.size > 1
    parent_module = constant(nesting[0..-2].join('::')) # For example, would be A::B for A::B::C
    const_to_remove = nesting[-1]                       # For example, would be :C   for A::B::C
    parent_module.ignore_access.remove_const_before_was_added_to_Kernel(const_to_remove)
  else
    ignore_access.remove_const_before_was_added_to_Kernel(const_name)
  end
end

#remove_const!Object

This is similar to Kernel#remove_const, but it only works for modules/classes.

This is similar to the built-in Module#remove_module, but it lets you do it in a more object oriented manner, calling remove! on the module/class/constant itself that you want to remove, rather than on its parent.

Makes it possible to write simply:

A::B::C.remove_const!

rather than having to think about which module the constant is actually defined in and calling remove_const on that module. This is how you would have to otherwise do it:

A::B.send(:remove_const, :C)


27
28
29
30
31
32
33
34
35
# File 'lib/qualitysmith_extensions/module/remove_const.rb', line 27

def remove_const!
  if split.size > 1
    parent_module = modspace      # For example, would be A::B for A::B::C
    const_to_remove = split.last  # For example, would be :C   for A::B::C
    parent_module.ignore_access.remove_const(const_to_remove)
  else
    Object.ignore_access.remove_const(name)
  end
end

#remove_module(const) ⇒ Object

This is similar to remove_const, but it only works for modules/classes.

This is similar to the built-in Module#remove_module, but it is accessible from all “levels” (because it is defined in Kernel) and can handle hierarchy.

Makes it possible to write simply:

remove_module(A::B::C)

rather than having to think about which module the constant is actually defined in and calling remove_const on that module. This is how you would have to otherwise do it:

A::B.send(:remove_const, :C)

You can pass in either a constant or a symbol. Passing in a constant is preferred

This method is partially inspired by Facets’ Kernel#constant method, which provided a more user-friendly alternative to const_get.



32
33
34
35
36
37
38
39
40
41
# File 'lib/qualitysmith_extensions/kernel/remove_module.rb', line 32

def remove_module(const)
  const = Module.by_name(const.to_s) if const.is_a?(Symbol)
  if const.split.size > 1
    parent_module = const.modspace      # For example, would be A::B for A::B::C
    const_to_remove = const.split.last  # For example, would be :C   for A::B::C
    parent_module.ignore_access.remove_const(const_to_remove)
  else
    Object.ignore_access.remove_const(const.name)
  end
end

#require_all(what, options = {}) ⇒ Object

requires all Ruby files specified by what, but not matching any of the exclude filters.

  • If what is a string, recursively requires all Ruby files in the directory named what or any of its subdirectories.

  • If what is a FileList, requires all Ruby files that match the what FileList.

Options: :exclude: An array of regular expressions or glob patterns that will be passed to FileList#exclude. If you specify this option, a file will not be included if it matches any of these patterns. :exclude_files: An array of filenames to exclude. These will be matched exactly, so if you tell it to exclude ‘bar.rb’, ‘foobar.rb’ will not be excluded.

Examples:

require_all 'lib/', :exclude => [/ignore/, /bogus/]   # will require 'lib/a.rb', 'lib/long/path/b.rb', but not 'lib/ignore/c.rb'
require_all File.dirname(__FILE__), :exclude_files => ['blow_up_stuff.rb']


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/qualitysmith_extensions/kernel/require_all.rb', line 27

def require_all(what, options = {})
  files, exclusions = [nil]*2

  case what
    when String
      base_dir = what
      base_dir += '/' unless base_dir[-1].chr == '/'
      files = FileList[base_dir + "**/*.rb"]
    when FileList
      files = what
    else
      raise ArgumentError.new("Expected a String or a FileList")
  end
  files = files.exclude(*exclusions)                                       if (exclusions = options.delete(:exclude))
  files = files.exclude(*exclusions.map {|a| File.exact_match_regexp(a) }) if (exclusions = options.delete(:exclude_files))

  files.each do |filename|
    # puts "requiring #{filename}" if filename =~ /test/
    require filename
  end
end

#require_local_all(dir = './', options = {}) ⇒ Object

requires all Ruby files in dir (relative to File.dirname(__FILE__)) or any of its subdirectories.

This is just a shortcut for this:

require_all File.join(File.dirname(__FILE__), dir)

All of the options available for require_all are still available here.

Raises:

  • (ArgumentError)


56
57
58
59
60
61
62
63
# File 'lib/qualitysmith_extensions/kernel/require_all.rb', line 56

def require_local_all(dir = './', options = {})
  raise ArgumentError.new("dir must be a String") unless dir.is_a?(String)
  local_dir = File.dirname( caller[0] )
  require_all(
    File.join(local_dir, dir),
    options
  )
end

#require_once(name) ⇒ Object

Fixes bug in Ruby (1.8, at least – not sure if 2.0 fixes it) where a file can be required twice if the path is spelled differently.

Raises:

  • (NotImplementedError)


12
13
14
15
16
# File 'lib/qualitysmith_extensions/kernel/require_once.rb', line 12

def require_once(name)
  raise NotImplementedError
  # store expand_path(name) in an array ($required_files or something)
  # only do the require if it wasn't already in the array
end

#trap_chain(signal_name, *args, &block) ⇒ Object

Calling Kernel#trap() by itself will replace any previously registered handler code. Kernel#trap_chain(), on the other hand, will add the block you supply to the existing “list” of registered handler blocks. Similar to the way Kernel#at_exit() works, Kernel#trap_chain() will prepend the given block to the call chain for the given signal_name. When the signal occurs, your block will be executed first and then the previously registered handler will be invoked. This can be called repeatedly to create a “chain” of handlers.



13
14
15
16
17
18
19
# File 'lib/qualitysmith_extensions/kernel/trap_chain.rb', line 13

def trap_chain(signal_name, *args, &block)
  previous_interrupt_handler = trap(signal_name, *args) {}
  trap(signal_name, *args) do
    block.call
    previous_interrupt_handler.call unless previous_interrupt_handler == "DEFAULT"
  end
end

#windows_platform?Boolean

Returns:

  • (Boolean)


12
13
14
15
16
17
18
19
# File 'lib/qualitysmith_extensions/kernel/windows_platform.rb', line 12

def windows_platform?
  RUBY_PLATFORM =~ /mswin32/
  
  # What about mingw32 or cygwin32?
  #RUBY_PLATFORM =~ /(win|w)32$/
   
  # What about 64-bit Windows?
end