Skip to content

Commit

Permalink
TomDoc overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
copiousfreetime committed Aug 9, 2011
1 parent de36410 commit bca3fcb
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 120 deletions.
66 changes: 47 additions & 19 deletions lib/deprecatable.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Allow methods to be deprecated and record when they are called. Each method
# that is marked via the deprecated class method is wrapped, and calls to the
# deprecated method are recorded.
require 'deprecatable/options'
require 'deprecatable/registry'
require 'deprecatable/alerter'

# Allow methods to be deprecated and record and alert when those
# deprecated methods are called.
#
# There are configurable options for the extended class:
#
Expand All @@ -13,25 +16,25 @@
# ...
# end
#
# deprecate :bar, "foo"
# deprecate :bar, :message => "Foo#bar has been deprecated, use Foo#foo instead"
#
# end
#
require 'deprecatable/options'
require 'deprecatable/registry'
require 'deprecatable/alerter'
module Deprecatable

VERSION = '1.0.0'

# Deprecate a method in the included class.
# Public: Deprecate a method in the included class.
#
# method_name - The method in this class to deprecate.
# options - a hash of the current understood options:
#
# :message => override the default message that would be issued with this message
# :removal_date => The date on which the deprecated method will be removed
# :removal_version => The version on which the deprecated method will be
# removed
# options - a hash of the current understood options (default: {})
# :message - A String to output along with the rest of
# the notifcations about the deprecated
# method.
# :removal_date - The date on which the deprecated method
# will be removed.
# :removal_version - The version on which the deprecated
# method will be removed.
#
# returns the instance of DeprecatedMethod created to track this deprecation.
def deprecate( method_name, options = {} )
Expand All @@ -43,34 +46,59 @@ def deprecate( method_name, options = {} )
return dm
end

# Access the global registry
#
# Returns the instance of the registry
# The global Deprecatable::Registry instance. It is set here so it is
# allocated at parse time.
@registry = Deprecatable::Registry.new

# Public: Get the global Deprecatable::Registry instance
#
# Returns the global Deprecatable::Registry instance.
def self.registry
@registry
end

# Access the global Options
# The global options for Deprecatable. It is set here so it is allocated at
# parse time.
@options = Deprecatable::Options.new

# Public: Access the global Options
#
# Returns the global Deprecatable::Options instance.
def self.options
@options
end

# Access the global Alerter
# The global Alerter for Deprecatable. It is set here so it is allocated at
# parse time.
@alerter = Deprecatable::Alerter.new

# Public: Access the global Alerter
#
# Returns the global Alerter instance
def self.alerter
@alerter
end

# Public: Set the global Alerter
#
# alerter - Generally an instance of Alerter, but may be anything that
# responds_to? both :alert and :report. See the Alerter
# documetation for more information
#
# Returns nothing.
def self.alerter=( a )
@alerter = a
end
end

require 'deprecatable/util'
require 'deprecatable/call_site_context'
require 'deprecatable/call_site'
require 'deprecatable/deprecated_method'

# The at_exit handler is set at all times, and it will always fire, unless the
# process is killed with prejudice and/or the ruby process exists using 'exit!'
# instead of the normal 'exit'
at_exit do
if ::Deprecatable.options.has_at_exit_report? then
::Deprecatable.alerter.final_report
Expand Down
67 changes: 57 additions & 10 deletions lib/deprecatable/alerter.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
require 'stringio'
module Deprecatable
# The Default Alerter for Deprecatable
# An Alerter formats and emits alerts, and formats and emits reports.
#
# If you wish to impelement your own Alerter class, then it must implement
# the following methods:
#
# * alert( DeprecatedMethod, CallSite )
# * final_reort()
#
# These are the two methods that are invoked by the Deprecatable system at
# various points.
#
# Any class can be an Alerter, it just needs to respond_to?( :alert )
# The Alerter also doe the at_exit report, and that is done with the
# 'final_report' which takes no arguments.
class Alerter
# alert that the deprecated method was invoked at a specific call site
# Public: Alert that the deprecated method was invoked at a specific call
# site.
#
# deprecated_method - an instance of DeprecatedMethod
# call_site - an instance of CallSite showing this particular
# invocation
# This just prints it out using the global 'warn' method
#
# Returns nothing.
def alert( deprecated_method, call_site )
lines = deprecated_method_report( deprecated_method, call_site )
lines << "To turn this report off do one of the following:"
Expand All @@ -21,8 +29,14 @@ def alert( deprecated_method, call_site )
lines.each { |l| warn_with_prefix l }
end

# Render the final report
def final_report
# Public: Render the final deprecation report showing when and where all
# deprecated methods in the Registry were calld.
#
# registry - An instance of Deprecatable::Registry
# (default: Deprecatable.registry)
#
# Returns nothing.
def final_report( registry = Deprecatable.registry )
lines = [ "Deprecatable 'at_exit' Report",
"=============================" ]
lines << ""
Expand All @@ -32,8 +46,7 @@ def final_report
lines << "* set the environment variable `DEPRECATABLE_HAS_AT_EXIT_REPORT=\"false\"`"
lines << ""


