понедельник, 19 октября 2015 г.

Как быстрее исправлять испорченные подписи к фоткам в Моём Мире


Не смотря на мои письменные заявления администрация mail.ru не желает исправлять ошибку, которая приводит к замене некоторых символов в описании фотографий при копировании/перемещении оных из одного альбома Моего Мира в другой. Искать такие траблы - пол дела. Самое глазоломательное - целить мышкой в них, ибо двойным щелчком амперсанд и семисолон не выделяются. Поэтому я наколенил прогу:


Прога проверяет текст в кармане и "куоты" обратно заменяет на кавычки. Надо только выделить текст для редактирования, скопировать, дождаться пика, и вставить обратно. Для пущей юзабильности я даже чекбокс-выключатель на енабельность таймера добавил. Но это уже баловство, конечно.

Хорошо. Но как подумаешь, сколько стоит Ембаркадеровский продукт, так и начнёшь прикидывать - не поставить ли себе Лазарус?

воскресенье, 3 мая 2015 г.

Как обоблачиться с YandexDisk - 2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

§2.5. Протокольные компоненты

часть 2. TREST~ и TDataSet

Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

Если подумать, то в большинстве случаев REST-протокол - это только транспорт, доставляющий нам такие же данные, что мы получали в старых настольных клиент-серверных приложениях в виде TDataSet. А поскольку TDataSet прекрасно подходит и для хранения локальных данных, то было бы неплохо и вполне естественно приводить получаемый от сервера JSON к этому формату. Особенно, если принять во внимание возможности компонента TFDLocalSQL из палитры FireDAC, который позволяет строить запросы одновременно по нескольким независимым TDataSet.


Разумеется, в компании Embarcadero, которая делает ставку именно на RAD-технологии, позаботились о том, чтобы прикладному программисту не пришлось бы писать свой код для рутинной операции преобразования REST-ответа от сервера в TDataSet. Достаточно набросать на форму полдюжины (да, это не для графоманов) компонентов, как вся цепочка преобразования не только будет готова к запуску приложения, но может начать действовать (если, конечно, ваша MS-Windows к этому готова) уже в процессе проектирования!

Давайте же, запустив предварительно Fiddler, кинем на форму (или модуль данных) компонент TRESTClient, расположенный на палитре RESTClient, и заполним его свойства и параметры:
  BaseURL = 'https://cloud-api.yandex.net/v1/disk' 
  Params = <
    item
      Kind = pkHTTPHEADER
      name = 'Authorization'
      Value = 'токен'//[OAutht ]<OAuthtoken>
    end>
  ProxyPort = 8888
  ProxyServer = '127.0.0.1' //смотрим Fiddler
 
Замечу, что в документации ЯД сказано, что OAuth-токен должен предваряться типом токена - словом "OAuth " с пробелом, но REST API прекрасно работает и без этого префикса. Однако, если вы через HTTP пытаетесь скачать файлы по полученным через REST ссылкам, то такая приставка уже становится строго обязательна.
 
Далее кладём TRESTRequest с увеличенным лимитом потребления (100 вместо 20):
  Params = <
    item
      name = 'path'
      Value = 'app:/'
    end
    item
      name = 'limit'
      Value = '100'
    end
    item
      name = 'offset'
      Value = '0'
    end>
  Resource = 'resources'
 
Ну что, можно уже делать запрос? Да, можно! Прямо в ObjectInspector у TRequest имеется команда Execute..., которая сделает запрос к ЯД без всякой компиляции и запуска программы.  Не верите? Даже после того как появилось сообщение "Response: 200 - OK"? Тогда загляните в Fiddler, и вы увидите и запрос к ЯД, и ответ:



А хотите использовать REST Debugger из меню Tools? Посмотрите на эту программу с громким названием хотя бы из интереса - она построена именно на тех компонентах, что мы сейчас используем. Если у вас не триальная версия Делфи, то вы найдете исходники в каталоге $(BDS)\source\data\rest.

Dернувшись в Делфи, присовокупим к уже имеющимся компонентам TRESTResponse для сохранения результата запроса и укажем JSON-путь до интересующего нас списка файлов, записав '_embedded.items' (эти  слова можно скопировать из дерева Фиддлера или со вкладки Response\Body программки RESTDebugger) в свойство RootElement. Добавим к этому преобразователь JSON → TDataSet  - TRESTResponseDataSetAdapter и собственно сам набор данных - TFDMemTable, который расположен на палитре FireDAC. Надо только связать эти компоненты: TRESTResponse → TRESTRequest.Response,  TRESTResponse → TRESTResponseDataSetAdapter.ResponseJSONTFDMemTable → TRESTResponseDataSetAdapter.Dataset.

