Oracle DB, Oracle APEX, Linux etc.

суббота, 19 декабря 2009 г.

Oracle APEX: 4.0 Early Adopter Now!

Ну, кто ещё не слышал, для тех новость: попробовать APEX 4.0 можно уже сейчас - http://tryapexnow.com/.

Всем приятного тестирования!
Читать далее

четверг, 3 декабря 2009 г.

Oracle DB 11gR2: New features, new bugs...

Недавно наткнулся на новоприобретённый баг, появившийся в Oracle DB 11.2.0.1: MERGE позволяет обходить ограничения-проверки (CHECK CONSTRAINTS), изменяя данные.

Вот тестовый скрипт:

drop table test_merge;

create table test_merge as select 1 m, 2 n from dual;

alter table test_merge add constraint test_merge_chk check (m <= n);

update test_merge set n = m - 1;

rollback;

select * from test_merge;

merge into test_merge tgt
using (select m, m-1 n from test_merge) src
on (tgt.m = src.m)
when matched then update set tgt.n = src.n;

commit;

select * from test_merge;


И вот результат:


Таблица создана.


Таблица изменена.

update test_merge set n = m - 1
*
ошибка в строке 1:
ORA-02290: нарушено ограничение целостности CHECK(XXX.TEST_MERGE_CHK)



Откат завершен.


M N
---------- ----------
1 2


1 строка объединена.


Фиксация обновлений завершена.


M N
---------- ----------
1 0


Как оказалось, этот баг не проявляется, если указать ветку WHEN NOT MATCHED в операторе MERGE. Так что обход бага в случае, когда нужно только обновить данные, но не вставлять новые, прост:
merge into test_merge tgt 
using (select m, m-1 n from test_merge) src
on (tgt.m = src.m)
when matched then update set tgt.n = src.n
when not matched then insert (m) values(null) where 1 = 0;

Главное, чтобы никто не "соптимизировал" этот MERGE, удалив "лишнюю" ветку. ;)

Читать далее

вторник, 1 декабря 2009 г.

Oracle APEX: Скрывать регион с отчётом без данных

Итак, у Вас есть регион с отчётом, и Вы не хотите показывать этот регион, если в отчёте нет данных (то есть, запрос отчёта не вернул ни одной строки). Вроде всё просто:

0. Указываете Static ID в атрибутах региона с отчётом. Например, HIDING_REGION.
1. В атрибутах отчёта вставляете следующее в When No Data Found Message:




И всё! :) Скрипт вставится в получаемую страницу (а значит, и выполнится) только тогда, когда запрос для отчёта не вернёт строк.
Читать далее

суббота, 7 ноября 2009 г.

Oracle Dates: Поиск неправильных дат

Возможно, для кого-то это будет открытием, но Oracle Database допускает вставку таких дат, как 127-ое мая или 51.51.2009. :) Данные, которые передаются во внутреннем формате, например, через OCI или EXP/IMP, не проверяются. Но потом при использовании этих данных в приложении возникают ошибки вроде ORA-01801. И эти данные нужно найти и исправить.

В этом топике на sql.ru было найдено несколько следующих решений для задачи поиска.

Итак, данные:
CREATE TABLE tmp(ID NUMBER PRIMARY KEY, d DATE);

declare
d date;
BEGIN
INSERT INTO tmp VALUES(1, to_date('01.12.1900', 'dd.mm.yyyy'));
INSERT INTO tmp VALUES(2, to_date('01.12.-1900', 'dd.mm.syyyy'));
dbms_stats.convert_raw_value(hextoraw('7764057f7f77aa'), d);
INSERT INTO tmp VALUES(3, d);
dbms_stats.convert_raw_value(hextoraw('F7640d01010101'), d);
INSERT INTO tmp VALUES(4, d);
COMMIT;
end;/


Следующие решения основываются на формате хранения даты в Oracle Database. Если рассмотреть побайтно дамп даты, то видны следующие компоненты:
Номер байта слева  - Компонент
в функции DUMP

1 - столетие + 100
2 - год в столетии + 100
3 - месяц
4 - день
5 - час + 1
6 - минута + 1
7 - секунда + 1


