суббота, 31 января 2015 г.

Draft. Mind Stream

Это в некотором смысле аналог WhiteStarUML и Rational Rose.

https://bitbucket.org/ingword/mindstream/src/d0af8fe402ae0267f8b709a3a30b0305127a70a7/?at=Developing

MindStream. Как мы пишем ПО под FireMonkey
MindStream. Как мы пишем ПО под FireMonkey. Часть 2
MindStream. Как мы пишем ПО под FireMonkey. Часть 3. DUnit + FireMonkey

"MindStream представляет собой кросс-платформенное ПО, для управления предприятием. Удобный графический интерфейс позволяет с легкостью отслеживать работу бизнес-процессов компании на любом устройстве, и  в любом уровне детализации.


Основой инновации продукта, является, как ни странно - природа человека. А именно свойство упрощать и шаблонизировать основной вид деятельности - “работу”. В любом виде деятельности (одного типа, ремонт электродвигателей например, или любой другой), если “приглядеться” можно увидеть однообразные процессы.
Сама идея работы с “шаблонами” взята из программирования. После определения “шаблонов” в программировании, стало возможным лишь определить “шаблон”(описать шаблон на особом шаблонном языке), и получалась возможность изменить работу “шаблона” во всей программе.

Наш проект предполагает, использовать эту практику для удобной работы бизнес-систем.

Реализовано (для систем Windows, Android, iOS):
  • Построение графических примитивов
  • Построение архитектуры приложения
  • Создан инструмент для автоматизированного тестирования приложений для всей платформы FireMonkey

Следующие шаги:
  • Создание механизма сохранения и восстановления данных из файлов. (Сериализация и десериализация)
  • Аналитика оформления бизнес-процесов принятых во всем мире. Может имеет смысл привлечь менеджеров которые имеют практику построения, и выполнения на предприятиях.
  • Создание базы графических примитивов построенных на анализе предыдущего шага.
  • Разработка языка описывающего шаблоны.

Далее работа делится на 2 проекта:
  1. Front-End то что показывается пользователю, и который собственно и работает с продуктом.
  2. Back-End. Внесение шаблонов - уникальных, и общих в базу предприятия, или общую.
  3. Процесс шага 2 сделать удобным “графически”, то есть аналитик вносящий “шаблон” мог сделать это и на своем планшете, по дороге домой на метро, или на заднем сиденье Rolls&Roys."

пятница, 30 января 2015 г.

Offtopic. Анекдот

Поймал как–то мужик золотую рыбку.
Рыбка, разумеется, оказалась говорящей:
– Отпусти меня, мужик, я тебе три желания исполню!
Мужик почесал репу и отвечает:
– Итак, слушай сюда. Желание первое: рыбка не может причинить вред человеку или своим бездействием допустить, чтобы человеку был причинён вред... 

Проблемы Windows 8. Знаки вопроса вместо русского текста в не UTF формате

Собственно проблема уже довольно полно изложена в заголовке. Весь текст который не UTF, а такого как оказалось много, да и Delphi, оказывается по умалчанию сохраняет в ASCSII. wtf ? 
Проблема решается довольно просто, но найти не просто. Нарисовал гифку. Размер 3,5 мб
Прошу под кат.

четверг, 22 января 2015 г.

Коротко. Вопрос. Что делает код и что в нём не так?


    typedef std::list<TagFilterElem> TagChildrenForPrune;
    
    void pruneEmpty(TagChildrenForPrune & aTagChildren, TagChildrenForPrune::iterator & aLastContainer)
    {
        if (aLastContainer != aTagChildren.end()) {
            for (auto vIt = aLastContainer->rTagValue.rChildren.rbegin(); vIt != aLastContainer->rTagValue.rChildren.rend(); /*vIt++*/) {
                if (!vIt->is_type_of(evd::id_TextPara))
                    break;
                if (DocumentFilter::ChildHasText(*vIt))
                    break;
                aLastContainer->rTagValue.rChildren.erase(--aLastContainer->rTagValue.rChildren.end());
                vIt = aLastContainer->rTagValue.rChildren.rbegin();
            }
            if (aLastContainer->rTagValue.rChildren.size() == 0) {
                aTagChildren.erase(aLastContainer);
            }
        }
        aLastContainer = aTagChildren.end();
    }
    
    void PlainBlock (BlockStack::reference aChild)
    {
        if (aChild.is_type_of(evd::id_ContentsElement) || aChild.is_type_of(evd::id_Table)) {
            for (auto & vChild : aChild.rTagValue.rChildren) {
                PlainBlock(vChild);
            }
            if (IsMixed(aChild)) {
//                TagFilterElem::ElemData::TagChildren vTagChildren;
                TagChildrenForPrune vTagChildren;
                auto vLastContainer = vTagChildren.end();
                auto vFlagsIt = aChild.rTagValue.rTags.find(evd::ti_Flags);
                for (auto & vChild : aChild.rTagValue.rChildren) {
                    if (vChild.is_type_of(evd::id_ContentsElement)) {
                        pruneEmpty(vTagChildren, vLastContainer);
                        vTagChildren.push_back(vChild);
                    }
                    else
//                    if (vChild.is_leaf_para())
                    {
                        if (vLastContainer == vTagChildren.end()) {
                            TagFilterElem vContainer (evd::id_Block);
                            if (vFlagsIt != aChild.rTagValue.rTags.end()) {
                                vContainer.rTagValue.rTags.insert(*vFlagsIt);
                                long vFlags = vFlagsIt->second.IntValue() & ~(long)MASK_NEW_PAGE;
                                if (vFlags)
                                    vFlagsIt->second = vFlags;
                                else
                                    aChild.rTagValue.rTags.erase(vFlagsIt);
                                vFlagsIt = aChild.rTagValue.rTags.end();
                            }
                            vTagChildren.push_back(vContainer);
                            vLastContainer = --vTagChildren.end();
                        }
                        vLastContainer->rTagValue.rChildren.push_back(vChild);
                    }
//                    else
//                        ASSERT_NO_MSG(false);
                }
                pruneEmpty(vTagChildren, vLastContainer);
                aChild.rTagValue.rChildren.clear();
                aChild.rTagValue.rChildren.insert(aChild.rTagValue.rChildren.begin(), vTagChildren.begin(), vTagChildren.end());
                ASSERT_NO_MSG(!IsMixed(aChild));
            }
        }
    }
    

