суббота, 30 июня 2012 г.

Detlef Rössler – Mykene. Burg des Agamemnon

Книга из той же серии, что упомянутая раньше Kreta. Insel des Minos. Идём хронологически – сначала критяне, потом завоевавшие их минойцы, а дальше будут этруски из той же серии.
Скроена по тому же принципу, что предыдущая:

  1. Как раскопали
  2. Как жили цари
  3. Как велось хозяйство
  4. Как жил народ
  5. Каким богам поклонялись
  6. Как пришли завоеватели (в случае критян – минойцы, в случае минойцев – дорийцы) и всех завоевали

Sitecore: Database returns old value

Вы исправили какие-то данные в базе Sitecore, но на странице у вас то же самое? Значит, надо проверить следующие варианты:

  1. Вы исправили в master, но забыли опубликовать это в web. Перейдите в web и провериться.
  2. Вы изменили не во всех языках. Вернитесь в master и проверьте, выбрав другой флаг. Этот шаг очень часто забывают.
  3. Вы используете дурацкую систему кэширования и она возвращает вам старые значения.
Чтобы 1 и 2 портили вам как можно меньше крови - везде, где можно, заменяйте текстовые константы на внутренние ссылки sitecore. И тогда даже если элемент куда-нибудь переедет,  ссылка будет в порядке.

четверг, 28 июня 2012 г.

Tool: Notepad++ vs KomodoEdit

What is better - Notepad++ or Komodo Edit? I have been used Notepad++ for a long time, but what if the cross-platform Komodo Edit is better?

 I've downloaded and used for 60 days with Windows 7.

My verdict is: Komodo Edit has all of the pluses that Notepad++ has.

But there's 2 minuses of Komodo Edit:
  • It is loaded slowly. Cross-platform libraries aren't the fastest.
  • It isn't useful for modern Russian charsets. If you will make an HTML page with it, Safari will show you just ??????
So, I'm on Notepad++ still. But of course there aren't any Notepad++ for Unix.

Blog: Wordpress?

Работа над Yume Hikki идёт полным ходом. А значит, время подыскивать хостинг. Всё-таки это не эмулятор Радио-86 и raw-view с Google Code будет для него слишком медленным.


Blogspot не перестаёт радовать дизайном, удобством и стильностью. Но здесь только 10 статичных страниц и нет FTP-доступа.

Joomla для личного сайта вообще не годится. Её невозможно настроить двумя кликами.

А с Wordpress-ом у меня страшные ассоциации. На нём было сделано слишком много страшных, уродливых, совершенно неработоспособных сайтов. Поэтому уже первый взгляд на круглые кнопки его админки вселяет в меня ужас.

Выход один - преодолеть предубеждение. И пусть меня осудят на Луркморе, - но в умелых руках Wordpress будет слушаться. Ведь пользуется им Herb Sutter - а этот человек разбирается в надёжности.

Блогспот, в свою очередь, может похвастаться тем, что там одно время писал Линус Торвальдс. Записей маловато, зато можно заглянуть за плечо известному человеку:


 И сравнить с работой совсем другого человека (тоже IT):

Мне надоело подписывать договоры и платежки, решать хозяйственные вопросы, отвечать на полтораста электронных писем в день, 99% вопросов в которых находятся не в моей компетенции. В последние годы каждый вечер по 6 часов я работал почтальоном по переадресации писем, звонков и личных обращений, адресованных другим сотрудникам компании. В последние годы с ростом бизнеса моя почта приняла характер эпидемии, поэтому с сегодняшнего дня я отказываюсь и от своего корпоративного электронного почтового ящика – его будет проверять мой преемник.

среда, 20 июня 2012 г.

JavaScript: keydown event in Opera

Я уже писал про сайт-бродилку. Легко заметить, что в Opera он работает неправильно - если зажать стрелку, то главный герой сместится только на один шаг.

Почему так происходит? Можно ли это исправить?

Эксперименты показали, что если зажать кнопку со стрелкой, то Opera отловить как keydown только сам момент нажатия. Пока не отпустишь кнопку - события в JavaScript происходить не будет, оно будет перенаправлятсья на Scroll Bar.

