Ruby — это гибкий язык, позволяющий создавать на своей основе различные DSL. Появилась идея написать DSL для SQL-запросов (для SELECT'ов), максимально приближенный к оригиналу. Кажется, получилось. Получившаяся штука называется Boroda. Далее следуют примеры кода.
Получим:
Если кто не понял — мы пишем SQL-запросы на чистом Руби. Теперь попробуем что-нибудь посложнее.
Получим:
Теперь о том, как писать запросы при помощи Boroda. Из-за некоторых технических затруднений пришлось поменять порядок SQL-операторов. Сначало обязательно должен идти from. В нем имя таблицы указывается как символ (Symbol). Можно задавать алиасы для таблиц, если задавать их список в виде хеша, как сделано во втором примере. Далее должны следовать join'ы. Будет проще, записать возможный порядок вызова методов в следующем виде:
Другими словами все методы из последней группы вы можете вызывать в произвольном порядке. Boroda сама позаботиться о том чтобы получить правильный SQL-запрос.
Теперь подробнее про condition который используется в where и having. Использование следующих операторов имеет точно такой же смысл, какой они имеют в SQL:
+, -, *, /, >, <, >=, <=.
Из-за некоторых ограничений Руби на перегрузку операторов, пришлось немного поизвращаться:
Очень важно: при использовании последних двух операторов операнды нужно ОБЯЗАТЕЛЬНО заключать в скобки, иначе в SQL-запросе может получится совсем не то, что вы ожидаете. Это вызвано тем, что данные два оператора имеют высокий приоритет в Руби.
На данный момент Boroda — это маленький модуль, генерящий SQL для SELECT-запросов. Код модуля лежит на GitHub'е. Если у кого-то возникнет желание, то Boroda может стать gem'ом.
Заранее предупреждаю, что пока не рекомендую использовать этот код в production'e, т. к. он еще не достаточно протестирован и может быть неустойчив к SQL-инъекциям.
Очень интересно услышать ваше мнение про этот DSL и получить полезные советы по улучшению :)
require 'boroda' sql = Boroda.build do from :posts, :users select posts.* where (posts.author_id == users.id) & (users.name == 'Vlad Semenov') end
Получим:
SELECT posts.*
FROM posts, users
WHERE (posts.author_id = users.id) AND (users.name = 'Vlad Semenov')
Если кто не понял — мы пишем SQL-запросы на чистом Руби. Теперь попробуем что-нибудь посложнее.
min_rating = 5 sql = Boroda.build do from :posts => :p left join :comments => :c on c.post_id == p.id select p.id, p.title, p.content, c.id.count => :comment_count group by p.id where (p.title.like '%programming%') | # Выбираем все посты, содержащие в заголовке 'programming' (p.rating > min_rating) # Или с рейтингом больше 5-ти order by p.created_at.desc limit 10 offset 20 end
Получим:
SELECT p.id, p.title, p.content, COUNT(c.id) AS comment_count FROM posts AS p LEFT JOIN comments AS c ON c.post_id = p.id WHERE (p.title LIKE '%programming%') OR (p.rating > 5) GROUP BY p.id ORDER BY p.created_at DESC LIMIT 10 OFFSET 20
Теперь о том, как писать запросы при помощи Boroda. Из-за некоторых технических затруднений пришлось поменять порядок SQL-операторов. Сначало обязательно должен идти from. В нем имя таблицы указывается как символ (Symbol). Можно задавать алиасы для таблиц, если задавать их список в виде хеша, как сделано во втором примере. Далее должны следовать join'ы. Будет проще, записать возможный порядок вызова методов в следующем виде:
from tables [[left|right] [outer|inner] join tables on condition | using columns [..]] [select columns] [ where condition | group by columns | having condition | order by columns | limit number | offset number ]*
Другими словами все методы из последней группы вы можете вызывать в произвольном порядке. Boroda сама позаботиться о том чтобы получить правильный SQL-запрос.
Теперь подробнее про condition который используется в where и having. Использование следующих операторов имеет точно такой же смысл, какой они имеют в SQL:
+, -, *, /, >, <, >=, <=.
Из-за некоторых ограничений Руби на перегрузку операторов, пришлось немного поизвращаться:
a == b # => a = b a <=> b # => a <> b (a) & (b) # => (a) AND (b) (a) | (b) # => (a) OR (b)
Очень важно: при использовании последних двух операторов операнды нужно ОБЯЗАТЕЛЬНО заключать в скобки, иначе в SQL-запросе может получится совсем не то, что вы ожидаете. Это вызвано тем, что данные два оператора имеют высокий приоритет в Руби.
На данный момент Boroda — это маленький модуль, генерящий SQL для SELECT-запросов. Код модуля лежит на GitHub'е. Если у кого-то возникнет желание, то Boroda может стать gem'ом.
Заранее предупреждаю, что пока не рекомендую использовать этот код в production'e, т. к. он еще не достаточно протестирован и может быть неустойчив к SQL-инъекциям.
Очень интересно услышать ваше мнение про этот DSL и получить полезные советы по улучшению :)