Magic

Jan. 19th, 2017 11:24 am
recoder: (masked)

Any sufficiently advanced technology is indistinguishable from magic.
© Arthur C. Clarke
Не в первый раз слышу в программерских кругах обсуждения "магии" в разных местах: в языках, во фреймворках и библиотеках, и т.д. И каждый раз мне хочется встрять в это обсуждение и рявкнуть: в программировании магии нет!

Программистское брюзжание )

JSON APIs

Nov. 14th, 2016 07:53 am
recoder: (Default)

А подскажите мне, коллеги-программисты, какой нонеча самый кошерный способ правильно описывать HTTP APIs? Ну вот так, чтобы свой обычный REST JSON API, описать его один раз и чтобы дальше всё само получилось: документация, клиенты для скриптовых (и не только) языков, какой-нибудь online playground, и всё такое?

Мы в нашей конторе пять лет назад, когда внедряли REST, ничего зрелого и толкового не нашли, и поэтому запилили свой велосипед: Sleepwalker. Это потом бурным цветом расцвели RAML, Swagger, WADL и прочие, а у нас уже наросли кучи полезного code base.

Вот я и думаю - если бы новый проект начинать сейчас, то что бы следовало взять за основу? Там же только на первый взгляд всё несложно, а чуть углубился в детали - и из-под каждой по дьяволу мерещится. А вдруг хочется поддержать не только JSON, а например ещё и XML? А если захочется какой-нибудь CSV или вообще blob наружу выдать? А как ошибки документировать? А как HTTP errors пересекать с ошибками приложения? А как bulk-операции реализовывать? И прочее, и прочее, и прочее...

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

coding

recoder: (Default)

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

Пошли профайлить плюсовые JSON парсеры. Потыкали boost с его свойственными деревьями - оказалось на удивление медленно, причём и в компиляции и в рантайме. Посмотрели на Poco::JSON - более менее фурычит, хотя звёзд с неба и не хватает. Пошли смотреть что ещё бывает в современном мире C++...

А потом по приколу прогнали Python парсер на тех же тестовых данных. И тихо офигели: мерзкий питон уделал наши крутые библиотки в несколько раз. Проверили в разных аспектах - всё верно, питон быстрее плюсов. Даже для эксперимента вызвали из плюсов питоновский парсер - и он всё равно летает.

Как теперь жить с этим знанием? Переписывать всё нафик на Питоне? Таскать везде с собой кусок питона? И самый главный вопрос - как вообще такое могло получиться?!

coding

recoder: (Default)

Сижу, изучаю очередной JavaScript-фреймворк. На этот раз YUI3. Раньше приходилось покопаться в Dojo, была задача за пару месяцев изучить ExtJS (aka Sencha) и пописать на нём. И всё это время одолевает меня один вопрос-непонимание...

И нет, этот вопрос отнюдь не "Ну почему же универсальным языком интернетов оказался этот ужасный монстр?" У нас, программистов, ответ "так исторически сложилось" подходит почти к любому вопросу. Ладно... Я другого не понимаю.

Каждый серьёзный фреймворк начинается с того, что вводится своя система классов, свойств, модулей и пр. Почему? Потому что на голом JS писать большие системы так же удобно как на ассемблере и вообще "см. рис.1." Потом поверх этой системы наворачиваются всякие паттерны, на которых строятся MVC, виджеты и всё остальное. Причём шаг влево, шаг вправо от этих паттернов - и всё начинает становиться раком в самый неподходящий момент (поскольку язык динамический и никаких compile time checks нет и вообще "см. рис.1.").

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

Делов-то: взять, скажем CoffeeScript, форкнуть его и допилить немножко. Радикально было бы адаптировать какой-нибудь Haxe или Elm. Но практически хватило бы даже минимальной кодогенерации в JavaScript. А никто не делает!

Впрочем - выяснилось, что я немножко вру. Таки-есть один скриптовый язык, заточеный под Ember.js (Иуда рулит!) - EmberScript. Тем более непонятно, почему другие серьёзные игроки в эту сторону не смотрят. Занимался бы я UI так, как занимался десять лет назад - точно бы учудил такую надстройку к ExtJS. А сейчас - нет, лучше пойду свою REST-систему на C++ допиливать...

coding

