Tôi đã viết Gem đầu tiên của mình như thế nào?
Lý do Mỗi lần tạo mới một Rails project, do là người thích sự hoàn hảo, nên mình thường dành ra một chút thời gian để beautify lại Gemfile, Lúc ban đầu tạo một project thì Gemfile sẽ như thế này: source 'https://rubygems.org' git_source ( :github ) do | repo_name | repo_name = ...
Lý do
Mỗi lần tạo mới một Rails project, do là người thích sự hoàn hảo, nên mình thường dành ra một chút thời gian để beautify lại Gemfile, Lúc ban đầu tạo một project thì Gemfile sẽ như thế này:
source 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.3' # Use sqlite3 as the database for Active Record gem 'sqlite3' # Use Puma as the app server gem 'puma', '~> 3.0' # Use SCSS for stylesheets gem 'sass-rails', '~> 5.0' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # Use CoffeeScript for .coffee assets and views gem 'coffee-rails', '~> 4.2' # See https://github.com/rails/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library gem 'jquery-rails' # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks gem 'turbolinks', '~> 5' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 2.5' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 3.0' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platform: :mri end group :development do # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. gem 'web-console', '>= 3.3.0' gem 'listen', '~> 3.0.5' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
Lúc đó, trước khi code mỗi project mình thường phải tự beautify lại Gemfile này. Nó bao gồm các công việc như:
- Xóa các dòng comment
- Thay thế dấu ' bằng "
- Remove các dòng trống
Lặp đi lặp lại các công việc nhàm chán đó là điều không mấy thú vị. Vào một ngày đẹp trời, chán việc phải chỉnh bằng tay, mình chợt nảy ra ý tưởng viết một Gem để thực hiện công việc này một cách tự động, và thế là gem gf_beauty ra đời.
Thực hiện
Gemspec
Đầu tiên chúng ta cần tạo một file gemspec như sau:
Gem::Specification.new do |s| s.name = "gf_beauty" s.version = "1.0.9" s.date = "2017-06-02" s.summary = "Gemfile Beautify" s.description = "A simple gem which will beautify your Gemfile" s.authors = ["Tran Xuan Nam"] s.email = "tran.xuan.nam@framgia.com" s.files = ["lib/gf_beauty.rb"] s.executables << "gf_beauty" s.homepage = "https://rubygems.org/gems/gf_beauty" s.license = "MIT" end
Trong đó:
- name là tên của gem mình muốn tạo. Chú ý là tên này không được trùng lặp với gem trên trang http://guides.rubygems.org
- version là version của gem
- date là ngày tháng tạo gem
- summary , description là tóm tắt về chức năng của gem
- authors, email là thông tin về tác giả của gem
- files đây là danh sách các file dùng để xây dựng gem
- executables vì gem của mình viết dùng để chạy bằng command line nên cần phải đưa vào tên của command line mà tôi mình để người dùng có thể chạy (hơi lan man, phần sau mình sẽ hướng dẫn kĩ hơn)
- homepage, license các thông tin thêm cho gem
Sau khi tạo được xong file gemspec chúng ta sẽ đến phần viết implement chức năng cho gem.
lib/gf_beauty.rb
❯ touch lib/gf_beauty.rb
class GfBeauty def self.process current_project = `pwd` current_project = current_project.gsub(/ /,"") content = File.read(current_project + "/Gemfile") content = content.gsub(/#s.*s*/,"") content = content.gsub(/'/, '"') content = content.gsub(/s*^group/, " group") content = content.gsub(/s*^s+^gem/, " gem") File.open(current_project + "/Gemfile", "w") do |file| file.puts content end system "echo", "-e", "e[92mYour Gemfile was successfully beautified!" rescue Exception => e system "echo", "-e", "e[91m#{e.message}" system "echo", "-e", "e[91mMake sure you're in the right place!" end end
Do gem của mình sau khi người dùng cài đặt, họ có thể chạy bằng Command Line như thế này:
❯ gf_beauty
Vậy nên, mình phải tạo một executable file thực hiện.
bin/gf_beauty
❯ touch bin/gf_beauty
Công việc của file này rất đơn giản, nó sẽ gọi đến thư viện mà chúng ta đã viết ở trên.
#!/usr/bin/env ruby require "gf_beauty" GfBeauty.process
Dùng lệnh chmod để file này có thể thực thi từ Command Line
❯ sudo chmod a+x bin/gf_beauty
Đến bước này, cơ bản là chúng ta đã hoàn thành gem file của mình.
Publish
Đầu tiên chúng ta cần tạo một tài khoản ở trên trang https://rubygems.org để có thể publish gem lên trên đó. Sau khi tạo tài khoản xong, đăng nhập ở local bằng Command Line:
❯ curl -u your_user_name https://rubygems.org/api/v1/api_key.yaml >~/.gem/credentials; chmod 0600 ~/.gem/credentials Enter host password for user 'your_user_name':
Nhập account mà bạn vừa đăng kí vào. Đến đây bạn có thể publish gem từ local lên https://rubygems.org bằng Command Line được rồi.
Tiếp theo, chúng ta cần build gemspec
❯ gem build gf_beauty.gemspec Successfully built RubyGem Name: gf_beauty Version: 1.0.9 File: gf_beauty-1.0.9.gem
Sau đó là push lên https://rubygems.org bằng:
❯ gem push gf_beauty-1.0.9.gem Pushing gem to https://rubygems.org... Successfully registered gem: gf_beauty (1.0.9)
Sử dụng
Đến đây, bạn có thể sử dụng gem của mình vừa publish lên rồi.
❯ gem install gf_beauty
❯ gf_beauty
Bùm, file Gemfile của bạn sạch sẽ tinh tươm.
source "https://rubygems.org" git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end gem "rails", "~> 5.0.3" gem "sqlite3" gem "puma", "~> 3.0" gem "sass-rails", "~> 5.0" gem "uglifier", ">= 1.3.0" gem "coffee-rails", "~> 4.2" gem "jquery-rails" gem "turbolinks", "~> 5" gem "jbuilder", "~> 2.5" group :development, :test do gem "byebug", platform: :mri end group :development do gem "web-console", ">= 3.3.0" gem "listen", "~> 3.0.5" gem "spring" gem "spring-watcher-listen", "~> 2.0.0" end gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
Kết luận
Tuy là gem đơn giản, phục vụ mục đích cá nhân là nhiều, nhưng qua đây mình học được cách đưa gem chính chủ của mình lên https://rubygems.org để người khác có thể sử dụng. Toàn bộ source mình publish lên để các bạn có thể theo dõi, nhớ star cho repo của mình nếu cảm thấy có ích nhé (honho) https://github.com/namtx/gf_beauty Happy coding