Use variable to access config in Grails Holders - grails

Using Grails 2.3.7, I set a property in my config file:
foo.bar = ['whatever']
I can access using Holders...
Holders.config.foo.bar
For convenience I put Holders in util method:
static getCfgProp(key){
Holders.config.get(key)
}
But getCfgProp('foo.bar') doesn't work (guessing because foo.bar is nested map key).
It works if I flatten the config:
static getCfgProp(key){
Holders.getFlatConfig().get(key)
}
..but don't want to do that each time method is invoked.
Tried these, none worked, I must be missing something simple
Holders.config."${key}"
Holders.config."$key"
Holders.config.getProperty(key)
Holders.config.(key)

This is what I've used for displaying a config var value (via a form input):
grailsApplication.config.flatten()."${it}"
where ${it} is the input string. This works for both non-nested and nested keys due to the flatten() method.
EDIT: just realised this is the equivilent of your Holders.getFlatConfig() so probably not useful. Not sure why you
don't want to do that each time method is invoked
Performance? Have you benchmarked it?

This has worked for me with grails-2.5.6:
Holders.config[key].subkey.subsubkey...
Holders.config[key][subkey].subsubkey...
// for Holders.config.foo.bar.zet
Holders.config['foo'].bar.zet
Holders.config['foo']['bar'].zet
Holders.config['foo']['bar']['zet']

Related

Use plugin config as paramater in services.yml

The documentation only shows that you can pass the SystemConfigService as a parameter to another service.
Is there also the possibility to pass directly the value from the plugin configuration?
Background of the question: I would like to initialize directly an instance of an external component. But this expects fixed arguments as strings. Alternatively, one would otherwise have to write some kind of factory.
Hm, it's possible to do. I have not done this directly myself, but 99% confident that it will work. You may need to play around with it a little.
In the services.xml you can use symfony expressions.
<argument type="expression">service('Shopware\Core\System\SystemConfig\SystemConfigService').get('SwagBasicExample.config.example')</argument>
You may need to find the alias name for the Shopware\Core\System\SystemConfig\SystemConfigService class instead. Also check the Symfony documentation, you can do a lot more with this!
I myself passed an array as an argument, but used a custom class as a config getter like so:
<argument type="expression">
{
"shop_is_active": service('config_bridge').get('isActive'),
"customer_number": service('config_bridge').get('customerNumber'),
"shop_number": service('config_bridge').get('shopNumber'),
"apikey": service('config_bridge').get('apiKey')
}
</argument>
Not strictly necessary as Shopware already requires it, but always a good practice to add the requirement to your plugin composer file:
"require": {
...,
"symfony/expression-language": "~5.3.0|~5.4.0"
},
As of today it's not possible to inject specific system_config values in services.

Unit testing grails ConfigSlurper behavior

I'd like to write tests that would test behavior of externalized configs and assert that what gets set is what I expect. This is for the specific case where something like this is done:
Config.groovy:
a.reused.value = 'orig'
my.variable = '${a.reused.value}'
Externalized groovy file:
a.reused.value = 'new_value'
I expect that both a.reused.value and my.variable would be 'new_value'.
Now, I think I could have my unit test read in strings representing these config files (I do similar things for other unit tests to populate Holders.grailsApplication.config, for example), utilizing perhaps merge?
But what I cannot figure out is how to get the value that Grails actually gets during application run time. Instead, I get "${a.reused.value}" in my unit tests.
Is there a way to mimic this behavior of what Grails does of actually resolving this value? I did some digging around in Grails 2.4.4 source (which is what we are using) and didn't have any luck in figuring this part out. I also did try Eval.me(), but that doesn't seem to be quite right either.
While setting my.variable, you are not using a GString object, causing the expression to be treated as a value itself. Use double quotes to resolve expression automatically.
a.reused.value = 'orig' my.variable = "${a.reused.value}"
Update 1:
What you want to do is directly not possible. You are assigning the value to a variable from an expression. During evaluation of the config object for the first time, my.variable has been assigned a value, and now it doesn't contain an expression any more. So you have two options: 1) either reassign the second variable in external config also or 2) use a closure to assign the value to second variable.
my.variable = { -> "$a.reused.value" }
and while accessing do: grailsApplication.config.my.variable.call()
But again, in your code, you would have to be sure that this variable contains a closure not a value itself.

How do you use concordion:run with parameters?

