tap 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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