CoffeeScriptRails遗留程序中最常犯的不当(下)

本文由 伯乐在线
algo31031
翻译。未经许可,禁止转发!
英文出处:EZEQUIEL
DELPERO
。欢迎出席翻译组

在模型的实例方法里,本来不供给的时候利用了“self.”

不好的

; html-script: false ] # app/models/user.rb class User <
ActiveRecord::Base def full_name "#{self.first_name}
#{self.last_name}" end end

1
2
3
4
5
6
7
8
9
; html-script: false ]
# app/models/user.rb
class User &lt; ActiveRecord::Base
 
  def full_name
    &quot;#{self.first_name} #{self.last_name}&quot;
  end
 
end

那段代码并不复杂不过中间并不须要使用“self.”。把“self.”去掉会使代码更简明且不影响可用性。

好的

在模型里,只有在实例方法里要求赋值时,才会用到“self.”,不然通篇的“self.”只会徒增代码复杂度。

; html-script: false ] # app/models/user.rb class User <
ActiveRecord::Base def full_name "#{first_name}
#{last_name}" end end

1
2
3
4
5
6
7
8
9
; html-script: false ]
# app/models/user.rb
class User &lt; ActiveRecord::Base
 
  def full_name
    &quot;#{first_name} #{last_name}&quot;
  end
 
end

使用原则表明式并且重临了标准

不好的

; html-script: false ] # app/models/user.rb class User <
ActiveRecord::Base def full_name if name name else "No name"
end end end

1
2
3
4
5
6
7
8
9
10
11
12
13
; html-script: false ]
# app/models/user.rb
class User &lt; ActiveRecord::Base
 
  def full_name
    if name
      name
    else
      &quot;No name&quot;
    end
  end
 
end

或者

; html-script: false ] # app/models/user.rb class User <
ActiveRecord::Base def full_name name ? name : "No name" end
end

1
2
3
4
5
6
7
8
9
; html-script: false ]
# app/models/user.rb
class User &lt; ActiveRecord::Base
 
  def full_name
    name ? name : &quot;No name&quot;
  end
 
end

那段代码的标题在于:在不需求的地方增添了决定语句。

好的

有种更方便的处理格局也能达到规定的标准平等效果

; html-script: false ] # app/models/user.rb class User <
ActiveRecord::Base def full_name name || "No name" end end

1
2
3
4
5
6
7
8
9
; html-script: false ]
# app/models/user.rb
class User &lt; ActiveRecord::Base
 
  def full_name
    name || &quot;No name&quot;
  end
 
end

轻巧的话那段代码会在name不为false或nil时将其回到,不然重回”No name”.

动用相当的话,||和&&这么些操作符会对升官你的代码质量提供巨大助力。

在视图层使用行内样式

不好的

; html-script: false ] <!– app/views/projects/show.html.erb –>
… <h3
style="font-size:20px;letter-spacing:normal;color:#95d60a;line-height:100%;margin:0;font-family:'Proxima
Nova';"> SECRET PROJECT </h3> …

1
2
3
4
5
6
7
; html-script: false ]
&lt;!– app/views/projects/show.html.erb –&gt;
&lt;h3 style=&quot;font-size:20px;letter-spacing:normal;color:#95d60a;line-height:100%;margin:0;font-family:&#039;Proxima Nova&#039;;&quot;&gt;
  SECRET PROJECT
&lt;/h3&gt;

那里大家只列出一个标签,全体的体裁都写在了标签里。今后,请思虑一下,假若持有的竹签都吸收行内样式。那会把您的HTML变得和其难度,除外,每当你须求引入另二个均等的h三成分时,将不得不把同样代码照搬一边,形成冗余。

好的

; html-script: false ] // app/assets/stylesheets/application.css
.project-title { font-size: 20px; letter-spacing: normal; color:
#95d60a; line-height: 100%; margin: 0; font-family:'Proxima
Nova'; }

1
2
3
4
5
6
7
8
9
10
; html-script: false ]
// app/assets/stylesheets/application.css
.project-title {
    font-size: 20px;
    letter-spacing: normal;
    color: #95d60a;
    line-height: 100%;
    margin: 0;
    font-family:&#039;Proxima Nova&#039;;
}