Решение "в лоб", через указания границ для компонентов дампа даты:
WITH t AS 
(SELECT id, DUMP(d) AS dmp
FROM tmp),
comps as
(SELECT id, dmp
, substr(dmp, instr(dmp, ':', 1, 1)+1, instr(dmp, ',', 1, 1) - instr(dmp, ':') - 1) - 100 cc
, substr(dmp, instr(dmp, ',', 1, 1)+1, instr(dmp, ',', 1, 2) - instr(dmp, ',', 1, 1) - 1) - 100 yy
, substr(dmp, instr(dmp, ',', 1, 2)+1, instr(dmp, ',', 1, 3) - instr(dmp, ',', 1, 2) - 1) mm
, substr(dmp, instr(dmp, ',', 1, 3)+1, instr(dmp, ',', 1, 4) - instr(dmp, ',', 1, 3) - 1) dd
, substr(dmp, instr(dmp, ',', 1, 4)+1, instr(dmp, ',', 1, 5) - instr(dmp, ',', 1, 4) - 1) - 1 hh
, substr(dmp, instr(dmp, ',', 1, 5)+1, instr(dmp, ',', 1, 6) - instr(dmp, ',', 1, 5) - 1) - 1 mi
, substr(dmp, instr(dmp, ',', 1, 6)+1) - 1 ss
from t)
select id, dmp from comps
where cc not between -47 and 99
or (yy not between -99 and 99 or (cc = -47 and yy < -12))
or mm not between 1 and 12
or (mm in (1, 3, 5, 7, 8, 10, 12) and dd not between 1 and 31)
or (mm in (4, 6, 9, 11) and dd not between 1 and 30)
or ((mod(cc, 4) = 0 or (mod(cc, 4) <> 0 and mod(yy, 4) = 0)) and mm = 2 and dd not between 1 and 29)
or (mod(cc, 4) <> 0 and mod(yy, 4) <> 0 and mm = 2 and dd not between 1 and 28)
or hh not between 0 and 23
or mi not between 0 and 59
or ss not between 0 and 59;


Решение, использующее функцию IsDateValid, автор функции Elic:
create or replace function IsDateValid(aDate varchar2, aFormat varchar2) return int
is
d date;
begin
d := to_date(aDate, aFormat);
return 1;
exception
when others then
if sqlcode between -1899 and -1800 then
return 0;
else
raise;
end if;
end IsDateValid;
/

WITH t AS
(SELECT id, DUMP(d) AS dmp
FROM tmp),
comps as
(SELECT id, dmp
, substr(dmp, instr(dmp, ':', 1, 1)+1, instr(dmp, ',', 1, 1) - instr(dmp, ':') - 1) - 100 cc
, substr(dmp, instr(dmp, ',', 1, 1)+1, instr(dmp, ',', 1, 2) - instr(dmp, ',', 1, 1) - 1) - 100 yy
, substr(dmp, instr(dmp, ',', 1, 2)+1, instr(dmp, ',', 1, 3) - instr(dmp, ',', 1, 2) - 1) mm
, substr(dmp, instr(dmp, ',', 1, 3)+1, instr(dmp, ',', 1, 4) - instr(dmp, ',', 1, 3) - 1) dd
, substr(dmp, instr(dmp, ',', 1, 4)+1, instr(dmp, ',', 1, 5) - instr(dmp, ',', 1, 4) - 1) - 1 hh
, substr(dmp, instr(dmp, ',', 1, 5)+1, instr(dmp, ',', 1, 6) - instr(dmp, ',', 1, 5) - 1) - 1 mi
, substr(dmp, instr(dmp, ',', 1, 6)+1) - 1 ss
from t)
select id, dmp
from comps
where IsDateValid( to_char(dd, '900.')||to_char(mm, '900.')||to_char(cc*100+yy, '90000')||to_char(hh, '900')||':'||to_char(mi, '900')||':'||to_char(ss, '900')
, 'DD.MM.SYYYY HH24:MI:SS') = 0;


Решение, использующее собственную функцию IsDateDumpValid:
create or replace function IsDateDumpValid(p_date date)
return integer
is
l_date_str varchar2(32);
l_try_date date;
begin
select to_char(dd, '900.')||to_char(mm, '900.')||to_char(cc*100+yy, '90000')||to_char(hh, '900')||':'||to_char(mi, '900')||':'||to_char(ss, '900')
into l_date_str
from (SELECT substr(dmp, instr(dmp, ':', 1, 1)+1, instr(dmp, ',', 1, 1) - instr(dmp, ':') - 1) - 100 cc
, substr(dmp, instr(dmp, ',', 1, 1)+1, instr(dmp, ',', 1, 2) - instr(dmp, ',', 1, 1) - 1) - 100 yy
, substr(dmp, instr(dmp, ',', 1, 2)+1, instr(dmp, ',', 1, 3) - instr(dmp, ',', 1, 2) - 1) mm
, substr(dmp, instr(dmp, ',', 1, 3)+1, instr(dmp, ',', 1, 4) - instr(dmp, ',', 1, 3) - 1) dd
, substr(dmp, instr(dmp, ',', 1, 4)+1, instr(dmp, ',', 1, 5) - instr(dmp, ',', 1, 4) - 1) - 1 hh
, substr(dmp, instr(dmp, ',', 1, 5)+1, instr(dmp, ',', 1, 6) - instr(dmp, ',', 1, 5) - 1) - 1 mi
, substr(dmp, instr(dmp, ',', 1, 6)+1) - 1 ss
from (select dump(p_date) dmp from dual) t) comps;
begin
l_try_date := to_date(l_date_str, 'DD.MM.SYYYY HH24:MI:SS');
return 1;
exception
when others then
if sqlcode between -1899 and -1800 then
return 0;
else
raise;
end if;
end;
end;
/
select id from tmp where IsDateDumpValid(d) = 0;