Я до сих пор помню своё удивление, когда не в интерпретаторе, а в компилирующей среде разработки прямо во время дизайна вдруг ожила таблица на форме FishFаctory в первой версии Делфи. И двадцать лет спустя, когда компоненты Делфи начинают работать в то время, когда ещё нет самой программы,  я никак не могу отделаться от ощущения маленького чуда.

Итак, продолжаем волшебство. Снова запустим запрос, чтобы наши компоненты пропитались данными, а потом дважды щёлкнем на TFDMemTable или откроем Fields Editor с помощью Object Inspector. Здесь есть команда Add all fields, которая добавит в таблицу все поля из полученного JSON.



Имея таблицу с полями, мы положим на форму TBindSourceDB, привяжем к таблице, а потом разместим удобным образом TListView и запустить на нём команду Bind Visualy. Не правда ли, это волшебство - протягивая стрелки от значения к значению, видеть, как на форме список заполняется данными?

А если хочется добавить иконок, то можно добавить поле в таблицу, и грузить в него с помощью TIdHTTP данные по ссылке из поля preview. Но чтобы получить ссылку для скачивания самого файла нужен будет ещё один запрос по адресу 'cloud-api.yandex.net/v1/disk/resources/download'. Сходите на Полигон ЯД, и вы легко во всём разберётесь. Удачи!

-----------------------

Всё, больше я не буду продолжать эту статью о сервисах Яндекса, авторизации OAuth, о том, как держать бубен, чтобы коробочный Indy выполнял запросы по HTTPS. Пока я её писал Яндекс добавил новый сервис DataSync API для хранения и синхронизации структурированных пользовательских данных в облаке, а Embarcadero выпустила Delphi XE8 с новыми компонентами для HTTP-протокола, а мой младший сын научился кататься на двухколёсном велосипеде. Надеюсь, мои скромные заметки принесут пользу не только мне, приводящему этой писаниной свои мысли в порядок, но и кому-то из прочитавших сей скучный трактат.

Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

суббота, 18 апреля 2015 г.

Как обоблачиться с YandexDisk - 2.5 Протокольные компоненты, часть 1. TidHTTP и JSON

§2.5. Протокольные компоненты

часть 1. TidHTTP и JSON


Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

Чтобы работать с REST API у нас имеется множество специальных компонентов. Но в принципе достаточно иметь и не специальные, лишь бы по http(s) отправить параметры и получить текст в виде JSON, парсить который ещё проще, чем XML - у нас для этого есть прекрасный модуль (начиная с XE6) System.JSON с его TJSONObject, TJSONValue и TJSONArray. Так что же выбрать? Будем мы сосать молочко из сосочки или станем глотать молоко прямо из пакета? А если пить из пакета через трубочку? 


Давайте попробуем для начала  через компоненты HTTP запросить у Яндекс-Диска количество элементов в папке. Дело в том, что ЯД по-умолчанию выдаёт список содержания пачками по 20 штук. Разумеется, мы можем попросить выдать нам не 20, а какое-то другое количество элементов. Например - всё сразу. Но, поскольку ЯД не понимает (или я не знаю как дать ему понять), что такое "всё сразу", ему нужно сказать сколько это будет конкретно. И хорошо бы действительно знать это число, а не заказывать в параметре limit наобум 100-500 элементов.

function TForm2.GetFolderCount(const AFolder: String): Integer;
var
  LAnswer : String;
begin
  Result := 0;
  //
  with TIdHTTP.Create do
  try
    {$ifdef MSWindows}
    with ProxyParams do begin
      ProxyPort := 8888;
      ProxyServer := '127.0.0.1';
    end;
    {$endif}
    Request.CustomHeaders.AddValue( 
      'Authorization', FAuthorization );
    LAnswer := Get(
      'https://cloud-api.yandex.net/v1/disk/resources?limit=0&path='
      + HTTPEncode( AFolder ) );
    if ResponseCode <> 200 then
      Exit;
  finally  Free
  end;
  //
  with TJSONObject.ParseJSONValue( LAnswer ) as TJSONObject do
  try
    Result := ((
      GetValue( '_embedded' ) as TJSONObject ).
      GetValue( 'total' ) as TJSONNumber ).AsInt;
  finally Free;
  end;
end;
...
// calling of GetFolderCount
const
  LFolder = 'app:/';
begin
  ShowMessage( Format( '%d record(s) in folder %s', 
    [GetFolderCount( LFolder ), LFolder] ) );
...

Рассмотрим некоторые особенности кода, приведённого выше. Он делится на две части. Первая - получение информации от ЯД, вторая - обработка полученного результата. Какие замечания можно сделать по первой части?