; html-script: false ] <!– app/views/projects/show.html.erb –>
… <h3 class="project-title"> SECRET PROJECT
</h3> …

1
2
3
4
5
6
7
; html-script: false ]
&lt;!– app/views/projects/show.html.erb –&gt;
&lt;h3 class=&quot;project-title&quot;&gt;
  SECRET PROJECT
&lt;/h3&gt;

明天笔者么能够复用样式了,并且HTML的可读性也有所升高。

注意:那只是个简易的范例,实际运用时你应当把CSS拆分成多个小文件,并因而application.css来加载这一个文件。别的唯有在email模板里,才会用到行内样式。

在视图层使用JavaScript

不好的

; html-script: false ] <!– app/views/questions/show.html.erb –>
… <textarea rows="4" cols="50"
class='wysihtml5'> Insert your question details here.
</textarea> … <script> $(document).ready(function(){
$('textarea.wysihtml5').wysihtml5({ "font-styles":
true, //Font styling, e.g. h1, h2, etc. Default true.
"emphasis": true, //Italics, bold, etc. Default true.
"lists": true, //(Un)ordered lists, e.g. Bullets, Numbers.
Default true. "html": false, //Button which allows you to edit
the generated HTML. Default false. "link": true, //Button to
insert a link. Default true. "image": true, //Button to insert
an image. Default true. "color": true //Button to change color
of font. Default true. }); }); <script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
; html-script: false ]
&lt;!– app/views/questions/show.html.erb –&gt;
&lt;textarea rows=&quot;4&quot; cols=&quot;50&quot; class=&#039;wysihtml5&#039;&gt;
  Insert your question details here.
&lt;/textarea&gt;
&lt;script&gt;
  $(document).ready(function(){
  $(&#039;textarea.wysihtml5&#039;).wysihtml5({
    &quot;font-styles&quot;: true, //Font styling, e.g. h1, h2, etc. Default true.
    &quot;emphasis&quot;: true, //Italics, bold, etc. Default true.
    &quot;lists&quot;: true, //(Un)ordered lists, e.g. Bullets, Numbers. Default true.
    &quot;html&quot;: false, //Button which allows you to edit the generated HTML. Default false.
    &quot;link&quot;: true, //Button to insert a link. Default true.
    &quot;image&quot;: true, //Button to insert an image. Default true.
    &quot;color&quot;: true //Button to change color of font. Default true.
  });
});
&lt;script&gt;

那里的逻辑和特定页面耦合在协同,导致代码不可复用。

好的

Rails里面有尤其用于集体和存放javascript代码的地点:“app/assets/javascripts/”。

; html-script: false ] // app/assets/javascripts/application.js …
$(document).ready(function(){
$('textarea.wysihtml5').wysihtml5({ "font-styles":
true, //Font styling, e.g. h1, h2, etc. Default true.
"emphasis": true, //Italics, bold, etc. Default true.
"lists": true, //(Un)ordered lists, e.g. Bullets, Numbers.
Default true. "html": false, //Button which allows you to edit
the generated HTML. Default false. "link": true, //Button to
insert a link. Default true. "image": true, //Button to insert
an image. Default true. "color": true //Button to change color
of font. Default true. }); }); …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; html-script: false ]
// app/assets/javascripts/application.js
$(document).ready(function(){
  $(&#039;textarea.wysihtml5&#039;).wysihtml5({
    &quot;font-styles&quot;: true, //Font styling, e.g. h1, h2, etc. Default true.
    &quot;emphasis&quot;: true, //Italics, bold, etc. Default true.
    &quot;lists&quot;: true, //(Un)ordered lists, e.g. Bullets, Numbers. Default true.
    &quot;html&quot;: false, //Button which allows you to edit the generated HTML. Default false.
    &quot;link&quot;: true, //Button to insert a link. Default true.
    &quot;image&quot;: true, //Button to insert an image. Default true.
    &quot;color&quot;: true //Button to change color of font. Default true.
  });
});

