---
title: "hotpatchR Overview"
author: "David Munoz Tord"
date: "2026-04-15"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{hotpatchR Overview}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

# Introduction

This vignette explains the `hotpatchR` design and why runtime namespace patching is the right tool for legacy hotfix workflows.

## The legacy package lockdown problem

A loaded R package lives in a locked namespace. Internal functions are resolved from inside the package bubble, so calling a hidden helper from the global environment does not change the package's internal behavior.

This is the namespace trap that makes legacy hotfixing difficult: a visible exported function may still invoke a broken internal helper even after you have sourced a fixed version elsewhere.

## Why the global workaround fails

The usual workaround is:

- identify the broken internal function
- find every package caller that depends on it
- copy the broken internals into a hotfix script
- source the script into the global environment
- run tests and hope the package resolves the new bindings

That workflow is brittle because a bug in one hidden helper can force you to patch many callers, even when only one implementation needs to change.

## hotpatchR philosophy

Instead of pulling functions out into the global environment, `hotpatchR` performs surgical edits inside the package namespace.
That means:

- fix only the broken internal function
- exported callers automatically resolve to the updated implementation
- the package remains loaded and otherwise unchanged

## Basic workflow

Before using the package, install it from CRAN or GitHub:

```r
install.packages("hotpatchR")
# or, for the development version:
# remotes::install_github("munoztd0/hotpatchR")
```

The package includes a real example of this pattern with an exported parent function and an internal child helper.

```{r example}
library(hotpatchR)

baseline <- dummy_parent_func("test")
print(baseline)
#> "Parent output -> I am the BROKEN child. Input: test"

inject_patch(
  pkg = "hotpatchR",
  patch_list = list(
    dummy_child_func = function(x) {
      paste("I am the FIXED child! Input:", x)
    }
  )
)

patched_result <- dummy_parent_func("test")
print(patched_result)
#> "Parent output -> I am the FIXED child! Input: test"
```


## How inject_patch works

`inject_patch()`

1. identifies the target namespace or environment
2. unlocks the binding for the named object
3. assigns the replacement function into that environment
4. re-locks the binding

Because the replacement function can be defined with the package namespace as its parent, it still has access to the package's internal helpers.

## Rolling back a patch

If you need to restore the original binding, `undo_patch()` reverses the previous change.

```{r undo-example}
undo_patch(pkg = "hotpatchR", names = "dummy_child_func")
restored_result <- dummy_parent_func("test")
print(restored_result)
#> "Parent output -> I am the BROKEN child. Input: test"
```

## Hotfix scripts

`apply_hotfix_file()` is a convenience wrapper for scripted hotfix application. A compatible hotfix file should define:

- `pkg` (optional, if not passed explicitly)
- `patch_list`, a named list of replacement functions

Example hotfix file for this package:

```r
#
pkg <- "hotpatchR"
patch_list <- list(
  dummy_child_func = function(x) {
    paste("I am the FIXED child! Input:", x)
  }
)
```

Then apply it with:

```r
apply_hotfix_file("dev/hotpatchR_hotfix.R")
```

## Next steps

The current package is focused on the core runtime patching path.
Future enhancements may include patch comparison, dependency scanning, and CI-friendly test wrappers that explicitly preserve the patched namespace during test execution.
