Module: Kernel

Defined in:
lib/quality_extensions/object/default.rb,
lib/quality_extensions/kernel/die.rb,
lib/quality_extensions/range_list.rb,
lib/quality_extensions/kernel/backtrace.rb,
lib/quality_extensions/kernel/trap_chain.rb,
lib/quality_extensions/kernel/require_all.rb,
lib/quality_extensions/global_variable_set.rb,
lib/quality_extensions/kernel/remove_const.rb,
lib/quality_extensions/kernel/require_once.rb,
lib/quality_extensions/kernel/sleep_loudly.rb,
lib/quality_extensions/module/remove_const.rb,
lib/quality_extensions/kernel/remove_module.rb,
lib/quality_extensions/kernel/capture_output.rb,
lib/quality_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

#backtraceObject

Equivalent to calling caller(0)



11
12
13
14
# File 'lib/quality_extensions/kernel/backtrace.rb', line 11

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.



23
24
25
26
27
28
29
30
# File 'lib/quality_extensions/kernel/backtrace.rb', line 23

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.



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
# File 'lib/quality_extensions/kernel/capture_output.rb', line 36

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/quality_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



11
12
13
14
# File 'lib/quality_extensions/kernel/die.rb', line 11

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/quality_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/quality_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/quality_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



17
18
19
20
# File 'lib/quality_extensions/kernel/backtrace.rb', line 17

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

#RangeList(*args) ⇒ Object



14
15
16
# File 'lib/quality_extensions/range_list.rb', line 14

def RangeList(*args)
  RangeList.new(*args)
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)


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

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)


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

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.



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

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']


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
# File 'lib/quality_extensions/kernel/require_all.rb', line 28

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
  if (exclusions = options.delete(:exclude))
    exclusions = [exclusions] if exclusions.is_a? String
    files = files.exclude(*exclusions)
  end
  if (exclusions = options.delete(:exclude_files))
    exclusions = [exclusions] if exclusions.is_a? String
    files = files.exclude(*exclusions.map {|a| File.exact_match_regexp(a) })
  end

  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.expand_path(File.join(File.dirname(__FILE__), dir))

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

Raises:

  • (ArgumentError)


63
64
65
66
67
68
69
70
# File 'lib/quality_extensions/kernel/require_all.rb', line 63

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.expand_path(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)


13
14
15
16
17
# File 'lib/quality_extensions/kernel/require_once.rb', line 13

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

#sleep_loudly(n, step = -1,, options = {}, &block) ⇒ Object

Sleeps for integer n number of seconds, by default counting down from n (inclusive) to 0, with a step size of -1, printing the value of the counter at each step (3, 2, 1, 0 (output 4 times) if n is 3), each time separated by a ‘, ’.

In effect, it is a simple on-screen countdown (or count-up) timer.

To change the step size, supply a value for step other than -1 (the default). It will sleep for step.abs seconds between each iteration, and at each iteration will either yield to the supplied block or (the default) output the current value of the counter).

The value of step also determines in which direction to count: If step is negative (the default), it counts down from n down to 0 (inclusive). If step is positive, it counts up from 0 up to n (inclusive).

step does not need to be an integer value.

If n is not evenly divisible by step (that is, if step * floor( / ? ) > n), the final step size will be shorter to ensure that the total amount slept is n seconds. More precisely, the amount of time it sleeps before the final iteration (during which it won’t sleepat all) will

If a block is provided, all of the default output is overridden, and the block will be yielded with the value of the counter i once every second instead of the default behavior, allowing you to customize what gets output, if anything, or what else happens, every n.abs seconds.

Note that it produces output (or executes your block, if supplied) n+1 times, not n times. This allows you to output (or not) both when the timer is first started and when it finishes. But because it sleeps for 1 second after the first n iterations only and not after the last, the total delay is still only n seconds.

Examples:

sleep_loudly(3) 
  3<sleep 1>2, <sleep 1>1, <sleep 1>0

sleep_loudly(3) {|i| puts(i == 0 ? 'Done' : i)}
3<sleep 1>
2<sleep 1>
1<sleep 1>
Done

sleep_loudly(10*60, :up, 60) {|i| print i*60, ", "} # sleep for 10 minutes, outputting after every 60 seconds
  0<sleep 60>2, <sleep 60>2, <sleep 60>3, 

sleep_loudly(3, :up) {|i| print i}
  0<sleep 1>1<sleep 1>2<sleep 1>3

sleep_loudly(3, :up) {|i| print i+1 unless i==3}
  1<sleep 1>2<sleep 1>3<sleep 1>


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
81
82
83
84
85
86
87
88
89
# File 'lib/quality_extensions/kernel/sleep_loudly.rb', line 41

def sleep_loudly(n, step = -1, options = {}, &block)
  debug = options[:debug] == true ? 1 : 0
  #debug = 1

  old_sync, STDOUT.sync = STDOUT.sync, true
  if step < 0
    starti, endi = n, 0
  elsif step > 0
    starti, endi = 0, n
  else
    raise ArgumentError, "step must be positive or negative, not 0"
  end

  puts "Counting from #{starti} to #{endi} in increments of #{step} (total time should be n=#{n})" if debug

  i = starti
  final = false
  loop do
    print 'final' if final
    if block_given?
      yield *[i, final][0..block.arity-1]
    else
      print "#{i}"
    end

    break if final

    remaining = (i - endi).abs

    # if n was a multiple of step, remaining will eventually be 0, telling us that there is one final iteration to go
    # if n was not a multiple of step, use a different, smaller step as the final step; and we know that there is one final iteration to go
    if remaining < step.abs
      s = (step < 0 ? i-endi : endi-i)
      print " (using smaller final step #{s}) " if debug
      final = true # the next iteration is the final one
    else
      s = step
    end
    i += s
    print " (+#{s}=#{i}) " if debug

    print ", " unless block_given?
    print " (sleeping for #{s.abs}) " if debug
    sleep s.abs 
  end

  print "\n" unless block_given?
  STDOUT.sync = old_sync
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.



15
16
17
18
19
20
21
# File 'lib/quality_extensions/kernel/trap_chain.rb', line 15

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/quality_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