Коротко. MindStream. DUnit не освобождает свои тесты. НИКОГДА

Почему не освобождает? Не понял ещё.

Факт тот, что TAbstractTest.Destroy - НЕ ВЫЗЫВАЕТСЯ. Никогда.

Мне на работе - это не критично было. У меня тесты не захватывали объекты.

А вот в MindStream - захватывают. Например - TmsShapeTestPrim. Он в поле f_Context захватывает rShapeClass : ImsShapeClass.

Нашёл утечки при помощи - http://programmingmindstream.blogspot.ru/2014/12/4.html

В чём проблемы? Буду искать. Потом -  отпишусь.

Пока приведу лишь ссылку на коммит - https://bitbucket.org/ingword/mindstream/commits/fad86d6f51bc2d35b13edbd2f23fe2ff70e76232

Ну и ещё коммит, который "вставляет костыль" - https://bitbucket.org/ingword/mindstream/commits/d38d712cc8fcd8d3fc4356716bf172cb82790ea4

(+)
https://bitbucket.org/ingword/mindstream/commits/1266ae1b6cf9ac62e1bffeb2674efce5b1765583

Коротко. "Вкусности" Delphi XE7. На примере TPointF и TPolygon

https://bitbucket.org/ingword/mindstream/src/1db98fbc5815c7da5a4f54d922add1d78fdf814a/AbstractShapes/msSVGShape.pas?at=B43

Смотрим код:

function TmsSVGShape.GetPolygon: TPolygon;
var
 l_PolygonSVG : TPolygon;
 l_SVG_String: string;
 l_PD: TPathData;
 {l_Point: TPointF;}
 l_R : TRectF;
 l_P : TPointF;
 l_Mid : TPointF;
 l_StartPoint : TPointF;
begin
 l_PD := TPathData.Create;
 try
  l_SVG_String := GetPolygonSVG;
  l_PD.Data := l_SVG_String;
  {l_Point:= }l_PD.FlattenToPolygon(l_PolygonSVG);
  l_R := PolygonBounds(l_PolygonSVG);

  l_StartPoint := StartPoint;
  Result := nil;
  for l_P in l_PolygonSVG do
  begin
   l_Mid := (l_R.BottomRight + l_R.TopLeft) / 2;
   Result := Result + [(l_P - l_Mid) + l_StartPoint];
  end;//for l_P in l_PolygonSVG
 finally
  FreeAndNil(l_PD);
 end;//try..finally
end;

Вроде код как код.

Но! Что тут "необычного"?

Во-первых строчка:

l_Mid := (l_R.BottomRight + l_R.TopLeft) / 2;

Тут складываются две ТОЧКИ (TPointF) и результат делится пополам. И ОПЯТЬ получается ТОЧКА (таким образом мы получаем точку, которая содержит перенос начала координат в середину начального прямоугольника).

Раньше такое делать было нельзя.

Теперь - МОЖНО. Приятно :-)

Во-вторых строчка:

Result := Result + [(l_P - l_Mid) + l_StartPoint];

В этой строчке - тоже есть - во-первых:

Создаётся динамический массив из одного элемента - [(l_P - l_Mid) + l_StartPoint]. В синтаксисе похожем на "множество" (set of).

И во-вторых:

Складываются ДВА ДИНАМИЧЕСКИХ массива:
Result := Result + ... Через операцию + (ПЛЮС).

Такого - ТОЖЕ раньше не было. И тоже - приятно:-)

Т.е. избавляемся от множества написания вызовов GetLength/SetLength/Item[i].

Т.е. динамические массивы всё больше и больше становятся похожими на строки.

"Синтаксический сахар"? Конечно! Но! Всё равно - приятно.

Коротко. MindStream. TmsSVGShape. Только код

https://bitbucket.org/ingword/mindstream/src/0c810398728cabf912dd5863bc87ab432259b70d/AbstractShapes/msSVGShape.pas?at=MS-45_SVGShape