; html-script: false ] <!– app/views/questions/show.html.erb –>
… <textarea rows="4" cols="50"
class='wysihtml5'> Insert your question details here.
</textarea> …

1
2
3
4
5
6
7
; html-script: false ]
&lt;!– app/views/questions/show.html.erb –&gt;
&lt;textarea rows=&quot;4&quot; cols=&quot;50&quot; class=&#039;wysihtml5&#039;&gt;
  Insert your question details here.
&lt;/textarea&gt;

今后我们得以在view层任哪个地方方用那段代码了。只必要页面上有叁个含有wysihtml伍以此class的textarea,刚才的那段js就会被施行。

注意:那只是个大约的范例,实际使用时供给怀恋是否必要把您的JavaScript拆分成若干小的文书,并透过application.js来加载这一个文件。其余,假使您使用的是CoffeeScript而非JavaScript,请坚定不移不要把CoffeeScript与日常JavaScript在一道混写。

调用方法时把另五个措施的调用作为参数

不好的

; html-script: false ] # app/services/find_or_create_topic.rb class
FindOrCreateTopic … def self.perform(user, name) find(user,
sluggify(name)) || create(user, name) end … end

1
2
3
4
5
6
7
8
9
; html-script: false ]
# app/services/find_or_create_topic.rb
class FindOrCreateTopic
  …
  def self.perform(user, name)
    find(user, sluggify(name)) || create(user, name)
  end
  …
end

那段代码里调用了find方法并传到了一个参数,首参数为user,首个参数则是直接调用了sluggify这几个措施并把name作为参数字传送给sluggify。你可能会有问号,这么写有啥难点啊?作者鲜明完全能够看懂那段代码呀。是的,代码只怕简单通晓,但是每一次到那边您都急需团结做一点脑筋转换,而那多亏小编一向着力想要幸免的。

好的

防止要求脑力调换的三个比较灵通的办法便是:使用有含义的变量名。

; html-script: false ] # app/services/find_or_create_topic.rb class
FindOrCreateTopic … def self.perform(user, name) slug = sluggify(name)
find(user, slug) || create(user, name) end … end

1
2
3
4
5
6
7
8
9
10
; html-script: false ]
# app/services/find_or_create_topic.rb
class FindOrCreateTopic
  …
  def self.perform(user, name)
    slug = sluggify(name)
    find(user, slug) || create(user, name)
  end
  …
end

那般做可防止止脑筋调换。换用含义明确的变量名之后,每当你再调用find方法,就会精通find接受一个user和2个slug做参数。

不曾行使类来隔绝Rake Tasks

不好的

