I want to create some rules for a project and 'prefer' the YAML route.

Is there some documentation already on how to write these by hand?

What I did so far:

Inspect modules/rules/config/schema/rules.schema.yml of which I'm still not sure how to read exactly.

Checking with rules/tests/modules/rules_test_default_component/rules_test_default_component.info.yml I manage to create a new component in a new module

Using the Rules UI I created a Reaction rule and was able to export it again into my module.

Enable my module showed both Reaction and component

Using drush @drupal.d8 config-get rules.settings I was unable to start debugging as

log_errors: warning
debug_log: false
debug: false
log_level: info

was transformed into carbage

log_errors: 0
debug_log: true
debug: true
log_level: 0

Trying to set modules/rules/config/install/rules.settings.yml I still did not manage to see some logging.

I found probably some bugs?

- I can delete these items which should not be the case. I expected disable/enable
- Uninstall my module left the two items intact. I expected these to disappear. But uninstalling rules module deletes the items so probably I should have set dependencies better? But then modules/rules/tests/modules/rules_test_default_component/config/install/rules.component.rules_test_default_component.yml should have too.

Comments

clemens.tolboom created an issue. See original summary.

Torenware’s picture

I've been "reverse engineering" this for a while, and yeah, there's likely a bug or two.

No one's actually documented the format yet, although working on a doc would be a good thing. This is how I've approached this so far:

  1. Use code to create a reaction rule. There are some examples in the test suite for this.
  2. In the code, save your rule as a reaction rule configuration.
  3. Using the Devel module's config support, use "drush config-get" to get your saved reaction rule's YAML.

Some sample code:

$expressionManager = \Drupal::service('plugin.manager.rules_expression');
$storage = \Drupal::service('entity.manager')->getStorage('rules_reaction_rule');

$and = $expressionManager->createAnd()
  ->addCondition('rules_test_false')
  ->addCondition('rules_test_true', ContextConfig::create()->negateResult())
  ->negate();


$or = $expressionManager->createOr()
  ->addCondition('rules_test_true', ContextConfig::create()->negateResult())
  ->addCondition('rules_test_false')
  ->addCondition($and);


$rule = $expressionManager->createReactionRule();
$rule->addExpressionObject($or);

// Add an action to it.
$rule->addAction('rules_test_log');

// Grab the rule's configuration and make sure it contains no
// embedded objects, which will cause a crash when we try to
// save it later. This may or may not still be needed; see
// below.
$rule_config = _get_rules_configuration($rule);
print "Rule Config:\n";
print_r($rule_config);

//
// Saving a rule as a ConfigEntity:
//
$new_rule = $expressionManager->createReactionRule($rule_config);
$new_entity = $storage
    ->create(['id' => 'test_rule_with_event']);
$new_entity->setExpression($new_rule);
$new_entity->set('event', 'rules_user_login');
$new_entity->set('tag', 'demo example');
$new_entity->set('description', 'Reaction Rule built via code w/o UI');
$new_entity->save();

print "Rule saved.\n";

/**
 * This routine was needed back in August to get around a bug in how
 * reaction rules were saved. It may or may not still be needed.
 * It's possible that getConfiguration() can now do the job itself,
 * without Rules crashing when a rule is saved.
 */
function _get_rules_configuration($expression) {
  $config = $expression;
  if (is_object($expression) and $expression instanceof ExpressionBase) {
    $config = $expression->getConfiguration();
    //this should be an array; recurse down it
  }
  if (!is_array($config)) {
    return $config;
  }
  $new_config = array();
  foreach ($config as $key => $value) {
    if (is_object($value) and $value instanceof ExpressionBase) {
      $value = _get_rules_configuration($value);
    }
    else if (is_array($value)) {
      $value = _get_rules_configuration($value);
    }
    $new_config[$key] = $value;
  }
  return $new_config;
}
Torenware’s picture

As for logging: I'm not sure if the old logging system works or not. There is a logging "widget" implemented in the rules_test module found in the tests/modules directory. I use it in my sample.

clemens.tolboom’s picture

