From cc4324c8bf19cc8b14dc4d1c70651b2bfa37eb8e Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 19 Nov 2025 10:19:07 +0000 Subject: [PATCH 1/5] DOC-5963 prepared PHP landing example for notebook --- content/develop/clients/php/_index.md | 44 +++---------------- .../client-specific/php/landing.php | 42 ++++++++++++++++++ 2 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 local_examples/client-specific/php/landing.php diff --git a/content/develop/clients/php/_index.md b/content/develop/clients/php/_index.md index d9f94a6ab9..063ba77936 100644 --- a/content/develop/clients/php/_index.md +++ b/content/develop/clients/php/_index.md @@ -40,51 +40,19 @@ composer require predis/predis Connect to a locally-running server on the standard port (6379) with the following code: -```php - 'tcp', - 'host' => '127.0.0.1', - 'port' => 6379, - 'password' => '', - 'database' => 0, - ]); -``` +{{< clients-example set="landing" step="connect" lang_filter="PHP" >}} +{{< /clients-example >}} Store and retrieve a simple string to test the connection: -```php -echo $r->set('foo', 'bar'), PHP_EOL; -// >>> OK - -echo $r->get('foo'), PHP_EOL; -// >>> bar -``` +{{< clients-example set="landing" step="set_get_string" lang_filter="PHP" >}} +{{< /clients-example >}} Store and retrieve a [hash]({{< relref "/develop/data-types/hashes" >}}) object: -```php -$r->hset('user-session:123', 'name', 'John'); -$r->hset('user-session:123', 'surname', 'Smith'); -$r->hset('user-session:123', 'company', 'Redis'); -$r->hset('user-session:123', 'age', 29); - -echo var_export($r->hgetall('user-session:123')), PHP_EOL; -/* >>> -array ( - 'name' => 'John', - 'surname' => 'Smith', - 'company' => 'Redis', - 'age' => '29', -) -*/ -``` +{{< clients-example set="landing" step="set_get_hash" lang_filter="PHP" >}} +{{< /clients-example >}} ## More information diff --git a/local_examples/client-specific/php/landing.php b/local_examples/client-specific/php/landing.php new file mode 100644 index 0000000000..1701af8ccb --- /dev/null +++ b/local_examples/client-specific/php/landing.php @@ -0,0 +1,42 @@ +// EXAMPLE: landing +// BINDER_ID php-landing +// STEP_START connect + 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'database' => 0, + ]); +// STEP_END + +// STEP_START set_get_string +echo $r->set('foo', 'bar'), PHP_EOL; +// >>> OK + +echo $r->get('foo'), PHP_EOL; +// >>> bar +// STEP_END + +// STEP_START set_get_hash +$r->hset('user-session:123', 'name', 'John'); +$r->hset('user-session:123', 'surname', 'Smith'); +$r->hset('user-session:123', 'company', 'Redis'); +$r->hset('user-session:123', 'age', 29); + +echo var_export($r->hgetall('user-session:123')), PHP_EOL; +/* >>> +array ( + 'name' => 'John', + 'surname' => 'Smith', + 'company' => 'Redis', + 'age' => '29', +) +*/ +// STEP_END From 041b32839fcb6b27841768aacf610e7755f3ca10 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 19 Nov 2025 11:17:03 +0000 Subject: [PATCH 2/5] DOC-5963 enabled Lettuce sync examples --- build/local_examples.py | 3 +++ build/tcedocs/README.md | 30 +++++++++++++++++++++--------- build/tcedocs/SPECIFICATION.md | 7 ++++--- config.toml | 13 +++++++------ data/components/index.json | 1 + data/components/lettuce_sync.json | 16 ++++++++++++++++ 6 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 data/components/lettuce_sync.json diff --git a/build/local_examples.py b/build/local_examples.py index 9d6bdd3130..3b5f3c9857 100644 --- a/build/local_examples.py +++ b/build/local_examples.py @@ -57,12 +57,15 @@ def get_client_name_from_language_and_path(language: str, path: str) -> str: """Get client name from language with path-based overrides. For Java (.java) files, override based on path substrings: + - If 'lettuce-sync' in path -> Lettuce-Sync - If 'lettuce-async' in path -> Java-Async - If 'lettuce-reactive' in path -> Java-Reactive Substring checks are case-sensitive and can appear anywhere in the path. """ if language == 'java': + if 'lettuce-sync' in path: + return 'Lettuce-Sync' if 'lettuce-async' in path: return 'Java-Async' if 'lettuce-reactive' in path: diff --git a/build/tcedocs/README.md b/build/tcedocs/README.md index ddc22a6d65..d55891ac73 100644 --- a/build/tcedocs/README.md +++ b/build/tcedocs/README.md @@ -8,7 +8,7 @@ There are two sections that need to updated when new languages are added. 1. In the `[params]` section: ```toml - clientsExamples = ["Python", "Node.js", "Java-Sync", "Java-Async", "Java-Reactive", "Go", "C#", "RedisVL", "PHP"] + clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] ``` The order of the `clientsExamples` list matters: it's the order in which the language tabs are presented for each code example. @@ -18,13 +18,17 @@ There are two sections that need to updated when new languages are added. [params.clientsConfig] "Python"={quickstartSlug="redis-py"} "Node.js"={quickstartSlug="nodejs"} - "Java-sync"={quickstartSlug="jedis"} - "Java-async"={quickstartSlug="lettuce"} - "Java-reactive"={quickstartSlug="lettuce"} + "Java-Sync"={quickstartSlug="jedis"} + "Lettuce-Sync"={quickstartSlug="lettuce"} + "Java-Async"={quickstartSlug="lettuce"} + "Java-Reactive"={quickstartSlug="lettuce"} "Go"={quickstartSlug="go"} - "C#"={quickstartSlug="dotnet"} + "C#-Sync"={quickstartSlug="dotnet"} + "C#-Async"={quickstartSlug="dotnet"} "RedisVL"={quickstartSlug="redis-vl"} "PHP"={quickstartSlug="php"} + "Rust-Sync"={quickstartSlug="rust"} + "Rust-Async"={quickstartSlug="rust"} ``` This configuration, along with the configuration steps below, is used to control the behavior of the Hugo shortcode that was developed to show tabbed code examples. @@ -36,7 +40,7 @@ A shortcode is a simple snippet inside a content file that Hugo will render usin The folder `data/components` contains one component configuration file for each supported language. These files contain information about the GitHub repos that house the code examples. -Here is the configuration file for Python, `redis_py.json`: +Here is the configuration file for Python, `redis_py.json`: ```json { @@ -65,15 +69,19 @@ Register your component file by adding it to the `clients` array in the `index.j Here is an example: ```json "clients": [ - "nredisstack", + "nredisstack_sync", + "nredisstack_async", "go_redis", "node_redis", "php", "redis_py", "jedis", + "lettuce_sync", "lettuce_async", "lettuce_reactive", - "redis_vl" + "redis_vl", + "redis_rs_sync", + "redis_rs_async" ] ``` @@ -133,7 +141,8 @@ Add a source code file to an appropriate client repo. Consult the /data/componen | C# | [NRedisStack](https://github.com/redis/NRedisStack) | `tests/Doc` | | Go | [go-redis](https://github.com/redis/go-redis) | `doctests` | | Java | [jedis](https://github.com/redis/jedis) | `src/test/java/io/redis/examples` | -| | [Lettuce](https://github.com/redis/lettuce) | `src/test/java/io/redis/examples/async` or | +| | [Lettuce](https://github.com/redis/lettuce) | `src/test/java/io/redis/examples/sync`, | +| | | `src/test/java/io/redis/examples/async`, or | | | | `src/test/java/io/redis/examples/reactive` | | Node.js | [node-redis](https://github.com/redis/node-redis) | `doctests` | | PHP | [Predis](https://github.com/predis/predis) | Examples, for now, are stored in `local_examples` | @@ -151,6 +160,9 @@ local_examples │   │   ... │   ├── jedis │   │   ... +│ ├── lettuce-sync +│ │ ... + │   ├── lettuce-async │   │   ... │   ├── lettuce-reactive diff --git a/build/tcedocs/SPECIFICATION.md b/build/tcedocs/SPECIFICATION.md index aacb7dc8b4..e9ef010519 100644 --- a/build/tcedocs/SPECIFICATION.md +++ b/build/tcedocs/SPECIFICATION.md @@ -245,7 +245,8 @@ The system operates in three distinct phases: Some languages have multiple client implementations (sync/async, different libraries). The system uses directory path to determine which variant: -- Java files in `lettuce-async/` → `Java-Async` (Lettuce async client) +- Java files in `lettuce-sync/` → `Lettuce-Sync` (Lettuce synchronous client) +- Java files in `lettuce-async/` → `Java-Async` (Lettuce asynchronous client) - Java files in `lettuce-reactive/` → `Java-Reactive` (Lettuce reactive client) - Java files elsewhere → `Java-Sync` (Jedis synchronous client) - Rust files in `rust-async/` → `Rust-Async` @@ -253,7 +254,7 @@ Some languages have multiple client implementations (sync/async, different libra - C# files in `async/` → `C#-Async` - C# files in `sync/` → `C#-Sync` -This allows the same language to appear multiple times in the tab interface with different implementations. +This allows the same language to appear multiple times in the tab interface with different implementations. The order of checks matters: more specific paths (e.g., `lettuce-sync`) should be checked before generic ones (e.g., `Java-Sync`). **Outputs**: - Copies files to `examples/{example_id}/local_{filename}` @@ -1126,7 +1127,7 @@ def main(): **Client Examples Order**: ```toml [params] -clientsExamples = ["Python", "Node.js", "Java-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] +clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] ``` This controls: diff --git a/config.toml b/config.toml index f011d5dfa1..f836f4c5bf 100644 --- a/config.toml +++ b/config.toml @@ -45,7 +45,7 @@ tagManagerId = "GTM-TKZ6J9R" gitHubRepo = "https://github.com/redis/docs" # Display and sort order for client examples -clientsExamples = ["Python", "Node.js", "Java-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] +clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] searchService = "/convai/api/search-service" ratingsService = "/docusight/api/rate/docs" @@ -60,16 +60,17 @@ rdi_current_version = "1.15.0" [params.clientsConfig] "Python"={quickstartSlug="redis-py"} "Node.js"={quickstartSlug="nodejs"} -"Java-sync"={quickstartSlug="jedis"} -"Java-async"={quickstartSlug="lettuce"} -"Java-reactive"={quickstartSlug="lettuce"} +"Java-Sync"={quickstartSlug="jedis"} +"Lettuce-Sync"={quickstartSlug="lettuce"} +"Java-Async"={quickstartSlug="lettuce"} +"Java-Reactive"={quickstartSlug="lettuce"} "Go"={quickstartSlug="go"} "C#-Sync"={quickstartSlug="dotnet"} "C#-Async"={quickstartSlug="dotnet"} "RedisVL"={quickstartSlug="redis-vl"} "PHP"={quickstartSlug="php"} -"Rust-sync"={quickstartSlug="rust"} -"Rust-async"={quickstartSlug="rust"} +"Rust-Sync"={quickstartSlug="rust"} +"Rust-Async"={quickstartSlug="rust"} # Mount directories for duplicate content [module] diff --git a/data/components/index.json b/data/components/index.json index 6c1d428882..84fdda688e 100644 --- a/data/components/index.json +++ b/data/components/index.json @@ -14,6 +14,7 @@ "php", "redis_py", "jedis", + "lettuce_sync", "lettuce_async", "lettuce_reactive", "redis_vl", diff --git a/data/components/lettuce_sync.json b/data/components/lettuce_sync.json new file mode 100644 index 0000000000..0dac9fee0e --- /dev/null +++ b/data/components/lettuce_sync.json @@ -0,0 +1,16 @@ +{ + "id": "lettuce_sync", + "type": "client", + "name": "lettuce_sync", + "language": "Lettuce-Sync", + "label": "Lettuce-Sync", + "repository": { + "git_uri": "https://github.com/redis/lettuce" + }, + "examples": { + "git_uri": "https://github.com/redis/lettuce", + "path": "src/test/java/io/redis/examples/sync", + "pattern": "*.java" + } +} + From 7aad5c71727ca226d1fdb4c9ed4e892576259b12 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 19 Nov 2025 11:17:58 +0000 Subject: [PATCH 3/5] DOC-5963 prepared Lettuce landing page for notebook --- content/develop/clients/lettuce/_index.md | 36 +++++++------------ .../lettuce-sync/ConnectBasicTest.java | 34 ++++++++++++++++++ 2 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 local_examples/client-specific/lettuce-sync/ConnectBasicTest.java diff --git a/content/develop/clients/lettuce/_index.md b/content/develop/clients/lettuce/_index.md index fb1274a84c..da73847e3d 100644 --- a/content/develop/clients/lettuce/_index.md +++ b/content/develop/clients/lettuce/_index.md @@ -54,36 +54,26 @@ To build from source, see the instructions on the [Lettuce source code GitHub re ## Connect and test -Connect to a local server using the following code. This example -also stores and retrieves a simple string value to test the connection -and closes the connection after use. +Connect to a local server using the following code. First, import +the required classes. -```java -import io.lettuce.core.*; -import io.lettuce.core.api.StatefulRedisConnection; -import io.lettuce.core.api.sync.RedisCommands; +{{< clients-example set="landing" step="import" lang_filter="Lettuce-Sync" >}} +{{< /clients-example >}} -public class ConnectBasicTest { +Use the following code to connect to the server. - public void connectBasic() { - RedisURI uri = RedisURI.Builder - .redis("localhost", 6379) - .build(); +{{< clients-example set="landing" step="connect" lang_filter="Lettuce-Sync" >}} +{{< /clients-example >}} - RedisClient client = RedisClient.create(uri); - StatefulRedisConnection connection = client.connect(); - RedisCommands commands = connection.sync(); +Test the connection by storing and retrieving a simple string. - commands.set("foo", "bar"); - String result = commands.get("foo"); - System.out.println(result); // >>> bar +{{< clients-example set="landing" step="set_get_string" lang_filter="Lettuce-Sync" >}} +{{< /clients-example >}} - connection.close(); +Close the connection when you're done. - client.shutdown(); - } -} -``` +{{< clients-example set="landing" step="close" lang_filter="Lettuce-Sync" >}} +{{< /clients-example >}} ## More information diff --git a/local_examples/client-specific/lettuce-sync/ConnectBasicTest.java b/local_examples/client-specific/lettuce-sync/ConnectBasicTest.java new file mode 100644 index 0000000000..c859ca431e --- /dev/null +++ b/local_examples/client-specific/lettuce-sync/ConnectBasicTest.java @@ -0,0 +1,34 @@ +// EXAMPLE: landing +// BINDER_ID lettuce-landing +// STEP_START import +import io.lettuce.core.*; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.sync.RedisCommands; +// STEP_END + + +public class ConnectBasicTest { + + public void connectBasic() { + // STEP_START connect + RedisURI uri = RedisURI.Builder + .redis("localhost", 6379) + .build(); + + RedisClient client = RedisClient.create(uri); + StatefulRedisConnection connection = client.connect(); + RedisCommands commands = connection.sync(); + // STEP_END + + // STEP_START set_get_string + commands.set("foo", "bar"); + String result = commands.get("foo"); + System.out.println(result); // >>> bar + // STEP_END + + // STEP_START close + connection.close(); + client.shutdown(); + // STEP_END + } +} \ No newline at end of file From 3ba34c014b161e44e8a1c3c8ca48897ad184e14d Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 19 Nov 2025 12:43:33 +0000 Subject: [PATCH 4/5] DOC-5963 enabled support for C TCEs --- build/components/example.py | 1 + build/local_examples.py | 3 + build/tcedocs/README.md | 16 +++- build/tcedocs/SPECIFICATION.md | 132 ++++++++++++++++++++++++++++++++- config.toml | 3 +- data/components/hi_redis.json | 16 ++++ data/components/index.json | 3 +- 7 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 data/components/hi_redis.json diff --git a/build/components/example.py b/build/components/example.py index 5b8841067d..3e298714d0 100644 --- a/build/components/example.py +++ b/build/components/example.py @@ -28,6 +28,7 @@ 'java-async': '//', 'java-reactive': '//', 'go': '//', + 'c': '//', 'c#': '//', 'c#-sync': '//', 'c#-async': '//', diff --git a/build/local_examples.py b/build/local_examples.py index 3b5f3c9857..470fec6315 100644 --- a/build/local_examples.py +++ b/build/local_examples.py @@ -23,6 +23,8 @@ '.py': 'python', '.js': 'node.js', '.go': 'go', + '.c': 'c', + '.h': 'c', '.cs': 'c#', '.java': 'java', '.php': 'php', @@ -34,6 +36,7 @@ 'python': 'Python', 'node.js': 'Node.js', 'go': 'Go', + 'c': 'C', 'c#': 'C#-Sync', 'java': 'Java-Sync', # Default to sync, could be overridden 'php': 'PHP', diff --git a/build/tcedocs/README.md b/build/tcedocs/README.md index d55891ac73..e75c978d86 100644 --- a/build/tcedocs/README.md +++ b/build/tcedocs/README.md @@ -8,7 +8,7 @@ There are two sections that need to updated when new languages are added. 1. In the `[params]` section: ```toml - clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] + clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] ``` The order of the `clientsExamples` list matters: it's the order in which the language tabs are presented for each code example. @@ -23,6 +23,7 @@ There are two sections that need to updated when new languages are added. "Java-Async"={quickstartSlug="lettuce"} "Java-Reactive"={quickstartSlug="lettuce"} "Go"={quickstartSlug="go"} + "C"={quickstartSlug="hiredis"} "C#-Sync"={quickstartSlug="dotnet"} "C#-Async"={quickstartSlug="dotnet"} "RedisVL"={quickstartSlug="redis-vl"} @@ -81,7 +82,8 @@ Here is an example: "lettuce_reactive", "redis_vl", "redis_rs_sync", - "redis_rs_async" + "redis_rs_async", + "hi_redis" ] ``` @@ -107,14 +109,18 @@ PREFIXES = { 'java-async': '//', 'java-reactive': '//', 'go': '//', + 'c': '//', 'c#': '//', 'redisvl': '#', - 'php': '//' + 'php': '//', + 'rust': '//' } ``` The `TEST_MARKER` dictionary maps programming languages to test framework annotations, which allows the parser to filter such source code lines out. The `PREFIXES` dictionary maps each language to its comment prefix. Python, for example, uses a hashtag (`#`) to start a comment. +⚠️ **CRITICAL**: The `PREFIXES` dictionary is **essential** for the example parser to work. If you add a new language, you **must** add an entry to this dictionary, or examples will fail to process with an "Unknown language" error. This is the most commonly missed step when adding a new language. + ## Understand special comments in the example source code files Each code example uses special comments, such as `HIDE_START` and `REMOVE_START`, to control how the examples are displayed. The following list gives an explanation: @@ -138,6 +144,7 @@ Add a source code file to an appropriate client repo. Consult the /data/componen | Programming Language | GitHub Repo | Default directory | |----------------------|-----------------------------------------------------|---------------------------------------------------| +| C | [hiredis](https://github.com/redis/hiredis) | `examples` | | C# | [NRedisStack](https://github.com/redis/NRedisStack) | `tests/Doc` | | Go | [go-redis](https://github.com/redis/go-redis) | `doctests` | | Java | [jedis](https://github.com/redis/jedis) | `src/test/java/io/redis/examples` | @@ -157,6 +164,9 @@ At times, it can take quite a while to get new or updated examples through the r local_examples ├── client-specific │   ├── go +│ ├── c +│ │ ... + │   │   ... │   ├── jedis │   │   ... diff --git a/build/tcedocs/SPECIFICATION.md b/build/tcedocs/SPECIFICATION.md index e9ef010519..c39277bcb8 100644 --- a/build/tcedocs/SPECIFICATION.md +++ b/build/tcedocs/SPECIFICATION.md @@ -1127,7 +1127,7 @@ def main(): **Client Examples Order**: ```toml [params] -clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] +clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] ``` This controls: @@ -1490,9 +1490,10 @@ See [Appendix: Adding a Language](#adding-a-language) for complete step-by-step 1. ✅ Update `config.toml` (clientsExamples, clientsConfig) 2. ✅ Create component config in `data/components/` 3. ✅ Register in `data/components/index.json` -4. ✅ Add language to `PREFIXES` in `build/components/example.py` +4. ✅ Add language to `PREFIXES` in `build/components/example.py` ⚠️ **CRITICAL - DO NOT SKIP** 5. ✅ Add extension mapping in `build/local_examples.py` 6. ✅ Add test markers if needed +7. ⚠️ Check if Jupyter notebook support is needed (update `build/jupyterize/` if applicable) ### Customizing the UI @@ -2139,3 +2140,130 @@ OK {{< /clients-example >}} ``` + +## Lessons Learned: Adding the C (hiredis) Client + +### Critical Discovery: The PREFIXES Dictionary + +When adding the C client, a critical step was initially missed: **adding the language to the `PREFIXES` dictionary in `build/components/example.py`**. + +**Why this matters**: The `PREFIXES` dictionary maps each language to its comment prefix character(s). This is used by the example parser to: +- Identify special markers like `EXAMPLE:`, `STEP_START`, `HIDE_START`, etc. +- Parse metadata from source files +- Process example files correctly + +**What happens if you skip this step**: +- The example parser will fail with an error: `Unknown language "c" for example {path}` +- Examples won't be processed +- The build system will silently skip C examples +- No error message will appear in the build output (just a debug log) + +**The fix**: +```python +# In build/components/example.py, add to PREFIXES dictionary: +PREFIXES = { + ... + 'c': '//', # C uses // for comments + ... +} +``` + +### Complete Checklist for Adding a New Language + +The original checklist was incomplete. Here's the comprehensive version: + +**Configuration Files**: +1. ✅ `config.toml` - Add to `clientsExamples` list and `clientsConfig` section +2. ✅ `data/components/{language}.json` - Create component configuration +3. ✅ `data/components/index.json` - Register the component + +**Build System**: +4. ✅ `build/components/example.py` - **CRITICAL**: Add to `PREFIXES` dictionary +5. ✅ `build/components/example.py` - Add to `TEST_MARKER` dictionary (if language has test annotations) +6. ✅ `build/local_examples.py` - Add file extension mapping to `EXTENSION_TO_LANGUAGE` +7. ✅ `build/local_examples.py` - Add language to `LANGUAGE_TO_CLIENT` mapping + +**Optional (if Jupyter notebook support is needed)**: +8. ⚠️ `build/jupyterize/jupyterize.py` - Add to `KERNEL_SPECS` dictionary +9. ⚠️ `build/jupyterize/jupyterize_config.json` - Add language-specific boilerplate and unwrap patterns + +**Documentation**: +10. ✅ `build/tcedocs/SPECIFICATION.md` - Update examples and checklist +11. ✅ `build/tcedocs/README.md` - Update tables and examples + +### Pre-existing Examples + +**Important**: Before adding a new language, check if examples already exist in the repository: +- Look in `local_examples/client-specific/{language}/` for local examples +- Check the client repository for remote examples +- Verify the component configuration points to the correct example directory + +For C (hiredis), there was already a `landing.c` example in `local_examples/client-specific/c/` that was ready to be processed once the language was properly configured. + +### Language-Specific Comment Prefixes + +Different languages use different comment styles. When adding a language, ensure the correct prefix is used: + +| Language | Prefix | Example | +|----------|--------|---------| +| Python | `#` | `# EXAMPLE: my_example` | +| C | `//` | `// EXAMPLE: my_example` | +| Java | `//` | `// EXAMPLE: my_example` | +| Go | `//` | `// EXAMPLE: my_example` | +| C# | `//` | `// EXAMPLE: my_example` | +| PHP | `//` | `// EXAMPLE: my_example` | +| Rust | `//` | `// EXAMPLE: my_example` | +| Node.js | `//` | `// EXAMPLE: my_example` | + +**Critical**: The `PREFIXES` dictionary uses **lowercase** language names as keys, but the `Example` class converts the language to lowercase before accessing it (line 57 in `example.py`). + +### Verification Steps + +After adding a new language, verify the integration: + +```bash +# 1. Check that the language is recognized +grep -r "c" build/components/example.py # Should find 'c': '//' in PREFIXES + +# 2. Process examples +python3 build/local_examples.py + +# 3. Verify examples were processed +grep -i "landing" data/examples.json | grep -i "c" + +# 4. Check for errors in the build output +python3 build/make.py 2>&1 | grep -i "error\|unknown language" + +# 5. Build and serve +hugo serve +``` + +### Common Mistakes to Avoid + +1. **Forgetting the PREFIXES entry**: This is the most common mistake. The build will appear to succeed but examples won't be processed. + +2. **Case sensitivity**: Language names in `PREFIXES` must be lowercase, but `clientsExamples` in `config.toml` uses proper case (e.g., `"C"` not `"c"`). + +3. **Inconsistent naming**: Ensure the language name is consistent across: + - `config.toml` clientsExamples (proper case, e.g., `"C"`) + - `config.toml` clientsConfig keys (proper case, e.g., `"C"`) + - `build/local_examples.py` LANGUAGE_TO_CLIENT values (proper case, e.g., `'C'`) + - `build/components/example.py` PREFIXES keys (lowercase, e.g., `'c'`) + +4. **Missing component registration**: If the component isn't registered in `data/components/index.json`, remote examples won't be fetched. + +5. **Wrong file extension mapping**: Ensure the file extension correctly maps to the language name in `EXTENSION_TO_LANGUAGE`. + +### Single-Variant vs Multi-Variant Languages + +**Single-variant languages** (Python, Go, PHP, C): +- One client implementation per language +- No path-based client name overrides needed +- File extension mapping is straightforward + +**Multi-variant languages** (Java, Rust, C#): +- Multiple client implementations (e.g., Sync, Async, Reactive) +- Require path-based client name overrides in `get_client_name_from_language_and_path()` +- More complex configuration + +C is a single-variant language, so it doesn't require path-based overrides. diff --git a/config.toml b/config.toml index f836f4c5bf..02aea7e018 100644 --- a/config.toml +++ b/config.toml @@ -45,7 +45,7 @@ tagManagerId = "GTM-TKZ6J9R" gitHubRepo = "https://github.com/redis/docs" # Display and sort order for client examples -clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] +clientsExamples = ["Python", "Node.js", "Java-Sync", "Lettuce-Sync", "Java-Async", "Java-Reactive", "Go", "C", "C#-Sync", "C#-Async", "RedisVL", "PHP", "Rust-Sync", "Rust-Async"] searchService = "/convai/api/search-service" ratingsService = "/docusight/api/rate/docs" @@ -65,6 +65,7 @@ rdi_current_version = "1.15.0" "Java-Async"={quickstartSlug="lettuce"} "Java-Reactive"={quickstartSlug="lettuce"} "Go"={quickstartSlug="go"} +"C"={quickstartSlug="hiredis"} "C#-Sync"={quickstartSlug="dotnet"} "C#-Async"={quickstartSlug="dotnet"} "RedisVL"={quickstartSlug="redis-vl"} diff --git a/data/components/hi_redis.json b/data/components/hi_redis.json new file mode 100644 index 0000000000..106b765ba3 --- /dev/null +++ b/data/components/hi_redis.json @@ -0,0 +1,16 @@ +{ + "id": "hi_redis", + "type": "client", + "name": "hiredis", + "language": "C", + "label": "C", + "repository": { + "git_uri": "https://github.com/redis/hiredis" + }, + "examples": { + "git_uri": "https://github.com/redis/hiredis", + "path": "examples", + "pattern": "*.c" + } +} + diff --git a/data/components/index.json b/data/components/index.json index 84fdda688e..6a7b4f4caa 100644 --- a/data/components/index.json +++ b/data/components/index.json @@ -19,7 +19,8 @@ "lettuce_reactive", "redis_vl", "redis_rs_sync", - "redis_rs_async" + "redis_rs_async", + "hi_redis" ], "assets": [], "website": { From ec830eaa7f14d1dcff2377fcda990585cd568d6b Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 19 Nov 2025 12:44:31 +0000 Subject: [PATCH 5/5] DOC-5963 prepared hiredis landing page for notebook --- content/develop/clients/hiredis/_index.md | 41 ++-------------------- local_examples/client-specific/c/landing.c | 41 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 local_examples/client-specific/c/landing.c diff --git a/content/develop/clients/hiredis/_index.md b/content/develop/clients/hiredis/_index.md index c9038b537c..481dfbea2e 100644 --- a/content/develop/clients/hiredis/_index.md +++ b/content/develop/clients/hiredis/_index.md @@ -38,45 +38,8 @@ a string key using [`SET`]({{< relref "/commands/set" >}}) and [`GET`]({{< relref "/commands/get" >}}), and then finally closes the connection. An explanation of the code follows the example. -```c -#include -#include - -#include - -int main() { - // The `redisContext` type represents the connection - // to the Redis server. Here, we connect to the - // default host and port. - redisContext *c = redisConnect("127.0.0.1", 6379); - - // Check if the context is null or if a specific - // error occurred. - if (c == NULL || c->err) { - if (c != NULL) { - printf("Error: %s\n", c->errstr); - // handle error - } else { - printf("Can't allocate redis context\n"); - } - - exit(1); - } - - // Set a string key. - redisReply *reply = redisCommand(c, "SET foo bar"); - printf("Reply: %s\n", reply->str); // >>> Reply: OK - freeReplyObject(reply); - - // Get the key we have just stored. - reply = redisCommand(c, "GET foo"); - printf("Reply: %s\n", reply->str); // >>> Reply: bar - freeReplyObject(reply); - - // Close the connection. - redisFree(c); -} -``` +{{< clients-example set="landing" step="connect" lang_filter="C" >}} +{{< /clients-example >}} For a real project, you would build your code with a makefile, but for this simple test, you can just place it in a file called `main.c` and diff --git a/local_examples/client-specific/c/landing.c b/local_examples/client-specific/c/landing.c new file mode 100644 index 0000000000..a2032c07db --- /dev/null +++ b/local_examples/client-specific/c/landing.c @@ -0,0 +1,41 @@ +// EXAMPLE: landing +// BINDER_ID c-landing +// STEP_START connect +#include +#include + +#include + +int main() { + // The `redisContext` type represents the connection + // to the Redis server. Here, we connect to the + // default host and port. + redisContext *c = redisConnect("127.0.0.1", 6379); + + // Check if the context is null or if a specific + // error occurred. + if (c == NULL || c->err) { + if (c != NULL) { + printf("Error: %s\n", c->errstr); + // handle error + } else { + printf("Can't allocate redis context\n"); + } + + exit(1); + } + + // Set a string key. + redisReply *reply = redisCommand(c, "SET foo bar"); + printf("Reply: %s\n", reply->str); // >>> Reply: OK + freeReplyObject(reply); + + // Get the key we have just stored. + reply = redisCommand(c, "GET foo"); + printf("Reply: %s\n", reply->str); // >>> Reply: bar + freeReplyObject(reply); + + // Close the connection. + redisFree(c); +} +// STEP_END