Можно сделать 30 контролеров, в каждом будет 2 метода (show, calc) и две страницы.
Ни в коем случае так не делать!
Можно хранить контент вопросов в виде шаблона
Liquid (рендеринг на стороне сервера) или шаблона с рендерингом на стороне браузера (Riot.js, Vue.js, React.js, ...):
В базе данных хранить записи модели QuizQuestion:
- question (string)
- annotation (string)
- html_template (string)
- calc_params (json?)
Общий для всех вопросов шаблон (erb) - partial
<h1 class="header"><%= quiz_question.question %></h1>
<span class="annotation"><%= quiz_question.annotation %><span>
<div class="content"><%= raw content %></div>
<button>Показать/рассчитать</button>
<% @quiz_questions.find_each do |quiz_question| %>
<%
content = Liquid::Template.parse(quiz_question.html_template)
# content.render( 'name' => 'vasya', 'age' => 25 )
%>
<%= render partial: 'quiz_template', locals: {quiz_question: quiz_question, content: content} %>
<% end %>
Вместо raw безопаснее таки использовать
sanitize, указав белый список разрешенных тегов.
В контроллере при показе вопроса компилировать шаблон на основе имеющихся данных (calc_params) в целевой HTML.
Как-то так.
Для отображения на 1-й странице 30 вопросов использовать
def index
@quiz_questions = QuizQuestion.limit(30).offset(0)
end