Опыт создания DSL на Ruby. Где применить, как готовить и сколько добавить метапрограммирования.
Антон Сидельников[email protected]
Содержание
• О самой концепции DSL• Чем Ruby хорош для DSL• Использование Ruby-based DSL в
реальных проектах• Ruby-based DSL Roadmap
2
Предметно-ориентированный язык (Domain Specific Language)
• Предназначен для решения узкого круга задач
• Понятия предметной области являются элементарными
• Задачи решаются в терминах предметной области
3
Как можно применить DSL
• Конфиги (apache, etc.), Make• Языки описания данных (HTML, TeX)• Язык описания правил (ipfw)• Язык для написания тестов• Высокоуровневый язык для написания
бизнес-логики
4
Использование DSL
Pro•Повышение уровня абстракции
•Более явная логика кода
•Повышение скорости разработки
•Можно передать управление не-программисту
Contra•Расходы на создание и поддержку языка
•Область применения действительно мала
•Требует обучения пользователя
5
Два подхода к созданию DSL
External•Придумываем грамматику
•Пишем интерпретатор...
•…транслятор...
•...или компилятор (если скучно)
Internal•Выбираем язык по вкусу
•Используем синтаксис и семантику старого языка для создания иллюзии нового
6
Ruby-based DSL
• Internal DSL• Поддерка IDE• Доступ к Ruby gems и прочему
существующему коду• Еще большая скорость разработки• Гибкий синтаксис, обилие syntax sugar• Развитая система метарограммирования• Возможность интеграции с другими языками
7
bender = Person.new 'Bender'bender.wishes :lunapark, :with => [:blackjack, :hookers]bender.wishes :death, :to_all => :humans
Bender.wishes {lunapark with blackjack, hookersdeath to_all humans
}
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"} ); });
8
“По моему опыту, большинство грамотно написанных на Ruby программ уже являются DSL – просто по природе синтаксиса Ruby.”
- Jamis Buck, 37 signals
9
Простой пример
<?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
Простой пример
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
Жизненный пример
monkey – язык для функционального тестирования промышленных энергетических сетей. Основные функции:
• Описание тест-кейсов• Сбор статистики• Анализ результатов исполнения тест-
кейсов
12
Исходная инфраструктура системы
• Система управления сетями, написанная на Java
• Доступ к данным через удаленную SQL-базу• Надо тестировать код на mockup-системах• Нужен контроль состояния работающих
систем
13
Исходная ситуация с тестированием
А как нам писать функциональные тесты
для диагностики работающи систем?
А как нам писать функциональные тесты
для диагностики работающи систем?Окей, вот вам
WebAPI Окей, вот вам
WebAPI
СерверСервер
Java CoreJava Core SQL-базаSQL-база
WebAPIWebAPI SSH-доступSSH-доступ А тесты на PHP писать?!А тесты на PHP писать?!
14
Решение: JRuby(в других условиях здесь могли бы быть С-Ruby,
IronRuby, а может и другие языки, например Scala или Clojure)
15
JRuby
• Выполняется на JVM• Прямая работа с Java-объектами через
публичные интерфейсы• Контроль результатов и сбор данных
напрямую из SQL-базы• Обработка данных и анализ результатов
на стороне Ruby• Быстро и эффективно
16
Результат
СерверСерверJava CoreJava Core
Ruby DSL-frameworkRuby DSL-framework
Запросы к ядру
Запросы к ядру
Сбор данных из БД
Сбор данных из БД
Методы анализа данных
Методы анализа данных
А где PHP, к которому мы уже привыкли?!
А где PHP, к которому мы уже привыкли?!
SQL-базаSQL-база
17
РезультатTestTouCostOneDay = testcase(MockBaseTestCase, 'TestTouCostOneDay') {
request categorizedMetricDataRequest(subscriber, sdp) { metric(Cost) granularity(DAY) interval(accountInterval.start, accountInterval.start) timezone("PST") }
validate {granularity.should == DAYprocessor(TouProcessor).cost.should
be_granular_limiteddpAccount.servicePlan.costLimit
}}
18
Как подойти к созданию Ruby-DSL?
19
DSL Roadmap
• Начните с фиксации предполагаемого DSL в виде валидного Ruby-синтаксиса
• Не увлекайтесь моделью – все равно её переделывать
• Сразу подумайте о том, как вы хотите соединить DSL с существующей средой
20
DSL Roadmap
• Используйте Test-Driven Development – это естественный способ опробовать язык до того, как он окончательно создан
• Ведите разработку малыми итерациями• Согласуйте вид DSL с экспертом предметной
области, если сами им не являетесь
21
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
DSL Roadmap
• Минимизируйте зашумленность языка• Используйте syntax sugar:
• опциональные скобки и терминальные символы
• символы вместо строк• блоки• литеральные массивы и хэши• группировка аргументов
23
DSL Roadmap
• Зафиксируйте модель, когда вы целиком разобрались с внешним видом языка
• Наведите порядок, проведите рефакторинг
24
Дополнительная информация:
• Книга “Metaprogramming Ruby” из серии “The Pragmatic Bookshelf”
• http://rubyconf2008.confreaks.com/advanced-dsls-in-ruby.html
• http://obiefernandez.com/presentations/obie_fernandez-agile_dsl_development_in_ruby.pdf
• Martin Fowler “Domain Specific Languages”
25
О чем это все было?
• Существуют случаи, когда использование DSL оправдано
• Динамические языки позволяют сравнительно дешево получать это преимущество
• Современные языки можно интегрировать в широкий класс промышленных проектов
• Лучший способ оценить плюсы и минусы – попробовать
26