Поскольку TIdHTTP мы создаём вручную, а не ищем его в палитре компонентов, то не забываем добавить в uses модуль IdHTTP. Если кто не знает (ну вдруг!), то можно найти компонент в палитре, кинуть на форму, всё сохранить, и удалить его - в uses будет уже всё добавлено. Работу с прокси-сервером мы как раз обсуждали чуть ранее. Думаю, очевидно, что в FAuthorization хранится наш OAuth-токенА вот что касается HTTPEncode, то для неё следует подключить модуль Web.HTTPApp. Надеюсь, вы заметили, что делая запрос мы указали в параметрах limit=0, чтобы не получать неинтересующий нас на данный момент список файлов внутри папки, а только информацию о ней самой. 

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


Выделенный на рисунке элемент Items - это массив записей содержимого папки. Естественно, сейчас он пуст. Итак, пора перейти ко второй части кода, где мы вытаскиваем из JSON количество файлов в папке.

Парсинг выглядит довольно просто. Однако, надо понимать следующее: память под объекты выделяется один раз - классовой функцией ParseJSONValue. Метод GetValue возвращает только ссылку на элемент в коллекции. И второе: хотя ввиду неизменности интерфейса ЯД проверять успешность получения элементов структуры особой нужды нет, в общем случае не грех завести переменную для проверки наличия результата GetValue. Имейте в виду особый порядок обработки аппаратных исключений на мобильных платформах.

to be continued...

Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

четверг, 9 апреля 2015 г.

Как обоблачиться с YandexDisk - 2.4 Скрипач как прокси

§2.4. Скрипач как прокси


Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

Совсем не секрет, что путь к успеху часто усеян розами без лепестков, но с шипами. Поэтому не стоит сильно удивляться, что даже установив библиотекинеобходимые Indy для работы по https, вы не сможете сделать запрос не только из среды разработки, но и даже из своей запущенной из-под Windows (отладочной версии) программы.

В чём может быть проблема, если в свойствах компонентов или, например, в поле URL вызванного из RAD отладчика Tools/REST Debugger указан адрес сервера, а попытка выполнить запрос заканчивается ошибкой? Вполне возможно, дело в том, что для выхода в Интернет требуется прокси-сервер. Посмотрите настройки подключения вашего браузера. Скорее всего такие же буквы или цифры нужно указывать и в соответствующих свойствах отладчика или компонентов.


Как правило, указание прокси-сервера требуется в условиях работы в офисе. А если работа идёт в домашних условиях, то указание прокси уже строго не приветствуется. Да и бывает, что офис, в котором поднимается проект, тоже не всегда один и тот же - всю неделю мы, скажем, в филиале в Кожевниках, а на выходные нас пригласили в штаб-квартиру на Таганке. В общем, есть смысл подумать над тем, чтобы иметь некий универсальный подход, не слишком отягощённый необходимостью вникать в местные условия.

Поэтому я всем и каждому советую (© Старуха Шапокляк) везде и всюду качать и ставить такую вещь как Fiddler, что в переводе означает "скрипач". На самом деле это одобренный и рекомендуемый фирмой-производителем ОС бесплатный WEB-отладчик-прокси. Это чудо-средство позволяет просматривать веб-разговоры и даже разговаривать самому. Но в рамках нашей текущей задачи нам достаточно и того, что стоит его запустить, как у вас по адресу 127.0.0.1:8888 установится прокси сервер, который станет смотреть в Паутину либо прямо, либо через тот прокси, который стоит в вашем офисе. Местные условия выхода в Интернет определяются "скрипачом" автоматически. Вам же останется только у себя выставить прокси в 127.0.0.1 с номером порта 88888 и не забыть в программе на старте почистить эти значения, если приложение запускается на мобильном устройстве.

procedure Form1.FormCreate(Sender: TObject);
begin
{$IFNDEF MSWINDOWS}
 RESTClient.ProxyServer := '';
{$ENDIF}
end;

Ну вот - кажется, ещё одной засадой на нашем пути стало меньше.


to be continued...

Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

среда, 1 апреля 2015 г.

Как обоблачиться с YandexDisk - 2.3 Indy и HTTPS

§2.3. Indy и HTTPS


Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet
Как же нам построить программу для работы с REST API Яндекс Диска? Embarcadero предлагает нам мультиплатформенные Indy-компоненты. Но с маленькой оговоркой - они не содержат SSL-шифрования. Т.е. работа по протоколу https, который необходим ЯД (да и прочим сервисам с авторизацией), может стать несколько более сложной, чем "раз, и поехали". Причины этого нюанса лежат в юридической плоскости. Нам же нужно разобраться со следствием в разрезе работоспособности наших приложений. 

