Skip to content

Latest commit

 

History

History
243 lines (179 loc) · 4.99 KB

README.md

File metadata and controls

243 lines (179 loc) · 4.99 KB

interface-implements

nimble

There are two ways to achieve polymorphism in Nim. One is to create toInterface converter and another is dynamic dispatch.

toInterface converter

interface definition

type IRepository* = object
  exec*: proc(msg:string):string

implementation

type Repository* = object

proc exec(self:Repository, msg:string):string =
  return &"Repository {msg}"

converter toInterface*(self:Repository):IRepository =
  return IRepository(
    exec: proc(msg:string):string = self.exec(msg)
  )

dynamic dispatch

interface definition

type IRepository* = object of RootObj

method exec*(self:IRepository, msg:string):string {.base.} = raise newException(CatchableError, "error")

implementation

type Repository* = object of IRepository

method exec*(self:Repository, msg:string):string =
  return &"Repository {msg}"

install

nimble install interface_implements

implements

implements macro creates toInterface converter.

import interface_implements

implements Repository, IRepository:
  proc func1(self:Repository, msg:string):string =
    return "Repository1 " & msg

  proc func2(self:Repository, number:int):string =
    return "Repository2 " & $number

This is converted to bellow.

proc func1(self:Repository, msg:string):string =
  return "Repository " & msg

proc func2(self:Repository, number:int):string =
  return "Repository2 " & $number

converter toInterface*(self:Repository):IRepository =
  return IRepository(
    func1: proc(msg:string):string = self.func1(msg),
    func2: proc(number:int):string = self.func2(number)
  )

API

macro implements*(implName, interfaceName, procs:untyped):untyped

Example

repository_interface.nim

type IRepository* = object
  exec*: proc(msg:string):string

mock_repository.nim

import interface_implements
import ./repository_interface

type MockRepository = object

proc newMockRepository*():MockRepository =
  return MockRepository()

implements MockRepository, IRepository:
  proc exec(self:MockRepository, msg:string):string =
    return "MockRepository " & msg

repository.nim

import interface_implements
import ./repository_interface

type Repository = object

proc newRepository*():Repository =
  return Repository()

implements Repository, IRepository:
  proc exec(self:Repository, msg:string):string =
    return "Repository " & msg

usecase.nim

import ./repository_interface

type Usecase = object
  repository: IRepository

func newUsecase*(repository:IRepository):Usecase =
  return Usecase(repository:repository)

proc exec*(self:Usecase, msg:string):string =
  return self.repository.exec(msg)

presentation layer

block:
  let repository = newMockRepository()
  let usecase = newUsecase(repository)
  assert "MockRepository mock" == usecase.exec("mock")

block:
  let repository = newRepository()
  let usecase = newUsecase(repository)
  assert "Repository exec" == usecase.exec("exec")

interfaceDef

interfaceDef macro creates method of fields of Object. This is only available in Nim 2.0.0 and above.

import interface_implements

interfaceDef:
  type IRepository* = object of RootObj
    exec: proc(self:IRepository, msg:string):string

This is converted to bellow.

type IRepository* = object of RootObj

method exec*(self:IRepository, msg:string):string {.base.} = raise newException(CatchableError, "Implementation exec of IRepository is not found")

API

macro interfaceDefs*(body:untyped):untyped

Example

repository_interface.nim

interfaceDef:
  type IRepository* = object of RootObj
    exec*: proc(msg:string):string

mock_repository.nim

import interface_implements

type MockRepository = object of IRepository

proc newMockRepository*():MockRepository =
  return MockRepository()

method exec*(self:MockRepository, msg:string):string =
  return "MockRepository " & msg

repository.nim

import interface_implements

type Repository = object of IRepository

proc newRepository*():Repository =
  return Repository()

method exec*(self:Repository, msg:string):string =
  return "Repository " & msg

usecase.nim

import ./repository_interface

type Usecase = object
  repository: IRepository

func newUsecase*(repository:IRepository):Usecase =
  return Usecase(repository:repository)

proc exec*(self:Usecase, msg:string):string =
  return self.repository.exec(msg)

presentation layer

block:
  let repository = newMockRepository()
  let usecase = newUsecase(repository)
  assert "MockRepository mock" == usecase.exec("mock")

block:
  let repository = newRepository()
  let usecase = newUsecase(repository)
  assert "Repository exec" == usecase.exec("exec")

Both pattern achieve structure as bellow.