From 2c2baac644fa4413c89adaef77f79db9cc30c4c0 Mon Sep 17 00:00:00 2001 From: John Carroll Date: Thu, 28 Dec 2017 02:52:53 -0800 Subject: [PATCH 1/3] Proposed ActiveLabel api --- docs/ActiveLabel.rst | 289 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 233 insertions(+), 56 deletions(-) diff --git a/docs/ActiveLabel.rst b/docs/ActiveLabel.rst index 6608e0182..6e469cc8c 100644 --- a/docs/ActiveLabel.rst +++ b/docs/ActiveLabel.rst @@ -1,135 +1,312 @@ ActiveLabel =========== -``ActiveNode`` provides an ability to define inheritance of models which also gives subclasess the labels of their parent models. In Ruby, however, inheritence of classes is not sufficient. Sometimes is makes more sense to be able to build a module which defines behavior (or "concerns") which could be applied to any model. This is what ``ActiveLabel`` provides. - -``ActiveLabel`` modules can be defined in two ways: - - * Default: Where the module's behavior is always defined on the ``ActiveNode`` model and the model's nodes always have a corresponding label in Neo4j - * Optional: Where the module's behavior is defined on the class only when the model's nodes have a corresponding label in Neo4j +As you build out your application's models, you likely will want to share code between them. +Neo4jrb's ``ActiveNode`` module supports class inheritance, allowing you to create "submodels" which +inherit the methods and labels of their ``ActiveNode parents`` while also adding their own submodel specific +label & methods. This code sharing strategy should be familiar to anyone coming from the ActiveRecord world. + +Sometime's however, inheritance is not always appropriate. Sometimes what you want to do is conditionally add +a module of functionality to an ActiveNode model, but only if a specific label is present on the node. +For an example of when this is needed, look at the Neo4j's example movie database (https://neo4j.com/developer/movie-database/). +In this example, a Person node is sometimes an Actor, sometimes a Director, sometimes a User, and sometimes a +combination of Actor, Director, and/or User. + +Multiple inheritance such as this is not possible using ``ActiveNode`` inheritence (or with ActiveRecord). This +is where ``ActiveLabel`` comes to the rescue! ``ActiveLabel`` allows you to create a Ruby module which is only +applied to an ``ActiveNode`` model when a specific label is attached to an instance of the model. Using our +example movie database from above, you could create an ``ActiveLabel`` module which only adds Actor methods and +properties to an instance of Person if a Person node also has an Actor label. + +``ActiveLabel`` can fully replace ``ActiveNode`` inheritence, but it involves a different way of thinking +then what many ActiveRecord developers might be used to. If you're just starting out with Neo4jrb, you might find +it easiest to stick with ``ActiveNode`` and inheritence. As you get more comfortable with Neo4j's flexibility and +easy polymorphism, you'll find that ``ActiveLabel`` can be incredibly powerful and flexible. .. code-block:: ruby class Person include Neo4j::ActiveNode + include Actor + include Director + include User property :name, type: String - - label :HasAddress - label :Destroyable end - class Organization + class Movie include Neo4j::ActiveNode - property :title, type: String + property :name, type: String + end - label :HasAddress - label :Destroyable +.. code-block:: ruby + + module Actor + include Neo4j::ActiveLabel + include InShowbusiness + + has_many :out, :acts_in, type: :ACTS_IN, model_class: :Movie end + module Director + include Neo4j::ActiveLabel + include InShowbusiness -.. code-block:: ruby + has_many :out, :directed, type: :DIRECTED, model_class: :Movie + end + + module InShowbusiness + include Neo4j::ActiveLabel - class Address - property :line1, type: String - property :line2, type: String - property :country, type: String - property :postal_code, type: String + self.associated_labels = [:Actor, :Director] + self.associated_labels_matcher = :any + + property :biography + property :lastModified + property :version end - module HasAddress + module User include Neo4j::ActiveLabel - included do - has_one :out, :address, type: :HAS_ADDRESS - end + property :login + property :password + property :roles module InstanceMethods - def distance_from(has_address_object) - address.distance_from(has_address_object.address) + def administrate_stuff + puts "administrate stuff" end end end +Creating +-------- + +``ActiveLabel`` modules are defined by creating a standard ruby module with ``include Neo4j::ActiveLabel``. +By convention, the ``ActiveLabel`` module will be associated with a label equal to the module name. For example, +in the example above, the ``Actor`` ``ActiveLabel`` module is associated with the ``:Actor`` label. You can +customize the label(s) which an ``ActiveLabel`` module is associated with using ``self.associated_labels =``. You must also +include an ``ActiveLabel`` module in an ``ActiveNode`` class if you want the class to respond to the ``ActiveLabel``. + +``ActiveLabel`` modules have several parts: + .. code-block:: ruby - module Destroyable - include Neo4j::ActiveLabel + module Actor + include Neo4j::ActiveLabel # adds ActiveLabel functionality to the Actor module - follows_label :Destroyed + # ``ActiveLabel`` modules can have associations and properties just like ``ActiveNode`` classes + property :popularity + has_one :out, :friend, type: :FRIEND, model_class: :Person included do - property :destroyed_at, type: DateTime + # When a node is retrieved from the database, it is mapped to an ``ActiveNode`` class and a new + # instance of that class is created. We'll call this created object obj A. + # If obj A's class includes this ``ActiveLabel``, and, additionally, obj A has the label associated + # with this ``ActiveLabel``, then this included block will be evaluaded + # within the context of obj A. end module InstanceMethods - def destroy - destroyed_at = Time.now + # After obj A have been found and initialized, before the included block is evaluated, obj A will + # be extended with the InstanceMethods (e.g. obj.extend(InstanceMethods)) - super + def act + puts "I acted!" end end module ClassMethods - def destroyed_recently - all.where("#{identity}.destroyed_at > ?", 1.week.ago) + # Similar to ``ActiveSupport::Concern``, when this ``ActiveLabel`` module is included in an + # ``ActiveNode`` class, the class will be extended with these singleton methods (e.g. Person.extend(ClassMethods)) + + def actor_popularity_scale + puts "5 stars = excellent. 1 star = poor." end end end +``ActiveLabel`` modules only describe functionality that is tied to a label. Actually adding that label to instances of a class +is a seperate step. If you'd like to add a label to specific instances of a class, you can use standard ``neo4j-core`` methods +``add_label()`` or ``remove_label``. You can also use special helper methods that ``ActiveLabel`` adds to a class when it is +included in a class -Creating --------- +.. code-block:: ruby + + # Initializes a Person with additional Actor label + Person.actor.new + + # Creates a Person with additional Actor label + Person.actor.create + + # Creates a Person with additional Actor AND Director labels + Person.actor.director.create + +If you'd like to `always` add one or more additional labels to instances of a class, you can use the ``ActiveNode`` ``label`` method -If an ``ActiveLabel`` does not declare ``follows_label``, creating a node will attach the corresponding label. Otherwise you must trigger the attachment of the label: +.. code-block:: ruby + + class Person + include Neo4j::ActiveNode + include Actor + include Director + include User + + # the ``label :Actor, optional: true`` automatically method adds the label ``:Actor`` to every instance of the Person class. The :Actor label is technically + # optional, even though it is always added, because a node will still be mapped to the Person class even if you manually remove the + # :Actor label from it. + label :Actor, optional: true + + # If you call the ``label`` method without the ``optional: true`` argument, then nodes will only be mapped to the Person class if the label is + # also present on the node. (i.e. removing the :User label from a node will mean that that node is no longer considered a Person) + label :User + end + + +Including an ``ActiveLabel`` module in a class will `automatically` add a few helper methods to the class and class instances. +Using the ``Actor`` ``ActiveLabel`` module as an example. + +1. You can call ``person.actor?`` which will return true if the obj has the label associated with the ``Actor`` ``ActiveLabel``. +2. You can call ``Person.actor.new`` or ``Person.actor.create`` to inialize / create a new ``Person`` instance with the label +associated with ``Actor``. +3. You can call ``Person.actor.all`` or ``Person.actor.first`` to return all ``Person`` nodes with the ``Actor`` label. In fact, +calling ``Person.actor`` simply adds a label scope, which can be combined with any custom scopes you have (e.g. +``Person.most_popular`` -> ``Person.actor.most_popular`` + +To dry up your code, you can include ``ActiveLabel B`` inside ``ActiveLabel A``. This ensures that when you include +``ActiveLabel A`` in a module you also always include ``Activelabel B`` .. code-block:: ruby - # Node gets both `Person` and `HasAddress` labels - person = Person.create + module Hollywood + include Neo4j::ActiveLabel + + self.associated_labels = [:Actor, :Director] + self.associated_labels_matcher = :any - # `Destroyed' label is added. `mark_destroyed` method is automatically defined via `follows_label` definition - person.label_as_destroyed + property :name + end - # `Destroyed' label is removed - person.label_as_not_destroyed + module Actor + include Neo4j::ActiveLabel + include Hollywood + end + + module Director + include Neo4j::ActiveLabel + include Hollywood + end Querying -------- -``ActiveLabel`` allows your Ruby module to act like a model class. However, since you can add a label to any module, you can query for nodes across modules: +Querying for ``ActiveLabel``s is easy, and can cut accross classes. .. code-block:: ruby - Destroyable.all - - HasAddress.as(:obj).address.where(postal_code: '12345').pluck('DISTINCT obj') + # This returns all nodes which have the Actor label + Actor.all -By default this returns all nodes for all models where the ``ActiveLabel`` module is defined. If ``follows_label`` is declared, this returns just those nodes which have the label. + # This returns all nodes with the Director label which have a directed association to + # a node with the title "Star Wars" + # This works because the ``Director`` ``ActiveLabel`` defines a ``directed`` association + Director.as(:dir).directed.where(title: 'Star Wars').pluck('DISTINCT dir') -By defining the ``follows_label``, some methods are automatically provided to allow you to filter and interrogate: +Including an ``ActiveLabel`` module in a class will `automatically` add a few helper methods to the class and class instances. .. code-block:: ruby - Person.labeled_as_destroyed + Person.actor.all + + Person.actor.first - Person.first.labeled_as_destroyed? +Calling ``Person.actor`` simply adds a label ``:Actor`` scope, which can be combined with any custom scopes you have (e.g. +``Person.most_popular`` -> ``Person.actor.most_popular`` Associations ~~~~~~~~~~~~ -You can even create associations to traverse to labels: +You can create associations with ActiveLabels: .. code-block:: ruby - class Organization + class Movie include Neo4j::ActiveNode - has_many :out, :addressables, type: :HAS_ADDRESSABLE_OBJECT, label_class: :HasAddress + has_many :in, :actors, type: :ACTS_IN, label_module: :Actor - # `model_class` acts as a filter to the `label_class` argument. Both `model_class` and `label_class` can be arrays - has_many :out, :addressable_people, type: :HAS_ADDRESSABLE_OBJECT, label_class: :HasAddress, model_class: :Person + # `model_class` acts as a filter to the `label_module` argument. Both `model_class` and `label_module` can be arrays + has_many :in, :human_actors, type: :ACTS_IN, label_module: :Actor, model_class: :Person end +If you want more control over your association, you can use the ``node_labels:`` option instead + +.. code-block:: ruby + + class Movie + include Neo4j::ActiveNode + + # The node_labels option accepts a two dimentional array. Each array in the node_labels array includes a set of labels + # that the association will match against. In the example below, the ``actors`` association only includes nodes with + # an ``<-[:ACTS_IN]-`` relation to the ``Movie`` which have either ``:Actor:Person`` labels or ``:Actor:Animal`` labels + has_many :in, :actors, type: :ACTS_IN, node_labels: [[:Actor, :Person], [:Actor, :Animal]] + + # Other valid params for the node_labels option are + has_many :in, :actors, type: :ACTS_IN, node_labels: [[:Actor, :Person], :Actor] + + # or + has_many :in, :actors, type: :ACTS_IN, node_labels: :Actor + end + +Note, while the ``label_module`` option requires the params to resolve to ``ActiveLabel`` modules, the ``node_labels`` +option doesn't. It simply matches against the specified labels. + +Multiple Conditions +------------------- + +Sometimes you may wish for ``ActiveLabel`` code to be associated with an array of labels, rather than a single label. +Perhaps the code triggers if `any` label in the array is present, or perhaps it only triggers if `all` labels in the +array are present. + +.. code-block:: ruby + + module Hollywood + include Neo4j::ActiveLabel + + self.associated_labels = [:Actor, :Director] + self.associated_labels_matcher = :any + + # OR + + self.associated_labels = [:Actor, :Director] + self.associated_labels_matcher = :all + + end + +By default, ``self.associated_labels_matcher == :all`` + +included_if block +~~~~~~~~~~~~~~~~~ + +Sometimes conditional functionality is limited to one class, and is simple enough that a full ``ActiveLabel`` seems like +overkill. You can make use of ``included_if_any`` and ``included_if_all`` methods to specify blocks of code that only +run if `any` or `all` of the specified labels are present on a node. + +.. code-block:: ruby + + class Person + include Neo4j::ActiveLabel + + # only run if a Person node also has the Actor or Director labels + included_if_any :Actor, :Director do + property :medium_ego + end + + # only run if a Person node also has the Actor AND Director labels + included_if_all :Actor, :Director do + property :large_ego + end + end From 19e45d8551187e23fc364d555397ac97082fea2c Mon Sep 17 00:00:00 2001 From: John Carroll Date: Thu, 28 Dec 2017 03:22:19 -0800 Subject: [PATCH 2/3] upd: ActiveLabel API docs --- docs/ActiveLabel.rst | 62 +++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/docs/ActiveLabel.rst b/docs/ActiveLabel.rst index 6e469cc8c..45c3a5147 100644 --- a/docs/ActiveLabel.rst +++ b/docs/ActiveLabel.rst @@ -3,7 +3,7 @@ ActiveLabel As you build out your application's models, you likely will want to share code between them. Neo4jrb's ``ActiveNode`` module supports class inheritance, allowing you to create "submodels" which -inherit the methods and labels of their ``ActiveNode parents`` while also adding their own submodel specific +inherit the methods and labels of their ``ActiveNode`` parents while also adding their own submodel specific label & methods. This code sharing strategy should be familiar to anyone coming from the ActiveRecord world. Sometime's however, inheritance is not always appropriate. Sometimes what you want to do is conditionally add @@ -12,16 +12,18 @@ For an example of when this is needed, look at the Neo4j's example movie databas In this example, a Person node is sometimes an Actor, sometimes a Director, sometimes a User, and sometimes a combination of Actor, Director, and/or User. -Multiple inheritance such as this is not possible using ``ActiveNode`` inheritence (or with ActiveRecord). This +Multiple inheritance such as this is not possible with ``ActiveNode`` (or with ActiveRecord). This is where ``ActiveLabel`` comes to the rescue! ``ActiveLabel`` allows you to create a Ruby module which is only applied to an ``ActiveNode`` model when a specific label is attached to an instance of the model. Using our example movie database from above, you could create an ``ActiveLabel`` module which only adds Actor methods and -properties to an instance of Person if a Person node also has an Actor label. +properties to an instance of Person if a Person node also has an Actor label. Or only adds InShowbusiness methods +and properties to an instance of Person if a Person node has `either` Actor or Director labels. ``ActiveLabel`` can fully replace ``ActiveNode`` inheritence, but it involves a different way of thinking then what many ActiveRecord developers might be used to. If you're just starting out with Neo4jrb, you might find -it easiest to stick with ``ActiveNode`` and inheritence. As you get more comfortable with Neo4j's flexibility and -easy polymorphism, you'll find that ``ActiveLabel`` can be incredibly powerful and flexible. +it easiest to stick with the "ActiveRecord" like workflow provided by ``ActiveNode`` and inheritence. +But as you get more comfortable with Neo4j's flexibility and +polymorphism, you'll might find that ``ActiveLabel`` is the better option for many tasks. .. code-block:: ruby @@ -106,13 +108,13 @@ include an ``ActiveLabel`` module in an ``ActiveNode`` class if you want the cla # When a node is retrieved from the database, it is mapped to an ``ActiveNode`` class and a new # instance of that class is created. We'll call this created object obj A. # If obj A's class includes this ``ActiveLabel``, and, additionally, obj A has the label associated - # with this ``ActiveLabel``, then this included block will be evaluaded + # with this ``ActiveLabel``, then this included block will be evaluated # within the context of obj A. end module InstanceMethods - # After obj A have been found and initialized, before the included block is evaluated, obj A will - # be extended with the InstanceMethods (e.g. obj.extend(InstanceMethods)) + # After obj A has been found and initialized, before the included block is evaluated, obj A will + # be extended with these InstanceMethods (e.g. obj.extend(InstanceMethods)) def act puts "I acted!" @@ -155,26 +157,26 @@ If you'd like to `always` add one or more additional labels to instances of a cl include Director include User - # the ``label :Actor, optional: true`` automatically method adds the label ``:Actor`` to every instance of the Person class. The :Actor label is technically - # optional, even though it is always added, because a node will still be mapped to the Person class even if you manually remove the - # :Actor label from it. + # ``label :Actor, optional: true`` automatically adds the label ``:Actor`` + # to every instance of the Person class. The :Actor label is technically + # optional, even though it is always added, because a node will still be mapped + # to the Person class even if you manually remove the :Actor label from it. label :Actor, optional: true - # If you call the ``label`` method without the ``optional: true`` argument, then nodes will only be mapped to the Person class if the label is - # also present on the node. (i.e. removing the :User label from a node will mean that that node is no longer considered a Person) + # If you call the ``label`` method without the ``optional: true`` argument, + # then nodes will only be mapped to the Person class if the label is + # also present on the node. (i.e. removing the :User label from a node will + # mean that that node is no longer considered a Person) label :User end Including an ``ActiveLabel`` module in a class will `automatically` add a few helper methods to the class and class instances. -Using the ``Actor`` ``ActiveLabel`` module as an example. +For example, using the ``Actor`` ``ActiveLabel`` module: 1. You can call ``person.actor?`` which will return true if the obj has the label associated with the ``Actor`` ``ActiveLabel``. -2. You can call ``Person.actor.new`` or ``Person.actor.create`` to inialize / create a new ``Person`` instance with the label -associated with ``Actor``. -3. You can call ``Person.actor.all`` or ``Person.actor.first`` to return all ``Person`` nodes with the ``Actor`` label. In fact, -calling ``Person.actor`` simply adds a label scope, which can be combined with any custom scopes you have (e.g. -``Person.most_popular`` -> ``Person.actor.most_popular`` +2. You can call ``Person.actor.new`` or ``Person.actor.create`` to initialize / create a new ``Person`` instance with the additional ``Actor`` label. +3. You can call ``Person.actor.all`` or ``Person.actor.first`` to return all ``Person`` nodes with the ``Actor`` label. In fact, calling ``Person.actor`` simply adds a label scope, which can be combined with any custom scopes you have (e.g. ``Person.most_popular`` -> ``Person.actor.most_popular``) To dry up your code, you can include ``ActiveLabel B`` inside ``ActiveLabel A``. This ensures that when you include ``ActiveLabel A`` in a module you also always include ``Activelabel B`` @@ -203,7 +205,7 @@ To dry up your code, you can include ``ActiveLabel B`` inside ``ActiveLabel A``. Querying -------- -Querying for ``ActiveLabel``s is easy, and can cut accross classes. +Querying for ``ActiveLabel``s is easy, and can allow you to query across classes. .. code-block:: ruby @@ -223,7 +225,7 @@ Including an ``ActiveLabel`` module in a class will `automatically` add a few he Person.actor.first -Calling ``Person.actor`` simply adds a label ``:Actor`` scope, which can be combined with any custom scopes you have (e.g. +Calling ``Person.actor`` simply adds a label scope, which can be combined with any custom scopes you have (e.g. ``Person.most_popular`` -> ``Person.actor.most_popular`` Associations @@ -238,7 +240,8 @@ You can create associations with ActiveLabels: has_many :in, :actors, type: :ACTS_IN, label_module: :Actor - # `model_class` acts as a filter to the `label_module` argument. Both `model_class` and `label_module` can be arrays + # `label_module` acts as a filter to the `model_class` argument. + # Both `model_class` and `label_module` can be arrays has_many :in, :human_actors, type: :ACTS_IN, label_module: :Actor, model_class: :Person end @@ -249,9 +252,10 @@ If you want more control over your association, you can use the ``node_labels:`` class Movie include Neo4j::ActiveNode - # The node_labels option accepts a two dimentional array. Each array in the node_labels array includes a set of labels - # that the association will match against. In the example below, the ``actors`` association only includes nodes with - # an ``<-[:ACTS_IN]-`` relation to the ``Movie`` which have either ``:Actor:Person`` labels or ``:Actor:Animal`` labels + # The node_labels option accepts a two dimentional array. Each array in the node_labels + # array includes a set of labels that the association will match against. In the example + # below, the ``actors`` association only includes nodes which have either ``:Actor:Person`` + # OR ``:Actor:Animal`` labels and have an ``<-[:ACTS_IN]-`` relation to a ``Movie`` node has_many :in, :actors, type: :ACTS_IN, node_labels: [[:Actor, :Person], [:Actor, :Animal]] # Other valid params for the node_labels option are @@ -261,8 +265,8 @@ If you want more control over your association, you can use the ``node_labels:`` has_many :in, :actors, type: :ACTS_IN, node_labels: :Actor end -Note, while the ``label_module`` option requires the params to resolve to ``ActiveLabel`` modules, the ``node_labels`` -option doesn't. It simply matches against the specified labels. +Note, while the ``label_module`` option requires its params to resolve to ``ActiveLabel`` modules, the ``node_labels`` +option doesn't. The ``node_labels`` option simply matches against the specified labels. Multiple Conditions ------------------- @@ -286,12 +290,12 @@ array are present. end -By default, ``self.associated_labels_matcher == :all`` +By default, ``self.associated_labels_matcher == :any`` included_if block ~~~~~~~~~~~~~~~~~ -Sometimes conditional functionality is limited to one class, and is simple enough that a full ``ActiveLabel`` seems like +Sometimes conditional functionality is limited to one class, and is simple enough that a full ``ActiveLabel`` module seems like overkill. You can make use of ``included_if_any`` and ``included_if_all`` methods to specify blocks of code that only run if `any` or `all` of the specified labels are present on a node. From 8f5b1ecf4b6976c4b26f34dd095b8e333500a78c Mon Sep 17 00:00:00 2001 From: John Carroll Date: Thu, 28 Dec 2017 03:46:20 -0800 Subject: [PATCH 3/3] upd: clarified stuff --- docs/ActiveLabel.rst | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/ActiveLabel.rst b/docs/ActiveLabel.rst index 45c3a5147..b2bf84b7b 100644 --- a/docs/ActiveLabel.rst +++ b/docs/ActiveLabel.rst @@ -170,14 +170,6 @@ If you'd like to `always` add one or more additional labels to instances of a cl label :User end - -Including an ``ActiveLabel`` module in a class will `automatically` add a few helper methods to the class and class instances. -For example, using the ``Actor`` ``ActiveLabel`` module: - -1. You can call ``person.actor?`` which will return true if the obj has the label associated with the ``Actor`` ``ActiveLabel``. -2. You can call ``Person.actor.new`` or ``Person.actor.create`` to initialize / create a new ``Person`` instance with the additional ``Actor`` label. -3. You can call ``Person.actor.all`` or ``Person.actor.first`` to return all ``Person`` nodes with the ``Actor`` label. In fact, calling ``Person.actor`` simply adds a label scope, which can be combined with any custom scopes you have (e.g. ``Person.most_popular`` -> ``Person.actor.most_popular``) - To dry up your code, you can include ``ActiveLabel B`` inside ``ActiveLabel A``. This ensures that when you include ``ActiveLabel A`` in a module you also always include ``Activelabel B`` @@ -202,6 +194,16 @@ To dry up your code, you can include ``ActiveLabel B`` inside ``ActiveLabel A``. include Hollywood end +Helper methods +~~~~~~~~~~~~~~ + +Including an ``ActiveLabel`` module in a class will `automatically` add a few helper methods to the class and class instances. +For example, using the ``Actor`` ``ActiveLabel`` module: + +1. You can call ``person.actor?`` which will return true if the obj has the label associated with the ``Actor`` ``ActiveLabel``. +2. You can call ``Person.actor.new`` or ``Person.actor.create`` to initialize / create a new ``Person`` instance with the additional ``Actor`` label. +3. You can call ``Person.actor.all`` or ``Person.actor.first`` to return all ``Person`` nodes with the ``Actor`` label. In fact, calling ``Person.actor`` simply adds a label scope, which can be combined with any custom scopes you have (e.g. ``Person.most_popular`` -> ``Person.actor.most_popular``) + Querying -------- @@ -296,8 +298,10 @@ included_if block ~~~~~~~~~~~~~~~~~ Sometimes conditional functionality is limited to one class, and is simple enough that a full ``ActiveLabel`` module seems like -overkill. You can make use of ``included_if_any`` and ``included_if_all`` methods to specify blocks of code that only -run if `any` or `all` of the specified labels are present on a node. +overkill. You can make use of ``ActiveNode``'s ``included_if_any`` and ``included_if_all`` methods to specify blocks of code that only +run if `any` or `all` of the specified labels are present on a node (note, these methods resolve their params to labels, +rather than ``ActiveLabel`` modules. This means that you can match against an optional label which does not have an associated ``ActiveLabel`` +module). .. code-block:: ruby