Как же нам получить возможность общения по https? (альтернативные варианты типа Synapse здесь не рассматриваются ввиду ограниченности моего кругозора) Для дооснащения Indy-компонентов полноценной поддержкой SSL фирма Embarcadero рекомендует обратиться к ресурсу OpenSSL.org. Здесь лежат Си-исходники библиотек шифрования и рекомендации по их компиляции. К счастью, на сайте есть ссылки и на библиотеки, уже откомпилированные под разные платформы. Вот взять хотя бы http://indy.fulgan.com/SSL/ - вы тоже заметили там слово "indy"? Я лично как раз поэтому и доверился ей. Там, правда, капчу требуют, но на это не стоит обижаться.


В итоге мы имеем пару DLL - libeay32.dll и ssleay32.dll в openssl-1.0.2a-i386-win32.zip для Win32, пару DLL(удивительно, но имена те же, с цифрами 32) в openssl-1.0.2a-x64_86-win64.zip для Win64 и набор для статической линковки под iOS (судя по дате, его уже давно не обновляли) -  libcrypto.a и libssl.a в OpenSSLStaticLibs.7z. А где же Андроидные бинарники? Видите ли, Андроид предоставляет стандартный доступ к шифрованию SSL, и Indy прекрасно может этим пользоваться безо всяких дополнительных приспособлений.


Что делать с DLL - понятно. Есть вариант А - надёжный: кладём их в каталог с приложением  и для дизайн-тайма в C:\Program Files (x86)\Embarcadero\Studio\версия\bin и bi64. Есть вариант Б, который выглядит гораздо предпочтительнее: выложить DLL в системный каталог - C:\Windows\System32 и C:\Windows\SysWOW64 в соответствии с разрядностью библиотек. Важное замечание по второму варианту: имейте в виду, что скорее всего DLL с такими именами у вас уже есть, но они старых версий, и в них нет всех нужных функций. Indy при этом выдаёт сообщение о том, что не удаётся загрузить библиотеки SSL. Обновите их и не забудьте перезагрузить систему. Если и это не поможет, добро пожаловать в "DLL hell"! 

Для того, чтобы запустить йОсное приложение, файлы libcrypto.a и libssl.a надо подключить к проекту через deployment, а в раздел uses добавить модуль IdSSLOpenSSLHeaders_Static, где содержатся объявления об импорте функций и некоторые прочие заголовочные штуки - непонятные, но нужные.


Теперь мы можем делать запросы по https не только в запущенном на устройстве приложении, но даже в ещё незапущенном, в режиме проектирования, прямо в ObjectInspector! 

Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

воскресенье, 1 марта 2015 г.

Как обоблачиться с YandexDisk - 2.1 Подключение ЯД к Windows

Данная статья является продолжением предыдущей

Часть II. Практика

§2.1. Подключение ЯД к Windows


Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

Начнём боевые действия с заброски десанта в тыл противника. Т.е. выложим файлы с картинками на ЯД. Если вы уже установили приложение ЯД, то у вас уже есть доступ к ЯД через проводник. Если же вы не стали загружать приложение от Яндекса, то пора пойти в Проводник, открыть меню Сервис, и выбрать команду "Подключить сетевой диск".



Если в вашем проводнике не оказалось меню Сервис, то вы скорее всего, счастливый обладатель новейшей OS MS-Windows 8.x. Тогда ищите Мой Компьютер и жмите правую кнопку - там "Подключить сетевой диск"  должна быть. Впрочем, обладателям Win7 этот способ также доступен.


В открывшемся диалоге выбираем "Подключение к сайту...". Далее шагая по мастеру-волшебнику мы выбираем сетевое размещение. У меня это единственный пункт. У вас, если судить по характеру окна, вероятно, могут быть другие варианты. В таком случае Вы уж там сами подумайте. Я же всегда безальтернативно дохожу до места, где надо ввести "Сетевой адрес или адрес в Интернете" - https://webdav.yandex.ru. Имя ресурсу я дам "Prikol". Но вам, возможно, захочется чего-то более общего, т.к. в дальнейшем этим ЯДом будут пользоваться и другие ваши приложения.






Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

Как обоблачиться с YandexDisk - 2.2 Создание папки приложения

Данная статья является продолжением предыдущей

Часть II. Практика

§2.2. Создание папки приложения



Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