Элегантное решение от Elic'а, использующее переполнение разрядов в дате. (К сожалению, им нельзя поймать случаи неправильных годов в датах.)
select id from tmp
where d <> d + numtodsinterval(trunc(1e8/3),'second') - numtodsinterval(trunc(1e8/3),'second');

Читать далее

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

ApEx: обновление переменных в сессии без сабмита

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

Чтобы ассинхронно обновить значение переменной в JavaScript, можно воспользоваться классом apex.ajax.ondemand:

/* Сохранить в сессии значение элемента асинхронно */
function saveThisItemAsync(p){
/* создаём пустой вызов к БД - без имени процесса и callback-функции */
var aget = new apex.ajax.ondemand();
/* добавляем нашу переменную-элемент */
aget.ajax.add(p.id, $v(p));
aget._get();
}


Для синхронного обновления можно воспользоваться им же, просто вызвав ajax.get(). :) Вот JS-функция для синхронного обновления значения переменной в сессии:

/* Сохранить в сессии значение элемента */
function saveThisItem(p){
var get = new apex.ajax.ondemand();
get.ajax.add(p.id, $v(p));
get.ajax.get();
}


Если кто-то хочет посмотреть, что там внутри apex.ajax.ondemand находится - пожалуйста:

apex.ajax = {
...
ondemand : function (pWidget,pReturn){
var that = this;
this.ajax = new htmldb_Get(null,$x('pFlowId').value,'APPLICATION_PROCESS='+pWidget,0);
this._get = _get;
this._set = _set;
this._return = !!pReturn?pReturn:_return;
return;
function _get(pValue){
that.ajax.GetAsync(that._return);
}
function _set(pValue){}
function _return(pValue){}
}
}

Таким образом, всё, что можно делать с htmldb_Get, можно делать и с apex.ajax.ondemand. Просто иногда короче. :)
Спасибо за информацию для размышления и написания этого поста kvad на sql.ru: Oracle APEX за этот пост
.


Читать далее

пятница, 2 октября 2009 г.

SyntaxHighlighter: Подсветка кода в блоге

В определённый момент мне захотелось добавить подсветку кода в блог. После непродолжительных поисков я наткнулся на SyntaxHighlighter. Всё что нужно, чтобы использовать его, это...
  • Скачать с сайта набор скриптов и таблиц стилей и загрузить их на какой-либо хостинг ИЛИ не скачивать, а воспользоваться бесплатным хостингом проекта.
  • Изменить HTML-макет Вашего блога, добавив в тег HEAD библиотеку jQuery, стили и скрипты SyntaxHighlighter. Вариат первый - Вы загрузили всё это на хостинг, тогда указывайте соответствующие пути к файлам на хостинге. Например, для кистей (так называются классы с определённым синтаксисом содержащегося кода) Shell, JavaScript, XML/HTML, SQL:
    <script type='text/javascript' src="http://path/to/jquery" /> 
    <link href='http://path/to/SyntaxHighlighter/styles/shCore.css' rel='stylesheet' type='text/css'/>
    <link href='http://path/to/SyntaxHighlighter/styles/shThemeRDark.css' rel='stylesheet' type='text/css'/>
    <script src='http://path/to/SyntaxHighlighter/scripts/shCore.js' type='text/javascript'/>
    <script src='http://path/to/SyntaxHighlighter/scripts/shBrushBash.js' type='text/javascript'/>
    <script src='http://path/to/SyntaxHighlighter/scripts/shBrushJScript.js' type='text/javascript'/>
    <script src='http://path/to/SyntaxHighlighter/scripts/shBrushXml.js' type='text/javascript'/>
    <script src='http://path/to/SyntaxHighlighter/scripts/shBrushSql.js' type='text/javascript'/>
    

    Полный список кистей находится здесь.

    А вот вариант с использованием бесплатного хостинга проекта:
    <script src='http://www.google.com/jsapi' type='text/javascript'/>
    <script type='text/javascript'> google.load('jquery', '1');</script>
    <link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
    <link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeRDark.css' rel='stylesheet' type='text/css'/>
    <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'/>
    <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js' type='text/javascript'/>
    <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'/>
    <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'/>
    <script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'/>
    


    За тему расцветки синтаксиса отвечает файл shThemeRDark.css. Если хотите изменить тему расцветки - выберите подходящую из этого списка.
  • Нужно добавить запуск SyntaxHighlighter при полной загрузке документа (страницы). Для этого я использую jQuery, тег HEAD и следующий скрипт:
    $(document).ready(
    function runSyntaxHighlighter(){
    SyntaxHighlighter.config.bloggerMode = true;
    SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf';
    SyntaxHighlighter.all();
    }
    );
    

    Если Вы пользуетесь не бесплатным хостингом проекта, а своим собственным, то замените путь http://alexgorbatchev.com/pub/sh/current на соответствующий.
  • И теперь, чтобы включить для кода подсветку, заключите его в теги:
    <pre class="brush: brushName">ваш код</pre>
    

