Description
Rules Collections adds the ability to loop in Rules Conditions.
There are two kinds of loops: Any and All.
Any evaluates to true if any item in the list causes its loop body to evaluate to true.
All evaluates to true if every item in the list causes its loop body to evaluate to true.
Motivation
Commerce uses Rules to control the majority of its functions. For all events involving an Order entity it is often necessary to alter behavior depending on the kinds of LineItem entities are in the order. For example, only enable shipping if any line item in the order meets some criteria for 'being shippable'. This can be awkward or can even involve dropping down into PHP within the rule. But it is simple enough to encode the store's criteria for being shippable as a set of conditions on the line item. The only thing missing is a way to loop over a list in a condition.
This module closes that gap in a generic way that is not dependent on Commerce, only Rules itself.
Known shortcomings
The "+Add any" and "+Add all" links in the condition pane come before the usual "+Add condition" link as they appear to always be displayed in alphabetical order(fixed, thanks to mitchell)The "+Add any" and "+Add all" links appear in the links for any and all loops though Rules does not support lists of lists(rules does support lists of lists, my mistake)
Why not make this a patch to Rules
Integrating this directly into Rules would be tricky. To do it properly there would be some refactoring necessary in Rules' core. For example, the UI plugin for the Any and All loops is derived from RulesLoopUI which is a RulesActionContainerUI not a RulesConditionContainerUI and then the functionality of RulesConditionContainerUI (a negation checkbox) is bolted on. Similarly, the code in the base class for Any and All is genetically related to both RulesLoop and RulesConditionContainer.
This isn't a problem in practice, despite its inelegance, and works quite well. It does however point to the need for some changes in Rules' core in order to support the features provided by this module without any code duplication or any such infelicities. I do not see a way to do this without breaking the API of Rules at this time, though I am by no mean an expert in Rules' codebase and found most of this out trying to get my module to work :).
Providing the functionality as a separate module does three things:
- It let's those who require this functionality now to get it.
- It will act as a barometer for the need of this functionality (ratio of installs of this module to installs of the rules module). If it remains small, then there's little point in making such sweeping changes to appease a small group who can simply install this module.
- It demonstrates the areas that need to be refactored in Rules' core in order to support such functionality, should it be deemed worth the effort.
Requirements
Resources
- Project page: https://drupal.org/sandbox/soapboxcicero/1966034
- Issue queue: https://drupal.org/project/issues/1966034
- Git repo:
git clone --branch 7.x-1.x http://git.drupal.org/sandbox/soapboxcicero/1966034.git rules_collections cd rules_collections - Parview Online Review Link: http://ventral.org/pareview/httpgitdrupalorgsandboxsoapboxcicero1966034git
(Note that the documentation errors from parview are related to it not recognizing {@inheritdoc} which I read was the standard here: https://drupal.org/node/1354#classes and the only other error is about the name of a Rules method I am overloading and have no control over)
Comments
Comment #1
PA robot commentedWe are currently quite busy with all the project applications and we prefer projects with a review bonus. Please help reviewing and we will take a look at your project right away :-)
Also, you should get your friends, colleagues or other community members involved to review this application. Let them go through the review checklist and post a comment that sets this issue to "needs work" (they found some problems with the project) or "reviewed & tested by the community" (they found no major flaws).
I'm a robot and this is an automated message from Project Applications Scraper.
Comment #2
blazas commentedHi,
you have problem issues in your branch - function comments and method form_extract_values is not in lowerCamel format, it must not contain underscores e.g. formExtractValues.
All of the issues you can check on http://ventral.org/pareview/httpgitdrupalorgsandboxsoapboxcicero1966034git.
There is a problem with branch name, please see http://drupal.org/node/1015226
Comment #3
Matters commentedPlease check your rules_collections.core.inc on line 76. You are trying to access a class field, but was treated like a variable.
Comment #4
h3rj4n commentedIf you comment and the action lays with the maintainer, please set the status to 'needs work'.
Comment #5
mitchell commentedHi soapboxcicero. Thank you for contributing Rules Collections. I just have a short comment and question.
See the fix in #1806560: Alter conditional plugins' module_implements order to adjust UI.
Rules Collections' functionality fits the issues: #1382022: Allow components as conditions #1379306: How to evaluate a condition iteratively?, is that right?
Comment #6
soapboxcicero commented@blazas I included that link in the bottom of my post with an explanation of how those errors were false positives. Namely I'm using {@inheritdoc} and that that I'm overloading the underscored name from another module so I cannot change it. I'm not sure I understand about the branch name. It's 7.x-1.x. Am I misreading the page you linked?
@Matters Thanks! fixed it.
@mitchell Thanks for the link. Works like a charm. Committed. And yes it is related to those issues.
In testing those fixed I noticed that Rules now does in fact support lists of lists (could have for some time and I never noticed) so I retract my question about removing the Add Any/All links on Any/All loops.
Comment #7
blazas commentedHi,
{@inheritdoc} seems ok. As for the branch name - you should any suffix - -dev, -rc1 (all on the link I added to my first comment) so on. Now your project is not in release stage. It's just for reviews.
Comment #8
mitchell commentedI should have been more specific, soapboxcicero. The module author committed a more elegant fix than mine.
Your research about refining Rules' class structure is really insightful. It would be great if you could file an issue about this. You may also be interested in Conditional Rules and potential efforts to merge that in the future.
The last question I have before rtbc is about the name Rules Collections. Could you please explain your reasoning for naming it this? Maybe something like Rules Conditions Lists or Rules List Condition are also worth considering.
Also, the 7.x-1.x-dev branch is unnecessary. The '-dev' release can be packaged by d.o from the latest 7.x-1.x code. And, the label for the 'all' plugin in
rules_collections.rules.incis a repeat.Comment #9
soapboxcicero commented@blazas, thanks for the clarification but I'm going to go with what mitchell said because that's my understanding from reading the page you linked, but I can change the branch if someone can explain why it has to be changed.
@mitchell, in no particular order:
First, I fixed the label error. Thanks for pointing that out.
I see the patch you meant, now. I'm not sure that it's better. I don't see any way calling the module hooks in a different order would effect the functioning of this particular module. zhangtaihao may be correct that module weights should be deprecated in favor of altering the registry. I don't quite buy the argument, but maybe I would after discussing it. Regardless, I think altering the module weight is something far more Drupal contrib devs are familiar with. I'd rather go with the code more people can understand, all else being equal. Though if the weight causes some issue that I'm not seeing, even potentially, I'll happily make the change. I just don't want to make it artificially harder for others to understand the code just so I can claim having a more elegant solution.
For making an issue on Rules, I'm not really sure what I could tell them other than what I wrote above. I'm not an expert on rules. Everything I know about how it works on the inside was from targeted incursions into its depths to try to figure out what I needed to build this module. The only thing I know for certain is that such changes would be fairly sweeping and would certainly require a new backwards-incompatible version. Even then it wouldn't touch on any more than what would be needed to make this module have less code in it. Maybe it would shine a light on a category of issues, though. Judging by your profile, you seem to have a lot of experience with Rules and its extension modules so any advice would be appreciated, or even just some links to similar issues I could use as templates.
Last but not least, honestly, the major reason was that Drupal modules all have incredibly broad, vague names. Aside from that, time and need permitting, I'd like to add more helpers for dealing with lists (suggestions/code welcome). I don't need it that often but if I can figure out how to do a "Filter" action (an action with a condition for a loop body that returns a second list containing only those items for which the loop body evaluates TRUE). Maybe even just Rules List would be better as Rules has only one kind of collection, though.
Comment #10
blazas commentedHi,
it's not neccessary to have it. I had similar problem with my module and name branch. So now it is ok - 7.x-1.x.
Comment #11
mitchell commented@soapboxcicero:
I tried to test if this improved upon Conditional Rules; and they are actually incompatible, because this is a condition plugin type rather than a condition plugin instance. That's not necessarily a design flaw, but it points to a very big set back, as Conditional Rules is a well received enhancement.
Also, Conditional Rules already provides, what seems to be, equivalent functionality- ALL = IF: Data Comparison \ list = list, THEN.. and (I think) ANY = IF: List Contains \ list - thing, THEN..
"Filter" matches the intent of Conditional Rules perfectly. Please give it a try. If you ever want to participate in this project, then many of us would be happy to work with you :) (I wrote some handbook pages and provided examples.)
Comment #12
soapboxcicero commented@mitchell:
Sorry it took so long to respond but things have been hectic. I'm also sorry that my response became so long but I'm trying to make sure we're on the same page exactly. I used a kind of lazy pseudocode to write any example rules below. I hope it's clear If it's confusing I can rewrite them.
Incompatible with Conditional Rules
I have not used Conditional Rules—though I have come close to needing it a handful of times but a chance to play with it always seem to elude me—so I'm not sure how any/all being plugins (which is necessary for them to function as they do) is incompatible. Do you mean that you can't use any/all as the predicate in its conditionals? That makes sense. In php that would be like doing
it's just confusing and messy (if that's even legal and not one of php's many strange corner cases :)) and in both cases you'd want to factor that out into a function/rules condition, respectively, if for no other reason than readability. I'd even say that it would probably be MORE awkward to read in Rules UI.
(I'm sure there's some php function on arrays, which I'm not remembering at the moment, that would let you do something similar without an anonymous function, given the function pred, but I'm trying to make a point).
My understanding is also that rules conditionals can only be embedded in action containers and this module's loops are for condition containers.
ANY and ALL are already provided by ConditionalRules
ANY
For ANY you can get equivalent functionality with conditional rules only in the case where you want to check that any item in a list is a particular object, by doing:
. But ANY is far more general and allows you to test for properties of the items, like
or
, to check that any item in the list has a quantity greater than or equal to 5 or that any item in the list is of the page content type. Not to mention since it's a conditions plugin you can check multiple properties (or sets of properties by embedding an OR container in the loop body).
ALL
For ALL you can't do list1 = list2, as you said above, that's not what ALL does. The 'loop body' of ALL is a condition container that runs on the individual items in the list being looped over. ALL evaluates to true only if every single item in the list being looped over causes its loop body to evaluate to true. So
would be true if EVERY item in the list has a quantity greater than 5 (whereas the version with ANY returns true only if at least one has a quantity greater than 5).
here be dragons
(warning below involves mathematical logic and you can easily skip to next section!)
I assume that by list1 = list2 you meant something like:
You can't do that with all, but you could sort of fudge list1 = list2 by combing any and all
but that wouldn't actually say if list1 = list2, it would say that every item in list2 is in list1 at least once, which isn't equality but a subset relation: list1 ⊆ list2. If list1 were [1, 2, 3, 4] and list2 were [1, 2, 3, 4, 5], the above would evaluate true. You could do
which would give list1 ⊆ list2 and list2 ⊆ list1 and you could decide that that meant list1 = list2 but that's set equality which means that if list1 was [1, 2, 3, 4] and list2 was [4, 3, 3, 1, 3, 2, 1] they'd still evaluate true.
Of course I wouldn't recommend doing this as it is HORRIBLY inefficient. If this functionality is needed it would be better to create standard additional rules conditions to test whether list1 ⊆ list2 or list1 = list2, in the set equality sense. I'd be happy to do so if there are any use cases for this, but I can't think of any myself.
Filter
Actually filter seems to be the only thing discussed so far that you could do today with conditional rules, if I understand the module correctly:
But I think having a separate filter would be preferable as it would be cleaner to read.
Comment #12.0
soapboxcicero commenteddel known shortcomings
Comment #13
PA robot commentedClosing due to lack of activity. Feel free to reopen if you are still working on this application.
I'm a robot and this is an automated message from Project Applications Scraper.
Comment #14
soapboxcicero commentedThe module is complete. I'm just waiting on approval to take it out of sandbox. Is there anything else I could be doing to act on this?
Comment #15
kscheirerThanks for detailing the differences with Conditional Rules module.
You should remove the 7.x-1.x-dev branch, please have a look at the release naming conventions.
Do you have a username and id to add to the README?
Instead of setting the module weight in rules_collections_install(), hook_enable() might be better.
The ventral report doesn't like your function doc format, please add caps and end in periods.
Those are all minor issues though, looks like a nice module, thanks!
----
Top Shelf Modules - Enterprise modules from the community for the community.
Comment #16
soapboxcicero commentedDeleted the -dev branch
I do have a username and id! Fixed.
What is the benefit of using hook_enable vs. hook_install? I've only ever seen it done in hook_install.
The ventral report errors are false positives caused by using {@inheritdoc}, see this bug: https://drupal.org/node/1879768
Comment #17
kscheirerThanks for the updates! hook_enable gets fired every time the module is enabled, while hook_install just happens when the module is first installed. It's a small difference.
Comment #18
mitchell commented@soapboxcicero:
Sorry for leaving you hanging, and thank you for your patience. I updated your account so you can promote Rules Collections to a full project, and I started preparing Rules for the maturation of your work.
I think, for at least the medium term, Rules Collections should continue as you had originally planned, "helpers for dealing with lists (suggestions/code welcome)", because fago has said on multiple occasions that he wants to limit what is included in Rules Core, hence step 1 above.
The FILTER discussion is in #1391840: Action: "Filter list items" based upon any condition(s), so maybe let's use that issue as a [meta] of the code, the examples, and the documentation. I look forward to discussing SUBSET and SUPERSET.
Please see also the Git Vet Promotion Message.
Comment #19
soapboxcicero commented@kscheirer
The only bad thing that can happen if that code fails is that buttons for adding Any/All show up in before the And/Or buttons so even if it doesn't work no biggie; but it's a simple change and clear, so I've gone ahead and applied it.
@mitchell
Wow, thanks for all that hard work!
I've gone ahead an promoted to a full project and created a beta release which I hope to bump to a full release as soon as a few people try it and make sure there aren't any strange corner cases.
I've renamed the project to Rules List Conditions on the advice of rszrama (and fixed the link/name on the handbook page you made).
In the issue for renaming the module I had a bit of a braindump you may find interesting including some stuff specifically regarding filter: https://drupal.org/node/1980880#comment-7349716
I'll get to those issues you pointed to me as soon as I get a chance; thanks for the links.
Comment #20.0
(not verified) commentedanchors