tap 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,9 +8,9 @@ module Tap
8
8
 
9
9
  # Returns the default name for the class: to_s.underscore
10
10
  attr_accessor :default_name
11
-
11
+
12
12
  def self.extended(base)
13
- caller.each_with_index do |line, index|
13
+ caller.each do |line|
14
14
  case line
15
15
  when /\/framework.rb/ then next
16
16
  when /^(([A-z]:)?[^:]+):(\d+)/
@@ -66,7 +66,8 @@ module Tap
66
66
  when /\/tap\/support\/declarations.rb/ then next
67
67
  when /^(([A-z]:)?[^:]+):(\d+)/
68
68
  subclass.source_file = File.expand_path($1)
69
- subclass.lazydoc[const_name, false]['manifest'] = subclass.lazydoc.register($3.to_i - 1)
69
+ lzd = subclass.lazydoc(false)
70
+ lzd[const_name, false]['manifest'] = lzd.register($3.to_i - 1)
70
71
  break
71
72
  end
72
73
  end
@@ -82,13 +83,14 @@ module Tap
82
83
  array.join(' ')
83
84
  else ""
84
85
  end
85
- subclass.lazydoc[const_name, false]['args'] ||= comment
86
+ subclass.lazydoc(false)[const_name, false]['args'] ||= comment
86
87
 
87
88
  # Set the subclass constant
88
89
  current.const_set(subclass_const, subclass)
89
90
  end
90
91
 
