Это плохой стиль, чтобы скрыть переменные экземпляра в фильтрах?

Я вижу это все время в коде rails:

before filter :get_post, only: [:edit, :update, :destroy]

def edit
  # code .......
end

def update
  # code .......
end

def destroy
  # code .......
end

private
  def get_post
    @post = Post.find(params[:id])
  end

Я понимаю, что он держится от повторения одной и той же строки кода три раза, но нет ли там гораздо более легкого для чтения и лучшего способа выполнить одно и то же, просто рефакторинг кода в частный метод, не скрывая переменную экземпляра и перед фильтром?

private
  def get_post(post_id)
    Post.find(post_id)
  end

Затем вы можете сохранить переменную экземпляра в действии

def edit
  @post = get_post(params[:id])
end

Не имеет смысла концептуально скрывать переменные экземпляра в частных методах. Почему это так распространено в рельсах?

Ответ 1

Вероятно, вы увидите много разных мнений относительно этой конкретной практики. Лично я согласен с вами; Я чувствую, что строгое соблюдение DRY служит лишь путанице, спрятав переменную экземпляра в методе внизу файла. Я даже не уверен, что метод get_post действительно покупает вас сам. Тем не менее, я склонен предпочитать объяснение (если это слово) во всех случаях, и для команды, которая последовательно использует этот трюк в каждом контроллере, может быть не так много (если таковая имеется) путаницы.

Если фильтр инкапсулирует немного сложнее, это может стоить того. Например, популярная библиотека авторизации CanCan Райана Бэйтса вводит макрос класса контроллера с именем load_and_authorize_resource, который устанавливает before_filter таким образом, что ресурсы RESTful автоматически загружаются и авторизуются для доступа к текущему пользователю. Это потенциально может быть больше, чем строка или два за метод, и не будет повторяться дословно каждый раз.

Там популярная среда, которую используют некоторые Rails-разработчики, которая должна указывать before_filter как блок, поэтому вам не нужно повторять себя, но переменная экземпляра находится вверху файла:

before_filter(only: [:edit, :update, :destroy]) do
  @post = Post.find(params[:id])
end

Как упоминалось в комментариях, есть некоторые драгоценные камни, такие как decent_exposure, которые помогают формализовать этот шаблон и сделать его значительно менее "скрытым" "или запутанным (поскольку он явно объявлен и API известен).

Ответ 2

Как заявили другие, это вопрос мнения. Я начал использовать перед фильтрами, чтобы скрыть общие переменные экземпляра между контроллерами. Я по-прежнему делаю это иногда, но я обнаружил, что это запах кода, что контроллер раздувается. Если у вас так много переменных экземпляра, которые вам нужно скрыть в фильтрах, вы делаете слишком много. Мое мнение таково, что действие контроллера должно быть очень маленьким, и если все действия имеют одну общую линию, которая обрабатывает переменную экземпляра, это не проблема.

Мое средство для того, чтобы мои действия с контроллером доходили до уровня, с которым мне было удобно иметь общую переменную экземпляра, заключалось в том, чтобы использовать объект-сервисы/презентаторы для консолидации информации, которая была однажды в нескольких переменных экземпляра, в одну переменную экземпляра для каждого действия контроллера. Я обнаружил, что это стало большим улучшением в удобочитаемости и ремонтопригодности моих контроллеров и представлений.

Ответ 3

Я думаю, что ваше предложение - это улучшение по сравнению с типичным соглашением Rails. Я бы сделал шаг еще дальше, и полностью исключил переменную экземпляра. Это вводит в заблуждение использование переменной экземпляра, поскольку оно обычно не представляет собой общее состояние для нескольких действий с контроллером и часто даже не разделяется между любыми методами (кроме фильтра перед тем, как он присваивает ему).

Соглашение Rails о заполнении переменной экземпляра для использования в представлении - плохая идея, использование которой должно исчезнуть. В чистой реализации MVC ничто из контроллера не будет доступно в представлении. Я думаю, что это намного более эффективная реализация, которая должным образом разделяет слои:

def edit
  post = get_post(params[:post_id])
  render 'edit', locals: { post: post }
end

Отсутствие утечки, отсутствие зависимости от переменной экземпляра в представлении, явное намерение и представление, которое более легко повторное использование и может быть отображено из других представлений, не вставляя присвоение переменной хакерского inline-экземпляра.

Rails имеет ряд соглашений, которые делают для быстрой начальной загрузки, но они не делают для хорошего кода.

Ответ 4

Это соглашение о конфигурации.

Ни один из подходов не ошибается, но люди, которым нравится Ruby, часто предпочитают минималистичные шаблоны кодирования и ценят, используя самый быстрый подход для решения проблемы, чтобы они могли решать более сложные и интересные проблемы.

Если вы видели, что этот подход используется достаточно часто, становится второй натурой, чтобы все функции-члены (очевидно, не функции сбора) предварительно загружали переменную-член для вас, прежде чем вы определяете свой код. Он доходит до того, что указание переменных-членов в функции не создает существенного преимущества в ясности (если есть).