Class Buildr::ArtifactNamespace
In: lib/buildr/packaging/artifact_namespace.rb  (CVS)
Parent: Object

An ArtifactNamespace is a hierarchical dictionary used to manage ArtifactRequirements. It can be used to have different artifact versions per project or to allow users to select a version for addons or modules.

Namespaces are opened using the Buildr.artifact_ns method, most important methods are:

need
Used to create a requirement on the namespace.
use
Set the artifact version to use for a requirement.
values_at
Reference requirements by name.
each
Return each ArtifactRequirement in the namespace.

The method_missing method for instances provides some syntactic sugar to these. See the following examples, and the methods for ArtifactRequirement.

Avoiding constant pollution on buildfile

Each project has its own ArtifactNamespace inheriting the one from the parent project up to the root namespace.

Consider the following snippet, as project grows, each subproject may need different artifact combinations and/or versions. Assigning artifact specifications to constants can make it painful to maintain their references even if using structs/hashes.

  -- buildfile --
  SPRING = 'org.springframework:spring:jar:2.5'
  SPRING_OLD = 'org.springframework:spring:jar:1.0'
  LOGGING = ['comons-logging:commons-logging:jar:1.1.1',
             'log4j:log4j:jar:1.2.15']
  WL_LOGGING = artifact('bea:wlcommons-logging:jar:8.1').from('path/to/wlcommons-logging.jar')
  LOGGING_WEBLOGIC = ['comons-logging:commons-logging:jar:1.1.1',
                      WL_LOGGING]
  COMMONS = struct :collections => 'commons-collection:commons-collection:jar:3.1',
                   :net => 'commons-net:commons-net:jar:1.4.0'

  define 'example1' do
    define 'one' do
      compile.with SPRING, LOGGING_WEBLOGIC, COMMONS
    end
    define 'two' do
      compile.with SPRING_OLD, LOGGING, COMMONS
    end
    define 'three' do
      compile.with "commons-collections:commons-collections:jar:2.2"
    end
  end

With ArtifactNamespace you can do some more advanced stuff, the following annotated snipped could still be reduced if default artifact definitions were loaded from yaml file (see section below and ArtifactNamespace.load).

  -- buildfile --
  artifact_ns do |ns| # the current namespace (root if called outside a project)
    # default artifacts
    ns.spring = 'org.springframework:spring:jar:2.5'
    # default logger is log4j
    ns.logger = 'log4j:log4j:jar:1.2.15'

    # create a sub namespace by calling the #ns method,
    # artifacts defined on the sub-namespace can be referenced by
    # name :commons_net or by calling commons.net
    ns.ns :commons, :net => 'commons-net:commons-net:jar:1.4.0',
                    :logging => 'comons-logging:commons-logging:jar:1.1.1'

    # When a child namespace asks for the :log artifact,
    # these artifacts will be searched starting from the :current namespace.
    ns.virtual :log, :logger, :commons_logging
  end

  artifact_ns('example2:one') do |ns| # namespace for the one subproject
    ns.logger = artifact('bea:wlcommons-logging:jar:8.1').from('path/to/wlcommons-logging.jar')
  end
  artifact_ns('example2:two') do |ns|
    ns.spring = '1.0' # for project two use an older spring version (just for an example)
  end
  artifact_ns('example2:three').commons_collections = 2.2'
  artifact_ns('example2:four') do |ns|
    ns.beanutils = 'commons-beanutils:commons-beanutils:jar:1.5'        # just for this project
    ns.ns(:compilation).use :commons_logging, :beanutils, :spring       # compile time dependencies
    ns.ns(:testing).use :log, :beanutils, 'cglib:cglib-nodep:jar:2.1.3' # run time dependencies
  end

  define 'example2' do
    define 'one' do
      compile.with :spring, :log, :commons # uses weblogic logging
    end
    define 'two' do
      compile.with :spring, :log, :commons # will take old spring
    end
    define 'three' do
      compile.with :commons_collections
      test.with artifact_ns('example2:two').spring # use spring from project two
    end
    define 'four' do
      compile.with artifact_ns.compilation
      test.with artifact_ns.testing
    end
    task(:down_them_all) do # again, just to fill this space with something ;)
      parent.projects.map(&method(:artifact_ns)).map(&:artifacts).map(&:invoke)
    end
  end