Всё. :) Есть ещё масса настроек, возможностей и прочего, что я советую прочитать на сайте этого проекта.

ИЗМ: Небольшое изменение в блоге.


Читать далее

ApEx: Popup Key LOV, null key и небольшой баг

Недавно наткнулся на подобный баг: есть страница, на ней есть элемент типа Popup Key LOV (выбор нужного значения из списка во всплывающем окне). Если разрешить отображать строку с ключом NULL (aka return value) и, например, описанием "Это строка с ключом NULL", то обработчик для щелчка по ней во всплывающем окне будет следующим:
<a href="javascript:passBack('');">Это строка с ключом NULL</a>

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

Решение: 0. Поменять запрос LOV, добавив в него
select 'Это строка с ключом NULL' d, null r from dual union all

1. Добавить Computation на страницу, где можно выбирать значение с ключом NULL, для элемента, основанного на этом LOV, заменяя его значение либо функцией:
FUNCTION f_lov_null(p_item_name IN VARCHAR2) Return VARCHAR2 IS
BEGIN
IF v(p_item_name) = 'undefined' THEN
Return(NULL);
ELSE
return(v(p_item_name));
END IF;
END;

либо каждый раз конкретно:
nullif(lower(v('YOUR_ITEM_NAME')), 'undefined')


Мне пока хватило варианта с nullif.

Читать далее

вторник, 15 сентября 2009 г.

ApEx: Асинхронность и часть страницы

Вначале отписал на sql.ru, дублирую здесь.

У htmldb_Get().get второй и третий параметры - теги, которые ограничивают необходимую часть результата (по умолчанию "<!--START-->" и "<!--END-->"). У htmldb_Get().getAsync параметр всего один - функция обратного вызова. А мне нужны и асинхронность, и часть результата, ограниченная определёнными тегами. И поскольку нигде я подобной штуки не нашёл, то сделал сам. Пишу здесь, авось кому-то пригодится.

Итак, у нас есть страница M, в которой будем через AJAX асинхронно обновлять HTML-код региона с id=RESULT_REGION (в принципе, обновлять можем любой не-input DOM-элемент, у которого есть атрибут innerHTML). В качестве содержимого для этого региона берём что-нибудь со страницы N, ограниченное тегами "<!--START_X-->" и "<!--END_X-->".

У htmldb_Get есть метод getPartial, который возвращает часть ответа между двумя тегами (первый и второй параметры метода). Но этот метод работает с полем htmldb_Get.response, которое при вызове GetAsync не заполняется. Поэтому, чтобы не писать собственный Trim для ответов сервера, а использовать уже готовый, надо присваивать этому полю p.response.

Ниже идёт JS-код из заголовка страницы M:
<script type="text/javascript">
var get;

function doIt(){
get = new htmldb_Get(null, $v('pFlowId'),null,N /*Номер страницы с нужным содержимым*/ );
get.GetAsync(f_AsyncReturn);
};

function f_AsyncReturn(){
if (p.readyState==4) {
/*Присваиваем текст ответа нашей переменной get*/
get.response = p.responseText;
/*Присваиваем содержимому нужного элемента часть ответа между двух тегов*/
$x('RESULT_REGION').innerHTML=get.getPartial("", "");
get = null;
}
}
</script>


Полезные ссылки по теме: примеры Карла Бэкстрома по htmldb_Get.get() и htmldb_Get.GetAsync(), описание XMLHttpObject на w3schools.

UPD: Здесь kvad предложил улучшенный вариант, чтобы уйти от возможных проблем с переменной get при множественных асинхронных запросах. Привожу его ниже с небольшими изменениями:

/*В функцию добавлены параметры - регион и страница, которую запрашиваем*/
/*Кроме того, я добавил параметры тегов, с которого и по который считывать страницу*/
function doIt(frame, page, from_tag, to_tag){
var get;
get = new htmldb_Get(null, $v('pFlowId'),null,page);
get.p = get.GetAsync(function(){f_AsyncReturn(get, frame, from_tag, to_tag)});
};

function f_AsyncReturn(get, frame, from_tag, to_tag){
if (get.p && get.p.readyState==4) {
get.response = get.p.responseText;
$x(frame).innerHTML = get.getPartial(from_tag, to_tag);
get = null;
}
}

Читать далее

вторник, 8 сентября 2009 г.

ApEx 3.2.1: Что-то не срослось...

