{"id":17180104,"url":"https://github.com/theseems/clinq","last_synced_at":"2025-04-13T17:13:07.430Z","repository":{"id":46080749,"uuid":"409278718","full_name":"TheSeems/ClinQ","owner":"TheSeems","description":"Experimental validation mini-framework","archived":false,"fork":false,"pushed_at":"2021-11-16T07:17:13.000Z","size":63,"stargazers_count":13,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-13T17:13:00.482Z","etag":null,"topics":["declarative","java","token","validation"],"latest_commit_sha":null,"homepage":"","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/TheSeems.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-22T16:31:16.000Z","updated_at":"2022-09-27T17:22:13.000Z","dependencies_parsed_at":"2022-08-31T03:50:29.095Z","dependency_job_id":null,"html_url":"https://github.com/TheSeems/ClinQ","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheSeems%2FClinQ","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheSeems%2FClinQ/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheSeems%2FClinQ/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheSeems%2FClinQ/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheSeems","download_url":"https://codeload.github.com/TheSeems/ClinQ/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248750126,"owners_count":21155687,"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":["declarative","java","token","validation"],"created_at":"2024-10-15T00:28:44.046Z","updated_at":"2025-04-13T17:13:07.395Z","avatar_url":"https://github.com/TheSeems.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ClinQ\n\nSimple yet interesting validation mini-framework\n\n## Features\n\n### Fluent checks\n\n```java\nClinQ.\u003cInteger\u003echecker()\n\t.with(i -\u003e i % 2 == 0)\n\t.with(i -\u003e i \u003e 100)\n\t.with(i -\u003e i \u003c 1000)\n\t.check(myInteger)\n```\n\n### Mappings\n\n```java\nClinQ.\u003cInteger\u003echecker()\n\t.and(i -\u003e i % 2 == 0)\n\t.map(Double::valueOf)  // Map integer to double and make further checks with it\n\t.and(d -\u003e Math.pow(d, 3.5) \u003e 500)\n\t.check(myInteger);\n```\n\n### Nested checks\n\nFor example we have a sample data transfer object:\n\n```java\npublic class SampleDto {\n\tprivate final String name;\n\tprivate final List\u003cInteger\u003e scores;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic List\u003cInteger\u003e getScores() {\n\t\treturn scores;\n\t}\n\n\tpublic SampleDto(String name, List\u003cInteger\u003e scores) {\n\t\tthis.name = name;\n\t\tthis.scores = scores;\n\t}\n}\n```\n\nThen we can validate it field-by-field using the nested checkers:\n\n```java\nvar checker = ClinQ.\u003cSampleDto\u003echecker()\n    .with(Objects::nonNull)\n    .with(SampleDto::getName, nameChecker -\u003e nameChecker\n\t    .with(Objects::nonNull)\n\t    .with(str -\u003e str.length() \u003e 2))\n    .with(SampleDto::getScores, scoresChecker -\u003e scoresChecker\n\t    .with(Objects::nonNull)\n\t    .with(scores -\u003e scores.size() \u003e 2)\n\t    .with(scores -\u003e scores.stream().allMatch(score -\u003e score \u003e 0)))\n    .with(dto -\u003e dto.getName().length() == dto.getScores().size());\n\nSampleDto one = new SampleDto(\"hi!\", List.of(1, 2, 3));\nAssertions.assertTrue(checker.check(one));\n```\n\n### Error description\n\n#### In-place\n\nYou can specify error message together with check\n\n```java\nvar checker = Clinq.\u003cString\u003echecker()\n\t.notNull(\"Input is null\")\n\t.and(\"Length should be at least 2\", name -\u003e name.length() \u003e= 2)\n\t.mapCheck(\"First char should be digit\", name -\u003e name.charAt(0), Character::isDigit)\n\t.mapCheck(\"Second char should be letter\", name -\u003e name.charAt(1), Character::isLetter);\n```\n\n#### Out-of-place\n\nYou can specify error message straight under the check\n\n```java\nvar checker = Clinq.\u003cString\u003echecker()\n\t.notNull(\"Input is null\")\n\t.and(name -\u003e name.length() \u003e= 2)\n\t    .error(\"Length should be at least 2\")\n\t.mapCheck(name -\u003e name.charAt(0), Character::isDigit)\n\t    .error(\"First char should be digit\")\n\t.mapCheck(name -\u003e name.charAt(1), Character::isLetter)\n\t    .error(\"Second char should be letter\");\n```\n\n#### Collecting errors\n\nYou can collect errors with basically everything that can collect\n\n```java\nTestCheckErrors errors = new TestCheckErrors();\nchecker.check(\"i1nvalid\", errors);\nerrors.assertSame(List.of(\"First char should be digit\", \"Second char should be letter\"));\n```\n\n### Blocking checks\n\nYou can declare every check blocking. It means when it's failed the validation completely stops with the verdict:\nincorrect.  \nIt can be declared in two styles similar to errors:\n\n#### In-place\n\n```java\nvar checker = Clinq.\u003cString\u003echecker()\n\t.notNull(\"Input is null\")\n\t.and(\"Length should be at least 2\", name -\u003e name.length() \u003e= 2)\n\t// notice that we won't go further if this fails\n\t.mapCheckBlocking(\"First char should be digit\", name -\u003e name.charAt(0), Character::isDigit)\n\t.mapCheck(\"Second char should be letter\", name -\u003e name.charAt(1), Character::isLetter);\n\nTestCheckErrors errors = new TestCheckErrors();\nchecker.check(\"i1nvalid\", errors);\nerrors.assertSame(List.of(\"First char should be digit\"));\n```\n\n#### Out-of-place\n\n```java\nvar checker = Clinq.\u003cString\u003echecker()\n\t.notNull(\"Input is null\")\n\t.and(name -\u003e name.length() \u003e= 2)\n\t    .error(\"Length should be at least 2\")\n\t.mapCheck(name -\u003e name.charAt(0), Character::isDigit)\n\t    .error(\"First char should be digit\")\n\t    .blocking()  // notice that we won't go further if this fails\n\t.mapCheck(name -\u003e name.charAt(1), Character::isLetter)\n\t    .error(\"Second char should be letter\");\n\nTestCheckErrors errors = new TestCheckErrors();\nchecker.check(\"i1nvalid\", errors);\nerrors.assertSame(List.of(\"First char should be digit\"));\n```\n\n## How to use\n\nCurrently, this library can be fetched via JitPack:\n\n[![](https://jitpack.io/v/theseems/ClinQ.svg)](https://jitpack.io/#theseems/ClinQ)\n\nHere are specified instructions for Gradle (for maven etc. they can be found at JitPack - just click on the badge above)\n\nStep 1. Add the JitPack repository to your build file\n\n```groovy\nallprojects {\n    repositories {\n        ...\n        maven { url 'https://jitpack.io' }\n    }\n}\n```\n\nStep 2. Add the dependency\n\n```groovy\ndependencies {\n    implementation 'com.github.theseems:ClinQ:v1.1'\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheseems%2Fclinq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheseems%2Fclinq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheseems%2Fclinq/lists"}