bahuvrihi-tap 0.10.7 → 0.10.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/MIT-LICENSE +0 -2
  2. data/README +20 -31
  3. data/bin/rap +18 -8
  4. data/cgi/run.rb +47 -37
  5. data/cmd/console.rb +1 -1
  6. data/cmd/destroy.rb +3 -3
  7. data/cmd/generate.rb +3 -3
  8. data/cmd/manifest.rb +61 -53
  9. data/cmd/run.rb +1 -1
  10. data/doc/Class Reference +119 -110
  11. data/doc/Command Reference +76 -123
  12. data/doc/Syntax Reference +290 -0
  13. data/doc/Tutorial +307 -237
  14. data/lib/tap.rb +1 -12
  15. data/lib/tap/app.rb +46 -71
  16. data/lib/tap/constants.rb +1 -1
  17. data/lib/tap/declarations.rb +110 -100
  18. data/lib/tap/env.rb +141 -173
  19. data/lib/tap/exe.rb +5 -5
  20. data/lib/tap/file_task.rb +2 -2
  21. data/lib/tap/generator/base.rb +0 -4
  22. data/lib/tap/generator/destroy.rb +8 -12
  23. data/lib/tap/generator/generate.rb +19 -14
  24. data/lib/tap/generator/generators/command/command_generator.rb +1 -1
  25. data/lib/tap/generator/generators/config/config_generator.rb +3 -3
  26. data/lib/tap/generator/generators/file_task/file_task_generator.rb +1 -1
  27. data/lib/tap/generator/generators/generator/generator_generator.rb +27 -0
  28. data/lib/tap/generator/generators/generator/templates/task.erb +27 -0
  29. data/lib/tap/generator/generators/root/root_generator.rb +12 -12
  30. data/lib/tap/generator/generators/root/templates/Rakefile +1 -2
  31. data/lib/tap/generator/generators/root/templates/tapfile +11 -8
  32. data/lib/tap/generator/generators/task/task_generator.rb +1 -3
  33. data/lib/tap/generator/generators/task/templates/test.erb +1 -3
  34. data/lib/tap/root.rb +4 -2
  35. data/lib/tap/support/aggregator.rb +16 -3
  36. data/lib/tap/support/assignments.rb +10 -9
  37. data/lib/tap/support/audit.rb +58 -62
  38. data/lib/tap/support/class_configuration.rb +32 -43
  39. data/lib/tap/support/combinator.rb +7 -7
  40. data/lib/tap/support/configurable.rb +13 -14
  41. data/lib/tap/support/configurable_class.rb +6 -30
  42. data/lib/tap/support/configuration.rb +36 -9
  43. data/lib/tap/support/constant.rb +75 -13
  44. data/lib/tap/support/constant_manifest.rb +115 -0
  45. data/lib/tap/support/dependencies.rb +27 -67
  46. data/lib/tap/support/dependency.rb +44 -0
  47. data/lib/tap/support/executable.rb +78 -109
  48. data/lib/tap/support/executable_queue.rb +1 -1
  49. data/lib/tap/support/gems.rb +6 -0
  50. data/lib/tap/support/gems/rack.rb +197 -84
  51. data/lib/tap/support/instance_configuration.rb +29 -3
  52. data/lib/tap/support/intern.rb +46 -0
  53. data/lib/tap/support/join.rb +67 -11
  54. data/lib/tap/support/joins.rb +2 -0
  55. data/lib/tap/support/joins/fork.rb +1 -0
  56. data/lib/tap/support/joins/merge.rb +3 -1
  57. data/lib/tap/support/joins/sequence.rb +2 -2
  58. data/lib/tap/support/joins/switch.rb +3 -1
  59. data/lib/tap/support/joins/sync_merge.rb +6 -0
  60. data/lib/tap/support/lazy_attributes.rb +16 -1
  61. data/lib/tap/support/lazydoc.rb +21 -21
  62. data/lib/tap/support/lazydoc/comment.rb +59 -55
  63. data/lib/tap/support/lazydoc/definition.rb +36 -0
  64. data/lib/tap/support/lazydoc/document.rb +37 -13
  65. data/lib/tap/support/manifest.rb +120 -131
  66. data/lib/tap/support/minimap.rb +90 -0
  67. data/lib/tap/support/node.rb +4 -6
  68. data/lib/tap/support/parser.rb +63 -6
  69. data/lib/tap/support/schema.rb +11 -2
  70. data/lib/tap/support/shell_utils.rb +3 -5
  71. data/lib/tap/support/string_ext.rb +60 -0
  72. data/lib/tap/support/tdoc.rb +2 -2
  73. data/lib/tap/support/templater.rb +29 -15
  74. data/lib/tap/support/validation.rb +22 -11
  75. data/lib/tap/task.rb +155 -156
  76. data/lib/tap/tasks/load.rb +95 -8
  77. data/lib/tap/test/extensions.rb +2 -1
  78. data/lib/tap/test/script_tester.rb +7 -1
  79. data/template/index.erb +39 -32
  80. metadata +13 -13
  81. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +0 -15
  82. data/lib/tap/patches/rake/rake_test_loader.rb +0 -8
  83. data/lib/tap/patches/rake/testtask.rb +0 -57
  84. data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -51
  85. data/lib/tap/patches/ruby19/parsedate.rb +0 -16
  86. data/lib/tap/spec.rb +0 -42
  87. data/lib/tap/spec/adapter.rb +0 -25
  88. data/lib/tap/spec/inheritable_class_test_root.rb +0 -9
  89. data/lib/tap/support/constant_utils.rb +0 -127
  90. data/lib/tap/support/summary.rb +0 -30
