Tuesday, June 26, 2007

GWT: Changing locale using cookies

Продолжая работу над своим проектом портирования функционала поисковой системы Look'а на GWT (lab.look.org.ua), я дошел до и18нации. В GWT, как известно, есть довольно хорошо продуманные средства для хорошей локализации. Но мне захотелось, чтоб текущий выбор языка хранился в куках, а не в переменной. В общем, решение получилось довольно простое.

1. Выбор языка и сохранение в cookie.

Этим занимается вот такой виджет:

package client.widgets;

import com.google.gwt.user.client.ui.*;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Cookies;

import java.util.Date;

/**
* Created by Olostan
* Date: 24.06.2007 23:37:00
*/
public class LangSelection extends Composite {
private static native void RefreshWindow() /*-{
$wnd.location.reload();
}-*/;
private class LangImage extends Image implements ClickListener {
final String code;
public LangImage(String code) {
super();
this.code = code;
this.setUrl("images/flags/"+code+".gif");
this.addClickListener(this);
this.setStyleName("lang-image");
}

public void onClick(Widget sender) {
Date date = new Date();
if (!code.equals("en")) {
Cookies.setCookie("locale",code, new Date(date.getTime()+60*60*60*60));
} else {
Cookies.setCookie("locale","",new Date(date.getTime()-1000));
}
RefreshWindow();
}
}
String[] lang = new String[] { "en","uk","ru" };
public LangSelection() {
HorizontalPanel panel = new HorizontalPanel();
for(int c=0;c<lang.length;c++) {
LangImage img = new LangImage(lang[c]);
panel.add(img);
}
initWidget(panel);
setStyleName("lang-widget");
}
}

 


Тут самый важный код  - это тело метода onClick. Он именно записывает выбранный язык в Cookies и делает рефреш странички.


2. Установка языка при загрузке модуля


Тут пришлось применить вот такой интересный трюк: в исходном html файле в блоке head добавляется вот такой код:

<head>
<title>MyModule title</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function getCookie(name) {
var dc = document.cookie;
var prefix = name + "=";
var begin = dc.indexOf("; " + prefix);
if (begin == -1) {
begin = dc.indexOf(prefix);
if (begin != 0) return null;
} else {
begin += 2;
}
var end = document.cookie.indexOf(";", begin);
if (end == -1) {
end = dc.length;
}
return unescape(dc.substring(begin + prefix.length, end));
}
var locale=getCookie("locale");
if (locale!="") {
document.writeln("<meta name=\"gwt:property\" content=\"locale="+locale+"\"> ");
}
</script>
<meta name='gwt:module' content='MyModule'>
<link rel=stylesheet href="MyModule.css">
</head>

 


(тут попрошу прощение за потерянный origin кода на яваскрипте чтения значения cookie).


Таким образом при загрузке модуля будет установленна нужный язык.


Пример того, как оно работает можно посмотреть вот тут: lab.look.org.ua

Monday, June 25, 2007

Interesting code puzzles

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

Вот и решил собрать небольшую подборочку таких приколов. Если ещё найду, буду дополнять.

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

Какой метод будет вызван?

(C#, на Java вроде то же самое)

using System;

namespace ConsoleApplication2
{
class Program
{
private static void A(Object o)
{
Console.WriteLine("Object");
}
private static void A(String s)
{
Console.WriteLine("String");
}
static void Main(string[] args)
{
A(null);
}
}
}


Ответ: "String". Потому, что String наследуется от Object, а null можно привести к String. т.е. полностью однозначный вызов метода с параметром наиболее "высокого" в иерархии классов параметра.


Пока не нашёл точной спецификации в MSDN описания этого поведения. Подредактирую этот пост как найду.



Инкриментинг


(C#, на Java же самое)

using System;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
int tricky = 0;
for (int i = 0; i < 3; i++)
tricky += tricky++;
Console.WriteLine(tricky);
}
}
}


Ответ: 0. Потому, что каждый раз к 0 будет прибавляться 0, с постинкримент будет происходить до изменения переменной.


Кстати, интересно то, что если в C/C++ выражение i=i++ является неопределённым (т.е. выдаёт на разных компайлерах разные результаты - см п 3.8), то в C#/Java всё довольно чётко определенно.



Массив


(C#, на Java же самое)

using System;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
int[] arr = new int[2] { 0, 1} ;
int i = 0;
try
{
arr[i] = i = 2;
Console.WriteLine("{" + arr[0] + "," + arr[1] + "} i=" + i);
} catch (IndexOutOfRangeException)
{
Console.WriteLine("Out of bounds!");
}
}
}
}

Ответ:



{2,1} 2

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


Равенства


Думаю всем известно, что в Java и в C# есть разные способы сравнивать. Вот интересный и простой тест (хотя в Java есть только два способа из-за невозможности перегрузки операторов). Первая часть скорее проверка базовых знаний, а вторая уже немного интересней.

