2016年2月27日 星期六

Making Generators in Rails 


class LayoutGenerator < Rails::Generators::Base
  source_root File.expand_path('../templates'__FILE__)
  argument :layout_name:type => :string:default => "application"
  class_option :stylesheet:type => :boolean:default => true:description => "Include stylesheet file"

  def generate_layout
    copy_file "stylesheet.css""public/stylesheets/#{file_name}.css" if options.stylesheet?
    template "layout.html.erb""app/views/layouts/#{file_name}.html.erb"
  end

  private
  def file_name
    layout_name.underscore
  end

end

every public method in the class (Rails::Generators::Base) will be executed when the generator runs

  • arguments optional:  we can define a default value for each argument. We’ll add a layout_name argument with a default value of application
  • template method: which takes similar arguments to copy_file but which will parse any erb in the template before copying it to the destination directory
  • class_option: the ability to pass an option that will stop the stylesheet file being generated. We can do this by using the class_option method







Rebuilding Rails

 1. Zero to “It Works!”
     Just describe the basic gem, and how to use it

     ActiveSupport: some utils like single to plural, time and date support
     ActiveModel: thin wrapper around many different ActiveModel implementations to tell Rails how to use them
     ActiveRecord: ORM
     ActionPack: routing, request through controller, action, render to view
     ActionMailer: email

  1. http code: 303 redirect

router的作法, 由rack的env來 找出controller和action
  1. autoloading
用to_underscore來找出所要呼叫的controller
再用const_missing和const_get來自動require

  1. Rendering Views
render: 先找出view的檔案 filename = File.join 'app', 'views', controller_name, "#{view_name}.html.erb"
     def controller_name
           klass = self.class
           klass = klass.to_s.gsub /Controller$/, ''
           Rulers.to_underscore klass
     end

想從controller傳instance variable到view時需要用到instance_variables和instance_variable_get來倒到view
eruby.result locals.merge(:env => env).merge(view_assigns).merge(other_settings)
    def view_assigns
      hash = {}
      variables = instance_variables
      variables -= [:@env]
      variables.each { |name| hash[name] = instance_variable_get(name) }
      hash
    end

     5. Basic Models
like ActiveRecord
find, all, create, update …..
Exercise 2,3 沒做

     6. Request, Response
using Rackʼs Request and Response objects
Exercise 沒做


     7
     8. Rack Middleware

use ‘use’ add layer by layer 

2016年2月18日 星期四

Reading Rails - Concern 


major feature:
  1. auto extend ClassMethods and included InstanceMethods
  2. Dependency resolution

  • Included

Module defines the callback included which is called when a module is included into another class or module

if both two module are extend ActiveSupport::Concern, and both have included do 
module Named
  extendActiveSupport::Concern
  included do
     base.validates_presence_of:first_name,:last_name
  end#...
end

module Mailable
  extendActiveSupport::Concern
  include Named
  included do
     email_regexp =/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/base.validates_format_of:email,with:email_regexp
  end#...
end


Concern delays calling these blocks until your module is included in something that is not a Concern.

  • Class Methods

Calling include only mixes in the instance methods

If there is a ClassMethods module in a Concern, it will automatically extend whatever it is included in.
module Exclaimable
     def self.included(base)
          base.extend(ClassMethods)
     end
     module ClassMethods
          def shout!
               puts"Look out!"
          end
     end
end


  • How It Works

module ActiveSupport
  module Concern
    def self.extended(base)
      base.instance_variable_set("@_dependencies", [])
    end

    def append_features(base)
      if base.instance_variable_defined?("@_dependencies")
        base.instance_variable_get("@_dependencies") << self
        return false
      else
        return false if base < self
        @_dependencies.each { |dep| base.send(:include, dep) }
        super
        base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
        base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods")
        base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
      end
    end

    def included(base = nil, &block)
      if base.nil?
        @_included_block = block
      else
        super
      end
    end
  end
end

Concern delays calling included blocks and mixing in ClassMethods by keeping track of modules in @_dependencies. When a Concern is included in another class, it triggers all the logic on that class.

While unpacking how Concern works, we also came across some other interesting things:
  • Module defines included and extended callbacks.
  • Ruby provides methods such as instance_method_get to access an object's internals.
  • Class methods are not mixed in with include.
  • include takes any number of arguments: include ModA, ModB, ModC.
  • Classes can be compared with equality operations: ClassA < ClassB.
  • By convention, anything starting with a capital letter is a constant.



2016年2月15日 星期一

Rails on Rack 


rake is a wrapper for wrap up the http response and request, provide a unite API for server, framework and middleware software

  • Rack::Sendfile
設定伺服器的 X-Sendfile 標頭(header)。使用 config.action_dispatch.x_sendfile_header 來設定。

  • ActionDispatch::Static
用來決定是否由 Rails 提供靜態 assets。使用 config.serve_static_assets 選項來啟用或禁用(true 啟用)

  • Rack::Lock
將 env["rack.multithread"] 設為 false ,則可將應用程式包在 Mutex 裡。

  • ActiveSupport::Cache::Strategy::LocalCache::Middleware