unit msSVGShape;

interface

uses
 msInterfaces,
 msPolygonShape,
 System.Types,
 System.Math.Vectors,
 FMX.Graphics,
 System.SysUtils
 ;

type
 TmsSVGShape = class abstract(TmsPolygonShape)
 // - базовый класс для реализации SVG объектов
 protected
  function GetPolygon: TPolygon; override; final;
  procedure DoDrawTo(const aCtx: TmsDrawContext); override;
  function GetPolygonSVG: String; virtual; abstract;
  procedure TransformDrawOptionsContext(var theCtx: TmsDrawOptionsContext); override;
 end;//TmsSVGShape

implementation

uses
 System.UITypes
 ;

// TmsSVGShape

function TmsSVGShape.GetPolygon: TPolygon;
var
 l_PolygonSVG : TPolygon;
 l_SVG_String: string;
 l_PD: TPathData;
 l_Point: TPointF;
begin
 l_PD := TPathData.Create;
 try
  l_SVG_String := GetPolygonSVG;
  l_PD.Data := l_SVG_String;
  l_Point:= l_PD.FlattenToPolygon(l_PolygonSVG);
  Result := l_PolygonSVG;
 finally
  FreeAndNil(l_PD);
 end;//try..finally
end;

procedure TmsSVGShape.DoDrawTo(const aCtx: TmsDrawContext);
var
 l_OriginalMatrix: TMatrix;
begin
 l_OriginalMatrix := aCtx.rCanvas.Matrix;
 try
  inherited;
 finally
  aCtx.rCanvas.SetMatrix(l_OriginalMatrix);
  // - восстанавливаем ОРИГИНАЛЬНУЮ матрицу
 end;//try..finally
end;

procedure TmsSVGShape.TransformDrawOptionsContext(
  var theCtx: TmsDrawOptionsContext);
begin
  inherited;
 theCtx.rFillColor := TAlphaColorRec.Azure;
end;

end.

https://bitbucket.org/ingword/mindstream/src/0c810398728cabf912dd5863bc87ab432259b70d/ConcreteShapes/SVG/msFolder.pas?at=MS-45_SVGShape

unit msFolder;

interface

uses
 msSVGShape,
 System.Types,
 System.Math.Vectors,
 FMX.Graphics,
 System.SysUtils,
 msInterfaces,
 System.UITypes
 ;

type
 TmsFolder = class(TmsSVGShape)
 // - "папка"
 protected
  function GetPolygonSVG: String; override;
 end;//TmsFolder

implementation

// TmsFolder

function TmsFolder.GetPolygonSVG: String;
begin
  Result := 'M 40,40, L 100,40, L 100,80, L 40,80, L 40,40' +
  // begin UHO
  'L 40,20' +
  'L 60, 20' +
  'L 60, 40';
end;

end.

Ссылка. Что-то издали похожее на монады

http://habrahabr.ru/post/247997/

(+)
http://habrahabr.ru/post/183150/#comment_6362554 ("Как я уложил монады у себя в голове:

Монада — это виртуальная машина которая исполняет ваш код. Не зря ведь говорится, что код исполняется «в монаде». Она может быть написана самостоятельно или взята из библиотеки.
Признаком того, что сейчас у нас работает именно эта монада, является тот самый контейнерный тип данных, который ей соответствует. Поэтому вызовы >>= и Return (благодаря полиморфизму) будут использовать реализацию из неё.
Получив вызов >>=, монада определяет, каким образом исполнять следующую команду (функцию), т.е. работает в точности как процессор в компьютере.
Кроме того, в этом контейнере хранится служебная информация для самой виртуальной машины (вроде состояния в монаде State).
Проверка типов самого языка контролирует корректный вход и выход из монады. Вот и всё.")

четверг, 15 января 2015 г.

вторник, 13 января 2015 г.

MindStream. Коротко. Скрытие методов. Только код

Репозитарий - https://bitbucket.org/ingword/mindstream/src/0b84d9ed2f7caaf0f038bd2dda55bbc7bb269520/AbstractShapes/msSpecialArrow.pas?at=ShapesGroup

И ещё - https://bitbucket.org/ingword/mindstream/commits/ac3fa2f9459760b4883aebb8aad37ad07671493b?at=ShapesGroup

Код:

unit msSpecialArrow;

interface

uses
 System.Types,
 msInterfaces,
 msLineWithArrow
 ;

type
 TmsSpecialArrow = class abstract(TmsLineWithArrow)
 protected
  class function InitialLength: Integer;
  class function CreateCompletedInternal(const aStartPoint: TPointF; const aFinishPoint: TPointF): ImsShape;
 public
  class procedure CreateCompleted;
 end;//TmsSpecialArrow

implementation

// TmsSpecialArrow

class function TmsSpecialArrow.CreateCompletedInternal(const aStartPoint: TPointF; const aFinishPoint: TPointF): ImsShape;
begin
 Result := inherited CreateCompleted(aStartPoint, aFinishPoint);
end;

class procedure TmsSpecialArrow.CreateCompleted;
begin
 Assert(false, 'Используйте фабрику Create');