Поставил ApEx 3.2.1 дома. По пока непонятным причинам пришлось удалять 3.2 и ставить патч начисто. Неясными в руководстве по установке оказались два момента:
0. Нигде не сказано, что вначале нужно остановить экземпляр и запустить его уже в режиме апгрейда. Без этого лично я на Oracle 11.1.0.6 for Linux x86 получаю ORA-39702. Ладно, домашний сервер остановить не проблема, но вот зачем - вопрос открыт. По-моему, достаточно просто отключить HTTP-сервер, чтобы убрать пользовательские сеансы.
1. Запускаю apxpatch - отрабатывает без проблем. Запускаю apxldimg - всё отлично. Запускаю прослушиватель, HTTP-сервер, захожу в ApEx - в углу всё та же версия 3.2.0.00.27. То есть, патч не применился. Что не так - разберусь уже позже.

UPD: Может быть, всё из-за того, что я скачивал полный ApEx 3.2.1, а не патчсет с Металинка. Тем не менее, apxpatch в нём был, а значит, должен был отработать нормально.

PS: По прошлой заметке - к сожалению, всё оказалось плохо. Скрипты шапки намертво прописаны в WWV_FLOW_UTILITIES, потому пользуюсь внешними JS-библиотеками через связку Substitution strings+Templates.
Читать далее

пятница, 4 сентября 2009 г.

ApEx и автоматическое добавление в HEAD страницы скриптов и стилей

На sql.ru я уже задал этот вопрос, но решил написать и здесь.

Чего хочется: чтобы Апекс, генерируя страницу, автоматом дописывал в <HEAD> нужные либы и таблицы стилей.

Где-то тут:

<head>
<title>Edit Page Template</title>
<link type="image/x-icon" href="/i/favicon.ico" rel="icon"/>
<link type="image/x-icon" href="/i/favicon.ico" rel="shortcut icon"/>
<script type="text/javascript" src="/i/javascript/apex_ns_3_1.js"></script>
<script type="text/javascript" src="/i/javascript/apex_3_1.js"></script>
<script type="text/javascript" src="/i/javascript/apex_get_3_1.js"></script>
<script type="text/javascript" src="/i/javascript/apex_builder.js"></script>
<script type="text/javascript"></script>
<link type="text/css" href="/i/css/apex_3_1.css" rel="stylesheet"></link>
<script type="text/javascript"></script>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<link type="text/css" href="/i/css/apex_builder_3_1.css" rel="stylesheet"></link>
<!-- То, что хочу подключить -->
</head>


Знаю, что дока говорит: подключайте скрипты и CSS через шаблоны страниц либо через атрибут HTML Header. Используя строки подстановки, можно всё это поддерживать в актуальном состоянии при изменениях. Знаю, что ещё можно создать страницу #0, на ней создать HTML-регион, в него вписать нужное.

Но хочется простого инсёрта или вызова процедуры, куда передать "jquery-1.3.2.js" и путь в XML DB. :) Откуда-то же тянет Апекс всё это дело в заголовок каждой страницы...

После поисков в таблицах и пакетах наткнулся в заврапированном теле WWV_FLOW_UTILITIES на такое:

1G_PAGE_HTML_HEAD:
1<link rel="stylesheet" href=":
1css/apex_3_1.css" type="text/css" /><!--[if IE]><link rel="stylesheet" href=":
1css/apex_ie_3_1.css" type="text/css" /><![endif]--><script src=":
1javascript/apex_ns_3_1.js" type="text/javascript"></script><script src=":
1javascript/apex_3_1.js" type="text/javascript"></script><script src=":
1javascript/apex_get_3_1.js" type="text/javascript"></script>:


Хардкод налицо. :( Тем не менее, есть ма-а-аленькая надежда, что на дело влияет не он. Вечерком попробую скопировать и анврапнуть flowu.plb, поменять этот хардкод, добавив к нему jQuery UI, и установить заново. Если получится, и Апекс будет возвращать страницы с нужным мне хэдером - плохо. Править пакеты на тесте и продакшнах, понятное дело, мне никто не даст, потому такой способ подойдёт только на девелоперской или домашней базе побаловаться.

Читать далее

среда, 17 июня 2009 г.

Oracle APEX: 2.x -> 3.2

Чтоб учить быстрее, апгрейднул домашнюю версию до рабочей. Если всё делать по руководству - проблем не возникает...

Единственный совет, который могу дать, состоит в том, чтобы распаковывать загруженный с OTN архив в /tmp под *никсами и C:\TEMP под виндой. Есть там одно запоздалое уточнение в руководстве, что они под этими папками в скриптах подразумевают путь, куда распаковали архив с APEX.
Читать далее

воскресенье, 24 мая 2009 г.

Pidgin - вроде всё хорошо, но...

Жутко не хватает возможности по комбинации клавиш изменять раскладку уже написанного текста (всего или части), как это делает, например, плагин Миранды Switch Layout. Потому я решился, во-первых, на изучение того, как пишутся плагины для Пиджина на C, и, во-вторых, на написание подобного плагина в свободное время. Раз пишу для себя, то затягивать особо не буду... ;)
Читать далее

понедельник, 18 мая 2009 г.

Установка Mono 2.4 и MonoDevelop 2.0 в deb-дистрибутивах by Jan Dzik

