Open Source Project Studying

I study the open source project gitlab. It’s a well coding rails structure. Here are some examples i think they are good.

Don’t query data in View/Helper

Avoid perform query in view/helper in Xdite’s Maintainable Rails View.

dashboard_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def show
  @projects_limit = 30
  @groups = current_user.authorized_groups.sort_by(&:human_name)
  @has_authorized_projects = @projects.count > 0
  @projects_count = @projects.count
  @projects = @projects.limit(@projects_limit)

  @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
  @events = @event_filter.apply_filter(@events)
  @events = @events.limit(20).offset(params[:offset] || 0)

  @last_push = current_user.recent_push
  respond_to do |format|
    format.html
    format.js
    format.atom { render layout: false }
  end
end

Group class methods

You could put all your class methods inside class << self. Avoid to put self. in each methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# method 1
class Ability
  class << self
    def allowed(user, subject)
      ...
    end
  end
end

# method 2
class Ability
  def self.allowed(user,subject)
    ...
  end
end

Always sexy validations

1
2
3
4
5
6
7
8
9
10
11
validates :name, presence: true
validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
validates :bio, length: { within: 0..255 }
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
validates :username, presence: true, uniqueness: true,
          exclusion: { in: Gitlab::Blacklist.path },
          format: { with: Gitlab::Regex.username_regex,
                    message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }

validate :namespace_uniq, if: ->(user) { user.username_changed? }

Delgate method

Reference delegate in rails

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Post
  belongs_to :user
  delegate :name, :to => :user, :allow_nil => true, :prefix => true
  delegate :name, :to => :user, :allow_nil => true, :prefix => "author"

end

class User
  has_many :posts
end

# You can call it in Post instance
post = Post.find(param[:id])
post.user_name # return user's name
post.author_name # return user' name

Some basics i didn’t mention before.

object.map(&:foo)

Below are equivalent.

1
2
3
4
5
6
7
Base = Struct.new(:name)

tag1 = Base.new('hello')
tag2 = Base.new('bye')

result = [tag1, tag2].map { |tag| tag.name }
result = [tag1, tag2].map(&:name)

send

1
2
3
4
5
6
7
8
9
# These statements are equal
@person.age
@person.send :age

@person.age = 5
@person.send :age=, 5

@person.set_age(5)
@person.send :set_age, 5

content_for ( yield )

Learned from Xdite’s Maintainable Rails View

layout.html.erb
1
2
3
4
5
6
7
8
# In your layout, you can put a sidebar with yield :sidebar
<div class="main">
  <%= yield %>
</div>

<div class="sidebar">
  <%= yield :sidebar %>
</div>
xxx.html.erb
1
2
3
4
# Use content_for :sidebar to render sidebar.
<%= content_for :sidebar do %>    
  <%= render "ad/foo"%>
<% end %>

change entire layout

Use layout to make profiles page use profile layout not application layout

1
2
3
4
5
class ProfilesController < ApplicationController

  layout 'profile'
  ...
end

redirect from localhost/project/yyy.git to localhost/project/yyy

application_controller.rb
1
2
3
4
5
6
def project
  if id =~ /\.git\Z/
    redirect_to request.original_url.gsub(/\.git\Z/, '') and return
  end
  ...
end

Good gems

  1. annotate: add comments to your ActiveRecord models/specs/routes..rb
  2. awesome_print: pretty print your ruby objects
  3. better_errors: better error page for rails
  4. acts-as-taggable-on: a tagging plugin
  5. rails_best_practices: code metric tool for rails

Comments