C0 code coverage information

Generated on Wed Oct 07 08:34:04 -0700 2009 with rcov 0.8.2.1


Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
Name Total lines Lines of code Total coverage Code coverage
lib/buildr/packaging/artifact.rb 749 366
98.7%  
97.3%  
  1 # Licensed to the Apache Software Foundation (ASF) under one or more
  2 # contributor license agreements.  See the NOTICE file distributed with this
  3 # work for additional information regarding copyright ownership.  The ASF
  4 # licenses this file to you under the Apache License, Version 2.0 (the
  5 # "License"); you may not use this file except in compliance with the License.
  6 # You may obtain a copy of the License at
  7 #
  8 #    http://www.apache.org/licenses/LICENSE-2.0
  9 #
 10 # Unless required by applicable law or agreed to in writing, software
 11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 13 # License for the specific language governing permissions and limitations under
 14 # the License.
 15 
 16 
 17 require 'buildr/core/project'
 18 require 'buildr/core/transports'
 19 require 'buildr/packaging/artifact_namespace'
 20 
 21 
 22 module Buildr
 23 
 24   desc 'Download all artifacts'
 25   task 'artifacts'
 26 
 27   desc "Download all artifacts' sources"
 28   task 'artifacts:sources'
 29 
 30   # Mixin with a task to make it behave like an artifact. Implemented by the packaging tasks.
 31   #
 32   # An artifact has an identifier, group identifier, type, version number and
 33   # optional classifier. All can be used to locate it in the local repository,
 34   # download from or upload to a remote repository.
 35   #
 36   # The #to_spec and #to_hash methods allow it to be used everywhere an artifact is
 37   # accepted.
 38   module ActsAsArtifact
 39 
 40     ARTIFACT_ATTRIBUTES = [:group, :id, :type, :classifier, :version]
 41 
 42     class << self
 43     private
 44       def included(mod)
 45         mod.extend self
 46       end
 47     end
 48 
 49     # The artifact identifier.
 50     attr_reader :id
 51     # The group identifier.
 52     attr_reader :group
 53     # The file type. (Symbol)
 54     attr_reader :type
 55     # The version number.
 56     attr_reader :version
 57     # Optional artifact classifier.
 58     attr_reader :classifier
 59 
 60     def snapshot?
 61       version =~ /-SNAPSHOT$/
 62     end
 63 
 64     # :call-seq:
 65     #   to_spec_hash => Hash
 66     #
 67     # Returns the artifact specification as a hash. For example:
 68     #   com.example:app:jar:1.2
 69     # becomes:
 70     #   { :group=>'com.example',
 71     #     :id=>'app',
 72     #     :type=>:jar,
 73     #     :version=>'1.2' }
 74     def to_spec_hash
 75       base = { :group=>group, :id=>id, :type=>type, :version=>version }
 76       classifier ? base.merge(:classifier=>classifier) : base
 77     end
 78     alias_method :to_hash, :to_spec_hash
 79 
 80     # :call-seq:
 81     #   to_spec => String
 82     #
 83     # Returns the artifact specification, in the structure:
 84     #   <group>:<artifact>:<type>:<version>
 85     # or
 86     #   <group>:<artifact>:<type>:<classifier>:<version>
 87     def to_spec
 88       classifier ? "#{group}:#{id}:#{type}:#{classifier}:#{version}" : "#{group}:#{id}:#{type}:#{version}"
 89     end
 90 
 91     # :call-seq:
 92     #   pom => Artifact
 93     # 
 94     # Convenience method that returns a POM artifact.
 95     def pom
 96       return self if type == :pom
 97       Buildr.artifact(:group=>group, :id=>id, :version=>version, :type=>:pom)
 98     end
 99 
