четверг, ноября 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 есть...