recoder: (Default)

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

Ну разве это всё не красота?!

coding

recoder: (Default)

Коллеги интересуются, что в конце концов случилось с идеей написания веб-сервиса на плюсах. Спрашивали - отвечаем!

Сугубо Программистское )

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

coding

recoder: (Default)

Возникла идея в рабочей системе вообще отказаться от интерпретируемых языков. Статику отдавать через Apache, клиента сделать на статическом JavaScript, а динамику отдавать через FastCGI из своего C++ приложения в XML/JSON.

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

CppCMS: похоже, довольно полная библиотека для написания приложения. Всё как у взрослых: кое-какие routes, контроллеры, темплейты+views+шкуры и всё прочее. Немного смущает что, как и другие фреймворки, у них свои строки, массивы и прочие базовые классы. Но в целом - кажется приемлемо.

Wt: любопытная widget-ориентированная библиотека для написания веб-систем. Оригинальная, но уж очень самобытная. А к самобытным системам ни Dojo не прикрутишь, ни программистов потом не найдёшь. Плавали - знаем.

TreeFrog: похоже на попытку аккуратного портирования Rails на плюсы, но результат непонятный: проект молодой, документация почти несуществующая.

ffead: занятная штука, судя по всему написанная сумасшедшим Java-программистом, который почему-то начал писать на C++. Посмотреть конечно прикольно, но не брать же это в production.

CPP SERV: уже другие безумные Javaнцы переписывали на C++ - на этот раз servlets. Однако года три назад вылечились и бросили это дело.

Есть ещё кучка библиотек помельче для разработки мини-сервисов: Klone, PoCo, NanoGear, REST CGI. Но с ними опять придётся выстраивать собственно архитектуру с самого начала. А не хочется.

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

Вот, сижу теперь, думаю... Записываться в экстремалы с такой системой или плюнуть и откопать PHP?

coding

recoder: (Default)

Вялотекущие архитектурные изыскания вышли в стадию активного прототипирования клиентской части. Очень хотелось найти JS-фреймворк, одновременно предоставляющий достаточный уровень абстракции над DOM, единообразную поддержку разных событий, средства моделирования данных (чтобы не возиться с прикручиванием Backbone, Knockout и прочих), инструменты для разработки UI-компонент и обладающую уже готовым набором этих компонент. Искать пришлось довольно долго.

Довольно быстро с дистанции сошли SproutCore (который теперь Ember), Qooxdoo, dHTMLx, SmartClient, Rico. Туда же пошли монстры вроде AppCraft.

В финал вышли: jQuery UI, ExtJS, Dojo и YUI3. Отбросил YUI3, потому что где будет Yahoo даже через полгода - совсем непонятно, плюс даже третья версия показывает признаки старости. jQuery UI выглядел неплохо, но разработка его как целого тулкита у меня вызывает лёгкое недоумение (они там похоже простое дерево третий год пишут). ExtJS, особенно четвёртая его инкарнация, очень хорош, однако его лицензионные заморочки настораживают даже меня (а что скажут юристы?!). И остался Dojo Toolkit.

Программистское про Dojo Framework )

Резюмируя, в общем Dojo мне понравился и, если не наткнусь на что-то совершенно фатальное, буду строить UI именно на нём. Если у кого будут вопросы по Dojo - приходите обсуждать на ХэшКод или на StackOverflow.

coding javascript

recoder: (Default)

Гуманитарным складом ума обычно называют отсутствие математического.
bash.org.ru
Наткнулся намедни на Hacker News на ссылочку на старую статейку Джеффа Этвуда под названием "Separating Programming Sheep from Non-Programming Goats". Где он действительно пишет о том, как отделять способных к программированию агнцев от неспособных к нему козлищ.

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

Супер-вопрос на $1000000 )

Нам, программистам, ответы конечно же очевидны, однако по результатам этого теста народ делится примерно таким образом: 44% формируют в голове какую-то модель устройства присваивания, 39% сделать это вообще не удаётся, и ещё 8% вообще плюют на ответы и не пишут вообще ничего. Поучительно, что когда этот тест повторили через три недели обучения, расклад на группы практически не изменился.