@Torenware thanks for the reply. I will try your code as that leads to a YAML file in the end :-)

Regarding logging the code is in place to trigger logging. My guess now is drush breaking the config.

TR’s picture

Status: Active » Closed (outdated)

The core configuration system has undergone some pretty significant changes since this issue was first posted, and rather than try to answer everything in the original post in the context of how it now works, it's easier to point you to some documentation like https://www.drupal.org/docs/8/configuration-management/managing-your-sit... (along with other related pages that are linked from that).

clemens.tolboom’s picture

Assigned: Unassigned » clemens.tolboom
Status: Closed (outdated) » Active

@TR ehm ... I guess you haven't read this issues purpose which is about Create rules YAML files.

The link you gave us is not pointing to Rules related documentation. As Rules phase 3 is still not done #2 seems a good example of how one can build a Rule through code.

I reopen and assign to myself to check the current docs.

clemens.tolboom’s picture

Checking https://www.drupal.org/project/rules the are several links to documentation

In the Docs block
- https://www.drupal.org/docs/8/modules/rules (which seems autogenerated and is kinda empty)
- http://drupal.org/documentation/modules/rules (tags as external but is for D7/6)

On https://www.drupal.org/project/rules the page itself
- http://docs.d8rules.org/ which is for developing/contributing rules.

https://github.com/fago/rules-docs is 2 years old

So #2 is nowhere covered. I'll try to create a PR for the docs page.

clemens.tolboom’s picture

I managed to create both a Rule and a Component with a slightly rewrite of #2. Not sure how to wrap this in either (part of) rules.api.php or documentation.

/**
 * Generate an empty component.
 *
 * @see \Drupal\Tests\rules\Kernel\ConfigEntityTest::testSavingEmptyRule
 */
function rule_test_generate_component() {
  /** @var \Drupal\rules\Engine\ExpressionInterface $expressionManager */
  $expressionManager = \Drupal::service('plugin.manager.rules_expression');

  // Create an empty rule
  $rule = $expressionManager->createRule();

  /** @var \Drupal\rules\Entity\ReactionRuleConfig $storage */
  $storage = \Drupal::service('entity_type.manager')
                    ->getStorage('rules_component');

  $config_entity = $storage->create([
    'id' => 'test_rule_component',
  ])->setExpression($rule);

  $config_entity->save();
}

/**
 * Generate a Rule with (fake) conditions and  action.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 *
 * @see \Drupal\Tests\rules\Kernel\ConfigEntityTest::testReactionRuleSaving
 * @see \Drupal\Tests\rules\Kernel\RulesEngineTest::testRuleCreation
 */
function rule_test_generate_rules_expression() {

  /** @var \Drupal\rules\Engine\ExpressionManager $expressionManager */
  $expressionManager = \Drupal::service('plugin.manager.rules_expression');

  $and = $expressionManager->createAnd()
                                 ->addCondition('rules_test_false')
                                 ->addCondition('rules_test_true', ContextConfig::create()->negateResult())
                                 ->negate();

  $or = $expressionManager->createOr()
                                ->addCondition('rules_test_true', ContextConfig::create()->negateResult())
                                ->addCondition('rules_test_false')
                                ->addExpressionObject($and);

  /** @var \Drupal\rules\Plugin\RulesExpression\RuleInterface $rule */
  $rule = $expressionManager->createRule();

  $rule->addCondition('rules_test_true')
       ->addCondition('rules_test_true')
       ->addExpressionObject($or);

  // Add an action to it.
  $rule->addAction('rules_test_log');

  /** @var \Drupal\rules\Entity\ReactionRuleConfig $storage */
  $storage = \Drupal::service('entity_type.manager')
                    ->getStorage('rules_reaction_rule');

  $config_entity = $storage->create([
    'id' => 'test_rule',
  ])->setExpression($rule);
  $config_entity->save();
}

TR’s picture

@TR ehm ... I guess you haven't read this issues purpose which is about Create rules YAML files.

