我总是在我的Rails models
顶部获得大量代码。我正在寻找建议,以最好的方式打破标准的Ruby风格。例如,我现在看的一行是:
delegate :occupation, :location, :picture_url, :homepage_url, :headline, :full_name, :to => :profile, :prefix => true, :allow_nil => true
分解这些长方法调用线的传统方式是什么?
答案 0 :(得分:64)
简短的回答是取决于。
首先,您可以使用“新”Ruby哈希语法保存少量字符:
result = very_long_method_name(something: 1, user: user, flange_factor: 1.34)
VS
result = very_long_method_name(:something => 1, :user => user, :flange_factor => 1.34)
有时你需要初始化一个数组或散列,特别是对于散列,用这种方式编写它们很好:
args = {
first_name: "Aldo",
email: "nospam@mail.example.com",
age: Float::INFINITY
}
同一行上的相同哈希值(不太好):
args = {first_name: "Aldo", email: "nospam@mail.example.com", 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是你的朋友...我总是需要谷歌才能正确理解语法,但一旦你做对了就会让某些事情变得更好:
execute <<-SQL
UPDATE people
SET smoker = 0
OK, this is a very bad example.
SQL
对于简单的情况,我倾向于这样做:
# Totally random example, it's 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)
}
这可能是最好的方式。
不幸的是,人们倾向于写长行,这很糟糕:
git diff
之类的东西很长时候很痛苦我的编辑器中有一个标尺,以便我知道何时要越过第80个字符串。 但很少通过几个字符越过它,它实际上比分裂更好。
有几种方法可以在80年代保持线路,并且通常取决于具体情况。 长线的问题不仅仅是糟糕的风格,长线往往是复杂性过高的症状。
答案 1 :(得分:18)
有些事情:
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
将所有这些全部放在一行上的想法让我觉得这是一个讽刺性的想法,这意味着你必须滚动任意数量才能看到被委派的内容。 EW。
我可能也会对某些内容进行排序,也许是按字母顺序排列。
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 :(得分:7)
虽然这个问题已经有两个很好的答案,但我想将未来的读者引用到Ruby Style Guide来解决这些问题。
目前Source Code Layout部分提供了大量有关如何在各种情况下断行的信息:
# starting point (line is too long)
def send_mail(source)
Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
end
# bad (double indent)
def send_mail(source)
Mailer.deliver(
to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text)
end
# good
def send_mail(source)
Mailer.deliver(to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text)
end
# good (normal indent)
def send_mail(source)
Mailer.deliver(
to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text
)
end
# bad - need to consult first line to understand second line
one.two.three.
four
# good - it's immediately clear what's 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
答案 3 :(得分:1)
根据我的经验,似乎惯例实际上并没有打破界限。我见过的大多数项目,包括rails本身的代码库,似乎没有任何问题,因为它们有很长的不间断线。
所以我想说如果你想遵循惯例,不要分手。如果你决心打破界限,那么就没有广泛遵循的惯例如何去做。您可以使用您喜欢的任何编码风格。