@@ -5,20 +5,29 @@ require 'tap/support/configuration'
5
5
  module Tap
6
6
  module Support
7
7
 
8
- # ClassConfiguration tracks and handles the class configurations defined in a
9
- # Configurable class.
8
+ # ClassConfiguration tracks configurations defined by a Configurable class.
10
9
  class ClassConfiguration
11
10
  include Enumerable
12
11
 
13
- # The class receiving new configurations
12
+ config_templates_dir = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates"
13
+
14
+ # The path to the :doc template (see inspect)
15
+ DOC_TEMPLATE_PATH = File.join(config_templates_dir, 'doc.erb')
16
+
17
+ # The path to the :nodoc template (see inspect)
18
+ NODOC_TEMPLATE_PATH = File.join(config_templates_dir, 'nodoc.erb')
19
+
20
+ # The Configurable class receiving new configurations
14
21
  attr_reader :receiver
15
22
 
16
- # Tracks the assignment of the config keys to receivers
23
+ # An Assignments tracking the assignment of config keys to receivers
17
24
  attr_reader :assignments
18
25
 
19
- # A map of (key, config) pairs.
26
+ # A map of [key, Configuration] pairs
20
27
  attr_reader :map
21
-
28
+
29
+ # Generates a new ClassConfiguration for the receiver. If a parent is
30
+ # provided, configurations will be inherited from it.
22
31
  def initialize(receiver, parent=nil)
23
32
  @receiver = receiver
24
33
 
@@ -34,9 +43,9 @@ module Tap
34
43
  end
35
44
  end
36
45
 
37
- # Initializes a Configuration using the inputs and sets it in self
38
- # using name as a key, overriding the current config by that name,
39
- # if it exists. Returns the new config.
46
+ # Initializes a Configuration using the inputs and sets using name
47
+ # as a key. Any existing config by the same name is overridden.
48
+ # Returns the new config.
40
49
  def add(name, default=nil, attributes={})
41
50
  self[name] = Configuration.new(name.to_sym, default, attributes)
42
51
  end
@@ -46,12 +55,12 @@ module Tap
46
55
  self[key] = nil
47
56
  end
48
57
 
49
- # Gets the configuration specified by key. The key is symbolized.
58
+ # Gets the config specified by key. The key is symbolized.
50
59
  def [](key)
51
60
  map[key.to_sym]
52
61
  end
53
62
 
54
- # Assigns the configuration to key. A nil config unassigns the
63
+ # Assigns the config to key. A nil config unassigns the
55
64
  # configuration key. The key is symbolized.
