Monthly Archives: Липень 2014

Proxylocal на CentOS

Я б Ruby поставив тільки за те, що лише вона може ганяти proxylocal. :) Proxylocal - це геніальна програма, яка дозволяє захостити будь-який локальний порт на доступному в інтернеті веб-сервері з унікальним доменом, незважаючи на те чи ви з’єднані через NAT чи через якусь іншу дупу. Досить написати:

$ proxylocal 8080
Local server on port 8080 is now publicly available via:

http://luag.t.proxylocal.com/

або

$ proxylocal 8080 --host bunyk
Local server on port 8080 is now publicly available via:

http://bunyk.t.proxylocal.com/

Інсталюється він дуже просто, якщо у вас стоїть Rubygems:

gem install proxylocal

Але якщо ні, то доведеться поставити його і ще кілька пакетів щоб все могло скомпілюватись:

yum -y install ruby gcc ruby-devel rubygems gcc-c++

make: g++: Command not found означає що бракує gcc-c++, а

mkmf.rb can't find header files for ruby at /usr/lib/ruby/ruby.h – що бракує ruby-devel.

P.S. Якщо цікавитесь Ruby – раджу оцей блог.


Filed under: Кодерство, Павутина Tagged: linux, ruby

Вступ до ZODB

ZODB – то така старовинна технологія об’єктних баз даних, знайдена десь поряд з антикітерським механізмом, але на відміну від останнього про неї збереглось ще трохи документації, чим можна скористатись. Якщо вам потрібно розібратись в чомусь написаному з допомогою цієї ZODB, то можете витратити 20 хв і прочитати мою публікацію. Вона коротка тому ви нічим не ризикуєте. :)

Щоб встановити ZODB, досить написати:

pip install ZODB

Щоб відкрити з’єднання з базою в файлі потрібно виконати:

import ZODB

connection = ZODB.connection('data.db')

Щоб створити базу даних в пам’яті можна замість назви файлу передати None.

Щоб додати об’єкт в базу, треба відкрити базу, і прив’язати наш об’єкт до якогось об’єкта що вже існує в базі. В кожній базі для цього існує кореневий об’єкт, і називається він root:

root = connection.root

Наприклад:

root.db_name = 'test'

Щоправда зміни ще не записані в базу. Щоб їх записати, треба зробити комміт:

import transaction
transaction.commit()

Тепер при наступному відкритті матимемо:

root.db_name
# 'test'

Правда з мутабельними об’єктами така штука вже не проходить:

root.numbers
# [1, 2]
root.numbers.append(3)
root.numbers
# [1, 2, 3]
transaction.commit() 

Якщо цю сесію перевідкрити, отримаємо все ті ж:

root.numbers
# [1, 2]

Для того щоб об’єкт знав що він змінився (хоча атрибут посилається на той самий список), треба перед коммітом спеціально його позначити:

root._p_changed = True
transaction.commit()

Окрім пошуку даних в ієрархії об’єктів та їх атрибутів, можна зберігати дані за ключем. Робиться це за допомогою бінарного дерева:

from BTrees import OOBTree
root.de_uk = OOBTree.BTree()
root.de_uk['Kofferraum'] = 'Багажник'

Цей об’єкт сам слідкує за змінами в собі, тому можна зразу коммітити, і потім отримувати дані:

root.de_uk
# <BTrees.OOBTree.OOBTree object at 0x8f47a04>
print root.de_uk['Kofferraum']
# Багажник

Ну й на останок – як зберігати атрибути підоб’єктів кореневого об’єкта. Просто так:

class Something(object):
     pass
 
root.something = Something()
root.something.a = 1
transaction.commit()

Виходить як завжди невдало:

root.something
<broken __main__.Something instance>

broken __main__.Something означає що воно не змогло знайти код для класу об’єкту в модулі __main__. Потурбуйтесь про те, щоб ваші класи не перейменовувались і не переміщувались, якщо в них є екземпляри записані в базу, це раз. Ну і два – як і зі списками та словниками, атрибути звичайних класів так просто не зберігаються при комміті. Навіть якщо використати _p_changed = True. Треба наслідувати об’єкт від Persistent:

from persistent import Persistent
class Subobject(Persistent):
    pass

root.subobject = Subobject()
root.subobject.a = 2
transaction.commit()

Й це все що я хотів написати про ZODB.

Посилання


Filed under: Кодерство Tagged: Python

Чим відрізняється декоратор від адаптера? (І про фасад)

Цих вихідних їхав поїздом додому, і в купе побачив на столі книжку “Heads first Design Patterns” видавництва O’reilly. Моїми сусідами по купе були хлопець з дівчиною. Вони виглядали сонними, і навіть плутали напрям поїздки (думали що поїзд до Києва їде, а не до Франківська, хоча їхали до Франківська), і активно проявляли намір лягти спати, тому я запитав:

- Можна я вашу книжку подивлюсь? Бо завжди хотів знати чим відрізняється декоратор від адаптера.

На що хлопець відповів:

- Так, звісно.

А дівчина:

