• Главная
  • Карта сайта
Не найдено

Перетинаючи кордони: Розширення в rails

  1. Серія контенту:
  2. Цей контент є частиною серії: Перетинаючи кордони
  3. Про цю серії
  4. API для реалізації кінцевих автоматів
  5. Малюнок 1. Кінцевий автомат для CTP
  6. Лістинг 1. Приклад автомата
  7. Лістинг 2. Маніпулювання автоматом.
  8. Модулі типу acts_as
  9. Лістинг 3. Ініціалізувалися код для acts_as_state_machine
  10. Ініціалізація модуля.
  11. Лістинг 4. Структура модуля
  12. модулі acts_as
  13. Лістинг 5. Додавання acts_as
  14. Додавання методів класу і методів екземпляра
  15. Лістинг 6. Методи класу модуля act_as_state_machine
  16. Лістинг 7. Методи примірника для модуля acts_as_state_machine
  17. Висновок
  18. Ресурси для скачування

перетинаючи кордони

Анатомія плагіна acts_as

Серія контенту:

Цей контент є частиною # з серії # статей: Перетинаючи кордони

https://www.ibm.com/developerworks/ru/views/global/libraryview.jsp?series_title_by=Пересекая+границы

Слідкуйте за виходом нових статей цієї серії.

Цей контент є частиною серії: Перетинаючи кордони

Слідкуйте за виходом нових статей цієї серії.

Коли пишеться ця стаття, Техас і Оклахома розморожують після довгого крижаного зливи. Водії і бояться не ожеледиці, а роз'їжджають по ньому інших лихих техасців, починали знову з'являтися на дорогах. Моє життя знову входить в нормальну колію після трьох днів бездіяльності. Мені довелося випробувати схоже відчуття холоду в іншій ситуації - після переходу з Java на Ruby. Коли я працював з Java, то завжди міг знайти бібліотеку для Spring або компонент для Eclipse для вирішення якої-небудь спеціальної проблеми. На щастя, це почуття теж почало швидко танути завдяки ефективній архітектурі плагінів, яку тисячі розробників використовували для розширення можливостей Rails.

Про цю серії

У серії перетинаючи кордони Брюс Тейт просуває ідею, що сьогодні Java програмістам корисно вивчати інші мови і підходи. Ситуація в області розробки ПО змінилася, і технологія Java більше не є найкращим вибором для всіх проектів з розробки ПЗ. Інші framework'і (каркаси для розробки додатків) впливають на побудову framework'ов для Java, і знання, які можна отримати з інших мов, можуть привнести свіжі ідеї в Java-програмування. Код, написаний вами на мові Python, Ruby, Smalltalk (продовжите перелік самі), може змінити ваш підхід до написання коду на мові Java.

Ця серія статей знайомить з поняттями і прийомами, які хоча і радикально відрізняються від Java, але можуть бути безпосередньо застосовані в Java програмуванні. У деяких випадках необхідно інтегрувати таку технологію, щоб отримати від неї користь. В інших можна безпосередньо застосувати поняття, що лежать в основі технології. Не так важливі конкретні інструменти розробки, як ідея, що іншу мову і framework'і можуть вплинути на Java співтовариство: розробників, framework'і і навіть фундаментальні підходи.

Якщо ви вже працювали скільки-небудь часу з Rails, то, без сумніву, звернули уваги на команди acts_as в моделі ActiveRecord. Хоча модель ActiveRecord займається управлінням і прив'язкою об'єктів до даних в СУБД (persistence), часто виникає бажання додати в класи ширшу функціональність, ніж просто збереження і завантаження з бази. Наприклад, використовуючи модуль acts_as_tree, можна додати функціональність дерева до класу з атрибутом parent_id. Чи не використовуючи нічого, крім оголошення модуля acts_as_tree в моделі ActiveRecord, можна динамічно додавати методи для управління деревом, такі як методи для отримання одного з батьків або нащадків даного елемента. За останній місяць мені вдалося знайти плагіни для Rails для проведення голосування, управління версіями, AJAX, складових ключів та іншої різноманітної функціональності, що не підтримується базовим Rails.