56
65
  def []=(key, config)
57
66
  key = key.to_sym
@@ -91,7 +100,7 @@ module Tap
91
100
  end
92
101
 
93
102
  # Calls block once for each [receiver, key, config] in self,
94
- # passing those elements as parameters, in the order in
103
+ # passing those elements as parameters in the order in
95
104
  # which they were assigned.
96
105
  def each
97
106
  assignments.each do |receiver, key|
@@ -100,7 +109,7 @@ module Tap
100
109
  end
101
110
 
102
111
  # Calls block once for each [key, config] pair in self,
103
- # passing those elements as parameters, in the order in
112
+ # passing those elements as parameters in the order in
104
113
  # which they were assigned.
105
114
  def each_pair
106
115
  assignments.each do |receiver, key|
@@ -114,7 +123,7 @@ module Tap
114
123
  InstanceConfiguration.new(self, receiver, store)
115
124
  end
116
125
 
117
- # Returns a hash of the (key, config.default) values in self.
126
+ # Returns a hash of the [key, config.default] pairs in self.
118
127
  def to_hash
119
128
  hash = {}
120
129
  each_pair {|key, config| hash[key] = config.default }
@@ -130,36 +139,16 @@ module Tap
130
139
  code_comments
131
140
  end
132
141
 
133
- # The path to the :doc template (see format_str)
134
- DOC_TEMPLATE_PATH = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates/doc.erb"
135
-
136
- # The path to the :nodoc template (see format_str)
137
- NODOC_TEMPLATE_PATH = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates/nodoc.erb"
138
-
139
- # Formats the configurations using the specified template. Two default
140
- # templates are defined, <tt>:doc</tt> and <tt>:nodoc</tt>. These map
141
- # to the contents of DOC_TEMPLATE_PATH and NODOC_TEMPLATE_PATH and
142
- # correspond to the documented and undocumented config generator templates.
143
- #
144
- # == Custom Templates
145
- #
146
- # format_str initializes a Templater which formats each [receiver, configurations]
147
- # pair in turn, and puts the output to the target using <tt><<</tt>. The
148
- # templater is assigned the following attributes for use in formatting:
149
- #
150
- # receiver:: The receiver
151
- # configurations:: An array of configurations and associated comments
152
- #
153
- # In the template these can be accessed as any ERB locals, for example:
154
- #
155
- # <%= receiver.to_s %>
156
- # <% configurations.each do |key, config, comment| %>
157
- # ...
158
- # <% end %>
142
+ # Inspects the configurations using the specified template. Templates
143
+ # are used for format each [receiver, configurations] pair in self.
144
+ # See DEFAULT_TEMPLATE as a model. The results of each template cycle
145
+ # are pushed to target.
159
146
  #
160
- # The input template may be a String or an ERB; either may be used to
161
- # initialize the templater.
162
- def format_str(template=:doc, target="")
147
+ # Two default templates are defined, <tt>:doc</tt> and <tt>:nodoc</tt>.
148
+ # These map to the contents of DOC_TEMPLATE_PATH and NODOC_TEMPLATE_PATH
149
+ # and correspond to the documented and undocumented
150
+ # Tap::Generator::Generators::ConfigGenerator templates.
151
+ def inspect(template=:doc, target="")
163
152
  Lazydoc.resolve_comments(code_comments)
164
153
 
165
154
  template = case template
@@ -9,14 +9,14 @@ module Tap
9
9
  # c = Combinator.new [1,2], [3,4]
10
10
  # c.to_a # => [[1,3], [1,4], [2,3], [2,4]]
11
11
  #
12
- # Combinators can take any object that responds to :each as an
12
+ # Combinators can take any object that responds to each as an
13
13
  # input set; normally arrays are used.
14
14
  #
15
15
  # === Implementation
16
16
  #
17
17
  # Combinator iteratively combines each element from the first set (a)
18
18
  # with each element from the second set (b). When more than two sets
19
- # are given, the Combinator bundles all but the first set into a new
19
+ # are given, the second and remaining sets are bundled into a
20
20
  # Combinator, which then acts as the second set.