Теперь мы видим ЯД у себя в Проводнике, и тут же возникает вопрос: а где та самая папка приложения, в которой будут расположены файлы приложения? Не волнуйтесь, папка появится, как только вы начнёте работать от имени вашего приложения, для чего у нас уже есть токен. И чтобы им воспользоваться открывать Делфи, создавать форму, кидать на неё компоненты, устанавливать параметры и вызывать методы - вовсе не обязательно. Если кто-то не в курсе, что в наборе инструментов Delphi есть REST-Debugger, то тому пока можно так и оставаться в неведении. Для тренировок Яндекс прямо в браузере предлагает нам свой собственный REST-отладчик, который называется "Полигон": https://tech.yandex.ru/disk/poligon/

Полагаю, вы сразу найдёте поле, куда вставить свой токен. А чтобы выполнить запрос к папке приложения надо раскрыть раздел "/v1/disk/resources : Файлы и папки" и подраздел "GET /v1/disk/resources Получить метаинформацию о файле или каталоге".


Вот здесь мы и укажем имя своей папки - "app:/". И сразу же можно нажать "Попробовать!".


Вуаля! - папка приложения готова. Можете заглянуть в Проводник и нажать F5 для обновления - всё есть. Осталось разложить по подкаталогам ту кучу художественных образов, которые мы собираемся использовать в нашем приложении.

to be continued...

Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

четверг, 19 февраля 2015 г.

Как обоблачиться с YandexDisk

Эту статью я написал потому, что сам очень хотел прочитать такую - о сервисах Яндекса, авторизации OAuth, о том, как держать бубен, чтобы коробочный Indy выполнял запросы по HTTPS. Это всё не сложно на самом деле, но прочти я такую статью раньше - глядишь, и если бы не одним седым волосом в моей бороде было бы меньше, то уж наверняка одним бы волосом на моей макушке - больше. 

Часть I. Теория

2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

YandexDisk (далее - ЯД, в хорошем смысле) может служить не только в качестве личного облачного хранилища, но и выступать как источник данных для вашего приложения.  Если вам нужно просто иметь доступное отовсюду место, куда можно заглянуть на предмет наличия каких-либо новостей, то бесплатное облако Яндекса может вполне удовлетворить ваши потребности.


В качестве примера давайте сделаем по следам Праздника Влюблённых (или в преддверии Международного Женского Дня) приложение для украшения фоток. Возьмём фотографию с камеры или из галереи, а потом налепим на неё каких-нибудь прикольных штучек. Например, пусть это будут сердечки, цветочки, звёздочки, очки, усы, рамки, буквы... Вот, видите - ассортимент может быть самый разнообразный. Не обновлять же всякий раз приложение, когда мы творчески разродившись добавим в нашу коллекцию приколов новую фишку? Значит, надо дать приложению возможность скачивать эти картинки по мере их создания.

