Module: Boson::CommentInspector

Extended by:
CommentInspector
Included in:
CommentInspector
Defined in:
lib/boson/inspectors/comment_inspector.rb

Overview

Scrapes comments right before a method for its attributes. Method attributes must begin with ‘@’ i.e.:

# @desc Does foo
# @options :verbose=>true
def foo(options={})

Some rules about these attributes:

  • Attribute definitions can span multiple lines. When a new attribute starts a line or the comments end, then a definition ends.

  • If no @desc is found in the comment block, then the first comment line directly above the method is assumed to be the value for @desc. This means that no multi-line attribute definitions can occur without a description since the last line is assumed to be a description.

  • options, option, config and render_options attributes can take any valid ruby since they’re evaled in their module’s context.

  • desc attribute is not evaled and is simply text to be set as a string.

This module was inspired by pragdave.

Constant Summary collapse

EVAL_ATTRIBUTES =
[:options, :render_options, :config]

Instance Method Summary collapse

Instance Method Details

#eval_comment(value, mod, mattr) ⇒ Object



47
48
49
50
51
52
53
54
55
56
# File 'lib/boson/inspectors/comment_inspector.rb', line 47

def eval_comment(value, mod, mattr)
  value = "{#{value}}" if !value[/^\s*\{/] && value[/=>/]
  mod.module_eval(value)
rescue Exception
  if Runner.debug
    warn "DEBUG: Error while evaluating @#{mattr} in module #{mod.to_s[/\w+$/]}:\n  " +
      $!.message.gsub(/\n/, "\n  ")
  end
  nil
end

#parse_option_comments(arr, mod) ⇒ Object

:stopdoc:



36
37
38
39
40
41
42
43
44
45
# File 'lib/boson/inspectors/comment_inspector.rb', line 36

def parse_option_comments(arr, mod)
  arr.inject({}) {|t,e|
    key, val = e.join(' ').split(/\s*,\s*/, 2)
    if val
      key = key.sub(/^\s*:/, '').to_sym
      t[key] = eval_comment(val, mod, 'option')
    end
    t
  }
end

#scrape(file_string, line, mod, attribute = nil) ⇒ Object

Given a method’s file string, line number and defining module, returns a hash of attributes defined for that method.



25
26
27
28
29
30
31
32
33
# File 'lib/boson/inspectors/comment_inspector.rb', line 25

def scrape(file_string, line, mod, attribute=nil)
  hash = scrape_file(file_string, line) || {}
  options = (arr = hash.delete(:option)) ? parse_option_comments(arr, mod) : {}
  hash.select {|k,v| v && (attribute.nil? || attribute == k) }.each do |k,v|
    hash[k] = EVAL_ATTRIBUTES.include?(k) ? eval_comment(v.join(' '), mod, k) : v.join(' ')
  end
  (hash[:options] ||= {}).merge!(options) if !options.empty?
  attribute ? hash[attribute] : hash
end

#scrape_file(file_string, line) ⇒ Object

Scrapes a given string for commented @keywords, starting with the line above the given line



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/boson/inspectors/comment_inspector.rb', line 59

def scrape_file(file_string, line)
  lines = file_string.split("\n")
  saved = []
  i = line -2
  while lines[i] =~ /^\s*#\s*(\S+)/ && i >= 0
    saved << lines[i]
    i -= 1
  end

  saved.empty? ? {} : splitter(saved.reverse)
end

#splitter(lines) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/boson/inspectors/comment_inspector.rb', line 71

def splitter(lines)
  hash = {}
  i = 0
  # to magically make the last comment a description
  unless lines.any? {|e| e =~  /^\s*#\s*@desc/ }
    last_line = lines.pop
    hash[:desc] = (last_line =~ /^\s*#\s*([^@\s].*)/) ? [$1] : nil
    lines << last_line unless hash[:desc]
  end

  option = []
  while i < lines.size
    while lines[i] =~ /^\s*#\s*@(\w+)\s*(.*)/
      key = $1.to_sym
      hash[key] = [$2]
      i += 1
      while lines[i] =~ /^\s*#\s*([^@\s].*)/
        hash[key] << $1
        i+= 1
      end
      option << hash.delete(:option) if key == :option
    end
    i += 1
  end
  hash[:option] = option if !option.empty?
  hash
end