CodeFest 2012. Сидельников А. — Опыт создания DSL на Ruby. Где применить, как готовить и сколько добавить метапрограммирования
1. Опыт создания DSL на Ruby.
Где применить, как готовить и
сколько добавить
метапрограммирования.
Антон Сидельников
ndmeredian@gmail.com
2. Содержание
• О самой концепции DSL
• Чем Ruby хорош для DSL
• Использование Ruby-based DSL в
реальных проектах
• Ruby-based DSL Roadmap
2
3. Предметно-ориентированный язык
(Domain Specific Language)
• Предназначен для решения узкого
круга задач
• Понятия предметной области являются
элементарными
• Задачи решаются в терминах
предметной области
3
4. Как можно применить DSL
• Конфиги (apache, etc.), Make
• Языки описания данных (HTML, TeX)
• Язык описания правил (ipfw)
• Язык для написания тестов
• Высокоуровневый язык для написания
бизнес-логики
4
5. Использование DSL
Pro Contra
•Повышение уровня абстракции •Расходы на создание и
•Более явная логика кода поддержку языка
•Повышение скорости разработки •Область применения
действительно мала
•Можно передать управление не-
программисту •Требует обучения пользователя
5
6. Два подхода к созданию DSL
External Internal
•Придумываем грамматику •Выбираем язык по вкусу
•Пишем интерпретатор... •Используем синтаксис и
•…транслятор... семантику старого языка для
•...или компилятор (если скучно) создания иллюзии нового
6
7. Ruby-based DSL
• Internal DSL
• Поддерка IDE
• Доступ к Ruby gems и прочему
существующему коду
• Еще большая скорость разработки
• Гибкий синтаксис, обилие syntax sugar
• Развитая система метарограммирования
• Возможность интеграции с другими языками
7
8. Person bender = new Person("Bender");
bender.wish("lunapark", new HashMap<String, String[]>() {{ put(“with", new String[]{"blackjack", "hookers"} ); }});
bender.wish("death", new HashMap<String, String[]>() {{ put("to_all", new String[]{"humans"} ); });
bender = Person.new 'Bender'
bender.wishes :lunapark, :with => [:blackjack, :hookers]
bender.wishes :death, :to_all => :humans
Bender.wishes {
lunapark with blackjack, hookers
death to_all humans
}
8
9. “По моему опыту, большинство грамотно
написанных на Ruby программ уже являются
DSL – просто по природе синтаксиса Ruby.”
- Jamis Buck, 37 signals
9
10. Простой пример
Перепиши мне
конфиг, а то я тут
ничего не понимаю..!
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<repository>
<solver class="implementation.solvers.SempSolver">
<setup>
<param type="string" name="SOLVER_HOME">"~/.wine/drive_c/semp/"</param>
</setup>
<method name="EfficiencyTrendAnalysis" input="reflexive">
<param type="string"
name="MODULE_PATH">"modules/EfficiencyTrendAnalysis/"<param>
...
</method>
...
</solver>
</repository>
10
11. Простой пример
Перепиши мне
конфиг, а то я тут
ничего не понимаю..!
require 'repo'
solver("implementation.solvers.SempSolver") {
SOLVER_HOME = "~/.wine/drive_c/semp/"
MODULES_HOME = File.join SOLVER_HOME, "modules"
method {
name "EfficiencyTrendAnalysis" Сам перепиши!
MODULE_PATH = File.join MODULES_HOME, name
}
}
11
12. Жизненный пример
monkey – язык для функционального
тестирования промышленных
энергетических сетей.
Основные функции:
• Описание тест-кейсов
• Сбор статистики
• Анализ результатов исполнения тест-
кейсов
12
13. Исходная инфраструктура
системы
• Система управления сетями, написанная на
Java
• Доступ к данным через удаленную SQL-базу
• Надо тестировать код на mockup-системах
• Нужен контроль состояния работающих
систем
13
14. Исходная ситуация с
тестированием
А как нам писать
функциональные тесты
для диагностики
Сервер
Окей, вот вам работающи систем?
WebAPI
Java Core SQL-база
WebAPI SSH-доступ А тесты на PHP писать?!
14
15. Решение: JRuby
(в других условиях здесь могли бы быть С-Ruby,
IronRuby, а может и другие языки, например Scala
или Clojure)
15
16. JRuby
• Выполняется на JVM
• Прямая работа с Java-объектами через
публичные интерфейсы
• Контроль результатов и сбор данных
напрямую из SQL-базы
• Обработка данных и анализ результатов
на стороне Ruby
• Быстро и эффективно
16
17. Результат
Сервер
Java Core SQL-база
Запросы к Сбор данных Методы анализа
ядру из БД данных
Ruby DSL-framework
А где PHP, к которому мы уже
привыкли?!
17
20. DSL Roadmap
• Начните с фиксации предполагаемого DSL в
виде валидного Ruby-синтаксиса
• Не увлекайтесь моделью – все равно её
переделывать
• Сразу подумайте о том, как вы хотите
соединить DSL с существующей средой
20
21. DSL Roadmap
• Используйте Test-Driven Development – это
естественный способ опробовать язык до того,
как он окончательно создан
• Ведите разработку малыми итерациями
• Согласуйте вид DSL с экспертом предметной
области, если сами им не являетесь
21
22. DSL Roadmap
• Разберитесь с тем, что может, и что не может
Ruby. Метапрограммирование является
основой создания DSL.
• Убедитесь, что знаете все эти слова:
• eval, instance_eval и class_eval
• define_method и alias_method
• method_missing и const_missing
• Open class(1.8) или Refinements (1.9)
• Но повторяю – не увлекайтесь моделью!
22
23. DSL Roadmap
• Минимизируйте зашумленность языка
• Используйте syntax sugar:
• опциональные скобки и терминальные
символы
• символы вместо строк
• блоки
• литеральные массивы и хэши
• группировка аргументов
23
24. DSL Roadmap
• Зафиксируйте модель, когда вы целиком
разобрались с внешним видом языка
• Наведите порядок, проведите рефакторинг
24
25. Дополнительная
информация:
• Книга “Metaprogramming Ruby” из серии “The Pragmatic
Bookshelf”
• http://rubyconf2008.confreaks.com/advanced-dsls-in-ruby.html
• http://obiefernandez.com/presentations/obie_fernandez-agile_d
• Martin Fowler “Domain Specific Languages”
25
26. О чем это все было?
• Существуют случаи, когда использование DSL
оправдано
• Динамические языки позволяют сравнительно
дешево получать это преимущество
• Современные языки можно интегрировать в
широкий класс промышленных проектов
• Лучший способ оценить плюсы и минусы –
попробовать
26