Well, yes I did, and part of my point was that this issue contains many different issues and guesses unrelated to that title, and is based on a codebase that has changed significantly since the original post, and there is nothing specific to answer and no specific bug to address. IMO the answer to most of the questions can be learned by reading the Core documentation which I linked to. Others, like why did you not see any logging, can be answered by looking at the issue queue and learning that logging is not implemented in the D8 version of Rules yet. But I don't see any value in keeping this issue open as it hasn't been addressed in more than two years, contains a lot of outdated information, and is way too broad to be answered effectively. It would be far more useful to have a specific question rather than a broad open-ended discussion about configuration.

Another part of my point is that this isn't a Rules issue. Config is managed by Core. The contents of config files are defined by the schema, which modules define, but all the code for importing, exporting, modifying, validating against schema, and inspecting config is provided by Core. The structure and content of the schema is also defined by Core. Modules may create configuration settings and configuration entities by providing yml files which are read when the module is installed, but after that configuration is managed by Core.

You also asked about how to interpret schemas - again, that's Core, not Rules. The Core Configuration Manager module (mentioned in the documentation I linked to) will allow you to inspect and modify configuration in detail, using the schema, and if you use the contributed Configuration Inspector module you can also verify configuration files against their schema.

Creating a yml file for ANY module-provided configuration entity is the same - there is nothing specific to Rules here. A short description specific to Rules would be:

  1. Create the Reaction Rule or Rules Component using the Rules UI. (Yes, the UI isn't fully ported to D8 yet, but that's an entirely different subject that is being handled by existing issues in this queue.)
  2. Go to the Configuration Manager and export the Rule. (You can also do this via drush.)
  3. You now have a yml file for that Rule.

This is true of ALL modules that provide configuration files, not just Rules. (For example, substitute View in the above steps, and you will have a prescription for how you can create a yml file for a View and how your contributed module can provide its own Views.)

These yml files typically have to be edited to add enforced dependencies, so that the configuration is deleted (if necessary and if desired) when the modules that provide them are uninstalled. Also, yml files provided in config/install are treated differently than yml files provided in config/optional. Again, that's because of Core, not Rules.

It seems you have done this already, so again it's not clear what you're asking and what you expect to get from this issue.

Trying to set modules/rules/config/install/rules.settings.yml ...

I don't know what this means - the yml is only read by core when the module is enabled - in order to change the values in the rules.settings config you need to use the Rules UI or you need to use drush or you need to use the Configuration Manager, etc. The Rules settings you show are no longer correct, and the drush output being incorrect was fixed many years ago when data typing was fixed in core configuration handling. Your contributed module cannot contain its own rules.settings.yml file - that's not the way configuration works.

As Rules phase 3 is still not done #2 seems a good example of how one can build a Rule through code.

Building a Rule through code was not the original question! The best place to figure that out is by looking at the Rules test cases, where exactly this procedure is done.

If you want to address that, then perhaps open another issue for API documentation. But if you're going to do that please go through the issue queue first - we don't need five different open issues all asking the same thing about how to create Rules programmatically. Consolidating the existing issues, adding information, and contributing will help focus the effort - the current state of the issue queue is pretty bad.

I can delete these items which should not be the case. I expected disable/enable

IF you mean you can delete the Reaction Rules and Rules Components you created, I'm not sure why you think you shouldn't be able to do this? And if you look at the issue queue, you will find that the disable/enable feature that was present in D7 Rules hasn't been ported to D8 yet. You can help out with that issue if this is important to you.

Uninstall my module left the two items intact. I expected these to disappear.

Again, this is answered by learning how Core config is intended to work. Read about enforced dependencies and config/install versus config/optional.

So, I don't expect that I answered all of your questions, and that again demonstrates that this issue should be closed because it is unfocused, concerns Core configuration and not Rules-specific issues, and is way too broad to be addressed here in the issue queue especially when most of the answers are in the Core documentation. You can either open some specific issues that can be answered or you can leave this open for another two years and get no replies. Which is more useful to you and to the community?

clemens.tolboom’s picture

Status: Active » Closed (outdated)

So it be. Rules drains me out and the UI seems to work a little.