понеділок, 31 грудня 2012 р.

Ретроспектива 2012



2012 рік добігає кінця. І за хорошою традицією в останньому записі я згадую найбільш значущі події в світі Ruby, що відбулися у цьому році.

Цього року формат статті зазнав деяких змін. Події розбиті по місяцях.

Січень
  • Випуск KidsRuby 1.0, редактора Ruby розробленого в освітніх цілях для дітей дошкільного й молодшого шкільного віку (але не менш корисного для дорослих). Він включає в себе навчальні посібники та графічну систему в стилі мови програмування Logo для більш наочного типу навчання.

Лютий

Березень
  • FXRuby - Ruby bindings для FOX (крос-платформної бібліотеки з відкритим вихідним кодом для побудови графічного інтерфейсу) повернувся з довгого періоду застою.
  •  Юкіхіро Мацумото (Yukihiro Matsumoto), автору мови програмування Ruby, присуджена премія "Free Software Awards 2011" номінації, що вручається за просування і розвиток вільного ПЗ.
  • Ruby став стандартом ISO.
    Після чотирьох років розробки, 31 березня цього року за результатами голосування Ruby версії 1.8 був прийнятий як стандарт ISO/IEC 30170. Будемо сподіватися, що наступну версію стандарту підготують швидше.
Квітень
  • mruby - полегшена реалізації мови Ruby, виконана за ISO стандартом для виконання різних середовищах. Може були як виконана інтерпретатором так і бути скомпільованою і виконаною віртуальною машиною,  відповідно до його модульної конструкції.
  • MobiRuby - інструментарій для створеня IOS додатків за допомогою мови Ruby. В майбутньому планується підтримка Android.
Травень
  • Компанія Red Hat запустила нову хмарну платформу OpenShift з підтримкою Java, Ruby, Node, Python, PHP і Perl.
  • Два ключових розробника команди JRuby(Thomas Enebo і Charles Nutter) переходять на роботу в Red Hat.
  • Перший попередній реліз JRuby 1.7. Цікавий тим, що це перший реліз з підтримкою  InvokeDynamic - рішення, що дозволяє взаємодіяти з динамічними мовами практично без втрати продуктивності.
  • Реліз RubyMotion - набір інструментів для IOS, який дозволяє швидко розробляти і тестувати власні програми для iPhone або iPad, за допомогою Ruby.
Червень
  • Вийшла ювілейна версія 1.0 бібліотеки R18n, яка дозволяє додати в додаток на Rails/Ruby підтримку декількох мов (i18n).
  • Реліз Sublime Text 2.0
Вересень

Жовтень
Листопад
Грудень
  • Heroku додали офіційну підтримку для JRuby

2013 рік обіцяє також бути багатим на нові релізи. Зокрема Ruby 2.0 і Rails 4.0.

