Проектные расширения#

Точка расширения#

Аббревиатуры:

  • Ept - Точка расширения

  • Ext - Расширения

Точка расширения (ExtensionPoint) – специфический пакет, методы которого объявлены таким образом, что к ним можно подключить дополнительный исполняемый код из других пакетов (расширений/Extension). Код расширений будет выполнен при вызове метода точки расширения.

class Btk_ExampleEpt extends ExtensionPoint {  
  val doSomeThing = declFunc("doSomeThing") {  
    (sf: SuperFunc[Int], a: Int, b: Int) =>  
      val superVal = sf().getOrElse(0)  
      a + b + superVal  
  }  
}  
object Btk_ExampleEpt extends PkgFactory[Btk_ExampleEpt]

Пример представляет объявление «Точки расширения» с методом doSomeThing

Правила наименования для точек расширения, которые используются для дополнения функционала сеттеров, вставки. удаления – Xxx_Ept, где Xxx – имя модуля, для которого точка расширения создается (например Bs_Ept, Btk_Ept).

Расширение (Extension) – специфический пакет, методы которого подключаются к методам точки расширения, и выполняются при вызове соответствующего метода точки расширения.

К методу точки расширения может быть подключено произвольное число методов расширений.

При первом обращении к классу точки расширения, производится поиск и сканирование файлов META-INF/extensions.xml. Расширения, у которых свойство targetEpt совпадает с именем точки расширения, подключаются к точке расширения.

class Gs3_Btk_ExampleExt extends Extension {  
  
subFunc("doSomeThing") { 
  (sf: SuperFunc[Int], a: Int, b: Int) =>  
  val superVal = sf(a - 2, b).getOrElse(0)  
  superVal + 2  
}  
}

Пример представляет объявление метода расширения. На вход метод получает ссылку на метод расширения, находящийся ниже в очереди вызовов.

Внимание

Если не вызвать sf(), методы расширений, находящиеся ниже в очереди не будут вызваны.

Правила наименования для расширений, которые используются для дополнения функционала других модулей для сеттеров, вставки. удаления – Xxx_YyyExt, где Xxx – имя модуля расширения, Yyy – имя модуля, в котором находится точка расширения (например Mct_BsExt ).

Конфигурирование расширений#

Конфигурация расширений находится в файле META-INF/extensions.xml.

Структура фала:

<?xml version="1.0" encoding="UTF-8"?>  
<exts>  
	<ext class="ru.bitec.app.btk.ext.Btk_ExceptionHandlerExt"
	     targetEpt="ExceptionHandlerEpt"/>  
</exts>

Порядок вызовов#

По умолчанию, метод точки расширения получает порядковый номер «0», а все методы расширений – номер «10». В результате, в первую очередь буду вызваны все методы расширений, в произвольном порядке, в конце будет вызван код точки расширения.

Для изменения порядка вызовов необходимо задать порядковые номера методам расширений.

subFunc("doSomeThing", 1) { 
  (sf: SuperFunc[Int], a: Int, b: Int) => 

}

Проектное перекрытие кода Api, Avi, Lib, Pkg#

Для перекрытия кода Api, Avi, Lib, Pkg необходимо:

  1. Создать свой класс-наследник от перекрываемого класса.

  2. Зарегистрировать перекрытие класса в файле META-INF/overrides.xml

Пример перекрытия Avi-класса#

Код исходного Avi-класса Gs3_RootTestAvi.scala#

object Gs3_RootTestAvi extends Gs3_RootTestAvi  
  
@AvmFile(name = "Gs3_RootTest.avm.xml")  
class Gs3_RootTestAvi extends Gs3_RootTestDvi {  
  override def list(): List = {  
    new List {  
    override def meta = this  
    }  
  }  
}

Код перекрытия Avi-класса Gs3_RootTestOverrideAvi.scala#

object Gs3_RootTestOverrideAvi extends Gs3_RootTestOverrideAvi  
  