Loading from a yaml file (e. profiles.yaml)

If your projects use lots of jars (after all we are using java ;) you may prefer to have constant artifact definitions on an external file. Doing so would allow an external tool (or future Buildr feature) to maintain an artifacts.yaml for you. An example usage is documented on the ArtifactNamespace.load method.

For addon/plugin writers & Customizing artifact versions

Sometimes users would need to change the default artifact versions used by some module, for example, the XMLBeans compiler needs this, because of compatibility issues. Another example would be to select the groovy version to use on all our projects so that Buildr modules requiring groovy jars can use user prefered versions.

To meet this goal, an ArtifactNamespace allows to specify ArtifactRequirement objects. In fact the only difference with the examples you have already seen is that requirements have an associated VersionRequirement, so that each time a user tries to select a version, buildr checks if it satisfies the requirements.

Requirements are declared using the ArtifactNamespace#need method, but again, syntactic sugar is provided by ArtifactNamespace#method_missing.

The following example is taken from the XMLBeans compiler module. And illustrates how addon authors should specify their requirements, provide default versions, and document the namespace for users to customize.

   module Buildr::XMLBeans

      # You need to document this constant, giving users some hints
      # about when are (maybe some of) these artifacts used. I mean,
      # some modules, add jars to the Buildr classpath when its file
      # is required, you would need to tell your users, so that they
      # can open the namespace and specify their defaults. Of course
      # when the requirements are defined, buildr checks if any compatible
      # version has been already defined, if so, uses it.
      #
      # Some things here have been changed to illustrate their meaning.
      REQUIRES = ArtifactNamespace.for(self).tap do |ns|

        # This jar requires a >2.0 version, default being 2.3.0
        ns.xmlbeans! 'org.apache.xmlbeans:xmlbeans:jar:2.3.0', '>2'

        # Users can customize with Buildr::XMLBeans::REQUIRES.stax_api = '1.2'
        # This is a non-flexible requirement, only satisfied by version 1.0.1
        ns.stax_api! 'stax:stax-api:jar:1.0.1'

        # This one is not part of XMLBeans, but is just another example
        # illustrating an `artifact requirement spec`.

        ns.need " some_name ->  ar:ti:fact:3.2.5 ->  ( >2 & <4)"

        # As you can see it's just an artifact spec, prefixed with
        # ' some_name -> ', this means users can use that name to
        # reference the requirement, also this string has a VersionRequirement
        # just after another ->.
      end

      # The REQUIRES constant is an ArtifactNamespace instance,
      # that means we can use it directly. Note that calling
      # Buildr.artifact_ns would lead to the currently executing context,
      # not the one for this module.
      def use
        # test if user specified his own version, if so, we could perform some
        # functionallity based on this.
        REQUIRES.some_name.selected? # => false

        REQUIRES.some_name.satisfied_by?('1.5') # => false
        puts REQUIRES.some_name.requirement     # => ( >2 & <4 )

        REQUIRES.artifacts # get the Artifact tasks
      end

   end

A more advanced example using ArtifactRequirement listeners is included in the artifact_namespace_spec.rb description for ‘Extension using ArtifactNamespace’ That‘s it for addon writers, now, users can select their prefered version with something like:

   require 'buildr/xmlbeans'
   Buildr::XMLBeans::REQUIRES.xmlbeans = '2.2.0'

More advanced stuff, if users really need to select an xmlbeans version per project, they can do so letting :current (that is, the currently running namespace) be parent of the REQUIRES namespace:

   Buildr::XMLBeans::REQUIRES.parent = :current