Так что из Opera можно бродить только шаг за шагом. И отладчик в ней вызывается не как у людей.

Tool: Development tool

  • Firefox - Firebug - F12
  • Chrome - Included - F12
  • Safari - Included - F12
  • Internet Explorer - Included - F12
  • Opera - Dragonfly (now is included) - Ctrl+Shift+I (!!!)
 I don't know why.

понедельник, 18 июня 2012 г.

Cygwin: Делаем немыслимые вещи

Немыслимо интересные вещи на bash (кое-что и на Cygwin неплохо работает). На случай, если сайт слетит (Web 1.0, что поделать) - сохраните у себя.

Перепостил бы целиком, но Linux-ом пока не пользуюсь.

C#: Labels instead of numbers in String.Format

Can be labels used instead of numbers in String.Format?

No, they aren't by default. But it's possible by the very nice Scott Hanselman's extention method for it:

public static class FormattableObject
{
    public static string ToString(this object anObject, string aFormat)
    {
        return FormattableObject.ToString(anObject, aFormat, null);
    }
 
    public static string ToString(this object anObject, string aFormat, IFormatProvider formatProvider)
  {
       StringBuilder sb = new StringBuilder();
     Type type = anObject.GetType();
     Regex reg = new Regex(@"({)([^}]+)(})",RegexOptions.IgnoreCase);
        MatchCollection mc = reg.Matches(aFormat);
      int startIndex = 0;
     foreach(Match m in mc)
      {
           Group g = m.Groups[2]; //it's second in the match between { and }
           int length = g.Index - startIndex -1;
           sb.Append(aFormat.Substring(startIndex,length));
             
            string toGet = String.Empty;
            string toFormat = String.Empty;
         int formatIndex = g.Value.IndexOf(":"); //formatting would be to the right of a :
           if (formatIndex == -1) //no formatting, no worries
          {
               toGet = g.Value;
            }
           else //pickup the formatting
            {
               toGet = g.Value.Substring(0,formatIndex);
               toFormat = g.Value.Substring(formatIndex+1);
            }
 
           //first try properties
          PropertyInfo retrievedProperty = type.GetProperty(toGet);
           Type retrievedType = null;
          object retrievedObject = null;
          if(retrievedProperty != null)
           {
               retrievedType = retrievedProperty.PropertyType;
             retrievedObject  = retrievedProperty.GetValue(anObject,null);
           }
           else //try fields
           {
               FieldInfo retrievedField = type.GetField(toGet);
                if (retrievedField != null)
             {
                   retrievedType = retrievedField.FieldType;
                   retrievedObject = retrievedField.GetValue(anObject);
                }
           }
 
           if (retrievedType != null ) //Cool, we found something
          {
               string result = String.Empty;
               if(toFormat == String.Empty) //no format info
               {
                   result = retrievedType.InvokeMember("ToString",
                     BindingFlags.Public | BindingFlags.NonPublic |
                     BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase
                     ,null,retrievedObject,null) as string;
              }
               else //format info
              {
                   result = retrievedType.InvokeMember("ToString",
                     BindingFlags.Public | BindingFlags.NonPublic |
                     BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase
                     ,null,retrievedObject,new object[]{toFormat,formatProvider}) as string;
             }
               sb.Append(result);
          }
           else //didn't find a property with that name, so be gracious and put it back
            {
               sb.Append("{");
             sb.Append(g.Value);
             sb.Append("}");
         }
           startIndex = g.Index + g.Length +1 ;
        }
       if (startIndex < aFormat.Length) //include the rest (end) of the string
      {
           sb.Append(aFormat.Substring(startIndex));
       }
       return sb.ToString();
   }
}

It's faster then others. Use it with objects:
Person p = new Person();
string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}");

воскресенье, 17 июня 2012 г.

Blog: Chrome says, that vising the blog may harm your computer