Моделі розширення в Rails, побудовані поверх можливостей мови Ruby, разюче відрізняються від їх аналогів в Java. У цій статті я досліджую відкриті плагіни acts_as, щоб показати модель розширення зсередини. Я представлю фрагмент реального робочого додатки, замість того щоб створювати закінчене іграшкове додаток, щоб охопити більше областей і познайомити вас з реальними плагінами і тим, як вони використовуються в комерційних додатках.

API для реалізації кінцевих автоматів

Як ви, напевно, знаєте, кінцевий автомат (state machine) - це математичне відображення стану деякої системи. Кінцевий автомат складається з вузлів, що представляють стану, і переходів між ними. У кожен вказаний момент часу у автомата є одне активний стан, також зване поточним станом. Події ініціюють переходи між станами. Щоб проілюструвати це поняття, я приведу приклад з моєї поточної роботи: розробки і підтримки проекту ChangingThePresent.org (CTP) - онлайнової торгового майданчика для некомерційних організацій і жертводавців (див. додаткові матеріали ). Проект CTP дозволяє благодійним організаціям розміщувати інформацію про себе і про типах заохочення жертводавців, таких як одна година спілкування з дослідником раку або комплект книг для одного студента. Майданчик дозволяє дарувальників за допомогою простої програми для онлайнових покупок робити, благодійні пожертвування в якості подарунка для іншої людини. Збір всієї цієї інформації породжує проблеми, пов'язані з логістикою, тому я спрощую процес за допомогою кінцевого автомата.

Для цього я використовую плагін, розроблений третьою Скотта Барона (Scott Baron), який має назву acts_as_state_machine (cм. додаткові матеріали ). Подібно до інших Rails-плагинам acts_as_state_machine на основі можливостей Ruby і ексклюзивної функціональності Rails створює не тільки бібліотеку, а й спеціалізований мову (Domain Specific Language - DSL), що розширює можливості користувача.

Замовник надає свою інформацію на сайт CTP (стан "відправлено" - submitted). Адміністратор CTP отримує цю інформацію і може її відредагувати (стан "обробка" - processing). Якщо СТР вносить зміни, то некомерційна організація повинна переглянути їх (стан "проглядається організацією" - nonprofit_reviewing). Коли СТР або організація схвалюють інформацію, то інформація може бути показана на сайті CTP (стан "схвалено" - accepted). На малюнку 1 показано графічне зображення кінцевого автомата.

Малюнок 1. Кінцевий автомат для CTP
перетинаючи кордони   Анатомія плагіна acts_as   Серія контенту:   Цей контент є частиною # з серії # статей: Перетинаючи кордони   https://www

За допомогою плагіна я можу безпосередньо представити мій клас у вигляді автомата, використовуючи DSL, щоб відобразити різні стани, переходи між ними і події, що викликають ці переходи. У лістингу 1 приведена спрощена версія кінцевого автомата, який я використовував для управління благодійними організаціями в CTP.

Лістинг 1. Приклад автомата

class Nonprofit <ActiveRecord :: Base acts_as_state_machine: initial =>: created,: column => 'status' # These are all of the states for the existing system. state: submitted state: processing state: nonprofit_reviewing state: accepted event: accept do transitions: from =>: processing,: to =>: accepted transitions: from =>: nonprofit_reviewing,: to =>: accepted end event: receive do transitions : from =>: submitted,: to =>: processing end # either a CTP or nonprofit user edits the entry, requiring a review event: send_for_review do transitions: from =>: processing,: to =>: nonprofit_reviewing transitions: from = >: nonprofit_reviewing,: to =>: processing transitions: from =>: accepted,: to =>: nonprofit_reviewing end

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