21
21
  #
22
22
  # c = Combinator.new [1,2], [3,4], [5,6]
@@ -35,14 +35,14 @@ module Tap
35
35
  class Combinator
36
36
  include Enumerable
37
37
 
38
- # The first set.
38
+ # The first set
39
39
  attr_reader :a
40
40
 
41
- # The second set.
41
+ # The second set
42
42
  attr_reader :b
43
43
 
44
- # Creates a new Combinator. Input sets must be an Array, or nil.
45
- # Within the Array any objects are allowed for combination.
44
+ # Creates a new Combinator. Each input must respond
45
+ # to each, or be nil.
46
46
  def initialize(*sets)
47
47
  @a = make_set(sets.shift)
48
48
  @b = make_set(*sets)
@@ -71,7 +71,7 @@ module Tap
71
71
  end
72
72
 
73
73
  # Passes each combination as an array to the input block.
74
- def each
74
+ def each # :yields: combination
75
75
  case
76
76
  when !(@a.empty? || @b.empty?)
77
77
  @a.each do |a|
@@ -21,7 +21,7 @@ module Tap
21
21
  # c.config.class # => InstanceConfiguration
22
22
  # c.config # => {:one => 'one', :two => 'two', :three => 'three'}
23
23
  #
24
- # The <tt>config</tt> object acts as a kind of forwarding hash; declared configurations
24
+ # The <tt>config</tt> object acts as a forwarding hash; declared configurations
25
25
  # map to accessors while undeclared configurations are stored internally:
26
26
  #
27
27
  # c.config[:one] = 'ONE'
@@ -33,8 +33,8 @@ module Tap
33
33
  # c.config[:undeclared] = 'value'
34
34
  # c.config.store # => {:undeclared => 'value'}
35
35
  #
36
- # The writer method for a configuration can be modified by providing a block to config.
37
- # The Validation module provides a number of common validation and string-transform
36
+ # The writer for a configuration can be defined by providing a block to config.
37
+ # The Validation module provides a number of common validation/transform
38
38
  # blocks which can be accessed through the class method 'c':
39
39
  #
40
40
  # class SubClass < ConfigClass
@@ -55,21 +55,21 @@ module Tap
55
55
  # s.two = nil # !> ValidationError
56
56
  # s.two = 'str' # !> ValidationError
57
57
  #
58
- # As shown above, configurations are inherited from the parent and can be
58
+ # As shown above, configurations are inherited from the parent and may be
59
59
  # overridden in subclasses. See ConfigurableClass for more details.
60
60
  #
61
61
  module Configurable
62
62
 
63
63
  # Extends including classes with ConfigurableClass
64
- def self.included(mod)
64
+ def self.included(mod) # :nodoc:
65
65
  mod.extend Support::ConfigurableClass if mod.kind_of?(Class)
66
66
  end
67
67
 
68
- # The instance configurations for self
68
+ # An InstanceConfiguration with configurations for self
69
69
  attr_reader :config
70
70
 
71
- # Reconfigures self with the given configuration overrides. Only
72
- # the specified configs are modified. Override keys are symbolized.
71
+ # Reconfigures self with the given overrides. Only the specified configs
72
+ # are modified. Keys are symbolized.
73
73
  #
74
74
  # Returns self.
75
75
  def reconfigure(overrides={})
@@ -81,9 +81,9 @@ module Tap
81
81
  self
82
82
  end
83
83
 
84
- # Reinitializes config with a copy of orig.config (this assures
85
- # that duplicates have their own copy of configurations,
86
- # separate from the original object).
84
+ # Reinitializes configurations in the copy such that
85
+ # the new object has it's own set of configurations,
86
+ # separate from the original object.
87
87
  def initialize_copy(orig)
88
88
  super
89
89
  initialize_config(orig.config)
@@ -91,9 +91,8 @@ module Tap
91
91
 
92
92
  protected
93
93
 
