Naming conventions for hierarchical field data.

neqis
neqis
edited May 2022 in DST Development

The DST guide states that field names should use snake_case and not other naming conventions, such as camelCase (dromedary or bactrian) or kebab-case (/spinal-case/lisp-case/dash-case). This isn't very conducive to hierarchical fields, such as dynamic lists of compound items (i.e. items with multiple fields), as only one separator is available for both path components and to join compound names. I've been considering a couple different solutions, and would especially like to hear from other DST authors, with an eye towards possibly creating a shared naming scheme for dynamic fields. I suspect static hierarchical field names wouldn't be seen in the wild (since static fields generally don't need to be named hierarchically), but it's important to consider the possibility.

Note that here, "static" and "dynamic" are in relation to the DSTs. Static fields are those that are defined by the DST author in the HTML template, while "dynamic" refers to fields that may not exist within the HTML template of a DST and are defined by the player (in DSTs that have such a feature). It does not mean "dynamic" as used in "Dynamic Sheet Template" and "Dynamic Sheet Field", which are only dynamic in respect to Obsidian Portal.

Before going into solutions, it's helpful to consider use-cases, as long as we don't limit ourselves to these cases (even subconsciously) when considering the solutions. Some to consider:

  • skills: besides static skills, a DST may allow a player to add their own, which would have name & rating fields.
  • equipment: items are likely have many fields, some of which may have compound names. A DST may have more specialized examples of this, such as "weapons" or even "melee weapons".
  • dark passions: compound name. Items have a name & rating.
  • special abilities/class abilities: compound name. Items may be simple or compound, or may have dynamic sub-lists of their own (such as a list of effects).
  • user-defined lists: besides adding items to a predefined list, a DST may allow a player to define their own lists of items, adding them to a section. This would allow a DST to be used with some optional rules that add stats, or with some changes in newer editions than the sheet was designed for.
  • user-defined sections: a DST may allow a user to add whole sections, then add sub-sections or lists to those sections, then add items to those lists. This would allow a DST to be used with just about any extension to or newer version of the underlying system.
Post edited by neqis on