用來做 memory cache。注意,此 cache 不是線程安全的。

  • Rack::Runtime
設定 X-Runtime 標頭,並記錄請求的執行時間(秒為單位)。

  • Rack::MethodOverride
如有設定 params[:_method],則允許可以重寫方法。這個 Middleware 實作了 HTTP PUT 與 DELETE 方法。

  • ActionDispatch::RequestId
在響應中產生獨立的 X-Request-Id 標頭,並啟用 ActionDispatch::Request#uuid 方法。

  • Rails::Rack::Logger
請求開始時通知 Log,請求結束寫入 Log。

  • ActionDispatch::ShowExceptions
Rescue 任何由應用程式拋出的異常,並呼叫處理異常的程式,將異常以適合的格式顯示給使用者。

  • ActionDispatch::DebugExceptions
負責記錄異常,並在請求來自本機時,顯示除錯頁面。

  • ActionDispatch::RemoteIp
檢查 IP 欺騙攻擊。

  • ActionDispatch::Reloader
準備與清除回呼。主要在開發模式下用來重新加載程式碼。

  • ActionDispatch::Callbacks
處理請求前,先執行預備好的回呼。

  • ActiveRecord::Migration::CheckPending
檢查是否有未執行的遷移檔案,有的話拋出 PendingMigrationError 錯誤。

  • ActiveRecord::ConnectionAdapters::ConnectionManagement
每個請求結束後,若 rack.test 不為真,則將作用中的連線清除。

  • ActiveRecord::QueryCache
啟用 Active Record 的查詢快取。

  • ActionDispatch::Cookies
幫請求設定 Cookie。

  • ActionDispatch::Session::CookieStore
負責把 Session 存到 Cookie。

  • ActionDispatch::Flash
config.action_controller.session_store 設定為真時,設定提示訊息的鍵。

  • ActionDispatch::ParamsParser
解析請求的參數放到 params Hash 裡。

  • ActionDispatch::Head
將 HTTP HEAD 請求轉換成 GET 請求處理。

  • Rack::ConditionalGet
給伺服器加入 HTTP 的 Conditional GET 支持,頁面沒有變化,就不會回傳響應。

  • Rack::ETag

為所有字串 Body 加上 ETag 標頭,用來驗證快取。 

張五常:思考的方法



一、誰是誰非不重要

二、問題要達、要淺、要重要、要有不同答案的可能性
          問題要一針見血
          問題要問得淺
          要斷定問題的重要性

三、不要將預感抹殺了
          純以預感而起,加上想像力去多方推敲,有了大概,再反覆以邏輯證實,是最有效的思考方法。只要得到的理論或見解是合乎邏輯及方法論的規格,是怎樣想出來的無關重要

四、轉換角度可事半功倍
          茅塞可以頓開
          角度可以衡量
          角度有遠近之分

五、例子遠勝符號
          例子要簡而貼切
          例子要分真假
          例子要新奇
          要將例子一般化
          要試找反證的例子

六、百思不解就要暫時擱置



2016年2月13日 星期六

ActiveModel: Make Any Ruby Object Feel Like ActiveRecord


  • ActiveModel API

the important thing about the ActiveModel API is that your models can become ActiveModel compliant without using a single line of Rails code
use "include ActiveModel::Lint::Tests” to make sure your model become ActiveModel compliant

  • The Validations System

use "include ActiveModel::Validations” to add validation module to your model

The validations system calls read_attribute_for_validation to get the attribute, but by default, it aliases that method to send, which supports the standard Ruby attribute system of attr accessor

validates_presence_of is using the more primitive validates_with, passing it the validator class, merging in {:attributes => attribute names} into the options passed to the validator

  • Serialization

include ActiveModel::Serialization