end;

class function TmsSpecialArrow.InitialLength: Integer;
begin
 Result := 50;
end;

end.

MindStream. Коротко. Статистика использования объектов

По мотивам - Коротко. Контроль за созданием/освобождением объектов. Только код. №4

А ещё мы сделали вывод в лог статистики использованных объектов при работе приложения.

Вот примерно так:

MindStream.exe.objects.log

Неосвобождено объектов: 0
TmsPaletteShape Неосвобождено: 0 Максимально распределено: 5
TmsPaletteShapeCreator Неосвобождено: 0 Максимально распределено: 1
TmsUpArrow Неосвобождено: 0 Максимально распределено: 1
TmsDashDotLine Неосвобождено: 0 Максимально распределено: 164
TmsLine Неосвобождено: 0 Максимально распределено: 278
TmsRectangle Неосвобождено: 0 Максимально распределено: 144
TmsCircle Неосвобождено: 0 Максимально распределено: 908
TmsLineWithArrow Неосвобождено: 0 Максимально распределено: 309
TmsDiagrammsController Неосвобождено: 0 Максимально распределено: 1
TmsStringList Неосвобождено: 0 Максимально распределено: 3
TmsCompletedShapeCreator Неосвобождено: 0 Максимально распределено: 2
TmsRoundedRectangle Неосвобождено: 0 Максимально распределено: 434
TmsTriangleDirectionRight Неосвобождено: 0 Максимально распределено: 5
TmsGreenCircle Неосвобождено: 0 Максимально распределено: 850
TmsSmallTriangle Неосвобождено: 0 Максимально распределено: 761
TmsShapeCreator Неосвобождено: 0 Максимально распределено: 1
TmsDashLine Неосвобождено: 0 Максимально распределено: 868
TmsGreenRectangle Неосвобождено: 0 Максимально распределено: 759
TmsDiagramm Неосвобождено: 0 Максимально распределено: 910
TmsDownArrow Неосвобождено: 0 Максимально распределено: 1
TmsDotLine Неосвобождено: 0 Максимально распределено: 274
TmsDiagramms Неосвобождено: 0 Максимально распределено: 3
TmsDiagrammsHolder Неосвобождено: 0 Максимально распределено: 18
TmsPointCircle Неосвобождено: 0 Максимально распределено: 717
TmsUseCaseLikeEllipse Неосвобождено: 0 Максимально распределено: 397
TmsBlackTriangle Неосвобождено: 0 Максимально распределено: 43
TmsRedRectangle Неосвобождено: 0 Максимально распределено: 139
TmsMoverIcon Неосвобождено: 0 Максимально распределено: 220
TmsTriangle Неосвобождено: 0 Максимально распределено: 437

По мне - это крайне полезно.

Ключевой код вот:

class destructor TmsObjectsWatcher.Destroy;
begin
 if (f_ObjectsCreated <> nil) then
  if (f_ObjectsCreated.Count > 0) then
  begin
   // Далее выводим статистику неосвобождённых объектов в лог:
   TmsLog.Log(ParamStr(0) + '.objects.log',
    procedure (aLog: TmsLog)
    var
     l_Item : TPair<String, TmsClassInstanceCount>;
    begin
     aLog.ToLog('Неосвобождено объектов: ' + IntToStr(f_ObjectsCreatedCount));
     for l_Item in f_ObjectsCreated do
     begin
      aLog.ToLog(l_Item.Key + ' Неосвобождено: ' + IntToStr(l_Item.Value.rCount) + ' Максимально распределено: ' + IntToStr(l_Item.Value.rMaxCount));
     end;//for l_Key
    end
   );
  end;//f_ObjectsCreated.Count > 0
 FreeAndNil(f_ObjectsCreated);
 FreeAndNil(f_DefferedObjects);
 if (f_ObjectsCreatedCount > 0) then
  raise Exception.Create('Какие-то объекты не освобождены: ' + IntToStr(f_ObjectsCreatedCount));
end;

Сам код тут - https://bitbucket.org/ingword/mindstream/src/70742a15a94e67beecfba8f3dcc7bfaa61d4a38a/Core/msCoreObjects.pas?at=Developing

Ссылка. Модули для рисования математических формул

http://www.delphikingdom.com/asp/viewitem.asp?catalogid=718&mode=print

Хорошая библиотека. Сам ею уже лет 10-ть как пользуюсь.

И процентов на 30-40 её переписал. "Под свои нужды".

Теперь хочу портировать её на FMX.

Буду рад если кто-то захочет посотрудничать. Возможны различные варианты сотрудничества.

Текущее состояние "поправленных" исходников тут - https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/Expr/

Что такое предусловия (постусловия), зачем они нужны и как реализованы

Предистория.

