Нет сомнений в том, что термин «DSL-Oriented-Programming» может быть применен к Rails.
Согласно веб-сайту Lotus , Lotus — это веб-фреймворк, цель которого сделать веб-разработку на Ruby более объектно-ориентированной. В Lotus реализована инкапсуляция слоев MVC, а также отсутствует возможность внесения исправлений.
Мы не будем делать приложение в Lotus, но мы посмотрим на его компоненты, чтобы увидеть, что оно предлагает.
Рельсы магические по причине. Когда мы видим, что Lotus убирает много этой магии, легко скептически относиться. Итак, постарайтесь сохранять непредвзятость.
Как это отличается
На первый взгляд Lotus выглядит как еще один фреймворк «я тоже», который выполняет такие вещи, как Rails. Но, как мы увидим, Lotus также поддерживает менее самоуверенный, более объектно-ориентированный подход к созданию приложений.
Вместо одной платформы Lotus в настоящее время состоит из нескольких драгоценных камней:
Lotus::Router
-
Lotus::Utils
-
Lotus::Validations
-
Lotus::Model
-
Lotus::View
-
Lotus::Controller
-
Lotus::Assets
(официально еще не является частью фреймворка)
Драгоценные камни спроектированы так, чтобы быть максимально независимыми, поэтому, если хотите, вы можете просто использовать Lotus::Router
для маршрутизации и Mongoid или Sequel для моделей.
Имейте в виду, что Lotus находится на ранней стадии разработки, поэтому некоторые из них могут измениться.
Лотос :: маршрутизатор
В Rails легко забыть, что все проходит через Rack под поверхностью. Lotus, с другой стороны, живет всего в паре домов от Стойки.
$ gem install lotus-router --version 0.2.0 $ gem install rack --version 1.6.0
Lotus позволяет размещать разные приложения Rack на каждом маршруте. Таким образом, у вас может быть приложение sinatra, сохраняющее mongoid в ‘/ dashboard’, и приложение для кемпинга, сохраняющееся в PostgreSQL в ‘/ posts’.
Вот очень простое приложение, которое использует Lotus::Router
.
require 'lotus/router' require 'rack' @router = Lotus::Router.new @router.get '/', to: ->(env) { [200, {}, ['Hello from Lotus']] } def app(env) @router.call(env) end Rack::Handler::WEBrick.run method(:app)
env
— это хеш, содержащий информацию о запросе и сервере. Это лингва франка системы промежуточного программного обеспечения Rack.
Если вам нравится, Lotus::Router
предоставляет очень знакомый DSL.
Lotus::Router.new do get '/', to: ->(env) { [200, {}, ['Hello World']] } get '/some-rack-app', to: SomeRackApp.new get '/posts', to: 'posts#index' get '/posts/:id', to: 'posts#show' mount Api::RackApp, at: '/api' namespace 'admin' do get '/users', to: AdminUsers::Index end resources 'users' do member do patch '/award' end collection do get '/search' end end end
Лотос :: Модель
$ gem install lotus-model -- version 0.2.0
То, как лотос обращается с моделями, вероятно, наименее волшебная вещь в этом.
Сейчас ситуация с постоянными данными разбита на:
-
Lotus::Model::Entity
— определяет атрибуты (для объектов, а не таблиц) -
Lotus::Model::Repository
— служит посредником между сущностями и уровнем персистентности (здесь вы вызываете методы CRUD) -
Lotus::Model::Mapper
— хранит сущности отдельно от деталей базы данных -
Lotus::Model::Adapter
— реализует постоянную логику -
Lotus::Model::Adapters::SomeAdapter::Query
— реализация запроса для
адаптер -
Lotus::Model::Adapters::SomeAdapter::Command
— получает запрос в
адаптер -
Lotus::Model::Adapters::SomeAdapter::Collection
— вставка / манипулирование
реализация для адаптера
Итак, около 50 часов Грегг Поллак говорит и указывает. Вот какая система
выглядит на практике:
require 'lotus/model' class Post include Lotus::Entity attributes :title, :content end class PostRepository include Lotus::Repository end Lotus::Model.configure do adapter type: :sql, uri: 'postgres://localhost/database' mapping do collection :posts do entity Post respository PostRepository attribute :id, Integer attribute :title, String attribute :content, String end end end Lotus::Model.load! post = Post.new(title: 'First Post', content: 'This is my first post.') post = PostRepository.create(post) puts post.id # => 1 p = PostRepository.find(post.id) p == post # => true
Очевидный вопрос: как насчет схемы базы данных? Миграции, как они работают?
Прямо сейчас ты сам по себе. Последняя информация о миграциях — это трельо
карта помечена как «Запланированная». В gitter гем Sequel упоминается как способ миграции, хотя вы можете использовать его и для всей модели.
Лотус :: Validations
Если у вас нет, установите драгоценный камень.
$ gem install lotus-validations --version 0.2.2
Проверки Lotus работают подобно ActiveRecord
в том, что класс определяет атрибуты и связанные с ними проверки. В отличие от ActiveRecord
, проверки Lotus отделены от логики постоянства. Это позволяет легко использовать их для более широкого спектра вещей.
require 'lotus/validations' class User include Lotus::Validations attribute :name, format: /\A[a-zA-z]+\z/ end robert = User.new(name: 'Robert') puts robert.valid? # => true invalid = User.new(name: '!Robert') puts invalid.valid? # => false
Обратите внимание, что поскольку библиотека проверок не имеет дело с постоянством, проверки уникальности не реализуются . Согласно философии Lotus, уникальность должна быть обеспечена в самой базе данных ( пример MongoDB ).
Лотос :: Просмотр
Уровень представления Lotus основан на принципе разделения представлений и шаблонов. Представления Lotus являются объектами, поэтому их можно протестировать.
$ gem install lotus-view --version 0.3.0
Вам может быть интересно, что будет делать объект просмотра. Представления Lotus принимают на себя некоторые обязанности, обычно выполняемые контроллерами в Rails, такие как рендеринг и обмен данными.
Вот как выглядит представление Lotus:
require 'lotus/view' module Posts class Index include Lotus::View end class XMLIndex < Index format :xml end end Lotus::View.configure do root 'app/templates' end Lotus::View.load! posts = PostRepository.all # Uses Posts::Index and "posts/index.html.erb" Posts::Index.render(format: :html, posts: posts) # Uses Posts::XMLIndex and "posts/index.xml.erb" Posts::Index.render(format: :xml, posts: posts)
Лотос :: Контроллер
В Lotus контроллер — это не что иное, как простой модуль Ruby, содержащий группу действий.
$ gem install lotus-controller --version 0.3.1
В Rails действие — это method
на контроллере. В Lotus Action
— это
объект .
require "lotus/controller" class Show include Lotus::Action def call(params) self.status = 210 self.body = 'Hello World' self.headers.merge!({ 'X-Some-Custom-Header' => 'TRUDAT'}) end end show = Show.new puts show.call({ some_param: 5}).inspect # => [210, {"X-Some-Custom-Header"=>"TRUDAT", "Content-Type"=>"application/octet-stream; charset=utf-8"}, ["Hello World"]]
Основной метод действия, с которым нужно иметь дело, это #call
который возвращает сериализованный Rack::Response
.
Поскольку действия являются объектами, мы можем использовать внедрение зависимостей для создания общих действий. Как следствие, проще создавать тестируемые действия .
require "lotus/controller" class Show include Lotus::Action def initialize(repository = Post) @repository = repository end def call(params) @whatever = @repository.find params[:id] end end show = Show.new(MemoryPostRepository) show.call({ id: 5 })
Lotus позволяет вам предоставлять обратные вызовы, такие как Rails before_action
. За исключением того, что теперь вы делаете это в действии вместо контроллера. Может показаться, что это побеждает цель, но вам все еще нужны методы многократного использования в ваших действиях на случай, если вы захотите добавить их к другим действиям. Например, может быть, у вас есть метод аутентификации, который вы хотите добавить к действию, просто включив миксин и установив его before
.
require "lotus/controller" require "your_project/mixins/authentication class Show include Lotus::Action include YourProject::Mixins::Authentication before { authenticate! } def call(params) end end
В контроллере Rails действия обмениваются данными с представлениями, устанавливая переменные экземпляра и разделяя контекст. Lotus выполняет это с помощью команды expose
DSL. По умолчанию все действия Lotus предоставляют #params
и #errors
.
require "lotus/controller" class Show include Lotus::Action expose :post def call(params) @post = Post.find params[:id] end end action = Show.new action.call({ id: 5 }) puts action.post.id # => 5 puts action.exposures # => { post: <a Post object> }
В целях безопасности действия Lotus предоставляют белый список разрешенных параметров.
require 'lotus/controller' class Upload include Lotus::Action params do param :title param :content end def call(params) puts params[:title] # => "The Foo in The Bar" puts params[:l33texploit] # => nil end end
Примечание. Вы не выполняете рендеринг в действиях Lotus. Рендеринг происходит в представлениях, как показано в предыдущем разделе. Контроллеры Lotus — это просто конечные точки HTTP для маршрутизатора.
Лотус :: Utils
$ gem install lotus-utils --version 0.3.3
Если вы использовали Ruby on Rails, вы использовали ActiveSupport
. Это модуль, который обеспечивает синтаксический сахар следующим образом:
28.days.ago
В настоящее время Lotus::Utils
не является Lotus-эквивалентом ActiveSupport
Rails. В основном это набор различных улучшений типов данных. Что-то, чтобы отметить, является отсутствием исправления обезьяны .
Вот что вы получаете в данный момент:
-
Lotus::Utils::String
— позволяет вам#underscore
и#demodulize
-
Lotus::Utils::PathPrefix
— подклассыLotus::Utils::String
-
Lotus::Utils::Class
— позволяет загружать классы по имени шаблона из заданного
Пространство имен -
Lotus::Utils::Hash
— оборачиваетHash
и добавляет некоторые методы, такие как
#symbolize!
и#stringify!
, -
Lotus::Utils::Attributes
— устанавливает / получает хэш Lotus, где все ключи
приводится к строкам (например,ActiveSupport::HashWithIndifferentAccess
), выполняет глубокое дублирование с помощью#to_h
-
Lotus::Utils::LoadPaths
— «Коллекция путей загрузки» (неLotus::Utils::LoadPaths
что
это действительно для нас, пожалуйста, оставьте комментарий, если вы знаете) -
Lotus::Utils::Kernel
— содержит методы приведения типов -
Lotus::Utils::ClassAttributes
— «наследуемая переменная уровня класса
аксессоры» -
Lotus::Utils::Callback
— хранит и запускаетprocs
-
Lotus::Utils::Deprecation
— используется для вывода сообщений об устаревании
Эти различные компоненты в геме lotus-utils
потребуются отдельно (за исключением пары ситуаций, когда одно зависит от другого).
require 'lotus/utils/string' puts Lotus::Utils::String.new('MyClass').underscore # => 'my_class' puts Lotus::Utils::String.new('Lotus::Utils::Hash').demodulize # => 'Hash' puts Lotus::Utils::String.new('my_class').classify # => 'MyClass'
Обратите внимание, что это не похоже на цель заменить ActiveSupport
.
Вот фрагмент разговора о Gitter, где кто-то спрашивает сопровождающего о добавлении ActiveSupport
стиле ActiveSupport
:
Так что, если вы надеетесь иметь ActiveSupport
который вы можете загружать по частям вместо автозагрузки патчей для обезьян, похоже, вам придется подождать.
Лотус :: Активы
lotus-assets
пока не являются официальными, но вышла версия 0.0.0. Прямо сейчас в дорожной карте trello перечислены «Помощники по ресурсам » и « Прекомпиляторы разработки (CoffeeScript и SASS)», как в
развитие.
Вывод
Lotus, безусловно, делает некоторые интересные моменты. Вероятно, у многих из вас не было «никаких обезьяньих заплаток». С другой стороны, магия в Rails — это то, что приводит мальчиков во двор. Действительно ли это должно быть исправлено пуристами? Вам решать. По крайней мере, если недостаток тестируемости или повторного использования в Rails расстраивает вас, это проект, над которым вы хотите работать.
Лука Гуиди, создатель Lotus, выпустил этот пост в начале года, чтобы обсудить план Lotus. Это определенно стоит прочитать, так что проверьте это.