Предисловие от автора перевода (т.е., меня)


Как я уже писал раньше, в определённый момент мне захотелось разобраться с программированием на C# под *никсами. Прочитав про Mono, я полез в deb-репозитории, но ни в lenny, ни в squeeze на тот момент не было даже Mono 2.2, что уж говорить про свеженькую стабильную 2.4... Стало ясно, что придётся собирать из исходников. Поискав в Сети, я наткнулся на статью, которая сильно облегчила процесс установки. И - да, она была на человеческом английском. На русском, к сожалению, я подобного не обнаружил, потому решил сделать этот перевод - неточный, но близкий к тексту и передающий основное. Возможно, он будет полезным кому-то с аллергией на английский. ;) Только учтите, что некоторые мысли в переводе пропущены как незначительные...

Введение от автора статьи


С грустью вынужден сказать, что в последние версии Убунту и Дебиан не включены самые последние пакеты Mono - предпочтение отдано стабильности, а не новизне версии. Хотя, судя по списку рассылки ответственных за пакеты, это не лёгкая задача - поддерживать пакеты для Mono и всего, что с ней связано. (Прим. пер.: род у Mono женский, поскольку это среда выполнения)

Я - чайник, когда дело касается последнего писка и новейших версий, особенно если над этим придётся подумать, как в случае с Mono. В последний стабильный релиз Mono, версию 2.4 входит масса таких новых особенностей, как интерактивная оболочка C#, расширения SIMD и улучшенная поддержка возможностей C# 3/.NET 3.5, таких как методы расширения и LINQ. Смотрите информацию по релизу здесь. Я разочаровался, когда обнаружил, что самая последная версия deb-пакетов Mono, которые я мог найти, - это версия 2.0.1 в неофициальных репозиториях.

Что же делать? Скачивать исходники, настраивать и собирать, чтобы запускать параллельно с версией Mono, установленной на моём десктопе с Убунту (среда выполнения Mono в Убунту всегда устанавливается по умолчанию, даже если запускать ОС с компакта, потому что она необходима для запуска .NET-приложений, как F-Spot, который стал стандартом на рабочем столе GNOME). Оказалось, что это не так сложно, как кажется.

Шаг 1: Установка требуемого для компиляции.


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

Откройте консоль и введите следующее для установки компилятора gcc и заголовочных файлов для разработки, необходимых для компиляции компиляторов Mono, инструментов и набора библиотек. (Прим. пер.: автор не боится тавталогий, приходится терпеть и переводить as is.)

user@system$> sudo apt-get update
user@system$> sudo apt-get install build-essential autoconf automake \
bison flex gtk-sharp2-gapi boo gdb valac libfontconfig1-dev \
libcairo2-dev libpango1.0-dev libfreetype6-dev libexif-dev \
libjpeg62-dev libtiff4-dev libgif-dev zlib1g-dev libatk1.0-dev \
libglib2.0-dev libgtk2.0-dev libglade2-dev libart-2.0-dev \
libgnomevfs2-dev libgnome-desktop-dev libgnome2-dev libgnomecanvas2-dev \
libgnomeui-dev libgnomeprint2.2-dev libgnomeprintui2.2-dev \
libpanel-applet2-dev libnautilus-burn-dev librsvg2-dev \
libgtkhtml3.14-dev libgtksourceview2.0-dev libgtksourceview-dev \
libvte-dev libwnck-dev libnspr4-dev libnss3-dev libxul-dev \
libwebkit-dev libvala-dev

Это должно установить всё необходимое ПО и заголовочные файлы, необходимые для компиляции.

Шаг 2: Загрузка архивов с исходниками mono 2.0


Отправляйтесь на http://ftp.novell.com/pub/mono/sources-stable/. Вам не нужен каждый архив в списке. В этом руководстве мы будем использовать mono-2.4.tar.bz2, libgdiplus-2.4.tar.bz2, gluezilla-2.4.tar.bz2, xsp-2.4.tar.bz2, mono-tools-2.4.tar.bz2, gecko-sharp-2.0-0.13.tar.bz2, mono-debugger-2.4.tar.bz2, mono-addins-0.4.zip, gtk-sharp-2.12.8.tar.bz2, gnome-sharp-2.20.1.tar.bz2, gnome-desktop-sharp-2.20.1.tar.bz2, webkit-sharp-0.2.tar.bz2.

Также зайдите на сайт MonoDevelop и скачайте исходники для monodevelop 2.0: monodevelop-2.0, monodevelop-debugger-mdb-2.0, monodevelop-debugger-gdb-2.0, monodevelop-database-2.0, monodevelop-java-2.0, monodevelop-vala-2.0.

Создайте папку в своём домашнем каталоге для разархивированных исходников, например, src/mono-2.4.

user@system$> mkdir -p src/mono-2.4; cd src/mono-2.4

Распакуйте скачанные архивы в эту папку.

Шаг 3: Подготовка параллельного окружения


