Идея проекта в создании фреймоворка по удобству использования приближенного к динамическим языкам на языке со статической типизацией.
Вся легкость построения приложений на RoR обусловлена динамической генерацией множества хелперных методов, предлагается данные методы генерировать в компайл тайме nemerle макросами.
Идея принадлежит Воронкову Василию
соглашение вместо конфигурации, как в RoR, DRY.
имеет из коробки linq ORM (BlToolkit), view engine (Spark view engine), но имеет "разъемы" для замены их на другие.
как MVC движок предлагается использовать ASP.NET MVC
модель по базе utythbhetncz в компайл тайме, аналогично тому, как это делают рельсы в рантайме. Вот тут сыграет главная мощь nemerle, все свойства и методы будут контролироваться компилятором. Спарк кстати тоже умеет прекомпилировать вьюхи. Возможно использование обратного сценария, генерации схемы по модели, но я пока вижу тут очень много граблей.
версионность схемы СУБД поддерживаемых BlToolkit, a la RoR (вот тут пригодятся)
Плюсы по сравнению с RoR
компилируемость и скорость, прощайте тормознутые рельсы.
более продвинутый ORM, все таки в рельсах он убог ужасно
контроль компилятором всей "динамики", больше не нужно бояться сделать опечатку.
есть мысли, как использовать данный движок для десктопного УИ без переделок (http://www.rsdn.ru/forum/prj/3763379.1.aspx
нет кучи готовых компонентов, типа установи и получи нужный функционал, но это дело наживное
не умеет подхватывать изменения "на лету", хотя view это не коснется
и теряем типизацию, либо вынуждены объявлять класс для типизации, и типизировать view что действительно геморойно.
// модельclass ProductListModel
{
public IEnumerable<Product> Products {get;set;}
}
// контроллерvar model = new ProductListModel() { Products = db.Products.ByCategory(categoryId) };
return View(model); // кстати попробуйте сделать вьюху с моделью типа string
// view, это спарк, самый краткий синтаксис, в ASP.NET мы получим еще больший оверхед
<viewdata model="ProductListModel">
Это все противоречит принципам DRY, чтобы передать простой список изделий мы должны сбегать в кучу мест и узаконить это простое намерение.
Хотя по идее достаточно вывести тип того что мы хотим передать и сгенерировать данный DTO класс.
избавляемся от строковых констант с именами и сигнатурами экшенов, контроллеров
В идеале синатксис
Html.ActionLink(product.Name, "Show", new {id = product.Id})
Здравствуйте, Ziaw, Вы писали:
Z>Идея проекта в создании фреймоворка по удобству использования приближенного к динамическим языкам на языке со статической типизацией. Z>Вся легкость построения приложений на RoR обусловлена динамической генерацией множества хелперных методов, предлагается данные методы генерировать в компайл тайме nemerle макросами.
Идея в общем здравая. Но IMHO все сделать столь же удобно не получится.
Некоторые методы в RoR доопределяются только на этапе выполнения из принципиальной необходимости.
Пример:
ExtUser.find_by_sql("SELECT user.*, account.* FROM user INNER JOIN ... ну и так далее
Набор методов класса ExtUser будет известен только после выполнения запроса на основе полученных данных. А на этапе компиляции не известно нужно ли делать в этом классе метод usr.directory_name() или нет.
Это только пример, который первым пришел в голову. Реально подобных мест в RoR куча.
Здравствуйте, Alex EXO, Вы писали:
AE>Здравствуйте, Ziaw, Вы писали:
Z>>Идея проекта в создании фреймоворка по удобству использования приближенного к динамическим языкам на языке со статической типизацией. Z>>Вся легкость построения приложений на RoR обусловлена динамической генерацией множества хелперных методов, предлагается данные методы генерировать в компайл тайме nemerle макросами. AE>Идея в общем здравая. Но IMHO все сделать столь же удобно не получится. AE>Некоторые методы в RoR доопределяются только на этапе выполнения из принципиальной необходимости. AE>Пример: AE>ExtUser.find_by_sql("SELECT user.*, account.* FROM user INNER JOIN ... ну и так далее AE>Набор методов класса ExtUser будет известен только после выполнения запроса на основе полученных данных. А на этапе компиляции не известно нужно ли делать в этом классе метод usr.directory_name() или нет. AE>Это только пример, который первым пришел в голову. Реально подобных мест в RoR куча.
Это проблемы роровского ORM, linq выводит получаемый запросом тип, о нем все известно на этапе компиляции.
Здравствуйте, Ziaw, Вы писали: Z>Это проблемы роровского ORM, linq выводит получаемый запросом тип, о нем все известно на этапе компиляции.
Не-е-е...
Это не "проблемы роровского ORM". Это возможности "роровского ORM".
С реляционными данным я хочу работать на полноценном реляционном языке. И декоративный SQL выражает работу с данными очень компактно.
Но вот потом, я хочу чтобы то, что я "нагреб" превратилось в объекты. Чтобы мне удобнее было работать с этим в слое бизнес-логики.
И это, на мой взгляд, работа ORM (одна из). "Сделать объекты из того, что я выгреб".
А на тему объектного описания запросов... ну как там в поговорке: "не говорите мне что делать, и я не скажу куда вам идти".
В общем то это достаточно традиционный отмаз: "у нас это не реализовано потому, что это не нужно". Во флейме его использовать можно и даже успешно. Но и что с того? Реальное удобство фреймворка от этого не повысится.
Да, на rsdn модно ругать RoR-овский ORM. Но выбирают часто именно RoR.
Здравствуйте, Alex EXO, Вы писали:
AE>Это не "проблемы роровского ORM". Это возможности "роровского ORM". AE>С реляционными данным я хочу работать на полноценном реляционном языке. И декоративный SQL выражает работу с данными очень компактно.
С linq знакомы? Вполне полноценный реляционный язык.
AE>Но вот потом, я хочу чтобы то, что я "нагреб" превратилось в объекты. Чтобы мне удобнее было работать с этим в слое бизнес-логики. AE>И это, на мой взгляд, работа ORM (одна из). "Сделать объекты из того, что я выгреб".
Именно это он и делает.
AE>А на тему объектного описания запросов... ну как там в поговорке: "не говорите мне что делать, и я не скажу куда вам идти". AE>В общем то это достаточно традиционный отмаз: "у нас это не реализовано потому, что это не нужно". Во флейме его использовать можно и даже успешно. Но и что с того? Реальное удобство фреймворка от этого не повысится. AE>Да, на rsdn модно ругать RoR-овский ORM. Но выбирают часто именно RoR.
Здравствуйте, Alex EXO, Вы писали:
AE>Это не "проблемы роровского ORM". Это возможности "роровского ORM". AE>С реляционными данным я хочу работать на полноценном реляционном языке. И декоративный SQL выражает работу с данными очень компактно.
"Полноценный реляционный язык" SQL'92 за рядом тонких моментов вполне себе можно сделать статически типизированным. Так что и динамика при работе с ним не нужна.
AE>Но вот потом, я хочу чтобы то, что я "нагреб" превратилось в объекты.
А во что оно должно превратиться? И чем оно лучше объектов?
AE>А на тему объектного описания запросов... ну как там в поговорке: "не говорите мне что делать, и я не скажу куда вам идти".
А тебе тут никто и не говорит, что делать.
AE>Да, на rsdn модно ругать RoR-овский ORM. Но выбирают часто именно RoR.
Вопрос в том, по какой причине.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали: AVK>"Полноценный реляционный язык" SQL'92 за рядом тонких моментов вполне себе можно сделать статически типизированным. AVK>Так что и динамика при работе с ним не нужна.
По сути — да, можно. И на стороне ORM-а можно вычитать конфигурацию системных таблиц и настроить классы.
(Что собственно рельсовый ORM и делает.)
Но всегда остаются случаи "единичных запросов", под которые писать ручками расширение классов слишком геморойно.
И здесь я вижу только два пути:
— динамически расширять экземпляры, не трогая класс в целом;
— "невидимым" образом генерить класс-наследника с набором атрибутов адаптированных под этот конкретный запрос
(запрос для этого придется распарсить на этапе препроцессинга). Для того моего примера мог бы быть автоматически
сгенерен класс _ExtUserAccount наследник от ExtUser с дополнительными атрибутами.
Но на сколько я понял, в данном проекте речь о автоматической генерации классов на основе запросов не идет.
А тогда, для "единичных запросов" писанины станет куда больше.
Не, такой фрейворк тоже будет полезен (за быстродействие можно порой кой чем пожертвовать),
но я бы не стал питать иллюзий, что он будет столь же удобен, как и динамический.
AVK>Вопрос в том, по какой причине.
Твоя версия?
Здравствуйте, Alex EXO, Вы писали:
AE>Но всегда остаются случаи "единичных запросов", под которые писать ручками расширение классов слишком геморойно.
Какое такое расширение классов? О чем ты?
AE>И здесь я вижу только два пути: AE>- динамически расширять экземпляры, не трогая класс в целом; AE>- "невидимым" образом генерить класс-наследника с набором атрибутов адаптированных под этот конкретный запрос
О чем ты вообще?
AE>Но на сколько я понял, в данном проекте речь о автоматической генерации классов на основе запросов не идет.
Не нужно никаких классов под каждый запрос генерить.
AE>но я бы не стал питать иллюзий, что он будет столь же удобен, как и динамический.
Пока что ты питаешь какие то странные иллюзии по поводу linq.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали: AVK>О чем ты вообще?
Да, пожалуй разговор и правда не складывается. На разных языках говорим.
AVK>Пока что ты питаешь какие то странные иллюзии по поводу linq.
А разве я вообще о linq?
Я о возможности реализации некоторых удобных фич на статически типизированном языке.
В прочем — неважно.
Здравствуйте, Alex EXO, Вы писали:
AVK>>Пока что ты питаешь какие то странные иллюзии по поводу linq. AE>А разве я вообще о linq? AE>Я о возможности реализации некоторых удобных фич на статически типизированном языке.
Ты так и не ответил — зачем что то там генерить под каждый запрос.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали: AVK>Ты так и не ответил — зачем что то там генерить под каждый запрос.
Попробую. Просто если ты не пользовался экземплярными расширениями, то сложно объяснить на сколько это порой удобно.
Предположим у нас есть класс User с данными о пользователе и класс Account с данными о правах, тарифе и прочем...
Я хочу получить список пользователей с информацией о правах, причем получить его одним sql-запросом (для быстроедействия).
Если я хочу работать не в идеологии DataSet-ов, а с полноценными объектами User, то ORM должен обернуть результат запроса в эти объекты.
Но! У нас там "лишние атрибуты", котрых нет в классе User. Классическое решение в ORM на этот случай: вложенный объект user.account класса Account. А уж разложить результат запроса по нескольким связанным объектам любой нормальны ORM сможет.
Толко вот это все так, пока мы вытаскиваем все поля Account. А если это "тяжелый" объект, а нам нужно в данном случае только одно поле is_admin ?
Попытка создать связанный объект класса Account над одним полем, приведет к "неполноценному" объекту с непредсказуемым поведением (особенно если например в классе Account перекрыт геттер Account.is_admin()).
В общем то по условиям задачи нам и не требуется класс Account, а нужно лишь одно дополнительное свойство в класс User. Да только вот в статически типизированном языке его там не предусмотрено.
Рельсы-то поступят просто: расширят объекты — результаты запроса еще одним атрибутом. (Реально, там механизм хитее, но внешне выглядит именно так.) Если рассматривать ситуацию с точки зрения строгой типизации, то как бы возник еще один класс, наследник от User с дополнительным атрибутом is_admin.
При статической типизации такой класс видимо придется создать явно, в автоматическом режиме. Потому, что заставлять его писать пользователя — есть изврат. Остается конечно вариант работы на уровне нетипизованных DataSet-ов, но его нельзя назвать особо удобным решением.
Здравствуйте, Alex EXO, Вы писали:
AE>Если я хочу работать не в идеологии DataSet-ов, а с полноценными объектами User, то ORM должен обернуть результат запроса в эти объекты.
Совсем нет. Для построения ad hoc запросов в статически типизированных языках обычно используют кортежи. В шарпе для этого есть анонимные типы. Выглядит это так:
var userAndRights =
from u in DB.Users()
select new {u.Name, u.Account.IsAdmin};
foreach (var unr in userAndRights)
Console.WriteLine(unr.Name, unr.IsAdmin);
Все на 100% статически типизированно и не требует никакой генерации классов и не нужной динамики.
P.S. В С# 4 можно и динамику прикрутить. Но в данной конкретной задаче это кривое решение.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали: AVK>Совсем нет. Для построения ad hoc запросов в статически типизированных языках обычно используют кортежи. В шарпе для этого есть анонимные типы. Выглядит это так: AVK>[c#] AVK>var userAndRights = AVK> from u in DB.Users() AVK> select new {u.Name, u.Account.IsAdmin}; AVK>foreach (var unr in userAndRights) AVK> Console.WriteLine(unr.Name, unr.IsAdmin);
1. unr здесь будет какого типа? Просто кортеж? Или я могу к ней методы класса User применять? Что нибудь типа unr.ban(Time.new + 1.month)
2. для того, чтобы сравняться с RoR по удобству, нужно сократить код выше до одной строки.
Здравствуйте, Alex EXO, Вы писали:
AE>1. unr здесь будет какого типа? Просто кортеж? Или я могу к ней методы класса User применять? Что нибудь типа unr.ban(Time.new + 1.month)
Чтобы забанить пользователя не нужен сам объект, нужен только ключ. Поэтому никакого смысла делать ban методом класса User нет.
AE>2. для того, чтобы сравняться с RoR по удобству, нужно сократить код выше до одной строки.
foreach (var unr in DB.Users().Select(u => new {u.Name, u.Account.IsAdmin})
Console.WriteLine(unr.Name, unr.IsAdmin);
Здравствуйте, AndrewVK, Вы писали: AVK>Методы ORM класса? Не думаю, что это разумный дизайн.
А зачем мне видеть ORM классы вообще? Пусть об этом потроха фреймворка заботятся.
Нет, я хочу чтобы был создан класс прикладной модели. О чем и речь.
То есть думать только о двух вещах: структуре базы как таковой (для оптимизации запросов), и прикладной модели предметной области.
Промежуточные уровни торчать на виду не должны.
Здравствуйте, Ziaw, Вы писали: Z>Чтобы забанить пользователя не нужен сам объект, нужен только ключ. Поэтому никакого смысла делать ban методом класса User нет.
Так это ж пример был.
Реально может быть какой-нибудь документ, который нужно обработать (с прикрепленной к нему фамилией подписавшего).
Да мало ли еще что. Вопрос о "смысле" в нашем разговоре не стоит. Разговор только о "возможности".
А уж для чего эту возможность применить пользователи фреймворка найдут.
Здравствуйте, Alex EXO, Вы писали:
Z>>Чтобы забанить пользователя не нужен сам объект, нужен только ключ. Поэтому никакого смысла делать ban методом класса User нет. AE>Так это ж пример был. AE>Реально может быть какой-нибудь документ, который нужно обработать (с прикрепленной к нему фамилией подписавшего).
Какая проблема передать в метод обработки документ и фамилию подписавшего разными параметрами, не пытаясь засунуть их в один класс?
AE>Да мало ли еще что. Вопрос о "смысле" в нашем разговоре не стоит. Разговор только о "возможности". AE>А уж для чего эту возможность применить пользователи фреймворка найдут.
Какой интересный подход, пихайте все возможности которые придут вам в голову, даже если не видите в них смысла. Пользователи найдут им применение.
Вначале небольшой дисклаймер. Я не претендую на авторство идеи или какую-то руководящую роль в проекте. Я всего лишь хочу чтобы идея не умерла.
Попробую расписать этапы развития проекта, чтобы было понятно, что он реален и не требует заоблачных вложений.
Milestone 0. ASP.NET MVC 2 on Nemerle
Это начальная точка проекта, точка отсчета.
Milestone 1. Model.
Требуется написать макросы для генерации свойств и атрибутов bltoolkit'а в пустые классы модели помеченные макросом. Свойства генерируются по схеме одноименной с классом таблицы либо таблицы зданной как параметр макроса.
Перед началом работы нужно:
Дописать абстракции для работы со схемами СУБД поддерживающимыми тулкитом (это можно оформить как часть тулкита, если IT не будет против).
Продумать механизм конфига приложения, как минимум строк подключения.
Продумать правила сопоставления имен классов и имен таблиц для разных субд.
Этап считается завершенным в случае когда для работы с новой таблицей требуется только создать одноименный класс в проекте. Связи между таблицами считаю необязательными на этом этапе, возможно кто-то добрый доделает их параллельно.
Milestone 2. Migrations.
Если кто-то не знает что это — http://flux88.com/blog/net-database-migration-tool-roundup/.
На этом этапе требуется продумать и реализовать DSL для выполнения миграций в БД (это тоже можно оформить как часть тулкита, сами механизмы по изменению схемы и возможно поддержки версионности, а не DSL). Очень желательно, чтобы DSL умел автоматически строить undo хотя бы для простых ситуаций.
Этап считается завершенным если в шаблоне приложения можно легко создавать и гонять вверх/вниз миграции. Интеграцей в студию всего этого можно потом заниматься паралельно.
Milestone 3. Constants.
На этом этапе хочется уметь генерировать навигационные механизмы для указания View, Controller, Action, и некоторых стандартных урлов без применения магических строк.
Не хочется пока углубляться в детали, раннее видение таково:
Views.Home.Index или Views.Index просто возвращают строку View
Controller.Home.Index(par1, par2) должен возвращать какой-то экшен дескриптор для построения уролов.
Action.Index то же самое в конексте текущего контроллера.
Похожее делают в MvcContrib через T4.
Этап считается завершенным если все это генерируется без участия человека, всякие хтмл хелперы можно потом писать параллельно.
Milestone 5. Немного магии.
Здесь я хочу избавиться от классов viewmodel. Точнее сделать их невидимыми для программиста, их поля будут задаваться прямо в контролле макросами.
def model = Views.Home.Model();
model.par1 = 10;
model.par2 = "welcome to nemerle on rails world";
Не уверен пока, что это вообще возможно, но очень хочется.
Milestone 6. Шаблоны для студии, правка багов, замечаний.
Собственно сабж, подготовка к бэте. На этом этапе революционное развитие первой версии останавливается, начинается эволюция.
Здравствуйте, Ziaw, Вы писали: Z>Какая проблема передать в метод обработки документ и фамилию подписавшего разными параметрами, не пытаясь засунуть их в один класс?
Н-да...
1. Кроме "фамилии подписавшего" объект может потребоваться расширить много чем... "фамилией согласовавшего", "номером курьера доставившего пакет"...
2. А кто сказал, что дополнительные поля понадобятся именно в этом методе? Это-то как раз не так — методы базового класса ничего про расширения не знаю, те обычно обрабатываются "внешней" функцией. Речь о другом — единожды прочитанный объект используется многократно. В одних случаях это методы базового класса, а в других — анализ дополнительных атрибутов. И чем больше прикладного функционала может опираться на один запрос к базе, тем меньше нагрузка на базу.
AE>>Да мало ли еще что. Вопрос о "смысле" в нашем разговоре не стоит. Разговор только о "возможности". AE>>А уж для чего эту возможность применить пользователи фреймворка найдут. Z>Какой интересный подход, пихайте все возможности которые придут вам в голову, даже если не видите в них смысла. Z>Пользователи найдут им применение.
Неверно. Не "все возможности которые придут вам в голову", а те, которые реально используются в удачном фреймворке.
Которым пользователи уже нашли применение.
В общем, я не вижу смысла в дальнейшем споре. RoR уже неоднократно пытались воспроизвести на других языках и в результате получались тупиковые, неинтересные проекты. Во многих случаях потому, что их авторы считали те или иные сложно реализуемые на их языках фичи RoR "не нужными".
Ну будет еще один мертвый проект, на сей раз на Немерле... Как говорится — ваше дело.
Просто RoR потому и является талантливо сделанным фреймворком, что в нем мало лишнего и почти все фичи имеют свой смысл.
Здравствуйте, Alex EXO, Вы писали:
AE>1. Кроме "фамилии подписавшего" объект может потребоваться расширить много чем... "фамилией согласовавшего", "номером курьера доставившего пакет"...
Все что требуется для метода обработки он должен запросить явно. Мне не нравится паттерн ActiveRecord, особенно когда он начинает требовать композитных классов.
AE>2. А кто сказал, что дополнительные поля понадобятся именно в этом методе? Это-то как раз не так — методы базового класса ничего про расширения не знаю, те обычно обрабатываются "внешней" функцией. Речь о другом — единожды прочитанный объект используется многократно. В одних случаях это методы базового класса, а в других — анализ дополнительных атрибутов. И чем больше прикладного функционала может опираться на один запрос к базе, тем меньше нагрузка на базу.
Нагрузка будет меньше если выбирать только то, что нужно одним запросом, а не джойнить кучу таблиц со всеми полями каждый раз в надежде, что это все понадобится.
Z>>Какой интересный подход, пихайте все возможности которые придут вам в голову, даже если не видите в них смысла. Z>>Пользователи найдут им применение. AE>Неверно. Не "все возможности которые придут вам в голову", а те, которые реально используются в удачном фреймворке. AE>Которым пользователи уже нашли применение.
Что-то я пока не увидел удачного варианта применения.
AE>В общем, я не вижу смысла в дальнейшем споре. RoR уже неоднократно пытались воспроизвести на других языках и в результате получались тупиковые, неинтересные проекты. Во многих случаях потому, что их авторы считали те или иные сложно реализуемые на их языках фичи RoR "не нужными".
Проект не копирует рельсы. Проект пытается взять от них некоторые удачные принципы.
AE>Ну будет еще один мертвый проект, на сей раз на Немерле... Как говорится — ваше дело. AE>Просто RoR потому и является талантливо сделанным фреймворком, что в нем мало лишнего и почти все фичи имеют свой смысл.
Здравствуйте, Alex EXO, Вы писали:
AE>Нет, я хочу чтобы был создан класс прикладной модели. О чем и речь.
Класс прикладной модели и ad hoc запрос — разные вещи. В модель добавляют вещи с многократным реюзом, на них не грех и отдельный класс завести хотя бы для явной декларации контрактов. А в случае adhoc запросов тебе нужны данные, а не готовая модель.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, Ziaw, Вы писали:
Z> имеет из коробки linq ORM (BlToolkit), view engine (Spark view engine), но имеет "разъемы" для замены их на другие.
Я не знаком с Spark view engine. Можно в двух словах описать, что это такое и что он дает?
Что касается по работе с БД... На мой взгляд самый правильный подход — это житие от модели. То есть на некотором ДСЛ-е описываем БД, а стандартный (но хитрый) компонент по этой модели генерирует или обновляет структуру БД (как на стестовых, так и на реальных данных). Что касается опсений, то я еще года два назад имен вполне осезаемое видение того как это можно сделать.
Теперь о том зачем жить от модели...
Для нормальной быстрой разработки нужно чтобы все данные необходимые для разработки лежало в системе контроля версий (скажем SVN). Разработчик должен поставить средства разработки, выкачать проект, нажать F5 в IDE или запустить батник и получить полностью рабочее окружение. Это не сложно реализуется если все данные удается хранить в файлах и они небольшого размера. Как только появляется БД, это становится проблематичным. Нам нужно чтобы БД испльзуемая для разработки автоматически обновлялась и соотвествовала требованиям которые к ней предъявляет код. Причем это дожно делаться автоматом. Конечно для отладки можно каждый раз генерировать БД с нуля и заполнять ее тестовыми данными, но это приводит к некоторым ограничениям. Например, у программиста может быть отдельные БД для тестирования производительности. Их уже в SVN не положишь.
Далее, разработав программу нам нужен максимально простое развертывание. Если базу придется обновлять вручную или придется для этого писать какие-то скрипты (как в РоР), то это будет совсем не здорово. Намного лучше иметь автоматически работающий компонент который при запуске приложения (или отдельной утилиты) сверял бы структуру БД со структурой модели и если они не совпадают, то обновлял бы структуру БД.
Так вот — это реально, хотя и не очень просто в реализации.
Ну, а генерировать модель по БД, конечно, тоже нужно. Но это к счастью не очень сложно. Нужно будет только разработать набор соглашений и генератор кода (того самого ДСЛ-я модели данных).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
\AVK>>О чем ты вообще? AE>Да, пожалуй разговор и правда не складывается. На разных языках говорим.
+1
AVK>>Пока что ты питаешь какие то странные иллюзии по поводу linq. AE>А разве я вообще о linq? AE>Я о возможности реализации некоторых удобных фич на статически типизированном языке. AE>В прочем — неважно.
Важно. Линк действительно демонстрирует, что то что ты считаешь проблемой — не проблема.
Так что поверь на слово, или разберись в линке и проверь сам.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Далее, разработав программу нам нужен максимально простое развертывание. Если базу придется обновлять вручную или придется для этого писать какие-то скрипты (как в РоР), то это будет совсем не здорово. Намного лучше иметь автоматически работающий компонент который при запуске приложения (или отдельной утилиты) сверял бы структуру БД со структурой модели и если они не совпадают, то обновлял бы структуру БД.
VD> Так вот — это реально, хотя и не очень просто в реализации.
Со всем согласен, кроме этого пункта.
Автоматическая миграция — это не просто "не очень просто", а иногда и невозможно реализовать без ручного написания скриптов. Простейший пример — переименования поля в entity. Имея на руках только модель, никак нельзя определить, было поле переименовано или одно было удалено и добавлено другое. А если рассмотривать более сложные рефакторинги (вынесение колонки во внешную таблицу), то тут вообще урыться можно.
Здравствуйте, Ziaw, Вы писали:
Z>Требуется написать макросы для генерации свойств и атрибутов bltoolkit'а в пустые классы модели помеченные макросом. Свойства генерируются по схеме одноименной с классом таблицы либо таблицы зданной как параметр макроса.
Как я уже говорил. Лучше идти от обратного. Создать модель данных (нечто-воде ER-модели) в виде некоторого ДСЛ-я или ХМЛ-я и по ней генерировать и обновлять БД. Для этого нужно создать довольно умный компонет.
С него и нужно начинать. Это однозначно будет изюминкой проекта.
Кроме того нужна утилита рверс-инжиниринга — генерирующая модель по БД. Особого ума тут не нужно, так как после генерации модели можно будет вносить изменения в нее, а БД обновлять по ней.
Z>Перед началом работы нужно: Z>Дописать абстракции для работы со схемами СУБД поддерживающимыми тулкитом (это можно оформить как часть тулкита, если IT не будет против).
Да. Нужны универсальные средства добычи метаинформации. В принципе этот код удобнее писать на Немерле, так как там будет не мало условной (основанного на условиях) генерации SQL-я под разные виды СУБД.
Z>Продумать механизм конфига приложения, как минимум строк подключения.
Это у ИТ в БЛтулките уже вроде как сделано.
Z>Продумать правила сопоставления имен классов и имен таблиц для разных субд.
А зачем для разных СУБД использовать разные имена для таблиц?
Z>Этап считается завершенным в случае когда для работы с новой таблицей требуется только создать одноименный класс в проекте. Связи между таблицами считаю необязательными на этом этапе, возможно кто-то добрый доделает их параллельно.
Не. Этап считается завершенным если мы смогли по БД сгенерировать модель данных, далее изменили эту модель данных, запустили синхронизатор структуры БД и он автоматом обновил стркутуру БД, и при этом не уничтожил данные в БД.
Короче, не стоит повторять проверенно не удобные решения и замахиваться на малое. Нужно сразу замахиваться на максимальный уровень.
Z>
Milestone 2. Migrations.
Z>Если кто-то не знает что это — http://flux88.com/blog/net-database-migration-tool-roundup/. Z>На этом этапе требуется продумать и реализовать DSL для выполнения миграций в БД (это тоже можно оформить как часть тулкита, сами механизмы по изменению схемы и возможно поддержки версионности, а не DSL). Очень желательно, чтобы DSL умел автоматически строить undo хотя бы для простых ситуаций.
Вот это точно не над. Это убогая идея. Наш синхронизатор структуры БД должен без каких либо дополнительных описаний (миграционных скриптов) менять структуру уже существующей БД. При этом должно быть совершенно по фигу какой версии эта БД. У нас есть только имеющаяся структура БД и требуемая модель. Все остальное реструктуризатор должен сделать сам. В итоге мы должны получить БД требуемой структуры. И все это одним запуском утилиты или специального метода.
Z>Этап считается завершенным если в шаблоне приложения можно легко создавать и гонять вверх/вниз миграции. Интеграцей в студию всего этого можно потом заниматься паралельно.
Ага. И интеграция никакая не нужна. В общем, привыкаем мыслить масштабнее!
Z>
Milestone 3. Constants.
Z>На этом этапе хочется уметь генерировать навигационные механизмы для указания View, Controller, Action, и некоторых стандартных урлов без применения магических строк.
Я не силен в ASP.NET MVC, но если я правильно понял, то тут речь идет о неком форкфлоу. Тут тоже есть над чем подумать. Как правильно заметил hardcase, неплохо было бы иметь реализацию локальных продолжений (континюэшонов) на базе которых форкфлову реализуется очень элегантно. Оно как бы живет своей жизнью. Его можно сериализовать, клонировать и перезапускать хоть тысячу раз.
Z>
Milestone 5. Немного магии.
Z>Здесь я хочу избавиться от классов viewmodel. Точнее сделать их невидимыми для программиста, их поля будут задаваться прямо в контролле макросами.
А какова цель viewmodel?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Lloyd, Вы писали:
L>Со всем согласен, кроме этого пункта.
L>Автоматическая миграция — это не просто "не очень просто", а иногда и невозможно реализовать без ручного написания скриптов. Простейший пример — переименования поля в entity.
Я же сказал, что продумывал уже это. Для переименования полей достаточно ввести не очень красивые но очень эффективные уникальные идентификаторы (GUID-ы). Любой сущности в модели при создании этой сущности присваивается GUID. Далее в БД создается таблица метаинформации. Так что если мы имеет дело с БД созданной по любой версии модели, то мы сможем автоматически преобразовать ее в другую версию, причем как в более новую, так и в более старую. И все это без промежуточных стадий!
L>Имея на руках только модель, никак нельзя определить, было поле переименовано или одно было удалено и добавлено другое. А если рассмотривать более сложные рефакторинги (вынесение колонки во внешную таблицу), то тут вообще урыться можно.
Ну, как видишь, простейшее решение позволяет это все отследить.
У этого решения конечно есть свои проблемы, но они не так страшны. Скажем возможно повреждение или расинхронизация таблиц хранящих дополнительные метаданные, но это можно проверять и требовать исправления проблемы. Ну, и раз уж мы живем от модели, то не черта лазить в БД напрямую. Меняй модель и получай новую структуру БД.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
L>>Имея на руках только модель, никак нельзя определить, было поле переименовано или одно было удалено и добавлено другое. А если рассмотривать более сложные рефакторинги (вынесение колонки во внешную таблицу), то тут вообще урыться можно.
VD>Ну, как видишь, простейшее решение позволяет это все отследить.
Если честно, то не вижу. Например, то же вынесение колонки может быть произведено как с удалением дубликатов, так и с сохранением оных.
Хотя, если цель — исключительно миграция структуры без сохранения данных, то наверное можно сделать.
VD>У этого решения конечно есть свои проблемы, но они не так страшны. Скажем возможно повреждение или расинхронизация таблиц хранящих дополнительные метаданные, но это можно проверять и требовать исправления проблемы. Ну, и раз уж мы живем от модели, то не черта лазить в БД напрямую. Меняй модель и получай новую структуру БД.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Ziaw, Вы писали:
Z>> имеет из коробки linq ORM (BlToolkit), view engine (Spark view engine), но имеет "разъемы" для замены их на другие.
VD>Я не знаком с Spark view engine. Можно в двух словах описать, что это такое и что он дает?
это язык для генерации html более удобный и интуитивный чем шаблоны асп.нет.
для примера проще показать код, он понятен любому знакомому с html и С#.
<viewdata products="IEnumerable[[Product]]"/>
<ul if="products.Any()">
<li each="var p in products">${p.Name}</li>
</ul>
<else>
<p>No products available</p>
</else>
VD>Что касается по работе с БД... На мой взгляд самый правильный подход — это житие от модели. То есть на некотором ДСЛ-е описываем БД, а стандартный (но хитрый) компонент по этой модели генерирует или обновляет структуру БД (как на стестовых, так и на реальных данных). Что касается опсений, то я еще года два назад имен вполне осезаемое видение того как это можно сделать.
Я не хочу начинать с задачи с непонятными перспективами. Если есть желание, прорабатывай этот момент, генерация модели по бд или наоборот для остальной части не так важно.
VD>Теперь о том зачем жить от модели...
VD>Для нормальной быстрой разработки нужно чтобы все данные необходимые для разработки лежало в системе контроля версий (скажем SVN). Разработчик должен поставить средства разработки, выкачать проект, нажать F5 в IDE или запустить батник и получить полностью рабочее окружение. Это не сложно реализуется если все данные удается хранить в файлах и они небольшого размера. Как только появляется БД, это становится проблематичным. Нам нужно чтобы БД испльзуемая для разработки автоматически обновлялась и соотвествовала требованиям которые к ней предъявляет код. Причем это дожно делаться автоматом. Конечно для отладки можно каждый раз генерировать БД с нуля и заполнять ее тестовыми данными, но это приводит к некоторым ограничениям. Например, у программиста может быть отдельные БД для тестирования производительности. Их уже в SVN не положишь.
Рельсы кладут, там 3 БД по умолчанию на проект. Продакшен, дев и тест. И это именно "запустить батник".
VD>Далее, разработав программу нам нужен максимально простое развертывание. Если базу придется обновлять вручную или придется для этого писать какие-то скрипты (как в РоР), то это будет совсем не здорово. Намного лучше иметь автоматически работающий компонент который при запуске приложения (или отдельной утилиты) сверял бы структуру БД со структурой модели и если они не совпадают, то обновлял бы структуру БД.
Это реально просто, нажать дну кнопку.
VD> Так вот — это реально, хотя и не очень просто в реализации.
Это не просто. На практике обычно идет эволюция БД с требованием поддержки всех имеющихся данных. Этот вопрос решается только скриптами миграции.
Я не представляю что должен сделать генератор по модели...
...имеющий БД с лишней таблицей.
...имеющий БД с недостающей таблицей в которую превратилось строковое поле в одной из предыдущих
...имеющей колонку с не совпадающим типом
Я не доверю слишком умным инструментам (кроме решарпера).
VD>Ну, а генерировать модель по БД, конечно, тоже нужно. Но это к счастью не очень сложно. Нужно будет только разработать набор соглашений и генератор кода (того самого ДСЛ-я модели данных).
юзабельных решений для подобной генерации я не встречал, а вот обратные зарекомендовали себя очень хорошо.
Здравствуйте, Lloyd, Вы писали:
L>Если честно, то не вижу.
Не видишь, так не видишь. Спорить уже нет сил.
L>Например, то же вынесение колонки может быть произведено как с удалением дубликатов, так и с сохранением оных. L>Хотя, если цель — исключительно миграция структуры без сохранения данных, то наверное можно сделать.
Если при преобразовании/копировании данных потребуется удалять данные, то данные можно спасать копируя в отдельные таблицы. Конечно при смене версий в двух направлениях данные не востановятся, но для реального развития БД и спасения данных это не обязательно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Timur_SPB, Вы писали:
T_S>А в чем суть проекта? T_S>Вопрос конечно наивный и простой, но ответ, понятный широким массам — это необходимое условие для киллерапп.
. Реализовать его у меня не получилось — банально нехватило соображалки.
Если он сможет шарить себя между запросами то почему бы и нет. Но делать его надо параллельно разработке движка, ибо для движка он совершенно некритичная фича.
Z>>Html.ActionLink(product.Name, "Show", new {id = product.Id})
Z>>
Z>>Может превартиться в: Z>>
Z>>Action.Show(product.Id).Link(product.Name)
Z>>
Z>>Наличие этого экшена проверит компилятор. Z>>[/list]
VD>Что делает этот код?
Это код для view, формирует ссылку для вызова экшена Show с параметром id=product.Id и текстом product.Name внутри.
VD>И не лучше ли для него более декларативный ДСЛ-ьчик сделать?
Здравствуйте, Ziaw, Вы писали:
Z>это язык для генерации html более удобный и интуитивный чем шаблоны асп.нет. Z>для примера проще показать код, он понятен любому знакомому с html и С#. Z>
Z><viewdata products="IEnumerable[[Product]]"/>
Z><ul if="products.Any()">
Z> <li each="var p in products">${p.Name}</li>
Z></ul>
Z><else>
Z> <p>No products available</p>
Z></else>
Z>
Мне кажется такую штуку проще и лучше реализовать на макрах, а не использовать внешние генераторы. Тогда у нас будет и интеллисенс и другие пряники.
Z>Я не хочу начинать с задачи с непонятными перспективами.
А что тут непонятного? Ты о чем?
Z>Если есть желание, прорабатывай этот момент, генерация модели по бд или наоборот для остальной части не так важно.
Не важно, конечно. Но без этой части другие начинать бесполезно. Как я уже говорил, нам нужно чтобы проекты собирались из запускались (хотя бы тесты) одним кликом.
Z>Рельсы кладут, там 3 БД по умолчанию на проект. Продакшен, дев и тест. И это именно "запустить батник".
Я в курсе. Но в рельсах для создания новой версии БД нужно писать скрипты. Это очень плохой подход. Скрипты еще приемлемы когда мы выпускаем новую версию продукта которая должна будучи проинсталлированной обновить БД у пользователя. Но для развития проектов нам нужна полная автоматика!
VD>>Далее, разработав программу нам нужен максимально простое развертывание. Если базу придется обновлять вручную или придется для этого писать какие-то скрипты (как в РоР), то это будет совсем не здорово. Намного лучше иметь автоматически работающий компонент который при запуске приложения (или отдельной утилиты) сверял бы структуру БД со структурой модели и если они не совпадают, то обновлял бы структуру БД.
Z>Это реально просто, нажать дну кнопку.
Не. Это сначала написать кучу скриптов, а потом нажать кнопку. А лучше чтобы сразу кнопку.
VD>> Так вот — это реально, хотя и не очень просто в реализации.
Z>Это не просто. На практике обычно идет эволюция БД с требованием поддержки всех имеющихся данных. Этот вопрос решается только скриптами миграции.
Уж для БД разработчиков срипты — это точно огромное зло. Все можно описать декларативно. Считай, что модель — это и есть такой скрипт. Только не императивный, а декларативный. А императивный скрипт нужно уметь вычислить на основании двух моделей.
Z>Я не представляю что должен сделать генератор по модели... Z>
Z>...имеющий БД с лишней таблицей.
Это не проблема.
Z>...имеющий БД с недостающей таблицей в которую превратилось строковое поле в одной из предыдущих Z>...имеющей колонку с не совпадающим типом Z>
Для таких вещей конечно будут нужны скрипты или ще что-то. Но как раз такого равития нужно избегать. 99% времени у тебя не будет подобных вывертов. А будет плановая реструктуризация. Изменение типов поле, переименования полей, разбиение или слияние таблиц и т.п. Вот это все надо делать в автомате!
Ну, а если нужны какие-то вещи которые нельзя описать моделью, то конечно унжны скрипты. Но лично я бы предпочел просто не делать такой алхимии в своем проекте.
Z>Я не доверю слишком умным инструментам (кроме решарпера).
А Решарперу значит довреяшь? Ну, вот и надо создать тоже интеллектуальный инструмент.
Z>юзабельных решений для подобной генерации я не встречал, а вот обратные зарекомендовали себя очень хорошо.
Дык ты никогда не получишь чего-то выдающегося если всегда будешь только проверенные решения использовать.
Я вот не встречал построителей парсеров которые позволяли бы отделять грамматику от ее обработчиков и которые бы статически проверяли код, но это не помешало сделать прототипо такого решения .
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Я не знаком с Spark view engine. Можно в двух словах описать, что это такое и что он дает?
Кстати, роадмапе я его выключил из списка ключевых фич, просто по признаку — его можно прикрутить сбоку. Главное сделать простое, удобное и понятное всем ядро. Кто будет ренедрить view по сути дело десятое, одним нравится спарк другим nhaml. Создатель спарка кстати сейчас переманен в команду asp.net, возможно спарк превратится в расширение asp.net.
Здравствуйте, VladD2, Вы писали:
VD>Я в курсе. Но в рельсах для создания новой версии БД нужно писать скрипты. Это очень плохой подход. Скрипты еще приемлемы когда мы выпускаем новую версию продукта которая должна будучи проинсталлированной обновить БД у пользователя. Но для развития проектов нам нужна полная автоматика!
По уровню автоматизации нет разницы написать table Product add Details : string в миграции или "public string Details {get {}; set {}}" в классе Product. Разница в нажатиях настолько невелика, что ей можно пренебречь.
Здравствуйте, Ziaw, Вы писали:
Z>Кстати, роадмапе я его выключил из списка ключевых фич, просто по признаку — его можно прикрутить сбоку. Главное сделать простое, удобное и понятное всем ядро. Кто будет ренедрить view по сути дело десятое, одним нравится спарк другим nhaml. Создатель спарка кстати сейчас переманен в команду asp.net, возможно спарк превратится в расширение asp.net.
Беглый взгляд показывает, что мы подобную фигню относительно не сложно можетм на макросе слабать. Обойдемся без внешних зависимостей и сов всеми вкусностями встроенного ДСЛ-я.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ziaw, Вы писали:
Z>По уровню автоматизации нет разницы написать table Product add Details : string в миграции или "public string Details {get {}; set {}}" в классе Product. Разница в нажатиях настолько невелика, что ей можно пренебречь.
Дело не в нажатиях. Дел в том, что первое — это имератив (указание действий), а второе — декларация. Декларацию можно анализировать, императив — практически нельзя. Его можно только исполнять.
Так что нужно стремится к тому, чтобы если это возможно, опираться на модель, и уж если модель ничем помочь не может, тогда прибегать к императиву.
Так что нажатия не важны. Но обычно и нажатий на декларативные решения тоже меньше приходится.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Ziaw, Вы писали:
Z>>По уровню автоматизации нет разницы написать table Product add Details : string в миграции или "public string Details {get {}; set {}}" в классе Product. Разница в нажатиях настолько невелика, что ей можно пренебречь.
VD>Дело не в нажатиях. Дел в том, что первое — это имератив (указание действий), а второе — декларация. Декларацию можно анализировать, императив — практически нельзя. Его можно только исполнять.
VD>Так что нужно стремится к тому, чтобы если это возможно, опираться на модель, и уж если модель ничем помочь не может, тогда прибегать к императиву.
VD>Так что нажатия не важны. Но обычно и нажатий на декларативные решения тоже меньше приходится.
Я говорил про уровень автоматизации который ты привел в качестве аргумента. Модель в базе, анализировать можно как ее, так и классы которые по ней получены.
Здравствуйте, VladD2, Вы писали:
VD>Так что нужно стремится к тому, чтобы если это возможно, опираться на модель, и уж если модель ничем помочь не может, тогда прибегать к императиву.
Еще одна проблема в том, схема становтися автоматически генерируемой частью приложения. А вот покрыть DDL всех поддерживамых СУБД дело бесперспективное, т.е. все равно рано или поздно придется как-то менять схему БД вне контекста модели, а это будет уже правки сгенеренного исходника. В миграции же мы можем тупо написать нужный скрипт на чистом SQL (например создание материализованной вьюхи или хранимки).
VD>Не. Это сначала написать кучу скриптов, а потом нажать кнопку. А лучше чтобы сразу кнопку.
Сразу не получится. Надо создать модели. Просто другим способом.
Z>>Я не представляю что должен сделать генератор по модели... Z>>
Z>>...имеющий БД с лишней таблицей.
VD>Это не проблема.
Все таки я хочу увидеть ее решение.
Z>>...имеющий БД с недостающей таблицей в которую превратилось строковое поле в одной из предыдущих Z>>...имеющей колонку с не совпадающим типом Z>>
VD>Для таких вещей конечно будут нужны скрипты или ще что-то. Но как раз такого равития нужно избегать. 99% времени у тебя не будет подобных вывертов. А будет плановая реструктуризация. Изменение типов поле, переименования полей, разбиение или слияние таблиц и т.п. Вот это все надо делать в автомате!
Я так и не понял как делать в автомтате
VD>А Решарперу значит довреяшь? Ну, вот и надо создать тоже интеллектуальный инструмент.
Решарпер спрашивает меня какой я хочу сделать рефакторинг, а не делает его сам по каким-то ему ведомым признакам.
VD>Дык ты никогда не получишь чего-то выдающегося если всегда будешь только проверенные решения использовать.
Влад, это нечестный прием. Практика показывает, что работают как раз простые и понятные всем решения. Гениальный апдейтер будут просто бояться исползовать, т.к. предсказать как он себя поведет можно будет только после набивания шишек.
VD>Я вот не встречал построителей парсеров которые позволяли бы отделять грамматику от ее обработчиков и которые бы статически проверяли код, но это не помешало сделать прототипо такого решения .
Я не против создания подобного прототипа, я против объявления этого механизма основным и блокирующим остальную разработку.
Здравствуйте, Ziaw, Вы писали:
Z>Еще одна проблема в том, схема становтися автоматически генерируемой частью приложения. А вот покрыть DDL всех поддерживамых СУБД дело бесперспективное, т.е. все равно рано или поздно придется как-то менять схему БД вне контекста модели, а это будет уже правки сгенеренного исходника. В миграции же мы можем тупо написать нужный скрипт на чистом SQL (например создание материализованной вьюхи или хранимки).
DDL у большинства серверов очень близкий по возможностям. Он же стандартизирован давно. Вот сехмы действительно разые.
Но это фигня. Данная проблема решается путем введения шаблонов генерирующих SQL. Делаем базовый шаблони и их наследники. В наследниках меняем то, что отличается у конкретных серверов.
Здравствуйте, VladD2, Вы писали:
VD>С него и нужно начинать. Это однозначно будет изюминкой проекта.
Я уже писал, что не хочу, чтобы это стало блокером проекта. Тем более, что для изюминки все равно придется написать почти все что нужно для миграций. А с миграциями можно идти дальше.
Z>>Продумать механизм конфига приложения, как минимум строк подключения.
VD>Это у ИТ в БЛтулките уже вроде как сделано.
Мне не нравится идея всех конфигов в одном, смешивать конфиги htttphandler, и приложения мне не хочется, но для начала она пойдет.
Z>>Продумать правила сопоставления имен классов и имен таблиц для разных субд.
VD>А зачем для разных СУБД использовать разные имена для таблиц?
Чтобы в MSSQL видеть таблицы вида MyProduct, а в оракле и фаерберде вида MY_PRODUCT. Впрочем это тоже не критично, но потом поменять будет сложно, для тех кто начал уже использовать.
VD>Не. Этап считается завершенным если мы смогли по БД сгенерировать модель данных, далее изменили эту модель данных, запустили синхронизатор структуры БД и он автоматом обновил стркутуру БД, и при этом не уничтожил данные в БД.
Вот вот, блокер в самом начале. Это плохой план.
Z>>
Milestone 3. Constants.
Z>>На этом этапе хочется уметь генерировать навигационные механизмы для указания View, Controller, Action, и некоторых стандартных урлов без применения магических строк.
VD>Я не силен в ASP.NET MVC, но если я правильно понял, то тут речь идет о неком форкфлоу. Тут тоже есть над чем подумать. Как правильно заметил hardcase, неплохо было бы иметь реализацию локальных продолжений (континюэшонов) на базе которых форкфлову реализуется очень элегантно. Оно как бы живет своей жизнью. Его можно сериализовать, клонировать и перезапускать хоть тысячу раз.
Не хочется задирать порог вхождения отказываясь от привычного всем workflow.
VD>А какова цель viewmodel?
Передать данные от контроллера во вью, чтобы она их показала.
Здравствуйте, VladD2, Вы писали:
VD>DDL у большинства серверов очень близкий по возможностям. Он же стандартизирован давно. Вот сехмы действительно разые.
VD>Но это фигня. Данная проблема решается путем введения шаблонов генерирующих SQL. Делаем базовый шаблони и их наследники. В наследниках меняем то, что отличается у конкретных серверов.
Далеко не так, и на боевых приложениях всегда хочется использовать нюансы конкретной субд. Если для введения хитрого индекса мне придется вникать и писать макросы, я буду плеваться.
Здравствуйте, AndrewVK, Вы писали: AVK>Класс прикладной модели и ad hoc запрос — разные вещи. В модель добавляют вещи с многократным реюзом, на них не грех и отдельный класс завести хотя бы для явной декларации контрактов. А в случае adhoc запросов тебе нужны данные, а не готовая модель.
Нет. В конечном итоге мне нужна именно готовая модель, как "кирпичик" моего приложения.
И если не задумываться об оптимизации, то я могу получить список моделей одним действием:
1) u_list = User.find(:all)
а потом использовать как будто все под рукой:
u_list.each {|u| puts "#{u.fio}, #{u.account.is_admin}"}
С прикладной точки зрения все будет работать.
Но дальше я хочу пооптимизировать и для начала избавиться от циклических запросов зная, что мне понадобятся account.
Уточняем это для ORM-а:
2) u_list = User.find(:all, :include=>[:account])
А дальше я хочу еще оптимизировать, беря из account только одно поле, которое понадобится:
3) u_list = User.find_by_sql("SELECT users.*, accounts.is_admin FROM...
Понимаешь? Я просто провожу последовательную оптимизацию. Правлю одну строчку.
(В случае 3) мне понадобится исправить еще и использование. Вот так: u_list.each {|u| puts "#{u.fio}, #{u.is_admin}"} )
Но никаких новых классов в системе я не создаю. Никакого "скачка сложности" нет при переходе к ad hoc запросу.
И думаю о двух вещах: об модели предметной области, с которой работает приложение и о структурах данных в базе (если мне этого захочется).
Как конвертировать одно в другое — задача ORM.
То есть мне легко тюнить приложение до нужного уровня оптимальности. Рост сложности при этом плавный и не большой.
И на мой взгляд, это ценное качество ActiveRecord.
Здравствуйте, VladD2, Вы писали: VD>Важно. Линк действительно демонстрирует, что то что ты считаешь проблемой — не проблема.
Влад, я знаю, что линк хороший ORM. (Во всяком случае он мне симпатичнее того же хиберната.)
Но речь-то не об этом. Речь о том, что ребята собираются делать фреймворк.
А фреймворк, это не код, это в первую очередь система идей. Причем именно "система", а не свалка.
По хорошему, первый вопрос к "делателям фреймворков": сформулируйте вашу систему идей полностью (как алгебру: определения, аксиомы, теоремы).
Уже после этого многое можно будет сказать о судьбе проекта. Но чтобы создать такую систему нужен талант.
Можно поступить проще, взять готовую систему и перенести в другую область (например, на другой язык).
Но для этого:
— эту систему нужно полностью понять;
— решить задачу отображения на новый язык всех ее компонентов.
Собственно, это я и проверял... (как выяснилось — проблемы уже на первом шаге).
То есть сам подход "взять от некоторые удачные принципы" — безнадежен. Поскольку, как я уже сказл, система — не свалка.
Метод "здесь читаем, здесь не читаем, а здесь рыбу заворачивали" — не проходит. На это уже наступили ребята из питоновской команды, котореые делали pylons. Получилось вроде бы и похоже, вроде бы и использовать можно, а все равно неудобно. (А ведь на питоне делать аналог рельсов куда проще, чем на nemerle.)
В оправдание ребят можно сказать, что копировать рельсы вообще очень сложно. Из-за того, что та система идей которая описывается в статьях и учебных материалах отличается от того, что есть реально в коде. На мой взгляд — преднамеренно. Это как топографические карты в советское время "с искажениями". Обычные туристы даже и не заметят, а враги — пролетят мимо. Часть принципиальных моментов не упоминается, у остальных переставлена значимость. В результате, большинство последователей дружно топают в болото. А тот, кто может разобраться в реальной системе идей рельсов, не станет ее копировать, поскольку способен создать свою, под свой язык и предметную область.
Здравствуйте, Alex EXO, Вы писали:
AE>В оправдание ребят можно сказать, что копировать рельсы вообще очень сложно. Из-за того, что та система идей которая описывается в статьях и учебных материалах отличается от того, что есть реально в коде. На мой взгляд — преднамеренно. Это как топографические карты в советское время "с искажениями". Обычные туристы даже и не заметят, а враги — пролетят мимо. Часть принципиальных моментов не упоминается, у остальных переставлена значимость. В результате, большинство последователей дружно топают в болото. А тот, кто может разобраться в реальной системе идей рельсов, не станет ее копировать, поскольку способен создать свою, под свой язык и предметную область.
Еще раз подчеркну, нет копирования рельс. Есть устранение некоторых недостатков ASP.NET MVC с помощью макросистемы Nemerle. Удачные принципы при этом в основном берутся из рельс, но лишь как из примера хорошей системы. Никто не собирается копировать роровский ORM просто потому, что есть bltoolkit. Никто не собирается клонировать erb. Нет никакого смысла копировать фреймворки тестирования какими бы суперскими они не были. Просто потому, что это ниша .net и там есть свои механизмы для всего этого. Все что планируется сделать до выхода в свет я расписал по шагам здесь
Здравствуйте, Ziaw, Вы писали: Z>Еще раз подчеркну, нет копирования рельс. Есть устранение некоторых недостатков ASP.NET MVC с помощью макросистемы Nemerle. Удачные принципы при этом в основном берутся из рельс, но лишь как из примера хорошей системы.
Ты все-таки рассматриваешь фреймворк как "набор фич"...
Я не особо знаю ASP.NET (поскольку не пишу под виндус), может быть он и является не более чем набором фич.
Ну тогда да, тогда наверное его можно улучшить "с помощью макросистемы Nemerle".
Тогда мы просто понимаем под "фреймворком" совершенно разные вещи и обсуждение не имеет смысла.
Здравствуйте, Alex EXO, Вы писали:
AE>И если не задумываться об оптимизации, то я могу получить список моделей одним действием: AE>1) u_list = User.find(:all) AE>а потом использовать как будто все под рукой: AE>u_list.each {|u| puts "#{u.fio}, #{u.account.is_admin}"} AE>С прикладной точки зрения все будет работать.
А с линком это будет работать уже с оптимизацией. И из БД будут выбираться только нужные поля.
AE>А дальше я хочу еще оптимизировать, беря из account только одно поле, которое понадобится: AE>3) u_list = User.find_by_sql("SELECT users.*, accounts.is_admin FROM...
AE>Понимаешь? Я просто провожу последовательную оптимизацию. Правлю одну строчку.
И что? С чего ты взял, что в случае с линком это невозможно? Более того, вместо втаптывания текста SQL, ты пишешь запрос на статически типизированном основном языке.
AE>Но никаких новых классов в системе я не создаю. Никакого "скачка сложности" нет при переходе к ad hoc запросу.
И ровно то же самое мы имеем в линке.
AE>Как конвертировать одно в другое — задача ORM.
С реляционными данным я хочу работать на полноценном реляционном языке.
Ты уж определись, то ли ты хочешь работать с реляционными данными, как с реляционными данными, то ли ты пытаешься все конвертировать.
Именно в этом заключается ключевое отличие разных видов ORM. Если в рор ОРМ требует работы с графом объектов, то никто его воспроизводить не собирается.
AE>И на мой взгляд, это ценное качество ActiveRecord.
А на мой взгляд ActiveRecord абсолютно негодная вещь.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали: AVK>А с линком это будет работать уже с оптимизацией. И из БД будут выбираться только нужные поля.
Расскажи, как линк определит, какие поля мне дальше понадобятся?
AVK>Ты уж определись, то ли ты хочешь работать с реляционными данными, как с реляционными данными, то ли ты пытаешься все конвертировать.
Сложные выборки хочу делать на реляционном (простые — пофигу на чем).
А вот бизнес-логику предпочитаю писать над объектами. По моему все просто...
Промежуточный слой кортежей и DataSet-ов... ну, лучше бы, что бы о нем не приходилось задумываться.
Однако я лучше соглашусь на него, чем выражать запросы к базе через объектную модель (именно поэтому я Владу и написал, что при отсутствии других вариантов предпочту линк хибернату). Но с AR все равно получается проще.
Начал работу. Немерл оказался в чем-то проще чем я думал. Но некоторые простые моменты стопорят страшно .
Проект и код можно смотреть на: http://code.google.com/p/nemerleonrails/
Здравствуйте, Alex EXO, Вы писали:
AVK>>А с линком это будет работать уже с оптимизацией. И из БД будут выбираться только нужные поля. AE>Расскажи, как линк определит, какие поля мне дальше понадобятся?
По их использованию в самом запросе. Запросы в линке ленивые, и реально исполняются только когда ты их начинаешь итерировать или сворачиваешь в скаляр.
AE>Сложные выборки хочу делать на реляционном (простые — пофигу на чем).
Ну вот линк и есть такой декларативный способ работы с реляционными и иерархическими данными.
AE>Промежуточный слой кортежей и DataSet-ов... ну, лучше бы, что бы о нем не приходилось задумываться.
А и не надо. Ты опять пытаешься в линке увидеть то, чего в нем нет.
AE>Но с AR все равно получается проще.
Чем проще?
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, Alex EXO, Вы писали:
AE>Но речь-то не об этом. Речь о том, что ребята собираются делать фреймворк.
Скорее библиотеку. Фрэймворк это базворд родившийся в следствии того, что во многих языках паттерны программирования нельзя заложить в библиотеку. Отсюда появилась необходимость создавать внешние тулы и другую фигню.
В Немерле же, который выбран в качестве средства реализации есть макры с помощью которых в библиотеку можно будет заложить почти все что нужно.
AE>А фреймворк, это не код, это в первую очередь система идей. Причем именно "система", а не свалка.
Ну, вот это не тот случай. Это будет именно библиотечный код который будет все эти идеи реализовывать на практике.
AE>По хорошему, первый вопрос к "делателям фреймворков": сформулируйте вашу систему идей полностью (как алгебру: определения, аксиомы, теоремы).
Мне кажется идея сформулирована весьма конкретно — создать аналог Руби на Рельсах, но на статически типизированном языке поддерживающем макросы.
Ну, а детали они уже будут прорабатываться по ходу дела.
AE>Уже после этого многое можно будет сказать о судьбе проекта.
О судьбе проекта можно будет сказать только когад его начнут делать и что-то начнет получаться (или он загнется).
AE>Но чтобы создать такую систему нужен талант.
О, да, капитан Очевидность! Чтобы сделать что-то стоящее нужен талант, стремление и время!
AE>Можно поступить проще, взять готовую систему и перенести в другую область (например, на другой язык). AE>Но для этого: AE>- эту систему нужно полностью понять; AE>- решить задачу отображения на новый язык всех ее компонентов.
Глупо — прямолинейно переносить на статически типизированный язык решения из динамически типизированного.
Нужно понимать и использовать особенности технологии на которую переносится решение.
AE>Собственно, это я и проверял... (как выяснилось — проблемы уже на первом шаге).
Ты просто плохо разбираешься в возможностях линка. И вместо того чтобы углубиться в этот вопрос вносишь поспешные суждения.
AE>Метод "здесь читаем, здесь не читаем, а здесь рыбу заворачивали" — не проходит. На это уже наступили ребята из питоновской команды, котореые делали pylons. Получилось вроде бы и похоже, вроде бы и использовать можно, а все равно неудобно. (А ведь на питоне делать аналог рельсов куда проще, чем на nemerle.)
Как я уже сказал делать полный аналог нет смысла. Нужно брать задачи и решать их имеющимися средствами. При этом получать те бенефиты которые можно получить и обходить те проблемы которые можно получить в следствии того, что весь процесс работы библиотеки (фрэймворка по вашему) будет протекать во время компиляции.
AE>В оправдание ребят можно сказать, что копировать рельсы вообще очень сложно. Из-за того, что та система идей которая описывается в статьях и учебных материалах отличается от того, что есть реально в коде. На мой взгляд — преднамеренно. Это как топографические карты в советское время "с искажениями".
Ну, а в статьях по Грувийным Рельсам все соответствует действительности?
AE> Обычные туристы даже и не заметят, а враги — пролетят мимо. Часть принципиальных моментов не упоминается, у остальных переставлена значимость. В результате, большинство последователей дружно топают в болото. А тот, кто может разобраться в реальной системе идей рельсов, не станет ее копировать, поскольку способен создать свою, под свой язык и предметную область.
Как я понял, ты считаешь, что знаешь эти моменты. Ну, так поделись с нами этими моментами.
Я не спец в Вебе. Давным давно много работал с СБУД (был пианером SQL-я в этой стране, можно сказать), но сейчас и с ним почти не работаю. Зато я поднаторел с самом Немреле и его макросах, так что я в этом проекте (если он случитя) буду выполнять роль эксперта по Немреле.
Если ты можешь помочь с идеологии, то вперед! Родина тебе не забудет!
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, AndrewVK, Вы писали: AE>>Расскажи, как линк определит, какие поля мне дальше понадобятся? AVK>По их использованию в самом запросе. Запросы в линке ленивые, и реально исполняются только когда ты AVK>их начинаешь итерировать или сворачиваешь в скаляр.
Если данные запроса все равно перепаковывать в объекты модели, то согласен,
во время перепаковки набор полей и определится.
Собственно предмета для спора нет, если вы делаете не аналог рельсов, а что-то иное.
Со своими принципами и идеями, то большинство моих вопросов снимается. А дальше — время рассудит, удачен окажется проект или нет.
Я бы пожелал вам удачи. Ибо "больше фреймворков хороших и разных".
Здравствуйте, Alex EXO, Вы писали:
AE>Если данные запроса все равно перепаковывать в объекты модели, то согласен,
Зачем их туда перепаковывать? ad hoc запросы тем и характерны, что не выставлятся просто наружу, а используются для дальнейшей обработки. И вот тут то вся мощь линка и проявляется — я могу описать в ленивом виде хитрющий алгоритм, разворачивающийся в несколько экранов sql, при этом код остается пристойным, легко читаемым и полностью контроллируемым компилятором.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
Здравствуйте, VladD2, Вы писали: VD>Мне кажется идея сформулирована весьма конкретно — создать аналог Руби на Рельсах, но на статически типизированном языке поддерживающем макросы.
Ребята утверждают иное. Ну да ладно — поспорить в хорошей компании конечно прикольно
но поддерживать беседу не смогу, ибо завтра отбываю "на кокосовые острова" и комп с собой не беру.
VD>Ты просто плохо разбираешься в возможностях линка. И вместо того чтобы углубиться в этот вопрос вносишь поспешные суждения.
Влад, я можно сказать в линке вообще не разбираюсь. Ну не писал я под винды, линк просто из любопытства глянул.
И обсуждался не линк и AR конкретно, а места в которых рельсы используют динамическое расширение классов.
И мой вопрос был — "чем вы это собираетесь заменять?"
В качестве примера мог быть взят не класс модели, а класс вьюхи (там в рельсах подобные фишки тоже есть).
Просто не классах вьюхи менее удобно демонстрировать, они скрыты.
VD>Ну, а в статьях по Грувийным Рельсам все соответствует действительности?
Грувийные рельсы идеологически гораздо более тривиальны. А последнее время, и практически имеют не много смысла.
Года полтора назад они имели ключевое свойство — работать под java-машиной и цеплять java-вский легаси код.
Хотя тормозили раза в 4-5 сильнее чем обычные рельсы.
Но примерно к осени прошлого года проект jRuby дорос то того, что под ним запустились рельсы.
И выяснилось, что с определенного уровня нагрузки его эффективность сравнивается с эффективностью обычного RoR (а потом, хоть и медленно, начинает опережать). Жавовский код из под jRuby тоже доступен. Так что Грувийные Рельсы меня последнее время мало интересуют.
VD>Как я понял, ты считаешь, что знаешь эти моменты.
Не все. Далеко не все.
Просто я в коде достаточно часто натыкаюсь на разные забаные моменты, которые все больше и больше складываются в некую систему.
Сейчас я пишу примерно 20-й крупный проект на рельсах (мелочь не считаю). И как-то я сел сравнить код своих первых проектов с нынешним... если первые были похожи на то, что показывают во всяких учебных роликах, то сейчас... как будто пишу на совсем другом фреймворке.
Но полностью я это пока не осмыслил...
Так сходу несколько отличий нынешних проектов от первых:
— Про разрекламированные scaffold я забыл где-то уже к третьему проекту. Они хорошо смотрятся в демонстрационных примерах, но в реальной работе либо их надо полностью переписывать, либо просто забыть.
— Не помню с какого именно проекта, но достаточно давно, мы перешли со стандартного шаблонизатора на Builder::XmlMarkup. А в helpers оказались не заготовки кусков html кода, а классы объектной модели интерфейса (опирающиеся на класс Builder).
— При работе с базой, практически все варианты запросов инкапсулированы в классах модели через named_scope. Весьма красивый механизм, но слабо документированный.
— Вынос валидации на уровень модели (который порой критикуют в форумах) оказался особо ценным при нескольких входах на модель (например, через интерфейс и через веб-сервисы). В результате на валидаторы у нас повешены не только тривиальные проверки заполнения полей, но и некоторые проверки, обеспечивающие целостность модели на уровне бизнес-логики.
Ну, это сходу, реально там отличий гораздо больше...
VD>Если ты можешь помочь с идеологии, то вперед! Родина тебе не забудет!
Свою идеологию фреймворка я пока не потяну. По крайней мере такого фреймворка, который понравился бы мне самому.
Ну и потом, хорошие фреймворки рождаются из личных задачь и потребностей. А .Net мне сейчас в работе не актуален.
Актуальны рельсы и Ocsigen. У рельсов и так превосходная команда архитекторов, что же касается Ocsigen-а... то поживем-увидим.
Здравствуйте, AndrewVK, Вы писали: AE>>Если данные запроса все равно перепаковывать в объекты модели, то согласен, AVK>Зачем их туда перепаковывать? ad hoc запросы тем и характерны, что не выставлятся просто наружу, а используются для дальнейшей обработки.
Мы сказали одно и то же разными словами. "обработка" <=> "перепаковка"
Z>Здесь я хочу избавиться от классов viewmodel. Точнее сделать их невидимыми для программиста, их поля будут задаваться прямо в контролле макросами.
Z>
Z>def model = Views.Home.Model();
Z>model.par1 = 10;
Z>model.par2 = "welcome to nemerle on rails world";
Z>
Z>Не уверен пока, что это вообще возможно, но очень хочется.
Я бы наоборот начал с этого пункта. Это то, что уже сейчас реально упростило бы работу над mvc проектами.
И наверное определять структуру модели нужно по инициализации, а не по использованию, для чего инициализацию стоит выделить явно. Это позволило бы более строго контролировать структуру класса модели. Что то вроде ананимных типов, только видимых как минимум в пределах сборки.
Z>def model = new(par1 = 10, par2 = "welcome to nemerle on rails world");
При этом представление будет автоматически параметризовываться "анонимным" типом, который будет выводится по уже существующему соглашению mvc о наименованиях и расположении представлений.
Здравствуйте, Alex EXO, Вы писали:
AE>- Не помню с какого именно проекта, но достаточно давно, мы перешли со стандартного шаблонизатора на Builder::XmlMarkup. А в helpers оказались не заготовки кусков html кода, а классы объектной модели интерфейса (опирающиеся на класс Builder).
Можно по подробнее?
AE>- При работе с базой, практически все варианты запросов инкапсулированы в классах модели через named_scope. Весьма красивый механизм, но слабо документированный.
И об этом тоже по подробнее, если можно.
AE>- Вынос валидации на уровень модели (который порой критикуют в форумах) оказался особо ценным при нескольких входах на модель (например, через интерфейс и через веб-сервисы). В результате на валидаторы у нас повешены не только тривиальные проверки заполнения полей, но и некоторые проверки, обеспечивающие целостность модели на уровне бизнес-логики.
Согласен. А где предлагается делать валидацию в ином случае?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, seregaa, Вы писали:
Z>>Здесь я хочу избавиться от классов viewmodel. Точнее сделать их невидимыми для программиста, их поля будут задаваться прямо в контролле макросами.
S>Я бы наоборот начал с этого пункта. Это то, что уже сейчас реально упростило бы работу над mvc проектами.
Присоединяйся. Я этот пункт в конец поставил в расчете на свои силы. К тому времени я буду лучше понимать возможности nemerle и мне будет проще задизайнить это. Правда для начала нужно все равно нужно уметь строго указывать какая view будет использоваться, это 3й мейлстоун.
S>И наверное определять структуру модели нужно по инициализации, а не по использованию, для чего инициализацию стоит выделить явно.
Возможно. Хотя это, имхо, затруднит использование из нескольких экшенов.
Здравствуйте, VladD2, Вы писали: AE>>- Не помню с какого именно проекта, но достаточно давно, мы перешли со стандартного шаблонизатора на Builder::XmlMarkup. А в helpers оказались не заготовки кусков html кода, а классы объектной модели интерфейса (опирающиеся на класс Builder). VD>Можно по подробнее?
Кусочек из экзампла:
Это базовый класс. То, что от него можно пронаследоваться и добавить всякие menu, calendar, box и т.д. я думаю, поняно.
Builder::XmlMarkup присутствует в базовой поставке рельсов с самых первых версий. И в документации описан. Но вот в учебной литературе не упоминается.
AE>>- При работе с базой, практически все варианты запросов инкапсулированы в классах модели через named_scope. Весьма красивый механизм, но слабо документированный. VD>И об этом тоже по подробнее, если можно.
named_scope добавлен в базовую поставку начиная с версии 2.0, до этого был доступен отдельно. В документации отсутствует, подробно документирован внутри исходника (named_scope.rb), и в сгенеренном по исходникам rubydoc.
Вот цитата из искходника (пардон, что длинная):
# Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
# such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
#
# class Shirt < ActiveRecord::Base
# named_scope :red, :conditions => {:color => 'red'}
# named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
# end
#
# The above calls to <tt>named_scope</tt> define class methods <tt>Shirt.red</tt> and <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>,
# in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
#
# Unlike Shirt.find(...), however, the object returned by <tt>Shirt.red</tt> is not an Array; it resembles the association object
# constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
# <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
# as with the association objects, name scopes acts like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
# <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really were an Array.
#
# These named scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
# Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
# for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
#
# All scopes are available as class methods on the ActiveRecord::Base descendent upon which the scopes were defined. But they are also available to
# <tt>has_many</tt> associations. If,
#
# class Person < ActiveRecord::Base
# has_many :shirts
# end
#
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
# only shirts.
#
# Named scopes can also be procedural.
#
# class Shirt < ActiveRecord::Base
# named_scope :colored, lambda { |color|
# { :conditions => { :color => color } }
# }
# end
#
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
#
# Named scopes can also have extensions, just as with <tt>has_many</tt> declarations:
#
# class Shirt < ActiveRecord::Base
# named_scope :red, :conditions => {:color => 'red'} do
# def dom_id
# 'red_shirts'
# end
# end
# end
#
#
# For testing complex named scopes, you can examine the scoping options using the
# <tt>proxy_options</tt> method on the proxy itself.
#
# class Shirt < ActiveRecord::Base
# named_scope :colored, lambda { |color|
# { :conditions => { :color => color } }
# }
# end
#
# expected_options = { :conditions => { :colored => 'red' } }
# assert_equal expected_options, Shirt.colored('red').proxy_options
VD>Согласен. А где предлагается делать валидацию в ином случае?
Оппоненты утверждают, что в контроллерах...
Здравствуйте, Alex EXO, Вы писали:
AE>>>- Не помню с какого именно проекта, но достаточно давно, мы перешли со стандартного шаблонизатора на Builder::XmlMarkup. А в helpers оказались не заготовки кусков html кода, а классы объектной модели интерфейса (опирающиеся на класс Builder). VD>>Можно по подробнее? AE>Кусочек из экзампла: AE>
AE>Это базовый класс. То, что от него можно пронаследоваться и добавить всякие menu, calendar, box и т.д. я думаю, поняно. AE>Builder::XmlMarkup присутствует в базовой поставке рельсов с самых первых версий. И в документации описан. Но вот в учебной литературе не упоминается.
(spark view engine)
AE>Оппоненты утверждают, что в контроллерах...
Цитатку можно? Я, например, считаю, что в разных случаях по разному. Но точно не в классе данных.
Здравствуйте, Ziaw, Вы писали: Z>А что это даeт? Какие плюсы по сравнению с:
Позволяет думать в терминах осмысленных объектов, а не в терминах html-разметки.
Разумеется, вариант не подходит, если верстку делает отдельный человек, знающий толко html.
Но при разработке приложений (а не сайтов) за интерфейс зачастую отвечают программисты.
AE>>Оппоненты утверждают, что в контроллерах... Z>Цитатку можно?
Цитатку из чего? Из споров в рубевых эхах? Извини, не буду искать...
Z>Я, например, считаю, что в разных случаях по разному. Но точно не в классе данных.
Ну вот. А на мой взгляд, модель определяет "набор допустимых операций над множеством прикладных сущностей", и о логической целостности и непротиворечивости модель должна уметь позаботиться сама. Так что лично я, вполне доволен тем, куда авторы рельсов поместили валидаторы.
Здравствуйте, Alex EXO, Вы писали:
AE>Здравствуйте, Ziaw, Вы писали: Z>>А что это даeт? Какие плюсы по сравнению с: AE>Позволяет думать в терминах осмысленных объектов, а не в терминах html-разметки. AE>Разумеется, вариант не подходит, если верстку делает отдельный человек, знающий толко html. AE>Но при разработке приложений (а не сайтов) за интерфейс зачастую отвечают программисты.
Для jscript тоже придумали тулзу, чтобы думать о тексте на нем в терминах осмысленных объектов?
Здравствуйте, Ziaw, Вы писали: Z>Для jscript тоже придумали тулзу, чтобы думать о тексте на нем в терминах осмысленных объектов?
А то... Если я кидаю на форму объект calendar, то естественно он с инкапсулированным jscript.
Как же иначе... (общий jscript-код в библиотеке, а настроечное замыкание — в контроле)
Здравствуйте, Alex EXO, Вы писали:
AE>Здравствуйте, Ziaw, Вы писали: Z>>Для jscript тоже придумали тулзу, чтобы думать о тексте на нем в терминах осмысленных объектов? AE>А то... Если я кидаю на форму объект calendar, то естественно он с инкапсулированным jscript. AE>Как же иначе... (общий jscript-код в библиотеке, а настроечное замыкание — в контроле)
Ты не понял. Вот мне надо на странице написать свой jscript. Как я это буду делать это в XML?
Здравствуйте, Ziaw, Вы писали: Z>Ты не понял. Вот мне надо на странице написать свой jscript. Как я это буду делать это в XML?
page.jscode("var x= ....")
В чем проблема?
Здравствуйте, Alex EXO, Вы писали:
AE>Здравствуйте, Ziaw, Вы писали: Z>>Ты не понял. Вот мне надо на странице написать свой jscript. Как я это буду делать это в XML? AE>page.jscode("var x= ....") AE>В чем проблема?
В том, что это не строка, а код. В котором хотелось бы иметь форматирование, хайлайтинг и автокомплишен. Который можно было бы дебажить.
Здравствуйте, Alex EXO, Вы писали:
AE>Здравствуйте, Ziaw, Вы писали: Z>>Ты не понял. Вот мне надо на странице написать свой jscript. Как я это буду делать это в XML? AE>page.jscode("var x= ....") AE>В чем проблема?
Кстати, если я сделаю 5 таких однострочных вызовов, они все будут в тегах <script>?
Здравствуйте, Ziaw, Вы писали: Z>В том, что это не строка, а код. В котором хотелось бы иметь форматирование, хайлайтинг и автокомплишен. Который можно было бы дебажить.
Большой код я бы вынес в *.js файл. И удобнее, и кешироваться будет.
Но если уж упереться, то в Ruby есть форматированная запись строки (и даже несколько форматов).
И вставлять этот текст через
def jscode(js)
self.script(:type=>"text/javascript") { self<<"\n//<![CDATA[\n"; self<<js; self<<"\n//]]>\n" }
end
Но вообще-то разговор какой-то странныq. Я так и не могу понять, что ты хочешь...
Писать на RoR? Тогда начинать стоит с документации...
Делать аналог Builder::XmlMarkup на Немерле? Тогда какая разница как у нас реализован тот или иной хелпер?
(И все равно начинать нужно будет с документации.)
Здравствуйте, Alex EXO, Вы писали:
AE>Но вообще-то разговор какой-то странныq. Я так и не могу понять, что ты хочешь... AE>Писать на RoR? Тогда начинать стоит с документации... AE>Делать аналог Builder::XmlMarkup на Немерле? Тогда какая разница как у нас реализован тот или иной хелпер? AE>(И все равно начинать нужно будет с документации.)
Для начала я хочу понять, зачем вообще нужен XmlMarkup. Пока не понял.
Здравствуйте, Ziaw, Вы писали: Z>Для начала я хочу понять, зачем вообще нужен XmlMarkup. Пока не понял.
А мне, честно говоря, не понятно зачем доказывать его нужность. Что мне с того? Омар на блюде или икру на хлеб?
Изначально, Влад спросил к чему мы пришли за время использования рельсов — я ответил.
Здравствуйте, Alex EXO, Вы писали:
AE>Здравствуйте, Ziaw, Вы писали: Z>>Для начала я хочу понять, зачем вообще нужен XmlMarkup. Пока не понял. AE>А мне, честно говоря, не понятно зачем доказывать его нужность. Что мне с того? Омар на блюде или икру на хлеб? AE>Изначально, Влад спросил к чему мы пришли за время использования рельсов — я ответил.
А мне было интересно — почему. Чем писать код который пишет html удобнее чем писать html. Но, похоже, ответа я не услышу.