Спустя некоторые время после реализации автоматизированного тестирования стали видны проблемы, связанные с процессом обработки результатов автопрогона тестов. Основной из них оказалось влияние непрошедних тестов на начальную (умолчательную) настройку системы. И хотя у нас существует постулат, что программно тесты не влияют друг на друга (например, результат работы функции "А" в тесте 1 не доступен тесту 2) оказалось не учтенной ситуация, когда после теста "залипают" неудалившиеся комментарии, фильтры, вкладки, окна и т.д.. Из-за этого в конце прогона тестов накапливается большое количество однотипных ложных срабатываний, когда имеет место быть не ошибка, а неверные начальные настройки.
Начальными умолчательными настройками в Немезисе принято считать следующие:
  • Главное окно развернуто на весь экран;
  • Главное окно активно (находится поверх всех окон);
  • Окно "Совет дня" скрыто;
  • Состояние настроек конфигурации равно умолчательному;
  • Оболочка запущена в режиме без вкладок;
  • В оболочке, на момент запуска теста, не должно быть пользовательских комментариев, фильтров и сохраненных объектов.
Помимо прочего перед каждым тестом выполняется следующая подготовка (словарь Common\TestProlog.script):
TRY
 "Закрыть все окна кроме текущего" 
EXCEPT
END 
StyleTable:Restore 
ОсновноеМеню 
"Сбрасываем вкладку БП в умолчательное состояние" 
"Выключаем машину времени" 
clipboard:clear
При написании и ручной отладки тестов подготовка оболочки полностью лежит на плечах тестировщика. Но, из-за большого количества однотипных ошибок в авторежиме, пришлось ввести проверку предусловий и постусловий. Предусловия - автоматичеки готовят систему к проведению теста (задают те умолчательные настройки, которые автор теста посчитал правильными). Постусловия - автоматически по окончанию теста проверяют, чтобы умолчательные настройки не изменились.
Пример предусловия: перед выполнением теста происходит проверка на наличие во вкладке "Мои документы" пользовательских объектов. Если они присутствуют, то происходит их удаление, если же удаление не сработало, результатом работы теста сразу становится ASSERT (до выполнения сценария теста дело не доходит).
Пример постусловия: в текущей реализации, постусловия проверяют, чтобы после теста главное окно не изменилось. Если же это произошло, тест тоже считается непрошедшим (с получением ASSERT). Следующий выполняющийся тест считает новое окно - своим главным окном, и значит, исключения не получаем, тест проходит успешно. Таким образом, удалось решить проблему с поиском тестов с "внезапно" меняющимся главным окном (ложные срабатывания из-за смены индекса главного окна, конечно, остались, но это вопрос к минимизации информации в эталонах). Другими, словами, не приходится искать 1 проблемный тест среди всех полутора тысяч, он сам сообщает о проблеме.

Реализация.

Для реализации системы проверки пред и постусловий были созданы словари:
  • Common\TestWithParameters.script (здесь находится описание всех возможных параметров, сгруппированных по видам использования).
  • Common\TestParamsDefine.script (объявление костант/переменных для поддержки параметров в скриптах).
  • Common\TestExecuteCommand.script (поддержка слова "Выполнить" и другие вспомогательные слова).
  • MainBasic.script (содержание всех слов, используемых в TestExecuteCommand.script, кроме нескольких подключенных словарей). По сути, MainBasic.script заменил HLTCLike.script.
Добавление новых параметров в словари, может создаваться как от описания названия параметра до добавления кода, который выполняет этот параметр, так и наоборот. Главное, о чем стоит помнить, это не пропускать ни одного словаря, чтобы не пропустить неприсвоенных и используемых переменных. Также следует хорошо продумать, как этот параметр впишется в уже имеющуюся картину проверок.
Для проверки любого параметра в тесте нужно использовать конструкцию вида:
Параметры: ( Описание_ параметра )
Выполнить (
 Сценарий_теста
)
Следует знать, что у параметров есть свои умолчательные значения:
PROCEDURE "Первоначальные параметры"
 "Проверить предусловия"
 "Не открывать документ"
 "Без проверок"
 "Импорт консультации не нужен"
 "Выполнить тест в основной конфигурации"
 "Не очищать строку Базового Поиска" 
 "Без изменения базы"
 "Проблем с фокусом после переключения баз не возникло"
 "Без установки размеров"
 "Не запоминать позицию мыши"
 "Не восстанавливать настройки до теста"
 "Используем умолчательные настройки" 
 "Не восстанавливать настройки после теста"
 "Не закрывать никакую вкладку"
 "Не проверять отсутствие комментариев"
 "Не проверять отсутствие пользовательских фильтров"
 "Не должно измениться основное окно"
 1 >>> g_WasInit
;
Они будут запущены в любом случае, при использовании слова Выполнить ( ... ).
Состояние любого параметра можно изменить, просто вписав противоположное значение в конструкцию Параметры: ( ... ).

Вывод.

Введенная система пред и постусловий позволила автоматически настраивать оболочку под определенные условия, и в автоматическом же режиме отслеживать их корректную настройку.
Упростился разбор результатов прохождения автотестов. Сократилось количество ложный срабатываний.

Планы и немного истории.