Сейчас самое время подготовить нашу систему для двух сред выполнения Mono. Хорошей идеей будет установить всё связанное с Mono 2.2 по определённому пути вроде /opt/mono-2.2 и создать скрипт, который будет загружать окружение, используя версию, размещённую по этому пути, вместо обычной установки. Так Вы не превратите уже установленную у Вас версию mono в нечто нестабильное или неработающее.

Теперь напишем скрипт, который будет загружать окружение mono-2.4.

user@system$> cat > mono-2.4-environment
#!/bin/bash
MONO_PREFIX=/opt/mono-2.4
GNOME_PREFIX=/opt/gnome-2.4
export DYLD_LIBRARY_PATH=$MONO_PREFIX/lib:$DYLD_LIBRARY_PATH
export LD_LIBRARY_PATH=$MONO_PREFIX/lib:$LD_LIBRARY_PATH
export C_INCLUDE_PATH=$MONO_PREFIX/include:$GNOME_PREFIX/include
export ACLOCAL_PATH=$MONO_PREFIX/share/aclocal
export PKG_CONFIG_PATH=$MONO_PREFIX/lib/pkgconfig:$GNOME_PREFIX/lib/pkgconfig
PATH=$MONO_PREFIX/bin:$PATH
PS1="[mono-2.4] \w @ "
[Нажмите Ctrl+D, чтобы закончить файл]
user@system$> sudo mv mono-2.4-environment /usr/local/bin
user@system$> sudo chmod +x /usr/local/bin/mono-2.4-environment
И создадим второй скрипт, который будет загружать окружение и выполнять переданные аргументы.
user@system$> cat > mono-2.4
#!/bin/bash
MONO_PREFIX=/opt/mono-2.4
GNOME_PREFIX=/opt/gnome-2.4
export DYLD_LIBRARY_PATH=$MONO_PREFIX/lib:$DYLD_LIBRARY_PATH
export LD_LIBRARY_PATH=$MONO_PREFIX/lib:$LD_LIBRARY_PATH
export C_INCLUDE_PATH=$MONO_PREFIX/include:$GNOME_PREFIX/include
export ACLOCAL_PATH=$MONO_PREFIX/share/aclocal
export PKG_CONFIG_PATH=$MONO_PREFIX/lib/pkgconfig:$GNOME_PREFIX/lib/pkgconfig
PATH=$MONO_PREFIX/bin:$PATH

exec "$@"
[Нажмите Ctrl+D, чтобы закончить файл]

user@system$> sudo mv mono-2.4 /usr/local/bin
user@system$> sudo chmod +x /usr/local/bin/mono-2.4
Загрузите окружение:
user@system$> source mono-2.4-environment
После этого пользовательское приглашение в консоли изменится на [mono-2.4], показывая, что Вы работаете в окружении Mono 2.4.
[mono-2.4] ~ @ cd ~/src/mono-2.4
Прим.пер.: дальше автор обращает внимание на то, что до конца установки мы будем работать в этом окружении, но пользовательское приглашение он будет писать старое - user@system$>; в переводе всё так же.

И теперь создадим папку, куда всё будет устанавливаться:
user@system$> sudo mkdir -p /opt/mono-2.4

Шаг 4: libgdiplus


Перед компиляцией основных исходников Mono нам надо скомпилировать libgdiplus, если мы хотим пользоваться тем, что есть в пространстве имён System.Drawing.
user@system$> cd libgdiplus-2.4
user@system$> ./configure --prefix=/opt/mono-2.4 --with-pango
Скрипт configure должен вывести что-то вроде такого:
---
Configuration summary

* Installation prefix = /opt/mono-2.4
* Cairo = 1.8.0 (system)
* Text = pango
* EXIF tags = yes
* Codecs supported:

- TIFF: yes
- JPEG: yes
- GIF: yes
- PNG: yes
NOTE: if any of the above say 'no' you may install the
corresponding development packages for them, rerun
autogen.sh to include them in the build.

---
Если по какой-нибудь опции будет стоять "no" ("нет"), тогда у Вас отсутствует что-то зависимое. Вы можете использовать Synaptic, чтобы найти соответствующий пакет для установки. Тем не менее, эти зависимости не обязательны. (Прим. пер.: хотите — устанавливайте, хотите — нет.)

Пришло время собрать исходники:
user@system$> make
user@system$> sudo make install

Шаг 5: mono-2.4


Самое время скомпилировать основные исходники mono, которые дадут нам, кроме всего прочего, компиляторы C# и базовые библиотеки классов.
user@system$> cd ../mono-2.4
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
Проверим, что компиляторы mono установились:
user@system$> which gmcs
Должно вывести: /opt/mono-2.4/bin/gmcs.

Ваша существующая установка mono будет сохранена, раз всё устанавливается в папку /opt/mono-2.4.

Шаг 6: gtk+ и gnome