94
- # Initializes config to an InstanceConfiguration specific for self.
95
- # Default config values are assigned or overridden if specified in
96
- # overrides. Override keys are symbolized.
94
+ # Initializes config to an InstanceConfiguration. Default config values
95
+ # are overridden as specified by overrides. Keys are symbolized.
97
96
  def initialize_config(overrides={})
98
97
  class_config = self.class.configurations
99
98
  @config = class_config.instance_config
@@ -7,9 +7,9 @@ module Tap
7
7
  module Support
8
8
  autoload(:Templater, 'tap/support/templater')
9
9
 
10
- # ConfigurableClass encapsulates class methods used to declare class configurations.
11
- # When configurations are declared using the config method, ConfigurableClass
12
- # generates accessors in the class, much like attr_accessor.
10
+ # ConfigurableClass defines class methods used by a Configurable class to
11
+ # declare configurations. In addition to registering key-value pairs,
12
+ # these methods generate readers and writers, much like attr_accessor.
13
13
  #
14
14
  # class ConfigurableClass
15
15
  # extend ConfigurableClass
@@ -22,31 +22,6 @@ module Tap
22
22
  # c.respond_to?('one') # => true
23
23
  # c.respond_to?('one=') # => true
24
24
  #
25
- # If a block is given, the block will be used to create the writer method
26
- # for the config. Used in this manner, config defines a <tt>config_key=</tt> method
27
- # wherein <tt>@config_key</tt> will be set to the return value of the block.
28
- #
29
- # class AnotherConfigurableClass
30
- # extend ConfigurableClass
31
- # config(:one, 'one') {|value| value.upcase }
32
- # end
33
- #
34
- # ac = AnotherConfigurableClass.new
35
- # ac.one = 'value'
36
- # ac.one # => 'VALUE'
37
- #
38
- # The block has class-context in this case. To have instance-context, use the
39
- # config_attr method which defines the writer method using the block directly.
40
- #
41
- # class YetAnotherConfigurableClass
42
- # extend ConfigurableClass
43
- # config_attr(:one, 'one') {|value| @one = value.reverse }
44
- # end
45
- #
46
- # ac = YetAnotherConfigurableClass.new
47
- # ac.one = 'value'
48
- # ac.one # => 'eulav'
49
- #
50
25
  module ConfigurableClass
51
26
  include Tap::Support::LazyAttributes
52
27
 
@@ -54,7 +29,7 @@ module Tap
54
29
  attr_reader :configurations
55
30
 
56
31
  # Sets the source_file for base and initializes base.configurations.
57
- def self.extended(base)
32
+ def self.extended(base) # :nodoc:
58
33
  caller.each_with_index do |line, index|
59
34
  case line
60
35
  when /\/configurable.rb/ then next
@@ -70,7 +45,7 @@ module Tap
70
45
  # When subclassed, the parent.configurations are duplicated and passed to
71
46
  # the child class where they can be extended/modified without affecting
72
47
  # the configurations of the parent class.
73
- def inherited(child)
48
+ def inherited(child) # :nodoc:
74
49
  unless child.instance_variable_defined?(:@source_file)
75
50
  caller.first =~ Lazydoc::CALLER_REGEXP
76
51
  child.instance_variable_set(:@source_file, File.expand_path($1))
@@ -80,6 +55,7 @@ module Tap
80
55
  super
81
56
  end
82
57
 
58
+ # Returns the lazydoc for self.
83
59
  def lazydoc(resolve=true)
84
60
  Lazydoc.resolve_comments(configurations.code_comments) if resolve
85
61
  super
@@ -1,8 +1,12 @@
1
1
  module Tap
2
2
  module Support
3
+
4
+ # Represents a configuration declared by a Configurable class.
3
5
  class Configuration
4
6
  class << self
5
- SHORT_REGEXP = /^-[A-z]$/
7
+
8
+ # Matches a short option
9
+ SHORT_OPTION = /^-[A-z]$/
6
10
 
7
11
  # Turns the input string into a short-format option. Raises
8
12
  # an error if the option does not match SHORT_REGEXP.
@@ -13,11 +17,12 @@ module Tap
13
17
  def shortify(str)
14
18
  str = str.to_s
15
19
  str = "-#{str}" unless str[0] == ?-