Кожен вираз відповідає синтаксису Ruby. Після визначення класу слід acts_as_state_machine: initial =>: created,: column => 'status'. Для Java розробника може здатися дивним, що наведений виклик методу замість його визначення. Ruby посилається на виклики цих методів, оголошених на рівні класу, як на макроси. Макроси часто використовуються в Ruby, щоб додавати функціональності класу в момент його завантаження. Насправді визначення методів def - це просто макроси Ruby.

Потім наведено набір станів, наприклад, state: submitted. Це виклики методів, кожен з яких приймає на вхід єдиний параметр - символ (певне користувачем ім'я). Команда event - це теж виклик методу, який приймає символ, що визначає назву події, і Зміст, визначальним переходи для цієї події.

Кожен перехід це виклик методу, супроводжуваний хеш-таблицею. В Ruby хеш-таблиця представляється у вигляді набору пар key => value, розділених комами і поміщеними в {}. Коли хеш-таблиця використовується в якості останнього параметра при виклику функції, то дужки не обов'язкові. Можна бачити, що ці методи - стану, переходи і події - в поєднанні зі своїм спорядженням і хеш-таблицями утворюють повноцінний DSL.

Щоб використовувати автомат, я можу створити об'єкт типу Nonprofit і викликати на ньому методи для кожної події, завершуючи їх символом!, Як показано в лістингу 2.

Лістинг 2. Маніпулювання автоматом.

>> np = Nonprofit.find (2) => ... >> np.current_state =>: submitted >> np.receive! => True >> np.accept! => True >> np.current_state =>: accepted

Символ! використовується в Ruby для методів, які змінюють і зберігають параметр за один прийом. Так що вимоги до плагіну для кінцевого автомата досить очевидні. потрібно:

  • зручне місце для зберігання коду автомата,
  • спосіб для визначення в класах методів, які будуть потрібні мені для DSL,
  • спосіб підключити методи до класу Nonprofit або до будь-якого іншого класу.

У решти статті ми детально розберемо плагін. Якщо ви хочете дослідити код по ходу читання, скачайте плагін acts_as_state_machine. В додаткових матеріалах наведено посилання на сайт Скотта Баррона (Scott Barron), де можна, слідуючи інструкціям, скачати плагін через Subversion. Перейдіть в каталог trunk / lib, де знаходиться файл act_as_state_machine.rb. Ініціалізувалися код знаходиться в файлі trunk / init.rb. Вам будуть потрібні тільки ці два файли.

Модулі типу acts_as

В принципі все плагіни типу acts_as працюють однаково. Для побудови acts_as модуля виконайте наступні дії:

  1. Створіть модуль. Назва методу класу (не започатковано макросу) повинно починатися з acts_as_.
  2. У Ініціалізується коді відкрийте базовий клас ActiveRecord і додайте свій модуль acts_as_.
  3. Розширте поведінку використовуваного класу в функції acts_as_, наприклад, acts_as_state_machine.

Ознайомтеся з ініціалізувалися кодом з файлу init.rb, наведеними в лістингу 3.

Лістинг 3. Ініціалізувалися код для acts_as_state_machine

require 'acts_as_state_machine' ActiveRecord :: Base.class_eval do include ScottBarron :: Acts :: StateMachine end

Цей код відкриває базовий клас ActiveRecord (ActiveRecord :: Base) і додає acts_as_state_machine. Метод class_eval відкриває клас і запускає наступне замикання в контексті зазначеного класу. І це все. Насправді принцип дуже простий: код розкриває базовий клас ActiveRecord і підмішує в нього модуль ScottBarron :: Acts :: StateMachine. В Ruby можна розкрити будь-який клас і швидко його перевизначити.

Ця можливість Ruby - одна з найсильніших його сторін, оскільки вона значно підвищує його гнучкість. Але ця ж можливість може привести і до слабкості. Занадто велика гнучкість може призвести до коду, який важко розуміти і підтримувати, так що будьте обережні. Тепер відкрийте файл acts_as_state.rb, щоб побачити, який код буде доданий.

Ініціалізація модуля.

На цьому етапі я відійду від деталей реалізації автомата. Замість цього я сконцентруюся на демонстрації інтерфейсу автомата через плагін. У лістингу 4 наведено визначення модуля і частина інтерфейсу власне автомата.

Лістинг 4. Структура модуля

module Acts #: nodoc: module StateMachine #: nodoc: class InvalidState <Exception #: nodoc: end class NoInitialState <Exception #: nodoc: end def self.included (base) #: nodoc: base.extend ActMacro end module SupportingClasses class State attr_reader: name def initialize ... end def entering ... end ... end class StateTransition attr_reader: from,: to,: opts def initialize ... end def perform ... end ... end class Event .. . def fire ... end def transitions ... end ... end

вгорі лістингу 4 можна побачити вкладене оголошення модуля. У модуля є визначення методів, але немає базової ієрархії успадкування. Замість цього модуль можна підключити до будь-якого існуючого класу Ruby. Якщо такий підхід для вас в новинку, можете вважати, що модуль - це інтерфейс плюс реалізація цього інтерфейсу. Перевага модуля в тому, що його функціональність можна підключити до будь-якого існуючого класу Ruby, і таких модулів можна підключити скільки завгодно. Також можна підсилити існуючі можливості класу. Ця техніка називається змішуванням (mixing). У C ++ для отримання подібного результату використовується множинне спадкування, але це породжує безліч значних ускладнень. Розробники Java відмовилися від множинного спадкоємства, щоб позбутися від цих ускладнень. За допомогою модулів можна реалізувати деякі переваги множинного спадкоємства без ускладнень. Такі мови, як Smalltalk і Python, також підтримують успадкування шляхом змішування.

У решти лістингу 4 наведені деталі, що відносяться до реалізації автомата. Вам достатньо просто знати, що ці класи представляють автономну реалізацію автомата. Залишок коду більш цікавий, так як він представляє інтерфейс автомата клієнтам плагінa.

модулі acts_as

Нагадаю, що розробнику плагіна потрібні три речі: місце, щоб помістити реалізацію, спосіб надати DSL (методи класу), і спосіб надати автомату доступ до методів екземпляра. Це включає в себе методи подій, які ви бачили в лістингу 3 . В лістингу 4 представлено місце для розміщення реалізації. Наступний фрагмент займається обробкою DSL.

У архітектури плагінів типу acts_as є одна точка прив'язки: макрос acts_as. Клієнти acts_as плагінa вводять цей метод з викликом методу в цільовому класі. У моєму випадку, я викликаю acts_as в лістингу 1 в класі Nonprofit в цьому фрагменті коду:

acts_as_state_machine: initial =>: created,: column => 'status'

Тепер розберемо лістинг 5, в якому оголошується макрос модуль ActMacro для плагінa acts_as_state_machine. Цей клас обробляє параметри для модуля і надає різні методи класу і примірника.

Лістинг 5. Додавання acts_as

module ActMacro # Configuration options are # # * + column + - specifies the column name to use for keeping the state (default: state) # * + initial + - specifies an initial state for newly created objects (required) def acts_as_state_machine (opts) self. extend (ClassMethods) raise NoInitialState unless opts [: initial] write_inheritable_attribute: states, {} write_inheritable_attribute: initial_state, opts [: initial] write_inheritable_attribute: transition_table, {} write_inheritable_attribute: event_table, {} write_inheritable_attribute: state_column, opts [: column] || 'State' class_inheritable_reader: initial_state class_inheritable_reader: state_column class_inheritable_reader: transition_table class_inheritable_reader: event_table self.send (: include, ScottBarron :: Acts :: StateMachine :: InstanceMethods) before_create: set_initial_state after_create: run_initial_state_actions end end

У модулі з лістингу 5 є єдиний метод: acts_as_state_machine. Це метод виконує п'ять дій:

  • оголошує методи класу,
  • обробляє виняткові ситуації, що скидаються автоматом,
  • управляє параметрами,
  • оголошує методи примірника,
  • управляє фільтрами, що спрацьовують до і після методу.

Метод acts_as_state_machine спочатку оголошує методи класу. Повний зміст цих методів можна подивитися в лістингу 6. ​​Потім цей метод обробляє виключення. В даному випадку єдиним винятком є ​​ситуація, коли клієнт не вказує початковий стан. Коротко пройдемося по успадкованим атрибутам - докладно ми розберемо їх пізніше. Метод self.send оголошує методи екземпляра. Вони наведені в лістингу 7. Нарешті, фільтри before (до) і after (після) - це макроси ActiveRecord, які викликають set_initial_state і run_initial_state_actions до і після того, як ActiveRecord створює запис.

Повернемося до макросам write_inheritable_attribute і class_inheritable_reader. ВВи можете задатися питанням, чому модуль не використовує просте успадкування. Причина дуже проста - модуль зберігає ієрархію спадкування в собі. Ці макроси дозволяють модулю проектувати свої атрибути на цільової клас - в даному випадку, Nonprofit. Найбільш важливі параметри - це state_column і набір таблиць переходів, що містять стану, події і переходи. Тепер прийшов час додати методи класу, які утворюють DSL.

Додавання методів класу і методів екземпляра

У лістингу 6 нарешті, можна побачити реалізацію DSL.

Лістинг 6. Методи класу модуля act_as_state_machine

module ClassMethods def states read_inheritable_attribute (: states) .keys end def event (event, opts = {}, & block) tt = read_inheritable_attribute (: transition_table) et = read_inheritable_attribute (: event_table) e = et [event.to_sym] = SupportingClasses :: Event.new (event, opts, tt, & block) define_method ( "# {event.to_s}!") {e.fire (self)} end def state (name, opts = {}) state = SupportingClasses :: State. new (name.to_sym, opts) read_inheritable_attribute (: states) [name.to_sym] = state define_method ( "# {state.name}?") {current_state == state.name} end ...

Макроси state і event, як і обіцялося, виявилися простими методами, оголошеними в модулі, званому ClassMethods. Метод event зчитує параметр з таблиці переходів, а потім параметр з таблиці подій. Метод додає подія в таблицю подій, потім динамічно визначає метод для події, підключаючи новий метод до методу fire на подію event.

Після методу event модуль визначає метод state. Цей метод зчитує таблицю станів і додає новий стан. Таким способом в цільової клас додається зручний метод, який повертає true, якщо об'єкт знаходиться в поточному стані. Наприклад, код nonprofit.submitted поверне true, якщо прапор стану буде submitted. Тепер DSL підтримується повністю.

Методи примірника працюють точно також як методи класу. Ці методи наведені в лістингу 7.

Лістинг 7. Методи примірника для модуля acts_as_state_machine

module InstanceMethods def set_initial_state write_attribute self.class.state_column, self.class.initial_state.to_s end ... def current_state self.send (self.class.state_column) .to_sym end ... end

Макрос ActMacro відкриває клас і додає ці методи. Мені не потрібно проходити через макрос read_inheritable_attribute, щоб використовувати параметри, так як існують змінні екземпляра класу, певні ActiveRecord. Я навів тільки методи, які встановлюють початкове і повертають поточний стан. Решта працюють аналогічно.

Перший метод в лістингу 7 встановлює початковий стан, оновлюючи існуючий стовпець в об'єкті ActiveRecord. Нагадаю, що я задаю назва стовпчика при виклику ActMacro. Метод current_state просто повертає значення змінної примірника. Метод send викликає метод на ім'я єдиного символьного параметра, в даному випадку це назва state_column.

Висновок

Можна подуматі, что Було б простіше побудуваті автомат и використовуват его як бібліотеку. Однако плагін acts_as набагато могутніше. ВІН дозволяє Фактично Додати стовпець для автомата в базу Даних. Інші плагіни дозволяють управляти версіями, будувати історії аудиту, обробляти зображення і виконувати сотні інших простих завдань так, як якщо б ці завдання були прозорою інтеграцією між середовищем виконання Rails і базою даних.

Можливо, вам доводилося використовувати мову Java для інтеграції плагінів Eclipse, завдань Ant або бібліотек Spring c вашим кодом або для підключення EJB-компонентів. Безліч ідей з Java-спільноти змінили уявлення розробників про розширення функціональності. Це коротке знайомство в плагінами Ruby типу acts_as показує новий спосіб вирішення подібних завдань. Гнучкість мови Ruby змінила мій погляд на розширення функціональності. Плагін acts_as дозволяє новому поколінню розробників спробувати себе в написанні розширень. Результатом буде нова хвиля розширень для Ruby. Багато з цих прийомів також доступні і Java-розробникам через аспектно-орієнтоване програмування або удосконалення байт-коду.

Наступного разу я завершу цю серію статей докладним порівнянням підходів до вирішення однієї складного завдання з використанням Ruby і мого досвіду застосування Java платформи. Поки ж - продовжуйте перетинати кордони.

Ресурси для скачування

Схожі тими

  • Оригінал статті (EN).
  • Java To Ruby: Things Every Manager Should Know (EN) (Pragmatic Bookshelf, 2006): книга автора статті про те, коли і де можна буде переключитися з мови програмування Java на Ruby on Rails і як це зробити.
  • Beyond Java (EN) (O'Reilly, 2005): книга автора статті про розвиток і зрілості мови Java і про технології, які можуть змагатися з платформою Java в деяких областях.
  • Plugins in Ruby on Rails : Ознайомтеся з документацією по архітектурі плагінів для Rails.
  • acts_as_state_machine : Плагін для Rails, який дозволяє моделі ActiveRecord діяти як кінцевий автомат.
  • Changing The Present : Сайт для некомерційних організацій, побудований на Ruby on Rails, з якого був узятий приклад для цієї статті.
  • Class and instance variables in Ruby (EN) (John Nunemaker, RailsTips.org, листопад 2006): робота зі змінними класу і примірника, коли ви використовуєте множинне спадкування, може здатися важким. У цій статті пропонується прийом, званий успадкованими атрибутами.
  • розділ технологія Java : Сотні статей з усіх аспектів Java-програмування.

Підпішіть мене на ПОВІДОМЛЕННЯ до коментарів

Jsp?
Name}?
Провайдеры:
  • 08.09.2015

    Batyevka.NET предоставляет услуги доступа к сети Интернет на территории Соломенского района г. Киева.Наша миссия —... 
    Читать полностью

  • 08.09.2015
    IPNET

    Компания IPNET — это крупнейший оператор и технологический лидер на рынке телекоммуникаций Киева. Мы предоставляем... 
    Читать полностью

  • 08.09.2015
    Boryspil.Net

    Интернет-провайдер «Boryspil.net» начал свою работу в 2008 году и на данный момент является одним из крупнейших поставщиков... 
    Читать полностью

  • 08.09.2015
    4OKNET

    Наша компания работает в сфере телекоммуникационных услуг, а именно — предоставлении доступа в сеть интернет.Уже... 
    Читать полностью

  • 08.09.2015
    Телегруп

    ДП «Телегруп-Украина» – IT-компания с 15-летним опытом работы на рынке телекоммуникационных услуг, а также официальный... 
    Читать полностью

  • 08.09.2015
    Софтлинк

    Высокая скоростьМы являемся участником Украинского центра обмена трафиком (UA — IX) с включением 10 Гбит / сек... 
    Читать полностью