На очереди компиляция библиотек, необходимых для среды рабочего стола, они зовутся gtk-sharp, gnome-sharp, gnome-desktop-sharp, gtksourceview-sharp.
user@system$> cd ../gtk-sharp-2.12.8
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../gnome-sharp-2.20.1
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../gnome-desktop-sharp-2.20.1
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install

Шаг 7: gluezilla, gecko-sharp, webkit-sharp


Теперь давайте скомпилируем библиотеки для встраивания html-движков gecko и webkit.
user@system$> cd ../gluezilla-2.4
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../gecko-sharp-2.0-0.13
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../webkit-sharp-0.2
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install

Шаг 8: собираем Mono.Addins


Пора скомпилировать библиотеки Mono.Addins:
user@system$> cd ../mono-addins-0.4
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install

Шаг 9: собираем инструменты Mono


Теперь время скомпилировать изумительную коллекцию инструментов Mono, включающую в себя Gendarme (прим. пер.: утилита для поиска часто встречающихся проблем в .NET-приложениях и библиотеках) и GSharp (интерактивную C#-оболочку).
user@system$> cd ../mono-tools-2.4
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
Этим Вы установили утилиту gsharp. Проверьте:
user@system$> gsharp
Немного поиграйтесь с командами на C#. :) Введите quit;, когда закончите.

Шаг 10: собираем Mono XS


Это действительно очень полезная вещь. XSP - это автономный веб-сервер, написанный на C#. Его можно запустить из командной строки или даже встроить в Ваше приложение, ссылаясь на сборку Mono.WebServer.
user@system$> cd ../xsp-2.4
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install

Шаг 11: собираем Mono Debugger


Подбираясь ближе к концу нашего сеанса компиляции, давайте соберём отладчик mono, который позволит нам отлаживать наш несовершенный код.
user@system$> cd ../mono-debugger-2.4
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install

Шаг 12: собираем MonoDevelop 2.0


А теперь вопрос: чем это всё будет без хорошей среды разработки? MonoDevelop 2.0 наконец-то даст нам поддержку отладчика!
user@system$> cd ../monodevelop-2.0
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../monodevelop-debugger-mdb-2.0
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../monodevelop-debugger-gdb-2.0
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../monodevelop-database-2.0
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../monodevelop-java-2.0
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
user@system$> cd ../monodevelop-vala-2.0
user@system$> ./configure --prefix=/opt/mono-2.4
user@system$> make
user@system$> sudo make install
И... Это всё. У Вас теперь есть работающее окружение mono-2.4 и превосходная среда разработки для работы с ним.

Вы можете запустить любую .NET-программу следующим образом:
user@system$> mono-2.4 PATH_TO_THE_PROGRAM_AND_ITS_ARGUMENTS_IF_ANY
ЗАМЕЧАНИЕ! Это будет работать только для shell-скриптов, которые обычно идут вместе с mono-приложениями. Если Вы заглянете в /opt/mono-2.4/bin/monodevelop, то увидите, что это просто shell-скрипт, который, в сущности, выполняет что-то вроде: mono /opt/mono-2.4/lib/monodevelop/bin/MonoDevelop.exe. Попытка запустить .NET-приложение, например main.exe, просто набрав mono-2.4 main.exe, выдаст ошибку. Взамен Вам стоит писать mono-2.4 mono main.exe.

Счастливого хака!

Читать далее

вторник, 12 мая 2009 г.

deb-репозитарии и gpg-ключи

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

Так загружаем ключ 1234567890ABCDEF и добавляем его:
$ gpg --keyserver keyserver.name --recv-key 1234567890ABCDEF &&  gpg -a --export 1234567890ABCDEF | sudo apt-key add - 

А это список серверов, название одного из которых подставляем вместо keyserver.name:

pgpkeys.mit.edu
keys.niif.hu

Другие сервера ключей можно и гуглом отыскать...
Читать далее

четверг, 16 апреля 2009 г.

Mono 2.4 и MonoDevelop 2.0: из исходников в массы

Зная, что уже они появились, ставить из дебиановского testing-репозитория Mono 2.0.1 и MonoDevelop 1.0 было неохота. Уж слишком много вкусностей прибавилось в Mono с версиями 2.2 и 2.4, да и MonoDevelop покрасивее стал. Потому, порыскав в русскоязычной Сети и ничего особо не найдя, я обнаружил это отличное руководство, которое в свободное время постараюсь перевести на великий и могучий, немного доработать под Debian и выложить здесь. Если свободного времени будет совсем много - напишу и выложу здесь скрипт для установки.

Коротко о результатах: Mono установлена, MonoDevelop установлен, mono-2.4 monodevelop запускает ожидаемый MonoDevelop.

Коротко о крепком заднем уме: лишь установив всё по инструкции, я задумался о том, что это ведь не Debian-way - устанавливать при помощи sudo make install. Придётся завтра (ну, то есть уже сегодня) всё переустанавливать sudo checkinstall...

PS: Ну и обязательный "Hello, World!" был написан, собран и выполнен. ;)
Читать далее