Получается, что довольно приличный процент учеников и студентов учить программированию просто бесполезно, у них биохимия такая! А у нас в школах все как один Basic учили (кстати, а что сейчас учат?). Зачем? Пусть лучше сидят, в Civilization играются - всё больше толку будет. Может из них потом менеджеры получатся?

Вот такая оказывается загогулина в нашем ремесле.

coding

recoder: (Default)

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

Дано: линуксовая система с жадным до памяти софтом с C++ API.

Надо: прикрутить сверху Web UI поверх Apache, чтобы всё красиво конфигурить, смотреть всякие результаты: таблично и графически, плюс сваять своё подобие портальной системки чтоб уметь строить страницы с наборами своих виджетов.

Вот и думаю - как бы всё это задизайнить правильно и с перспективой? Собственно вопрос архитектуры распадается на два связанных вопроса: 1) на чём писать клиентскую сторону? и 2) на чём писать серверную сторону?

Клиент. Есть большой соблазн взять какой-нибудь ExtJS или Dojo (или SproutCore, или dhtmlx, или SmartClient, или на базе jQueryUI что-то своё) и построить в браузере монолитное веб-приложение со всеми рюшечками. Заманчиво конечно замахнуться на такое ультра-современное решение ибо и HTML5 на пороге, веб-приложения проникают везде и повсюду, да и демки все выглядят захватывающе. Отдельный плюс - серверная компонента сокращается до тоненькой прослойки над C++ API, которую можно написать на чём угодно, хоть на C++ сделать модуль к Апачу. Однако мне кажется что риск слишком велик. Написание приложений на JS - дело относительно молодое, подходы не устоялись, API всё ещё гуляют, плюс зоопарк браузеров просто вразнос пошёл. Опять же - непонятно, где искать JS-программистов соответствующего уровня? (Да даже оценивать JS-программистов - уже задача нетривиальная!) И сколько займёт процесс изучения конкретного выбранного JS-фреймворка, даже если у него такая неплохая документация как у Sencha? В общем, технологически - амбициозно, а вот организационно - всё же рисковано.

Сервер. Здоровая альтернатива - делать всё более-менее традиционно: MVC-фреймворк на сервере, те же jQueryUI или Dojo для оживляжа страниц, и JSON/XML для транспорта. Подводных камней видится гораздо меньше, но проще всё равно не становится. Всё равно надо думать - для начала о серверной платформе. Попробуем решить задачу перебором...

Мне так кажется, что на один бокс сажать Java с другими прожорливыми компонентами - нерационально. Прожорливость + непредсказуемость GC = проблемы. Так что отпадают многочисленные Java-фреймворки (Spring, Stripes, Play, и др.), химерный GWT и JVM-based Scala (Lift) и Groovy (Grails). При всей моей трогательной любви к Perl реальных перспектив у него не вижу ни с Perl5 ни с Perl6 несмотря на Ренессанс, да и TIMTOWTDI в командной разработке не будет помогать. Так что отпадают Catalyst, Mojo, Dancer. PHP сразу нет, потому что дикая эклектика и вообще плохо пахнет. ASP - нет, потому что среда линуксовая и Microsoft сюда тянуть себе дороже.

Насчёт написания веб-компонента на C++ пока не могу даже определиться - сумасшествие это или реальный вариант. Гугление вроде бы выдаёт некоторую информацию, но стоит ли бросаться в этот омут? Наверное всё же - нет.

Итого что у нас остаётся: модный Ruby и кошерный Python. То есть Rails и что-то из Django, Pyramid, TurboGears. Rails я на зуб пробовал - мне понравилось. И общая архитектура, и инструментарий для тестирования, и динамичность сообщества разработчиков. Однако несколько смущает сам Ruby: во-первых, непонятно где искать рубистов, а во-вторых опять же может выйти TIMTOWTDI. Питоновые фреймворки руками не щупал, но верю что там тоже всё должно быть примерно аналогично (и даже более однозначно из-за the python way). Есть правда озабоченность моментом Python 2.x против Python3K... А точнее пока непонятно как сравнивать.

Так что судя по всему, придётся выбирать между Rails, Pyramid и TurboGears. Методом тыка и прототипирования.

Коллеги, веб-девелоперы - скажите своё слово, поделитесь советом, а?

coding

recoder: (Default)

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

