{"id":13623914,"url":"https://github.com/wizzardo/webery","last_synced_at":"2026-03-15T03:35:41.796Z","repository":{"id":20095661,"uuid":"23365088","full_name":"wizzardo/webery","owner":"wizzardo","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-24T10:42:18.000Z","size":1327,"stargazers_count":212,"open_issues_count":5,"forks_count":18,"subscribers_count":17,"default_branch":"master","last_synced_at":"2024-10-25T09:11:47.645Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wizzardo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-08-26T20:05:43.000Z","updated_at":"2024-10-24T10:42:22.000Z","dependencies_parsed_at":"2023-10-02T10:30:33.306Z","dependency_job_id":"ec6d69cc-eee2-4c9c-a2c3-fbeb823df137","html_url":"https://github.com/wizzardo/webery","commit_stats":null,"previous_names":["wizzardo/http"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wizzardo%2Fwebery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wizzardo%2Fwebery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wizzardo%2Fwebery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wizzardo%2Fwebery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wizzardo","download_url":"https://codeload.github.com/wizzardo/webery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223684860,"owners_count":17185725,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:36.932Z","updated_at":"2025-12-18T16:30:52.930Z","avatar_url":"https://github.com/wizzardo.png","language":"Java","funding_links":[],"categories":["Java","开发框架"],"sub_categories":[],"readme":"[![CircleCI](https://circleci.com/gh/wizzardo/webery.svg?style=svg)](https://circleci.com/gh/wizzardo/webery)\n[![codecov](https://codecov.io/gh/wizzardo/webery/branch/master/graph/badge.svg)](https://codecov.io/gh/wizzardo/webery)\n\nJava HTTP-server\n=========\n\nserver based on my [epoll](https://github.com/wizzardo/epoll)\n\n```java\nHttpServer\u003cHttpConnection\u003e server = new HttpServer\u003c\u003e(8080);\nserver.getUrlMapping()\n        .append(\"/\", (request, response) -\u003e response.setBody(\"It's alive!\"));\nserver.start();\n```\n\n---\n\n\u003ca name=\"up\"/\u003e\n\n## Framework\n\n- [Installation](#installation)\n- [Initialization](#initialization)\n- [Building and running](#building-and-running)\n- [Url-mapping](#url-mapping)\n- [Dependency injection](#di)\n- [Configuration](#configuration)\n- [Template engine](#template-engine)\n- [i18n](#i18n)\n- [Taglib](#taglib)\n\n\n---\n\n\u003ca name=\"installation\"/\u003e\n\n#### Installation [↑](#up)\n\n##### Gradle\n```\nbuild.gradle\n```\n```groovy\napply plugin: 'java'\napply plugin: 'application'\n\nrepositories {\n    jcenter()\n    mavenCentral()\n    maven {\n        url \"https://oss.sonatype.org/content/repositories/snapshots/\"\n    }\n}\n\nsourceCompatibility = 1.8\ntargetCompatibility = 1.8\n\nversion = '0.1'\n\nmainClassName = \"com.example.MyWebApp\"\n\ndependencies {\n    compile 'com.wizzardo:http:0.2+'\n}\n\n//create a single Jar with all dependencies\ntask fatJar(type: Jar) {\n    manifest {\n        attributes(\n                \"Main-Class\": mainClassName\n        )\n    }\n    baseName = project.name + '-all'\n    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }\n    with jar\n}\n```\n\n---\n\n\u003ca name=\"initialization\"/\u003e\n\n#### Initialization [↑](#up)\n\n```java\nimport com.wizzardo.http.framework.Controller;\nimport com.wizzardo.http.framework.Environment;\nimport com.wizzardo.http.framework.WebApplication;\nimport com.wizzardo.http.framework.template.Renderer;\n\npublic class MyWebApp {\n\n    static class AppController extends Controller {\n        public Renderer index() {\n            return renderString(\"It's alive!\");\n        }\n    }\n\n    public static void main(String[] args) {\n        WebApplication application = new WebApplication(args);\n        application.onSetup(app -\u003e {\n            app.getUrlMapping()\n                    .append(\"/\", AppController.class, \"index\");\n        });\n        application.start();\n    }\n}\n```\n\n---\n\n\u003ca name=\"building-and-running\"/\u003e\n\n#### Building and running [↑](#up)\n\n```bash\n./gradlew fatJar\njava -jar build/libs/MyWebApp-all.jar env=prod profiles.active=profile_A,profile_B\n```\n\n---\n\n\u003ca name=\"url-mapping\"/\u003e\n\n#### Url-mapping [↑](#up)\n\nControllers and actions could be mapped to static paths or\nto something dynamic with variables and wildcards\n```java\nurlMapping\n    .append(\"/index\", AppController.class, \"index\")\n    .append(\"/books/$id?\", AppController.class, \"books\") // 'id' - is optional\n    .append(\"/optionals/$foo?/$bar?\", AppController.class, \"optionals\") // 'foo' and 'bar' - are optional\n    .append(\"/${foo}-${bar}\", AppController.class, \"fooBar\")\n    .append(\"/any/*\", AppController.class, \"any\")\n    .append(\"*.html\", AppController.class, \"html\")\n    .append(\"/upload\", AppController.class, \"upload\", Request.Method.POST) // only POST method is allowed\n    ;\n```\n\n---\n\n\u003ca name=\"di\"/\u003e\n\n#### Dependency injection [↑](#up)\nFramework supports simple dependency injections, to make class or interface injectable simple annotate it with @Injectable.\n\nThere are several scopes for it:\n- SINGLETON - one instance per jvm, default\n- PROTOTYPE - new instance for every injection\n- SESSION - one instance per user-session\n- REQUEST - new instance every request\n- THREAD_LOCAL - one instance per thread\n\nControllers are stateful so their scope is PROTOTYPE, Services - SINGLETON.\n```java\nstatic class AppController extends Controller {\n    AppService appService;\n\n    public Renderer index() {\n        return renderString(appService.getMessage());\n    }\n}\n```\n\nTo make dependency injection work with implicit dependencies, you need to specify package for scan:\n```java\n    application.onSetup(app -\u003e {\n        DependencyFactory.get(ResourceTools.class)\n                .addClasspathFilter(className -\u003e className.startsWith(\"com.example\"));\n    });\n```\n\n##### Raw usage of DI\n```java\nDependencyFactory.get().register(CustomBean.class, new SingletonDependency\u003c\u003e(CustomBean.class));\nCustomBean bean = DependencyFactory.get(CustomBean.class);\n```\n\n---\n\n\u003ca name=\"configuration\"/\u003e\n\n#### Configuration [↑](#up)\n\n```\nsrc/main/resources/Config.groovy\n```\n```groovy\nserver {\n    host = '0.0.0.0'\n    port = 8080\n    ioWorkersCount = 1\n    ttl = 5 * 60 * 1000\n    context = 'myApp'\n    basicAuth {\n        username = 'user'\n        password = 'pass'\n        token = true\n        tokenTTL = 7 * 24 * 60 * 60 * 1000l\n        tokenized { // creates mapping with path '/resourceName/*' and respective name for static resources available by token\n            resourceName = 'path/to/resource'\n        }\n    }\n\n    ssl {\n        cert = '/etc/ssl/certs/hostname.crt'\n        key = '/etc/ssl/private/hostname.key'\n    }\n    \n    session {\n        ttl = 30 * 60\n    }\n    \n    resources {\n        path = 'public' // load static files from resource folder 'public'\n        mapping = '/static'\n        cache = {\n            enabled = true\n            gzip = true\n            ttl = -1L\n            memoryLimit = 32 * 1024 * 1024L\n            maxFileSize = 5 * 1024 * 1024L\n        }\n    }\n    debugOutput = false // dump of all requests and responses to System.out\n}\n//this configuration will be only applied for certain environment\nenvironments {\n    dev {\n        custom.key = true\n    }\n    prod {\n        custom.key = false\n        server.ioWorkersCount = 4\n    }\n}\n\n//this configuration will be only applied for certain profiles\nprofiles {\n    profile_A {\n        environments {\n            dev {\n                value = 'value_dev_A'\n            }\n            prod {\n                value = 'value_prod_A'\n            }\n        }\n    }\n    profile_B {\n        value = 'value_B'\n    }\n}\n```\nConfiguration stored in Holders:\n```java\n    boolean key = Holders.getConfig().config(\"custom\").get(\"key\", defaulValue);\n```\nor it can be mapped to pojo and injected to other objects:\n```java\npublic class CustomConfig implements Configuration {\n    public boolean key;\n\n    @Override\n    public String prefix() {\n        return \"custom\";\n    }\n}\n```\n\nConfiguration is loaded in this order:\n - Default configuration and manifest\n - Config.groovy\n - External configuration (```webApp.onLoadConfiguration(app -\u003e app.loadConfig(\"MyCustomConfig.groovy\"))```)\n - Profiles and environments\n - OS environment variables (```System.getenv()```)\n - Java System properties (```System.getProperties()```)\n - Command line arguments\n\n---\n\n\u003ca name=\"template-engine\"/\u003e\n\n#### Template engine [↑](#up)\n\nThis framework has it's own template engine, inspired and based on Groovy Server Pages (GSP)\n```java\nstatic class AppController extends Controller {\n    public Renderer index() {\n        model().append(\"name\", params().get(\"name\", \"%user name%\"));\n        return renderView();\n    }\n}\n```\nEngine will try to render html from template 'resources/views/controller_name/view_name.gsp', by default 'view_name' is 'action_name':\n```\nsrc/main/resources/views/app/index.gsp\n```\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n      \u003ctitle\u003eHello\u003c/title\u003e\n   \u003c/head\u003e\n   \u003cbody\u003e\n      Hello, ${name}!\n   \u003c/body\u003e\n\u003c/html\u003e\n```\n\n---\n\n\u003ca name=\"i18n\"/\u003e\n\n#### i18n [↑](#up)\n\n```java\n    MessageBundle ms = DependencyFactory.get(MessageBundle.class);\n\n    //load message bundle from resources/i18n/messages.properties\n    //and lazy load any other language, for example messages_en.properties, messages_fr.properties\n    ms.load(\"messages\");\n\n    String foo = ms.get(\"foo\");\n    String fooDe = ms.get(Locale.GERMANY,\"foo\");\n\n    //it also supports templates\n    //foobar = {0} {1}\n    String foobar = ms.get(\"foobar\", \"foo\", \"bar\"); // \"foo bar\"\n\n```\n\n---\n\n\u003ca name=\"taglib\"/\u003e\n\n#### Taglib [↑](#up)\n\n - [checkBox](#checkBox)\n - [collect](#collect)\n - [createLink](#createLink)\n - [each](#each)\n - [else](#else)\n - [elseif](#elseif)\n - [form](#form)\n - [formatBoolean](#formatBoolean)\n - [hiddenField](#hiddenField)\n - [if](#if)\n - [join](#join)\n - [layoutBody](#layoutBody)\n - [layoutHead](#layoutHead)\n - [layoutTitle](#layoutTitle)\n - [link](#link)\n - [message](#message)\n - [passwordField](#passwordField)\n - [radio](#radio)\n - [resource](#resource)\n - [set](#set)\n - [textArea](#textArea)\n - [textField](#textField)\n - [while](#while)\n\n---\n\n\u003ca name=\"checkBox\"/\u003e\n\n##### checkBox [↑](#taglib)\nGenerates a checkbox form field.\n\n###### Template:\n```html\n\u003cg:checkBox name=\"myCheckbox\" value=\"${true}\"/\u003e\n\u003cg:checkBox name=\"myCheckbox\" id=\"myCheckbox_${1}\" checked=\"${true}\"/\u003e\n```\n###### Result:\n```html\n\u003cinput type=\"checkbox\" name=\"myCheckbox\" id=\"myCheckbox\" value=\"true\"/\u003e\n\u003cinput type=\"checkbox\" name=\"myCheckbox\" id=\"myCheckbox_1\" checked=\"checked\"/\u003e\n```\n###### Attributes\n- name - The name of the checkbox\n- value (optional) - The value of the checkbox\n- checked (optional) - Expression if evaluates to true sets to checkbox to checked\n\n---\n\n\u003ca name=\"collect\"/\u003e\n\n##### collect [↑](#taglib)\nIterate over each element of the specified collection transforming the result using the expression in the closure\n\n###### Template:\n```html\n\u003cdiv\u003e\n    \u003cg:collect in=\"${books}\" expr=\"${it.title}\"\u003e\n        $it\u003cbr/\u003e\n    \u003c/g:collect\u003e\n\u003c/div\u003e\n```\n\n###### Action:\n```java\nmodel().append(\"books\", Arrays.asList(new Book(\"Book one\"), new Book(\"Book two\")))\n```\n\n###### Book.java:\n```java\npublic class Book {\n    public final String title;\n\n    public Book(String title) {\n        this.title = title;\n    }\n}\n```\n\n###### Result:\n```html\n\u003cdiv\u003e\n    Book one\n    \u003cbr/\u003e\n    Book two\n    \u003cbr/\u003e\n\u003c/div\u003e\n```\n\n###### Attributes\n- in - The object to iterative over\n- expr - A expression\n\n---\n\u003ca name=\"createLink\"/\u003e\n\n##### createLink [↑](#taglib)\nCreates a link that can be used where necessary (for example in an href, JavaScript, Ajax call etc.)\n\nController:\n```java\npublic class BookController extends Controller {\n\n    public Renderer list() {\n        return renderString(\"list of books\");\n    }\n\n    public Renderer show() {\n        return renderString(\"some book\");\n    }\n}\n```\nurl-mapping:\n```java\napp.getUrlMapping()\n        .append(\"/book/list\", BookController.class, \"list\")\n        .append(\"/book/$id\", BookController.class, \"show\");\n```\n\n###### Template:\n```html\n// generates \u003ca href=\"/book/1\"\u003elink\u003c/a\u003e\n\u003ca href=\"${createLink([controller:'book', action:'show', id: 1])}\"\u003elink\u003c/a\u003e\n\n// generates \"/book/show?foo=bar\u0026boo=far\"\n\u003cg:createLink controller=\"book\" action=\"show\" params=\"[foo: 'bar', boo: 'far']\"/\u003e\n\n// generates \"/book/list\"\n\u003cg:createLink controller=\"book\" action=\"list\" /\u003e\n\n// generates \"http://localhost:8080/book/list\"\n\u003cg:createLink controller=\"book\" action=\"list\" absolute=\"true\"/\u003e\n\n// generates \"http://ya.ru/book/list\"\n\u003cg:createLink controller=\"book\" action=\"list\" base=\"http://ya.ru\"/\u003e\n```\n\n###### Attributes:\n- action (optional) - The name of the action to use in the link; if not specified the current action will be linked\n- controller (optional) - The name of the controller to use in the link; if not specified the current controller will be linked\n- id (optional) - The id to use in the link\n- fragment (optional) - The link fragment (often called anchor tag) to use\n- mapping (optional) - The named URL mapping to use, default mapping = controllerName + '.' + actionName\n- params (optional) - A Map of request parameters\n- absolute (optional) - If true will prefix the link target address with the value of the server.host property from config\n- base (optional) - Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the absolute property if both are specified.\n\n---\n\u003ca name=\"each\"/\u003e\n\n##### each [↑](#taglib)\nIterate over each element of the specified collection.\n\n###### Template:\n```html\n\u003cdiv\u003e\n    \u003cg:each in=\"[1,2,3]\" var=\"i\"\u003e\n        $i\u003cbr/\u003e\n    \u003c/g:each\u003e\n\u003c/div\u003e\n```\n###### Result:\n```html\n\u003cdiv\u003e\n    1\n    \u003cbr/\u003e\n    2\n    \u003cbr/\u003e\n    3\n    \u003cbr/\u003e\n\u003c/div\u003e\n```\n\n###### Attributes:\n- in - The collection to iterate over\n- status (optional) - The name of a variable to store the iteration index in. Starts with 0 and increments for each iteration.\n- var (optional) - The name of the item, defaults to \"it\".\n\n---\n\u003ca name=\"else\"/\u003e\n\n##### else [↑](#taglib)\nThe logical else tag\n\n###### Template:\n```html\n\u003cg:if test=\"${false}\"\u003e\n    never happen\n\u003c/g:if\u003e\n\u003cg:else\u003e\n    Hello, world!\n\u003c/g:else\u003e\n```\n\n###### Result:\n```html\n    Hello, world!\n```\n\n---\n\u003ca name=\"elseif\"/\u003e\n\n##### elseif [↑](#taglib)\nThe logical elseif tag\n\n###### Template:\n```html\n\u003cg:if test=\"${false}\"\u003e\n    never happen\n\u003c/g:if\u003e\n\u003cg:elseif test=\"${true}\"\u003e\n    Hello, world!\n\u003c/g:elseif\u003e\n```\n\n###### Result:\n```html\n    Hello, world!\n```\n\n###### Attributes:\n- test - The expression to test\n\n\n---\n\u003ca name=\"form\"/\u003e\n\n##### form [↑](#taglib)\nCreates a form, extends 'createLink' tag\n\n---\n\u003ca name=\"formatBoolean\"/\u003e\n##### formatBoolean [↑](#taglib)\nOutputs the given boolean as the specified text label.\n\n###### Template:\n```html\n\u003cg:formatBoolean boolean=\"${myBoolean}\" /\u003e\n\u003cg:formatBoolean boolean=\"${myBoolean}\" true=\"True!\" false=\"False!\" /\u003e\n```\n\n###### Result:\n```html\ntrue\nTrue!\n```\n\n###### Attributes:\n- boolean - Variable to evaluate\n- true (optional) - Output if value is true. If not specified, 'boolean.true' or 'default.boolean.true' is looked up from the Message Source\n- false (optional) - Output if value is false. If not specified, 'boolean.false' or 'default.boolean.false' is looked up from the Message Source\n\n---\n\u003ca name=\"hiddenField\"/\u003e\n\n##### hiddenField [↑](#taglib)\nCreates a input of type 'hidden' (a hidden field).\n\n###### Template:\n```html\n\u003cg:hiddenField name=\"myField\" value=\"myValue\" /\u003e\n```\n\n###### Result:\n```html\n\u003cinput type=\"hidden\" name=\"myField\" id=\"myField\" value=\"myValue\"/\u003e\n```\n\n###### Attributes:\n- name - The name of the text field\n- value (optional) - The value of the text field\n\n---\n\u003ca name=\"if\"/\u003e\n\n##### if [↑](#taglib)\nThe logical if tag to switch on an expression.\n\n###### Template:\n```html\n\u003cg:if test=\"${true}\"\u003e\n    Hello!\n\u003c/g:if\u003e\n```\n\n###### Result:\n```html\n    Hello!\n```\n\n###### Attributes:\n- test - The expression to test\n\n---\n\u003ca name=\"join\"/\u003e\n\n##### join [↑](#taglib)\nConcatenate the toString() representation of each item in this collection with the given separator.\n\n###### Template:\n```html\n\u003cg:join in=\"['Hello', 'World!']\" delimiter=\", \"/\u003e\n```\n\n###### Result:\n```html\nHello, World!\n```\n\n###### Attributes:\n- in - The collection to iterate over\n- delimiter (optional) - The value of the delimiter to use during the join. Default: ', '\n\n---\n\u003ca name=\"layoutBody\"/\u003e\n\n##### layoutBody [↑](#taglib)\nUsed in layouts to output the contents of the body tag of the decorated page.\n\n###### Decorated page template:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003cmeta name=\"layout\" content=\"myLayout\" /\u003e\n        \u003cscript src=\"myscript.js\" /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003ePage to be decorated\u003c/body\u003e\n\u003c/html\u003e\n```\n###### Layout template `views/layouts/myLayout.gsp`:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003cscript src=\"global.js\" /\u003e\n        \u003cg:layoutHead /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003e\u003cg:layoutBody /\u003e\u003c/body\u003e\n\u003c/html\u003e\n```\n\n###### Result:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003cscript src=\"global.js\" /\u003e\n        \u003cscript src=\"myscript.js\" /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003ePage to be decorated\u003c/body\u003e\n\u003c/html\u003e\n```\n\n---\n\u003ca name=\"layoutHead\"/\u003e\n\n##### layoutHead [↑](#taglib)\nUsed in layouts to render the contents of the head tag of the decorated page\n\n###### Decorated page template:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003cmeta name=\"layout\" content=\"myLayout\" /\u003e\n        \u003cscript src=\"myscript.js\" /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003ePage to be decorated\u003c/body\u003e\n\u003c/html\u003e\n```\n###### Layout template `views/layouts/myLayout.gsp`:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003cscript src=\"global.js\" /\u003e\n        \u003cg:layoutHead /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003e\u003cg:layoutBody /\u003e\u003c/body\u003e\n\u003c/html\u003e\n```\n\n###### Result:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003cscript src=\"global.js\" /\u003e\n        \u003cscript src=\"myscript.js\" /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003ePage to be decorated\u003c/body\u003e\n\u003c/html\u003e\n```\n\n---\n\u003ca name=\"layoutTitle\"/\u003e\n\n##### layoutTitle [↑](#taglib)\nUsed in layouts to render the contents of the title tag of the decorated page\n\n###### Decorated page template:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003cmeta name=\"layout\" content=\"myLayout\" /\u003e\n        \u003ctitle\u003eHello World!\u003c/title\u003e\n        \u003cscript src=\"myscript.js\" /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003ePage to be decorated\u003c/body\u003e\n\u003c/html\u003e\n```\n###### Layout template `views/layouts/myLayout.gsp`:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n\t\t\u003ctitle\u003e\u003cg:layoutTitle default=\"Some Title\" /\u003e\u003c/title\u003e\n        \u003cscript src=\"global.js\" /\u003e\n        \u003cg:layoutHead /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003e\u003cg:layoutBody /\u003e\u003c/body\u003e\n\u003c/html\u003e\n```\n\n###### Result:\n```html\n\u003chtml\u003e\n   \u003chead\u003e\n        \u003ctitle\u003eHello World!\u003c/title\u003e\n        \u003cscript src=\"global.js\" /\u003e\n        \u003cscript src=\"myscript.js\" /\u003e\n   \u003c/head\u003e\n   \u003cbody\u003ePage to be decorated\u003c/body\u003e\n\u003c/html\u003e\n```\n\n---\n\u003ca name=\"link\"/\u003e\n\n##### link [↑](#taglib)\nCreates an html anchor tag with the href set based on the specified parameters. Extends 'createLink' tag.\n\n###### Template:\n```html\n\u003cg:link controller=\"book\" action=\"show\" params=\"[id: 1]\"\u003elink\u003c/g:link\u003e\n```\n\n###### Result:\n```html\n\u003ca href=\"/book/1\"\u003elink\u003c/a\u003e\n```\n\n###### Attributes:\n[same as in createLink tag](#createLink)\n\n---\n\u003ca name=\"message\"/\u003e\n\n##### message [↑](#taglib)\nResolves a message from the given code.\n\n###### Template:\n```html\n// test.message.1.args = test message: {0}\n\u003cg:message code=\"test.message.${1}.args\" args=\"['one']\"/\u003e\n```\n\n###### Result:\n```html\ntest message: one\n```\n\n###### Attributes:\n- code - The code to resolve the message for.\n- default (optional) - The default message to output if the error or code cannot be found in messages.properties.\n- args (optional) - A list of argument values to apply to the message when code is used.\n- locale (optional) Override Locale to use instead of the one detected\n\n---\n\u003ca name=\"passwordField\"/\u003e\n\n##### passwordField [↑](#taglib)\nCreates a input of type 'password' (a password field). An implicit \"id\" attribute is given the same value as name unless you explicitly specify one.\n\n###### Template:\n```html\n\u003cg:passwordField name=\"myPasswordField\" value=\"${'myPassword'}\" /\u003e\n```\n\n###### Result:\n```html\n\u003cinput type=\"password\" name=\"myPasswordField\" id=\"myPasswordField\" value=\"myPassword\" /\u003e\n```\n\n###### Attributes:\n- name - The name of the password field\n- value - The value of the password field\n\n---\n\u003ca name=\"radio\"/\u003e\n\n##### radio [↑](#taglib)\nGenerates a radio button\n\n###### Template:\n```html\n\u003cg:radio name=\"myGroup\" value=\"1\"/\u003e\n\u003cg:radio name=\"myGroup\" value=\"2\" checked=\"true\"/\u003e\n```\n\n###### Result:\n```html\n\u003cinput type=\"radio\" name=\"myGroup\" value=\"1\" /\u003e\n\u003cinput type=\"radio\" name=\"myGroup\" checked=\"checked\" value=\"2\" /\u003e\n```\n\n###### Attributes:\n- value - The value of the radio button\n- name - The name of the radio button\n- checked - boolean to indicate that the radio button should be checked\n\n---\n\u003ca name=\"resource\"/\u003e\n\n##### resource [↑](#taglib)\nGenerates a link (URI) string for static resources. Can be used in an href, JavaScript, Ajax call, etc.\n\n###### Template:\n```html\n\u003cg:resource dir=\"css\" file=\"main.css\" /\u003e\n\u003cg:resource dir=\"css\" file=\"main.css\" absolute=\"true\" /\u003e\n\u003cg:resource dir=\"js\" file=\"test.js\"/\u003e\n\u003cimg src=\"${resource(dir:'img', file:'logo.jpg')}\"\u003e\n\u003cg:resource dir=\"js\" file=\"test.tag\" tag=\"script\" url=\"src\" type=\"riot/tag\"/\u003e\n```\n\n###### Result:\n```html\n\u003clink rel=\"stylesheet\" href=\"/static/css/main.vEA6A.css\"\u003e\n\u003clink rel=\"stylesheet\" href=\"http://localhost:8080/static/css/main.vEA6A.css\"\u003e\n\u003cscript type=\"text/javascript\" src=\"/static/js/test.v207A.js\"\u003e\u003c/script\u003e\n\u003cimg src=\"/static/img/logo.v53B0.jpg\"/\u003e\n\u003cscript type=\"riot/tag\" src=\"/static/js/test.v73CF.tag\"\u003e\u003c/script\u003e\n```\n\n###### Attributes:\n- dir - the name of the directory containing the resource\n- file - the name of the resource file\n- tag - the name of the html tag\n- url - the name of the attribute for the url\n- absolute - If true will prefix the link target address with the value of the `host` property from Config.groovy.\n\n---\n\u003ca name=\"set\"/\u003e\n\n##### set [↑](#taglib)\nSets the value of a variable accessible with the GSP page.\n\n###### Template:\n```html\n\u003cg:set var=\"counter\" value=\"${0}\" /\u003e\n\u003cg:while test=\"${counter \u003c 3}\"\u003e\n    \u003cp\u003eCurrent i = ${i}\u003c/p\u003e\n    \u003cg:set var=\"counter\" value=\"${counter + 1}\" /\u003e\n\u003c/g:while\u003e\n```\n\n###### Result:\n```html\n    \u003cp\u003eCurrent i = 0\u003c/p\u003e\n    \u003cp\u003eCurrent i = 1\u003c/p\u003e\n    \u003cp\u003eCurrent i = 2\u003c/p\u003e\n```\n\n###### Attributes:\n- var - The name of the variable\n- value - The initial value\n\n---\n\u003ca name=\"textArea\"/\u003e\n\n##### textArea [↑](#taglib)\nCreates a HTML text area element. An implicit \"id\" attribute is given the same value as name unless you explicitly specify one.\n\n###### Template:\n```html\n\u003cg:textArea name=\"myField\" value=\"myValue\" rows=\"5\" cols=\"40\"/\u003e\n```\n\n###### Result:\n```html\n\u003ctextarea name=\"myField\" id=\"myField\" rows=\"5\" cols=\"40\" \u003e\nmyValue\n\u003c/textarea\u003e\u003e\n```\n\n###### Attributes:\n- name - The name of the text area\n- value - The initial text to display in the text area. By default the text area will be empty.\n\n---\n\u003ca name=\"textField\"/\u003e\n\n##### textField [↑](#taglib)\nCreates a input of type 'text' (a text field). An implicit \"id\" attribute is given the same value as the name unless you explicitly specify one.\n\n###### Template:\n```html\n\u003cg:textField name=\"myField\" value=\"${'input'}\" /\u003e\n```\n\n###### Result:\n```html\n\u003cinput type=\"text\" name=\"myField\" id=\"myField\" value=\"input\" /\u003e\n```\n\n###### Attributes:\n- name - The name of the text field\n- value - The initial text to display in the text field. By default the text field will be empty.\n\n---\n\u003ca name=\"while\"/\u003e\n\n##### while [↑](#taglib)\nExecutes a condition in a loop until the condition returns false.\n\n###### Template:\n```html\n\u003cg:while test=\"${i \u003c 3}\"\u003e\n    \u003cp\u003eCurrent i = ${i++}\u003c/p\u003e\n\u003c/g:while\u003e\n```\n\n###### Result:\n```html\n    \u003cp\u003eCurrent i = 0\u003c/p\u003e\n    \u003cp\u003eCurrent i = 1\u003c/p\u003e\n    \u003cp\u003eCurrent i = 2\u003c/p\u003e\n```\n\n###### Attributes:\n- test - The conditional expression\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwizzardo%2Fwebery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwizzardo%2Fwebery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwizzardo%2Fwebery/lists"}