- Ви що, теж програміст?! Як вас всіх скрізь багато розвелось!

Вони лягли на свої полиці, я теж підклав під голову рюкзак, вмостився на своїй полиці і взявся за книжку.

Книжка чудова, всім рекомендую. Правда, напевне дорога як холєра, але якщо вам пощастить до неї дістатись – читайте без жодних сумнівів. Щоб довго не тягнути інтригу, скажу що про відмінність декоратора і адаптера я таки дізнався. Там в цій книжці навіть цілий діалог між декоратором та адаптером описують, де ці два шаблони доводять один одному хто з них крутіший. :)

Отож, і декоратор і адаптер – шаблони обгортки. Вони при створенні екземпляра себе приймають якийсь об’єкт, і запам’ятовують його десь в своїх атрибутах. Ключова відмінність – декоратор має той самий тип що й огортуваний ним об’єкт, тобто інтерфейс об’єкта що декорується не змінюється, просто його функціональність розширюється, а адаптер – змінює інтерфейс зовсім.

Як приклад для декораторів наводили клас – напій.

# coding=utf-8
class Beverage(object):
    def __init__(self,
        description = 'Невідомий напій', 
        price = 0,
    ):
        self._description = description
        self._price = price

    @property
    def description(self):
        return self._description

    @property
    def price(self):
        return self._price

class Tea(Beverage):
    def __init__(self,
        description = 'Чай',
        price = 2,
    ):
        super(Tea, self).__init__(description, price)

class Coffee(Beverage):
    def __init__(self,
        description = 'Кава',
        price = 3,
    ):
        super(Coffee, self).__init__(description, price)

tea = Tea()
print tea.description, tea.price # чай 2
coffee = Coffee()
print coffee.description, coffee.price # кава 3

А тепер уявімо собі що нам треба ще написати класи для тих самих напоїв, але з цукром, а ще з молоком. Можна було б наслідуватись, але тоді в нас буде ціла купа різних класів. Цукор і молоко – це декоратори напою.

class BeverageDecorator(Beverage): # важливо що декорований напій, це теж напій
    _description = 'Невідомий додаток'
    _price = 0
    def __init__(self, beverage):
        self.beverage = beverage
    
    @property
    def price(self):
        return self._price + self.beverage.price

    @property
    def description(self):
        return self.beverage.description + ' і ' + self._description

class Sugar(BeverageDecorator):
    _description = 'Цукор'
    _price = 0.1

class Milk(BeverageDecorator):
    _description = 'Молоко'
    _price = 0.2

my_coffee = Milk(Sugar(Sugar(Coffee())))
print my_coffee.description, my_coffee.price
# Кава і Цукор і Цукор і Молоко 3.4

Бачимо, що декорований напій – все одно напій, хоча й з дещо зміненою функціональністю. Адаптери натомість можуть взяти один об’єкт і повернути цілком інший.

class FahrenheitThermometer(object):
    @staticmethod
    def get_temperature():
        return 451

class CelsiusAdapter(object):
    def __init__(self, fahrenheit_thermometer):
        self.thermometer = fahrenheit_thermometer

    def __call__(self):
        return (self.thermometer.get_temperature() - 32) * 5.0 / 9

f = FahrenheitThermometer()
print f.get_temperature()
c = CelsiusAdapter(f)
print c()

Бачимо що адаптером ми можемо цілком поміняти інтерфейс нашого об’єкта, змусити його повертати інші дані, які запитуються іншим способом.

Багато із звичних декораторів в Python є насправді адаптерами. Тому можна сказати що в Python декоратор – це обгортка, яку крім того можна ще й особливим синтаксисом застосувати до огортуваного об’єкта. Той же @property, @staticmethod – змінюють інтерфейс виклику методу, роблячи його зручнішим. @twisted.internet.defer.inlineCallbacks – теж змінює інтерфейс, перетворюючи співпрограму (coroutine) на набір асинхронних функцій.

Крім того, є особливий вид адаптера, який адаптує зразу кілька об’єктів, тільки називають його фасадом. Наприклад, нехай ми маємо такі об’єкти як вольтметр, та амперметр, і хочемо виміряти потужність нашої лампочки. Для цього треба послідовно під’єднати амперметр, паралельно вольтметр, і помножити їх показники. Це можна абстрагувати ватметром (в якого в реальності все одно чотири клеми, але не зациклюйтесь на реальності, ми тут моделі будуємо):

class Voltmeter(object):
    def value(self):
        return 220

class Ammeter(object):
    def value(self):
        return 0.36

class Wattmeter(object):
    def __init__(self, voltmeter, ammeter):
        self.voltmeter = voltmeter
        self.ammeter = ammeter

    def value(self):
        return self.voltmeter.value() * self.ammeter.value()

w = Wattmeter(Voltmeter(), Ammeter())
print w.value()

Взагалі, це дуже часто застосовні адаптери, і їх не обов’язково знати щоб застосовувати. Але просто іноді варто знати значення цих слів, щоб розуміти про що люди пишуть.


Filed under: Кодерство, Нещоденник Tagged: книжки, розробка, Python