Last active
August 30, 2023 09:41
-
-
Save DiggesT/8d1d16fa08c5637b5a15b10d1976d82e to your computer and use it in GitHub Desktop.
Scala code research
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients | |
import sttp.model.{MediaType, StatusCode} | |
import sttp.capabilities.fs2.Fs2Streams | |
import sttp.tapir.* | |
import sttp.tapir.swagger.bundle.SwaggerInterpreter | |
import sttp.tapir.json.circe.jsonBody | |
import io.foldables.ratio.BuildInfo | |
import io.foldables.ratio.http.Common | |
import io.foldables.ratio.common.data.{Collection, Filter, Failure} | |
import io.foldables.ratio.services.auth.model.Session | |
import io.foldables.ratio.services.blobs.model.Blob | |
import io.foldables.ratio.services.clients.model.{Client} | |
object Api: | |
val rootPath = | |
Common.ApiV1Path / "clients" // ApiV1Path: EndpointInput[Unit] = "api" / "v1" | |
val root = // tapir | |
endpoint | |
.in(rootPath) | |
.securityIn(auth.bearer[Session.Token]()) | |
val getClient | |
: Endpoint[ // Endpoint[SECURITY_INPUT, INPUT, ERROR_OUTPUT, OUTPUT, -R] | |
Session.Token, | |
Client.Id, | |
Failure, | |
Client.WithMeta, | |
Any | |
] = | |
root // val root: Endpoint[Token, Unit, Unit, Unit, Any] | |
.get // def get: Endpoint[Token, Unit, Unit, Unit, Any] | |
// def in[J, IJ](i: EndpointInput[J])(implicit concat: ParamConcat.Aux[Unit, J, IJ]): Endpoint[Token, IJ, Unit, Unit, Any] | |
.in(path[Client.Id]) | |
// def errorOut[F, EF](o: EndpointOutput[F])(implicit ts: ParamConcat.Aux[Unit, F, EF]): Endpoint[Token, Id, EF, Unit, Any] | |
.errorOut(Common.notFound) | |
// def out[P, OP](i: EndpointOutput[P])(implicit ts: ParamConcat.Aux[Unit, P, OP]): Endpoint[Token, Id, Failure, OP, Any] | |
.out(jsonBody[Client.WithMeta]) | |
// val listClients: Endpoint[Session.Token, Filter, Failure, Collection[ | |
// Client.WithMeta | |
// ], Any] = | |
// root.get | |
// .in(Filter.filter) | |
// .errorOut(Common.unauthorized) | |
// .out(jsonBody[Collection[Client.WithMeta]]) | |
// val putClient: Endpoint[ | |
// Session.Token, | |
// (Client.Id, Client.UpdatedBase), | |
// Failure, | |
// Client.WithMeta, | |
// Any | |
// ] = | |
// root.put | |
// .in(path[Client.Id]) | |
// .in(jsonBody[Client.UpdatedBase]) | |
// .errorOut(Common.unauthorized) | |
// .out(jsonBody[Client.WithMeta]) | |
val postClient: Endpoint[ | |
Session.Token, | |
Client.Base, | |
Failure, | |
Client.WithMeta, | |
Any | |
] = | |
root.post | |
.in(jsonBody[Client.Base]) | |
.errorOut(Common.unauthorized) | |
.out(jsonBody[Client.WithMeta]) | |
// GET — получение ресурса | |
// POST — создание ресурса | |
// PUT — обновление ресурса | |
val endpoints: List[AnyEndpoint] = | |
List( | |
Api.getClient, | |
Api.postClient | |
) | |
def swaggerEndpoints[F[_]] = | |
SwaggerInterpreter() | |
.fromEndpoints[F](endpoints, "clients", BuildInfo.version) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Message(sender: String, recipient: String, body: String) | |
val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?") | |
println(message1.sender) // печатает guillaume@quebec.ca | |
message1.sender = "travis@washington.us" // эта строка не компилируется | |
val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") | |
val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") | |
val messagesAreTheSame = message2 == message3 // true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Point: | |
private var _x = 0 | |
private var _y = 0 | |
private val bound = 100 | |
def x: Int = _x | |
def x_=(newValue: Int): Unit = | |
if newValue < bound then | |
_x = newValue | |
else | |
printWarning() | |
def y: Int = _y | |
def y_=(newValue: Int): Unit = | |
if newValue < bound then | |
_y = newValue | |
else | |
printWarning() | |
private def printWarning(): Unit = | |
println("WARNING: Out of bounds") | |
end Point | |
val point1 = Point() | |
point1.x = 99 | |
point1.y = 101 // prints the warning |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients.model | |
import java.time.OffsetDateTime | |
//import java.time.{LocalDate} | |
import java.util.UUID | |
import cats.implicits.* | |
import io.circe.generic.semiauto.* | |
import io.circe.{Decoder, Encoder, parser} | |
import sttp.tapir.Schema.SName | |
import sttp.tapir.Schema | |
import io.github.arainko.ducktape.* | |
import io.foldables.ratio.common.{MkId} | |
import io.foldables.ratio.common.data.Metadata | |
//import io.foldables.ratio.rrule.RRule | |
import io.foldables.ratio.services.auth.model.{Actor} | |
//Классы образцы (Case classes) похожи на обычные классы с несколькими ключевыми отличиями | |
//Классы образцы хороши для моделирования неизменяемых данных | |
//сущность клиента, все необходимые данные, которые будут занесены в БД | |
//дополнить электронноой карточкой, записями | |
case class Client( | |
firstName: String, // should be Person.Name? | |
middleName: Option[String], | |
lastName: String | |
// birthday: Option[LocalDate] | |
// date of registration | |
// electronic card | |
// list of visit records | |
) | |
object Client: | |
// UUID is a class that represents an immutable universally unique identifier (UUID). | |
// Opaque types offer a sound abstraction over implementation details, without imposing performance overhead | |
opaque type Id = UUID // final class UUID: UUID | |
object Id extends MkId[Id](identity, identity) | |
// metadata is data that describes other data | |
type Meta = Metadata.Default[Id] | |
// client with meta | |
final case class WithMeta(meta: Meta, origin: Client) | |
object WithMeta: | |
// Semi-automatic codec derivation. | |
// This object provides helpers for creating [[io.circe.Decoder]] and [[io.circe.ObjectEncoder]] | |
// instances for case classes, "incomplete" case classes, sealed trait hierarchies, etc. | |
given Decoder[WithMeta] = deriveDecoder[WithMeta] | |
given Encoder[WithMeta] = deriveEncoder[WithMeta] | |
// With tapir, you can describe HTTP API endpoints as immutable Scala values. | |
// Each endpoint can contain a number of input and output parameters | |
given Schema[WithMeta] = Schema | |
.derived[WithMeta] | |
.copy(name = Some(Schema.SName("ClientWithMeta"))) | |
// new client | |
final case class New( | |
createdBy: Actor.Id, | |
firstName: String, | |
middleName: Option[String], | |
lastName: String | |
): | |
def withMeta(meta: Meta): Client.WithMeta = | |
Client.WithMeta(meta, this.to[Client]) | |
object New: | |
given Decoder[New] = deriveDecoder[New] | |
given Encoder[New] = deriveEncoder[New] | |
given Schema[New] = | |
Schema.derived[New].copy(name = Some(SName("ClientNew"))) | |
/** For frontend-form (createdBy comes from HTTP headers) */ | |
final case class Base( | |
firstName: String, | |
middleName: Option[String], | |
lastName: String | |
): | |
def toNew(createdBy: Actor.Id): Client.New = | |
this | |
.into[Client.New] | |
.transform(Field.const(_.createdBy, createdBy)) | |
object Base: | |
given Decoder[Base] = deriveDecoder[Base] | |
given Encoder[Base] = deriveEncoder[Base] | |
given Schema[Base] = | |
Schema.derived[Base].copy(name = Some(SName("ClientBase"))) | |
given Decoder[Client] = deriveDecoder[Client] | |
given Encoder[Client] = deriveEncoder[Client] | |
given Schema[Client] = Schema.derived[Client] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients.model | |
import java.time.{Instant, LocalDateTime, ZoneId} | |
import cats.MonadThrow | |
import cats.implicits.* | |
import cats.effect.{Clock, Ref} | |
import cats.effect.std.UUIDGen | |
import io.foldables.ratio.common.data.{Collection, Filter, Metadata} | |
import io.foldables.ratio.core.utils.Update.findAndUpdate_ | |
import io.foldables.ratio.services.auth.model.{Actor, User} | |
trait Clients[F[_]]: // why do we need clients? | |
// maybe the client can have an add method? | |
def add(client: Client.New): F[Client.WithMeta] | |
// def update(id: Client.Id, ckient: Client.Updated): F[Option[Client.WithMeta]] | |
// def list(filter: Filter): F[Collection[Client.WithMeta]] | |
// def get(id: Client.Id): F[Option[Client.WithMeta]] | |
object Clients: | |
def apply[F[_]](implicit ev: Clients[F]): Clients[F] = | |
ev | |
type InMemoryDb[F[_]] = Ref[F, InMemory] | |
final case class InMemory(clients: List[Client.WithMeta]): | |
def counts: Map[String, Int] = | |
Map("clients" -> clients.length) | |
private object InMemory: | |
val Empty = InMemory(Nil) | |
// def filterItems(filter: Filter, item: Client.WithMeta): Boolean = | |
// filter.query match | |
// case None => true | |
// case Some(term) => | |
// val q = term.toLowerCase | |
// item.origin.title.toLowerCase.contains(q) | |
def inMemory[F[_]: MonadThrow: Ref.Make: Clock: UUIDGen] | |
: F[(InMemoryDb[F], Clients[F])] = | |
Ref.of(InMemory.Empty).map { inMemory => | |
val clients = new Clients[F]: | |
// def get(id: Client.Id): F[Option[Client.WithMeta]] = | |
// inMemory.get.map { memory => | |
// memory.clients.find(_.meta.id == id) | |
// } | |
def add(client: Client.New): F[Client.WithMeta] = | |
Metadata.Default.create(Client.Id.apply)(client.createdBy).flatMap { | |
meta => | |
inMemory.modify { memory => | |
val newClient = client.withMeta(meta) | |
val updated = memory.copy(clients = newClient :: memory.clients) | |
(updated, newClient) | |
} | |
} | |
// def update(id: Client.Id, client: Client.Updated): F[Option[Client.WithMeta]] = | |
// Metadata.getTime[F].flatMap { updatedAt => | |
// inMemory.modify { memory => | |
// def addMeta(p: Client.WithMeta): Client.WithMeta = | |
// p.copy(meta = p.meta.revise(updatedAt, client.updatedBy), origin = p.origin.update(client)) | |
// val (found, updated) = findAndUpdate_(memory.clients, _.meta.id == id, addMeta) | |
// (memory.copy(clients = updated), found) | |
// } | |
// } | |
// def list(filter: Filter): F[Collection[Client.WithMeta]] = | |
// inMemory | |
// .get | |
// .map(_.clients) | |
// .map(Collection.fromList(filterItems, filter)) | |
(inMemory, clients) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients | |
import cats.effect.Concurrent | |
import io.circe.Json | |
import io.circe.syntax.* | |
import org.http4s.{EntityDecoder, EntityEncoder} | |
import org.http4s.circe.* | |
import io.foldables.ratio.core.protocol | |
import io.foldables.ratio.services.auth.model.Session | |
import io.foldables.ratio.services.clients.model.Client | |
object Codecs: | |
given [F[_]](using Concurrent[F]): EntityDecoder[F, Client] = | |
jsonOf[F, Client] | |
given [F[_]]: EntityEncoder[F, Client] = jsonEncoderOf[F, Client] | |
given [F[_]](using Concurrent[F]): EntityDecoder[F, Client.Base] = | |
jsonOf[F, Client.Base] | |
given [F[_]]: EntityEncoder[F, Client.Base] = jsonEncoderOf[F, Client.Base] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def loadFromPath[F[_]: Sync](path: JPath): F[Config] = | |
val source = ConfigSource.default(ConfigSource.file(path)).withFallback(ConfigSource.default) | |
loadF[F, Config](source) | |
def load(args: List[String]): IO[Either[Help, (SubCommand, Option[Config])]] = //input array: List[String], output: why IO | |
command.parse(args) match // how it matching string with help/commands? | |
case Left(help) => IO.pure(help.asLeft) //help command | |
case Right(cli) => //other commands | |
cli match //match with SubCommand | |
case SubCommand.Run(configPath, _) => //Run command | |
loadFromPath[IO](configPath).map(c => (cli, Some(c)).asRight) | |
case SubCommand.Load(configPath, _) => //Load command | |
loadFromPath[IO](configPath).map(c => (cli, Some(c)).asRight) | |
case SubCommand.ApiExport(path) => //ApiExport command | |
IO.pure(Right(SubCommand.ApiExport(path), None)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients | |
import cats.implicits.* | |
import skunk.* | |
import skunk.Codec | |
import skunk.implicits.* | |
import skunk.data.Type | |
import skunk.codec.`enum` | |
import skunk.codec.temporal.{timestamp, timestamptz} | |
import skunk.codec.all.* | |
//import io.foldables.ratio.rrule.RRule | |
import io.foldables.ratio.database.Decoders.metaDefault | |
import io.foldables.ratio.services.clients.model.Client | |
import io.foldables.ratio.services.users.Decoders.{actorId, userId} | |
object Decoders: | |
val id = uuid.imap(Client.Id.apply)(Client.Id.unbox) | |
val firstName: Codec[String] = varchar(256) | |
val middleName: Codec[Option[String]] = varchar(256).opt | |
val lastName: Codec[String] = varchar(256) | |
// def status: Codec[Client.Status] = | |
// `enum`.`enum`[Client.Status]( | |
// _.toString.toLowerCase, | |
// s => Client.Status.fromString(s).toOption, | |
// Type("client_status") | |
// ) | |
// def rrule: Codec[RRule] = | |
// varchar(512).eimap(RRule.parse)(_.asString) | |
// def origin = | |
// (userId.opt ~ timestamptz.opt) | |
// .imap { case assigneeId ~ due ~ rrule => | |
// Client(assigneeId, due, rrule) | |
// } { t => | |
// t.assigneeId ~ t.due ~ t.repeating | |
// } | |
// val clientMeta = metaDefault(id) | |
// val clientWithMeta = | |
// (clientMeta ~ origin).imap { case meta ~ origin => | |
// Client.WithMeta(meta, origin) | |
// } { t => t.meta ~ t.origin } | |
val ClientNew = | |
(actorId ~ firstName ~ middleName ~ lastName) | |
.imap { case createdBy ~ firstName ~ middleName ~ lastName => | |
Client.New(createdBy, firstName, middleName, lastName) | |
} { t => | |
t.createdBy ~ t.firstName ~ t.middleName ~ t.lastName | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients.model | |
import io.circe.syntax.* | |
import io.foldables.ratio.bus.Event | |
import io.foldables.ratio.services.auth.model.Actor | |
import io.foldables.ratio.services.clients.model.Client | |
//registration of various events | |
object events: | |
val Service = "client" | |
// bus.Event.New | |
def added(author: Actor.Id, client: Client.WithMeta): Event.New = | |
Event.New( | |
Service, | |
"client_added", | |
Event.Version(1, 0), | |
client.asJson, | |
Client.Id.unbox(client.meta.id), | |
author | |
) | |
// def updated(author: Actor.Id, client: Client.WithMeta): Event.New = | |
// Event.New(Service, "client_updated", Event.Version(1, 0), client.asJson, Client.Id.unbox(client.meta.id), author) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients | |
import java.util.UUID | |
import java.io.FileInputStream | |
import cats.{MonadThrow, Monad} | |
import cats.implicits.* | |
import cats.effect.Async | |
import io.circe.* | |
import io.circe.syntax.* | |
import org.http4s.HttpRoutes | |
import sttp.capabilities.fs2.Fs2Streams | |
import sttp.tapir.* | |
import sttp.tapir.server.ServerEndpoint | |
import sttp.tapir.server.http4s.Http4sServerInterpreter | |
import io.foldables.ratio.http.Auth | |
import io.foldables.ratio.bus.Bus | |
import io.foldables.ratio.common.data.Failure | |
import io.foldables.ratio.services.auth.model.{Persons, Session} | |
import io.foldables.ratio.services.blobs.model.Blobs | |
import io.foldables.ratio.services.users.Codecs.given | |
import io.foldables.ratio.services.clients.Codecs.given | |
import io.foldables.ratio.services.clients.model.Clients | |
object Http: | |
def build[F[_]: Clients: Session: Persons: Blobs: Bus: Async]: HttpRoutes[F] = | |
Http4sServerInterpreter[F]().toRoutes(endpoints[F]) | |
def openApi[F[_]](using Async[F]) = | |
Http4sServerInterpreter.apply[F]().toRoutes(Api.swaggerEndpoints[F]) | |
def endpoints[F[_]: Clients: Persons: Session: Blobs: Bus: Async] | |
: List[ServerEndpoint[Fs2Streams[F], F]] = | |
List( | |
// ToDo: get | |
// Api.getClient | |
// .serverSecurityLogic(Auth.authWithTokenF[F]) | |
// .serverLogic(user => | |
// id => | |
// Service | |
// .getById(user, id) | |
// .map(_.toRight(Failure.NotFound(id.toString))) | |
// ), | |
// Api | |
// .getClients | |
// .serverSecurityLogic(Auth.authWithTokenF[F]) | |
// .serverLogicSuccess(user => filter => Service.list(user, filter)), | |
// Api | |
// .putClient | |
// .serverSecurityLogic(Auth.authWithTokenF[F]) | |
// .serverLogicSuccess(user => (id, updated) => Service.update(user, id, updated).map(_.toRight(Failure.NotFound(id.toString)))), | |
Api.postClient | |
.serverSecurityLogic(Auth.authWithTokenF[F]) | |
.serverLogicSuccess(user => client => Service.add(user, client)) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract class Monoid[A] { | |
def add(x: A, y: A): A | |
def unit: A | |
} | |
object ImplicitTest { | |
implicit val stringMonoid: Monoid[String] = new Monoid[String] { | |
def add(x: String, y: String): String = x concat y | |
def unit: String = "" | |
} | |
implicit val intMonoid: Monoid[Int] = new Monoid[Int] { | |
def add(x: Int, y: Int): Int = x + y | |
def unit: Int = 0 | |
} | |
def sum[A](xs: List[A])(implicit m: Monoid[A]): A = | |
if (xs.isEmpty) m.unit | |
else m.add(xs.head, sum(xs.tail)) | |
def main(args: Array[String]): Unit = { | |
println(sum(List(1, 2, 3))) // использует intMonoid неявно | |
println(sum(List("a", "b", "c"))) // использует stringMonoid неявно | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Values cannot be re-assigned | |
val x = 1 + 1 | |
println(x) // 2 | |
x = 3 // This does not compile. | |
var x = 1 + 1 | |
x = 3 // This compiles because "x" is declared with the "var" keyword. | |
//You can combine expressions by surrounding them with {}. We call this a block | |
println({ | |
val x = 1 + 1 | |
x + 1 | |
}) // 3 | |
//Methods look and behave very similar to functions (val), but there are a few key differences between them | |
def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier | |
println(addThenMultiply(1, 2)(3)) // 9 | |
//class | |
class Greeter(prefix: String, suffix: String): | |
def greet(name: String): Unit = | |
println(prefix + name + suffix) | |
val greeter = Greeter("Hello, ", "!") | |
greeter.greet("Scala developer") // Hello, Scala developer! | |
//By default, instances of case classes are immutable, and they are compared by value (unlike classes, whose instances are compared by reference) | |
case class Point(x: Int, y: Int) | |
val point = Point(1, 2) //point == anotherPoint | |
val anotherPoint = Point(1, 2) | |
val yetAnotherPoint = Point(2, 2) | |
//Objects are single instances of their own definitions. You can think of them as singletons of their own classes | |
bject IdFactory: | |
private var counter = 0 | |
def create(): Int = | |
counter += 1 | |
counter | |
val newId: Int = IdFactory.create() | |
println(newId) // 1 | |
val newerId: Int = IdFactory.create() | |
println(newerId) // 2 | |
//Traits are abstract data types containing certain fields and methods | |
trait Greeter: | |
def greet(name: String): Unit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Main extends IOApp: | |
def run(args: List[String]): IO[ExitCode] = //function, input list, output IO | |
greet //print version | |
*> //IO[Unit] *> IO[B] => IO[ExitCode] | |
Config. //load config file (object) | |
load(args). //load(args: List[String]): IO[Either[Help, (SubCommand, Option[Config])]], pattern matching the input command | |
flatMap { //flatMap[B](f: Either[Help, (SubCommand, Option[Config])] => IO[B]): IO[B], it's important to return IO[B] | |
case Right((Config.SubCommand.Load(_, fixtures), Some(config))) => //subcommand "Load" matched with Some parameters | |
Resources. //excexute Load command with parameters | |
build[IO](config) //build[F[_$2]: Async: Trace: Network: std.Console](config: Config): Resource[F, Resources[F]] | |
.use{ resources => //use? | |
import resources.given //something like lambda function (block) | |
//using for load fixtures | |
val path = Path.fromNioPath(fixtures) | |
Fixtures.load[IO](path) | |
}.as(ExitCode.Success)// | |
case Right((run: Config.SubCommand.Run, Some(config))) => //subcommand "Run" matched, why "run" parameter nedeed | |
config.database match //database type matching | |
case _: Config.Database.Postgres if run.fixtures.isDefined => //fixtures are defined | |
IO.println("It is prohibited to autoload fixtures with Postgres in config, use `load` command instead") .as(ExitCode.Error)//should use another command, casting as error, can't excute command | |
case _ => | |
Resources.//excexute Run command with parameters | |
build[IO](config).use { resources => //same build function | |
import resources.given | |
Server.server(config).use { _ => | |
run.fixtures.traverse_(jpath => Fixtures.load[IO](Path.fromNioPath(jpath))) *> IO.never | |
} | |
}.as(ExitCode.Success)// | |
case Right((Config.SubCommand.ApiExport(path), None)) => //subcommand "ApiExport" matched | |
Server.exportApi[IO](Path.fromNioPath(path)).as(ExitCode.Success)//exportApi[F[_$3]: Async](root: Path): F[Unit] | |
case Right(other) => //undefined command | |
IO.println(s"$other is not expected").as(ExitCode.Error)//print error text, why casting error type? can't execute command? | |
case Left(help) => //help command | |
IO.println(help.toString).as(ExitCode.Error) //help returning from load(), ptint help, why casting error type? | |
} | |
def greet: IO[Unit] = | |
IO.println(s"Running Ratio ${BuildInfo.version}...") // IO used for execute println only when greet called |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Iterable[A]: | |
... | |
def foldLeft[B](z: B)(op: (B, A) => B): B | |
... | |
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) | |
val res = numbers.foldLeft(0)((m, n) => m + n) | |
println(res) // 55 | |
def foldLeft1[A, B](as: List[A], b0: B, op: (B, A) => B) = ??? | |
def notPossible = foldLeft1(numbers, 0, _ + _) | |
def firstWay = foldLeft1[Int, Int](numbers, 0, _ + _) | |
def secondWay = foldLeft1(numbers, 0, (a: Int, b: Int) => a + b) | |
def foldLeft2[A, B](as: List[A], b0: B)(op: (B, A) => B) = ??? | |
def possible = foldLeft2(numbers, 0)(_ + _) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients | |
import java.time.LocalDateTime | |
import skunk.* | |
import skunk.codec.temporal.timestamp | |
import skunk.implicits.* | |
import scala.Tuple | |
import scala.compiletime.constValue | |
import cats.implicits.* | |
import cats.effect.{Concurrent, Resource} | |
import io.foldables.ratio.common.Table | |
import io.foldables.ratio.common.data.{Collection, Filter, Metadata} | |
import io.foldables.ratio.database.Postgres as CommonPg | |
import io.foldables.ratio.services.users.Decoders.actorId | |
import io.foldables.ratio.services.clients.model.{Client, Clients} | |
object Postgres: | |
object Queries: | |
val TT = Table.build[Client.WithMeta](Table.Config("clients", "items")) | |
val _TT = Table.build[Client.New](Table.Config.default) | |
// def getClientsF: Fragment[Void] = | |
// sql"""SELECT ${TT.columns.f} FROM ${TT.tableName.f}""" | |
// ToDo: get | |
// def get: Query[Client.Id, Client.WithMeta] = | |
// sql"""SELECT ${TT.columns.f} | |
// FROM ${TT.tableName.f} | |
// WHERE ${TT.id eql Decoders.id}""" | |
// .query(Decoders.clientWithMeta) | |
// def update = | |
// sql"""UPDATE ${TT.tableName.f} | |
// SET ${TT.updated_by eql actorId.opt}, | |
// ${TT.updated_at.currentTimestamp}, | |
// ${TT.row_version.increment}, | |
// ${TT.title eql Decoders.title}, | |
// ${TT.assignee_id eql actorId.opt}, | |
// ${TT.status eql Decoders.status} | |
// WHERE ${TT.id eql Decoders.id} | |
// RETURNING ${TT.columns.f}""" | |
// .query(Decoders.clientWithMeta) | |
def addClient: Query[Client.New, Client.Id ~ LocalDateTime] = | |
sql"""INSERT INTO ${TT.tableName.f} | |
(${_TT.columns.f}) | |
VALUES (${Decoders.ClientNew}) | |
RETURNING ${TT.select(List("id", "created_at")).f}""" | |
.query(Decoders.id ~ timestamp) | |
def build[F[_]: Concurrent](session: Resource[F, Session[F]]): Clients[F] = | |
new Clients[F]: | |
// def get(id: Client.Id): F[Option[Client.WithMeta]] = | |
// session.use { s => s.prepare(Queries.get).flatMap(_.option(id)) } | |
// def update(id: Client.Id, client: Client.Updated): F[Option[Client.WithMeta]] = | |
// session.use { s => | |
// s.prepare(Queries.update).flatMap(_.option(Some(client.updatedBy) ~ client.title ~ client.assigneeId ~ client.status ~ id)) | |
// } | |
// def list(filter: Filter): F[Collection[Client.WithMeta]] = | |
// session.use(CommonPg.mkCollection[F, Client.WithMeta](filter, Queries.getClientsF, List("title"), Decoders.clientWithMeta)) | |
def add(client: Client.New): F[Client.WithMeta] = | |
session.use { s => | |
for { | |
idCreated <- s.prepare(Queries.addClient).flatMap(_.unique(client)) | |
(id, createdAt) = idCreated | |
meta = Metadata.Default.init(id, createdAt, client.createdBy) | |
} yield client.withMeta(meta) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def file(name: String): Resource[IO, File] = Resource.make(openFile(name))(file => close(file)) | |
val concat: IO[Unit] = | |
( | |
for { | |
in1 <- file("file1") | |
in2 <- file("file2") | |
out <- file("file3") | |
} yield (in1, in2, out) | |
).use { case (file1, file2, file3) => | |
for { | |
bytes1 <- read(file1) | |
bytes2 <- read(file2) | |
_ <- write(file3, bytes1 ++ bytes2) | |
} yield () | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Resources[F[_]](random: Random[F], | |
passwords: Passwords[F], | |
persons: Persons[F], | |
tasks: Tasks[F], | |
session: Session[F], | |
inventory: Inventory[F], | |
blobs: Blobs[F], | |
comments: Comments[F], | |
fixtures: Fixtures[F], | |
events: Events[F], | |
bus: Bus[F], | |
eventStorage: EventStorage[F], | |
chats: Chats[F]): | |
given Random[F] = random | |
given Persons[F] = persons | |
given Tasks[F] = tasks | |
given Inventory[F] = inventory | |
given Session[F] = session | |
given Blobs[F] = blobs | |
given Comments[F] = comments | |
given Passwords[F] = passwords | |
given Fixtures[F] = fixtures | |
given Events[F] = events | |
given Bus[F] = bus | |
given EventStorage[F] = eventStorage | |
given Chats[F] = chats | |
object Resources: | |
def build[F[_]: Async: Trace: Network: Console](config: Config): Resource[F, Resources[F]] = //can't understand function definition | |
for { //sequence comprehensions | |
//variables for Resources | |
random <- Resource.eval(Random.scalaUtilRandom[F])//random: Random[F] | |
passwords = auth.Service.argon[F](random, config.auth.pepper)//passwords: Passwords[F] | |
_ <- Resource.eval(precreate[F](List(config.storage._1, config.storage._2, config.storage._3))) //what does it mean _? unit function? | |
a <- buildDb[F](random, passwords, config.database, config.storage) //a: (Persons[F], Tasks[F], Session[F], Inventory[F], Blobs[F], Comments[F], Fixtures[F], Events[F], Bus[F], Storage[F], Chats[F]) | |
_ <- notifications.Service.run[F](config.notifications.flatMap(_.telegram), a._9, a._1, a._11).background //what does it mean _? unit function? | |
} yield Resources(random, passwords, a._1, a._2, a._3, a._4, a._5, a._6, a._7, a._8, a._9, a._10, a._11) //return Resource[F, Resources[F]] type | |
def mkPostgresF[F[_]: Async: Files: Clock: Console: Network: Trace: Random: Passwords](storage: Config.Storage)(session: Resource[F, DbSession[F]]) = | |
for { | |
p <- Resource.eval(users.Postgres.build[F](session)) | |
t <- Resource.pure(tasks.Postgres.build[F](session)) | |
s <- Resource.eval(Session.inMemory[F]) | |
i <- Resource.pure(inventory.Postgres.build[F](session)) | |
b <- Resource.pure(blobs.Postgres.build[F](blobs.Service.read(storage), blobs.Service.write(storage), session)) | |
c <- Resource.pure(comments.Postgres.build[F](session)) | |
f <- Resource.pure(Fixtures.postgres[F](session)) | |
e <- Resource.pure(calendar.Postgres.build[F](session)) | |
eb <- bus.Postgres.build[F](session) | |
es <- Resource.pure(bus.Postgres.buildStorage(session)) | |
ch <- Resource.pure(notifications.Postgres.build[F](session)) | |
_ <- eb.subscribe.evalTap(es.add).compile.drain.background | |
} yield (p, t, s, i, b, c, f, e, eb, es, ch) | |
def inMemory[F[_]: Concurrent: Passwords: Random: Clock: Files: UUIDGen: Console](storage: Config.Storage) = | |
for { | |
p <- Persons.inMemory[F].map(_._2) | |
t <- Tasks.inMemory[F].map(_._2) | |
s <- Session.inMemory[F] | |
i <- Inventory.inMemory[F].map(_._2) | |
b <- Blobs.inMemory[F](blobs.Service.read(storage), blobs.Service.write(storage)).map(_._2) | |
c <- Comments.inMemory[F].map(_._2) | |
f = Fixtures.inMemory[F] | |
e <- Events.inMemory[F].map(_._2) | |
eb <- Bus.inMemory[F] | |
es <- EventStorage.inMemory[F].map(_._2) | |
ch <- Chats.inMemory[F].map(_._2) | |
} yield (p, t, s, i, b, c, f, e, eb, es, ch) | |
def buildDb[F[_]: Async: Files: Clock: Console: Network: Trace](random: Random[F], | |
passwords: Passwords[F], | |
database: Config.Database, | |
storage: Config.Storage): Resource[F, (Persons[F], Tasks[F], Session[F], Inventory[F], Blobs[F], Comments[F], Fixtures[F], Events[F], Bus[F], EventStorage[F], Chats[F])] = | |
given Random[F] = random | |
given Passwords[F] = passwords | |
val resources = database match | |
case pg: Config.Database.Postgres => | |
Postgres.build[F](pg).flatMap(mkPostgresF[F](storage)) | |
case Config.Database.InMemory => | |
Resource.eval(inMemory[F](storage)) | |
resources | |
def precreate[F[_]: Files: Monad](paths: List[Path]): F[Unit] = | |
paths.traverse_ { path => Files[F].exists(path).ifM(Monad[F].unit, Files[F].createDirectories(path)) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.foldables.ratio.services.clients | |
import cats.Monad | |
import cats.implicits.* | |
import io.circe.* | |
import io.circe.syntax.* | |
import io.foldables.ratio.bus.Bus | |
import io.foldables.ratio.common.data.{Collection, Filter} | |
import io.foldables.ratio.services.auth.model.User | |
import io.foldables.ratio.services.clients.Codecs.given | |
import io.foldables.ratio.services.clients.model.{Client, Clients, events} | |
import io.foldables.ratio.services.users.Codecs.given | |
object Service: | |
// ToDo: get | |
// def getById[F[_]: Clients]( | |
// requester: User.Display, | |
// id: Client.Id | |
// ): F[Option[Client.WithMeta]] = | |
// Clients[F].get(id) | |
// def list[F[_]: Clients]( | |
// requester: User.Display, | |
// filter: Filter | |
// ): F[Collection[Client.WithMeta]] = | |
// Clients[F].list(filter) | |
// def update[F[_]: Clients: Bus: Monad]( | |
// requester: User.Display, | |
// id: Client.Id, | |
// client: Client.UpdatedBase | |
// ): F[Option[Client.WithMeta]] = | |
// Clients[F].update(id, client.toUpdated(requester.userMeta.id)).flatMap { | |
// case Some(client) => | |
// Bus[F] | |
// .publish(events.updated(requester.userMeta.id, client)) | |
// .as(Some(client)) | |
// case None => | |
// Monad[F].pure(None) | |
// } | |
def add[F[_]: Clients: Bus: Monad]( | |
requester: User.Display, | |
client: Client.Base | |
): F[Client.WithMeta] = | |
Clients[F].add(client.toNew(requester.userMeta.id)).flatMap { client => | |
Bus[F].publish(events.added(requester.userMeta.id, client)).as(client) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.collection.mutable.ArrayBuffer | |
trait Pet: | |
val name: String | |
class Cat(val name: String) extends Pet | |
class Dog(val name: String) extends Pet | |
val dog = Dog("Harry") | |
val cat = Cat("Sally") | |
val animals = ArrayBuffer.empty[Pet] | |
animals.append(dog) | |
animals.append(cat) | |
animals.foreach(pet => println(pet.name)) // Prints Harry Sally |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment