SlideShare ist ein Scribd-Unternehmen logo
1 von 62
Downloaden Sie, um offline zu lesen
встраивание языка в строковой
интерполятор
.
Михаил Лиманский
14 августа 2015 г.
FPConf 2015
введение
.
Строковая интерполяция – это просто
val language = ”English”
.
Строковая интерполяция – это просто
val language = ”English”
val embedded = s”Embedded $language”
.
MongoDB
MongoDB – документо-ориентированная база данных,
хранящая документы BSON.
db.people.insert({ name: ”John Doe”, age: 42 })
.
MongoDB
MongoDB – документо-ориентированная база данных,
хранящая документы BSON.
db.people.insert({ name: ”John Doe”, age: 42 })
db.people.insert({
name: ”William Smith”,
age: 28,
phone: [ ”1234567”, ”7654321” ]
})
.
MongoDB
MongoDB – документо-ориентированная база данных,
хранящая документы BSON.
db.people.insert({ name: ”John Doe”, age: 42 })
db.people.insert({
name: ”William Smith”,
age: 28,
phone: [ ”1234567”, ”7654321” ]
})
db.people.insert({
name: ”Alice White”,
age: 29,
address: {
country: ”UK”,
city: ”London”
}
})
.
MongoDB
MongoDB – документо-ориентированная база данных,
хранящая документы BSON.
db.people.insert({ name: ”John Doe”, age: 42 })
db.people.insert({
name: ”William Smith”,
age: 28,
phone: [ ”1234567”, ”7654321” ]
})
db.people.insert({
name: ”Alice White”,
age: 29,
address: {
country: ”UK”,
city: ”London”
}
})
db.people.insert({ name : ”Ivan Petrov”, age : 28 })
.
Поиск и обновление
db.people.find({ name: ”John Doe”})
db.people.find({ age: { $lt : 30 }})
db.people.find({ phone: { $not: { $size : 0 }}})
.
Поиск и обновление
db.people.find({ name: ”John Doe”})
db.people.find({ age: { $lt : 30 }})
db.people.find({ phone: { $not: { $size : 0 }}})
db.people.update({ age : 42},
{ $set : { name : ”Ford Prefect” } })
.
Поиск и обновление
db.people.find({ name: ”John Doe”})
db.people.find({ age: { $lt : 30 }})
db.people.find({ phone: { $not: { $size : 0 }}})
db.people.update({ age : 42},
{ $set : { name : ”Ford Prefect” } })
db.people.aggregate(
[ { $group : { _id : ”$age”, count : {$sum : 1} } },
{ $sort : { count : -1 } },
{ $limit : 5 }
])
.
MongoDB в Scala
Основные драйвера для работы с MongoDB:
• Casbah – синхронный, поверх драйвера для Java.
.
MongoDB в Scala
Основные драйвера для работы с MongoDB:
• Casbah – синхронный, поверх драйвера для Java.
• ReactiveMongo – асинхронный, основан на акторах
Akka.
.
MongoDB в Scala
Основные драйвера для работы с MongoDB:
• Casbah – синхронный, поверх драйвера для Java.
• ReactiveMongo – асинхронный, основан на акторах
Akka.
• Tepkin – реактивный, на Akka IO и Akka Streams.
.
Пример использования Casbah
val name = ”John Doe”
people.insert(MongoDBObject(
”name” -> ”James Bond”,
”age” -> 80,
”phone” -> List(”007007”),
”address” -> MongoDBObject(”country” -> ”UK”)))
.
Пример использования Casbah
val name = ”John Doe”
people.insert(MongoDBObject(
”name” -> ”James Bond”,
”age” -> 80,
”phone” -> List(”007007”),
”address” -> MongoDBObject(”country” -> ”UK”)))
val a = people.findOne(MongoDBObject(”name” -> name))
val b = people.find(MongoDBObject(”age” ->
MongoDBObject(”$lt” -> 30)))
.
Пример использования Casbah
val name = ”John Doe”
people.insert(MongoDBObject(
”name” -> ”James Bond”,
”age” -> 80,
”phone” -> List(”007007”),
”address” -> MongoDBObject(”country” -> ”UK”)))
val a = people.findOne(MongoDBObject(”name” -> name))
val b = people.find(MongoDBObject(”age” ->
MongoDBObject(”$lt” -> 30)))
// Using Casbah DSL
val c = people.find(”age” $lt 30)
val d = people.find(”phone” -> $not(_ $size 0))
people.update(MongoDBObject(”age” -> 42),
$set(”name” -> ”Ford Prefect”))
.
Пример использования Casbah
val name = ”John Doe”
people.insert(MongoDBObject(
”name” -> ”James Bond”,
”age” -> 80,
”phone” -> List(”007007”),
”address” -> MongoDBObject(”country” -> ”UK”)))
val a = people.findOne(MongoDBObject(”name” -> name))
val b = people.find(MongoDBObject(”age” ->
MongoDBObject(”$lt” -> 30)))
// Using Casbah DSL
val c = people.find(”age” $lt 30)
val d = people.find(”phone” -> $not(_ $size 0))
people.update(MongoDBObject(”age” -> 42),
$set(”name” -> ”Ford Prefect”))
val e = people.aggregate(List(
MongoDBObject(”$group” ->
MongoDBObject(”_id” -> ”$age”, ”count” ->
MongoDBObject(”$sum” -> 1))),
MongoDBObject(”$sort” -> MongoDBObject(”count” -> -1)),
MongoDBObject(”$limit” -> 5)))
.
Пример использования Casbah
.
Пример работы с ReactiveMongo
// Future[BSONDocument]
val a = people.find(BSONDocument(”name” -> ”John Doe”))
.one[BSONDocument]
// Future[List[Person]]
val b = people.find(BSONDocument(”age” ->
BSONDocument(”$lt” -> 30))
).cursor[Person].collect[List]()
val futureUpdate = people.update(
BSONDocument(”age” -> 42),
BSONDocument(”$set” -> BSONDocument(”name” -> ”Ford Prefect”)))
// Future
val e = db.command(RawCommand(BSONDocument(
”aggregate” -> ”people”,
”pipeline” -> BSONArray(
BSONDocument(”$group” ->
BSONDocument(”_id” -> ”$age”,
”count” -> BSONDocument(”$sum” -> 1))),
BSONDocument(”$sort” -> BSONDocument(”count” -> -1)),
BSONDocument(”$limit” -> 5)))))
.
Почему мир так несправедлив?
.
Meet MongoQuery
Пример использования MongoQuery с Casbah:
import com.github.limansky.mongoquery.casbah._
val name = ”John Doe”
val a = people.findOne(mq”{ name : $name }”)
.
Meet MongoQuery
Пример использования MongoQuery с Casbah:
import com.github.limansky.mongoquery.casbah._
val name = ”John Doe”
val a = people.findOne(mq”{ name : $name }”)
val b = people.find(mq”{age : { $$lt : 30 }}”)
.
Meet MongoQuery
Пример использования MongoQuery с Casbah:
import com.github.limansky.mongoquery.casbah._
val name = ”John Doe”
val a = people.findOne(mq”{ name : $name }”)
val b = people.find(mq”{age : { $$lt : 30 }}”)
val d = people.find(
mq”{ phone : { $$not : { $$size : 0 }}}”)
people.update(mq”{ age : 42 }”,
mq”{ $$set { name : ’Ford Prefect’ }}”)
val e = people.aggregate(List(
mq”””{ $$group :
{ _id : ”$$age”, count : { $$sum : 1 }}}”””,
mq”{ $$sort : { count : -1 }}”,
mq”{ $$limit : 5}”))
.
имплементация
.
Строковая интерполяция
implicit class MongoHelper(val sc: StringContext)
extends AnyVal {
def mq(args: Any*): DBObject = {
Parser.parseQuery(sc.parts, args) match {
case Success(v, _) =>
createObject(v)
case NoSuccess(msg, _) =>
throw new MqException(s”Invalid object: $msg”)
}
}
}
.
Строковая интерполяция
implicit class MongoHelper(val sc: StringContext)
extends AnyVal {
def mq(args: Any*): DBObject = {
Parser.parseQuery(sc.parts, args) match {
case Success(v, _) =>
createObject(v)
case NoSuccess(msg, _) =>
throw new MqException(s”Invalid object: $msg”)
}
}
}
mq”{ name : $name }”
sc.parts == List(”{ name: ”, ” }”)
args = List(name)
.
Строковая интерполяция
.
Заворачиваем интерполятор в макрос
implicit class MongoHelper(val sc: StringContext) extends AnyVal {
def mq(args: Any*): DBObject = macro MongoHelper.mq_impl
}
object MongoHelper {
def mq_impl(c: Context)(args: c.Expr[Any]*):
c.Expr[DBObject] = ???
}
.
Заворачиваем интерполятор в макрос
implicit class MongoHelper(val sc: StringContext) extends AnyVal {
def mq(args: Any*): DBObject = macro MongoHelper.mq_impl
}
object MongoHelper {
def mq_impl(c: Context)(args: c.Expr[Any]*):
c.Expr[DBObject] = {
import c.universe._
val q”$cn(scala.StringContext.apply(..$pTrees))”
= c.prefix.tree
val parsed = parse(c)(pTrees)
wrapObject(c)(parsed, args.map(_.tree).iterator)
}
}
.
Заворачиваем интерполятор в макрос
object MongoHelper {
def parse(c: Context)(pTrees: List[c.Tree]) = {
import c.universe._
val parts = pTrees map {
case Literal(Constant(s: String)) => s
}
parser.parse(parts) match {
case Success(v, _) => v
case NoSuccess(msg, reader) =>
val partIndex = reader.asInstanceOf[PartReader].part
val pos = pTrees(partIndex).pos
c.abort(pos.withPoint(pos.point + reader.offset)),
s”Invalid BSON object: $msg”)
}
}
}
.
Парсим BSON
mq”{ name : $name, age : { $$gte : 18, $$lte : $max }}”
.
Парсим BSON
mq”{ name : $name, age : { $$gte : 18, $$lte : $max }}”..
Lexical
List(”{”, Field(”name”), ”:”, Placeholder, ”,”, Field(”age”),
”:”, ”{”, Keyword(”$gte”), ”:”, NumericLit(18), ”,”,
Keyword(”$lte”), ”:”, Placeholder, ”,”, ”}”, ”}”)
.
Парсим BSON
mq”{ name : $name, age : { $$gte : 18, $$lte : $max }}”..
Lexical
List(”{”, Field(”name”), ”:”, Placeholder, ”,”, Field(”age”),
”:”, ”{”, Keyword(”$gte”), ”:”, NumericLit(18), ”,”,
Keyword(”$lte”), ”:”, Placeholder, ”,”, ”}”, ”}”)..
Syntactical
Object(List(
(Member(”name”), Placeholder),
(Member(”age”), Object(List(
(Keyword(”$gte”), 18),
(Keyword(”$lte”), Placeholder))
))
))
.
Создаем объекты
protected def wrapObject(c: Context)(obj: Object,
args: Iterator[c.Tree]): c.Expr[DBType] = {
val dbparts = obj.members.map {
case (lv, v) => (lv.asString, wrapValue(c)(v, args))
}
c.Expr(q”com.mongodb.casbah.commons.MongoDBObject(..$dbparts)”)
}
.
Обрабатываем значения
protected def wrapValue(c: Context) (value: Any,
args: Iterator[c.Tree]): c.Expr[Any] = {
import c.universe._
value match {
case BSON.Placeholder =>
c.Expr(args.next())
.
Обрабатываем значения
protected def wrapValue(c: Context) (value: Any,
args: Iterator[c.Tree]): c.Expr[Any] = {
import c.universe._
value match {
case BSON.Placeholder =>
c.Expr(args.next())
case o: BSON.Object =>
wrapObject(c)(o, args)
.
Обрабатываем значения
protected def wrapValue(c: Context) (value: Any,
args: Iterator[c.Tree]): c.Expr[Any] = {
import c.universe._
value match {
case BSON.Placeholder =>
c.Expr(args.next())
case o: BSON.Object =>
wrapObject(c)(o, args)
case BSON.Id(id) =>
c.Expr(q”new org.bson.types.ObjectId($id)”)
.
Обрабатываем значения
protected def wrapValue(c: Context) (value: Any,
args: Iterator[c.Tree]): c.Expr[Any] = {
import c.universe._
value match {
case BSON.Placeholder =>
c.Expr(args.next())
case o: BSON.Object =>
wrapObject(c)(o, args)
case BSON.Id(id) =>
c.Expr(q”new org.bson.types.ObjectId($id)”)
case a: List[_] =>
val wrapped = a.map(i => wrapValue(c)(i, args))
c.Expr[List[Any]](q”List(..$wrapped)”)
.
Обрабатываем значения
protected def wrapValue(c: Context) (value: Any,
args: Iterator[c.Tree]): c.Expr[Any] = {
import c.universe._
value match {
case BSON.Placeholder =>
c.Expr(args.next())
case o: BSON.Object =>
wrapObject(c)(o, args)
case BSON.Id(id) =>
c.Expr(q”new org.bson.types.ObjectId($id)”)
case a: List[_] =>
val wrapped = a.map(i => wrapValue(c)(i, args))
c.Expr[List[Any]](q”List(..$wrapped)”)
case v =>
c.Expr[Any](Literal(Constant(v)))
}
}
.
типобезопасность
.
Типобезопасность
.
mqt – typechecking interpolator
case class Phone(kind: String, number: String)
case class Person(name: String, age: Int, phone: List[Phone])
.
mqt – typechecking interpolator
case class Phone(kind: String, number: String)
case class Person(name: String, age: Int, phone: List[Phone])
// OK
persons.update(mq”{}”, mqt”{ $$inc : { age : 1 } }”[Person])
persons.find(mqt”{ phone.number : ’223322’ }”[Person])
.
mqt – typechecking interpolator
case class Phone(kind: String, number: String)
case class Person(name: String, age: Int, phone: List[Phone])
// OK
persons.update(mq”{}”, mqt”{ $$inc : { age : 1 } }”[Person])
persons.find(mqt”{ phone.number : ’223322’ }”[Person])
// COMPILE ERROR
persons.update(mq”{}”, mqt”””{$$set : { nme : ”Joe” }}”””[Person])
persons.find(mqt”{ name.1 : ’Joe’ }”[Person])
persons.find(mqt”{ phone.num : ’223322’ }”[Person])
.
Передача типа в интерполятор
implicit class MongoHelper(val sc: StringContext) extends AnyVal {
def mq(args: Any*): DBObject = macro MongoHelper.mq_impl
def mqt(args: Any*) = new QueryWrapper
}
class QueryWrapper {
def apply[T]: DBObject = macro MongoHelper.mqt_impl[T]
}
object MongoHelper {
def mqt_impl[T: c.WeakTypeTag](c: Context):
c.Expr[DBObject] = ???
}
.
Внутренности mqt_impl
def mqt_impl[T: c.WeakTypeTag](c: Context): c.Expr[DBObject] = {
val q”$cn(scala.StringContext.apply(..$pTrees)).mqt(..$aTrees)”
= c.prefix.tree
val args = aTrees.map(c.Expr(_))
val parsed = parse(c)(pTrees)
checkObject(c)(c.weakTypeOf[T], parsed)
wrapObject(c)(parsed, args.iterator)
}
.
Проверяем тип
def checkType(c: Context)(tpe: c.Type, obj: Object) = {
import c.universe._
val ctor = tpe.decl(termNames.CONSTRUCTOR).asMethod
val params = ctor.paramLists.head
val className = t.typeSymbol.name.toString
val fields = params.map(s => s.name.toString -> s).toMap
obj.members.foreach { case (m, _) =>
if (!fields.contains(m.name)) {
c.abort(c.enclosingPosition,
s”Class $className doesn’t contain field ’${m.name}’”)
}
}
}
.
тестирование
.
Тестируем интерполятор
it should ”support nested objects” in {
val q = mq”””{ user : ”Joe”, age : {$$gt : 25}}”””
q should equal(MongoDBObject(”user” -> ”Joe”,
”age” -> MongoDBObject(”$gt” -> 25)))
}
.
Тестируем ошибки
import scala.reflect.runtime.{ universe => ru }
class CompileTest extends FlatSpec {
val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader]
val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator)
val mirror = ru.runtimeMirror(cl)
val tb = mirror.mkToolBox(options = s”-cp $cp”)
.
Тестируем ошибки
import scala.reflect.runtime.{ universe => ru }
class CompileTest extends FlatSpec {
val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader]
val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator)
val mirror = ru.runtimeMirror(cl)
val tb = mirror.mkToolBox(options = s”-cp $cp”)
def getError(q: String): String = {
val e = intercept[ToolBoxError] {
tb.eval(tb.parse(q))
}
e.message
}
.
Тестируем ошибки
import scala.reflect.runtime.{ universe => ru }
class CompileTest extends FlatSpec {
val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader]
val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator)
val mirror = ru.runtimeMirror(cl)
val tb = mirror.mkToolBox(options = s”-cp $cp”)
def getError(q: String): String = {
val e = intercept[ToolBoxError] {
tb.eval(tb.parse(q))
}
e.message
}
it should ”fail on malformed BSON objects” in {
val e = getError(”””mq”{ test 5 }” ”””)
e should include(”‘:’ expected, but 5 found”)
}
}
.
заключение
.
Выводы
Минусы
• Достаточно сложная реализация
.
Выводы
Минусы
• Достаточно сложная реализация
• Не подсвечивается в IDE
.
Выводы
Минусы
• Достаточно сложная реализация
• Не подсвечивается в IDE
Плюсы
.
Выводы
Минусы
• Достаточно сложная реализация
• Не подсвечивается в IDE
Плюсы
• Меньше ограничений на встраиваемый язык
.
Выводы
Минусы
• Достаточно сложная реализация
• Не подсвечивается в IDE
Плюсы
• Меньше ограничений на встраиваемый язык
• Можно сохранить имеющийся язык
.
Выводы
Минусы
• Достаточно сложная реализация
• Не подсвечивается в IDE
Плюсы
• Меньше ограничений на встраиваемый язык
• Можно сохранить имеющийся язык
• Мартин Одерски сказал, что интерполяторы это круто
.
Дололнительная инофрмация
• MongoDB: www.mongodb.org
• Casbah: github.com/mongodb/casbah
• ReactiveMongo: www.reactivemongo.org
• MongoQuery: github.com/limansky/mongoquery
.
Вопросы?
.

Weitere ähnliche Inhalte

Was ist angesagt?

Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Vasya Petrov
 
Разработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PIРазработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PIIlya Chesnokov
 
Новые возможности и оптимизация запросов в Google BigQuery
Новые возможности и оптимизация запросов в Google BigQueryНовые возможности и оптимизация запросов в Google BigQuery
Новые возможности и оптимизация запросов в Google BigQueryМаркетинг-аналитика с OWOX BI
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4Technopark
 
Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиИнтуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиГлеб Тарасов
 
Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011ilyubin
 
CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...
CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...
CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...CodeFest
 
Практическое применение MongoDB Aggregation Framework
Практическое применение MongoDB Aggregation FrameworkПрактическое применение MongoDB Aggregation Framework
Практическое применение MongoDB Aggregation FrameworkДенис Кравченко
 
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"Yandex
 
Приложения для Windows Phone: как мы это делаем #codefest
Приложения для Windows Phone: как мы это делаем #codefestПриложения для Windows Phone: как мы это делаем #codefest
Приложения для Windows Phone: как мы это делаем #codefestActis Wunderman
 
CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...
CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...
CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...CodeFest
 
JavaScript-библиотека
JavaScript-библиотекаJavaScript-библиотека
JavaScript-библиотекаVasya Petrov
 
jQuery: быстрая разработка веб-интерфейсов на JavaScript
jQuery: быстрая разработка веб-интерфейсов на JavaScriptjQuery: быстрая разработка веб-интерфейсов на JavaScript
jQuery: быстрая разработка веб-интерфейсов на JavaScriptConstantin Kichinsky
 
константин лебедев
константин лебедевконстантин лебедев
константин лебедевkuchinskaya
 
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...Yandex
 
MongoDB. Области применения, преимущества и узкие места, тонкости использован...
MongoDB. Области применения, преимущества и узкие места, тонкости использован...MongoDB. Области применения, преимущества и узкие места, тонкости использован...
MongoDB. Области применения, преимущества и узкие места, тонкости использован...phpdevby
 

Was ist angesagt? (20)

Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1
 
Разработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PIРазработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PI
 
Новые возможности и оптимизация запросов в Google BigQuery
Новые возможности и оптимизация запросов в Google BigQueryНовые возможности и оптимизация запросов в Google BigQuery
Новые возможности и оптимизация запросов в Google BigQuery
 
Укрощение XML
Укрощение XMLУкрощение XML
Укрощение XML
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4
 
Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиИнтуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
 
Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011
 
CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...
CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...
CodeFest 2013. Никонов Г. — Как мы разрабатываем приложения для Windows Phone...
 
Практическое применение MongoDB Aggregation Framework
Практическое применение MongoDB Aggregation FrameworkПрактическое применение MongoDB Aggregation Framework
Практическое применение MongoDB Aggregation Framework
 
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
Андрей Субботин "Локализация приложений для iOS: как не прострелить себе ногу"
 
Приложения для Windows Phone: как мы это делаем #codefest
Приложения для Windows Phone: как мы это делаем #codefestПриложения для Windows Phone: как мы это делаем #codefest
Приложения для Windows Phone: как мы это делаем #codefest
 
Pagination MongoDb
Pagination MongoDbPagination MongoDb
Pagination MongoDb
 
Romanenko
RomanenkoRomanenko
Romanenko
 
CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...
CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...
CodeFest 2014. Бартунов О. — Hstore — документо-ориентированное хранилище и д...
 
JavaScript-библиотека
JavaScript-библиотекаJavaScript-библиотека
JavaScript-библиотека
 
jQuery: быстрая разработка веб-интерфейсов на JavaScript
jQuery: быстрая разработка веб-интерфейсов на JavaScriptjQuery: быстрая разработка веб-интерфейсов на JavaScript
jQuery: быстрая разработка веб-интерфейсов на JavaScript
 
константин лебедев
константин лебедевконстантин лебедев
константин лебедев
 
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
 
MongoDB. Области применения, преимущества и узкие места, тонкости использован...
MongoDB. Области применения, преимущества и узкие места, тонкости использован...MongoDB. Области применения, преимущества и узкие места, тонкости использован...
MongoDB. Области применения, преимущества и узкие места, тонкости использован...
 
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кодаSECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
 

Andere mochten auch

Advantages of english
Advantages of englishAdvantages of english
Advantages of englishMongtitya
 
Orlando higuita_angie_mican:1003
Orlando higuita_angie_mican:1003Orlando higuita_angie_mican:1003
Orlando higuita_angie_mican:1003Angie_Stephany
 
2014 Annual Report
2014 Annual Report2014 Annual Report
2014 Annual ReportNancy Nguyen
 
Kadın genital ultrasonografisi
Kadın genital ultrasonografisiKadın genital ultrasonografisi
Kadın genital ultrasonografisiuzmdrbahriyildiz
 
Böbreküstü bezi ultrasonografisi
Böbreküstü bezi ultrasonografisiBöbreküstü bezi ultrasonografisi
Böbreküstü bezi ultrasonografisiuzmdrbahriyildiz
 
Vehiculos extraordinarios
Vehiculos extraordinariosVehiculos extraordinarios
Vehiculos extraordinariosCarlos Colomer
 
Como Escribir Guion dramatico de_radio
Como Escribir Guion dramatico de_radioComo Escribir Guion dramatico de_radio
Como Escribir Guion dramatico de_radiojenirlopez
 
As metodologias de investigação em sociologia e ciências sociais
As metodologias de investigação em sociologia e ciências sociaisAs metodologias de investigação em sociologia e ciências sociais
As metodologias de investigação em sociologia e ciências sociaisCatarina Alexandra
 

Andere mochten auch (15)

Advantages of english
Advantages of englishAdvantages of english
Advantages of english
 
Orlando higuita_angie_mican:1003
Orlando higuita_angie_mican:1003Orlando higuita_angie_mican:1003
Orlando higuita_angie_mican:1003
 
DDoS Defense Mechanism
DDoS Defense MechanismDDoS Defense Mechanism
DDoS Defense Mechanism
 
CYP 2014
CYP 2014CYP 2014
CYP 2014
 
Av lesson 5
Av lesson 5Av lesson 5
Av lesson 5
 
Multiple Regression
Multiple RegressionMultiple Regression
Multiple Regression
 
FIAZ_CV
FIAZ_CVFIAZ_CV
FIAZ_CV
 
ULTRASONOGRAFİ DERSLERİ
ULTRASONOGRAFİ DERSLERİULTRASONOGRAFİ DERSLERİ
ULTRASONOGRAFİ DERSLERİ
 
2014 Annual Report
2014 Annual Report2014 Annual Report
2014 Annual Report
 
Kadın genital ultrasonografisi
Kadın genital ultrasonografisiKadın genital ultrasonografisi
Kadın genital ultrasonografisi
 
Nokia
NokiaNokia
Nokia
 
Böbreküstü bezi ultrasonografisi
Böbreküstü bezi ultrasonografisiBöbreküstü bezi ultrasonografisi
Böbreküstü bezi ultrasonografisi
 
Vehiculos extraordinarios
Vehiculos extraordinariosVehiculos extraordinarios
Vehiculos extraordinarios
 
Como Escribir Guion dramatico de_radio
Como Escribir Guion dramatico de_radioComo Escribir Guion dramatico de_radio
Como Escribir Guion dramatico de_radio
 
As metodologias de investigação em sociologia e ciências sociais
As metodologias de investigação em sociologia e ciências sociaisAs metodologias de investigação em sociologia e ciências sociais
As metodologias de investigação em sociologia e ciências sociais
 

Ähnlich wie Встраивание языка в строковой интерполятор

Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?Vasil Remeniuk
 
JavaScript Базовый. Занятие 11.
JavaScript Базовый. Занятие 11.JavaScript Базовый. Занятие 11.
JavaScript Базовый. Занятие 11.Igor Shkulipa
 
Базы данных. MongoDB
Базы данных. MongoDBБазы данных. MongoDB
Базы данных. MongoDBVadim Tsesko
 
Cтрах и ненависть в MongoDB
Cтрах и ненависть в MongoDBCтрах и ненависть в MongoDB
Cтрах и ненависть в MongoDBDmitry Viskov
 
работа с сетью
работа с сетьюработа с сетью
работа с сетьюNoveo
 
Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)Dmitry Stropalov
 
Vsevolod Rodionov "Neural networks in js"
Vsevolod Rodionov "Neural networks in js"Vsevolod Rodionov "Neural networks in js"
Vsevolod Rodionov "Neural networks in js"OdessaJS Conf
 
Нейронные сети на JS
Нейронные сети на JSНейронные сети на JS
Нейронные сети на JSVsevolod Rodionov
 
MongoDB basics in Russian
MongoDB basics in RussianMongoDB basics in Russian
MongoDB basics in RussianOleg Kachan
 
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...Magneta AI
 
Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)
Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)
Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)IT-Доминанта
 