::Deprecatable.registry.items.each do |dm|
registry.items.each do |dm|
lines += deprecated_method_report( dm )
end
lines.each { |l| warn_without_prefix l }
Expand All @@ -43,6 +56,12 @@ def final_report
private
###################################################################

# Format a report of the data in a DeprecatedMethod
#
# dm - A DeprecatedMethod instance
# call_site - A CallSite instance (default :nil)
#
# Returns an Array of Strings which are the lines of the report.
def deprecated_method_report( dm, call_site = nil )
m = "`#{dm.klass}##{dm.method}`"
lines = [ m ]
Expand Down Expand Up @@ -72,6 +91,13 @@ def deprecated_method_report( dm, call_site = nil )
return lines
end

# Format a report about a CallSite
#
# cs - A CallSite instance
# include_count - Should the report include the invocation count from the
# CallSite instance. (default: false)
#
# Returns an Array of Strings which are the lines of the report.
def call_site_report( cs, include_count = false )
header = [ "Called" ]
header << "#{cs.invocation_count} time(s)" if include_count
Expand All @@ -86,28 +112,49 @@ def call_site_report( cs, include_count = false )
return lines
end

# Emit a warning message without a prefix to the message.
#
# Returns nothing.
def warn_without_prefix( msg = "" )
warn msg
end

# Emit a warning message WITH a prefix to the message.
#
# Returns nothing.
def warn_with_prefix( msg = "" )
warn "DEPRECATION WARNING: #{msg}"
end

# Emit a warning message.
#
# Returns nothing.
def warn( msg )
Kernel.warn( msg )
end
end

# StringIOAlerter is used to capture all alerts in an instance of StringIO
# instead of emitting them as Ruby warnings. This is mainly used in testing,
# and may have uses in other situations too.
class StringIOAlerter < Alerter
# Initialize the StringIOAlerter
#
# Returns nothing.
def initialize
@stringio = StringIO.new
end

# Capture the warning into the StringIO instance
#
# Returns nothing.
def warn( msg )
@stringio.puts msg
end

# Access the contens of the internal StringIO instance.
#
# Returns a String containing all the warnings so far.
def to_s
@stringio.string
end
Expand Down
58 changes: 44 additions & 14 deletions lib/deprecatable/call_site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,85 @@ module Deprecatable
# that it was invoked, and an extraction of the source code around the
# invocation site
class CallSite
# Generate the hash key for a call site with the given file and line number
# Generate the hash key for a call site with the given file and line
# number.
#
# file - A String that is the filesystem path to a file.
# line_number - An Integer that is the line number in the given file.
#
# Returns a String that is generally used as a unique key.
def self.gen_key( file, line_number )
"#{file}:#{line_number}"
end

# The fully expanded path of the file
# Public: Get the fully expand path of the file of the CallSite
#
# Returns the String filesystem path of the file.
attr_reader :file

# The line number in the file of the call site. Line numbers start at 1
# Public: Get the line number of the CallSite in the file.
# Line numbers start at 1.
#
# Returns the line number of a line in the file.
attr_reader :line_number

# The number of lines of padding before and after to also capture when
# getting the context
# Public: Gets the number of lines before and after the line_nubmer
# to also capture when gettin the context.
#
# This number is the number both before AND after 'line_number' to
# capture. If this number is 2, then the total number of lines captured
# should be 5. 2 before, the line in question, and 2 after.
#
# Returns the number of lines
attr_reader :context_padding

# The number of times this call site has been invoked
# Public: The number of times this CallSite has been invoked.
#
# Returns the Integer number of times this call site has been invoked.
attr_reader :invocation_count

# Create a new instance of CallSite
#
# file - A String pathname of the file where the CallSite
# happend
# line_number - The Integer line number in the file.
# context_padding - The Integer number of lines both before and after
# the 'line_nubmer' to capture.
def initialize( file, line_number, context_padding )
@file = File.expand_path( file )
@line_number = line_number
@context_padding = context_padding
@invocation_count = 0
end

# return the hash key for this call site
# The unique identifier of this CallSite.
#
# Returns the String key of this CallSite.
def key
CallSite.gen_key( file, line_number )
end

# increment the invocation count by the amount given
# Increment the invocation count by the amount given
#
# count - the amount to increment the invocation count by
# count - The amount to increment the invocation count by
# This should rarely, if ever be set.
# (default: 1)
#
# returns the invocation count
# Returns the Integer invocation count.
def increment_invocation_count( count = 1 )
@invocation_count += count
end

# Retrieve the call site context. This goes to the source file, finds the
# right lines of the file ane captures them with some annotation
# Retrieve the lazily loaded CallSiteContext.
#
# returns an instances of CallSiteContext
# Returns an instances of CallSiteContext
def context
@context ||= CallSiteContext.new( @file, @line_number, @context_padding )
end

# return the formatted context lines
# Access the lines of the context in a nicely formatted way.
#
# Returns an Array of Strings containing the formatted context.
def formatted_context_lines
context.formatted_context_lines
end
Expand Down
Loading

0 comments on commit bca3fcb

Please sign in to comment.