; html-script: false ] #
lib/tasks/recalculate_badges_for_users.rake namespace :users do desc
"Recalculates Badges for Users" task recalculate_badges:
:environment do user_count = User.count User.find_each do |user| puts
"#{index}/#{user_count} Recalculating Badges for:
#{user.email}" if user.questions_with_negative_votes >= 1 ||
user.answers_with_negative_votes >= 1
user.grant_badge('Critic') end user.answers.find_each do
|answer| question = answer.question next unless answer &&
question days = answer.created_at – question.created_at days_count =
(days / 1.day).round if (days_count >= 60) &&
(question.vote_count >= 5) grant_badge('Necromancer')
&& return end end end end end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
; html-script: false ]
# lib/tasks/recalculate_badges_for_users.rake
namespace :users do
 
  desc &quot;Recalculates Badges for Users&quot;
  task recalculate_badges: :environment do
    user_count = User.count
 
    User.find_each do |user|
      puts &quot;#{index}/#{user_count} Recalculating Badges for: #{user.email}&quot;
 
      if user.questions_with_negative_votes &gt;= 1 || user.answers_with_negative_votes &gt;= 1
        user.grant_badge(&#039;Critic&#039;)
      end
 
      user.answers.find_each do |answer|
        question   = answer.question
        next unless answer &amp;&amp; question
 
        days       = answer.created_at – question.created_at
        days_count = (days / 1.day).round
 
        if (days_count &gt;= 60) &amp;&amp; (question.vote_count &gt;= 5)
          grant_badge(&#039;Necromancer&#039;) &amp;&amp; return
        end
      end
    end
  end
 
end

以此rake
task有标题多多。最重大的难题是,那个rake太长了并且不好测试。代码写的初1看也很难驾驭。你不得不相信那一个task的确是为用户重新总结奖章系统的,因为它上面描述就这么写的。

好的

杀鸡取蛋那几个主题素材最佳的措施正是,把业务逻辑挪到八个一定的类里面。所以,让大家新建个类来化解它吗。

; html-script: false ] # app/services/recalculate_badge.rb class
RecalculateBadge attr_reader :user def initialize(user) @user = user
end def grant_citric if grant_citric?
user.grant_badge('Critic') end end def grant_necromancer
user.answers.find_each do |answer| question = answer.question next
unless answer && question if grant_necromancer?(answer,
question) grant_badge('Necromancer') && return end
end end protected def grant_citric?
(user.questions_with_negative_votes >= 1) ||
(user.answers_with_negative_votes >= 1) end def
days_count(answer, question) days = answer.created_at –
question.created_at (days / 1.day).round end def
grant_necromancer?(answer, question) (days_count(answer,question)
>= 60) && (question.vote_count >= 5) end end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
; html-script: false ]
# app/services/recalculate_badge.rb
class RecalculateBadge
  attr_reader :user
 
  def initialize(user)
    @user = user
  end
 
  def grant_citric
    if grant_citric?
      user.grant_badge(&#039;Critic&#039;)
    end
  end
 
  def grant_necromancer
    user.answers.find_each do |answer|
      question = answer.question
      next unless answer &amp;&amp; question
 
      if grant_necromancer?(answer, question)
        grant_badge(&#039;Necromancer&#039;) &amp;&amp; return
      end
    end
  end
 
  protected
 
    def grant_citric?
      (user.questions_with_negative_votes &gt;= 1) ||
      (user.answers_with_negative_votes &gt;= 1)
    end
 
    def days_count(answer, question)
      days = answer.created_at – question.created_at
      (days / 1.day).round
    end
 
    def grant_necromancer?(answer, question)
      (days_count(answer,question) &gt;= 60) &amp;&amp;
      (question.vote_count &gt;= 5)
    end
 
end

; html-script: false ] #
lib/tasks/recalculate_badges_for_users.rake namespace :users do desc
"Recalculates Badges for Users" task recalculate_badges:
:environment do user_count = User.count User.find_each do |user| puts
"#{index}/#{user_count} Recalculating Badges for:
#{user.email}" task = RecalculateBadge.new(user)
task.grant_citric task.grant_necromancer end end end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; html-script: false ]
# lib/tasks/recalculate_badges_for_users.rake
namespace :users do
 
  desc &quot;Recalculates Badges for Users&quot;
  task recalculate_badges: :environment do
    user_count = User.count
 
    User.find_each do |user|
      puts &quot;#{index}/#{user_count} Recalculating Badges for: #{user.email}&quot;
      task = RecalculateBadge.new(user)
      task.grant_citric
      task.grant_necromancer
    end
  end
 
end

如您所见,今后以此rake
task可读性要好的多,而且还能独自测试每一种特许徽章的章程。除此以外我么也得以在有须要时复用这么些类。当然那段代码只是点到即止,诸位能够再做进一步优化。

从未有过遵循Sandi Metz的条条框框

  1. 各样类代码不能超越100行
  2. 种种方法代码不得以当先五行
  3. 主意参数不能超过多少个,hash项也席卷在内
  4. 控制器之能够起首化一个对象。而且视图层只好够采纳1个实例变量,并且只可以够在这几个目的上调用方法(@object.collaborator.value那种是不得以的)。

越多关于Sandi
Metz的平整请移步至thoughtbot,参阅Sandi
Metz’ Rules For
Developers
那篇博文。

赞 2 收藏
评论

有关笔者:algo31031

CoffeeScript 1

(搜狐天涯论坛:@韩冰40三)

个人主页
·
小编的篇章