Кратко о MongoDB
Кратко о MongoDBКратко о MongoDB
Кратко о MongoDBGleb Lebedev
 

Ähnlich wie Встраивание языка в строковой интерполятор (18)

Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
 
JavaScript Базовый. Занятие 11.
JavaScript Базовый. Занятие 11.JavaScript Базовый. Занятие 11.
JavaScript Базовый. Занятие 11.
 
Базы данных. MongoDB
Базы данных. MongoDBБазы данных. MongoDB
Базы данных. MongoDB
 
Cтрах и ненависть в MongoDB
Cтрах и ненависть в MongoDBCтрах и ненависть в MongoDB
Cтрах и ненависть в MongoDB
 
работа с сетью
работа с сетьюработа с сетью
работа с сетью
 
Nosql and Mongodb
Nosql and MongodbNosql and Mongodb
Nosql and Mongodb
 
msumobi2. Лекция 1
msumobi2. Лекция 1msumobi2. Лекция 1
msumobi2. Лекция 1
 
Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)
 
Vsevolod Rodionov "Neural networks in js"
Vsevolod Rodionov "Neural networks in js"Vsevolod Rodionov "Neural networks in js"
Vsevolod Rodionov "Neural networks in js"
 
Нейронные сети на JS
Нейронные сети на JSНейронные сети на JS
Нейронные сети на JS
 
