feature:refine

Table of contents

  1. Introduction
  2. Choosing the base and its points
  3. Sharing a base with other refiners
  4. What it writes
  5. Running it on code you already wrote
  6. Reference
    1. Arguments
    2. Errors
  7. Requirements
  8. See also

Introduction

feature:refine turns a feature into a refiner of another one, through an interactive wizard. You pick the base and its extension points, and the wizard declares the refinement, scaffolds the code and adds the refiner to your product. Check first whether filling the base’s hook slots is enough (see Refine, extend or compose).

splent feature:refine splent_feature_notes_tags
  Step 1: Which feature do you want to refine?

    1. notes
       1 service(s), 1 model(s), 3 template(s), 1 hook(s), routes

  Select: 1
  ...
  Select models to extend (space-separated numbers, or 0 for none) [0]: 1
    mixin: Notes <- NotesTagsMixin
  ...
  Write to pyproject.toml and scaffold code? [Y/n]: y
  pyproject.toml updated.
  scaffolded: models.py <- NotesTagsMixin
  scaffolded: __init__.py
  ...
  done.
What feature:refine reads and what it writes READS Contracts extension points Other refiners what they already claim The product the features it declares feature:refine 1 Choose the base by number or by name 2 Choose the points taken ones are refused 3 Write and scaffold asks before overwriting 4 Add to the product with feature:add WRITES pyproject.toml [tool.splent.refinement] [tool.splent.contract] Skeleton code models.py, services.py __init__.py, hooks.py routes_<refiner>.py templates/hooks/*.html The active product declares the refiner
It refreshes the contracts of the editable features first, then writes into the refiner and declares it in the product.

Choosing the base and its points

Answer Select with a number or a name (notes or splent_feature_notes). Then answer each kind of point with numbers separated by spaces, or 0 for none. The contracts of your editable features are refreshed first.

Point What the refiner does
Models Extends them with a mixin
Services Overrides them with a replacement class
Templates Overrides them
Hook slots the base offers Fills them with register_template_hook
Hooks the base registers Overrides them with replace_template_hook
Routes Adds them to the base’s blueprint (yes or no)

A refiner is never offered as a base, since chains of refiners are not supported.


Sharing a base with other refiners

When other refiners of the base are in your product, the wizard lists what each one claims.

  Refining: shop

  Other refiners of shop
    collections
      extends model Item, adding collection_id
    shop_staff
      extends model Item, adding created_by

A taken target is marked (taken by <refiner>) and cannot be chosen.

Target Taken when
A template or a service Any refiner of the product overrides it
A hook Another refiner of the same base overrides it
A model Never. It shows who else extends it

Two refiners of one model must add attributes with different names, which product:validate checks.


What it writes

The refinement goes into the refiner’s pyproject.toml, after a backup.

[tool.splent.refinement]
refines = "splent_feature_notes"

[tool.splent.refinement.extends]
models = [{ target = "Notes", mixin = "NotesTagsMixin" }]

[tool.splent.refinement.overrides]
services = [{ target = "NotesService", replacement = "NotesServiceWithTags" }]

Then it scaffolds the code.

File What you get
models.py The mixin classes, instead of the scaffold’s db.Model
services.py The replacement classes. repositories.py becomes a comment
__init__.py The blueprint, refine_model() and refine_service() in init_feature, and inject_context_vars
hooks.py One *args, **kwargs stub per slot you fill (register_template_hook) or hook you override (replace_template_hook)
templates/hooks/<slot>.html One per stub, dots turned into underscores. Put the markup there
routes_<refiner>.py A sample view on the base blueprint. Import only that blueprint at module level
config.py, signals.py A one-line comment, unless they hold your code
migrations/env.py Untouched. The framework migrates the injected columns

Class names join the target with the refiner’s name minus the base’s, so notes_tags gets NotesTagsMixin and NotesServiceWithTags. Last, the wizard updates the refiner’s contract and runs feature:add.


Running it on code you already wrote

The wizard overwrites a code file without asking only when it is absent, holds only comments, pass or a docstring, or is still what feature:create wrote. Anything else gets Overwrite it?, which defaults to No.

A hooks.py that already registers hooks is kept. Add the replace_template_hook("<slot>", ...) call of a new override yourself, or startup warns that it is never applied.


Reference

Arguments

Argument Description
REFINER_NAME The feature that becomes the refiner, such as splent_feature_notes_tags

Errors

Message What to do
<refiner> not found in workspace. Check the name
<x> refines <refiner>, so <refiner> cannot refine another feature. Pick another refiner
<refiner> is named after the product ... Rename it (see Naming)
Refinement already configured (refines: ...) Answer Overwrite? with yes
No features with extensible points found in this product. Declare [tool.splent.contract.extensible] in the base
Nothing selected. Aborting. Choose at least one point
Refusing to write pyproject.toml: the result is not valid TOML. Fix it by hand

All but Refinement already configured and Nothing selected exit 1.


Requirements

  • A selected product (SPLENT_APP).
  • An editable refiner, at the workspace root or behind the product’s features/ link. Run feature:unlock on a pinned one first.
  • A base with at least one extension point in [tool.splent.contract.extensible].

See also


Back to top

splent. Distributed by an LGPL license v3. Contact us: drorganvidez@us.es