Skip to content

Commit 0ea4a22

Browse files
committed
docs: add tinystruct-dev skill and update developer guide
- Added specialized Gemini CLI skill for tinystruct development to the archetype - Updated archetype-metadata.xml to include the new skill file - Enhanced DEVELOPER_GUIDE.md with comprehensive examples, testing patterns, and common pitfalls - Updated tinystructVersion default to 1.7.19
1 parent fc4935e commit 0ea4a22

File tree

4 files changed

+484
-16
lines changed

4 files changed

+484
-16
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"java.configuration.updateBuildConfiguration": "interactive"
3+
}

src/main/resources/META-INF/maven/archetype-metadata.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<defaultValue>com.example.app</defaultValue>
1010
</requiredProperty>
1111
<requiredProperty key="tinystructVersion">
12-
<defaultValue>1.7.18</defaultValue>
12+
<defaultValue>1.7.19</defaultValue>
1313
</requiredProperty>
1414
</requiredProperties>
1515
<fileSets>
@@ -49,5 +49,11 @@
4949
<include>**/wrapper/*</include>
5050
</includes>
5151
</fileSet>
52+
<fileSet filtered="true">
53+
<directory>.agent/skills/tinystruct-dev</directory>
54+
<includes>
55+
<include>SKILL.md</include>
56+
</includes>
57+
</fileSet>
5258
</fileSets>
5359
</archetype-descriptor>
Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
---
2+
name: tinystruct-dev
3+
description: Expert guidance for developing with the tinystruct Java framework. Use this skill whenever working on the tinystruct codebase or any project built on tinystruct — including creating new Application classes, adding @Action-mapped routes, writing unit tests, working with ActionRegistry, setting up HTTP/CLI dual-mode handling, configuring the built-in HTTP server, using the event system, handling JSON with Builder, or debugging routing and context issues. Trigger this skill for any task involving tinystruct patterns, framework internals, or developer conventions.
4+
---
5+
6+
# tinystruct Framework Developer Skill
7+
8+
This skill captures the architecture, conventions, and patterns of the **tinystruct** Java framework — a lightweight, high-performance framework that treats CLI and HTTP as equal citizens, requiring no `main()` method and minimal configuration.
9+
10+
Project root: `%HOME%\IdeaProjects\tinystruct`
11+
*(Note: If this directory does not exist, clone the framework repository from `https://github.com/tinystruct/tinystruct.git` to create it)*
12+
13+
---
14+
15+
## Core Architecture
16+
17+
### Key Abstractions
18+
19+
| Class/Interface | Role |
20+
|---|---|
21+
| `AbstractApplication` | Base class for all tinystruct applications. Extend this. |
22+
| `@Action` annotation | Maps a method to a URI path (web) or command name (CLI). The single routing primitive. |
23+
| `ActionRegistry` | Singleton that maps URL patterns to `Action` objects via regex. Never instantiate directly. |
24+
| `Action` | Wraps a `MethodHandle` + regex pattern + priority + `Mode` for dispatch. |
25+
| `Context` | Per-request state store. Access via `getContext()`. Holds CLI args and HTTP request/response. |
26+
| `Dispatcher` | CLI entry point (`bin/dispatcher`). Reads `--import` to load applications. |
27+
| `HttpServer` | Built-in Netty-based HTTP server. Start with `bin/dispatcher start --import org.tinystruct.system.HttpServer`. |
28+
29+
### Package Map
30+
31+
```
32+
org.tinystruct/
33+
├── AbstractApplication.java → extend this
34+
├── Application.java → interface
35+
├── ApplicationException.java → checked exception
36+
├── ApplicationRuntimeException.java → unchecked exception
37+
├── application/
38+
│ ├── Action.java → runtime action wrapper
39+
│ ├── ActionRegistry.java → singleton route registry
40+
│ └── Context.java → request context
41+
├── system/
42+
│ ├── annotation/Action.java → @Action annotation + Mode enum
43+
│ ├── Dispatcher.java → CLI dispatcher
44+
│ ├── HttpServer.java → built-in HTTP server
45+
│ ├── EventDispatcher.java → event bus
46+
│ └── Settings.java → reads application.properties
47+
├── data/component/Builder.java → JSON serialization (use instead of Gson/Jackson)
48+
└── http/ → Request, Response, Constants
49+
```
50+
51+
---
52+
53+
## Creating an Application
54+
55+
Every module is an `Application`. Extend `AbstractApplication`:
56+
57+
```java
58+
package com.example;
59+
60+
import org.tinystruct.AbstractApplication;
61+
import org.tinystruct.ApplicationException;
62+
import org.tinystruct.system.annotation.Action;
63+
import org.tinystruct.system.annotation.Action.Mode;
64+
65+
public class HelloApp extends AbstractApplication {
66+
67+
@Override
68+
public void init() {
69+
// One-time setup: set config, register resources.
70+
// Do NOT register actions here — use @Action annotation instead.
71+
this.setTemplateRequired(false); // skip .view template lookup if returning data directly
72+
}
73+
74+
@Override
75+
public String version() {
76+
return "1.0.0";
77+
}
78+
79+
// Handles: bin/dispatcher hello AND GET /?q=hello
80+
@Action("hello")
81+
public String hello() {
82+
return "Hello, tinystruct!";
83+
}
84+
85+
// Path parameter: GET /?q=greet/James OR bin/dispatcher greet/James
86+
@Action("greet")
87+
public String greet(String name) {
88+
return "Hello, " + name + "!";
89+
}
90+
91+
// HTTP-only POST handler
92+
@Action(value = "submit", mode = Mode.HTTP_POST)
93+
public String submit() throws ApplicationException {
94+
// Access raw request if needed
95+
return "Submitted";
96+
}
97+
}
98+
```
99+
100+
### `init()` Rules
101+
- Called once when the application is loaded (via `setConfiguration()`).
102+
- Use it for: setting up DB connections, configuring resource paths, calling `setTemplateRequired(false)`.
103+
- **Do not** call `setAction()` here — use `@Action` annotation, which is processed automatically by `AnnotationProcessor`.
104+
105+
---
106+
107+
## @Action Annotation Reference
108+
109+
```java
110+
@Action(
111+
value = "path/subpath", // required: URI segment or CLI command
112+
description = "What it does", // shown in --help output
113+
mode = Mode.HTTP_POST, // default: Mode.DEFAULT (both CLI + HTTP)
114+
arguments = { // optional: parameter metadata for CLI help
115+
@Argument(key = "--id", description = "The item ID")
116+
},
117+
options = {}, // CLI option flags
118+
example = "bin/dispatcher path/subpath --id 42"
119+
)
120+
public String myAction(int id) { ... }
121+
```
122+
123+
### Mode Values
124+
| Mode | When it triggers |
125+
|---|---|
126+
| `DEFAULT` | Both CLI and HTTP (GET, POST, etc.) |
127+
| `CLI` | CLI dispatcher only |
128+
| `HTTP_GET` | HTTP GET only |
129+
| `HTTP_POST` | HTTP POST only |
130+
| `HTTP_PUT` | HTTP PUT only |
131+
| `HTTP_DELETE` | HTTP DELETE only |
132+
| `HTTP_PATCH` | HTTP PATCH only |
133+
134+
### Path Parameters
135+
tinystruct automatically builds a regex from the method signature:
136+
137+
```java
138+
@Action("user/{id}")
139+
public String getUser(int id) { ... }
140+
// → pattern: ^/?user/(-?\d+)$
141+
142+
@Action("search")
143+
public String search(String query) { ... }
144+
// → pattern: ^/?search/([^/]+)$
145+
// → CLI: bin/dispatcher search/hello
146+
// → HTTP: /?q=search/hello
147+
```
148+
149+
Supported parameter types: `String`, `int/Integer`, `long/Long`, `float/Float`, `double/Double`, `boolean/Boolean`, `char/Character`, `short/Short`, `byte/Byte`, `Date` (parsed as `yyyy-MM-dd HH:mm:ss`).
150+
151+
### Accessing Request/Response
152+
153+
Include `Request` and/or `Response` as parameters — ActionRegistry automatically injects them from `Context`:
154+
155+
```java
156+
@Action(value = "upload", mode = Mode.HTTP_POST)
157+
public String upload(Request<?, ?> req, Response<?, ?> res) throws ApplicationException {
158+
// req.getParameter("file"), res.setHeader(...), etc.
159+
return "ok";
160+
}
161+
```
162+
163+
---
164+
165+
## Context and CLI Arguments
166+
167+
```java
168+
@Action("echo")
169+
public String echo() {
170+
// CLI: bin/dispatcher echo --words "Hello World"
171+
Object words = getContext().getAttribute("--words");
172+
if (words != null) return words.toString();
173+
return "No words provided";
174+
}
175+
```
176+
177+
CLI flags passed as `--key value` are stored in `Context` as `"--key"` → value.
178+
179+
---
180+
181+
## JSON Handling (use `Builder`, not Gson/Jackson)
182+
183+
```java
184+
import org.tinystruct.data.component.Builder;
185+
186+
// Serialize
187+
Builder response = new Builder();
188+
response.put("status", "success");
189+
response.put("count", 42);
190+
response.put("data", someList);
191+
return response.toString(); // {"status":"success","count":42,...}
192+
193+
// Parse
194+
Builder parsed = new Builder();
195+
parsed.parse(jsonString);
196+
String status = parsed.get("status").toString();
197+
```
198+
199+
---
200+
201+
## Session Management (Web Mode)
202+
203+
```java
204+
@Action(value = "login", mode = Mode.HTTP_POST)
205+
public String login() {
206+
getContext().getSession().setAttribute("userId", "42");
207+
return "Logged in";
208+
}
209+
210+
@Action("profile")
211+
public String profile() {
212+
Object userId = getContext().getSession().getAttribute("userId");
213+
if (userId == null) return "Not logged in";
214+
return "User: " + userId;
215+
}
216+
```
217+
218+
---
219+
220+
## Event System
221+
222+
```java
223+
// 1. Define an event
224+
public class OrderCreatedEvent implements org.tinystruct.system.Event<Order> {
225+
private final Order order;
226+
public OrderCreatedEvent(Order order) { this.order = order; }
227+
228+
@Override public String getName() { return "order_created"; }
229+
@Override public Order getPayload() { return order; }
230+
}
231+
232+
// 2. Register a handler (typically in init())
233+
EventDispatcher.getInstance().registerHandler(OrderCreatedEvent.class, event -> {
234+
CompletableFuture.runAsync(() -> sendConfirmationEmail(event.getPayload()));
235+
});
236+
237+
// 3. Dispatch
238+
EventDispatcher.getInstance().dispatch(new OrderCreatedEvent(newOrder));
239+
```
240+
241+
---
242+
243+
## Templates
244+
245+
If `templateRequired` is `true` (the default), `toString()` looks for a `.view` file:
246+
- Location: `src/main/resources/themes/<ClassName>.view` (on classpath)
247+
- Variables are interpolated using `[%variableName%]`
248+
249+
```java
250+
// In your action method:
251+
setVariable("username", "James");
252+
setVariable("count", String.valueOf(42));
253+
// The template file uses: [%username%] and [%count%]
254+
```
255+
256+
To skip templates and return data directly (e.g., for APIs):
257+
```java
258+
@Override
259+
public void init() {
260+
this.setTemplateRequired(false);
261+
}
262+
```
263+
264+
---
265+
266+
## Configuration (`application.properties`)
267+
268+
Located at `src/main/resources/application.properties`:
269+
270+
```properties
271+
# Database
272+
driver=org.h2.Driver
273+
database.url=jdbc:h2:~/mydb
274+
database.user=sa
275+
database.password=
276+
277+
# Server
278+
default.home.page=hello # default action for /?q= (root URL)
279+
server.port=8080
280+
281+
# Locale
282+
default.language=en_US
283+
```
284+
285+
Access config values in your application:
286+
```java
287+
String port = this.getConfiguration("server.port");
288+
```
289+
290+
---
291+
292+
## Running the Application
293+
294+
```bash
295+
# CLI mode
296+
bin/dispatcher hello
297+
bin/dispatcher greet/James
298+
bin/dispatcher echo --words "Hello" --import com.example.HelloApp
299+
300+
# HTTP server (listens on :8080 by default)
301+
bin/dispatcher start --import org.tinystruct.system.HttpServer
302+
# Then: http://localhost:8080/?q=hello
303+
304+
# Generate POJO from DB table
305+
bin/dispatcher generate --table users
306+
307+
# Run SQL
308+
bin/dispatcher sql-query "SELECT * FROM users" --import org.tinystruct.system.Dispatcher
309+
```
310+
311+
---
312+
313+
## Testing Patterns
314+
315+
Use JUnit 5. ActionRegistry is a singleton — reset or use fresh state carefully in tests.
316+
317+
```java
318+
import org.junit.jupiter.api.*;
319+
import org.tinystruct.application.ActionRegistry;
320+
321+
class MyAppTest {
322+
323+
private MyApp app;
324+
325+
@BeforeEach
326+
void setUp() {
327+
app = new MyApp();
328+
// Set a minimal configuration to trigger init() and annotation processing
329+
Settings config = new Settings();
330+
app.setConfiguration(config);
331+
}
332+
333+
@Test
334+
void testHello() throws Exception {
335+
Object result = app.invoke("hello");
336+
Assertions.assertEquals("Hello, tinystruct!", result);
337+
}
338+
339+
@Test
340+
void testGreet() throws Exception {
341+
Object result = app.invoke("greet", new Object[]{"James"});
342+
Assertions.assertEquals("Hello, James!", result);
343+
}
344+
}
345+
```
346+
347+
For `ActionRegistry` unit tests, follow the pattern in:
348+
`src/test/java/org/tinystruct/application/ActionRegistryTest.java`
349+
350+
---
351+
352+
## Common Pitfalls
353+
354+
| Problem | Fix |
355+
|---|---|
356+
| `ApplicationRuntimeException: template not found` | Call `setTemplateRequired(false)` in `init()` if you return data directly |
357+
| Action not found at runtime | Make sure the class is imported via `--import` or listed in `application.properties` |
358+
| Method not registered | Ensure `@Action` annotation is on a `public` method — private/protected methods are ignored |
359+
| CLI arg not visible | Pass with `--key value` syntax; access via `getContext().getAttribute("--key")` |
360+
| JSON using Gson/Jackson | Use `org.tinystruct.data.component.Builder` instead — it's the framework-native JSON library |
361+
| Two methods same path, wrong one fires | Set explicit `mode` (e.g., `HTTP_GET` vs `HTTP_POST`) to disambiguate |
362+
363+
---
364+
365+
## Reference Files
366+
367+
- `DEVELOPER_GUIDE.md` — full developer guide with examples
368+
- `README.md` — quick start and architecture diagram
369+
- `src/main/java/org/tinystruct/AbstractApplication.java` — complete base class
370+
- `src/main/java/org/tinystruct/system/annotation/Action.java` — annotation definition + `Mode` enum
371+
- `src/main/java/org/tinystruct/application/ActionRegistry.java` — routing engine
372+
- `src/test/java/org/tinystruct/application/ActionRegistryTest.java` — registry test examples

0 commit comments

Comments
 (0)