MongoDB basics in Russian
MongoDB basics in RussianMongoDB basics in Russian
MongoDB basics in Russian
 
Spring data jee conf
Spring data jee confSpring data jee conf
Spring data jee conf
 
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
Scala, Play Framework и SBT для быстрого прототипирования и разработки веб-пр...
 
Js fuckworks
Js fuckworksJs fuckworks
Js fuckworks
 
Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)
Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)
Pony ORM - маппер нового поколения (Алексей Малашкевич и Александр Козловский)
 
Xtend
XtendXtend
Xtend
 
Кратко о MongoDB
Кратко о MongoDBКратко о MongoDB
Кратко о MongoDB
 
Основы MongoDB + NodeJS
Основы MongoDB + NodeJSОсновы MongoDB + NodeJS
Основы MongoDB + NodeJS
 

Встраивание языка в строковой интерполятор

  • 1. встраивание языка в строковой интерполятор . Михаил Лиманский 14 августа 2015 г. FPConf 2015
  • 3. Строковая интерполяция – это просто val language = ”English” .
  • 4. Строковая интерполяция – это просто val language = ”English” val embedded = s”Embedded $language” .
  • 5. MongoDB MongoDB – документо-ориентированная база данных, хранящая документы BSON. db.people.insert({ name: ”John Doe”, age: 42 }) .
  • 6. MongoDB MongoDB – документо-ориентированная база данных, хранящая документы BSON. db.people.insert({ name: ”John Doe”, age: 42 }) db.people.insert({ name: ”William Smith”, age: 28, phone: [ ”1234567”, ”7654321” ] }) .
  • 7. MongoDB MongoDB – документо-ориентированная база данных, хранящая документы BSON. db.people.insert({ name: ”John Doe”, age: 42 }) db.people.insert({ name: ”William Smith”, age: 28, phone: [ ”1234567”, ”7654321” ] }) db.people.insert({ name: ”Alice White”, age: 29, address: { country: ”UK”, city: ”London” } }) .
  • 8. MongoDB MongoDB – документо-ориентированная база данных, хранящая документы BSON. db.people.insert({ name: ”John Doe”, age: 42 }) db.people.insert({ name: ”William Smith”, age: 28, phone: [ ”1234567”, ”7654321” ] }) db.people.insert({ name: ”Alice White”, age: 29, address: { country: ”UK”, city: ”London” } }) db.people.insert({ name : ”Ivan Petrov”, age : 28 }) .
  • 9. Поиск и обновление db.people.find({ name: ”John Doe”}) db.people.find({ age: { $lt : 30 }}) db.people.find({ phone: { $not: { $size : 0 }}}) .
  • 10. Поиск и обновление db.people.find({ name: ”John Doe”}) db.people.find({ age: { $lt : 30 }}) db.people.find({ phone: { $not: { $size : 0 }}}) db.people.update({ age : 42}, { $set : { name : ”Ford Prefect” } }) .
  • 11. Поиск и обновление db.people.find({ name: ”John Doe”}) db.people.find({ age: { $lt : 30 }}) db.people.find({ phone: { $not: { $size : 0 }}}) db.people.update({ age : 42}, { $set : { name : ”Ford Prefect” } }) db.people.aggregate( [ { $group : { _id : ”$age”, count : {$sum : 1} } }, { $sort : { count : -1 } }, { $limit : 5 } ]) .
  • 12. MongoDB в Scala Основные драйвера для работы с MongoDB: • Casbah – синхронный, поверх драйвера для Java. .
  • 13. MongoDB в Scala Основные драйвера для работы с MongoDB: • Casbah – синхронный, поверх драйвера для Java. • ReactiveMongo – асинхронный, основан на акторах Akka. .
  • 14. MongoDB в Scala Основные драйвера для работы с MongoDB: • Casbah – синхронный, поверх драйвера для Java. • ReactiveMongo – асинхронный, основан на акторах Akka. • Tepkin – реактивный, на Akka IO и Akka Streams. .
  • 15. Пример использования Casbah val name = ”John Doe” people.insert(MongoDBObject( ”name” -> ”James Bond”, ”age” -> 80, ”phone” -> List(”007007”), ”address” -> MongoDBObject(”country” -> ”UK”))) .
  • 16. Пример использования Casbah val name = ”John Doe” people.insert(MongoDBObject( ”name” -> ”James Bond”, ”age” -> 80, ”phone” -> List(”007007”), ”address” -> MongoDBObject(”country” -> ”UK”))) val a = people.findOne(MongoDBObject(”name” -> name)) val b = people.find(MongoDBObject(”age” -> MongoDBObject(”$lt” -> 30))) .
  • 17. Пример использования Casbah val name = ”John Doe” people.insert(MongoDBObject( ”name” -> ”James Bond”, ”age” -> 80, ”phone” -> List(”007007”), ”address” -> MongoDBObject(”country” -> ”UK”))) val a = people.findOne(MongoDBObject(”name” -> name)) val b = people.find(MongoDBObject(”age” -> MongoDBObject(”$lt” -> 30))) // Using Casbah DSL val c = people.find(”age” $lt 30) val d = people.find(”phone” -> $not(_ $size 0)) people.update(MongoDBObject(”age” -> 42), $set(”name” -> ”Ford Prefect”)) .
  • 18. Пример использования Casbah val name = ”John Doe” people.insert(MongoDBObject( ”name” -> ”James Bond”, ”age” -> 80, ”phone” -> List(”007007”), ”address” -> MongoDBObject(”country” -> ”UK”))) val a = people.findOne(MongoDBObject(”name” -> name)) val b = people.find(MongoDBObject(”age” -> MongoDBObject(”$lt” -> 30))) // Using Casbah DSL val c = people.find(”age” $lt 30) val d = people.find(”phone” -> $not(_ $size 0)) people.update(MongoDBObject(”age” -> 42), $set(”name” -> ”Ford Prefect”)) val e = people.aggregate(List( MongoDBObject(”$group” -> MongoDBObject(”_id” -> ”$age”, ”count” -> MongoDBObject(”$sum” -> 1))), MongoDBObject(”$sort” -> MongoDBObject(”count” -> -1)), MongoDBObject(”$limit” -> 5))) .
  • 20. Пример работы с ReactiveMongo // Future[BSONDocument] val a = people.find(BSONDocument(”name” -> ”John Doe”)) .one[BSONDocument] // Future[List[Person]] val b = people.find(BSONDocument(”age” -> BSONDocument(”$lt” -> 30)) ).cursor[Person].collect[List]() val futureUpdate = people.update( BSONDocument(”age” -> 42), BSONDocument(”$set” -> BSONDocument(”name” -> ”Ford Prefect”))) // Future val e = db.command(RawCommand(BSONDocument( ”aggregate” -> ”people”, ”pipeline” -> BSONArray( BSONDocument(”$group” -> BSONDocument(”_id” -> ”$age”, ”count” -> BSONDocument(”$sum” -> 1))), BSONDocument(”$sort” -> BSONDocument(”count” -> -1)), BSONDocument(”$limit” -> 5))))) .
  • 21. Почему мир так несправедлив? .
  • 22. Meet MongoQuery Пример использования MongoQuery с Casbah: import com.github.limansky.mongoquery.casbah._ val name = ”John Doe” val a = people.findOne(mq”{ name : $name }”) .
  • 23. Meet MongoQuery Пример использования MongoQuery с Casbah: import com.github.limansky.mongoquery.casbah._ val name = ”John Doe” val a = people.findOne(mq”{ name : $name }”) val b = people.find(mq”{age : { $$lt : 30 }}”) .
  • 24. Meet MongoQuery Пример использования MongoQuery с Casbah: import com.github.limansky.mongoquery.casbah._ val name = ”John Doe” val a = people.findOne(mq”{ name : $name }”) val b = people.find(mq”{age : { $$lt : 30 }}”) val d = people.find( mq”{ phone : { $$not : { $$size : 0 }}}”) people.update(mq”{ age : 42 }”, mq”{ $$set { name : ’Ford Prefect’ }}”) val e = people.aggregate(List( mq”””{ $$group : { _id : ”$$age”, count : { $$sum : 1 }}}”””, mq”{ $$sort : { count : -1 }}”, mq”{ $$limit : 5}”)) .
  • 26. Строковая интерполяция implicit class MongoHelper(val sc: StringContext) extends AnyVal { def mq(args: Any*): DBObject = { Parser.parseQuery(sc.parts, args) match { case Success(v, _) => createObject(v) case NoSuccess(msg, _) => throw new MqException(s”Invalid object: $msg”) } } } .
  • 27. Строковая интерполяция implicit class MongoHelper(val sc: StringContext) extends AnyVal { def mq(args: Any*): DBObject = { Parser.parseQuery(sc.parts, args) match { case Success(v, _) => createObject(v) case NoSuccess(msg, _) => throw new MqException(s”Invalid object: $msg”) } } } mq”{ name : $name }” sc.parts == List(”{ name: ”, ” }”) args = List(name) .
  • 29. Заворачиваем интерполятор в макрос implicit class MongoHelper(val sc: StringContext) extends AnyVal { def mq(args: Any*): DBObject = macro MongoHelper.mq_impl } object MongoHelper { def mq_impl(c: Context)(args: c.Expr[Any]*): c.Expr[DBObject] = ??? } .
  • 30. Заворачиваем интерполятор в макрос implicit class MongoHelper(val sc: StringContext) extends AnyVal { def mq(args: Any*): DBObject = macro MongoHelper.mq_impl } object MongoHelper { def mq_impl(c: Context)(args: c.Expr[Any]*): c.Expr[DBObject] = { import c.universe._ val q”$cn(scala.StringContext.apply(..$pTrees))” = c.prefix.tree val parsed = parse(c)(pTrees) wrapObject(c)(parsed, args.map(_.tree).iterator) } } .
  • 31. Заворачиваем интерполятор в макрос object MongoHelper { def parse(c: Context)(pTrees: List[c.Tree]) = { import c.universe._ val parts = pTrees map { case Literal(Constant(s: String)) => s } parser.parse(parts) match { case Success(v, _) => v case NoSuccess(msg, reader) => val partIndex = reader.asInstanceOf[PartReader].part val pos = pTrees(partIndex).pos c.abort(pos.withPoint(pos.point + reader.offset)), s”Invalid BSON object: $msg”) } } } .
  • 32. Парсим BSON mq”{ name : $name, age : { $$gte : 18, $$lte : $max }}” .
  • 33. Парсим BSON mq”{ name : $name, age : { $$gte : 18, $$lte : $max }}”.. Lexical List(”{”, Field(”name”), ”:”, Placeholder, ”,”, Field(”age”), ”:”, ”{”, Keyword(”$gte”), ”:”, NumericLit(18), ”,”, Keyword(”$lte”), ”:”, Placeholder, ”,”, ”}”, ”}”) .
  • 34. Парсим BSON mq”{ name : $name, age : { $$gte : 18, $$lte : $max }}”.. Lexical List(”{”, Field(”name”), ”:”, Placeholder, ”,”, Field(”age”), ”:”, ”{”, Keyword(”$gte”), ”:”, NumericLit(18), ”,”, Keyword(”$lte”), ”:”, Placeholder, ”,”, ”}”, ”}”).. Syntactical Object(List( (Member(”name”), Placeholder), (Member(”age”), Object(List( (Keyword(”$gte”), 18), (Keyword(”$lte”), Placeholder)) )) )) .
  • 35. Создаем объекты protected def wrapObject(c: Context)(obj: Object, args: Iterator[c.Tree]): c.Expr[DBType] = { val dbparts = obj.members.map { case (lv, v) => (lv.asString, wrapValue(c)(v, args)) } c.Expr(q”com.mongodb.casbah.commons.MongoDBObject(..$dbparts)”) } .
  • 36. Обрабатываем значения protected def wrapValue(c: Context) (value: Any, args: Iterator[c.Tree]): c.Expr[Any] = { import c.universe._ value match { case BSON.Placeholder => c.Expr(args.next()) .
  • 37. Обрабатываем значения protected def wrapValue(c: Context) (value: Any, args: Iterator[c.Tree]): c.Expr[Any] = { import c.universe._ value match { case BSON.Placeholder => c.Expr(args.next()) case o: BSON.Object => wrapObject(c)(o, args) .
  • 38. Обрабатываем значения protected def wrapValue(c: Context) (value: Any, args: Iterator[c.Tree]): c.Expr[Any] = { import c.universe._ value match { case BSON.Placeholder => c.Expr(args.next()) case o: BSON.Object => wrapObject(c)(o, args) case BSON.Id(id) => c.Expr(q”new org.bson.types.ObjectId($id)”) .
  • 39. Обрабатываем значения protected def wrapValue(c: Context) (value: Any, args: Iterator[c.Tree]): c.Expr[Any] = { import c.universe._ value match { case BSON.Placeholder => c.Expr(args.next()) case o: BSON.Object => wrapObject(c)(o, args) case BSON.Id(id) => c.Expr(q”new org.bson.types.ObjectId($id)”) case a: List[_] => val wrapped = a.map(i => wrapValue(c)(i, args)) c.Expr[List[Any]](q”List(..$wrapped)”) .
  • 40. Обрабатываем значения protected def wrapValue(c: Context) (value: Any, args: Iterator[c.Tree]): c.Expr[Any] = { import c.universe._ value match { case BSON.Placeholder => c.Expr(args.next()) case o: BSON.Object => wrapObject(c)(o, args) case BSON.Id(id) => c.Expr(q”new org.bson.types.ObjectId($id)”) case a: List[_] => val wrapped = a.map(i => wrapValue(c)(i, args)) c.Expr[List[Any]](q”List(..$wrapped)”) case v => c.Expr[Any](Literal(Constant(v))) } } .
  • 43. mqt – typechecking interpolator case class Phone(kind: String, number: String) case class Person(name: String, age: Int, phone: List[Phone]) .
  • 44. mqt – typechecking interpolator case class Phone(kind: String, number: String) case class Person(name: String, age: Int, phone: List[Phone]) // OK persons.update(mq”{}”, mqt”{ $$inc : { age : 1 } }”[Person]) persons.find(mqt”{ phone.number : ’223322’ }”[Person]) .
  • 45. mqt – typechecking interpolator case class Phone(kind: String, number: String) case class Person(name: String, age: Int, phone: List[Phone]) // OK persons.update(mq”{}”, mqt”{ $$inc : { age : 1 } }”[Person]) persons.find(mqt”{ phone.number : ’223322’ }”[Person]) // COMPILE ERROR persons.update(mq”{}”, mqt”””{$$set : { nme : ”Joe” }}”””[Person]) persons.find(mqt”{ name.1 : ’Joe’ }”[Person]) persons.find(mqt”{ phone.num : ’223322’ }”[Person]) .
  • 46. Передача типа в интерполятор implicit class MongoHelper(val sc: StringContext) extends AnyVal { def mq(args: Any*): DBObject = macro MongoHelper.mq_impl def mqt(args: Any*) = new QueryWrapper } class QueryWrapper { def apply[T]: DBObject = macro MongoHelper.mqt_impl[T] } object MongoHelper { def mqt_impl[T: c.WeakTypeTag](c: Context): c.Expr[DBObject] = ??? } .
  • 47. Внутренности mqt_impl def mqt_impl[T: c.WeakTypeTag](c: Context): c.Expr[DBObject] = { val q”$cn(scala.StringContext.apply(..$pTrees)).mqt(..$aTrees)” = c.prefix.tree val args = aTrees.map(c.Expr(_)) val parsed = parse(c)(pTrees) checkObject(c)(c.weakTypeOf[T], parsed) wrapObject(c)(parsed, args.iterator) } .
  • 48. Проверяем тип def checkType(c: Context)(tpe: c.Type, obj: Object) = { import c.universe._ val ctor = tpe.decl(termNames.CONSTRUCTOR).asMethod val params = ctor.paramLists.head val className = t.typeSymbol.name.toString val fields = params.map(s => s.name.toString -> s).toMap obj.members.foreach { case (m, _) => if (!fields.contains(m.name)) { c.abort(c.enclosingPosition, s”Class $className doesn’t contain field ’${m.name}’”) } } } .
  • 50. Тестируем интерполятор it should ”support nested objects” in { val q = mq”””{ user : ”Joe”, age : {$$gt : 25}}””” q should equal(MongoDBObject(”user” -> ”Joe”, ”age” -> MongoDBObject(”$gt” -> 25))) } .
  • 51. Тестируем ошибки import scala.reflect.runtime.{ universe => ru } class CompileTest extends FlatSpec { val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader] val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator) val mirror = ru.runtimeMirror(cl) val tb = mirror.mkToolBox(options = s”-cp $cp”) .
  • 52. Тестируем ошибки import scala.reflect.runtime.{ universe => ru } class CompileTest extends FlatSpec { val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader] val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator) val mirror = ru.runtimeMirror(cl) val tb = mirror.mkToolBox(options = s”-cp $cp”) def getError(q: String): String = { val e = intercept[ToolBoxError] { tb.eval(tb.parse(q)) } e.message } .
  • 53. Тестируем ошибки import scala.reflect.runtime.{ universe => ru } class CompileTest extends FlatSpec { val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader] val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator) val mirror = ru.runtimeMirror(cl) val tb = mirror.mkToolBox(options = s”-cp $cp”) def getError(q: String): String = { val e = intercept[ToolBoxError] { tb.eval(tb.parse(q)) } e.message } it should ”fail on malformed BSON objects” in { val e = getError(”””mq”{ test 5 }” ”””) e should include(”‘:’ expected, but 5 found”) } } .
  • 56. Выводы Минусы • Достаточно сложная реализация • Не подсвечивается в IDE .
  • 57. Выводы Минусы • Достаточно сложная реализация • Не подсвечивается в IDE Плюсы .
  • 58. Выводы Минусы • Достаточно сложная реализация • Не подсвечивается в IDE Плюсы • Меньше ограничений на встраиваемый язык .
  • 59. Выводы Минусы • Достаточно сложная реализация • Не подсвечивается в IDE Плюсы • Меньше ограничений на встраиваемый язык • Можно сохранить имеющийся язык .
  • 60. Выводы Минусы • Достаточно сложная реализация • Не подсвечивается в IDE Плюсы • Меньше ограничений на встраиваемый язык • Можно сохранить имеющийся язык • Мартин Одерски сказал, что интерполяторы это круто .
  • 61. Дололнительная инофрмация • MongoDB: www.mongodb.org • Casbah: github.com/mongodb/casbah • ReactiveMongo: www.reactivemongo.org • MongoQuery: github.com/limansky/mongoquery .