動機

也許有需要,也複習一下rails

||=

foo, bar = [], 1
foo ||= bar # foo = foo || bar
foo

這邊弔詭的是false value,也就是什麼value會被視為false

hash default value

default value不會自己被copy喔

還有後面看到的值寫不進去hash,應該牽涉到ruby處理method call的方式

a = hsh[:something]
a.send(:=, val)
hsh = Hash.new([])

hsh[:one] << 'one'
hsh[:two] << 'two'

hsh[:nonexistent]
# => ['one', 'two']

hsh
# => {}
hsh = Hash.new([])

hsh[:one] += ['one']
hsh[:two] += ['two']
# This is syntactic sugar for hsh[:two] = hsh[:two] + ['two']

hsh[:nonexistant]
# => []

hsh
# => { :one => ['one'], :two => ['two'] }
hsh = Hash.new { [] }
# This time, instead of a default *value*, we use a default *block*

hsh[:one] << 'one'
hsh[:two] << 'two'

hsh[:nonexistent]
# => []
# We *did* mutate the default value, but it was a fresh one every time.

hsh
# => {}
# But we never mutated the hash itself, therefore it is still empty!
hsh = Hash.new {|hsh, key| hsh[key] = [] }

hsh[:one] << 'one'
hsh[:two] << 'two'

hsh[:nonexistent]
# => []
# We *did* mutate the default value, but it was a fresh one every time.

hsh
# => { :one => ['one'], :two => ['two'], :nonexistent => [] }

proc & lambda & block

block沒有apply的一串代碼 lambda正常的lambda proc有點像macro(可以當成裡面的code複製貼上),但有自己的scope

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"
# Proc returns control not just from the proc, but also from the method enclosing the proc!
def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

equal

  • == : 值
  • eql? : 值 & type
  • equal? : 記憶體位置
  • === : lhs.includes?(rhs)
p Fixnum === 1 # => true
p 1 === Fixnum # => false
p 100 == 100.0 # => true
p 100.eql? 100.0 # => false

tap

就是把instance丟到block中跑再丟回原本的instance

# x 就是物件,caller,或是receiver
(1..5).tap { |x| puts "element: #{x.inspect}" }.to_a
  # => element: 1..5
  .tap { |x| puts "array: #{x.inspect}" }
  # => array: [1, 2, 3, 4, 5]
  .select { |x| x%2 == 0 }
  .tap{ |x| puts "evens: #{x.inspect}" }
   # => evens: [2, 4]
  .map{ |x| x*x }
  .tap{ |x| puts "squares: #{x.inspect}" }
   # => squares: [4, 16]

Present and exist

present: 看是不是false value

[ "", " ", false, nil, [], {} ].any?(&:present?)
# => false

用在orm上會悲劇

User.where(name: 'mike').present?
# User Load (8.1ms) SELECT "users".* FROM "users" WHERE "users"."name" = $1 ORDER BY users.id ASC  [["name", 'mike']]

exist: 基本上是用在orm上,看有沒有row存在

User.exists?(name: 'mike')
# User Exists (2.4ms)  SELECT 1 AS one FROM "users" WHERE "users"."name" = $1 ORDER BY users.id ASC LIMIT 1  [["name", 'mike']]
# 如果是any? 1 AS one的部分會變成COUNT(*)

freeze

freeze可以當成c++的const

所以會有

  • int* - pointer to int
  • int const * - pointer to const int
  • int * const - const pointer to int

就是在Pointer上的const或是在pointer內容的const

N+1 query

class User < ActiveRecord::Base
  has_many :clients
end

class Client < ActiveRecord::Base
  has_many :contacts
  belongs_to :user
end
class Contact < ActiveRecord::Base
  belongs_to :client
end

@contacts = @user.clients.includes( :contact )
class Post < ActiveRecord::Base
  has_many :comments
  belongs_to :user
end

class Comment < ActiveRecord::Base
  has_many :replies
  belongs_to :user
end

class Reply < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :posts
  has_many :comments
  has_many :replies
end

# 不包含user
Post.includes(:comments => [:replies])

# 包含user
Post.includes(:user, :comments= > [:user, {:replies => [:user]}])

如果是在別的table的東西就可以先用includes來preload

test data: fixture & factory bot

Rails內建有Fixture功能可以建立假資料,方法是為每個Model使用一份YAML資料。 Fixture的缺點是它是直接插入資料進資料庫而不使用ActiveRecord,對於複雜的Model資料建構或關連,會比較麻煩。

FactoryGirl這套工具,相較於Fixture的缺點是建構速度較慢。

order = FactoryBot.create(
  :order,
  line_items: [FactoryBot.create(:line_item, price_cents: 40000)],
  payments: [FactoryBot.create(:payment, amount_cents: 40000)]
)
# orders.yml
payments_equal_line_item_total:
  # no attributes needed

# line_items.yml
electric_dog_polisher:
  order: payments_equal_line_item_total
  name: 'Electric dog polisher'
  price_cents: 40000

# payment_methods.yml
visa:
  name: 'Visa'

# payments.yml
first:
  order: payments_equal_line_item_total
  payment_method: visa
  amount_cents: 40000

# order = orders(:payments_equal_line_item_total)

RESTful & CURD

這裡可以看成對array操作 像

arr # events_path, GET
arr[i] # event_path(@event), GET
arr << "something" # events_path, POST
arr.delete(i) # event_path(@event), DELETE
arr.update(i, "something") # event_path(@event), PATCH/PUT

那為什麼需要

  • edit_event_path(@event)
  • new_event_path
  • event_path(@event)

的GET

因為我們透過網頁與user互動,把input轉成資料再調用診真正的action

helperGETPOSTPATCH/PUTDELETE
event_path(@event)/events/1 : show action/events/1 : update action/events/1 : destroy action
events_path/events : index action/events : create action
edit_event_path(@event)/events/1/edit : edit action
new_event_path/events/new : new action

how to handle req & header

orm callback

Ref

菜鳥Rails工作面試初體驗 Jr. Ruby on Rails Engineer面試初體驗(上) See also: 我的中高階 Rails 工作面試心得分享 面試 interview Ruby 重要概念整理 Ruby hash default value behavior Ruby面試精選30題 - Day29 Ruby的tap method Ruby面試精選30題 - Day23 Ruby的’==’,’===’ ’eql?’ ’equal?’ When to use lambda, when to use Proc.new? Web, Rails, 用巢狀include和查表方式來避免 n+1 query A Visual Guide to Using :includes in Rails Ruby on Rails 實戰聖經 Factories and fixtures in Rails See also: Fast Ruby