Щиро вітаю Вас з наступаючим Новим Роком!
Нехай він принесе нам довгождане щастя, спокій, добробут, палке кохання, міцне здоров'я та здійснення наших самих заповітних мрій!

    пʼятницю, 28 грудня 2012 р.

    Історія одного тупняку, або як я конвертував DBF в SQLite3

    Нещодавно до мене потрапив файл у форматі dbf з досить цікавою інформацією. Але сьогодні не про це.

    Для роботи з цією інформацією, я вирішив імпортувати таблицю DBF у SQLite3. Використовувати сторонній софт для такої тривіальної задачки — це не наш варіант. Ми не шукаємо легких шляхів. Напишемо скрипт самі. Писати, звичайно, будемо на Ruby.

    DBF — застаріваючий формат зберігання даних, який (був?) широко розповсюджений на пострадянському просторі.

    Недовгі пошуки навели на бібліотеку dbf. Як описує її автор, це невелика швидка бібліотека для читання DBase, Xbase, Clipper і FoxPro файлів баз даних.

    Відкриваємо DBF файл:
    require 'dbf'
    dbf_table = DBF::Table.new(dbf_file, nil)
    dbf_columns = dbf_table.columns
    
    І відразу отримуємо помилку:
    dbf-2.0.3/lib/dbf/column/base.rb:19:in `initialize': column name cannot be empty (DBF::Column::NameError)
    

    Проблема виникає через те, що при ініціалізації поля, у його назві відсікаються всі не-ASCII символи. А такий порядок речей в моєму випадку недопустимий, оскільки
    імена полів у DBF таблиці записані кирилицею у кодуванні cp1251.

    Тут стане у нагоді "monkey patching".
    Ініціалізуємо таблицю у кодуванні cp1251 і конверуємо імена полів в utf-8:

    module DBF
      module Column
        class Base
          attr_reader :name, :type, :length, :decimal
    
          def initialize(name, type, length, decimal, version, encoding = nil)
            @name, @type, @length, @decimal, @version, @encoding = clean(name, encoding), type, length, decimal, version, encoding
    
            raise LengthError, "field length must be greater than 0" unless length > 0
            raise NameError, "column name cannot be empty" if @name.length == 0
          end
    
          def clean(value, encoding)
            value.force_encoding(encoding).encode('utf-8')
          end
    
        end
      end
    end
    
    dbf_table = DBF::Table.new(dbf_file, nil, 'cp1251')
    dbf_columns = dbf_table.columns
    

    Не вдаючись в подробиці створення схеми SQLite3 (повний код скрипта Ви знайдете в кінці статті), перейдемо відразу до імпорту. Задачка звичайно зовсім тривіальна і написання рішення займає лічені хвилини.
    dbf_table.each do |record|
      sqlite_db.execute("INSERT INTO the_table VALUES (?,?,?,?,?,?)", record.attributes.values)
    end
    
    Тут можна було б піти покурити, попити чаю або кави чи навіть подивитися фільм. Даний процес зайняв досить багато часу.
    Час роботи такого рішення мене не вдовольняв зовсім.
    Бувалі програмісти мабуть відразу зрозуміють де собака зарита. Але цей тупняк поглинув мене на кілька годин.
    Спочатку я грішив на повільність бібліотеки dbf. Але цей варіант був швидко відкинутий. Каменем спотикання була саме бібліотека SQLite3.
    Наступну годину я курив, пив пиво і дивився "Ruby Sparks". Між іншим красивий і милий романтичний фільм, але настирливий продакт-плейсмент Apple вже бісить. Паралельно ґвалтува ґуґл в пошуках рішення. І ось воно зійшло:
    sql_db.transaction do |db|
      dbf_table.each do |record|
        db.execute("INSERT INTO the_table VALUES (?,?,?,?,?,?)", record.attributes.values)
      end
    end
    

    Несподівано скрипт, який виконувався годинами, виконався за 10 секунд. Підвищення продуктивності over 9000%. Я отетерів(!) від такого результату.

    Мораль цієї історії проста... Комусь здається, що це тривіально, але мені просто хотілось поділитися з кимось цими думками.

    Нище ви знайдете повну версію скрипта для конвертування DBF у SQLite3.

    четвер, 27 грудня 2012 р.

    Робота з файлами eml в Ruby

    Сьогодні зіткнувся з проблемою пошуку вкладень серед багатьох файлів з розширенням eml.

    Формат eml використовується багатьма клієнтами електронної пошти, включаючи Microsoft Outlook Express, Windows Mail і Mozilla Thunderbird. Це звичайний текстовий файл у форматі MIME, що містять заголовки повідомлень електронної пошти, а також вміст повідомлення та вкладення.

    Відкрити його під Linux не складає труднощів, наприклад, за допомогою поштового клієнта KMail, однак таке рішення мені здалося занадто складним.

    Трохи погугливши, виявилося, що бібліотека email чудово вміє розбирати такі файли.

    В результаті вийшов невеликий скрипт, який вибирає всі файли з розширенням eml у поточній директорії, і відображає в консолі текстовий вміст листа, а прикріплені файли зберігає у відповідних директоріях.
    require 'mail'
    
    Dir.glob('*.eml') do |filename|
      mail = Mail.read(filename)
    
      puts <<EOF
      From:    #{mail.from}
      To:      #{mail.to}
      Subject: #{mail.subject}
      Date:    #{mail.date}
    
    EOF
    
      charset = mail.text_part.content_type_parameters['charset']
      puts mail.text_part.body.decoded.force_encoding(charset).encode('utf-8')
    
      if mail.multipart?
        dir = File.basename(filename, File.extname(filename))
        Dir.mkdir(dir) if !Dir.exists?(dir)
    
        mail.attachments.each do |attachment|
          file = File.open(dir + '/' + attachment.filename, 'wb') { |f| f.write attachment.body.decoded }
          puts "Saving `#{attachment.filename}`"
        end
      end
    
      puts "===\n\n"
    end