@AvmFile(name = "Gs3_RootTestOverride.avm.xml")  
class Gs3_RootTestOverrideAvi extends Gs3_RootTestAvi {  
  override def list(): List = {  
    new List {  
    override def meta = this  
    }  
  }  
  trait List extends super.List {

  }  
}

Аннотация @AvmFile может быть не указана, в этом случае будет использоваться аннотация из класса-предка.

Текст META-INF/overrides.xml#

<?xml version="1.0" encoding="UTF-8"?>  
<overrides>  
  <replace-with
      class="ru.bitec.app.gs3.test.Gs3_RootTestOverrideAvi">  
    <when-type-is class="ru.bitec.app.gs3.test.Gs3_RootTestAvi"/>  
  </replace-with>  
</overrides>

Пример перекрытия Api#

Код перекрытия#

class Gs3_RootTestOverrideApi extends Gs3_RootTestApi {  
  
}  
  
object Gs3_RootTestOverrideApi extends ApiFactory[
  java.lang.Long,
  Gs3_RootTestAro, 
  Gs3_RootTestOverrideApi
]

Текст META-INF/overrides.xml#

<replace-with
    class="ru.bitec.app.gs3.test.collections.Gs3_RootTestOverrideApi">  
  <when-type-is
  class="ru.bitec.app.gs3.test.collections.Gs3_RootTestApi"/>  
</replace-with>

Пример перекрытия Pkg#

Код перекрытия#

class Btk_OverridePkg extends Btk_Pkg {  
  
}  
  
object Btk_OverridePkg extends PkgFactory[Btk_OverridePkg]

Текст META-INF/overrides.xml#

<replace-with class="ru.bitec.app.gs3.Btk_OverridePkg">  
  <when-type-is class="ru.bitec.app.btk.Btk_Pkg"/>  
</replace-with>

Пример перекрытия Lib#

Код перекрытия#

class Btk_OverrideLib extends Btk_Lib {  
  
}  
object Btk_OverrideLib extends AviLibFactory[Btk_Lib]

Текст META-INF/overrides.xml#

<replace-with class="ru.bitec.app.gs3.Btk_OverrideLib">  
  <when-type-is class="ru.bitec.app.btk.Btk_Lib"/>  
</replace-with>

Запрет перекрытия#

Для запрета проектного перекрытия кода Avi-класса, необходимо отметить класс аннотацией @Final.

@Final
class Gs3_RootTestAvi extends Gs3_RootTestDvi {}  

Для запрета проектного перекрытия кода Api,Lib,Pkg классов, необходимо отметить класс аннотацией @Final или ключевым словом final.

@Final
class Gs3_RootTestApi extends Gs3_RootTestDpi[
  Gs3_RootTestAro,
  Gs3_RootTestApi, 
  Gs3_RootTestAta
] {}

или

final class Gs3_RootTestApi extends
Gs3_RootTestDpi[
  Gs3_RootTestAro, 
  Gs3_RootTestApi, 
  Gs3_RootTestAta
] {}

Jexl расширения методов#

С помощью расширений методов имеется возможность доработки и модификации стандартных серверных методов и операций пользовательских интерфейсов из приложения Настройки системы. Расширение представляет из себя jexl-скрипт, вызываемый перед или после выполнения стандартного метода/операции.

Расширения Api-методов#

Добавление точек расширения в Api предусмотрено только в следующие методы:

  • Сеттеры атрибутовDpi-сеттеры всех атрибутов выбранного класса;

  • AfterEditDpi-метод, вызываемый на окончание редактирования объекта класса, например при редактировании строки и перемещении курсора на другую строку;

  • BeforeEditDpi-метод, вызываемый в начале редактирования объекта класса;

  • FlushObject – метод записи (сохранения) объекта в базу данных;

  • DeleteDpi-метод удаления объекта класса из базы данных;

  • InsertDpi-метод создания нового объекта класса.