В планах расширить список постусловий (например, отслеживать СНР после теста).
В планах есть расширение количества доступных параметров.
В планах нет развития группы параметров, которые осуществляют проверку результаты работы теста. Такой параметр был введен, но признан неудачным опытом. Такая запись портит читаемость теста в целом (пропадает самодокументируемость кода).
В связи с вводом такой системы подготовки к тесту становится неактуальным большая часть кода словарей, ответственная за "восстановление" настроек до умолчательных. В планах обширный рефакторинг словарей и тестов, с целью исключить дублирование "настроек".
Можно рассмотреть ситуацию на примере восстановления позиции мыши:
Первоначально было:
VAR x_coord
VAR y_coord
"Запомнить позицию мыши" >>> y_coord >>> x_coord
TRY
 Код_теста
FINALLY
 x_coord y_coord mouse:SetCursorPosition 
END
Выделено в отдельную процедуру:
PROCEDURE "Запомнить позицию мыши и выполнить" IN aProc
 VAR x_coord
 VAR y_coord
 "Запомнить позицию мыши" >>> y_coord >>> x_coord
 TRY
  aProc DO
 FINALLY
  x_coord y_coord mouse:SetCursorPosition 
 END
;
Выделено в WORDWORKER (создаем самодокументируемый код):
VOID WORDWORKER "##Запомнить позицию мыши и выполнить"
 "Запомнить позицию мыши и выполнить {(@ ( WordToWork DO ) )}"
;
Этот же код реализован в виде условия, чтобы позиция мыши после теста восстановилась. Для его вызова достаточно выполнить:
Параметры: ( "Восстанавливать позицию мыши" )
Выполнить (
 Сценарий_теста
)
Т.е. становится неактуальным код "Запомнить позицию мыши и выполнить", "##Запомнить позицию мыши и выполнить". Рефакторинг подразумевает собой удаление дублирующегося кода, т.к. сейчас восстановление позиции мыши происходит дважды. Будем оптимизировать.

MindStream. Вспоминаем школьную тригонометрию. Только код. Что тут написано?

Только код.

По мотивам - MindStream. Как мы пишем ПО под FireMonkey. Часть 2

Репозитарий - https://bitbucket.org/ingword/mindstream/src/285eaac25daa915044ebda73d2313a6b5843f577/ConcreteShapes/msLineWithArrow.pas?at=Developing

unit msLineWithArrow;

interface

uses
 msShape,
 msLine,
 FMX.Graphics,
 System.Types,
 msInterfaces,
 System.Math.Vectors
 ;

type
 TmsLineWithArrow = class(TmsLine)
 protected
  function GetFinishPointForDraw: TPointF; override;
  procedure DoDrawTo(const aCtx: TmsDrawContext); override;
  function GetArrowAngleRotation : Single;
  function GetDrawBounds: TRectF; override;
 end;//TmsLineWithArrow

implementation

uses
 msSmallTriangle,
 SysUtils,
 System.Math,
 System.UITypes,
 FMX.Types
 ;


procedure TmsLineWithArrow.DoDrawTo(const aCtx: TmsDrawContext);
var
 l_Proxy : ImsShape;
 l_OriginalMatrix: TMatrix;
 l_Matrix: TMatrix;
 l_Angle : Single;
 l_CenterPoint,
 l_LineFinishPoint : TPointF;
begin
 inherited;
 if (StartPoint <> FinishPoint) then
 begin
  l_OriginalMatrix := aCtx.rCanvas.Matrix;
  try
   l_LineFinishPoint := TPointF.Create(FinishPoint.X - TmsSmallTriangle.InitialHeight / 2,
                                       FinishPoint.Y);
   l_Proxy := TmsSmallTriangle.Create(l_LineFinishPoint);
   try
    // in Radian
    l_Angle := GetArrowAngleRotation;

    l_CenterPoint := FinishPoint;

    l_Matrix := TMatrix.Identity;
    // - СНИМАЕМ оригинальную матрицу, точнее берём ЕДИНИЧНУЮ матрицу
    // https://ru.wikipedia.org/wiki/%D0%95%D0%B4%D0%B8%D0%BD%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D0%B0
    l_Matrix := l_Matrix * TMatrix.CreateTranslation(-l_CenterPoint.X,-l_CenterPoint.Y);

    // - задаём точку, вокруг которой вертим
    l_Matrix := l_Matrix * TMatrix.CreateRotation(l_Angle);

    // - задаём угол поворота
    l_Matrix := l_Matrix * TMatrix.CreateTranslation(l_CenterPoint.X,l_CenterPoint.Y);

    // - задаём начало координат
    l_Matrix := l_Matrix * l_OriginalMatrix;

    // - ПРИМЕНЯЕМ оригинальную матрицу
    // Иначе например ОРИГИНАЛЬНЫЙ параллельный перенос - не будет работать.
    // https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%BE%D1%81

    aCtx.rCanvas.SetMatrix(l_Matrix);
    // - применяем нашу "комплексную" матрицу

    l_Proxy.DrawTo(aCtx);
    // - отрисовываем примитив с учётом матрицы преобразований
   finally
    l_Proxy := nil;
   end;//try..finally
  finally
    aCtx.rCanvas.SetMatrix(l_OriginalMatrix);
  // - восстанавливаем ОРИГИНАЛЬНУЮ матрицу
  end;//try..finally
 end;//(StartPoint <> FinishPoint)
end;