100     # :call-seq:
101     #   sources_artifact => Artifact
102     # 
103     # Convenience method that returns a sources artifact.
104     def sources_artifact
105       sources_spec = to_spec_hash.merge(:classifier=>'sources')
106       sources_task = OptionalArtifact.define_task(Buildr.repositories.locate(sources_spec))
107       sources_task.send :apply_spec, sources_spec
108       sources_task
109     end
110 
111     # :call-seq:
112     #   pom_xml => string
113     #
114     # Creates POM XML for this artifact.
115     def pom_xml
116       xml = Builder::XmlMarkup.new(:indent=>2)
117       xml.instruct!
118       xml.project do
119         xml.modelVersion  '4.0.0'
120         xml.groupId       group
121         xml.artifactId    id
122         xml.version       version
123         xml.classifier    classifier if classifier
124       end
125     end
126     
127     def install
128       pom.install if pom && pom != self
129       invoke
130       installed = Buildr.repositories.locate(self)
131       unless installed == name # If not already in local repository.
132         mkpath File.dirname(installed)
133         cp name, installed
134         info "Installed #{installed}"
135       end
136     end
137 
138     def uninstall
139       installed = Buildr.repositories.locate(self)
140       rm installed if File.exist?(installed) 
141       pom.uninstall if pom && pom != self
142     end
143 
144     # :call-seq:
145     #   upload
146     #   upload(url)
147     #   upload(options)
148     #
149     # Uploads the artifact, its POM and digital signatures to remote server.
150     #
151     # In the first form, uses the upload options specified by repositories.release_to.
152     # In the second form, uses a URL that includes all the relevant information.
153     # In the third form, uses a hash with the options :url, :username, :password,
154     # and :permissions. All but :url are optional.
155     def upload(upload_to = nil)
156       # Where do we release to?
157       upload_to ||= Buildr.repositories.release_to
158       upload_to = { :url=>upload_to } unless Hash === upload_to
159       raise ArgumentError, 'Don\'t know where to upload, perhaps you forgot to set repositories.release_to' unless upload_to[:url]
160       invoke # Make sure we exist.
161 
162       # Upload POM ahead of package, so we don't fail and find POM-less package (the horror!)
163       pom.upload(upload_to) if pom && pom != self
164 
165       # Set the upload URI, including mandatory slash (we expect it to be the base directory).
166       # Username/password may be part of URI, or separate entities.
167       uri = URI.parse(upload_to[:url].clone)
168       uri.path = uri.path + '/' unless uri.path[-1] == '/'
169       uri.user = upload_to[:username] if upload_to[:username]
170       uri.password = upload_to[:password] if upload_to[:password]
171 
172       # Upload artifact relative to base URL, need to create path before uploading.
173       info "Deploying #{to_spec}"
174       path = group.gsub('.', '/') + "/#{id}/#{version}/#{File.basename(name)}"
175       URI.upload uri + path, name, :permissions=>upload_to[:permissions]
176     end
177 
178   protected
179 
180     # Apply specification to this artifact.
181     def apply_spec(spec)
182       spec = Artifact.to_hash(spec)
183       ARTIFACT_ATTRIBUTES.each { |key| instance_variable_set("@#{key}", spec[key]) }
184       self
185     end
186     
187     def group_path
188       group.gsub('.', '/')
189     end
190 
191   end
192 
193  
194   # A file task referencing an artifact in the local repository.
195   #
196   # This task includes all the artifact attributes (group, id, version, etc). It points
197   # to the artifact's path in the local repository. When invoked, it will download the
198   # artifact into the local repository if the artifact does not already exist.
199   #
200   # Note: You can enhance this task to create the artifact yourself, e.g. download it from
201   # a site that doesn't have a remote repository structure, copy it from a different disk, etc.
202   class Artifact < Rake::FileCreationTask
203 
204     # The default artifact type.
205     DEFAULT_TYPE = :jar
206 
207     include ActsAsArtifact
208 
209     class << self
210 
211       # :call-seq:
212       #   lookup(spec) => Artifact
213       #
214       # Lookup a previously registered artifact task based on its specification (String or Hash).
215       def lookup(spec)
216         @artifacts ||= {}
217         @artifacts[to_spec(spec)]
218       end
219 
220       # :call-seq:
221       #   list => specs
222       #
223       # Returns an array of specs for all the registered artifacts. (Anything created from artifact, or package).
224       def list
225         @artifacts ||= {}
226         @artifacts.keys
227       end
228 
229       # :call-seq:
230       #   register(artifacts) => artifacts
231       #
232       # Register an artifact task(s) for later lookup (see #lookup).
233       def register(*tasks)
234         @artifacts ||= {}
235         fail 'You can only register an artifact task, one of the arguments is not a Task that responds to to_spec' unless
236           tasks.all? { |task| task.respond_to?(:to_spec) && task.respond_to?(:invoke) }
237         tasks.each { |task| @artifacts[task.to_spec] = task }
238         tasks
239       end
240 
241       # :call-seq:
242       #   to_hash(spec_hash) => spec_hash
243       #   to_hash(spec_string) => spec_hash
244       #   to_hash(artifact) => spec_hash
245       #
246       # Turn a spec into a hash. This method accepts a String, Hash or any object that responds to
247       # the method to_spec. There are several reasons to use this method:
248       # * You can pass anything that could possibly be a spec, and get a hash.
249       # * It will check that the spec includes the group identifier, artifact
250       #   identifier and version number and set the file type, if missing.
251       # * It will always return a new specs hash.
252       def to_hash(spec)
253         if spec.respond_to?(:to_spec)
254           to_hash spec.to_spec
255         elsif Hash === spec
256           rake_check_options spec, :id, :group, :type, :classifier, :version
257           # Sanitize the hash and check it's valid.
258           spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k].to_s if spec[k] ; h }
259           fail "Missing group identifier for #{spec.inspect}" unless spec[:group]
260           fail "Missing artifact identifier for #{spec.inspect}" unless spec[:id]
261           fail "Missing version for #{spec.inspect}" unless spec[:version]
262           spec[:type] = (spec[:type] || DEFAULT_TYPE).to_sym
263           spec
264         elsif String === spec
265           group, id, type, version, *rest = spec.split(':').map { |part| part.empty? ? nil : part }
266           unless rest.empty?
267             # Optional classifier comes before version.
268             classifier, version = version, rest.shift
269             fail "Expecting <group:id:type:version> or <group:id:type:classifier:version>, found <#{spec}>" unless rest.empty?
270           end
271           to_hash :group=>group, :id=>id, :type=>type, :version=>version, :classifier=>classifier
272         else
273           fail 'Expecting a String, Hash or object that responds to to_spec'
274         end
275       end
276 
277       # :call-seq:
278       #   to_spec(spec_hash) => spec_string
279       #
280       # Convert a hash back to a spec string. This method accepts
281       # a string, hash or any object that responds to to_spec.
282       def to_spec(hash)
283         hash = to_hash(hash) unless Hash === hash
284         version = ":#{hash[:version]}" if hash[:version]
285         classifier = ":#{hash[:classifier]}" if hash[:classifier]
286         "#{hash[:group]}:#{hash[:id]}:#{hash[:type] || DEFAULT_TYPE}#{classifier}#{version}"
287       end
288 
289       # :call-seq:
290       #   hash_to_file_name(spec_hash) => file_name
291       #
292       # Convert a hash spec to a file name.
293       def hash_to_file_name(hash)
294         version = "-#{hash[:version]}" if hash[:version]
295         classifier = "-#{hash[:classifier]}" if hash[:classifier]
296         "#{hash[:id]}#{version}#{classifier}.#{hash[:type] || DEFAULT_TYPE}"
297       end
298 
299     end
300 
301     def initialize(*args) #:nodoc:
302       super
303       enhance do |task|
304         # Default behavior: download the artifact from one of the remote repositories
305         # if the file does not exist. But this default behavior is counter productive
306         # if the artifact knows how to build itself (e.g. download from a different location),
307         # so don't perform it if the task found a different way to create the artifact.
308         task.enhance do
309           unless File.exist?(name)
310             info "Downloading #{to_spec}"
311             download
312             pom.invoke rescue nil if pom && pom != self
313           end
314         end
315       end
316     end
317 
318     # :call-seq:
319     #   from(path) => self
320     #
321     # Use this when you want to install or upload an artifact from a given file, for example:
322     #   test = artifact('group:id:jar:1.0').from('test.jar')
323     #   install test
324     # See also Buildr#install and Buildr#upload.
325     def from(path)
326       path = File.expand_path(path.to_s)
327       enhance [path] do
328         mkpath File.dirname(name)
329         pom.invoke unless type == :pom
330         cp path, name
331         info "Installed #{path} as #{to_spec}"
332       end
333       unless type == :pom
334         pom.enhance do
335           mkpath File.dirname(pom.name)
336           File.open(pom.name, 'w') { |file| file.write pom.pom_xml }
337         end
338       end
339       self
340     end
341 
342   protected
343 
344     # :call-seq:
345     #   download
346     # 
347     # Downloads an artifact from one of the remote repositories, and stores it in the local
348     # repository. Raises an exception if the artifact is not found.
349     #
350     # This method attempts to download the artifact from each repository in the order in
351     # which they are returned from #remote, until successful.
352     def download
353       trace "Downloading #{to_spec}"
354       remote = Buildr.repositories.remote.map { |repo_url| URI === repo_url ? repo_url : URI.parse(repo_url) }
355       remote = remote.each { |repo_url| repo_url.path += '/' unless repo_url.path[-1] == '/' }
356       fail 'No remote repositories defined!' if remote.empty?
357       exact_success = remote.find do |repo_url|
358         begin
359           path = "#{group_path}/#{id}/#{version}/#{File.basename(name)}"
360           URI.download repo_url + path, name
361           true
362         rescue URI::NotFoundError
363           false
364         rescue Exception=>error
365           info error
366           trace error.backtrace.join("\n")
367           false
368         end
369       end
370 
371       if exact_success
372         return
373       elsif snapshot?
374         download_m2_snapshot(remote)
375       else
376         fail_download(remote)
377       end
378     end
379 
380     def download_m2_snapshot(remote_uris)
381       remote_uris.find do |repo_url|
382         snapshot_url = current_snapshot_repo_url(repo_url)
383         if snapshot_url
384           begin
385             URI.download snapshot_url, name
386           rescue URI::NotFoundError
387             false
388           end
389         else
390           false
391         end
392       end or fail_download(remote_uris)
393     end
394 
395     def current_snapshot_repo_url(repo_url)
396       begin
397         metadata_path = "#{group_path}/#{id}/#{version}/maven-metadata.xml"
398         metadata_xml = StringIO.new
399         URI.download repo_url + metadata_path, metadata_xml
400         metadata = REXML::Document.new(metadata_xml.string).root
401         timestamp = REXML::XPath.first(metadata, '//timestamp').text
402         build_number = REXML::XPath.first(metadata, '//buildNumber').text
403         snapshot_of = version[0, version.size - 9]
404         repo_url + "#{group_path}/#{id}/#{version}/#{id}-#{snapshot_of}-#{timestamp}-#{build_number}.#{type}"
405       rescue URI::NotFoundError
406         nil
407       end
408     end
409 
410     def fail_download(remote_uris)
411       fail "Failed to download #{to_spec}, tried the following repositories:\n#{remote_uris.join("\n")}"
412     end
413   end
414   
415   
416   # An artifact that is optional.
417   # If downloading fails, the user will be informed but it will not raise an exception.
418   class OptionalArtifact < Artifact
419     
420     protected
421     
422     # If downloading fails, the user will be informed but it will not raise an exception.
423     def download
424       super
425     rescue 
426       info "Failed to download #{to_spec}. Skipping it."
427     end
428     
429   end
430 
431 
432   # Holds the path to the local repository, URLs for remote repositories, and settings for release server.
433   #
434   # You can access this object from the #repositories method. For example:
435   #   puts repositories.local
436   #   repositories.remote << 'http://example.com/repo'
437   #   repositories.release_to = 'sftp://example.com/var/www/public/repo'
438   class Repositories
439     include Singleton
440 
441     # :call-seq:
442     #   local => path
443     #
444     # Returns the path to the local repository.
445     #
446     # The default path is .m2/repository relative to the home directory.
447     # You can set this using the M2_REPO environment variable or the repositories/local
448     # value in your settings.yaml file.
449     def local
450       @local ||= File.expand_path(ENV['M2_REPO'] || ENV['local_repo'] ||
451         (Buildr.settings.user['repositories'] && Buildr.settings.user['repositories']['local']) ||
452         File.join(ENV['HOME'], '.m2/repository'))
453     end
454 
455     # :call-seq:
456     #   local = path
457     #
458     # Sets the path to the local repository.
459     #
460     # The best place to set the local repository path is from a buildr.rb file
461     # located in your home directory. That way all your projects will share the same
462     # path, without affecting other developers collaborating on these projects.
463     def local=(dir)
464       @local = dir ? File.expand_path(dir) : nil
465     end
466 
467     # :call-seq:
468     #   locate(spec) => path
469     #
470     # Locates an artifact in the local repository based on its specification, and returns
471     # a file path.
472     #
473     # For example:
474     #   locate :group=>'log4j', :id=>'log4j', :version=>'1.1'
475     #     => ~/.m2/repository/log4j/log4j/1.1/log4j-1.1.jar
476     def locate(spec)
477       spec = Artifact.to_hash(spec)
478       File.join(local, spec[:group].split('.'), spec[:id], spec[:version], Artifact.hash_to_file_name(spec))
479     end
480 
481     # :call-seq:
482     #   remote => Array
483     #
484     # Returns an array of all the remote repository URLs.
485     #
486     # When downloading artifacts, repositories are accessed in the order in which they appear here.
487     # The best way is to add repositories individually, for example:
488     #   repositories.remote << 'http://example.com/repo'
489     #
490     # You can also specify remote repositories in the settings.yaml (per user) and build.yaml (per build)
491     # files.  Both sets of URLs are loaded by default into this array, URLs from the personal setting
492     # showing first.
493     #
494     # For example:
495     #   repositories:
496     #     remote:
497     #     - http://example.com/repo
498     #     - http://elsewhere.com/repo
499     def remote
500       unless @remote
501         @remote = [Buildr.settings.user, Buildr.settings.build].inject([]) { |repos, hash|
502           repos | Array(hash['repositories'] && hash['repositories']['remote'])
503         }
504       end
505       @remote
506     end
507 
508     # :call-seq:
509     #   remote = Array
510     #   remote = url
511     #   remote = nil
512     #
513     # With a String argument, clears the array and set it to that single URL.
514     #
515     # With an Array argument, clears the array and set it to these specific URLs.
516     #
517     # With nil, clears the array.
518     def remote=(urls)
519       case urls
520       when nil then @remote = nil
521       when Array then @remote = urls.dup
522       else @remote = [urls.to_s]
523       end
524     end
525 
526     # :call-seq:
527     #   release_to = url
528     #   release_to = hash
529     #
530     # Specifies the release server. Accepts a Hash with different repository settings
531     # (e.g. url, username, password), or a String to only set the repository URL.
532     #
533     # Besides the URL, all other settings depend on the transport protocol in use.
534     #
535     # For example:
536     #   repositories.release_to = 'sftp://john:secret@example.com/var/www/repo/'
537     #
538     #   repositories.release_to = { :url=>'sftp://example.com/var/www/repo/',
539     #                                :username='john', :password=>'secret' }
540     # Or in the settings.yaml file:
541     #   repositories:
542     #     release_to: sftp://john:secret@example.com/var/www/repo/
543     #
544     #   repositories:
545     #     release_to:
546     #       url: sftp://example.com/var/www/repo/
547     #       username: john
548     #       password: secret
549     def release_to=(options)
550       options = { :url=>options } unless Hash === options
551       @release_to = options
552     end
553 
554     # :call-seq:
555     #   release_to => hash
556     #
557     # Returns the current release server setting as a Hash. This is a more convenient way to
558     # configure the settings, as it allows you to specify the settings progressively.
559     #
560     # For example, the Buildfile will contain the repository URL used by all developers:
561     #   repositories.release_to[:url] ||= 'sftp://example.com/var/www/repo'
562     # Your private buildr.rb will contain your credentials:
563     #   repositories.release_to[:username] = 'john'
564     #   repositories.release_to[:password] = 'secret'
565     def release_to
566       unless @release_to
567         value = Buildr.settings.user['repositories'] && Buildr.settings.user['repositories']['release_to']
568         @release_to = Hash === value ? value.inject({}) { |hash, (key, value)| hash.update(key.to_sym=>value) } : { :url=>Array(value).first }
569       end
570       @release_to
571     end
572 
573   end
574 
575   # :call-seq:
576   #    repositories => Repositories
577   #
578   # Returns an object you can use for setting the local repository path, remote repositories
579   # URL and release server settings.
580   #
581   # See Repositories.
582   def repositories
583     Repositories.instance
584   end
585 
586   # :call-seq:
587   #   artifact(spec) => Artifact
588   #   artifact(spec) { |task| ... } => Artifact
589   #
590   # Creates a file task to download and install the specified artifact in the local repository.
591   #
592   # You can use a String or a Hash for the artifact specification. The file task will point at
593   # the artifact's path inside the local repository. You can then use this tasks as a prerequisite
594   # for other tasks.
595   #
596   # This task will download and install the artifact only once. In fact, it will download and
597   # install the artifact if the artifact does not already exist. You can enhance it if you have
598   # a different way of creating the artifact in the local repository. See Artifact for more details.
599   #
600   # For example, to specify an artifact:
601   #   artifact('log4j:log4j:jar:1.1')
602   #
603   # To use the artifact in a task:
604   #   compile.with artifact('log4j:log4j:jar:1.1')
605   #
606   # To specify an artifact and the means for creating it:
607   #   download(artifact('dojo:dojo-widget:zip:2.0')=>
608   #     'http://download.dojotoolkit.org/release-2.0/dojo-2.0-widget.zip')
609   def artifact(spec, &block) #:yields:task
610     spec = artifact_ns.fetch(spec) if spec.kind_of?(Symbol)
611     spec = Artifact.to_hash(spec)
612     unless task = Artifact.lookup(spec)
613       task = Artifact.define_task(repositories.locate(spec))
614       task.send :apply_spec, spec
615       Rake::Task['rake:artifacts'].enhance [task]
616       Artifact.register(task)
617       Rake::Task['artifacts:sources'].enhance [task.sources_artifact] unless spec[:type] == :pom
618     end
619     task.enhance &block
620   end
621 
622   # :call-seq:
623   #   artifacts(*spec) => artifacts
624   #
625   # Handles multiple artifacts at a time. This method is the plural equivalent of
626   # #artifact, but can do more things.
627   #
628   # Returns an array of artifacts built using the supplied
629   # specifications, each of which can be:
630   # * An artifact specification (String or Hash). Returns the appropriate Artifact task.
631   # * An artifact of any other task. Returns the task as is.
632   # * A project. Returns all artifacts created (packaged) by that project.
633   # * A string. Returns that string, assumed to be a file name.
634   # * An array of artifacts or a Struct.
635   # * A symbol. Returns the named artifact from the current ArtifactNamespace
636   #
637   # For example, handling a collection of artifacts:
638   #   xml = [ xerces, xalan, jaxp ]
639   #   ws = [ axis, jax-ws, jaxb ]
640   #   db = [ jpa, mysql, sqltools ]
641   #   artifacts(xml, ws, db)
642   #
643   # Using artifacts created by a project:
644   #   artifacts project('my-app')               # All packages
645   #   artifacts project('my-app').package(:war) # Only the WAR
646   def artifacts(*specs, &block)
647     specs.flatten.inject([]) do |set, spec|
648       case spec
649       when ArtifactNamespace
650         set |= spec.artifacts
651       when Symbol, Hash
652         set |= [artifact(spec)]
653       when /([^:]+:){2,4}/ # A spec as opposed to a file name.
654         set |= [artifact(spec)]
655       when String # Must always expand path.
656         set |= [File.expand_path(spec)]
657       when Project
658         set |= artifacts(spec.packages)
659       when Rake::Task
660         set |= [spec]
661       when Struct
662         set |= artifacts(spec.values)
663       else
664         fail "Invalid artifact specification in #{specs.inspect}"
665       end
666     end
667   end
668 
669   def transitive(*specs)
670     specs.flatten.inject([]) do |set, spec|
671       case spec
672       when /([^:]+:){2,4}/ # A spec as opposed to a file name.
673         artifact = artifact(spec)
674         set |= [artifact] unless artifact.type == :pom
675         set |= POM.load(artifact.pom).dependencies.map { |spec| artifact(spec) }
676       when Hash
677         set |= [transitive(spec)]
678       when String # Must always expand path.
679         set |= transitive(file(File.expand_path(spec)))
680       when Project
681         set |= transitive(spec.packages)
682       when Rake::Task
683         set |= spec.respond_to?(:to_spec) ? transitive(spec.to_spec) : [spec]
684       when Struct
685         set |= transitive(spec.values)
686       else
687         fail "Invalid artifact specification in: #{specs.to_s}"
688       end
689     end
690   end
691 
692   # :call-seq:
693   #   group(ids, :under=>group_name, :version=>number) => artifacts
694   #
695   # Convenience method for defining multiple artifacts that belong to the same group, type and version.
696   # Accepts multiple artifact identifiers followed by two or three hash values:
697   # * :under -- The group identifier
698   # * :version -- The version number
699   # * :type -- The artifact type (optional)
700   #
701   # For example:
702   #   group 'xbean', 'xbean_xpath', 'xmlpublic', :under=>'xmlbeans', :version=>'2.1.0'
703   # Or:
704   #   group %w{xbean xbean_xpath xmlpublic}, :under=>'xmlbeans', :version=>'2.1.0'
705   def group(*args)
706     hash = args.pop
707     args.flatten.map { |id| artifact :group=>hash[:under], :type=>hash[:type], :version=>hash[:version], :id=>id }
708   end 
709 
710   # :call-seq:
711   #   install(artifacts)
712   #
713   # Installs the specified artifacts in the local repository as part of the install task.
714   #
715   # You can use this to install various files in the local repository, for example:
716   #   install artifact('group:id:jar:1.0').from('some_jar.jar')
717   #   $ buildr install
718   def install(*args, &block)
719     artifacts = artifacts(args)
720     raise ArgumentError, 'This method can only install artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) }
721     all = (artifacts + artifacts.map { |artifact| artifact.pom }).uniq
722     task('install').tap do |task|
723       task.enhance all, &block
724       task 'uninstall' do
725         all.map(&:to_s ).each { |file| rm file if File.exist?(file) }
726       end
727     end
728   end
729 
730   # :call-seq:
731   #   upload(artifacts)
732   #
733   # Uploads the specified artifacts to the release server as part of the upload task.
734   #
735   # You can use this to upload various files to the release server, for example:
736   #   upload artifact('group:id:jar:1.0').from('some_jar.jar')
737   #   $ buildr upload
738   def upload(*args, &block)
739     artifacts = artifacts(args)
740     raise ArgumentError, 'This method can only upload artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) }
741     task('upload').tap do |task|
742       task.enhance &block if block
743       task.enhance artifacts do
744         artifacts.each { |artifact| artifact.upload }
745       end
746     end
747   end
748 
749 end

Generated using the rcov code coverage analysis tool for Ruby version 0.8.2.1.

Valid XHTML 1.0! Valid CSS!