How to run the same class multiple times? - ruby

I have a puppet module which deploys a JAR file and writes some properties files (by using ERB templates).
Recently we added a "mode" feature to the application, meaning the application can run in different modes depending on the values entered in the manifest.
My hierarchy is as follows:
setup
*config
**files
*install
Meaning setup calls the config class and the install class.
The install class deploys the relevant RPM file according to the mode(s)
The config class checks the modes and for each mode calls the files class with the specific mode and directory parameters, the reason for this structure is that the value of the properties depends on the actual mode.
The technical problem is that if I have multiple modes in the manifest (which is my goal) I need to call the files class twice:
if grep($modesArray, $online_str) == [$online_str] {
class { 'topology::files' :
dir => $code_dir,
mode => $online_str
}
}
$offline_str = "offline"
$offline_suffix = "_$offline_str"
if grep($modesArray, $offline_str) == [$offline_str] {
$dir = "$code_dir$offline_suffix"
class { 'topology::files' :
dir => $dir,
mode => $offline_str
}
However, in puppet you cannot declare the same class twice.
I am trying to figure out how I can call a class twice or perhaps a method which I can access its parameters from my ERB files, but I can't figure this out
The documentation says it's possible but doesn't say how (I checked here https://docs.puppetlabs.com/puppet/latest/reference/lang_classes.html#declaring-classes).
So to summarize is there a way to either:
Call the same class more then once with different parameters
(Some other way to) Create multiple files based on the same ERB file (with different parameters each time)

You can simply declare your class as a define:
define topology::files($dir,$mode){
file{"${dir}/filename":
content=> template("topology/${mode}.erb"),
}
}
That will apply a different template for each mode
And then, instantiate it as many times as you want:
if grep($modesArray, $online_str) == [$online_str] {
topology::files{ "topology_files_${online_str}" :
dir => $code_dir,
mode => $online_str
}
}
$offline_str = "offline"
$offline_suffix = "_$offline_str"
if grep($modesArray, $offline_str) == [$offline_str] {
$dir = "$code_dir$offline_suffix"
topology::files{ "topology_files_${online_str}" :
dir => $dir,
mode => $offline_str
}

Your interpretation of the documentation is off the mark.
Classes in Puppet should be considered singletons. There is exactly one instance of each class. It is part of a node's manifest or it is not. The manifest can declare the class as often as it wants using the include keyword.
Beware of declaration using the resource like syntax.
class { 'classname': }
This can appear at most once in a manifest. Parameter values are now permanently bound to your class. Your node has chosen what specific shape the class should take for it.
Without seeing the code for your class, your question makes me believe that you are trying to use Puppet as a scripting engine. It is not. Puppet only allows you to model a target state. There are some powerful mechanics to implement complex workflows to achieve that state, but you cannot use it to run arbitrary transformations in an arbitrary order.
If you add the class code, we can try and give some advice on how to restructure it to make Puppet do what you need. I'm afraid that may not be possible, though. If it is indeed necessary to sync one or more resources to different states at different times (scripting engine? ;-) during the transaction, you should instead implement that whole workflow as an actual script and have Puppet run that through an exec resource whenever appropriate.

Related

What does create method do in gradlePlugin.plugins?

In the gradle doc: https://docs.gradle.org/current/userguide/custom_plugins.html#sec:custom_plugins_standalone_project
The code block is:
gradlePlugin {
plugins {
create("simplePlugin") {
id = "org.example.greeting"
implementationClass = "org.example.GreetingPlugin"
}
}
}
I noticed it calls create method. And I looked the source code. It says:
Creates a new item with the given name, adding it to this container,
then configuring it with the given action.
What does it mean? Is it actually used anywhere? Or it can be any name does not really matter?
gradlePlugin.plugins is a NamedDomainObjectContainer - which are used by Gradle to hold multiple objects, each with a name.
The documentation on plugin development goes into more detail on the usage of NamedDomainObjectContainers.
Sometimes you might want to expose a way for users to define multiple, named data objects of the same type.
[...]
It’s very common for a plugin to post-process the captured values within the plugin implementation e.g. to configure tasks.
Since the elements of a NamedDomainObjectContainer can be any type, there's no specific usage for one. Generally they are used to configure the Gradle project, for example creating specific tasks, configuring source code locations.

Configure providers from variables, in a generic way

How can I create a recipe that will populate its attributes using the fiels from an instance of an object in a generic way?
As an example, consider the following recipe:
component = $auth_docker
docker_image component.name do
registry component.registry
tag component.tag
action :pull
end
When you have 50s of recipes that look like this, maintaining them really gets overwhelming.
In Python, i would probably implement a solution that would look a bit like this:
docker_image = DockerImage(**$auth_docker)
Or, I would create some sort of helper function to build it for me:
def generate_docker_image_lwrp(attributes):
lwrp = DockerImage()
lwrp.registry = attributes.registry
lwrp.tag = attributes.tag
return lwrp
The goal is to reduce maintenance on the recipes. For instance this morning I wanted to add Chef's "retries" attribute on all recipes that pull an image. I had to edit all of them - I don't want that. I should've been able to a) add the attribute to the stack's JSON b) edit the Ruby wrapper class so that instances of it (i.e.: $auth_docker) get the "retries" field, then c) add the retries attribute to the lwrp-generator. Since all recipes would use the same generator, recipes wouldn't need to be edited at all.
Is this possible using Chef, in a way that 'notifies' still work?
Quoting the Documentation
A definition is code that is reused across recipes, similar to a
compile-time macro. A definition is created using arbitrary code
wrapped around built-in chef-client resources—file, execute, template,
and so on—by declaring those resources into the definition as if they
were declared in a recipe. A definition is then used in one (or more)
recipes as if it were a resource.
Though a definition behaves like a resource, some key differences
exist. A definition:
Is not a resource or a lightweight resource Is defined from within the
/definitions directory of a cookbook Is loaded before resources during
the chef-client run; this ensures the definition is available to all
of the resources that may need it May not notify resources in the
resource collection because a definition is loaded before the resource
collection itself is created; however, a resource in a definition may
notify a resource that exists within the same definition Automatically
supports why-run mode, unlike lightweight resources Use a defintion
when repeating patterns exist across resources and/or when a simple,
direct approach is desired. There is no limit to the number of
resources that may be included in a definition: use as many built-in
chef-client resources as necessary.
I.e: you can create a definition for this in a library cookbook used solely for this.
docker_library/defintions/default.rb
define :my_docker_image, :component => nil do
component = params[:component]
docker_image component.name do
registry component.registry
tag component.tag
action :pull
end
end
in your recipes (need to have a depends on the docker_library cookbook in the metadata.rb):
my_component = $auth_docker
my_docker_image my_component.name do
component my_component
end
A more complete exemple of definition is available in the logrotate cookbook

Dynamically get the public path for a workbench package in laravel

I'd like to get the path to a package public directory (css etc) based on the package alias.
Is there anything already built into the laravel framework?
In other words something like:
public_path('myalias');
When I'm talking about alias, you would typically "alias" a module by adding the following within your service provider's boot method:
$this->package('namespace/package','alias_name');
For those wondering why someone might want to do this:
We are running a multi domain/subdomain application that makes use of a central piece of code for all of the domains and then specific packages per domain (I'll refer to them as funnels).
Each funnel has its own controllers that can possibly extend base controllers to implement their own functionality while re-using code where they can. They also have their own views.
The funnel refers to its own views by way of something like:
View::make('funnel::path.to.view')
The way we accomplish this is by doing some business logic on page load to only load the FunnelServiceProvider related to that particular domain and aliasing it to "funnel". This way our base controllers can also refer to funnel and not be tied to a particular packages views,includes,blocks etc.
My hope is to do something similar on the views so that I can simply call something like get_funnel_path() to get the path to the funnel that is currently being loaded.
The value could then be used to load css,js,images etc without worrying about the funnel path.
This would allow us to simply copy and paste views from one domain to the next without having to modify all of the paths in potentially multiple files. We could also make use of globally included files in all/most of the views.
An example of this might be the head. The head section should be the same for 99% of the files, however the path where it loads its resources should change based on the funnel.
We use the same naming conventions for css files as well as use sass, imports, merging for all of the funnels; so only the path needs to change.
You can do something like this although it will only work with your own packages and require a bit of work. Because the alias is not really stored somewhere you can easily access you have to do that yourself.
First create some kind of class to store your package names in. I called mine PackageManager:
class PackageManager {
private $packages = array();
public function addPackage($fullName, $alias){
$this->packages[$alias] = $fullName;
return $this;
}
public function getPublicPath($alias){
if(!isset($this->packages[$alias])) return public_path();
$path = 'packages/' . $this->packages[$alias];
return public_path($path);
}
}
Now let's register that class as a singleton in a service provider:
$this->app->singleton('packagemanager', function(){
return new PackageManager();
});
Then, in every package you want to register, add this call in the boot method right next to $this->package():
$this->app['packagemanager']->addPackage('vendor/package', 'alias');
After that you can do this anywhere in your application:
app('packagemanager')->getPublicPath('alias');
If you want a shorter syntax, add this helper function somewhere:
function public_package_path($alias){
return app('packagemanager')->getPublicPath($alias);
}
And just do:
public_package_path('alias');

CodeIgniter - where to put functions / classes?

Am having problems understanding where classes should be kept in CI. I am building an application that describes / markets mobile phones.
I would like for all of my functions (i.e. getphone, getdetails etc.) to reside in one class called Mobile - I understand that this file should be called Mobile.php and reside in the controllers folder.
Can I then have multiple functions inside Mobile.php? E.g.
public function getphone() {
xxx
xx
xx
}
public function getdetails() {
xxx
xx
xx
}
Or do I need to put each function in its own class?
I'd really appreciate looking at some sample code that works. I've been going through the documentation and google for a few hours, and tried all sorts of variations in the URL to find a test class, but without much luck! I've even messed around with the routes and .htaccess...
All I am trying to achieve is the following:
http:///model/HTC-Desire/ to be re-routed to a function that accepts HTC-Desire as a parameter (as I need it for a DB lookup). The default controller works fine, but can't get anything to work thereafter.
Any ideas?
Thanks
Actually it works like this:
Controllers and Models go to their perspective folders as you know it
If you want to create functions that are not methods of an object, you must create a helper file. More info here :
http://codeigniter.com/user_guide/general/helpers.html
Now if you want to create your own datatypes (classes that don't extend Models and Controllers), you add them to the library folder. So if let's say you want to create a class "Car" you create this file:
class Car{
function __construct(){}
}
and save it in the libraries folder as car.php
To create an instance of the Car class you must do the following:
$this->load->library('car');
$my_car = new Car();
More information on libraries here:
http://codeigniter.com/user_guide/general/creating_libraries.html
Yes, you can have as many functions in a controller class as you'd like. They are accessible via the url /class/function.
You can catch parameters in the class functions, though it's not advisable.
class Mobile extends CI_Controller{
public function getPhone($phoneModel=''){
echo $phoneModel;
//echo $this->input->post('phoneModel');
}
}
http://site.com/mobile/getPhone/HTC-Rad theoretically would echo out "HTC-Rad". HOWEVER, special characters are not welcome in URL's in CI by default, so in this example you may be met with a 'Disallowed URI characters" error instead. You'd be better off passing the phone model (or any other parameters) via $_POST to the controller.
Classes can exist both as Controllers and Models, as CodeIgniter implements the MVC pattern. I recommend reading more about that to understand how your classes/functions/etc. can best be organized.
Off the top of my head, Pyro CMS is an application built with CodeIgniter and the source code is freely available. I'm sure there are others.
I think it's best you handle it from one perspective, that is; create a utility class with all your functions in it.
The answer to the question of where to put/place the class file is the "libraries" folder.
This is clearly stated in the documentation. Place your class in the libraries folder.
When we use the term “Libraries” we are normally referring to the
classes that are located in the libraries directory and described in
the Class Reference of this user guide.
You can read more on creating and using libraries Creating Libraries — CodeIgniter 3.1.10 documentation
After placing the newly created class in the libraries folder, to use just simply load the library within your controller as shown below:
$this->load->library('yourphpclassname');
If you wish to receive several arguments within you constructor you have to modify it to receive an argument which would be an array and you loading/initialization would then be slightly different as shown below:
$params = array('type' => 'large', 'color' => 'red');
$this->load->library('yourphpclassname', $params);
Then, to access any of the functions within the class simply do that as shown below:
$this->yourphpclassname->some_method();
I hope this answers your question if you have further question do leave a comment and I would do well to respond to them.

Codeigniter: Use of load_class

I am writing my own logging class to save data in a DB. As I looked how CI is doing I noticed there is a log_message() function which handles the logging. There is a load_class function I can't assign to anything in the CI user guide.
1 Why do they put this into an extra function?
2 What/where loads this function files from?
Hope there are some CI guys how can answer :-)
Short answer:
You can write your own log class to override the default CI class:
<?php
// this file is /application/libraries/MY_Log.php
class MY_Log extends CI_Log {
public function write_log($level = 'error', $msg, $php_error = FALSE)
{
// Put your own logging function in here.
// If you want it to still log to a file as usual, use this:
parent::write_log($level, $msg, $php_error);
}
}
Long answer:
The load_class() function is basically a singleton loader. If the class has already been loaded, return a previous instance; otherwise, load it and create the singleton. It is very important in a framework like CI. You have to know that every time you call, say, a database function, it is applying it to the same object, not instantiating a new one (that would get really messy). All CI libraries function this way by default.
An important note: they changed how this functions significantly in version 2.0. Previously, it would only load from the /libraries folder, but now, it will load from /core or wherever you specify when calling the function.
Here's the process for loading, say, the Log class (from your example):
$_log =& load_class('Log');
$_log->write_log($level, $message, $php_error);
This runs the following checks, in sequence:
If the Log class already exists, we're done. Return the singleton.
If not, first check the /system/libraries folder for a "Log.php" file
If no file existed for step #2, now check /application/libraries for a "MY_Log.php" file (or whatever your subclass prefix is set to in your configuration)
If it loaded the default CI class (from the /system folder), but you DO have an extended class under /application, load that class too.
Return a new instance of the class (YOURS if it exists; otherwise, it's the CI_* class)
I've actually never needed to use the load_class() function, as it allows extension fairly seamlessly. However, it's good to know how it works.
So, to override a class, first find where the original resides (usually /system/libraries or /system/core). Put your extending file in the corresponding /application folder (this is important! If it's under /system/core, the extension MUST be under /application/core). Prefix both the filename and the class name with MY_ (or whatever you set in your configuration), and have it extend the CI_ base class.

Resources