function TmsLineWithArrow.GetArrowAngleRotation: single;
var
 l_ALength, l_CLength,
 l_AlphaAngle,
 l_X, l_Y, l_RotationAngle : Single;
 l_PointC : TPointF;
 l_Invert : SmallInt;
begin
 // Формула расчета растояний между двумя точками
 l_X := (FinishPoint.X - StartPoint.X) * (FinishPoint.X - StartPoint.X);
 l_Y := (FinishPoint.Y - StartPoint.Y) * (FinishPoint.Y - StartPoint.Y);

 l_CLength := sqrt( l_X + l_Y);

 l_PointC := TPointF.Create(FinishPoint.X, StartPoint.Y);

 // Формула расчета растояний между двумя точками
 l_X := (l_PointC.X - StartPoint.X) * (l_PointC.X - StartPoint.X);
 l_Y := (l_PointC.Y - StartPoint.Y) * (l_PointC.Y - StartPoint.Y);

 l_ALength := sqrt( l_X + l_Y);

 // In Radian
 l_AlphaAngle := ArcSin(l_ALength / l_CLength);

 l_Invert := 1;

 if (FinishPoint.X > StartPoint.X) then
 begin
  l_RotationAngle := Pi / 2 * 3;
  if FinishPoint.Y > StartPoint.Y then
   l_Invert := -1;
 end//FinishPoint.X > StartPoint.X
 else
 begin
  l_RotationAngle := Pi / 2;
  if FinishPoint.Y < StartPoint.Y then
   l_Invert := -1;
 end;//FinishPoint.X > StartPoint.X

 Result := l_Invert * (l_AlphaAngle + l_RotationAngle);
end;

function TmsLineWithArrow.GetDrawBounds: TRectF;
begin
 Result := inherited GetDrawBounds;
 if SameValue(Result.Left, Result.Right) then
 begin
  Result.Right := Result.Left + TmsSmallTriangle.InitialHeight;
  Result.Left := Result.Left - TmsSmallTriangle.InitialHeight;
 end;//SameValue(Result.Left, Result.Right)
 if SameValue(Result.Top, Result.Bottom) then
 begin
  Result.Bottom := Result.Top + TmsSmallTriangle.InitialHeight;
  Result.Top := Result.Top - TmsSmallTriangle.InitialHeight;
 end;//SameValue(Result.Top, Result.Bottom)
end;

function TmsLineWithArrow.GetFinishPointForDraw: TPointF;
var
 l_Angle : Single;
begin
 l_Angle := GetArrowAngleRotation;
 Result := TPointF.Create(FinishPoint.X - TmsSmallTriangle.InitialHeight * Cos(l_Angle),
                          FinishPoint.Y - TmsSmallTriangle.InitialHeight * Sin(l_Angle));
end;

end.

Что тут написано?

function TmsLineWithArrow.GetFinishPointForDraw: TPointF;
var
 l_Angle : Single;
begin
 l_Angle := GetArrowAngleRotation;
 Result := TPointF.Create(FinishPoint.X - TmsSmallTriangle.InitialHeight * Cos(l_Angle),
                          FinishPoint.Y - TmsSmallTriangle.InitialHeight * Sin(l_Angle));
end;

Что такое "единичная окружность"?

Что такое "проекция отрезка на оси"?

Что такое "граничные условия"?

Можно ли сделать проще?

Ссылка. Философия программирования 2 — Миф и язык

http://habrahabr.ru/post/247363/

Цитата:

"В прошлой своей статье я написал, что в России нет опенсорса. Как-то основатель Гитхаба Том Престон-Вернер, его жена, кстати, специалист по истории культуры, исправил баг в одной из ранних версий Руби, ну исправил, что такого, а то, что он весь процесс поиска бага публиковал на форуме подробно разжёвывая каждый шаг. Так вот — это совершенно типично для англосаксов. Я так скажу: когда три человека пишут маленькую программку, но по ходу дела проговаривают каждую написанную строчку достигая того, что все трое начинают говорить на одном языке и одинаково видеть этот небольшой код — тогда рождается живой проект. Сам код — это ничто, забудьте о выкладывании кода в открытый доступ, думайте о сообществе, о языке на котором говорит ваше сообщество, каждая запятая в вашем проекте должна как-то называться, и должно быть хотя бы три человека которые одинаковыми словами называют каждую маленькую фигулечку. Простой тест, вы должны не запинаясь, любому участнику проекта указать на любой символ в любом файле проекта по телефону не более чем за десять секунд, а он его должен найти и какой-то фразой подтвердить, что он его нашёл, так, чтобы вы были уверены. Это назыается общий язык. Чтобы не было этих «ща пришлю скрин, в нём обвёл красным место где -1 вместо 1». Это основа, вербальная работа с кодом.

Опенсорс это не сам код проекта, это запрограммировать друг друга на общее понимание. Вот вы сажаете рядом с собой паренька на 10 лет моложе себя и начинаете показывать свой код, и на всё говорите как оно называется, но не называется «вообще» и «правильно по учебнику», а так, как вы это сами называете, даже как это называется в вашем подсознании. Так возникает язык, общий язык, язык как его понимает философия. Языковая общность людей, группа объединённая языком. И сам проект — он в первую очередь в головах, на языках, в ушах."

