web-dev-qa-db-ja.com

Rubyの長い行を分割する方法

Rails modelsの上部に、常に大きな大きなコード行が表示されます。標準Rubyスタイルでそれらを分割する最良の方法の提案を探しています。たとえば、私が今見ている1行は次のとおりです。

delegate :occupation, :location, :picture_url, :homepage_url, :headline, :full_name, :to => :profile, :prefix => true, :allow_nil => true

これらの長いメソッド呼び出し行を分割するための従来のスタイルは何ですか?

37
Matthew

以下の線に沿ったもの:

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

それをすべて1行に残すという考えは、ひどい考えだと思います。つまり、委任されているものを見るには、任意の量をスクロールする必要があるということです。えー.

私もおそらく少し並べて、多分アルファベット順にします。

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

私のsymbol審員は、値がシンボルでもある場合、1.9ハッシュ構文に依存しています。面白そうだと思います。インデントする場所もわかりません。IDE再フォーマット中に失われる可能性がありますが、新しい構文を使用している場合は上記のように見えます。

18
Dave Newton

短い答えはitに依存します

基礎

はじめに、 "new" 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: "[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

条件付き

複雑なものに三項演算子を使用しないでください。

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

長い弦

ヒアドックはあなたの友人です...構文を正しくするためには常にグーグルで検索する必要がありますが、一度正しいと特定の事が読みやすくなります:

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)
}

これがおそらく最良の方法でしょう。

現実

残念ながら、人々は長い行を書く傾向があり、それは悪いです:

  • 長い行は読みにくい(印刷された本に超大きなページがない場合は理由がある)
  • ほとんどの場合2つのスクリーンを使用しますが、git diff長い行を持つコンソールからの痛み
  • 場合によっては、13インチのラップトップで作業し、画面のスペースを節約します
  • 2つの画面で作業するのが好きな場合でも、エディターを分割して2つのファイルを同時に編集するのが好きです-長い行により、水平スクロールバー(地球上で最も嫌われているもの)
  • はい、エディターでWordの折り返しを有効にできますが、それでもナイス(IMHO)ではありません

エディターにルーラーがあるので、行の80番目の文字を越えようとしていることがわかります。しかし、いくつかの文字で行を越えることはめったにありません。実際には分割するよりも優れています。

結論

80年代にラインを維持する方法はいくつかあり、状況によって異なります。長い行の問題は、スタイルが悪いだけではありません。長い行は、多くの場合、複雑すぎます

質問にはすでに2つの素晴らしい答えがありますが、将来の読者にはこのような問題について Ruby Style Guide を参照したいと思います。

現在、 ソースコードレイアウト セクションには、さまざまな状況で行を分割する方法に関する多くの情報があります。

# 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'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
7
jibiel

私の経験から、慣習は実際に行を分割することではないようです。 Rails自体のコードベースを含む、私が見たほとんどのプロジェクトは、途切れのない長い行を持つことに何の問題もないようです。

ですから、慣習に従うことを望むなら、行を分割しないでください。行を分割することに決めた場合、その方法について広く採用されている規則はありません。好みのコーディングスタイルを使用できます。

1
alexsanford1