Comments

  • neqis
    neqis
    Posts: 29 edited May 2022

    Returning to the topic, a simple solution is to avoid compound names in hierarchical fields, reserving underscores for separating path components. This wouldn't be a very complete solution, as it would require some way of distinguishing hierarchical fields from non-hierarchical, and it's not always possible to avoid compound names (such as with "dark passions").

    A couple of solutions would involve breaking the naming conventions, such as using underscores/snake_case for path components and either camelCase or kebab-case for compound names, or hyphenate path components and use snake_case for compound names. As some examples, in the template HTML the DSFs could have names like:

    • dsf_custom_superPower_powerSource
    • dsf_equipment_02_label
    • dsf_equipment-02_label
    • dsf-melee_01-num_attacks
    • dsf_specialAbilities_03_effect_01_maxTargets
    • dsf_specialAbilities03_effect01_maxTargets
    • dsf-special_abilities-03-effect-01-max_targets
    • dsf_specialAbilities-03_effect-01_maxTargets

    The two-convention schemes are simple to parse, as path components are separated by a single, unique character. The downside of any use of kebab-case is that dot notation couldn't be used to access fields as properties, such as in dynamic_sheet_attrs. When it comes to dynamic fields, this is less of an issue, as the field name is likely to be generated rather than static, and thus bracket notation will be required. It would crop up for static hierarchical fields, but as stated earlier, I expect them to be rare (and bracket syntax is perfectly functional). The only significant issue of using snake_case for hierarchy and camelCase for compound names is that it goes against the current naming conventions.

    The last example uses all three conventions, which is probably too much but does offer some interesting parsing possibilities using some simple regexes. The path components can be separated with /[-_]/, while splitting on /_/ keeps the indices attached to their collections; just the names can be extracted by splitting on /[-_]\d+(?:_|$)/. While you could perform the same operations on the other schemes, for the scheme in the last example they are represented within the syntax itself.

    Another solution specific to dynamic lists relies on list items being numbered, and each item including the number in the field name: use the numbers as indicators of separate path components. This (or something like it) is the approach currently taken in DSTs with dynamic/user defined fields, though the exact schemes vary. Some example DSFs currently in use:

    • dsf_specialty_value_01 (scheme: dsf_{name}_{name}_{index})

      Note: this case is a little different than all others path examples in this post in that it's subfield-first, rather than index-first; in terms of accessing values in JS, it would translate as specialty.value[0] (or specialty_value[0]), rather than specialty[0].value. This scheme probably wouldn't match the HTML structure of the dynamic lists in a DST, since in most cases dynamic fields that belong to the same item are all in the same DST element (which represents the item); the implication of the HTML structure matching this scheme is discussed below.

    • dsf_bg3 (scheme: dsf_{name}{index})
    • dsf_bg3_expanded2 (scheme: dsf_{name}{index}_{name}{index})
    • dsf_dark_passion_04_name (scheme: dsf_{name}_{index}_{name})
    • dsf_section_01_list_03_02_name (name of the 2nd item of the 3rd (unnamed) list of section 1; scheme: dsf((_{name})?_{index))+; JS: section[0].list[2][1].name; CSS: .section:nth-of-type(1) > .list:nth-of-type(3) > *:nth-of-type(2) .dsf.name )

    Separating indices from names with underscores, as is done in the 1st and last example, allows for names that include numbers (e.g. "top10"). Without separating the index from the name, the number would be interpreted as part of the name. The last scheme is only slightly more complex to parse than the mixed-convention schemes; the path components are relatively easily separated by splitting using the regex /_(\d+)(?:_|$)/.

    The first example suggests an interesting structure: multiple separate lists with related fields:

    	<div class="specialty">
    <ul class="types">
    <li><span class="dsf dsf_specialty_type_01"></span></li>
    <li><span class="dsf dsf_specialty_type_02"></span></li>
    </ul>

    <ul class="values">
    <li><span class="dsf dsf_specialty_value_01"></span></li>
    <li><span class="dsf dsf_specialty_value_02"></span></li>
    </ul>
    </div>

    This poses a problem for some schemes compatible with current naming conventions, as the numeric index doesn't separate path components. On the other hand, it seems unlikely to appear in the wild. Moreover, document structure is arguably distinct from data structure, so the structure of field names doesn't need to match the document structure. The above 2-ul example could use names like "dsf_specialty_02_value"; similarly, a single-list structure with multiple fields per item could just as well use names like "dsf_specialty_value_01". What's more important is that the name scheme makes sense for the structure of the data.

    One last solution, which I haven't yet observed in my admittedly small survey of DSTs but is compatible with current naming conventions is to use a double-underscore as a path separator, with a single underscore for compound names. Even though it fits conventions and can be used with arbitrary naming hierarchies, it wouldn't be my personal preference as it's a little more verbose, not as readable, and exposes the internals more.

    Post edited by neqis on
  • neqis
    neqis
    Posts: 29

    Besides distinguishing path components and parts of words, it's useful to distinguish dynamic fields from static (such as when creating the elements to hold the dynamic fields). One option is to prefix dynamic field names with "dyn_" to make this easy. Note for DSF classes, this would go immediately after the "dsf_" prefix; for example, a field named "dyn_equipment_01_label" would map to an element with class "dsf_dyn_equipment_01_label". An argument against this is the word "dynamic" is (over-)used, as "dsf" already means "dynamic sheet field" (even though "dynamic" applies to the sheet and not the fields, which are often static). A similar option would be to use "udf_" (short for "user defined field") for a field name prefix.

    What do other folks think about dynamic names for fields, both naming schemes and distinguishing dynamic from static field names?

  • neqis
    neqis
    Posts: 29

    Another approach (used in Chainsaw XIV's sheet libraries) is to avoid hierarchical fields entirely. Instead, related hierarchical fields are stored using JSON in a single field. This gives more control to the DST author, but at the same time requires the DST to assume management of the fields.

  • ChainsawXIV
    ChainsawXIV
    Posts: 530 edited June 2022

    Interesting topic, and worth discussing I think.

    Here's my take, coming from the perspective of someone who's mostly a designer:

    The function of the field naming convention is to make it easier for multiple authors working on sheets for the same system to create sheets which are compatible with one another.

    Crucially, this is much more relevant for static data than for dynamic data, because - in the absence of a much more elaborate underlying system - only static fields offer any likelihood that the same field will contain data describing the same gameplay element (for a dynamic skill list for example, we can only know that a given field is the first, second, third, or so on, with no guarantees that each time someone populates a field with a particular skill it will reside in the same index).

    As a result, cross-compatibility for dynamically defined fields is only relevant at the top level, to ensure that a list of skills (for example) on one sheet maps to the equivalent list of skills on another, as a whole, not that any given entry in that list corresponds to a particular entry in another.

    In the 1.0 version of my sheet libraries I used many numbered fields to store dynamic list entries, but I switched over to the single field approach in the 2.0 version in part for this reason. It's more viable for a well structured JSON blob to be shared by a variety of script implementations, since it only requires a basic understanding of a self-evident schema, and doesn't require those scripts to manage the structure of the data itself or assume any correspondence between numbered fields representing different data fields within a more complex dynamic entry such as a skill name and rating pair or multiple properties of an attack table entry (as implied in a naming-convention based approach).

    Beyond this, I think it's worth keeping in mind that while common conventions are good as a matter of form, in practice this use case is rarely relevant. Few systems have multiple sheets by multiple authors, and where they do it's common for later authors to template their work on the naming conventions established by earlier ones - particularly as there is frequently room for ambiguity within the convention even for entirely static fields, let alone dynamic ones.

    With that in mind, I think it's more valuable for you (or any individual author) to use a convention and approach that's comfortable and helps you be productive than to impose a global convention.

    Post edited by ChainsawXIV on
  • neqis
    neqis
    Posts: 29 edited June 2022

    The function of the field naming convention is to make it easier for multiple authors working on sheets for the same system to create sheets which are compatible with one another.

    To me, this is exactly why a convention for dynamic fields would be useful; it's the same reasoning for the existing convention for static fields.

    Crucially, this is much more relevant for static data than for dynamic data, because - in the absence of a much more elaborate underlying system - only static fields offer any likelihood that the same field will contain data describing the same gameplay element (for a dynamic skill list for example, we can only know that a given field is the first, second, third, or so on, with no guarantees that each time someone populates a field with a particular skill it will reside in the same index).

    Would you elaborate on this? My understanding leads to a different conclusion regarding cross-DST use of dynamic lists, namely that it's just as relevant as for static fields.

    Basically, the exact index used for any given gameplay element is immaterial as far as DSTs are concerned. Even field content generally won't matter. What's important for compatibility is how the names themselves are structured, as using the same convention allows for the fields to be more easily processed by different DSTs.

    The exact order of items would only matter if other fields (excluding fields for the same item, such as "skill_01_name" and "skill_01_value") depended on field values from the dynamic list, such as auto-calculated fields in a smart DST. A dumb DST (one that performs no auto-calculations) doesn't care about the order of fields; it might even include a feature to allow items to be reordered. The one situation where dynamic items wouldn't directly translate between DSTs is when you have a static list and a dynamic list that allows a player to extend the static list, and different DSTs have different static items (discussed below). A smart DST is unlikely to perform auto-calculations using dynamic fields (since the required field might be absent), but if it did, it would likely need to scan the items to find the fields needed for the calculation, rather than relying on them existing at specific indices (at that point, the field might as well be static).

    To take the skill example, suppose a user defined their own "Interrogation" and "Repair" skills in DST A:

    {

    dyn_skill_01_name: "Interrogation",
    dyn_skill_01_rating: "2",
    dyn_skill_02_name: "Repair",
    dyn_skill_02_rating: "3",

    }

    For either DST A or B, the item contents generally won't matter; the dynamic skill list will be filled out in order regardless, producing something like:


    <dl class="dynamic">
    <dt><span class="dsf dsf_dyn_skill_01_name">Interrogation</span></dt>
    <dd><span class="dsf dsf_dyn_skill_01_rating">2</span></dd>

    <dt><span class="dsf dsf_dyn_skill_02_name">Repair</span></dt>
    <dd><span class="dsf dsf_dyn_skill_02_rating">3</span></dd>
    </dl>

    If DST B were to have different static skills (e.g. a "skill_repair" DSF) than DST A, it would need to check the dynamic skill list for duplicates, but this isn't difficult. Moreover, using different naming schemes has no significance as far as this check is concerned; the only impact is that it would only require an additional step of translating DSF names, which would need to be done in any case. Thus a naming convention would simplify switching DSTs by allowing name translation to be skipped.

    A related, though distinct, issue arises if a DST has a dynamic lists that doesn't correspond to one in another DST (e.g. a "specialties" list completely lacking in another DST, or one DST has "merits & flaws" where another has two separate "merits" and "flaws" dynamic lists). This is perhaps what you're referring to in your next paragraph. Again, the affect of having different naming schemes is to require additional code to deal with them.

    As a result, cross-compatibility for dynamically defined fields is only relevant at the top level, to ensure that a list of skills (for example) on one sheet maps to the equivalent list of skills on another, as a whole, not that any given entry in that list corresponds to a particular entry in another.

    A shared naming convention would help in exactly this.

    In the 1.0 version of my sheet libraries I used many numbered fields to store dynamic list entries, but I switched over to the single field approach in the 2.0 version in part for this reason. […]

    The latter solution sidesteps the issue of naming conventions, and so is potentially beyond the scope of it. It is certainly a viable solution, and is thus worth mentioning as an alternative (particularly in documentation, if a naming convention were established & documented).

    Beyond this, I think it's worth keeping in mind that while common conventions are good as a matter of form, in practice this use case is rarely relevant. Few systems have multiple sheets by multiple authors, and where they do it's common for later authors to template their work on the naming conventions established by earlier ones - particularly as there is frequently room for ambiguity within the convention even for entirely static fields, let alone dynamic ones.

    The main use-case I've encountered this is where there's a generic sheet for a system but no character-type specific one (e.g. the World of Darkness, which lacks Wraith and Demon character sheets), so a player uses the generic sheet to start. A naming convention would ease the creation of a more appropriate, type-specific sheet later. Beyond this use-case, the original idea of multiple DSTs which might offer different features/styles still exists as a possibility.

    With that in mind, I think it's more valuable for you (or any individual author) to use a convention and approach that's comfortable and helps you be productive than to impose a global convention.

    I certainly don't intend this to be imposed on any sheet or sheet author. As a convention, rather than a standard, it would be a suggestion. By making it explicit & documented, it would give new sheet authors a common starting place.

    My DSTs currently have their own scheme. I could leave it at that, but thought it would be useful to check with the community before settling.

    Another aspect of raising this issue is that the existing convention is too limited to handle dynamic field names well, and so discussing changes/extensions seemed in order.

    Ultimately, having a common naming convention for dynamic fields makes as much sense as for static fields, and for the same reasons. A naming convention isn't so much about ensuring items maintain indices when switching as it is about easing switching. In some cases, switching would require no code (just as with static fields), whereas if different naming conventions are used, code would always be required just to translate the names. Without a convention, each different scheme encountered will require its own code to handle; this can result in a DST author having to add code to support compatibility with specific other DSTs.

    Post edited by neqis on
Sign In or Register to comment.

April 2024
Season of Strife

Read the feature post on the blog
Return to Obsidian Portal

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Discussions