четверг, ноября 10, 2005

Вассерман

Некто Анат. Вассерман излагает свои соображения относительно Чернобыля: http://awas.ws/HOBBY/TECHNICS/CHERNBYL.HTM

среда, ноября 09, 2005

У МТС установлена новая биллинговая система.

Какая разница и не наплевать ли? Нет. Не наплевать. Минута разговора мне теперь обходится в $35. Уже устал общаться финотделом и возвращать свои деньги. Обидно было бы сменить оператора, которым пользовался 5 лет.

Сегодня (вчера уже) после банки пива мне пришло в голову, что человек произошёл от трансформатора. Когда подопьёт - начинает гудеть.

вторник, ноября 08, 2005

О преобразованиях

Ещё один день закончился (как раз в то время, как у других он только начинается). Закончился он написанием шаблонов для двухступенчатых преобразований текстов. В общем и целом - ничего особенного. Однако для ряда случаев - очень полезна. Таковых случаев я знаю несколько, но все они сводятся к применению т.н. skins. В некотором смысле - высшая форма отделения представления от "логики". Говоря честно, мне по-английски такие тексты было бы писать проще, а то все эти кавычки... Ну так вот. Суть можно изложить в трёх предложениях:
1. Объект, моделирующий нечто, отдаёт свои данные в виде XML
2. По строке (а ведь всякий XML - строка, чем и ценится) строится метамодель представления (тоже в XML) путём применения к исходному XML XSLT
3. К метамодели применяется другой XSLT (а может, третий, четвёртый и т.п.), отвечающий за применение к метамодели skins.

В итоге получаем HTML, который:
а) на лету подстраивается под любого пользователя
б) позволяет радикально сократить число классов-представлений для отображения данных

