iT邦幫忙

2022 iThome 鐵人賽

DAY 2
2
Modern Web

Rails,我要進來囉系列 第 2

第二天:在 RailsGuides 無意間掏到寶藏?!

  • 分享至 

  • xImage
  •  

開場白

鼬~~~哩賀,我是寫程式的山姆老弟,今天是我們的開賽第二天!

不免俗的來看一下,RailsGuidesGetting Started with Rails 這篇寫了什麼,我看的文件是 Rails 7 的版本,有一些新的發現,覺得挺不錯的,分享給大家!

第一個寶藏-免費學習資源

Untitled

free-programming-books repository 中,含有大量的免費學習資源

跟大家分享我看到幾個很驚艷的

  1. Ruby one-liners cookbook - Sundeep Agarwal:這本跟 Rails 沒關係,但打開了我的眼界,原來 ruby 還可以這樣玩

    顧名思義,這本書就是在講「怎麼用 1 行 ruby 搞定需求」,實在是太多 Magic 了,Bash + Ruby + Regex 可以這麼酷

    備註:最前面有 $ 符號,表示是在終端機所輸入的指令

    # 如何優雅處理 JSON 格式資料?
    
    ## jp 的意思是 json parse 的縮寫
    ## 這行意思是,建立一個叫做 jp 的 bash function
    ## 這個 function 會對輸入參數執行 JSON.parse 並放在 input 的區域變數裡
    ## 然後可以針對 input 變數,執行隨後指定的 ruby 程式
    ## 簡單來說,就是建立一個 bash 捷徑,並利用 ruby 把 JSON 格式的資料,快速結構化取出
    ## 把下面這行加到 ~/.bashrc 或 ~/.zshrc,方便之後快速使用
    $ jp() { ruby -rjson -e 'input = JSON.parse(ARGF.read);'"$@" ; }
    
    ## 假設你已經有一堆 json 格式的資料
    $ cat sample.json
    {
        "fruit": "apple",
        "blue": ["toy", "flower", "sand stone"],
        "light blue": ["flower", "sky", "water"],
        "language": {
            "natural": ["english", "hindi", "spanish"],
            "programming": ["python", "kotlin", "ruby"]
        },
        "physics": 84
    }
    
    ## 可以直接用 hash 格式,取出所有資料
    $ jp 'input.each {|k,v| puts "#{k}: #{v}"}' sample.json
    fruit: apple
    blue: ["toy", "flower", "sand stone"]
    light blue: ["flower", "sky", "water"]
    language: {"natural"=>["english", "hindi", "spanish"], "programming"=>["python", "kotlin", "ruby"]}
    physics: 84
    
    ## 取出我想要的資料
    $ jp 'puts input.dig("language", "programming")' sample.json
    python
    kotlin
    ruby
    

    這只是其中一個範例,讓我體會到,原來可以利用 ruby 的特性跟 bash 結合,做出酷酷的功能

    這本書還有很多跟 Regex 相關的範例,但小弟的 Regex 實在是不太行,就不拿出來獻醜了

  2. Ruby Notes for Professionals - Compiled from StackOverflow Documentation (PDF):其實這本書叫做 Ruby on Rails Notes for Professionals,不知道為什麼被縮短成這樣

    這本書最後一次更新是 2018 年,有 Rails 5 的內容,還不算太舊,雖然書名是表示寫給專業的 Rails,但我大概看下來,覺得新手也蠻適合看的,從入門到進階的議題都有包含

    我挑其中一小段來跟大家分享,我選的這一章是 Rails Best Practice,其中的 Section 6.2: Domain Object (No More Fat Models) 小節,跟我以往用 Rails 的方式不太一樣,先直接上範例

    # Bad Practice
    # app/models/order.rb
    class Order < ActiveRecord::Base
    	SERVICE_COMMISSION = 0.15
    	STRIPE_PERCENTAGE_COMMISSION = 0.029
    	STRIPE_FIXED_COMMISSION = 0.30
    
    	...
    	def commission
    		amount * SERVICE_COMMISSION - stripe_commission
    	end
    
    	private
    
    	def stripe_commission
    		amount * STRIPE_PERCENTAGE_COMMISSION + STRIPE_FIXED_COMMISSION
    	end
    end
    

    作者認為,Controller 要越簡單越好、比較繁雜的細節要放在 Model,但很多細節都塞在 Model,也會讓 Model 變得太肥,塞太多客製化的細節,就像上面的 Bad Practice 範例一樣,一個訂單的 Model,放了一些針對某服務(這邊的例子是 Stripe 金流服務)的常數、和綁定某服務的計算公式,如果今天需要整合另一個金流服務的話,那 Model 就會越變越大、也不好修改

    所以作者建議要導入 Domain Object 的概念,優化成以下這樣

    # app/models/order.rb
    class Order < ActiveRecord::Base ...
        # No reference to commission calculation
    	# 簡化 Model 內部
    end
    
    # lib/commission.rb
    class Commission
    	SERVICE_COMMISSION = 0.15
    
    	def self.calculate(payment_method, model)
    		model.amount * SERVICE_COMMISSION - payment_commission(payment_method, model)
    	end
    
    	private
    
    	def self.payment_commission(payment_method, model)
    		# There are better ways to implement a static registry,
    		# this is only for illustration purposes. Object.const_get("#{payment_method}Commission").calculate(model)
    	end
    end
    
    # lib/stripe_commission.rb
    class StripeCommission
    	STRIPE_PERCENTAGE_COMMISSION = 0.029
    	STRIPE_FIXED_COMMISSION = 0.30
    
    	def self.calculate(model)
    		model.amount * STRIPE_PERCENTAGE_COMMISSION + STRIPE_PERCENTAGE_COMMISSION
    	end
    end
    
    # app/controllers/orders_controller.rb
    class OrdersController < ApplicationController
    	def create
    		@order = Order.new(order_params)
    		@order.commission = Commission.calculate("Stripe", @order)
    		...
    	end
    end
    

    從這個範例來看,作者的 Domain Object 的意思是,把一些特殊的商業邏輯,往 lib/ 拉,這個概念合理,但我看起來是有點問題的,就是當太多商業邏輯都是這樣拉的話,最後還是會在 lib/ 內混在一起,舉例來說,Stripe 金流拉出來放在 lib/,變成 lib/stripe_commission.rb;綠界的金流也拉出來 lib/ecpay_commission.rb,先不說其他金流,如果再拉第三方登入各平台的邏輯進來的話,那 lib/ 真的會很熱鬧,所以其實問題還是一樣

    我以前使用的習慣跟作者的差不多,只不過不是塞在 lib/,而是放在 app/services 底下,但概念上還是不同,services 感覺起來會是更大一點的,不知道這樣講有沒有人懂 XD,就是可能一個 service 就等同於串一個 stripe 金流的概念,所以一個 app/services/stripe_service.rb ,裡面可能會有 結帳開發票查訂單 等功能,跟 作者的意思不太一樣,就先解釋到這吧,不是這篇重點。

    總之,這本書編排方式不錯,每個 Section 的內容安排很精要,不會很多廢話,就算不看文字,直接看 code 也是會懂。

    第二個寶藏-Rails 的 Autoloading

    Getting Started with Rails 的第五點 - Autoloading,這段是我一直都很好奇,Rails 到底是怎麼做到的,這段講說:只要放在 app/ 底下的檔案,都不需要使用 require 去引用其他 ruby 檔案,就可以直接使用,是個很酷的功能!

    Untitled

    明天就來研究一下 autoloading 的機制吧,直接幫明天的我省下煩惱 XD

    第三個寶藏-Turbo

    在 **7.5 Deleting an Article 這一節,看到了酷東西,我之前沒用過,從 Turbo 官網的介紹是可以減少 JS 的數量,應該就是不需要每個 action 都要獨立出一個 js 檔案,這樣確實是挺方便的,跟之前看過的 Hotwire 不知道差在哪裡,後面幾天有機會來研究一下

    HTML Over The Wire | Hotwire 2022-08-27 23-38-48.png

    結果 Hotwire 官網就有介紹說,Hotwire 的核心就是用 Turbo XD

    總結

    Getting Started with Rails 這篇,大致上是讓 Rails 新手對 Model, View, Controller(MVC) 有個基礎的操作概念,以及怎麼讓一個基礎頁面呈現、操作兩個 Model 的 CRUD(Create-Read-Update-Delete),這篇是以部落格當作範例

    如果你是 Rails 新手的話,十分建議直接跟著這篇動手做,不要複製貼上,應該會蠻好理解 Rails 的運作,剛開始可能會覺得有點莫名其妙,心裡會感覺:「為什麼這樣就可以動?!」,但這就是 Rails 慣例優於設置的設計哲學所帶來的魔法,在新手階段不需要知道為什麼,只需要知道怎麼做,即使不知道為什麼,也能站在巨人的肩膀上,快速開發出一個像樣的網站

    再來是,關於我發現的寶藏,一直以來,我習慣了 Rails 所帶來的方便,這次有機會一探魔法底下的運作原理,實在很興奮,希望能把我理解的,分享給對 Rails 也有興趣的你們,我們明天見~


上一篇
第一天:來個開場白,這系列的走向?
下一篇
第三天:為什麼 Rails 不需要常常使用 require?Rails 的 autoloading 是什麼魔法?
系列文
Rails,我要進來囉30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Bater
iT邦新手 4 級 ‧ 2022-09-20 09:07:32

這篇不錯啊,感謝整理

Sam iT邦新手 4 級 ‧ 2022-09-20 09:55:29 檢舉

感謝大神光臨~

我要留言

立即登入留言