Version information
Start using this module
Add this module to your Puppetfile:
mod 'ptomulik-macro', '0.1.1'
Learn more about managing modules with a PuppetfileDocumentation
#ptomulik-macro
####Table of Contents
- Overview
- Module Description
- Usage
- Example 1: Defining macro in ruby code
- Example 2: Invoking macro in puppet manifest
- Example 3: Macro with parameters
- Example 4: Variable number of parameters
- Example 5: Default parameters
- Example 6: Invoking macro from macro
- Example 7: Using variables
- Example 8: Building dependencies between parameters
- Reference
- Limitations
##Overview
Puppet parser macros.
##Module Description
With this functionality module developers may define named macros and evaluate
them in manifests. This works similarly to parser
functions but
implementing a macro is a little bit easier. Also, macros may use
"hierarchical" names in foo::bar::geez
form, so a module developer may easily
establish one-to-one correspondence between macro names and class/define
parameters.
The main reason for this module being developed is exemplified in Example 8.
##Usage
###Example 1: Defining macro in ruby code
To define a macro named foo::bar in a module write a file named lib/puppet/macros/foo/bar.rb:
# lib/puppet/macros/foo/bar.rb
Puppet::Macros.newmacro 'foo::bar' do ||
'macro foo::bar'
end
The above macro simply returns the 'macro foo::bar'
string. Note the empty
argument list ||
. This enforces strict arity checking (zero arguments) on
ruby 1.8. Without ||
the block is assumed to accept arbitrary number of
arguments on 1.8 and no arguments on 1.9.
###Example 2: Invoking macro in puppet manifest
Nothing simpler than:
$foo_bar = determine('foo::bar')
notify { foo_bar: message => "determine('foo::bar') -> ${foo_bar}" }
If you don't need the value returned by macro, then you may invoke macro as a statement:
invoke('foo::bar')
###Example 3: Macro with parameters
Let's define macro sum2
which adds two integers:
# lib/puppet/macros/sum2.rb
Puppet::Macros.newmacro 'sum2' do |x,y|
Integer(x) + Integer(y)
end
Now sum2
may be used as follows:
$sum = determine('sum2', 1, 2)
notify { sum: message => "determine('sum2',1,2) -> ${sum}" }
###Example 4: Variable number of parameters
Let's redefine macro from Example 3 to accept arbitrary number of parameters:
# lib/puppet/macros/sum.rb
Puppet::Macros.newmacro 'sum' do |*args|
args.map{|x| Integer(x)}.reduce(0,:+)
end
Now, few experiments:
$zero = determine('sum')
$one = determine('sum',1)
$three = determine('sum',1,2)
notify { zero: message => "determine('sum') -> ${zero}" }
notify { one: message => "determine('sum',1) -> ${one}" }
notify { three: message => "determine('sum',1,2) -> ${three}" }
###Example 5: Default parameters
Default parameters work only with ruby 1.9+. If you don't care about compatibility with ruby 1.8, you may define a macro with default parameters in the usual way:
# lib/puppet/macros/puppet/config/content.rb
Puppet::Macros.newmacro 'puppet::config::content' do |file='/etc/puppet/puppet.conf'|
File.read(file)
end
Now you may use it with:
$content = determine('puppet::config::content')
notify { content: message => $content }
or
$content = determine('puppet::config::content','/usr/local/etc/puppet/puppet.conf')
notify { content: message => $content }
If you need the same for ruby 1.8, here is a workaround (note that the caller i.e the determine function, will check the minimum arity, so we only check the maximum):
# lib/puppet/macros/puppet/config/content.rb
Puppet::Macros.newmacro 'puppet::config::content' do |*args|
if args.size > 1
raise Puppet::ParseError, "Wrong number of arguments (#{args.size} for maximum 1)"
end
args << '/etc/puppet/puppet.conf' if args.size < 1
File.read(args[0])
end
###Example 6: Invoking macro from macro
You may invoke macro using call_macro
method:
# lib/puppet/macros/bar.rb
Puppet::Macros.newmacro 'bar' do
call_macro('foo::bar')
end
The first argument to call_macro
is the name of the macro to be invoked, the
second (if present) is an array of arguments to be passed to macro.
You may alternatively use function interface, but this isn't the recommended way (you may receive misleading exception messages in case you mess up with arguments to macro).
# lib/puppet/macros/bar.rb
Puppet::Macros.newmacro 'bar' do
function_determine(['foo::bar'])
end
If you test any of the above with the following puppet code:
$bar = determine('bar')
notify { bar: message => "determine('bar') -> ${bar}" }
then the following notice would appear on output:
Notice: determine('bar') -> macro foo::bar
Obviously the above text is the result of foo::bar
macro defined in
Example 1.
###Example 7: Using variables
You may access puppet variables, for example $::osfamily
(fact). The
following example determines default location of apache configs for operating
system running on slave:
# lib/puppet/macros/apache/conf_dir.rb
Puppet::Macros.newmacro 'apache::conf_dir' do
case os = lookupvar("::osfamily")
when /FreeBSD/; '/usr/local/etc/apache22'
when /Debian/; '/usr/etc/apache2'
else
raise Puppet::Error, "unsupported osfamily #{os.inspect}"
end
end
$apache_conf_dir = determine('apache::conf_dir')
notify { apache_conf_dir: message => "determine('apache::conf_dir') -> ${apache_conf_dir}" }
###Example 8: Building dependencies between parameters
Macros may be used to inter-depend parameters of defined types or classes. In
other words, if one parameter is altered by user, others should be adjusted
automatically, unless user specify them explicitly. For example, we may have a
defined type testmodule::foo
with two parameters $a
and $b
and we want
$b
to depend on $a
. So, we may define macros testmodule::foo::a
and
testmodule::foo::b
, and testmodule::foo::b
may accept $a
as an argument:
# lib/puppet/macros/testmodule/foo/a.rb
Puppet::Macros.newmacro 'testmodule::foo::a' do |a|
pp2r(a) ? a : 'default a'
end
# lib/puppet/macros/testmodule/foo/b.rb
Puppet::Macros.newmacro 'testmodule::foo::b' do |b, a|
pp2r(b) ? b : "default b for a=#{a.inspect}"
end
Then, if we split testmodule::foo
into actual implementation (let say
testmodule::impl::foo
) and a wrapper (let's call it simply testmodule::foo
),
the job may be finished as follows:
# manifests/impl/foo.pp
define testmodule::impl::foo($a, $b) {
notify{$title: message => "${title}: a=\'${a}\', b=\'${b}\'"}
}
# manifests/foo.pp
define testmodule::foo($a = undef, $b = undef)
{
$_a = determine('testmodule::foo::a', $a)
$_b = determine('testmodule::foo::b', $b, $_a)
testmodule::impl::foo{"$title":
a => $_a,
b => $_b
}
}
Now, the following manifest
puppet apply --modulepath $(pwd) <<!
testmodule::foo {defaults: }
testmodule::foo {custom_a: a => 'custom a' }
testmodule::foo {custom_b: b => 'custom b' }
testmodule::foo {custom_a_and_b: a => 'custom a', b => 'custom b' }
testmodule::foo {other: }
Testmodule::Foo[other] { a => 'other default a' }
!
would output these lines:
Notice: defaults: a='default a', b='default b for a="default a"'
Notice: custom_a: a='custom a', b='default b for a="custom a"'
Notice: custom_b: a='default a', b='custom b'
Notice: custom_a_and_b: a='custom a', b='custom b'
Notice: other: a='other default a', b='default b for a="other default a"'
##Reference
###Function reference
####Index of functions:
####determine Determine value of a macro.
This function ivokes a macro defined with Puppet::Macros.newmacro
method and returns its value. The function takes macro name as first
argument and macro parameters as the rest of arguments. The number of
arguments provided by user is validated against macro's arity.
Example:
Let say, you have defined the following macro in puppet/macros/sum.rb:
# puppet/macros/sum.rb
Puppet::Macros.newmacro 'sum' do |x,y|
Integer(x) + Integer(y)
end
You may then invoke the macro from puppet as follows:
$three = determine('sum',1,2) # -> 3
- Type: rvalue
[Index of functions|Table of Contents]
####invoke Invoke macro as a statement.
This function ivokes a macro defined with Puppet::Macros.newmacro
method. The function takes macro name as first argument and macro parameters
as the rest of arguments. The number of arguments provided by user is
validated against macro's arity.
Example:
Let say, you have defined the following macro in puppet/macros/print.rb:
# puppet/macros/pring.rb
Puppet::Macros.newmacro 'print' do |msg|
print msg
end
You may then invoke the macro from puppet as follows:
invoke('print',"hello world!\\n")
- Type: statement
[Index of functions|Table of Contents]
###API Reference
API reference may be generated with
bundle exec rake yard
The generated documentation goes to doc/
directory. Note that this works only
under ruby >= 1.9.
The API documentation is also available online.
##Limitations
- Currently there is no possibility to define macro in puppet manifests, that is
we only can define macro using ruby and use it in ruby or puppet. I believe
this functionality may implemented as an additional parser function (call it
macro
) and it should work with the help of puppet lambdas, which are available in future parser. - Currently there is no way to store and auto-generate documentation for macros. It should work similarly as for functions but its not implemented at the moment. This may be added in future versions.
2014-03-12 Pawel Tomulik ptomulik@meil.pw.edu.pl
- release 0.1.1
- fix path expansion in spec_helper_integration.rb
- corrected example 8 in README.md 2014-01-02 Pawel Tomulik ptomulik@meil.pw.edu.pl
- fixed monkey patching of Puppet::Util::Autoload.expand 2014-01-30 Pawel Tomulik ptomulik@meil.pw.edu.pl
- small corrections to API docs
- configured yardoc to use github flawored markup
- release 0.1.0
- moved macros from puppet/parser/macros to puppet/macros 2014-01-28 Pawel Tomulik ptomulik@meil.pw.edu.pl
- use pp2r() in Example 8
- release 0.0.18
- added fr2r method to Scope
- release 0.0.17
- fixed bug with missing Autoload.expand() method
- release 0.0.16
- added pp2r method to Scope 2014-01-27 Pawel Tomulik ptomulik@meil.pw.edu.pl
- release 0.0.15
- added call_macro to scope and fixed lambda binding issues
- release 0.0.14
- revised example 6
- release 0.0.13
- fixed specs for ruby 2.0
- prevent adding duplicates in $LOAD_PATH
- added specs for validators
- release 0.0.12
- revised Example 8
- release 0.0.11
- fixed bug with arity validation faililng on 1.9+
- cleanup yard documentation
- release 0.0.10
- switched back from lambdas to blocks in documentation and specs
- fixed typo in README.md
- release 0.0.9
- revised README.md
- release 0.0.8
- revised README.md
- revised documentation for functions
- release 0.0.7
- removed workaround for travis bug
- refactoring 2014-01-26 Pawel Tomulik ptomulik@meil.pw.edu.pl
- release 0.0.6
- added few "integration" tests
- included puppet 3.3 and 3.4 on travis
- included puppet 2.7 on travis
- optimized 'invoke' function for better code coverage
- release 0.0.5
- documented functions determine and invoke
- revised README.md
- revised Example 8
- release 0.0.4
- reworked Example 8 2014-01-25 Pawel Tomulik ptomulik@meil.pw.edu.pl
- release 0.0.3
- revised README.md
- release 0.0.2
- added specs to better cover newmacro, determine and invoke functions
- release 0.0.1
- corrected Example 6 in README.md
- initial commit
Copyright (C) 2014 Paweł Tomulik <ptomulik@meil.pw.edu.pl>. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.