using System;
namespace ConsoleApplication
{
class Program
{
public static void Check(object a, object b)
{
Console.Write(a == b ? "same" : "not same");
Console.Write(a.Equals(b) ? " and equals" : " not equals");
Console.Write(object.ReferenceEquals(a, b) ? " and ref equals" : " not ref equals");
Console.WriteLine();
}

static void Main(string[] args)
{
int inta = 1000;
int intb = 1000;
Check(inta, intb);

String stringA = "test";
String stringB = "test";
Check(stringA, stringB);

}
}
}

Ответ:


not same and equals not ref equals
same and equals and ref equals
Для int'ов - ссылки разные из-за того, что произошел boxing, а для строк ссылки одинаковые из-за того, что компилятор одинаковые строки собирает, и при инициализации ссылки на строку передаёт один и тот же объект.


В принципе если посмотреть на сборку, которая получается в результате, то объяснение ответа второго вывода довольно простое.


Инициализация


Этот пазл имхо уже довольно сложный. И снова таки как и в C# так и в Java поведение его одинаково.


Вот собственно код:

using System;
using System.Threading;

namespace ConsoleApplication
{
class Program
{
public class TestingClass
{
public static bool inited = false;
static TestingClass()
{
Thread thr = new Thread(new ThreadStart(delegate { inited = true; }));
thr.Start();
thr.Join();
}
}


static void Main(string[] args)
{
Console.WriteLine("Lets check if TestingClass was inited:");
Console.WriteLine(TestingClass.inited);
Console.WriteLine("Done.");
}
}
}


Ответ:


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



PS. Ах да, некоторые идеи а так же Java варианты некоторых пазлов можно посмотреть тут

Thursday, June 21, 2007

Storing revision information using MSBuild

После опубликования заметки про использование WSH для модификации/генерации файлов во время сборки проекта, Kostiantyn Kudriavtsev предложил альтернативный вариант - воспользоваться "тасками" MSBuild'а.

Действительно идея отличная при использовании автоматизированных сборок продуктов. В особенности с использованием MSBuild Community Tasks, в котором реализовано огромное количество "тасков" - от получения версии из SVN/VSS/TFS до модификаций файлов, создания архивов, работой с XML.

Нашел так же хороший простенький пример как раз подобной операции, что мне нужна была.

Как минус - подобный подход отличный для автоматизированных сборок - т.е. из Visual Studio при запуске проекта эти таски не будут выполняться. Слышал что возможно и в Visual Studio интегрировать, но пока хорошей информацией не могу поделится.

Tuesday, June 19, 2007

Storing revision information using Windows Script Hosting

Вот возникла задача в ASP.NET приложении отображать номер ревизии, дату когда происходил билд. Не знаю, возможно есть и более простые решения, но так, как я их не нашёл, решил написать простенький JScript код, который будет выполняться WScript'ом как prebuild-евент проекте.

Вот какой скриптик получился:

var ws = new ActiveXObject("Wscript.Shell");
var path = "";
if (WScript.Arguments.length == 1) {
path = WScript.Arguments(0);
}
var fso = new ActiveXObject("Scripting.FileSystemObject");
var oSvn = ws.Exec("svn info");
while (oSvn.Status == 0)
{
WScript.Sleep(100);
}
var vers = oSvn.StdOut.ReadAll();
var regex = new RegExp("Revision: (\\d+)","m");
var revArr = regex.exec(vers);
if (revArr==null) {
WScript.Echo("No revision found!");
} else {
rev = revArr[1];
var templateFile = fso.OpenTextFile(path+"ProductVersion.cs.template", 1);
var template = templateFile.ReadAll();
var regex2 = new RegExp("\\$Revision\\$","gm");
template = template.replace(regex2,rev);
var regex_date = new RegExp("\\$Date\\$","gm");
var date = new Date();
template = template.replace(regex_date,date.toGMTString());
var result = fso.CreateTextFile(path+"ProductVersion.cs", true);
result.Write(template);
result.Close();
}

Он фактически на основе темплейта ProductVersion.cs.template создаёт файлик ProductVersion.cs, в котором заменяються $Revision$ текущей ревизией, а $Date$ датой.


Тут разве что стоит отметить два "но":



  1. Скрипт должен запускаться в том месте, где доступна информация о ревизии (+ рядом должен лежать файлик темплейта). По этому, следует перед вызовом скрипта (через wscript/cscript) в prebuild добавить cd $(ProjectDir)
  2. Так, как проект ASP.NET 2.0 не имеет prebuild/postbuild евентов (во всяком случае я не нашёл), нужно иметь другой проект, который будет экспортить инфу о ревизии.

Кстати, почему-то подумалось: решение чем-то похоже на решение с "генераторами", про которое я недавно писал.

REST - Just rest!

