needuml

needuml behaves exactly like the uml directive from the Sphinx extension Sphinxcontrib-PlantUML. So it allows to define PlantUML diagrams.

But gives you access to need object data by supporting Jinja statements, which allows you to use loops, if-clauses, and it injects data from need-objects.

Example

.. needuml::

   {{uml('FEATURE_NEEDUML1')}}
   {{uml('COMP_NEEDUML2')}}

.. feature:: NeedUml example need
   :id: FEATURE_NEEDUML1
   :tags: needuml
   :status: draft

   Example Need for NeedUml.

.. comp:: NeedUml example need 2
   :id: COMP_NEEDUML2
   :tags: needuml
   :status: draft

   Secound example Need for NeedUml.

   .. needuml::

      {{flow('COMP_NEEDUML2')}} {
      card implement
      card {{needs['COMP_NEEDUML2'].status}}
      }

Result

@startuml

node "<size:12>Feature</size>\n**NeedUml example**\n**need**\n<size:10>FEATURE_NEEDUML1</size>" as FEATURE_NEEDUML1 [[../directives/needuml.html#FEATURE_NEEDUML1]] #FFCC00
card "<size:12>Component</size>\n**NeedUml example**\n**need 2**\n<size:10>COMP_NEEDUML2</size>" as COMP_NEEDUML2 [[../directives/needuml.html#COMP_NEEDUML2]] #BFD8D2 {
card implement
card draft
}
@enduml

Feature: NeedUml example need FEATURE_NEEDUML1 ../_images/arrow-right-circle.svg
status: draft
tags: needuml

Example Need for NeedUml.

Component: NeedUml example need 2 COMP_NEEDUML2 ../_images/arrow-right-circle.svg
status: draft
tags: needuml

Secound example Need for NeedUml.

@startuml

card "<size:12>Component</size>\n**NeedUml example**\n**need 2**\n<size:10>COMP_NEEDUML2</size>" as COMP_NEEDUML2 [[../directives/needuml.html#COMP_NEEDUML2]] #BFD8D2 {
card implement
card draft
}
@enduml

Options

extra

Allows to inject additional key-value pairs into the needuml rendering. :extra: must be a comma-separated list, containing key:value pairs.

Example

.. needuml::
   :extra: name:Roberto,work:RocketLab

   card "{{name}}" as a
   card "{{work}}" as b
   a -> b

Result

@startuml

card "Roberto" as a
card "RocketLab" as b
a -> b
@enduml

Note

:extra: values are only available in the current PlantUML code. It is not available in code loaded via uml(id). So we suggest to use them only in non-embedded needuml directives. In an embedded needuml, you can store the information in the options of the need and access them with needflow_need like in needuml introduction.

config

Allows to preconfigure PlantUML and set certain layout options.

For details please take a look into needflow config.

debug

If :debug: is set, a debug-output of the generated PlantUML code gets added after the generated image.

Helpful to identify reasons why a PlantUML build may have thrown errors.

Example

.. needuml::
   :debug:

   node "RocketLab" {
      card "Peter"
   }

Result

@startuml

node "RocketLab" {
   card "Peter"
}
@enduml

@startuml

node "RocketLab" {
   card "Peter"
}
@enduml

key

Allows to store multiple needuml inside a need under arch under the given key, e.g. need["arch"]["key_name"]. If no option key given, then the first needuml will be stored in the need under arch under diagram, need["arch"]["diagram"]. Option :key: value can’t be empty, and can’t be diagram.

Example

.. comp:: Component Y
   :id: COMP_002

   .. needuml::
      :key: sequence

      Alice -> Bob: Hi Bob
      Bob --> Alice: Hi Alice

   .. needuml::
      :key: class

      class System_A as A {
         todo
         open
      }

   .. needuml::

      B -> C: Hi
      C -> B: Hi there

Result

Component: Component Y COMP_002 ../_images/arrow-right-circle.svg

@startuml

Alice -> Bob: Hi Bob
Bob --> Alice: Hi Alice
@enduml

@startuml

class Foo
@enduml

@startuml

B -> C: Hi
C -> B: Hi there
@enduml

save

Specifies the file path to store generated Plantuml-code of current needuml. This given file path can be relative path or file name, e.g. needuml_group_A/my_needuml.puml or my_needuml.puml.

The file will be created and written during each build by using builder needumls or other builder like html with configuration option needs_build_needumls configured.

If given file path already exists, it will be overwritten.

Example

.. int:: Test needuml save
   :id: INT_001

   .. needuml::
      :save: needuml_group_A/my_needuml.puml

      Alice -> Bob: Hi Bob
      Bob --> Alice: Hi Alice

In this example, if builder needumls is used, the plantuml-code will be exported to file at outdir of current builder, e.g. _build/needumls/needuml_group_A/my_needuml.puml.

Result

Interface: Test needuml save INT_001 ../_images/arrow-right-circle.svg

@startuml

Alice -> Bob: Hi Bob
Bob --> Alice: Hi Alice
@enduml

Jinja context

When using Jinja statements, the following objects and functions are available.

needs

A Python dictionary containing all Needs. The need_id is used as key.

Example

.. needuml::

   node "{{needs["FEATURE_NEEDUML1"].title}}"

Result

@startuml

node "NeedUml example need"
@enduml

flow(id)

Loads a Sphinx-Need object as PlantUML object. We use the same layout used for needflow.

This functions represents each Need the same way.

Changed in version 1.0.3: In the past the returned plantuml representation string ends with a newline. Now it is up to the author of the Jinja template to write the newline, which is normally anyway the case. E.g. see the following example, where the two flow() are separated by a newlone. With this approach it is possible to write plantuml code following flow(). E.g. see even the following example, with text following {{flow(“COMP_001”)}}.

Example

.. needuml::

   {{flow("FEATURE_NEEDUML1")}}
   {{flow("COMP_001")}} {
   card manuall_written
   }

Result

@startuml

node "<size:12>Feature</size>\n**NeedUml example**\n**need**\n<size:10>FEATURE_NEEDUML1</size>" as FEATURE_NEEDUML1 [[../directives/needuml.html#FEATURE_NEEDUML1]] #FFCC00
card "<size:12>Component</size>\n**Component X**\n<size:10>COMP_001</size>" as COMP_001 [[../directives/needuml.html#COMP_001]] #BFD8D2 {
card manuall_written
}
@enduml

filter(filter_string)

Finds a list of Sphinx-Need objects that pass the given filter string.

Example

.. needuml::

   {% for need in filter("type == 'int' and status != 'open'") %}
   node "{{need.title}}"
   {% endfor %}

Result

@startuml


node "Test needuml save"

node "Interface A"

@enduml

ref(id, option, text)

Allows to create an hyperlink to a Sphinx-Need object in a PlantUML schema. The text associated to the hyperlink is either defined by option (in this case, Sphinx-Need picks the text of the field specified by option), or by the free text text.

Example

.. needuml::

   Alice -> Bob: {{ref("FEATURE_1", option="title")}}
   Bob -> Alice: {{ref("FEATURE_2", text="A completely free text")}}

Result

@startuml

Alice -> Bob:  [[../index.html#FEATURE_1 Filtering needs]]
Bob -> Alice:  [[../index.html#FEATURE_2 A completely free text]]
@enduml

uml(id)

Loads a Sphinx-Need object as PlantUML object or reuses the stored PlantUML code inside the Sphinx-Need object.

If diagram code is available in the need data under arch, the stored PlantUML diagram gets imported.

Please read Diagram support for details.

Example

.. needuml::

   allowmixing

   {{uml("COMP_001")}}
   {{uml("FEATURE_NEEDUML1")}}

Result

@startuml

allowmixing

class "Class X" as class_x {
  attribute_1
  attribute_2
  function_1()
  function_2()
  function_3()
}

class "Class Y" as class_y {
  attribute_1
  function_1()
}

class_x o-- class_y
node "<size:12>Feature</size>\n**NeedUml example**\n**need**\n<size:10>FEATURE_NEEDUML1</size>" as FEATURE_NEEDUML1 [[../directives/needuml.html#FEATURE_NEEDUML1]] #FFCC00
@enduml

Key argument

uml() supports key argument to define which PlantUML code to load from the Sphinx-Need object. key value by default is diagram. If no key argument given, then the PlantUML code is loaded from diagram under arch inside the need object.

Example

.. comp:: Z
   :id: COMP_Z

   .. needuml::

      {{uml('COMP_002', 'sequence')}}

Result

Component: Z COMP_Z ../_images/arrow-right-circle.svg

@startuml

Alice -> Bob: Hi Bob
Bob --> Alice: Hi Alice
@enduml

Additional keyword arguments

uml() supports additional keyword parameters which are then available in the loaded PlantUML code.

Example

.. comp:: Variant A or B
   :id: COMP_A_B

   .. needuml::

      {% if variant == "A" %}
        class "A" as cl
      {% elif variant == "B" %}
        class "B" as cl {
            attribute_x
            function_x()
        }
      {% else %}
        class "Unknown" as cl
      {% endif %}

   By default **Unknown** is shown, as no variant was set.

Result

Component: Variant A or B COMP_A_B ../_images/arrow-right-circle.svg

@startuml


 class "Unknown" as cl

@enduml

By default Unknown is shown, as no variant was set.

Passing variant="A" parameter to the uml() function, we get the following:

Example

.. needuml::
   :debug:

   {{uml("COMP_A_B", variant="A")}}

Result

@startuml


 class "A" as cl

@enduml

@startuml


 class "A" as cl

@enduml

Passing variant="B" parameter to the uml() function, we get the following:

Example

.. needuml::
   :debug:

   {{uml("COMP_A_B", variant="B")}}

Result

@startuml


 class "B" as cl {
     attribute_x
     function_x()
 }

@enduml

@startuml


 class "B" as cl {
     attribute_x
     function_x()
 }

@enduml

Chaining diagrams

PlantUML Need objects uses the needuml directive internally to define their diagrams. All features are available and uml() can be used multiple time on different levels of a planned architecture.

Interface: Interface A INT_A ../_images/arrow-right-circle.svg

@startuml

circle "Int A" as int
@enduml

Component: Component X COMP_X ../_images/arrow-right-circle.svg

@startuml

allowmixing

circle "Int A" as int

class "Class A" as cl_a
class "Class B" as cl_b

cl_a o-- cl_b
cl_a --> int
@enduml

System: System RocketScience SYS_ROCKET ../_images/arrow-right-circle.svg

@startuml

allowmixing

node "RocketScience" as rocket {
    allowmixing

circle "Int A" as int

class "Class A" as cl_a
class "Class B" as cl_b

cl_a o-- cl_b
cl_a --> int
    card "Service Y" as service

    int --> service
}
@enduml

And finally a needuml to make use of the Sphinx-Need system object:

@startuml

allowmixing

allowmixing

node "RocketScience" as rocket {
    allowmixing

circle "Int A" as int

class "Class A" as cl_a
class "Class B" as cl_b

cl_a o-- cl_b
cl_a --> int
    card "Service Y" as service

    int --> service
}

actor "A friend" as me #ff5555

me --> rocket: doing
@enduml

.. int:: Interface A
   :id: INT_A

   .. needuml::

      circle "Int A" as int

.. comp:: Component X
   :id: COMP_X

   .. needuml::

      allowmixing

      {{uml("INT_A")}}

      class "Class A" as cl_a
      class "Class B" as cl_b

      cl_a o-- cl_b
      cl_a --> int

.. sys:: System RocketScience
   :id: SYS_ROCKET

   .. needuml::

      allowmixing

      node "RocketScience" {
          {{uml("COMP_X")}}
          card "Service Y" as service

          int --> service
      }

And finally a ``needuml`` to make use of the Sphinx-Need system object:

.. needuml::

   allowmixing

   {{uml("SYS_ROCKET")}}

   actor "A friend" as me #ff5555

   me --> rocket: doing

NeedUml Examples

Example

.. needuml::

   allowmixing

   class "Sphinx-Needs" as sn {
     requirements
     specifications
     test_cases
     customize()
     automate()
     export()
   }

   {% set ids = ["FEATURE_1", "FEATURE_5", "FEATURE_7"]%}
   {% for need in needs.values() %}
       {% if need.id in ids %}
           card "{{need['title']}}" as need_{{loop.index}} #ffcc00
           need_{{loop.index}} --> sn
       {% endif %}
   {% endfor %}

   card "and much more..." as much #ffcc00
   much -> sn

Result

@startuml

allowmixing

class "Sphinx-Needs" as sn {
  requirements
  specifications
  test_cases
  customize()
  automate()
  export()
}



    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    
        card "Filtering needs" as need_150 #ffcc00
        need_150 --> sn
    

    

    

    

    
        card "Customizing everything" as need_154 #ffcc00
        need_154 --> sn
    

    

    
        card "Supports PlantUML for reusable Architecture elements" as need_156 #ffcc00
        need_156 --> sn
    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    


card "and much more..." as much #ffcc00
much -> sn
@enduml

Example

.. comp:: Component X
   :id: COMP_001

   .. needuml::

      class "Class X" as class_x {
        attribute_1
        attribute_2
        function_1()
        function_2()
        function_3()
      }

       class "Class Y" as class_y {
            attribute_1
            function_1()
       }

       class_x o-- class_y

Result

Component: Component X COMP_001 ../_images/arrow-right-circle.svg

@startuml

class "Class X" as class_x {
  attribute_1
  attribute_2
  function_1()
  function_2()
  function_3()
}

class "Class Y" as class_y {
  attribute_1
  function_1()
}

class_x o-- class_y
@enduml