В программировании же примерно как в Зазеркалье - надо бежать, чтобы оставаться на месте, а чтобы двигаться вперёд - надо бежать ещё быстрее.

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

» buzz coding

recoder: (Default)

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

Текст для любопытных программистов )

Очень любопытные технологии. Надо будет попробовать на практике.

coding

recoder: (Default)

Захотелось вот в рамках закрепления успехов Java-изучения попробовать сделать себе экспериментальный Web App, на котором и оттачивать умения. Гугление дало громадное множество самых разнообразных Web-фреймворков разной степени замороченности:

С одной стороны - глаза разбегаются от такого изобилия. А с другой стороны, большая часть этого добра - это монстры с наследственностью отягощённой совместимостью со старыми версиями - как себя, так и самой Java. Причём хорошим тоном является наличие десятков XML-конфигурационных файлов - это видимо делает их более enterprisey. Ужас.

А хочется чего-то похожего на Rails или Catalyst. Чтобы было гибкое и модульное. Чтобы минимум конфигураций, максимум соглашений и умолчаний. Чтобы MVC был родной, чтобы URI-mapping легко настраивался, чтобы генераторов результатов можно было несколько иметь. В общем, хочется среду, в которой будет приятно работать. Оно такое вообще бывает?

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

Part2: Судя по обсуждениям в [livejournal.com profile] ru_java, надо смотреть в сторону Spring, а лучше Guice, а лучше Grails. Ну тогда наверное, ещё лучше с Java вообще не возиться и двигать на оригинальные Rails. Не?

» buzz java coding

recoder: (Default)

Недавно коллега [livejournal.com profile] sigizmund напомнил о богатом выборе разнообразных языков программирования. Засиделся я что-то в нашей Java-shop'e... И задумался - чего бы ещё такого полезного изучить:

  • Может Ruby, который мне в первый заход активно не понравился? Как раз пока разберусь - тут и рельсы новые должны выйти... И JRuby опять же недалеко от Жабы.
  • Может Groovy, который судя по рассказам - гибрид Джавы с Перлом, что очень даже интригует!
  • Может Scala попробовать понять? Сломать себе мозг и уйти в просветлённое состояние...

coding

NoSQL

Apr. 8th, 2010 07:03 pm
recoder: (Default)

Смотрю я вот издалека на бучу вокруг NoSQL хранилищ, и вижу два разных взгляда.

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

А другой взгляд - это народ, которому надоело иметь пару лишних прослойки для удобного доступа к своим данным. Одна прослойка - это всяческие ORMы, транслирующие метаинформацию в SQL. А вторая прослойка - это собственно сам SQL, транслирующийся каким-то неочевидным образом (сдобренным разнообразными хинтованиями в разных диалектах) в последовательность операций доступа к собственно хранилищу данных. Обычно к тому моменту, как понимаешь что сделает цепочка API→ORM→SQL→Optimizer→DB, уже можешь и сам составлять тот самый последний уровень, имея в результате более чёткий и компактный код.

На практике же видимо основную роль будет играть крутизна порога вхождения или наличие всяких средств дизайна и пр. Но всё равно интересно будет наблюдать за раскладом на этом направлении.

Надо бы в свободное время поизучать всё это поглубже...

Другие о том же: 1, 2. 3.

coding

recoder: (Default)

Нельзя ждать милостей от Гугла, взять их - вот наша задача!

Так подумал я, сел на выходных с любимым Перлом и написал себе утилитку для формирования памятных дат в календаре по данных из Google Contacts, которую так давно хотелось. (Увы, встроеная фича меня устраивает сильно меньше чем на 100%.)

Оказалось всё очень даже несложно (если не заморачиваться с OAuth). Теперь на очереди - довести утилитку к релизному виду и переходить к другим своим мечтам - data mining например начать писать...

Part2: Кстати нашёл Google App'овский сервис для синхронизации контактов между несколькими аккаунтами: Share You Contacts.

coding

recoder: (Default)

Отец [livejournal.com profile] gaperton написал крайне занятную статью "Читай код" о том, что лучшая документация - это хороший код. И в своём посте высказывает ещё много интересных идей (с которыми я однозначно соглашусь):

Читай код! )

Главная мысль такова: если код в порядке и есть умные люди - то документация не нужна. А если нету первого или второго - то документация уже не поможет. Вывод для многих неочевидный.