Чтобы построить наше  фишкохранилище давайте заведём аккаунт на ЯД(https://disk.yandex.ru).

Думаю, ввести своё имя и телефон вы сможете без подсказок. Что касается предлагаемого Яндексом клиента для Windows, то тут советую подумать - нужно ли вам, чтобы каждый раз, когда вы сделаете копию экрана или воткнёте в телефон шнурок, выскакивало окошко с просьбой забрать у вас свеженький контент. Меня лично такая настойчивая забота несколько отпугивает. А подключить ЯД к проводнику Windows можно и другим, менее "дружественным" способом - через "Сервис->Подключить сетевой диск". Об этом позднее.

Сейчас же подумаем, как нам дать приложению доступ к данным. Разумеется, в Яндексе об этом уже подумали за нас - они предоставили нам программный интерфейс. И даже не один.


Ну, во-первых, давайте не будем рассматривать "внешние ссылки", т.к. они на самом-то деле ведут не к требуемому файлу, а на страницу скачивания, где искать уже ссылку на исходный файл - задача архисложная для слишком больших любителей. Мы пойдём другим путём(© Владимир Ильич Ульянов) - API Диска.

API Диска предлагает Виджеты Диска, REST API Диска, SDK Диска и WebDAV API Диска. Виджеты нам в приложении вряд ли помогут, SDK (основан на WebDAV) для Delphi пока не сделали, а выбор между REST и WebDAV - однозначно в пользу REST, т.к. о REST слышал даже самый ленивый менеджер проектов, а рассказывать на собеседовании, что вы освоили какой-то "Web-Dove" - ну, мало ли уже в компании всяких Web-разработчиков и Web-дизайнеров, так что не будем путать людей.

Естественно, это  всё шутки. А на самом деле я исхожу из того, что при определении разрешённых действий REST-ЯД даёт возможность ограничить доступ к диску рамками папки приложения. Если учесть, что к одному номеру телефона Яндекс разрешает привязывать только 3 аккаунта, то ограничение приложения своей папкой - весьма значительный фактор. Так что, выбор - очевиден.

Ну что ж, решив, в каком направлении будем двигаться, начинаем. Первым пунктом нам предлагают получить для своего приложения OAuth-токен. OAuth (Open Authorization, я так думаю ©Рубен Вартанович Хачикян) - это что такое? Это когда нам надо получить куда-то доступ, а пароль за нас вводит кто-то другой. Это весьма странная штука, найти аналогию которой в реальном мире довольно затруднительно.



Двигаясь в направлении получения токена не будем тратить время на изучение документации (её читают только от безысходности) и сразу идём регистрировать приложение, где заполняем анкету, в которой обозначим желаемые ресурсы. Их тут, оказывается, довольно много - не только ЯД. Так что мы можем пожелать одновременно и что-то ещё - например, Я-Фотки. Кстати, я обнаружил, что у Яндекса всё ещё нет своего приложения для Я-Телепрограмма - надо как-нибудь потом обязательно сделать.


После регистрации у нас появится ещё не токен, а только идентификатор приложения и плюс к нему - пароль. В этот момент могут возникнуть вполне логичные сомнения: а нужен ли ещё какой-то токен, если уже есть и логин(айди) и пароль? Не одно ли это и то же - этот айди и тот токен?

А где же токен? Вернёмся на шаг назад (кнопка "Редактировать") и ещё раз глянем в низ анкеты - там ясно написано, что будет токен, время жизни которого "Не менее, чем 1 год"!


   
Вот, кажется, и пришла пора ознакомиться с документацией, в которой какие-то схемы взаимодействия, УРЛы обратного вызова и много-много букв. Ну-ну-ну, не будем отчаиваться! Давайте просто немного подумаем, прикинем, что должно происходить в процессе, и наверняка разберёмся без лишних страданий.

Итак, как уже было замечено, айди - это не просто номер приложения, а обозначение списка ресурсов. Причём сейчас стало совершенно очевидно, что ресурсы принадлежат не приложению, а пользователю. Т.е. чтобы добраться до ресурса, нужен логин и пароль пользователя. А логин и пароль пользователя - это доступ не к ресурсам из списка, а ко ВСЕМ ресурсам пользователя, это ПОЛНЫЙ доступ ко всем ресурсам, включая их администрирование. Поэтому, если чисто конкретно для своего аккаунта мы можем вписать в своё приложение логин и пароль, то ждать от стороннего пользователя, чьими, скажем, фотками (только фотками и ничем больше!) мы хотим воспользоваться, что он для этого станет вводить нам в диалог свои входные данные - это, мягко скажем, слишком большие ожидания.

И как же тогда быть? Или точнее - как же они это делают?

"Они" вызывают на помощь Web-интерфейс сервера авторизации Яндекса (или того, кого им нужно), отправив ему при вызове в качестве параметра идентификатор приложения. Да, да, вот так в маленьком окошке браузера открывается Web-интерфейс, где Яндекс показывает  повешенные на идентификатор при регистрации приложения хотелки относительно ресурсов пользователя, а пользователь, видя знакомый логотип в окружении привычных красок и текстов, доверчиво вводит свои учётные сведения и, ознакомившись со всем списком желаний приложения, разрешает (или нет) приложению своими ресурсами пользоваться. 

Да вы наверняка сами часто видели такие окошки, когда какая-нибудь "Забавная ферма" или "Джонни-попрыгунчик" просят вас поделиться своими успехами где-нибудь в FaceBook® или в Одноклассниках®. И, если вы заметили, часто даже и пароль вводить не нужно - обычно он уже давно лежит в куках.
{Здесь должна быть картинка из "Забавная ферма" или "Джонни-попрыгунчик" и авторизация кого-нибудь в чём-то из, но я не играю ни в то, ни в другое, и поэтому у меня нет такой картинки.}
У Яндекса это окошко вызывается УРЛом https://oauth.yandex.ru/authorize?response_type=token&client_id=<идентификатор приложения>[&display=popup][&state=<произвольная строка>]. Добавка display=popup - это чтобы без навигации Яндекса и прочего дополнительного контента. Такой минимализм изначально предназначался для небольших экранов, но на самом деле, я считаю, для диалога в приложении по-другому и быть не должно. Параметр state - это придумываемая автором приложения метка, которая вместе с выдаваемым токеном пойдет по тому УРЛу, что мы задали во время регистрации приложения. Для Web-приложений может быть очень удобно указать свою точку входа, общую для нескольких идентификаторов, и по параметру state судить о том, какое приложение сейчас получило токен. 


Поскольку у нас не Web-приложение, а нормальное настоящее, то нам надо показывать форму с компонентом TWebBrowser, у которого при старте следует вызвать Navigate(<тот самый УРЛ>). Когда нажимается "Разрешить" и уходит POST с формы, обратно возвращается 302 - Temporary moved с "новым" Location https://oauth.yandex.ru/verification_code#access_token=<желанный OAuth-токен>&token_type=bearer&expires_in=31536000. Разумеется, браузер тут же попытается открыть эту страничку, где будет - что? - правильно, наш токен, аккуратно написанный на милой страничке  красивым почерком. Конечно, страничку грузить не обязательно, а достаточно повеситься на событие OnBeforeNavigate2 и вырезать параметр access_token=. Всё!

А вы знаете, что форма с браузером уже есть в готовом виде? Она лежит  в  $(BDS)\Source\data\rest в виде двух модулей REST.Authenticator.OAuth.WebForm - .Win и .FMX. Как ею пользоваться - в примерах Object Pascal\Database\RESTDemo\RESTDemos.dpr. В форму добавлена нерусская кнопка Close, событие редиректа почему-то отлавливается с двух событий - Before и After, а сам обработчик поиска токена в УРЛе в комплектацию формы не входит. Так что, если надумаете пользоваться, советую скопировать модуль с формой в папку проекта и подшаманить, заглядывая в RESTDemos.

А как же компоненты с такими интересными названиями - TOAuth1Authenticator и TOAuth2Authenticator? Увы, господа, эти компоненты ничего не делают. Ну, то есть не то, чтобы совсем ничего, но, скажем так, ничего, что приводит к вызову той самой аутентификации в окошке. Это всего лишь пристёжка к TRESTClient, которая добавляет в параметры уже полученный ранее  OAuth-токен. Често говоря, мне кажется, что делать для этого компонент - как из пушки по воробьям, проще самому добавить параметр ручками. Но тут играют роль общеконцептуальные соображения, т.к. аутентификация может быть не только OAuth.

Ну, вот. Откуда токен - мы знаем: из УРЛа с идентификатором приложения. Что такое OAuth-токен - мы тоже уже понимаем: токен - это идентификатор связки прав доступа с ресурсом пользователя. Срок действия - 31536000 секунд - позволяет нам зашить этот токен в приложение, и уже безо всяких особых форм с браузерами просто работать с Яндексом через REST или другой API.

--------------------------------------
Чтобы найти своё приложение я захожу на ЯД, выбираю "Разработчикам", "REST API Диска", "OAuth-токен" и тычу в. Это почему-то кажется мне проще, чем просто набрать oauth.yandex.ru.



2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

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

Как я боролся с iOS 8.1.3

Давно я не брал в руки йОС, но давеча мне говорят: "ну-ка, покажи нам на айпаде пару новых идей"! Я говорю: "сей момент"! И показываю. А потом, пока я ещё радостный и добрый, айпад тихо так говорит, что хочет чего-то новенького - обновиться желает. И я широким жестом разрешаю, а мне - оп-па! - и геморрой хидэйк на сутки - Unable to install package. (e800003a). и Unable to install package. (e8008016).

Ребята, кому есть до этого дело:
https://www.danielwolf.eu/blog/2015/1458-delphi-mobile-und-ios-8-1-3
http://blogs.embarcadero.com/ao/2013/05/24/39472

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

Как хранить числа в объектах TStringList

Как хранить числа в объектах TStringList прекрасно известно: MyStrings.AddObject( MyString, TObject( MyNumber ) )...казалось бы. Однако, мне (да вот - дошла очередь и до меня) пришлось убедиться, что к словам "прекрасно известно" следует добавить слово "было" - прекрасно известно было. Увы, двадцать лет работавший в Win32 и даже в Win64 способ теперь уже не работает. Точнее, не работает на мобильных платформах, т.е. там, где действует незримая власть коварного AUTOREFCOUNT aka ARC.

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


Цвет идёт к справочнику в виде строки #rrggbb - тут StringToAlphaColor() сразу в руки. Получаем бинарное значение TAlphaColor, которое на самом деле = type Cardinal, т.е. то, что отличнейшим образом укладывается в AddObject() рядом со строчкой из справочника. Теперь, получая данные, я легко в справочнике найду соответствующий цвет: 'критично'-#ff0000, 'неважно'-#cccccc, и т.д. Один раз цвета распознал, а потом пользуйся сколько влезет. Только обязательно надо не забыть включить для цвета канал Alpha (например, Result := TAlphaColorRec.Alpha + AColor), а то нарисованное будет настолько прозрачным, что его станет совершенно невозможно разглядеть.

Связка справочников - тоже штука банальная. У меня в одном из двух справочников дано дефолтное значение из второго. Надо при заполнении TStringList просто бежать по первому списку и искать в предварительно заполненном втором IndexOf() и класть в объекты первого списка найденные индексы, а текущий счётчик класть, разумеется, в объекты второго списка с этими индексами. Тут, правда, есть нюанс: нулевое значение nil, которым инициализируются объекты списков, будет в виде целого числа равно нулю, что означает не пустое значение, а индекс первого элемента. Надо быть внимательнее - ставить -1 везде, где только можно, или при сохранении найденных индексов добавлять единицу, а потом об этом надо не забывать, и оперировать уже с отниманием единицы. Я лично предпочитаю второй способ - точно не нарвёшься на случайный ноль.


Не долго думая, я сделал прототип на Win32. И всё у меня прекрасно заработало: метки красились нужным цветом, словари дружно связывались. Ура! Осталось только скомпилить под Андроид, который требовался Заказчику, и ... Вот тут-то и обнаружилось, что связки не связываются, а цвета не цветут. Как же так?

Чтобы не рвать куски из проекта, возьмём надуманный пример. Создадим проект пустого мобильного приложения, добавим в дереве проекта платформу Win32 и вставим на событие OnCreate главной и единственной формы код, что дан ниже (имя формы только подкорректируйте под себя). Ах, да - не забудем ещё бросить на форму TMemo c Align = alClient ( все эти манипуляции читатель может делать мысленно ).

const
  Labels: array [1..5] of String = ('Lorem','ipsum','dolor','sit','amet');
 
procedure TForm6.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  with TStringList.Create do
  try
    for I := Low(Labels) to High(Labels) do
      AddObject( Labels[ I ], TObject( I ) );
    Sort;
    for I := 0 to Count-1 do
      Strings[ I ] := Format( '%s=%d', [ Strings[ I ], Integer( Objects[ I ] ) ] );
    Memo1.Lines.Text := Text;
  finally Free;
  end;
end;

Ожидаемый результат получен:


А вот Андроидная версия скорее всего при запуске выдаст чёрный экран, упёршись в первую встречу с AddObject(). Не будем разбирать на части коробку передач, а удовлетворимся фактом, что нейтраль - тут, а чтобы ехать - там. Ясно, что использовать непонятные адреса для объектов, когда тут и там ссылки считают - это как ходить по коровьему пастбищу с закрытыми глазами.

А у меня в проекте этот фокус прошёл. Я поназаполнял словарей, развесил там индексы и цвета и пошёл дальше читать уже данные. И только когда стал доставать из закромов все свои богатства, то обнаружил сплошные nil. И сильно удивился - мыши съели?! - Так нет здесь мышей! Ну да говорят, что удивляться - это даже полезно.

Что делать? Проект - вон он уже дышит и играет мускулами! Не будешь же перетрахивать (© Лукашенко А.Г.) его из-за какой-то мелочи. Сейчас дадим таблеточку, и будет как здоровый!

type
  TData<T> = class
    FValue: T;
    constructor Create( const AValue: T );
  end;
 
const
  Labels: array [1..5] of String = ('Lorem','ipsum','dolor','sit','amet');
 
procedure TForm6.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  with TStringList.Create( True ) do
  try
    for I := Low(Labels) to High(Labels) do
      AddObject( Labels[ I ], TData<Integer>.Create( I ) );
    Sort;
    for I := 0 to Count-1 do
      Strings[ I ] := Format( '%s=%d', [ Strings[ I ], TData<Integer>( Objects[ I ] ).FValue ] );
 
    Memo1.Lines.Text := Text;
  finally Free;
  end;
end;
 
{ TData<T> }
 
constructor TData<T>.Create(const AValue: T);
begin
  FValue := AValue;
end;

Просто? Да! Мне даже как-то понравилось - появилось ещё "пустое значение": nil=Null. Так что и игры с плюс-минус единицей для индексов можно выкинуть. А если, например, нужно Альфа-канал цвета устанавливать по месту, то уже ни за что не спутаешь чёрный цвет с прозрачным - 0 уже не равен nil! И кода лишнего - с гулькин нос. Главное - при создании TStringList включить  параметр OwnsObjects, чтобы объекты прибивались самим списком, и всё. Красота!


...............

Конечно, старики в стойбище ворчат, что на новой мягкой земле уже нельзя просто оставить пойманную рыбу, как раньше на льду, а надо держать в садке, привязанном верёвкой к колышку, чтобы рыба не стухла, или не стащил кто. И чтобы оттуда достать рыбу, надо теперь этот садок вытягивать из воды, лезть в него рукой и там шарить. Как раньше хорошо было! А теперь таскайся с этой штукой. Это же, конечно, ещё и денег каких-то стоит!  Ох уж эти новые края с их климатом! Как тут другие люди живут?...