16
- raise "invalid short option: #{str}" unless str =~ SHORT_REGEXP
20
+ raise "invalid short option: #{str}" unless str =~ SHORT_OPTION
17
21
  str
18
22
  end
19
-
20
- LONG_REGEXP = /^--(\[no-\])?([A-z][\w-]*)$/
23
+
24
+ # Matches a long option
25
+ LONG_OPTION = /^--(\[no-\])?([A-z][\w-]*)$/
21
26
 
22
27
  # Turns the input string into a long-format option. Raises
23
28
  # an error if the option does not match LONG_REGEXP.
@@ -33,7 +38,7 @@ module Tap
33
38
  str = "--#{str}" unless str.index("--")
34
39
  str.gsub!(/_/, '-') if hyphenize
35
40
 
36
- raise "invalid long option: #{str}" unless str =~ LONG_REGEXP
41
+ raise "invalid long option: #{str}" unless str =~ LONG_OPTION
37
42
 
38
43
  if switch_notation && $1.nil?
39
44
  str = "--[no-]#{$2}"
@@ -43,12 +48,24 @@ module Tap
43
48
  end
44
49
  end
45
50
 
51
+ # The name of the configuration
46
52
  attr_reader :name
53
+
54
+ # The reader method, by default name
47
55
  attr_reader :reader
56
+
57
+ # The writer method, by default name=
48
58
  attr_reader :writer
59
+
60
+ # True if the default value may be duplicated
49
61
  attr_reader :duplicable
62
+
63
+ # An array of optional metadata for self
50
64
  attr_reader :attributes
51
-
65
+
66
+ # Initializes a new Configuration with the specified name and default
67
+ # value. Options may specify an alternate reader/writer; any
68
+ # additional options are set as attributes.
52
69
  def initialize(name, default=nil, options={})
53
70
  @name = name
54
71
  self.default = default
@@ -59,11 +76,12 @@ module Tap
59
76
  end
60
77
 
61
78
  # Sets the default value for self and determines if the
62
- # default is duplicable (ie not nil, true, false, Symbol,
63
- # Numeric, and responds_to?(:dup)).
79
+ # default is duplicable. Non-duplicable values include
80
+ # nil, true, false, Symbol, Numeric, and any object that
81
+ # does not respond to dup.
64
82
  def default=(value)
65
83
  @duplicable = case value
66
- when nil, true, false, Symbol, Numeric then false
84
+ when nil, true, false, Symbol, Numeric, Method then false
67
85
  else value.respond_to?(:dup)
68
86
  end
69
87
 
@@ -88,22 +106,29 @@ module Tap
88
106
  @writer = value == nil ? value : value.to_sym
89
107
  end
90
108
 
109
+ # The argument name for self: either attributes[:arg_name]
110
+ # or name.to_s.upcase
91
111
  def arg_name
92
112
  attributes[:arg_name] || name.to_s.upcase
93
113
  end
94
114
 
115
+ # The argument type for self: either attributes[:arg_type]
116
+ # or :mandatory
95
117
  def arg_type
96
118
  attributes[:arg_type] || :mandatory
97
119
  end
98
120
 
121
+ # The long version of name.
99
122
  def long(switch_notation=false, hyphenize=true)
100
123
  Configuration.longify(attributes[:long] || name.to_s, switch_notation, hyphenize)
101
124
  end
102
125
 
126
+ # The short version of name.
103
127
  def short
104
128
  attributes[:short] ? Configuration.shortify(attributes[:short]) : nil
105
129
  end
106
130
 
131
+ # The description for self: attributes[:desc]
107
132
  def desc
108
133
  attributes[:desc]
109
134
  end
@@ -119,6 +144,8 @@ module Tap
119
144
  self.default(false) == another.default(false)
120
145
  end
121
146
 
147
+ # Returns self as an argv that can be used to register
148
+ # an option with OptionParser.
122
149
  def to_optparse_argv
123
150
  argtype = case arg_type
124
151
  when :optional