Вкратце, механизм (С#, .NET 1.1)...
1. Класс, ответственный за что-то, должен уметь представить свои данные в XML


class BusinessClass{
int m_Id = 123;
string m_Name = "Name";
...
public string ToXml()
{
string s = "";
s += "<downloadupdate>";
s += "<id>";
s += m_Id.ToString();
s += "</id>";
s += String.Format("<name>{0}</name>", m_Name);
s += "</downloadupdate>";

return s;
}
}


2. Написать XSLT, который по XML-данным объекта формирует метамодель. То есть, например

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="yes"/>
<xsl:template match="downloadUpdate">
<line><xsl:apply-templates/></line>
</xsl:template>
<xsl:template match="downloadUpdate/id">
<cell><xsl:apply-templates/></cell>
</xsl:template>
<xsl:template match="downloadUpdate/name">
<cell><xsl:apply-templates/></cell>
</xsl:template>
</xsl:stylesheet>

3. Написать ещё один XSLT, который применял бы к метамодели skin.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="yes"/>
<xsl:template match="line">
<HTML>
<BODY>
<TABLE>
<TBODY>
<TR>
<xsl:apply-templates/>
</TR>
</TBODY>
</TABLE>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="cell">
<TD>
<xsl:apply-templates/>
</TD>
</xsl:template>
</xsl:stylesheet>


4. Это почти всё. Трансформации, КГБычно, выполняются примерно так.

XmlDocument doc = new XmlDocument();
doc.LoadXml(object.ToXml());

XPathNavigator nav = doc.DocumentElemen.CreateNavigator();

MemoryStream ms = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(ms, Encoding.UTF8);
XslTransform xt = new XslTransform();
xt.Load(PATH_TO_XSLT_1);
xt.Transform(nav, null, writer, null);

ms.Seek(0, SeekOrigin.Begin);
byte[] b = new byte[ms.Length];

ms.Read(b, 0, (int)ms.Length);
ms.SetLength(0);

string s = Encoding.UTF8.GetString(b);

Это первый шаг преобразования, после него получаем что-то вроде

<?xml version="1.0" encoding="utf-8"?>
<line>
<cell>123</cell>
<cell>Name</cell>
</line>


На втором шаге надо к строке s применить то же самое, что и к исходному XML, а потом выбросить результат в Response. Из побочных эхвектов я отмечаю большую нагрузку на процессор, что надо иметь в виду.

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

понедельник, ноября 07, 2005

}{орошо забытое старое

Бывает так, что надо запретить доступ только к некоторым страницам web-приложения. Чтобы не городить собственных безумств, надо пользоваться механизмом, уже существующим в .NET. Наиболее простой из них - Forms Auth.

Схема его работы проста:
1. В web.config указываются защищаемые страницы
2. Там же указывается, куда надо направить пользователя при попытке пробраться к данным, требующим авторизации
3. На странице проверки полномочий юзверя (п.2) выполняем проверку (можно стандартными средствами, через записи в web.config, а лучше - через БД) и если всё нормально - пользователь идёт на ту страницу, куда его с ходу не пустили.

Вроде ничего особенного, но.
1. Не нужно изобретать механизма. Обычно лепят что-то через cookie, session и пр. Это ужасно.
2. Это точно работает и гибко, при томЪ.

В деталях это выглядит так.
1. В web.config вносим запись


<configuration>
<location path="download.aspx">
<system.web>
<authorization>
<deny users="?">
</deny>
</authorization>
</system.web>
</location>

Это запретит доступ всем, кто не авторизован, к странице download.aspx
2. В тот же самый web.config вписываем

<configuration>
<system.web>
<authentication mode="Forms">
<forms loginurl="login.aspx"/>
</authentication>
</system.web>

К этой странице будет перенаправлен человек, про которого мы ещё не знаем, кто он.
3. В коде login.aspx надо написать свою проверку (как угодно), и если удалось понять, кто к нам пришёл, то, в случае наличия у пассажира полномочий - выполнить одну простую строчку в коде:
FormsAuthentication.RedirectFromLoginPage("ЗдесьИмяПользователя", false); это приведёт к тому, что пользователь после успешного опознания отправится прямиком на ту страницу, к которой и обращался первоначально.

Детали можно в MSDN посмотреть. Делается это дело через cookie со спец. именем (можно управлять в web.config) и спец. формированием пути при обращении к защищённому ресурсу. Дёшево и сердито.

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

static object m_RedirectSync = new object();
public static void Redirect(Page Current, string Url)
{
lock(m_RedirectSync)
{
Current.Response.Buffer = true;
Current.Response.Status = "302 Moved Temporarily";
Current.Response.AddHeader("Location", Url);
Current.Response.AddHeader("Content-Length", "0");
Current.Response.Flush();
}
}

HTTP придумали очень умные люди. Предусмотрительные, во всяком случае.
С практической т.зр., пользователи почти ничего не платят за этот маленький фокус. Ну, разве что отметят странность, почему это progressbar дважды дёргался при одном запросе страницы. Но и то - только те, что на обычных модемах. Прочие - не заметят вообще ничего.

суббота, ноября 05, 2005

Вчера ни с того, ни с сего дико заболел. Проспал сутки. А сегодня весь день потратил на переделку (а фактически - на написание с нуля) сайта. Горло болит.

среда, ноября 02, 2005

Нерадостно как-то

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

Код не просто нерабочий. Это просто мешанина какая. Имена переменных вроде

f_Id, mX
классов
_default, db, ThisIsMyFirstMeaningFullClass.

Как можно в многопользовательском приложении использовать DataReader? Почему надо непременно обращаться к БД непосредственно из кода форматирования страницы? С какой целью сделано дублирование классов и они были распиханы по разным каталогам? Я не понимаю. А когда я не понимаю, я начинаю дико злиться, потому что я не могу не понимать.

Лучше бы мне вместо этого "кода" прислали мешок змей или ещё чего. Иногда мне кажется, что я знаю причины этого скотства окружающих "профессиональных программистов", хотя и не желаю в них вникать.

Я ненавижу мерзавцев, которые делают свою работу кое-как.

ненавижу подлец сволочь скотина убивать убивать убивать убивать безответственность технохамство подлость мелочность лживость зачемязаплатилемуденьги? никомунельзяверить времяпотерянозря гадко гадко гадко гадко

вторник, ноября 01, 2005

Об именах

Сколько раз уже говорили людям: "используйте правильные имена для переменных, свойств и методов". Кивают головой, соглашаются и делают наоборот. А вот очень просто. Получаю от тестировщика: "Smoke test failed". Ну и причины там... Суть такая, что в процессе работы происходит обращение к неразмещённому объекту. Что-то вроде:

si.Subject.LastName = someName;

при этом свойство Subject получается следующим образом:


public Subject Subject
{
get
{
if (m_Subject == null)
{
if (!IsNew && SubjectId > 0)
{
SubjectsEngine eng = new SubjectsEngine();
m_Subject = eng.GetSelected(SubjectId)[0];
}
else if (ShouldCreateStub)
m_Subject = new Subject();
}

return m_Subject;
}

set
{
SubjectId = value.Id;
m_Subject = value;
}
}
Ничего особенного? Так и есть. Но представьте себе, что
ShouldCreateStub
называется не так, как оно тут называется, а, положим,
MustIgnoreEmptiness
и определено так:

private bool m_OldBehavior = true;

public bool MustIgnoreEmptiness
{
get {return m_OldBehavior;}
set {m_OldBehavior = value;}
}
? Как тут можно вообще понять, что имел в виду автор? Для чего он эдак накрутил? Я не знаю, а вот время потратил. И ведь styleguide есть...

понедельник, октября 31, 2005

Партнёрство

Мы готовимся подавать документы на партнёрство с Microsoft. Для этого нужно иметь в штате двоих сертифицированных людей и три ссылки на успешный опыт внедрения. НачинаемЪ.

суббота, октября 29, 2005

Case 360

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

Type type = null;
foreach(Assembly next in AppDomain.CurrentDomain.GetAssemblies())
{
// здесь какие-то проверки, результатом которых является
// заполнение переменной type
}

object obj = Activator.CreateInstance(type);
BaseType bt = (BaseType)obj; // *


// * - надо заявить, что Type является потомком BaseType

И вот, при выполнении // * происходит забавная вещь: исключение "Specified cast is not valid." Забавность этой вещи заключается в том, что возникает это исключение только при выполнении "большого проекта", в случае теста ошибка не воспроизводится. Причины мне до сих пор неизвестны, хотя есть подозрения, что происходит обращение к разным версиям сборки. Чтобы обойтись без приведения, пришлось использовать reflection, который оказался как раз к месту:

object sp = Activator.CreateInstance(type);
type.InvokeMember("LoadConfig", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, sp, null);


Исследования продолжаются. Система сообщения об ошибках, имеющаяся в .NET 1.1 является одной из самых бессмысленных из всех, что я только видел. Это очевиднейший пережиток "времён позднего DCOM", тратить время на борьбу с которым лично мне - жалко.

пятница, октября 28, 2005

Эта картинка не относится к делуСегодня днём мы будем пытаться выцарапать свои деньги. Надеюсь, что всё получится, потому что у нас масса планов и идей и не хотелось бы, чтобы какие-то уроды помешали в их воплощении. Но это всё будет позже, а пока мы пересылаем друг другу письма, в которых оттачиваем формулировки и аргументацию, которые должны подействовать. Вместо полезной работы.

Завтра, кстати говоря, начинается 3 (2-то уж точно) больших дела.
1. Наш инженер-теплотехник, которого я привлёк к работе в последнюю свою поездку в Псков, предоставил уже достаточно информации для того, чтобы можно было начать работу над расчётной моделью тепловых потерь и теплогидравлического состояния системы. Сколько при этом придётся сделать - я себе уже примерно представляю. Это должна быть интересная работа, поскольку расчёты будут выполняться не так, как в первой версии (комом), а на основе взаимодействия нескольких веб-сервисов. Клиентов не поймёшь: одни не любят WinForms-приложения, других воротит от "Web-примитива", а с WS это уже не играет никакой роли.
2. Пилотный проект системы мониторинга для нашего заказчика-кидальщика на основе рабочих мест-КПК. А то "эффект бомбы", "эффект бомбы"... Вот и посмотрим, что это за эффект такой. У меня уже сколько зря простаивает на столе этот iPAQ (QAPi) - музыку слушаю, когда не за компьютером да почту проверяю (вчера вечером в Шоколаднице на Софийке всё проклял, пока подключился к серверу - там у оператора какие-то проблемы с покрытием...). Забавно, кстати, на КПК расшифровывать письма через консоль и gpg. Минуту-другую-то у меня точно занимает. Но я отвлёкся. Эта система в идеале позволит нам выпустить в город злых людей в форме, обуздав при этом их жажду крови: они будут рвать только на основании огонька. Пока красный не загорится - они не должны будут и близко "принимать меры". Всё это в целом позволит гражданам вздохнуть свободнее и спокойнее. Да и люди в форме, может (можыд? мойшет?) быть, тоже одумаются.
3. Дневник программиста, видимо, станет таковым. Сейчас, например, меня интересуют реализации WS-Reliable, WS-Addressing и WS-Secure на платформах .NET 1.1 и .NET 2.0. В сравнении с тем, что есть для Java. А то я давно уже слышу, как это прекрасно - жить в Java, но что-то не очень верится. Посмотрим. И тут расскажу.

UPDATE
Встреча п.1 перенеслась. Жаль.

вторник, октября 25, 2005

Вторник

Нас проверяют на вшивость
Работа без договора- сущее наказание. В процессе сдачи проекта выяснилось, что заказчик отказывается доплатить нам $40'000 разницы между полной суммой работ и выплаченной частью. Как теперь разговаривать, какие аргументы использовать, если на той стороне просто не хотят платить? Всё усугубляется ещё и тем, что в команде не все верят в то, что нас пытаются кинуть. Я говорю: прекращаем всю работу над этим проектом до прояснения ситуации, а мне отвечают: "я не согласен. 2 дня до дня переговоров нам не делают погоды". С момента первого звонка - 10 августа -прошло 2 месяца, больше, и всё это время мы работали, исходя из этого дурацкого принципа: "ну ещё 2 дня ничего не решают".

Как бы не так! Решают. И ещё как! Теперь надо за 12 часов изменить стратегию работы, переоткрыть наши прочие направления и забыть уже не только про выходные, но и про "выспаться".

понедельник, октября 24, 2005

Понедельник

Главное окно FogBugzСегодня и завтра я работаю без тестировщика. Непривычно выпускать версию, писать на проект, что версия готова и не получать отклика от тестировщика. Чтобы времени не терять, я решил в FogBugz пройтись по инцидентам, что ещё не закрыты. Закрыта проблема с интерфейсом для поиска повторений в объектах, сомнительная ошибка с ключами, которые якобы не должны генерироваться, если не указан email отправителя... Дурацкий какой-то день, бестолковый.