#ruby-on-rails #ruby #controller #erb
Вопрос:
Я хочу написать простой генератор шаблонов erb для анализа сохраненных шаблонов erb из представлений с помощью Generator
модуля. Я вызываю контроллер Generator
из rails, чтобы сгенерировать его одноэлементные экземпляры и передать ему указатель WallController
by self
.
require 'generator'
class WallController < ApplicationController
def index
header = File.read 'app/views/application-header.html'.freeze
@instances = {header: header}
# Load view generators
Generator.generate_instances self
end
end
Первое, что на самом Generator.generate_instances
деле пытается сделать, это скопировать переменные WallController
экземпляра (следовательно, указатель на себя), чтобы выполнить правильный анализ шаблонов erb. Затем он генерирует методы, возвращающие текст, полученный в результате erb.
require 'erb'
module Generator
def self.generate_instances environment
# Mimic class environment
if environment.superclass == ApplicationController
environment.instance_variables.each do |v|
puts "Copy instance variable '#{v}' from #{environment.name} to #{self.name}"
value = environment.instance_variable_get(v)
self.send :instance_variable_set, v, value
end
end
# Parse the ERB templates
templates = @instances
return 0 if !templates.is_a?(Hash) or templates.empty?
templates.keys.each.with_index do |key, index|
define_singleton_method key do
ERB.new(templates.values[index]).result
end
end
end
end
Использование Generator
интерфейса будет выглядеть следующим образом:
<%=== Generator.header %>
Я новичок в rails, но я обнаружил, что включенные файлы контроллера rails ограничены одной статической структурой. Мне не удалось ни перезаписать class Object
, ни class Class
использовать одноэлементные методы, которые могли бы быть полезны.
Однако после выполнения приведенного выше примера переменные экземпляра WallController
возвращают WallController
адрес класса вместо значений, определенных WallController.index
.
undefined method `empty?' for #<WallController:0x000000000a1f90>
Существует ли правильный способ распределения переменных экземпляра контроллера rails между другими контроллерами? Если нет, то почему обычная копия экземпляра не работает?
Если бы мне пришлось писать это на ruby, это было бы легко:
module Y
def self.generate_environment environment
environment.instance_variables.each do |v|
puts "Copy #{v} from #{environment.name} to #{self.name}"
value = environment.instance_variable_get v
self.instance_variable_set(v, value)
end if environment.class == Class
puts "Retrived string: #{@hello}"
end
end
class X
def self.index
@hello = 'Hello, World!'
Y.generate_environment self
end
end
X.index
Комментарии:
1. Вы передаете экземпляр
WallController
вGenerator.generate_instances
asenvironment
, а затем проверяете, является ли этот экземплярClass
сenvironment == Class
. Этоenvironment == Class
всегда приведет к сбою, поэтому вы в конечном итоге не будете копировать какие-либо переменные экземпляра.2. Ой, спасибо тебе. Плохая опечатка. Я исправил это в вопросе сейчас.
3. Вы все еще передаете
self
, что является экземпляром, а затем проверяете , является лиenv.class == Class
это ложным, какself
иWallController
4.
WallController.class == class
, это правда.5. Однако я меняю его
environment.superclass == ApplicationController
на более конкретный.
Ответ №1:
Эта проблема может быть решена с помощью viewcomponent, который позволяет использовать стандартный код ruby для контроллера представления. Также решает проблему разделения кода представления на более мелкие повторно используемые компоненты с разумной скоростью.
Чтобы использовать драгоценный камень viewcomponent, сначала включите его в свой файл Gemfile.
gem 'view_component', require: 'view_component/engine'
После обновления ваших драгоценных bundle install
камней вам также потребуется перезагрузить сервер , если он запущен, чтобы применить новый драгоценный камень.
Затем создание компонента аналогично использованию других генераторов rails. Первый аргумент — это имя компонента, а второй-аргумент компонента.
rails generate component Header site_id
Теперь я сосредоточусь на файлах, созданных в app/component
каталоге, представлении и коде контроллера. Это будет просто контроллер для создания фрагмента заголовка представления.
Внутри app/component/header_component.rb
может быть инкапсулирован весь код, WallController
относящийся к представлению заголовка.
class HeaderComponent < ViewComponent::Base
def initialize(site_id:)
puts "Rendering header component for site: #{site_id}"
# Load site elements
@site = Site.find site_id
@menu_items = []
Site.all.each.with_index do |site, index|
@menu_items.push site.title => site.link
end
end
end
Аналогично, поместите весь erb-код представления заголовка в app/component/header.html.erb
.
Готовый компонент может быть сгенерирован из представления с помощью рендеринга rails:
<%= render HeaderComponent.new(site_id: 1) %>