Как разбить длинные строки Ruby
Я всегда получаю большие строки кода в верхней части моего Rails models
. Я ищу предложения для лучшего способа разбить их со стандартным рубиновым стилем. Например, одна строка, на которую я сейчас смотрю, такова:
delegate :occupation, :location, :picture_url, :homepage_url, :headline, :full_name, :to => :profile, :prefix => true, :allow_nil => true
Каков обычный стиль для разбивки этих длинных линий вызова метода?
Ответы
Ответ 1
Что-то по строкам:
delegate :occupation, :location, :picture_url,
:homepage_url, :headline, :full_name,
:to => :profile, :prefix => true, :allow_nil => true
Или, если вам нравится выделить хэш хэша (разумная вещь):
delegate :occupation, :location, :picture_url,
:homepage_url, :headline, :full_name,
:to => :profile, :prefix => true, :allow_nil => true
Идея оставить это все на одной линии поражает меня как безумную идею, это означает, что вам нужно прокрутить произвольную сумму, чтобы увидеть, что было делегировано. Ео.
Я бы, наверное, тоже немного подстроил, возможно, алфавит.
delegate :full_name, :headline, :homepage_url,
:location, :occupation, :picture_url,
:to => :profile, :prefix => true, :allow_nil => true
Если у файла не было много/какого-либо другого существенного контента, я мог бы поместить каждый символ метода в его собственную строку, просто чтобы упростить редактирование. В более крупном файле я бы не хотел занимать место для этого.
Не то, чтобы я когда-либо думал об этом.
Изменить Я думаю, что:/
В эти дни я мог бы сгруппировать делегированные методы с помощью "подобия", примерно:
delegate :full_name, :headline,
:location, :occupation,
:homepage_url, picture_url,
to: :profile, prefix: true, allow_nil: true
Мое жюри висело на синтаксисе хэша 1.9, когда значение также является символом; Я думаю, это выглядит забавно. Я также не уверен, где бы я отступил от него, но мог бы потерять его во время переформатирования IDE, но мне нравится, как он выглядит выше, если я использую новый синтаксис.
Ответ 2
Короткий ответ зависит от.
Основы
Для начала вы можете сохранить несколько символов, используя "новый" синтаксис хэша Ruby:
result = very_long_method_name(something: 1, user: user, flange_factor: 1.34)
против.
result = very_long_method_name(:something => 1, :user => user, :flange_factor => 1.34)
Hash/Массивы
Иногда вам нужно инициализировать массив или хэш, особенно для хэшей, которые приятно писать их следующим образом:
args = {
first_name: "Aldo",
email: "[email protected]",
age: Float::INFINITY
}
Тот же хеш в той же строке будет (не так хорошо):
args = {first_name: "Aldo", email: "[email protected]", age: Float::INFINITY}
Различные вызовы методов
Некоторые методы требуют много параметров или эти параметры имеют длинные имена:
%table
%thead
%th
%td= t("first_name", scope: "activemodel.lazy_model.not_so_active_model", some_interpolation_argument: "Mr.", suffix: "(Jr.)")
В этом случае я, вероятно, напишу так:
%table
%thead
%th
%td= t("first_name",
scope: "activemodel.lazy_model.not_so_active_model",
some_interpolation_argument: "Mr.",
suffix: "(Jr.)")
Это все еще не очень красиво, но я думаю, что это менее уродливо.
class person < ActiveRecord::Base
validates :n_cars, numericality: {
only_integer: true,
greater_than: 2,
odd: true,
message: t("greater_than_2_and_odd",
scope: "activerecord.errors.messages")
}
end
Опять же, не самый красивый код на земле, но у него есть какая-то структура.
Кроме того, иногда вы можете использовать переменные для разделения строк. Это всего лишь пример, но в основном вы называете блоки вещей (а иногда и после этого вы понимаете, что можете фактически переместить этот блок в методе)
class person < ActiveRecord::Base
NUMERICALITY_OPTS = {
only_integer: true,
greater_than: 2,
odd: true,
message: t("greater_than_2_and_odd", scope: "activerecord.errors.messages")
}
validates :n_cars, numericality: NUMERICALITY_OPTS
end
Блоки
Говоря о блоках (замыканиях):
User.all.map { |user| user.method_name }
можно записать следующим образом:
User.all.map(&:method_name)
Если у вас есть правильные блоки, попробуйте использовать do-end вместо фигурных скобок:
nicotine_level = User.all.map do |user|
user.smoker? ? (user.age * 12.34) : 0.1234
end
Условный
Не используйте тернарный оператор if для сложных вещей:
nicotine_level = user.smoker? ? (user.age * 1.234 + user.other_method) : ((user.age - 123 + user.flange_factor) * 0)
if user.smoker?
nicotine_level = user.age * 1.234 + user.other_method
else
nicotine_level = (user.age - 123 + user.flange_factor) * 0
end
Если у вас есть сложные операторы if:
if user.vegetarian? && !user.smoker? && (user.age < 25) && (user.n_girlfriends == 0) && (user.first_name =~ /(A|Z)[0-1]+/)
end
Скорее всего, лучше переместить вещи в методах и сделать вещи не только более короткими, но и читаемыми:
if user.healthy? && user.has_a_weird_name?
# Do something
end
# in User
def healthy?
vegetarian? && !smoker? && (age < 25) && (n_girlfriends == 0)
end
def user.has_a_weird_name?
user.first_name =~ /(A|Z)[0-1]+/
end
Длинные строки
Heredoc - ваш друг... Мне всегда нужно google, чтобы получить синтаксис правильно, но как только вы это исправите, читатели читают следующее:
execute <<-SQL
UPDATE people
SET smoker = 0
OK, this is a very bad example.
SQL
Запросы
Я делаю это для простых случаев:
# Totally random example, it just to give you an idea
def cars_older_than_n_days(days)
Car.select("cars.*, DATEDIFF(NOW(), release_date) AS age")
.joins(:brand)
.where(brand: {country: "German"})
.having("age > ?", days)
end
Иногда запросы даже хуже. Если я использую squeel, и запрос очень большой, я обычно использую круглые скобки следующим образом:
# Again, non-sense query
Person.where {
first_name = "Aldo" |
last_name = "McFlange" |
(
age = "18" &
first_name = "Mike" &
email =~ "%@hotmail.co.uk"
) |
(
person.n_girlfriends > 1 &
(
country = "Italy" |
salary > 1_234_567 |
very_beautiful = true |
(
whatever > 123 &
you_get_the_idea = true
)
)
)
}
Я бы сказал, если возможно, попытайтесь избежать сложных запросов и разделите их на меньшие области или что-то еще:
scope :healthy_users, lambda {
younger_than(25).
without_car.
non_smoking.
no_girlfriend
}
scope :younger_than, lambda { |age|
where("users.age < ?", age)
}
scope :without_car, lambda {
where(car_id: nil)
}
scope :non_smoking, lambda {
where(smoker: false)
}
scope :no_girlfriend, lambda {
where(n_girlfriends: 0)
}
Это, вероятно, лучший способ.
Реальность
К сожалению, люди склонны писать длинные строки, и это плохо:
- Длинные строки трудно читать (есть причина, если печатные книги не имеют сверхбольших страниц)
- Это правда, что мы в основном используем 2 экрана, но при использовании таких вещей, как
git diff
из консоли, имеющей длинные строки, больно.
- Иногда вы работаете на своем 13-дюймовом ноутбуке с меньшим имуществом экрана.
- Даже если мне нравится работать с 2 экранами, мне нравится разделять мой редактор, чтобы редактировать 2 файла одновременно - длинные строки заставляют меня использовать горизонтальную полосу прокрутки (самая ненавистная вещь на земле).
- Да, вы можете включить перенос слов в свой редактор, но он все же не так хорош (IMHO)
У меня есть линейка в моем редакторе, поэтому я знаю, когда я собираюсь пересечь 80-й char на линии.
Но редко пересекайте линию несколькими символами, это действительно лучше, чем расколоть ее.
Заключение
Существует несколько способов удержать линии под 80-ыми и часто зависит от ситуации.
Проблема с длинными строками - это не просто плохой стиль, длинные строки часто являются симптомом слишком большой сложности.
Ответ 3
Хотя у вопроса уже есть два отличных ответа, я хотел бы направить будущих читателей в Руководство по стилю Ruby для таких вопросов.
В настоящее время в разделе Source Code Layout содержится много информации о том, как разбить строки в различных ситуациях:
# starting point (line is too long)
def send_mail(source)
Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text)
end
# bad (double indent)
def send_mail(source)
Mailer.deliver(
to: '[email protected]',
from: '[email protected]',
subject: 'Important message',
body: source.text)
end
# good
def send_mail(source)
Mailer.deliver(to: '[email protected]',
from: '[email protected]',
subject: 'Important message',
body: source.text)
end
# good (normal indent)
def send_mail(source)
Mailer.deliver(
to: '[email protected]',
from: '[email protected]',
subject: 'Important message',
body: source.text
)
end
# bad - need to consult first line to understand second line
one.two.three.
four
# good - it immediately clear what going on the second line
one.two.three
.four
И это часто оказывается "решением" для слишком сложного кода, поскольку @Aldo уже упомянул:
# bad
some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
# good
if some_condition
nested_condition ? nested_something : nested_something_else
else
something_else
end
Ответ 4
По моему опыту, похоже, что соглашение фактически не разбивает линии. Большинство проектов, которые я видел, включая кодовую базу рельсов, похоже, не имеют проблем с действительно длинными непрерывными линиями.
Итак, я бы сказал, если вы хотите следовать соглашению, не разрывайте линии. Если вы решите разбить строки, то для этого не существует широко принятого соглашения. Вы можете использовать любой стиль кодирования, который вы предпочитаете.