"Складні речі - доступно, прості - робляться самі"

"Складні речі - доступно, прості - робляться самі"

Якщо перефразувати девіз мови Perl “Зробити прості речі простими, а складні можливими” для LISP, то вийде “Зробити складні речі доступними, а прості робляться самі”
– Всеволод Дьомкін, Grammarly. З презентації на HotCode 2013

Clojure Logo

У мене тут в чорновиках стільки записів — треба їх потроху публікувати. Але вони всі вже здоровезні, і я все не можу їх довести до ладу, тому я подумав, що варто робити з них менші і таки виводити в світ. Сьогодні я вирішив написати першу замітку про Clojure. Це, мабуть, наразі самий новий, але вже дуже популярний діалект LISP, що працює поверх JVM. Перші спроби наковбасити щось на Clojure у мене сталися ще кілька місяців тому, коли я написав простенький конвертор тест-кейсів з Testlink в TFS, але то було зроблено на колінці і окрім того, що я його написав дуже швидко, там пишатись особливо нема чим: це моя перша програмка на чомусь LISP-подібному не рахуючи простеньких завдань в універі, тому код страшненький, без всяких крутих ліспових фішечок типу макросів, з багами, але працює 🙂

Про Clojure я дізнався суто випадково на конференції Javascript Framework Days навесні цього року з презентації Олександра Соловйова про ClojureScript (це така підмножина мови, що транслюється в JavaScript, типу як CoffeeScript). До речі, рекомендую подивитись саму презенташку про FRP та ClojureScript – вона весела, динамічна, а мені не потрібно буде розповідати про те, що являють собою LISPоподібні мови і Clojure в тому числі.

А ось нижче приклад простенької задачки яку буквально на днях мну вирішив для себе. Суть полягає в тому, щоб розпарсити старий допотопний HTML на сайтах філармонії, театру Франка, органного залу та інших подібних закладів у iCalendar формат, аби їх можна було б собі підключити собі в Outlook чи Google Calendar. Парсити доводиться звичайний html, бо жоден з цих сайтів та агрегаторів не додумався зробити цього сам, або хоча б видавати афішу в rss-форматі (якщо хтось знає де є такі — скажіть). Вгадайте скільки рядків займає подібна програмка навіть на Python з будь-якими лібами 😉

Готовий закластися, що навіть на Python буде більше ніж це:

(ns eventic.core
  (:require [net.cgrand.enlive-html :as html]
            [clj-time.format :as time]
            [clj-ical.format])
  (:import java.net.URL java.nio.charset.Charset)) 
  
; хелпер для завантаження сторінки (штатний рідер enlive не шарить кодувань)
(defn fetch-page [url]
  (-> url java.net.URL. .getContent (java.io.InputStreamReader. "WINDOWS-1251") html/html-resource))
  
; закачуємо сторінку
(def p
  (fetch-page "http://www.filarmonia.com.ua/ua.afisha"))
  
; хелпер для конвертації дат виду "30.07.2013" в datetime
(defn to-date [date]
  (time/parse (time/formatter "dd.MM.yyyy") date))
  
; переводимо усі теги <br> в n, а всі інші теги прибираємо зберігаючи текст
(defn extractor [content]
  (map #(apply str (-> % (html/at [:br] (html/content "n") [:*] html/unwrap))) content)) 

; вибираємо дати і конвертуємо в date-time формат
(def dates
  (map #(-> % :content first to-date)
        (html/select p [:table :tr :td :p :span])))
    
; вибираємо заголовки
(def titles
  (map #(-> % :content first str)
        (html/select p [:table :tr :td :h2])))

; вибираємо описи одразу переводячи їх в plain text
(def descriptions 
  (rest (extractor
    (html/select p [:table :tr :td [:p (html/attr? :align)]]))))

; робимо структуру наповнення для iCalendar
(def ical
  (map #(vec (zipmap [:dtstart :summary :description] %))
        (map vector dates titles descriptions)))

; записуємо в файл
(spit "filarmony.ics"
  (->> (clj-ical.format/write-object
         (apply conj [:vcalendar] (map #(apply conj [:vevent] %) ical)))
       with-out-str))

І все. На виході отримуємо готовий файл у iCalendar форматі з усім необхідним. Можливо з першого погляду здаватиметься, що це якась тарабарщина, але насправді Clojure вчиться дуже швидко. Достатньо пари годин на день і вже за 3-4 дні можна писати маленькі корисні штуки для себе (я раніше постійно використовував Python для цього, але тепер в мене з’явилася нова робоча конячка 🙂 ). І суть навіть не в тому, що цей код компактний сам по собі. Суть в тому, що Clojure має такі засоби, що дозволяють створювати бібліотеки на кшталт Enlive, які надзвичайно потужні і прості в користуванні одночасно, а відповідно код що їх використовує стає простішим і його легше підтримувати.

А тим часом я думаю як це все оформити в простенький сервіс, який можна буде розгорнути на Google App Engine — все ж Clojure на JVM працює. Я вже надивився прикладів і презентацій на “Hackers with Attitude”, тож хочу тепер сам спробувати (і згодом описати це).

P.S. Ну і хто досі не читав “Перемогти посередність” Пола Грема, дуже рекомендую 😉

Sergii Gulenok

Sergii Gulenok

View Comments