ActiveRecord also comes with default serialization for JSON and XML


  • AttributeMethods: Makes it easy to add attributes that are set like table_name :foo
  • Callbacks: ActiveRecord-style lifecycle callbacks.
  • Dirty: Support for dirty tracking
  • Naming: Default implementations of model.model_name, which are used by ActionPack (for instance, when you do render :partial => model
  • Observing: ActiveRecord-style observers
  • StateMachine: A simple state-machine implementation for models
  • Translation: The core translation support

2016年2月8日 星期一

Confident Ruby

chapter 1.
     four parts of a method
  • collecting input
  • performing work
  • delivering output
  • handling failures
chapter 2.
1. We must identify the messages we want to send in order to accomplish the task at hand.
2. We must identify the roles which correspond to those messages.
3. We must ensure the method's logic receives objects which can play those roles
第二張 performing work
在講說
method裡面要做的事情
要定義好說
要傳遞的訊息是什麼(Message) 以及 誰接收這個訊息(Receiver Role)

def import_legacy_purchase_data(data)
          purchase_list = legacy_data_parser.parse_purchase_records(data)
purchase_list.each do |purchase_record|      
customer =    customer_list.get_customer(purchase_record.email_address)      
product  =    product_inventory.get_product(purchase_record.product_id)      
customer.add_purchased_product(product)      
customer.notify_of_files_available(product)      
log_successful_import(purchase_record)
end
end

chapter 3.
3.2 Use built-in conversion protocols
If we want to provide maximum leeway in input, we can use explicit conversion methods like #to_i. If we want to provide a little  exibility while ensuring that client code isn't blatantly mis-using our method, we can use an implicit conversion such as #to_int
3.3 Conditionally call conversion methods
在講說 有時候輸入的參數 要確認型別時 就條件式的轉換
她有這種型別方法的時候 就轉換
可能可以有好幾種型別  都沒有的時候就報錯
3.4 Define your own conversion protocols
建立自己的轉換型別的方法
3.5 Define conversions to user-defined types
定義好轉型的方法

主要方法接收參數時 只接有此轉型方法的參數
其餘的要報錯誤

3.6 Use built-in conversion functions
用最大的力量來轉型

3.7 Use the Array() conversion function to array-ify inputs
當參數需要是array時 用Array(參數) 先來強制轉型 

3.8 Define conversion functions
建立一個idempotent的方法 強制轉換, 中間可能包含多種的轉換 不過結果就是出來一種型態
當我們需要某一個型態的時候 都用這個方法來強制轉換

3.9 Replace "string typing" with classes
當傳入的參數是string 而且方法裡面有很多case when的時候
試著把傳入的參數 變成有意義的物件

3.12 Reject unworkable values with preconditions
在preconditions就把一些需要的檢查做掉 還有需要使用到的變數轉換

3.13 Use #fetch to assert the presence of Hash keys
用fetch來判斷hash有沒有哪些key值

3.14 Use #fetch for defaults
用fetch來判斷default值
用block來當做參數 丟入fetch
If we had used the block form, the expensive computation would only have been triggered when it was actually needed.

3.15 Document assumptions with assertions
接外部可能會變動的api時
要再接的時候 針對每個參數 做處理檢查 一有不同 就要報錯
ex: hash要用 fetch確保參數都有, 型別轉換也要不是這個型別的話 就要報錯  Kernel#Float
amount = transaction.fetch("amount")
amount_cents = (Float(amount) * 100).to_i

3.16 Handle special cases with a Guard Clause
特別case的處理時 可以放在方法的最上方 當發生時 就導去別處
而不用因為用到if else 導致程式的結構不好閱讀
方法裡面主要就放常常發生的case

3.17 Represent special cases as objects
特別的case處理時 如果這個case再很多地方用到
可以用一個特別的class來處理他
例如current_user,  可以有一個特別的class => GuessUser 來代表沒有current_user的情況

3.18 Represent do-nothing cases as null objects
有時候會有一些要判斷是不是nil的情況時
可以用一個null object來處理  就不用寫很多if來判斷是不是nil
但是在null object裡面 log要寫好 不然有一些錯誤永遠不會發現
class NullObject < BasicObject
     def method_missing(*) end
     def respond_to_missing?(name) true end
     def nil? true end
     def ! true end
end

3.19 Substitute a benign value for nil
當nil的情況 是要走另一種情形 不會導致錯誤, 給他一個有意義的值

3.20 Use symbols as placeholder objects
當nil的情況 會導致錯誤時
給他一個有意義的symbol來報錯 會比較好debug

3.21 Bundle arguments into parameter objects
當兩個參數一定是要一起出現時 用一個物件把他包起來
當有if的情況出現時, 看是不是可以在refactor

3.22 Yield a parameter builder object
後面看不太懂

3.23 Receive policies instead of data
可以用block的方式 來做錯誤處理
def delete_files(files, &error_policy)
     error_policy ||= ->(file, error) { raise error }
     files.each do |file|
          begin
                File.delete(file)
           rescue => error
               error_policy.call(file, error)
           end
     end
end

4.1 Write total functions
當回傳的形態是array時
不管任何情形都要回array

4.2 Call back instead of returning
有時候用callback來取代回傳直會更好
用callback有時能更明顯的表達出這個function在做什麼
command-query separation (CQS). CQS is a simplifying principle of OO design which advises us to write methods which either have side effects (commands), or return values (queries), but never both

4.3 Represent failure with a benign value
不要用nil回復失敗的情況
用個有意義一點的值

4.4 Represent failure with a special case object
錯誤case時用特別的物件來回
反正不要nil就是了

4.5 Return a status object
當回傳情況有多種時 可以用一個新的狀態物件來回傳

4.6 Yield a status object
一樣用callback 可以讓function更清楚的表達她是要做什麼
callback是一個狀態物件的各種處理情形
就可以知道回傳的各種情況 是怎麼處理

4.7 Signal early termination with throw
用throw和catch來提早結束某些情況

5.1 Prefer top-level rescue clause
用top-level的rescue

5.2 Use checked methods for risky operations
還是用block的方式 傳入錯誤的時候要怎麼處理

5.3 Use bouncer methods

在用另一個方法 來包裝錯誤處理完後 要回傳的東西