{"id":17343259,"url":"https://github.com/geirolz/scope","last_synced_at":"2025-04-14T19:53:57.363Z","repository":{"id":40588274,"uuid":"424377373","full_name":"geirolz/scope","owner":"geirolz","description":"A functional, compile-time and type-safe models layer separator","archived":false,"fork":false,"pushed_at":"2025-03-16T00:09:56.000Z","size":226,"stargazers_count":12,"open_issues_count":12,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T08:12:11.383Z","etag":null,"topics":["cats","functional-programming","lightweight","pure","scala","typesafe"],"latest_commit_sha":null,"homepage":"https://geirolz.github.io/scope/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/geirolz.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":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-03T20:54:13.000Z","updated_at":"2024-12-27T16:19:49.000Z","dependencies_parsed_at":"2023-09-24T02:26:11.034Z","dependency_job_id":"3c9f5c0b-4bf8-43d5-8cb2-cf1693a2a34a","html_url":"https://github.com/geirolz/scope","commit_stats":{"total_commits":185,"total_committers":7,"mean_commits":"26.428571428571427","dds":0.7513513513513513,"last_synced_commit":"84bbd2acedacdc4a740c43378bfd4b36f13ba10b"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geirolz%2Fscope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geirolz%2Fscope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geirolz%2Fscope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geirolz%2Fscope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/geirolz","download_url":"https://codeload.github.com/geirolz/scope/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248952029,"owners_count":21188421,"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":["cats","functional-programming","lightweight","pure","scala","typesafe"],"created_at":"2024-10-15T16:08:54.788Z","updated_at":"2025-04-14T19:53:57.338Z","avatar_url":"https://github.com/geirolz.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Scope\n[![Build Status](https://github.com/geirolz/scope/actions/workflows/cicd.yml/badge.svg)](https://github.com/geirolz/scope/actions)\n[![Sonatype Nexus (Releases)](https://img.shields.io/nexus/r/com.github.geirolz/scope-core_2.13?nexusVersion=2\u0026server=https%3A%2F%2Foss.sonatype.org)](https://mvnrepository.com/artifact/com.github.geirolz/scope-core)\n[![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-blue.svg?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org)\n[![Mergify Status](https://img.shields.io/endpoint.svg?url=https://api.mergify.com/v1/badges/geirolz/scope\u0026style=flat)](https://mergify.io)\n[![GitHub license](https://img.shields.io/github/license/geirolz/scope)](https://github.com/geirolz/scope/blob/main/LICENSE)\n\nA functional, compile-time and type-safe models layer separator\n\n## How to install\n\n```sbt\nlibraryDependencies += \"com.github.geirolz\" % \"scope-core\" % \"0.0.11\"\nlibraryDependencies += \"com.github.geirolz\" % \"scope-generic\" % \"0.0.11\"//optional - for scala 2 and 3\n```\n\n\n## How to use\n\n### Defining the ModelMapper\n\nGiven\n```scala\nimport scope.*\nimport scope.syntax.*\n\n//datatypes\ncase class UserId(value: Long)\ncase class Name(value: String)\ncase class Surname(value: String)\n\n//doman models\ncase class User(id: UserId, name: Name, surname: Surname)\n\n//http rest contracts\ncase class UserContract(id: Long, name: String, surname: String)\nobject UserContract{    \n    implicit val modelMapperForUserContract: ModelMapper[Scope.Endpoint, User, UserContract] =\n      ModelMapper.scoped[Scope.Endpoint](user =\u003e {\n        UserContract(\n            user.id.value,\n            user.name.value,\n            user.surname.value\n        )\n      })\n}\n```\n\n##### Side effects\nIf the conversion has side effects you can use `ModelMapperK` instead.\n```scala\nimport scala.util.Try\n\nimplicit val modelMapperKForUserContract: ModelMapperK[Try, Scope.Endpoint, User, UserContract] =\n  ModelMapperK.scoped[Scope.Endpoint](user =\u003e Try {\n    UserContract(\n        user.id.value,\n        user.name.value,\n        user.surname.value,\n    )\n  })\n// modelMapperKForUserContract: ModelMapperK[Try, Scope.Endpoint, User, UserContract] = scope.ModelMapperK@47cbc06c\n```\n\n##### Same fields different model\nOften in order to decouple things we just duplicate the same model changing just the name. \nFor example we could find `UserContract` form the endpoint and `User` from the domain that are actually equals deferring only on the name.\n\nIn these case macros can same us some boilerplate, importing the `scope-generic` module you can use `deriveCaseClassIdMap` to derive\nthe `ModelMapper` that map the object using the same fields. If the objects aren't equals from the signature point of view the compilation will fail.\nKeep in mind that this macro only supports the primary constructor, smart constructors are not supported.\n\n```scala\ncase class User(id: UserId, name: Name, surname: Surname)\n\ncase class UserContract(id: UserId, name: Name, surname: Surname)\nobject UserContract{    \n        \n    import scope.*\n    import scope.generic.syntax.*\n        \n    implicit val modelMapperForUserContract: ModelMapper[Scope.Endpoint, User, UserContract] =\n      ModelMapper.scoped[Scope.Endpoint].deriveCaseClassIdMap[User, UserContract]\n}\n```\n\n### Using the ModelMapper\nTo use the ModelMapper you have to provide the right `ScopeContext` implicitly\n\nGiven\n```scala\nval user: User = User(\n    UserId(1),\n    Name(\"Foo\"),\n    Surname(\"Bar\"),\n)\n```\n\n```scala\nimplicit val scopeCtx: TypedScopeContext[Scope.Endpoint] = ScopeContext.of[Scope.Endpoint]\n// scopeCtx: TypedScopeContext[Scope.Endpoint] = scope.TypedScopeContext@2517b11d\n\nuser.scoped.as[UserContract]\n// res0: UserContract = UserContract(\n//   id = UserId(value = 1L),\n//   name = Name(value = \"Foo\"),\n//   surname = Surname(value = \"Bar\")\n// )\n```\n\n##### Side effects\nIf the conversion has side effects you have to write \n```scala\nimport scala.util.Try\n\nuser.scoped.as[Try[UserContract]]\n// res1: Try[UserContract] = Success(\n//   value = UserContract(\n//     id = UserId(value = 1L),\n//     name = Name(value = \"Foo\"),\n//     surname = Surname(value = \"Bar\")\n//   )\n// )\n```\n\nIn this case if you don't have a `ModelMapperK` defined but just a `ModelMapper` if an `Applicative` instance \nis available in the scope for your effect `F[_]` the pure `ModelMapper` will be lifted using `Applicative[F].pure(...)`\n\n\n### ScopeContext\nIf the `ScopeContext` is wrong or is missing the compilation will fail\n```scala\nimplicit val scopeCtx: TypedScopeContext[Scope.Event] = ScopeContext.of[Scope.Event]\n\nuser.scoped.as[UserContract]\n// error: diverging implicit expansion for type scope.ModelMapper[scopeCtx.ScopeType,User,UserContract]\n// starting with method liftPureModelMapper in trait ModelMapperKInstances\n// user.scoped.as[UserContract]\n//               ^\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeirolz%2Fscope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeirolz%2Fscope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeirolz%2Fscope/lists"}