Ruby on Rails 網站程式設計基礎班

第十二課:開發自己的 gem

公告

 

1. 請繳回門禁卡

2. 完成留言板

    - 至少要有使用者登入登出功能

    - 將寫好的留言板上架至 Heroku

    - 死線是 11/27/2016 晚上 11:59:59

3. 寫下你對這堂課的建議 (至少兩項)

Rails Module

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :post
  has_many :votes, as: :voteable

  validates :content, presence: true, length: {minimum: 5}

  def up_votes
    self.votes.where(vote: true).length
  end

  def down_votes
    self.votes.where(vote: false).length
  end

  def total_votes
    up_votes - down_votes
  end
end
class Post < ActiveRecord::Base
  belongs_to :user

  has_many :comments
  has_many :categories_posts
  has_many :categories, through: :categories_posts
  has_many :votes, as: :voteable

  def up_votes
    self.votes.where(vote: true).length
  end

  def down_votes
    self.votes.where(vote: false).length
  end

  def total_votes
    up_votes - down_votes
  end
end

兩個需要投票的 model 有太多一樣的程式碼, 遵守 DRY 的原則,我們應該要把一樣的程式碼打包起來,集中到一個地方管理

Rails Module

# 我們到 /lib 底下,增加一個新檔案 voteable.rb

module Voteable
  def up_votes
    self.votes.where(vote: true).length
  end

  def down_votes
    self.votes.where(vote: false).length
  end

  def total_votes
    up_votes - down_votes
  end
end

Rails Module

class Post < ActiveRecord::Base
  include Voteable
  
  belongs_to :user

  has_many :comments
  has_many :categories_posts
  has_many :categories, through: :categories_posts
  has_many :votes, as: :voteable
end
class Comment < ActiveRecord::Base
  include Voteable

  belongs_to :user
  belongs_to :post
  has_many :votes, as: :voteable

  validates :content, presence: true, length: {minimum: 5}
end

有了 Voteable 模組後,我們只需 include 它就可使用定義在  Voteable 內的方法,原本和在 model 裡面和 voteable 相關的方法就可以移除掉

Rails Module

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Postboard2
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    config.autoload_paths += %W(#{config.root}/lib)
    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    # Do not swallow errors in after_commit/after_rollback callbacks.
    config.active_record.raise_in_transactional_callbacks = true
  end
end

最後需要通知 rails 載入 /lib 路徑下的檔案

把 module 打包成 gem

先到 https://rubygems.org/ 註冊帳號

把 module 打包成 gem

# 跳出 rails 專案,在 terminal 輸入...
bundle gem 你的_gem_名稱
# 到.gemspec檔裡,把以下程式碼註解掉...

  spec.homepage      = "TODO: Put your gem's website or public repo URL here."

  if spec.respond_to?(:metadata)
    spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
  else
    raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
  end

把 module 打包成 gem

到 /lib 底下,打開 .rb 檔,在裡面加入我們的 voteable 程式碼

module 你的module名稱
  def up_votes
    self.votes.where(vote: true).length
  end

  def down_votes
    self.votes.where(vote: false).length
  end

  def total_votes
    up_votes - down_votes
  end
end

把 module 打包成 gem

接下來執行:

gem build 你的_gem_名稱.gemspec

成功的話會在資料夾裡產生一個 .gem 檔,接下來就:

gem push .gem檔的名稱

成功!妳已經把 Gem 發佈在 rubygems.org 上面了! 接下來你就可以透過把它寫進 Gemfile 然後執行 bundle 把它裝起來

module vs gem

1. 放在 /lib 裡的 module,是整合跨 model 的一些功能,但是是專門為該 Rails 專案客製化的功能

 

2. 如果今天同樣的 module 也適合在其他不同的 Rails 專案重複使用,那就應該把該  module 打包成一個 gem,讓它能被快速安裝在不同的 rails 專案裡

deck

By Eugene Chang