Skip to content

Instantly share code, notes, and snippets.

@DiggesT
Last active August 30, 2023 09:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save DiggesT/8d1d16fa08c5637b5a15b10d1976d82e to your computer and use it in GitHub Desktop.
Save DiggesT/8d1d16fa08c5637b5a15b10d1976d82e to your computer and use it in GitHub Desktop.
Scala code research
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)
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
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
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]
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)
}
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]
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))
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
}
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)
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))
)
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 неявно
}
}
//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
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
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)(_ + _)
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)
}
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 ()
}
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)) }
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)
}
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