Всё чаще и чаще слышу последнее время про REST и всё, что с ним связанно. Интересно тем, что все давно пользуются так или иначе, но стандартизировать все ленятся.

А реализовывать чей-то API ещё хуже - то одно намудрят, то другое.

И по этому меня заинтересовала работа Thomas Steiner'а об автоматизации этого процесса. И ещё больше - реализация идея в виде работающего генератора кода (Кстати, ещё тем, что он написан на GWT, и доступен исходный код со странички проекта).

Довольно полезная тулзовина для реализации кода, использующего REST-запросы.

GWT + iPhone?

Сразу после того, как начали говорить о том, что в iPhone нельзя будет ставить свои проги, возникла мысль -  а как же "виджеты" и прочее, для чего нужно только одно - качественный броузер.

И, как оказалось, эта мысль была не "из воздуха". Как написал Robert Hanson на своём блоге, Apple недавно провели World Wide Developers Conference, на котором был анонс SDK для iPhone. И как написал Роберт "That's right, the new SDK is the same SDK that Web 2.0 developers have been working with for the past few years, including GWT."

Сразу же пошла волна плейсхолдеров для проектов с GWT-виджитами, которые бы использовали фичи iPhone по типу gwt-iphone.

Так же, стоит отметить интересные мысли Joel Webber'а о взаимодействии GWT не только с Safari на iPhone, а так же с броузером Opera на Wii.

Действительно - зачем придумывать новый SDK, если можно воспользоваться уже как бы можно сказать многолетним опытом разработки Web 2.0 приложений? А в особенности используя такие тулкиты, как GWT, Yahoo UI и подобные, которые позволяют создавать довольно сложные пользовательские интерфейсы?

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

К примеру, я так и не нашёл никакого достойного ответа Microsoft'а. Интересно, они имеют какую-то скрытую технологию или просто отстают в развитии?

Monday, June 18, 2007

GWT Generators (GWT-Exporter as example)

Ray Cromwell на своём блоге закончил цикл статей о том, как использовать генераторы в GWT.

Идея отличная: зачем делать что-то в run-time, если можно сделать то же самое в compile-time? Если можно просто при компиляции сгенерить нужный код?

Мало того, идея совсем не новая - насколько я знаю. есть много "мета-языков", которые делают то же самое.

Но в GWT это интегрировано в сам компилятор: к примеру, для классов, помеченных, как имплементирующие IsSerializable создаются автоматом "стабы", в которых и происходит сериализация/десиализация. В .NET происходит практически то же самое, но в рантайме - создаётся временная сборка, которая сериализует необходимый класс.

Вроде бы то же самое, но есть одно "но" - повторить подобную процедуру достаточно сложно. В GWT с помощью генераторов можно это сделать намного легче.

Как? Вот статьи Рея и открывают "мистику" генераторов:

GWT Demystified

GWT Demystified: Generators, Part 1

GWT Demystified: Generators Part Deux

и финальная GWT Demystified: Generators Part 3, Meet the Oracle

Так же доступны через svn исходники его проекта GWT-Exporter, позволяющего помечая интерфейсом Exportable генерировать "стабы" для вызова GWT-объектов из JavaScript (без него надо было бы создавать специальные методы для "бриджинга" вызовов к GWT-объектам, так как методы последнего могут быть изменены при компиляции)

В любом случае, очень интересная статейка.

Brainbench: ASP.NET

Недавно проглянул, что некоторые из моих сертификатиков на BrainBench скоро перестанут быть валидными - там срок активности вроде 3 года. Вот и решил пройти какой-то из их тестов.

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

Сразу же попался интересный вопрос про количество евентов у global.asax. Интересен он был тем, что варианты были такие - 2, 4, 6, 8, больше 15. Но, насколько я знаю, этот файл есть ничто иное, как обёртка над System.Web.HttpApplication, который в 1.1 имеет ровно 14 event'ов (хотя в 2.0 уже 17).

В остальном тест довольно интересный. Дал повод задуматься о том, что я не так часто, как стоило бы пользовался System.Web.Caching.Cache - оказывается довольно удобная и полезная штука.

В целом, результат, хоть и не очень высокий, но меня удовлетворил.

Интересно как-то пройти ASP.NET 2.0 и С# 2.0

Per compitalis ad astra

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

Для начала хотелось бы описать то, что "Per compitalis ad astra" лично для меня значит. Наверное всё пошло ещё со школьных времён: школа, которую я заканчивал в последние годы моего обучения там была переведена в статус "гимназии", и, кроме умного названия, получила девиз, который весел во многих холах. Девиз был не очень оригинален - "per aspera ad astra" :) Тогда нам объяснили гордый смысл пословицы, но звучание мне понравилась и до сих пор помню.

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

В общем, тут я буду постить разные интересные ссылки, мысльи, сниппеты кода для совершенно разных платформ, языков и т.д.