91
- DEFAULT_HELP_TEMPLATE = %Q{<%= task_class %><%= manifest.subject.to_s.strip.empty? ? '' : ' -- ' %><%= manifest.subject %>
92
+ DEFAULT_HELP_TEMPLATE = %Q{<% manifest = task_class.manifest %>
93
+ <%= task_class %><%= manifest.subject.to_s.strip.empty? ? '' : ' -- ' %><%= manifest.subject %>
92
94
 
93
95
  <% unless manifest.empty? %>
94
96
  <%= '-' * 80 %>
@@ -122,8 +124,6 @@ module Tap
122
124
  opts.separator "options:"
123
125
 
124
126
  opts.on_tail("-h", "--help", "Print this help") do
125
- args = lazydoc(true)[to_s]['args'] || Tap::Support::Comment.new
126
-
127
127
  opts.banner = "#{help}usage: tap run -- #{to_s.underscore} #{args.subject}"
128
128
  puts opts
129
129
  exit
@@ -166,33 +166,14 @@ module Tap
166
166
  [obj.reconfigure(path_configs).reconfigure(config), argv + use_args]
167
167
  end
168
168
 
169
- def lazydoc(resolve=false, args_method=:process)
170
- if resolve
171
- lazydoc = super(false)
172
- lazydoc.resolve(nil, /^\s*def\s+#{args_method}(\((.*?)\))?/) do |comment, match|
173
- comment.subject = match[2].to_s.split(',').collect do |arg|
174
- arg = arg.strip.upcase
175
- case arg
176
- when /^&/ then nil
177
- when /^\*/ then arg[1..-1] + "..."
178
- else arg
179
- end
180
- end.join(', ')
181
-
182
- lazydoc.default_attributes['args'] ||= comment
183
- end
184
-
185
- super(true)
186
- else
187
- super(false)
188
- end
169
+ def lazydoc(resolve=true)
170
+ lazydoc = super(false)
171
+ lazydoc.register_method_pattern('args', :process) unless lazydoc.resolved?
172
+ super
189
173
  end
190
-
174
+
191
175
  def help
192
- Tap::Support::Templater.new(DEFAULT_HELP_TEMPLATE,
193
- :task_class => self,
194
- :manifest => lazydoc(true)[to_s]['manifest'] || Tap::Support::Comment.new
195
- ).build
176
+ Tap::Support::Templater.new(DEFAULT_HELP_TEMPLATE, :task_class => self).build
196
177
  end
197
178
  end
198
179
  end
@@ -0,0 +1,63 @@
1
+ autoload(:Gem, 'rubygems')
2
+
3
+ module Tap
4
+ module Support
5
+ module Gems
6
+ module_function
7
+
8
+ # Finds the home directory for the user (method taken from Rubygems).
9
+ def find_home
10
+ ['HOME', 'USERPROFILE'].each do |homekey|
11
+ return ENV[homekey] if ENV[homekey]
12
+ end
13
+
14
+ if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
15
+ return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
16
+ end
17
+
18
+ begin
19
+ File.expand_path("~")
20
+ rescue
21
+ if File::ALT_SEPARATOR then
22
+ "C:/"
23
+ else
24
+ "/"
25
+ end
26
+ end
27
+ end
28
+
29
+ # The home directory for the user.
30
+ def user_home
31
+ @user_home ||= find_home
32
+ end
33
+
34
+ # Returns the gemspec for the specified gem. A gem version
35
+ # can be specified in the name, like 'gem >= 1.2'. The gem
36
+ # will be activated using +gem+ if necessary.
37
+ def gemspec(gem_name)
38
+ return gem_name if gem_name.kind_of?(Gem::Specification)
39
+
40
+ # figure the version of the gem, by default >= 0.0.0
41
+ gem_name.to_s =~ /^([^<=>]*)(.*)$/
42
+ name, version = $1.strip, $2
43
+ version = ">= 0.0.0" if version.empty?
44
+
45
+ return nil if name.empty?
46
+
47
+ # load the gem and get the spec
48
+ gem(name, version)
49
+ Gem.loaded_specs[name]
50
+ end
51
+
52
+ def select_gems(latest=true)
53
+ index = latest ?
54
+ Gem.source_index.latest_specs :
55
+ Gem.source_index.gems.collect {|(name, spec)| spec }
56
+
57
+ index.select do |spec|
58
+ yield(spec)
59
+ end.sort
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,90 @@
1
+ require 'rake'
2
+ require 'tap'
3
+
4
+ module Tap
5
+ module Support
6
+ module Gems
7
+ class RakeManifest < Support::Manifest
8
+ def initialize(env)
9
+ @env = env
10
+ rake = ::Rake.application
11
+ super(rake.have_rakefile(env.root.root) ? [rake.instance_variable_get(:@rakefile)] : [])
12
+ end
13
+ end
14
+
15
+ module Rake
16
+
17
+ def self.extended(base)
18
+ Tap::Env.instance_for(Dir.pwd).activate unless Tap::Env.instance
19
+ base.env = Tap::Env.instance
20
+ end
21
+
22
+ attr_accessor :env
23
+
24
+ def collect_tasks
25
+ ARGV.collect! do |arg|
26
+ next(arg) unless arg =~ /^:([a-z_\d]+):(.*)$/
27
+ env_pattern = $1
28
+ rake_task = $2
29
+
30
+ next(arg) unless entry = env.find(:envs, env_pattern, false)
31
+
32
+ mini_path, env = entry
33
+ root_path = env.root.root
34
+
35
+ if have_rakefile(root_path)
36
+ # load sequence echos that in raw_load_rakefile
37
+ puts "(in #{root_path})" unless options.silent
38
+ current_global_rakefile = $rakefile
39
+ $rakefile = @rakefile
40
+
41
+ namespaces = Tap::Root.split(mini_path, false).delete_if do |segment|
42
+ segment.empty?
43
+ end
44
+
45
+ #if @rakefile != ''
46
+ eval nest_namespace(%Q{load "#{File.join(root_path, @rakefile)}"}, namespaces.dup)
47
+ #end
48
+
49
+ $rakefile = current_global_rakefile
50
+ @rakefile = nil
51
+
52
+ namespaces << rake_task
53
+ namespaces.join(":")
54
+ else
55
+ fail "No Rakefile found for '#{env_pattern}' (looking for: #{@rakefiles.join(', ')})"
56
+ end
57
+ end
58
+
59
+ super
60
+ end
61
+
62
+ def have_rakefile(indir=nil)
63
+ return super() if indir == nil
64
+ Tap::Root.indir(indir) { super() }
65
+ end
66
+
67
+ protected
68
+
69
+ NAMESPACE_STR = %Q{
70
+ namespace(:'%s') do
71
+ %s
72
+ end
73
+ }.strip
74
+
75
+ def nest_namespace(nest_str, namespaces)
76
+ return nest_str if namespaces.empty?
77
+
78
+ NAMESPACE_STR % [
79
+ namespaces.shift,
80
+ namespaces.empty? ? nest_str : nest_namespace(nest_str, namespaces)
81
+ ]
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ Rake.application.extend Tap::Support::Gems::Rake
89
+ Tap::Env.manifests[:rakefiles] = Tap::Support::RakeManifest
90
+
@@ -41,9 +41,9 @@ module Tap
41
41
  # The ClassConfiguration specifying config keys
42
42
  attr_reader :class_config
43
43
 
44
- def initialize(class_config, receiver=nil)
44
+ def initialize(class_config, receiver=nil, store={})
45
45
  @receiver = receiver
46
- @store = {}
46
+ @store = store
47
47
  @class_config = class_config
48
48
  end
49
49
 
@@ -54,7 +54,7 @@ module Tap
54
54
  raise ArgumentError.new("receiver cannot be nil") if receiver == nil
55
55
 
56
56
  class_config.each_pair do |key, config|
57
- receiver.send(config.writer, store.delete(key))
57
+ receiver.send(config.writer, store.delete(key)) if config.writer
58
58
  end
59
59
  @receiver = receiver
60
60
 
@@ -70,7 +70,7 @@ module Tap
70
70
  # are stored in store. Returns the unbound receiver.
71
71
  def unbind
72
72
  class_config.each_pair do |key, config|
73
- store[key] = receiver.send(config.reader)
73
+ store[key] = receiver.send(config.reader) if config.reader
74
74
  end
75
75
  r = receiver
76
76
  @receiver = nil
@@ -92,7 +92,7 @@ module Tap
92
92
  def []=(key, value)
93
93
  case
94
94
  when bound? && config = class_config.map[key.to_sym]
95
- receiver.send(config.writer, value)
95
+ config.writer ? receiver.send(config.writer, value) : store[key] = value
96
96
  else store[key] = value
97
97
  end
98
98
  end
@@ -103,7 +103,7 @@ module Tap
103
103
  def [](key)
104
104
  case
105
105
  when bound? && config = class_config.map[key.to_sym]
106
- receiver.send(config.reader)
106
+ config.reader ? receiver.send(config.reader) : store[key]
107
107
  else store[key]
108
108
  end
109
109
  end
@@ -116,7 +116,7 @@ module Tap
116
116
  # Calls block once for each key-value pair stored in self.
117
117
  def each_pair # :yields: key, value
118
118
  class_config.each_pair do |key, config|
119
- yield(key, receiver.send(config.reader))
119
+ yield(key, receiver.send(config.reader)) if config.reader
120
120
  end if bound?
121
121
 
122
122
  store.each_pair do |key, value|
@@ -126,7 +126,7 @@ module Tap
126
126
 
127
127
  # Equal if the to_hash values of self and another are equal.
128
128
  def ==(another)
129
- to_hash == another.to_hash
129
+ another.respond_to?(:to_hash) && to_hash == another.to_hash
130
130
  end
131
131
 
132
132
  # Returns self as a hash.
@@ -0,0 +1,30 @@
1
+ require 'tap/support/lazydoc'
2
+
3
+ module Tap
4
+ module Support
5
+ module LazyAttributes
6
+
7
+ # The source_file for self. Must be set independently.
8
+ attr_accessor :source_file
9
+
10
+ # Returns the lazydoc for source_file
11
+ def lazydoc(resolve=true)
12
+ lazydoc = Lazydoc[source_file]
13
+ lazydoc.resolve if resolve
14
+ lazydoc
15
+ end
16
+
17
+ # Creates a lazy attribute reader for the specified attribute.
18
+ def lazy_attr(key, attribute=key)
19
+ instance_eval %Q{def #{key}; @#{key} ||= get_lazy_attr('#{attribute}'); end}
20
+ end
21
+
22
+ private
23
+
24
+ def get_lazy_attr(attribute)
25
+ lazydoc[self.to_s][attribute] || (lazydoc.attributes(self.to_s)[attribute] = Tap::Support::Comment.new)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -10,8 +10,7 @@ module Tap
10
10
  #
11
11
  # Constant attributes are designated the same as constants in Ruby, but with
12
12
  # an extra 'key' constant that must consist of only lowercase letters and/or
13
- # underscores. This format assures that attributes are sytactically invalid
14
- # outside of comments.
13
+ # underscores. Attributes are only parsed from comment lines.
15
14
  #
16
15
  # When Lazydoc finds an attribute it parses a Comment value where the subject
17
16
  # is the remainder of the line, and comment lines are parsed down until a
@@ -49,6 +48,8 @@ module Tap
49
48
  # # Const::Name::not_parsed
50
49
  # # :::+
51
50
  #
51
+ # Const::Name::not_parsed
52
+ #
52
53
  # # Const::Name::parsed subject
53
54
  # }
54
55
  #
@@ -127,10 +128,10 @@ module Tap
127
128
  # $3:: key
128
129
  # $4:: end flag
129
130
  #
130
- ATTRIBUTE_REGEXP = /(::|([A-Z][A-z]*::)+)([a-z_]+)(-?)/
131
-
132
- # A regexp matching constants.
133
- CONSTANT_REGEXP = /(::|([A-Z][A-z]*::)+)/
131
+ ATTRIBUTE_REGEXP = /([A-Z][A-z]*(::[A-Z][A-z]*)*)?::([a-z_]+)(-?)/
132
+
133
+ # A regexp matching constants from the ATTRIBUTE_REGEXP leader
134
+ CONSTANT_REGEXP = /#.*?([A-Z][A-z]*(::[A-Z][A-z]*)*)?$/
134
135
 
135
136
  class << self
136
137
 
@@ -159,7 +160,7 @@ module Tap
159
160
  end
160
161
 
161
162
  # Resolves all lazydocs which include the specified code comments.
162
- def resolve(code_comments)
163
+ def resolve_comments(code_comments)
163
164
  registry.each do |doc|
164
165
  next if (code_comments & doc.code_comments).empty?
165
166
  doc.resolve
@@ -185,14 +186,16 @@ module Tap
185
186
  # and <tt>:+</tt>.
186
187
  #
187
188
  # str = %Q{
188
- # Const::Name::key value
189
- # ::alt alt_value
189
+ # # Const::Name::key value
190
+ # # ::alt alt_value
191
+ # #
192
+ # # Ignored::Attribute::not_matched value
193
+ # # :::-
194
+ # # Also::Ignored::key value
195
+ # # :::+
196
+ # # Another::key another value
190
197
  #
191
- # Ignored::Attribute::not_matched value
192
- # :::-
193
- # Also::Ignored::key value
194
- # :::+
195
- # Another::key another value
198
+ # Ignored::key value
196
199
  # }
197
200
  #
198
201
  # results = []
@@ -213,17 +216,17 @@ module Tap
213
216
  when String then StringScanner.new(str)
214
217
  else raise TypeError, "can't convert #{str.class} into StringScanner or String"
215
218
  end
216
-
217
- regexp = /(#{key})([ \r\t-].*$|$)/
219
+
220
+ regexp = /^(.*?)::(:-|#{key})/
218
221
  while !scanner.eos?
219
- break if scanner.skip_until(CONSTANT_REGEXP) == nil
220
- const_name = scanner[1]
221
-
222
- case
223
- when scanner.scan(regexp)
224
- yield(const_name.chomp('::'), scanner[1], scanner[2].strip)
225
- when scanner.scan(/:-/)
226
- scanner.skip_until(/:\+/)
222
+ break if scanner.skip_until(regexp) == nil
223
+
224
+ if scanner[2] == ":-"
225
+ scanner.skip_until(/:::\+/)
226
+ else
227
+ next unless scanner[1] =~ CONSTANT_REGEXP
228
+ key = scanner[2]
229
+ yield($1.to_s, key, scanner.matched.strip) if scanner.scan(/[ \r\t].*$|$/)
227
230
  end
228
231
  end
229
232
 
@@ -267,9 +270,9 @@ module Tap
267
270
 
268
271
  scan(scanner, '[a-z_]+') do |const_name, key, value|
269
272
  comment = Comment.parse(scanner, false) do |line|
270
- if line =~ /::/ && line =~ ATTRIBUTE_REGEXP
273
+ if line =~ ATTRIBUTE_REGEXP
271
274
  # rewind to capture the next attribute unless an end is specified.
272
- scanner.unscan unless !$4.empty? && $1.chomp("::") == const_name && $3 == key
275
+ scanner.unscan unless $4 == '-' && $3 == key && $1.to_s == const_name
273
276
  true
274
277
  else false
275
278
  end
@@ -294,10 +297,13 @@ module Tap
294
297
  # attributes resolved or to-be-resolved for self. Attributes
295
298
  # are hashes of (key, comment) pairs.
296
299
  attr_reader :const_attrs
297
-
300
+
301
+ attr_reader :patterns
302
+
298
303
  def initialize(source_file=nil)
299
304
  self.source_file = source_file
300
305
  @code_comments = []
306
+ @patterns = {}
301
307
  @const_attrs = {}
302
308
  @resolved = false
303
309
  end
@@ -364,6 +370,25 @@ module Tap
364
370
  comment
365
371
  end
366
372
 
373
+ def register_pattern(key, regexp, &block) # :yields: comment, match
374
+ patterns[key] = [regexp, block]
375
+ end
376
+
377
+ def register_method_pattern(key, method, range=0..-1)
378
+ register_pattern(key, /^\s*def\s+#{method}(\((.*?)\))?/) do |comment, match|
379
+ args = match[2].to_s.split(',').collect do |arg|
380
+ arg = arg.strip.upcase
381
+ case arg
382
+ when /^&/ then nil
383
+ when /^\*/ then arg[1..-1] + "..."
384
+ else arg
385
+ end
386
+ end
387
+
388
+ comment.subject = args[range].join(', ')
389
+ end
390
+ end
391
+
367
392
  # Returns true if the code_comments for source_file are frozen.
368
393
  def resolved?
369
394
  @resolved
@@ -371,7 +396,7 @@ module Tap
371
396
 
372
397
  attr_writer :resolved
373
398
 
374
- def resolve(str=nil, comment_regexp=nil) # :yields: comment, match
399
+ def resolve(str=nil)
375
400
  return(false) if resolved?
376
401
 
377
402
  if str == nil
@@ -384,11 +409,18 @@ module Tap
384
409
  end
385
410
 
386
411
  lines = str.split(/\r?\n/)
387
- lines.each_with_index do |line, line_number|
388
- next unless line =~ comment_regexp
389
- comment = register(line_number)
390
- yield(comment, $~) if block_given?
391
- end unless comment_regexp == nil
412
+
413
+ patterns.each_pair do |key, (regexp, block)|
414
+ next if default_attributes.has_key?(key)
415
+
416
+ lines.each_with_index do |line, line_number|
417
+ next unless line =~ regexp
418
+
419
+ comment = register(line_number)
420
+ default_attributes[key] = comment
421
+ break if block.call(comment, $~)
422
+ end
423
+ end unless patterns.empty?
392
424
 
393
425
  code_comments.collect! do |comment|
394
426
  line_number = comment.line_number