I would like to run a Concordion spec using a parameter. What I'd like to do is execute the spec using concordion:run. A little research pointed me to the existence of a concordion:params attribute, but I cannot find any documentation or examples.
I'm not sure how these two commands fit together; should the params element be nested inside the run element or outside? What is the value to fill in concordion:params="?" Where do I specify the param values themselves--in a concordion:set call?
concordion:params is an attribute to be used on the same element as the concordion:run attribute.
For example, in MyIndex.html:
<a concordion:run="concordion" concordion:params="foo=5" href="MySpec.html">My Spec</a>
with the fixture class:
#RunWith(ConcordionRunner.class)
#FullOGNL
public class MyIndex {
public void setFoo(Integer foo) {
System.out.println("foo = " + foo);
}
}
Note that the #FullOGNL attribute is required to allow the syntax foo=5 in the expression that wouldn't otherwise be allowed.
NOTE:
Tim Wright has pointed out an issue with this approach:
The issue I see is that the same specification might be run from two
different specifications (or run twice from a single specification)
with different parameters as well as from jUnit with no parameters. As
we only create one HTML file, the behaviour might not be what the user
expects. It also means that using concordion:run will create a
different specification from running the spec directly as a jUnit test
- which is something we've tried hard to avoid.
The current behaviour (with the concordion run cache) is that the
first one to be called will create the HTML file - and the second one
will return a run results from the cache thus ignoring the parameter.
This may mean that we deprecate concordion:params and remove it in 2.0.

Render a partial from a task in Symfony 1.4

I'm trying to render a partial in a Symfony task and having no luck. I found docs in 1.1 that say to just call get_partial() but apparently that's no longer readily available in 1.4. I tried loading the helper manually with sfLoader::getHelpers('Partial'); but I get "Class sfLoader not found". Any help would be greatly appreciated.
For reference what I'm trying to do is generate an HTML file called 'header.html' from my global header partial used in all of my layouts for inclusion in a third-party forum I'm integrating (Simple Machines/SMF).
First load:
sfContext::getInstance()->getConfiguration()->loadHelpers('Partial');
then just call:
get_partial()
Don't forget to add an application name in your task's options. Look for this line:
new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED,'The application name', 'frontend')
Now $this->configuration returns the sfApplicationConfiguration that you need.
This will do the trick:
//load the Partial helper to be able to use get_partial()
$contextInstance = sfContext::createInstance($this->configuration);
$contextInstance->getConfiguration()->loadHelpers('Partial');
sfLoader was deprecated in symfony 1.2 - I think you need to look over the 1.4 API and the upgrade help from whichever version you're familiar with, as these are going to be resources you'll need to refer to a lot.
The trick to solving your problem is to load the helper with the loadHelpers() method provided by the sfApplicationConfiguration class - your task should hook this method in its configure() method. I've not done it before myself, mind...
Just called a partial in a task for use in sending free trial nag email here's the code:
//combines the partial with passed variables and puts the results in a string variable
$contextInstance = sfContext::createInstance($this->configuration);
$contextInstance->getConfiguration()->loadHelpers('Partial');
$partial_in_a_string = $contextInstance->getController()
->getAction('module_name', 'action_name')
->getPartial('module_name/partial_name', array('var_name'=>'var_value'));
you can access the current configuration with
$this->configuration
You can also define application command option in taskClass::configure() method (if you have used the symfony generate:task to generate the task class you should have frontend as default application option).
Optionally you can pass an application using --application=appName from cli when calling your task.
Be sure to have the application option in your configure method:
protected function configure()
{
$this->addOptions(array(
new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name', 'frontend')
))
....
And then, in the execute method:
protected function execute($arguments = array(), $options = array())
{
sfContext::createInstance($this->configuration)->getConfiguration()->loadHelpers('Partial');
On this way you will prevent these errors:
The "default" context does not exist.
and
Argument 1 passed to sfContext::createInstance() must be an instance
of sfApplicationConfiguration

Grails/Compass - How can I change the compqass Connection to point to a different directory?

I use the Searchable plugin with Grails I have the need to change the directory to which Compass points to, depending upon a UI choice by the user.
Normally, this value is set in the compassConnection variable of the searchable map in grails-app/conf/Searchable.groovy, like so, and gets called at app startup time:
searchable { compassConnection = new File( "/path/to/index/file/directory" ).absolutePath
...
}
To do what I need to do, I think that I need to get a handle into the compass config variable and then call config.setConnection("/new/path/to/index/dir")
I don't know how I can get a reference to the config compass variable
Am I right in assuming that I need to call setConnection(String) only, or is(are) there any other step(s) I need to take?
Really appreciate any help that I can get,
Shailen
In Searchable.groovy file, within searchable { } closure use this:
compassConnection = new File(
"${userHome}/.grails/projects/${appName}/searchable-index/${grailsEnv}").absolutePath
Well, sorry I was not careful reading your question. I guess this class must be your starting point: http://www.compass-project.org/docs/2.2.0/api/

Resources