On Tuesday, January 29, 2013 9:07:29 AM UTC-6, Ti Leggett wrote:On Jan 29, 2013, at 8:51 AM, jcbollinger wrote:
The only way to force parse-time ordering right now is to do a direct
include, unfortunately.
Yes, but I'm not sure I would say "unfortunately" there. The problem is
not so much with any limitation of Puppet DSL in this area, but rather an
issue of module design and manifest set architecture.
I mean, one should be very careful and deliberate about designing
modules such that their classes need to rely on class variables of other
modules' classes. Indeed, it is probably a poor idea to implement such a
design unilaterally -- instead, the module providing the class variables
should be designed and implemented in anticipation of that usage as well.
That probably means centralizing all variables intended for cross-module
reference in one well-known class, documenting their names and value
ranges, and committing to avoiding incompatible changes there.
I'm not sure I fully agree with this from a design standpoint. In
object-oriented programming, one of the design principles is that variables
relating to the object are encapsulated within the object and exposed or
not depending on how they should be accessed.
Indeed so. Do be aware, however, that Puppet DSL is not an object-oriented
language. Among other things, it has no language-enforced encapsulation at
all, and Puppet "classes" are not classes in the OO sense of the term.
Puppet has no accessor methods (or any methods at all, actually), so the
only external interface that modules can provide consists of the data
relied upon and and declared by the module.
Inasmuch as one can apply OO principles even in contexts that are not
natively OO, however, my advice cleaves closely to OO orthodoxy by
recommending well-defined and documented component interfaces and
discretionary observation of encapsulation (since the language won't
enforce it). The only way to adhere more strictly to OO principles would
be for modules to avoid relying on each others' data at all. That's a nice
ideal, but it is often not practical to apply as an absolute requirement.
Such a recommendation would not have been responsive to the question,
anyway.
IMHO, it also makes it more obfuscated when you're accessing say the SSL
CA cert path variable and that's in some 'common' module that everything
has to include.
If OO is the ideal design approach (which is not at all clear, but you
brought it up) then it would dictate that related data be grouped into
objects. That's the most fundamental and incontrovertible principle of OO
-- you can have OO without inheritance, without polymorphism, even without
enforced encapsulation, but you can't have it without *objects*.
Puppet's analogs of objects are classes and resources. Ergo, if one wants
to pursue an OO strategy then classes should contain and own their own data.
More generally, modules aren't modular if they have to rely on all their
data being provided by some other module. Nevertheless, it doesn't really
matter which class in which module is documented to provide the data of
interest. As long as the module documents it and commits to those details
remaining stable, it amounts to the same thing I described.
Granted it makes it easier on the module developer - just always in
include the common module and your variables should be there - but it also
makes it less explicit. I would argue, if you're writing a module that
depends on using the SSL CA cert path you have some dependency on the SSL
module and should have some understanding of what that module does and the
ramifications of using that module, so you should explicitly include that
module for that dependency. In just about every language you must include
the external modules/libraries you depend on for functionality outside the
standard norm. In puppet the standard norm - the stdlib.h equivalent if you
will - I would consider to be facter variables. You want to use LDAP or SSL
or Kerberos? You best include those modules explicitly and figure out what
you can use from them - ldap.h <> ldap::params, ssl.h <> ssl::params, etc.
Standardize how you create these public puppet 'headers' and use them
explicitly and appropriately that way.
And now that you drop the idea of a common data module, we come back around
to using other modules' classes' variables. Are you honestly arguing that
it is better to analyze class implementations and pull out variables
willy-nilly than to define and rely on documented interfaces? I think you
owe a penance at the OO altar.
John