Примечание

Все расширения вызываются из „Dpi“. Т.е. срабатывание перед сеттером - это срабатывание перед вызовом Dpi-сеттера.

Примечание

Для методов «FlushObject» и «BeforeEdit» можно добавлять только точки расширения «До».

Выполнение этих точек будет производиться в контексте сессии, без использования интерактивной бизнес логики (т.е. нельзя использовать методы открытия всплывающих окон и диалогов, открытия каких либо интерфейсов системы).

Для подключения точек расширения Api-методов перейдите: Настройки системы - Обозреватель проекта. Выбрав Api нужного класса, перейдите на закладку Точки расширения. На закладке Расширения до или Расширения после создайти запись нового расширения и пропишите ему исполняемый скрипт.

Пример скрипта с использованием вызова процедур из Bts:

Bts_ProcedureApi.execByMnemoCode("ProcedureCode", {"argName1": value1, "argName2": value2, ...})

Пример выброса ошибки, если у объекта не заполнен атрибут sDescription:

var aro = rop.copyAro();
if (aro.sDescription() == null){
  raise("Не заполнено описание!")
}

Расширения Avi-методов#

Добавление точек расширения в Avi предусмотрено в сеттеры выборки, в ее операции или на события выборки, такие как beforeEdit, afterEdit и т.д.

Выполнение этих точек будет производиться в контексте выборки.

Для подключения точек расширения Avi-методов перейдите: Настройки системы - Обозреватель проекта. Выбрав нужную выборку, перейдите на закладку Редактор операций. На закладке Расширения до или Расширения после создайте запись нового расширения и пропишите ему исполняемый скрипт.

Пользовательские события#

Реализована возможность для Api добавлять собственные события, а так же для существующих стандартных событий добавлять новые типы срабатывания.

Добавление нового события#

Новое событие будет отображено в списке событий Api. Оно создается без типов срабатываний, типы срабатывание привязываются к событию отдельным методом.

Пример добавления нового события:

Btk_MethodExtensionApi().registerClassEvent(
  idvClass,
  "SomeUserEventName",
  "Новое пользовательское событие"
)

Пример удаления пользовательского события:

Btk_MethodExtensionApi().unregisterClassEvent(
  idvClass,
  "SomeUserEventName"
)

Добавление типа срабатывания к событию#

Стандартные события (сеттеры, вставка и тд.) имеют по умолчанию 2 типа срабатывания: До и После, а пользовательские события создаются без типа срабатывания. Реализована возможность добавления нового типа срабатывания к событию. Каждый тип срабатывания будет отображаться отдельной закладкой у события.

Создание нового типа срабатывания:

Btk_MethodExtensionTypeApi().register("SomeUserEventType", "На пересчет денормализации")

Создание связи между событием и типом срабатывания:

Btk_MethodExtensionApi().registerEventTypeToClassEvent(
  idvClass,
  "SomeUserEventName",
  Btk_MethodExtensionTypeApi().findByMnemoCode("SomeUserEventType")
)

Удаление связи между событием и типом срабатывания:

Btk_MethodExtensionApi().unregisterEventTypeFromClassEvent(
  idvClass,
  "SomeUserEventName",
  Btk_MethodExtensionTypeApi().findByMnemoCode("SomeUserEventType")
)

Вызов пользовательского события или пользовательского типа срабатывания из кода приложения#

Разработчик в коде сам размещает вызов срабатывания события в нужном месте.

Пример вызова:

Btk_MethodExtensionApi().runMethodExtensionByClass(idvClass, "SomeUserEventType", "SomeUserEventName", "valueStr" -> "abc", "valueNum" -> 1.nn)

Переданные в метод значения параметров будут доступны в Jexl-скриптах этого события.

Пример обращения к переменной из Jexl-скрипта:

println("Значение переменной valueStr: " + valueStr + " Значение переменной valueNum: " + valueNum.toString());