Читай код, сука! )

coding management

recoder: (Default)

Поэкспериментировав на праздниках с Perl'ом и Catalyst'ом, испытал чувство глубокого удовлетворения. После прокуривания всех мануалов и сырцов всё пишется быстро, код получается компактный и мощный. Как завещал дедушка Ленин - буквам тесно, а мыслям просторно! Любая типичная задача уже решена на CPAN'е, причём обычно несколькими способами, так что приходится концентрироваться только на своих собственных проблемах. И это чертовски приятно.

А потом подумалось, что как это не прискорбно, perl никогда не станет распространённой enterprise platform. Нет, на нём конечно создавались и будут создаваться большие проекты типа del.icio.us, LiveJournal или Amazon. Но это будут оставаться проекты небольших групп увлечённых разработчков, а не традиционных корпоративных команд. С обычным корпоративным духом perl, увы, не совместим, причём по многим причинам:

  • Enterprise Software - это не технологии, это религия. Религия большого, дорогого и сложного. Требующего больших бюджетов, сложных процессов, множества сейлзов, менеджеров, консультантов и IT-инженеров. Perl с его мощностью и лаконичностью сюда вписывается плохо.
  • Perl умеет выражать мощные задачи в крайне компактной форме, что привносит сложности в процесс менеджмента, делая последнюю понятную метрику LoC совсем бесполезной.
  • Perl и его TMTOWTDI (плавно переходящее в TAFTMWTDI) не позволяют решать задачи методом "больших батальонов". То есть набирать толпу народа на проект и добавлять свежее мясо по мере роста потребностей или увольнений утомившихся.
  • Признаемся - Perl спокойно даёт любому программисту даже не пистолет для простреливания ноги, а огромный арсенал вплоть до ядерных боеголовок. (В отличие от Java, дающей каждому по игрушечному пистолету с войлочными пульками.) Так что пускать к разработке кого попало - это верный путь к полному развалу.
  • Наверное, его open-source'ная натура тоже плохо совместима с традиционными корпоративными концепциями

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

coding perl

Part2: кстати - с LISP'ом всё должно быть примерно так же...

recoder: (Default)

Любой программер, кто работал с большой и старой системой знает что с возрастом в ней накапливается некоторое количество хаков и недоделок, на которые так и не хватило времени. (См.: bit rot.) И сожалению, реальность данная нам в ощущениях обычно не позволяет полностью избавиться от этого мусора. Причём обычно из-за того, что удаление хаков потребует либо сокращения функциональности, либо уже системной реализации аналогичной функциональности, а это обычно грозит потерей человеко-часов без положительных внешних изменений. Ни то ни другое обычно не проходит через менеджмент. А избавиться от мусора в коде всё равно хочется.

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

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

coding

recoder: (Default)

Намедни прочитал отличный текст "If programming languages were religions...", в котором расклад по языкам и религиям получился такой:

  • C — Judaism
  • Java — Fundamentalist Christianity
  • PHP — Cafeteria Christianity
  • C++ — Islam
  • C# — Mormonism
  • Lisp — Zen Buddhism
  • Haskell — Taoism
  • Erlang — Hinduism
  • Perl — Voodoo
  • Lua — Wicca
  • Ruby — Neo-Paganism
  • Python — Humanism
  • COBOL — Ancient Paganism
  • APL — Scientology
  • LOLCODE — Pastafarianism
  • Visual Basic — Satanism

Попрограммировав пару вечеров на Perl могу заявить, что так оно и есть! Разрабатывая на Перле - реально чувствуешь себя магом voodoo. Пишешь много непонятных закорючек страшной силы, похожих на заклинания. Иногда они срабатывают, иногда нет... Периодически обращаешься к Великому Божеству CPAN'a - и он иногда тебе помогает, а иногда и нет. Но рано или поздно ты собираешь все нужные ингридиенты для вызова финального заклинания - и оно собирается и работает.

И это прекрасно!!! Ради этого и стоит программировать!..

coding perl

April 2017

S M T W T F S
      1
23 456 78
9101112131415
16171819202122
23242526272829
30      

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 22nd, 2017 04:36 pm
Powered by Dreamwidth Studios