@@ -1,18 +1,79 @@
1
- require 'tap/support/constant_utils'
2
- class String # :nodoc:
3
- include Tap::Support::ConstantUtils
4
- end
1
+ require 'tap/support/string_ext'
5
2
 
6
3
  module Tap
7
4
  module Support
5
+
6
+ # A Constant serves as a placeholder for an actual constant, sort of like
7
+ # autoload. Use the constantize method to retrieve the actual constant;
8
+ # if it doesn't exist, constantize requires require_path and tries again.
9
+ #
10
+ # Object.const_defined?(:Net) # => false
11
+ # $".include?('net/http') # => false
12
+ #
13
+ # http = Constant.new('Net::HTTP', 'net/http')
14
+ # http.constantize # => Net::HTTP
15
+ # $".include?('net/http') # => true
16
+ #
8
17
  class Constant
18
+ class << self
19
+
20
+ # Tries to find a declared constant under base with the specified
21
+ # const_name. When a constant is missing, constantize yields
22
+ # the current base and any non-existant constant names the block,
23
+ # if given, or raises a NameError. The block is expected
24
+ # to return the proper constant.
25
+ #
26
+ # module ConstName; end
27
+ #
28
+ # Constant.constantize('ConstName') # => ConstName
29
+ # Constant.constantize('Non::Existant') { ConstName } # => ConstName
30
+ #
31
+ def constantize(const_name, base=Object) # :yields: base, missing_const_names
32
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ const_name
33
+ raise NameError, "#{const_name.inspect} is not a valid constant name!"
34
+ end
35
+
36
+ constants = $1.split(/::/)
37
+ while !constants.empty?
38
+ unless const_is_defined?(base, constants[0])
39
+ if block_given?
40
+ return yield(base, constants)
41
+ else
42
+ raise NameError.new("uninitialized constant #{const_name}", constants[0])
43
+ end
44
+ end
45
+ base = base.const_get(constants.shift)
46
+ end
47
+ base
48
+ end
49
+
50
+ private
51
+
52
+ # helper method. Determines if a constant named
53
+ # name is defined in const. The implementation
54
+ # (annoyingly) has to be different for ruby 1.9
55
+ # due to changes in the API.
56
+ case RUBY_VERSION
57
+ when /^1.9/
58
+ def const_is_defined?(const, name) # :nodoc:
59
+ const.const_defined?(name, false)
60
+ end
61
+ else
62
+ def const_is_defined?(const, name) # :nodoc:
63
+ const.const_defined?(name)
64
+ end
65
+ end
66
+ end
9
67
 
10
- # The camelized name for self.
68
+ # The constant name
11
69
  attr_reader :name
12
70
 
13
- # The path to load to initialize the constant name.
71
+ # The path to load to initialize a missing constant
14
72
  attr_reader :require_path
15
-
73
+
74
+ # Initializes a new Constant with the specified constant
75
+ # name and require_path. The name should be a valid
76
+ # constant name.
16
77
  def initialize(name, require_path=nil)
17
78
  @name = name
18
79
  @require_path = require_path
@@ -48,9 +109,9 @@ module Tap
48
109
  @nesting_depth ||= nesting.split(/::/).length
49
110
  end
50
111
 
51
- # Returns the document for require_path, if set, or nil otherwise.
112
+ # Returns the Lazydoc document for require_path.
52
113
  def document
53
- require_path ? Support::Lazydoc[require_path] : nil
114
+ require_path ? Lazydoc[require_path] : nil
54
115
  end
55
116
 
56
117
  # True if another is a Constant with the same name
@@ -63,12 +124,13 @@ module Tap
63
124
 
64
125
  # Looks up and returns the constant indicated by name.
65
126
  # If the constant cannot be found, the constantize
66
- # requires require_path and tries again. Raises an
67
- # NameError if the constant cannot be found.
127
+ # requires require_path and tries again.
128
+ #
129
+ # Raises a NameError if the constant cannot be found.
68
130
  def constantize
69
- name.try_constantize do |const_name|
131
+ Constant.constantize(name) do
70
132
  require require_path if require_path
71
- name.constantize
133
+ Constant.constantize(name)
72
134
  end
73
135
  end
74
136