Что делать, если Гугл говорит, что ваш сайт опасен, а в коде и плагинах появились загадочные и явно зашифрованные куски? И то тут, то там находишь зловещий eval(base64_decode.

Правильный ответ - грохнуть всё и восстановить из backup-а.

Если пытаешься убрать руками:

  • Никаких гарантий, что убрал всё
  • Рискуешь что-то поломать
  • Потратишь много времени - намного больше, чем уйдёт, если заливать те же статьи снова.

пятница, 15 июня 2012 г.

jQuery: "name.replace is not a function" error

It's a very widely known error, since jQuery 1.0 probably. I'll describe he solution en English, because there aren't any English manual for it too.

If you implement the OOP inheritance in JavaScript using this very famous code:

Object.prototype.Inherits = function(parent)
{
 if(arguments.length > 1)
  parent.apply(this, Array.prototype.slice.call(arguments,1));
 else
  parent.call(this);
}

Function.prototype.Inherits = function(parent)
{
 this.prototype = new parent();
 this.prototype.constructor = this;
}

fadeIn() and fadeOut() will cause this error :-(.

Anyway, this version of inheritance isn't so good at all, because it breaks JavaScript foreach-like for too, adding 1 more element "Inherits" to every array.

But what if you have to use it? Is there any way to fix?

Firstly, update this implementation to make it safe:

Object.prototype.Inherits = function(parent)
{
 if(!parent || typeof(parent.call) != "function")
  return;
 if(arguments.length > 1)
  parent.apply(this, Array.prototype.slice.call(arguments,1));
 else
  parent.call(this);
}

Function.prototype.Inherits = function(parent)
{
 this.prototype = new parent();
 this.prototype.constructor = this;
}

Then update jQuery.js (is you have a min version, replace it with full one). Find "getComputedStyle = function( elem, name ) {" and add after first 2 lines:

getComputedStyle = function( elem, name ) {
 var ret, defaultView, computedStyle, width,
  style = elem.style;
 //Add this:
 if(typeof(name) != "string")
  return name;
 //-----

And it will work fine :-)

среда, 13 июня 2012 г.

JavaScript: Receiving GET params of url

Как прочитать в JavaScript параметры, которые идут в URL сразу после ? и имеют вид key=value?

Внимательно вчитаться в образец и сделать ещё удобней:

$.extend({
 getUrlVars: function(){
  var vars = [], hash;
  var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
  var hashesLength = hashes.length;
  var i = 0
  while(i < hashesLength)
  {
   hash = hashes[i].split('=');
   vars.push(hash[0]);
   vars[hash[0]] = hash[1];
   i++;
  }
  return vars;
 },
 UrlVars : null,
 getUrlVar : function(key) {
  if(!key)
   return null;
  if(!this.UrlVars)
   this.UrlVars = this.getUrlVars();
  if(typeof(this.UrlVars[key]) == "undefined")
   return null;
  else
   return this.UrlVars[key];
 }
});

И теперь в любом месте вызываем $.getUrlVar('key') и получаем этот параметр. Если параметра не было - вернёт null.

вторник, 12 июня 2012 г.

Life: Программист с Кубы

David Conde, программист из Матансаса, Куба.

При этом - Куба, Иран, Сирия, Северная Корея и Судан (после последнего референдума неясно, весь, или только Северный) не имеют доступа к Sourceforge.

UnitTest: Что такое UnitTest?


Unit в UnitTest - это unit of work. То есть a use case, вызванный public method-ом и закончившийся result-ом. А result-ом может быть:
  1. Возвращаемое значение или Exception.
  2. Заметные изменения в системе. Заметные - значит, система после них работает по-другому. Например, добавление пользователя - это заметное изменение, потому что теперь под ним можно заходить.
  3. Вызов внешней системы, которую мы не можем контролировать во время теста. Например, файловая система, сеть, user threads, или любая другая зависимость, которую мы не можем контролировать и которая выполняется медленно.
Для 3-его типа нужно использовать mock-объекты. Для 1-2 - порвать все зависимости и проверять возвращаемое значение или состояние системы.

Вроде бы просто. Тем не менее, я понял, что отчего-то был уверен, что UnitTest-у и случая 1 достаточно. Хотя уже созданы обёртки для эмуляции файловой системы. Или вот эксперименты над Python

Subversion: How to use SVN with Google code

Как использовать SVN в Google Code? Вроде бы всё очень просто, но всё равно постоянно забываешь. Поэтому переведём замечательный мануал по настройке.

Более поздняя версия статьи публиковалась на Standalone автора. Standalone давно умер, а блог на wordpress жив до сих пор

Помечаю в основном для себя, так что картинок не будет.

Если нужно просто утащить последний release чужого проекта, а пароля вы не знаете - достаточно выполнить пункты 1-4, а потом регулярно делать Update. Google Code даёт read-only доступ всем желающим.

  1. Скачиваем последний TortoiseSVN.
  2. Устанавливаем его и перезагружаемся.
  3. Создаём папку, щёлкаем по ней правой кнопкой, жмём SVN Checkout.
  4. URL of repository = https://[ProjectName].googlecode.com/svn/trunk, где [ProjectName] - имя нужного проекта. Если вы его забыли, посмотрите URL его профиля на Google Code - он будет иметь вид http://code.google.com/p/[ProjectName]/.
  5. Идём в профиль нашего Google Code Hosting и смотрим там пароль.
  6. Потом, когда будем делать Commit, у нас спросят имя и пароль. Вводим то, что нам показали на шаге 5.
SVN ещё долго не умрёт - ведь он прост, как грабли.

Blog: Оптимальный формат

Оптимальный формат программистского блога очень хорошо формулирует английское слово papers. Т.е., наброски по какому-то вопросу, выложенные в сеть, чтобы не потерялись.

Нечто наподобие научных заявок на статьи. За некоторые из научных papers даже дают миллион - например, за известный paper Григория Перельмана.

суббота, 9 июня 2012 г.

Script: Play mp3 at background with JavaScript

Как сделать фоновую мелодию для веб-страницы из mp3-файла?

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

Так как стандарт очень долго хранил молчание насчёт фоновых мелодий, каждый браузер реализовывал его по-своему

Путаница тут страшная. <embed>, который часто советуют, например, требует от Firefox подгружать плагин. <audio> толком не поддерживается даже в IE. Да и вообще, ещё 2 года назад разные браузеры поддерживали разные форматы и табличка выглядела приблизительно так:

Browser Ogg MP3 WAV
FireFox 3.6+
Safari 5+
Chrome 6
Opera 10.5+
Internet Explorer 9 (beta)

Так что используйте JPlayer. Но иногда и он обваливается с ошибкой, что b.test - не является функцией :(.

Мне jPlayer не помог. Пришлось родить кроссплатформенное решение:

<bgsound src="sound/plan.mp3" loop="1">
<object id="opera_player1" data="sound/plan.mp3" type="application/x-mplayer2" width="0" height="0">
 <param name="filename" value="sound/plan.mp3">
 <param name="autostart" value="1">
 <param name="autoplay" value="1">
 <param name="hidden" value="1">
 <param name="playcount" value="9999">
</object>
<audio src="sound/plan.mp3" autoplay="autoplay" loop="loop">
<object id="webkit_player" data="sound/plan.mp3" type="application/x-mplayer2" width="0" height="0">
 <param name="filename" value="sound/plan.mp3">
 <param name="autostart" value="1">
 <param name="autoplay" value="1">
 <param name="hidden" value="1">>
</object>
</audio>

В IE я это, впрочем, пока не тестировал.

Первый блок - исключительно для Opera. Во втором не забываем loop="loop" - Chrome игнорирует Playcount.

Важно помнить, что object в Firefox не понимает loop. Так что просто ставьте большой playcount. А вот webkit-браузеры понимают его слишком хорошо и будут играть мелодию, даже когда вы покините сайт. Поэтому не забудьте убрать param loop во втором блоке.

И никогда не пытайтесь сгенерировать этот код через JavaScript. Такая реализация страшно тормозит даже на локальном компьютере.

А ещё не забывайте, что браузеры - умные создания и если поместить эти object'ы в скрытый блок (не важно, через, display: none; или visibility: hidden), Firefox их проигрывать не будет.

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

пятница, 8 июня 2012 г.

C++: Счастливый билет с длинной арифметикой

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

Сколько таких билетов всего? Какова вероятность встретить счастливый?

Алгоритм для этих вычислений в Википедии настолько ужасен, что народ с Хабра довольно быстро породил в комментариях огромное количество вариантов на C++, Perl и... SQL.

Я тоже не смог пройти мимо и написал считалку на C++ с арифметикой указателей и поддержкой длинной арифметики. При желании она может считать хоть 12-значные числа.

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

void inline count_lucky(unsigned int num_length_val)
{
  bool is_odd = (num_length_val % 2) > 0;
  unsigned int num_length = (is_odd) ? num_length_val - 1 : num_length_val;
  register unsigned int lucky=1, total=pow(10.0,(int)num_length), i;
  char *num = new char[num_length], *end=num+(num_length-1);
  register char *pos=end;
  for(i=0; i < num_length; i++)
    num[i]=0;
  while(pos>=num)
  {
     if((*pos)<9)
     {
        (*pos)++;
        if(pos==end)
        {
           if(is_lucky((unsigned char*)num, num_length))
             lucky++;
        }
        else
          pos++;
     }
     else
     {
       *pos=-1;
       pos--;
     }
  }
  printf("Lucky are %d of %d (%.3f%%)\n",(is_odd) ? lucky * 10 : lucky, (is_odd) ? total * 10 : total,((float)lucky/total*100));
  delete[] num;
}
bool inline is_lucky(unsigned char* items, unsigned int items_count)
{
  unsigned char val1=0, val2=0;
  unsigned int half = items_count >> 1;
  for(unsigned int i=0; i < half; i++)
  {
    val1 += items[i];
    val2 += items[items_count-1-i];
  }
  return (val1==val2);
}
Увы! Как показывает практика, уже на 7-значном билете тупой перебор, развёрнутый и оптимизированный компилятором, оказывается эффективней. Похоже, компилятор попросту выполняет сам большую часть явно заданных вычислений.
В этом легко убедиться, запустив тест хотя бы для 7 разрядов:

void inline count_lucky(unsigned int num_length);
bool inline is_lucky(unsigned char* items, unsigned int items_count);

int _tmain(int argc, _TCHAR* argv[])
{
  clock_t start = clock(), mine;
  count_lucky(7);
  mine=clock();
  printf("Mine time was: %d\n", (mine - start));
  count_lucky_fromwiki();
  printf("His time was: %d\n", (clock() - mine));
  return 0;
}

void count_lucky_fromwiki()
{
  int total=0,lucky=0;
  for(int i1=0; i1<10; i1++)
    for(int i2=0; i2<10; i2++)
      for(int i3=0; i3<10; i3++)
        for(int i4=0; i4<10; i4++)
          for(int i5=0; i5<10; i5++)
            for(int i6=0; i6<10; i6++)
              for(int i7=0; i7<10; i7++, total++)
                  if(i1+i2+i3 == i5+i6+i7)
                    lucky++;
  printf("Lucky are %d of %d (%.3f%%)\n",lucky,total,((float)lucky/total*100));
}

Человек уже не может обогнать машину :(. Слава роботам!

В качестве утешения - на 8 вложенный циклах валился компилятор из Borland C++ Builder 6.

Script: Replace in multiple files in Windows

A simple script to replace somethign in multiple files for Windows...

I've seen such a script for MS DOS Batch files. But I'd lost it and nobody could help me. I had ActivePerl, but the one-liner for replace in multiple files could be used only in *nix shell because of "/' things.

And finally I've got it for MS PowerShell. Here it is:

(Get-Content C:\Scripts\Test.txt)
| Foreach-Object {$_ -replace "\*", "@")
| Set-Content C:\Scripts\Test.txt

Thank you, Scripting Guy!

Phylosophy: ElseIf vs Switch, Python vs XSLT

There're ELSEIF operator in Python, and there aren't any SWITCH.

There're SWITCH operator in XSLT (aka <xsl:choose>), and there aren't any ELSEIF. There aren't any ELSE in it even.

C#: Difference between && and &

(I'll write in English, because this problem is widely asked.)

What's the difference between && and & or || and | in C#? In C++ it's the difference between logical and bitwise AND.

The most of its usage as connected with universality of if in C++. For example, if(a) in C++ never executes when:
  • a is false (because false = 0)
  • a is null (because null = 0)
  • a is a number <=0.

So, if(a == true) or  (a > 0) is useless in C++. if(a) is much shorter.


JavaScript implemented 1 more option:
  • a is an empty string ("")
C# is a strongly-typed langauge and the if in C# works different. It accepts only boolean values in scope.

So, the only difference between | an || and & and && in C# is the short-circuiting.

i.e.:


if(foo() && boo())


if foo() == false, boo() willn't be executed.


if(foo() & boo())


The foo() and boo() will be executed.


So, never write this way:


if(a != 0 & b / a > 100)


if a == 0, it will fall in Division by DivideByZeroException. The right variant is:


if(a != 0 && b / a > 100)

The same thing:

 if(item != null & item.isActive())

Never write this way. It has to be written like this:


 if(item != null && item.isActive())

среда, 6 июня 2012 г.

Book: Reinhard Witte - Kreta. Insel des Minos

Book: Reinhard Witte - Kreta. Insel des Minos (Der Kinderbuchvelag. Berlin. Illustrationen von Ludwig Winkler).


There're 2 more books from the same serie to read.

Subversion: GoogleCode против SVN

Очень мной любимый эмулятор Радио-86РК на JavaScript не нуждается даже в хостинге, чтобы работать. Проект, залитый через Mercurial, доступен для чтения всем, а значит, нет нужды заморачиваться с хостингом. Достаточно поставить ссылку напрямую в репозиторий.

Отличная идея! А что будет, если залить проект через SVN?

Увы - ничего хорошего. HTML-документ, загруженный через SVN, можно открыть из браузера... но сервер вернёт его как [text/plain], а не как [text/html]. И браузер покажет вместо страницы её исходный код.

К счастью, Google Code поддерживает репозитории 3 форматов: SVN, Git и Mercurial. Вот и выпал случай их протестировать.
  • SVN - работает неправильно
  • Git - работает правильно (TortoiseGit + msysgit, указать в настройках Tortoise c:\Program Files\Git\bin\ как источник git.exe). Лучше брать git.exe из официального git-scm.
  • Mercurial (он же Hg) - работает правильно
Google Code не любит SVN. Или SVN не любит Web.

Я решил пользоваться Mercurial. Почему?

Life: В полку Блогспота прибыло!

Не секрет, что социальная сеть становится социальной не потому, что там народу много, а потому, что там много наших знакомых. В этом прелесть ВКонтакте - только вышел, а там все свои. И это убивает LiveJournal - из 5-7 людей, которых с которыми я познакомился прежде, чем прочитал их блоги, сейчас активно пишет 1, а большинство поудалялось.

Сегодня на Blogspot-е пополнение - мой бывший одноклассник и просто хороший человек застолбил себе домен Venarg. Туда он будет перекидывать всё, что скопилось в "Черновиках" Гуглопочты. Да, в наше время тоже приходится писать на конвертах - нормального сервиса хранения заметок так и не придумано.

Так получилось, что в Рунете Блогспот стал логовом программистов и админов. Почему? Здесь странная френдлента, так что его читают люди, освоившие RSS и Google Reader. Здесь нет топа ЖЖ, а значит и не нужно LJPromo, чтобы накручивать посещаемость. Здесь пока нет ветвистых комментов, а значит, нормального срача не устроишь. Кандидаты в президенты и простые демократические журналисты не открывают здесь свои представительства. Тут даже ботов нет.

А люди - есть. И сегодня их стало на 1 человек больше.

Blog: Lexa Андреев о блоговодстве

В своё время очень не любил этого автора. Посмотрел недавно - вырос, существенно вырос.

Его статья о том, как перестать быть блоггером и начать жить - великолепна.

Reinhard Witte – Kreta. Insel des Minos

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

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

Интересно, что гомеровские цитаты я понимал легче, чем весь остальной текст.

вторник, 5 июня 2012 г.

Life: Couchsurfing

Итак, каучсёрфинг.

Самым лучшим считается Couchsurfing.org. Там расширенные профили, так что больше доверия. Ещё есть HospitalityClub и Global Freeloaders, они хуже.

Ещё есть сервис для эсперантистов.

Да, ещё - если вы не женщина, ваши шансы невелеки.