Now, provided that the compiler does not caches its artifacts, it will select the correct version. (See the first section for how to select per project artifacts).

Methods

[]   []=   accessor   alias   artifacts   clear   clear   delete   each   fetch   group   instance   key?   keys   load   method_missing   need   ns   ns?   parent   parent=   root   root   root?   use   values   values_at   virtual  

Included Modules

DClone Enumerable

Classes and Modules

Class Buildr::ArtifactNamespace::ArtifactRequirement

External Aliases

instance -> []
instance -> for

Attributes

name  [R] 

Public Class methods

Forget all namespaces, create a new ROOT

Populate namespaces from a hash of hashes. The following example uses the profiles yaml to achieve this.

  -- profiles.yaml --
  development:
    artifacts:
      root:        # root namespace
        spring:     org.springframework:spring:jar:2.5
        groovy:     org.codehaus.groovy:groovy:jar:1.5.4
        logging:    # define a named group
          - log4j:log4j:jar:1.2.15
          - commons-logging:commons-logging:jar:1.1.1

      # open Buildr::XMLBeans namespace
      Buildr::XMLBeans:
        xmlbeans: 2.2

      # for subproject one:oldie
      one:oldie:
        spring:  org.springframework:spring:jar:1.0

  -- buildfile --
  ArtifactNamespace.load(Buildr.settings.profile['artifacts'])

Obtain the root namespace, returns the ROOT constant

Public Instance methods

:call-seq:

  artifact_ns[:name] -> ArtifactRequirement
  artifact_ns[:many, :names] -> [ArtifactRequirement]

Return an anonymous module

  # first create a requirement
  artifact_ns.cool_aid! 'cool:aid:jar:>=1.0'

  # extend an object as a cool_aid delegator
  jars = Object.new.extend(artifact_ns.accessor(:cool_aid))
  jars.cool_aid = '2.0'

  artifact_ns.cool_aid.version # -> '2.0'

Create an alias for a named requirement.

return Artifact objects for each requirement

Create a virtual group on this namespace. When the namespace is asked for the who artifact, it‘s value is an array made from the remaining names. These names are searched by default from the current namespace. Second form specified the starting namespace to search from.

First form creates an ArtifactRequirement on the namespace. It is equivalent to providing a requirement_spec to the need method:

  artifact_ns.need "cool_aid -> cool:aid:jar:2.3.4 -> ~>2.3"

the second argument is optional.

Second form can be used to select an artifact version and is equivalent to:

  artifact_ns.use :cool_aid => '1.0'

Third form obtains the named ArtifactRequirement, can be used to test if a named requirement has been defined. It is equivalent to:

  artifact_ns.fetch(:cool_aid) { nil }

Last form tests if the ArtifactRequirement has been defined and a version has been selected for use. It is equivalent to:

  artifact_ns.has_cool_aid?
  artifact_ns.values_at(:cool_aid).flatten.all? { |a| a && a.selected? }

Create a named sub-namespace, sub-namespaces are themselves ArtifactNamespace instances but cannot be referenced by the Buildr.artifact_ns, ArtifactNamespace.instance methods. Reference needs to be through this object using the given name

  artifact_ns('foo').ns(:bar).need :thing => 'some:thing:jar:1.0'
  artifact_ns('foo').bar # => the sub-namespace 'foo.bar'
  artifact_ns('foo').bar.thing # => the some thing artifact

See the top level ArtifactNamespace documentation for examples

Test if a sub-namespace by the given name exists

ROOT namespace has no parent

Set the parent for the current namespace, except if it is ROOT

Is this the ROOT namespace?

First and second form are equivalent, the third is used when an ArtifactRequirement has been previously defined with :name, so it just selects the version.

ArtifactNamespace#method_missing provides syntactic sugar for this.

Return all requirements for this namespace

Return only the named requirements

virtual(group_name, *members)

Alias for group

[Validate]