Ссылка. Как улучшить свой стиль программирования?

http://habrahabr.ru/post/230637/

Процитирую:

"
Ролевые игры. Часть 1

Вы и ваш двойник открыли бизнес по автоматизации чего-нибудь. Конкретно вы из вас двоих — директор.

Какая у вашего двойника часовая ставка? Возьмите калькулятор и поделите число, указанное в трудовом договоре на 160. Если вы совершаете эти действия в рабочее время, то поделите результат еще на 60. Вот на столько (+20% льготной ставки в ПФР + 0.2% за травматизм + 6% УСН (половину можно зачесть из налогов ПФР) = +23.2% минимум) вы нагрели своего работодателя, развлекаясь здесь со мной. Знаете, как выглядит миллион рублей? Вспомните объем (и, особенно, качество) написанного вами за последний год — вот это и есть миллион. Стоит покупать ваш труд? Вы, уже как директор, купили бы?

Рано или поздно вы подойдете к другому себе с простым вопросом «Когда будет готово?», с рациональным предложением «Работай быстрее» (потому что ты дорого мне обходишься) и с мудрым советом «Делай проще». Вас от души забавляют объяснения двойника, почему сроки затягиваются, потому что до того как стать директором, вы сами «отмазывались» точно так же и знаете, что за этим стоит.

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

А теперь, внимание, большой секрет. Для, вас, как директора, он уже никакой не секрет, а очевидная мысль — вам не нужны программисты. Вам надо решить проблему. Может, стоило заказать программу в другом месте или купить готовый модуль или вообще ничего не автоматизировать, а нанять людей на низкую зарплату. На текущий момент вы уже потратили 600 тыс. и не получили никакого пригодного результата.

Большой Секрет Еще Раз: программисты никому не нужны. И программы тоже. То есть совсем. Людям интересны они сами, их проблемы и некоторые другие люди. Просто так получилось, что часть проблем можно решить написанием кода. Если бы те же проблемы решались струганием деревянных дощечек и покраской их в зеленый цвет, и это было бы дешевле — так бы и делали.

Если из всей статьи вы готовы запомнить лишь одну мысль, то запомните эту: Программирование не самоценно, а программисты — обслуживающий персонал.

Эта мысль может вас задеть — всё нормально. Чем раньше вы примете её, тем более ценным специалистом вы сможете стать. В нашей культуре считается, что лучше быть господином, нежели слугой. Но если подумать, то общество и вы находитесь в некоторых отношениях. В этих отношениях вы можете занять роль «добровольного слуги» и общество (в лице работодателя и довольных клиентов) щедро вознаградит вас за вашу заботу.

Ролевые игры. Часть 2

Зайдем с другой стороны: ваш двойник — директор, а вы — разработчик.

К вам вот-вот заглянет директор, а фабрика абстрактных автоматизаторов еще не дописана, SQL-запросы не отлажены да и вообще за полгода пришло понимание, что всё должно быть не так. 

Дилемма: никто не хочет работать тяп-ляп. Все хотят гордиться своей работой. Следовательно: (логическая ловушка) Да, я делаю медленно, зато правильно.
Если с «медленно» всё ясно, то что такое «правильно» — совсем не очевидно. На самом деле всё просто. «Правильно» — это когда за отведенный срок (±) появляется пригодная к использованию программа или сервис. Если ваше другое понятие «правильно» не приводит к появлению пригодного к использованию продукта за разумный срок, то ваше «правильно» никуда не годится. Тут нет простора для маневра. Вспомните, что программирование не самоценно.

Работа занимает всё отведенное на неё время. Если разработчику дать в три раза больше времени, он не напишет в три раза лучше. Он напишет в три раза больше кода того же качества, которое обычно выдает. Поэтому, всеми силами старайтесь получить нечто рабочее. Любой ценой. Ваша цель — работающая программа, неважно как написанная. Есть много т.н. называемых «грязных проблем» — это такие проблемы, которые непонятно как решать, пока не попробуешь решить. Пример: краш-тест машин. «Грязные» проблемы всплывут во время первого прохода и их можно будет учесть во время чистовой работы. Желательно, чужими руками.

Второй Большой Секрет: код — это всегда плохо. «Красивого» кода не бывает. Чем меньше кода, тем лучше. Любой код содержит ошибки, компромиссы и проблемы производительности."

Неожиданно актуально...

Ну и ещё:

"P.S.
Третий секрет. Начните писать в commit'ах пользовательскую ценность добавленного кода. Не «добавил два метода», а «Теперь пользователю быстрее/легче/меньше...» или «появилась функция / изменилась реакция». Вам нечего написать? Вы ничего не сделали за день."

(+)
http://18delphi.blogspot.ru/2013/04/blog-post.html
http://18delphi.blogspot.ru/2013/04/blog-post_2.html

пятница, 2 января 2015 г.

Лучшие посты FMXExpress.com

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

На сайте FMXExpress.com решили подвести итоги года, и выложили десятку самых читаемых постов. Большинство с исходниками. 

Один из них - аналог популярного FlappyBird.