TODAY I LEARNED by vàithằngnhốnhăng

Rails Active Record & ORM

Active Record

Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database. It is an implementation of the Active Record pattern which itself is a description of an Object Relational Mapping system.

Active Record Pattern

In Active Record, objects carry both persistent data and behavior which operates on that data. Active Record takes the opinion that ensuring data access logic as part of the object will educate users of that object on how to write to and read from the database. active-record

ORM (Object Relational Mapping)

Object Relational Mapping, commonly referred to as its abbreviation ORM, is a technique that connects the rich objects of an application to tables in a relational database management system. Using ORM, the properties and relationships of the objects in an application can be easily stored and retrieved from a database without writing SQL statements directly and with less overall database access code.

Ruby's Ancestor Chain

Để xem “gia phả” của một class => gọi hàm ancestors của class đó. Mảng trả về chứa thông tin theo thứ tự sau:

  • calling class
  • Module mà class đó include
  • Ông già của class đó
  • Module mà ông già của class đó include
  • Ông già của ông già của class đó (ông nội :v)

Ví dụ:

irb> String.ancestors
 => [String, Comparable, Object, Kernel, BasicObject]

Rails ActiveSupport Concerns

Concern là gì

Concern thực chất là các đoạn code được tách nhỏ ra cho phép chúng ta có thể tổ chức code một cách mạch lạc, “sạch sẽ” hơn.

Chức năng của Concern

Chức năng của Concern = Module + extends methods in included callback

Module cho phép gom methods lại thành nhóm và sau đó các methods này được sử dụng bằng cách include module chứa chúng vào trong module/class khác.

# ../model/concerns/engine.rb
module Engine
  def start_engine
    "Jarvis, start the engine!"
  end
end

# ../model/tank.rb
class Tank < ActiveRecord::Base
  include Engine

  def run
    "I'm running."
  end
end

# ../model/iron_man.rb
class IronMan < ActiveRecord::Base
  include Engine

  def snap_fingers
    "I am Iron Man!"
  end
end

Sử dụng

$ rails c
$ puts Tank.new.start_engine
Jarvis, start the engine!
$ puts IronMan.new.start_engine
Jarvis, start the engine!

Hạn chế của việc class include module đó là class đó chỉ có thể truy cập tới instance methods của module mà không thể truy cập tới các class methods

Một cách giải quyết là ta nhóm các class methods trong một module khác và extend nó trong included callback, đồng thời trong callback này, có thể viết các method về validates, quan hệ, scope.. để các model có thể tái sử dụng

# ../model/concerns/engine.rb
module Engine
  def self.included klass
    klass.extend ModuleMethods

    klass.class_eval do
      has_one :piston
      validates :engine_type, presence: true
    end
  end

  module ModuleMethods
    def missile_launch
      "Fire!!"
    end
  end

  def start_engine
    "Jarvis, start the engine!"
  end
end

# ../model/tank.rb
class Tank < ActiveRecord::Base
  include Engine
end

# ../model/iron_man.rb
class IronMan < ActiveRecord::Base
  include Engine
end
$ rails c
$ puts IronMan.new.start_engine #OK
$ puts IronMan.missile_launch   #OK

Refactor lại 2 ví dụ trên sử dụng Concern

# ../model/concerns/engine.rb
module Engine extend ActiveSupport::Concern
  included do
    has_one :piston
    validates :engine_type, presence: true

    def start_engine
      "Jarvis, start the engine!"
    end

    class << self
      def missile_launch
        "Fire!!"
      end
    end
  end
end

# ../model/tank.rb
class Tank < ActiveRecord::Base
  include Engine
end

# ../model/iron_man.rb
class IronMan < ActiveRecord::Base
  include Engine
end

Giải quyết vấn đề về dependency

Trong ví dụ sau, module Bar sẽ phụ thuộc và module Foo, để sử dụng module Bar, cần include đồng thời cả Foo lẫn Bar trong class Test:

# test_dependency.rb

module Foo
  def self.included base
    base.class_eval do
      def self.method_of_foo
        puts "inside method of Foo"
      end
    end
  end
end

module Bar
  def self.included base
    base.method_of_foo
  end
end

class Test
  include Foo # Cần include module này do module Bar phụ thuộc vào module Foo
  include Bar # Bar mới là module thực sự cần dùng
end

Khá là vô lý khi sử dụng một module mà còn phải quan tâm xem nó phục thuộc vào module nào khác nữa. Vì vậy có thể thử include trực tiếp module Foo trong module Bar

# test_dependency_fail.rb

module Foo
  def self.included base
    base.class_eval do
      def self.method_of_foo
        puts "inside method of Foo"
      end
    end
  end
end

module Bar
  include Foo
  def self.included base
    base.method_of_foo
  end
end

class Test
  include Bar
end

==> gây lỗi undefined method 'method_of_foo' for Test:Class, bởi trong hoàn cảnh này, base là Bar chứ không phải class Test, nên chương trình sẽ không thực hiện đoạn code trong block base.class_eval

Giải quyết vấn đề này bằng việc dùng Concern

# test_dependency_success.rb
require "active_support/concern"

module Foo extend ActiveSupport::Concern
  included do
    class << self
      def method_of_foo
        puts "inside method of Foo"
      end
    end
  end
end

module Bar extend ActiveSupport::Concern
  include Foo

  included do
    self.method_of_foo
  end
end

class Test
  include Bar # Class Test không quan tâm đến các module mà Bar phụ thuộc nữa
end

Rails asset pipeline

Asset pipeline là gì

Asset pipeline cung cấp việc concatenate và minify/compress JavaScript, CSS assets (Cũng có thể là CoffeeScript, Sass và ERB). Mặc định nó đã có sẵn, có thể disable nếu không muốn dùng.

Lợi ích

  • Concatenate
    • combine thành 1 file => browser chỉ tốn 1 request để get assets
    • dùng fingerprint để tạo unique name cho mỗi file sau khi nối => cache hiệu quả hơn
  • Minification or compression
    • xóa space, comment… để giảm size của file assets
  • Precompilation
    • dịch bọn scss, coffee… thành css và js để dùng

Bonus: Fingerprint là kỹ thuật sử dụng nội dung của file để tạo ra tên file unique, hỗ trợ cho caching. Khi nội dung file thay đổi thì tên file cũng thay đổi.

Ví dụ: application.css sẽ được đổi tên thành

application-908e25f4bf641868d8683022a5b62f54.css

Đoạn hex kia đc gen ra bằng MD5 hexdigest từ nội dung của file.

Ruby Arguments

All about ruby arguments

ruby arguments

Rails Request/Response Cycle

Request: Browser -> Nginx -> Puma/Unicorn -> Rack (Middleware) -> Routing -> Controller (-> Model) -> Views -> Controller (response được tạo ra) Response: Controller -> Routing -> Rack (Middleware) -> Puma/Unicorn -> nginx -> Browser

Tại sao cần cả Nginx và Puma/Unicorn

  • Nginx gọi là web server. Puma/Unicorn gọi là Application server
  • Tầng nginx thường sử lý để redirect
    • Nginx viết bằng C nên rất nhanh
    • Những request động cần vào server thì redirect request vào Puma/Unicorn
    • Những request tĩnh, ví dụ 404.html, 500.html, images tĩnh v.v, /public (phân biệt với /public ở Rails app) mà không cần khởi động application server (giả sử App server chết thì nginx vẫn trả về trang 500.html cho người dùng được)
    • http redirects to https
    • Xử lý multi threads, bundle nhiều request vào file gửi cho Puma/Unicorn hiệu quả về performance hơn
    • nginx xử lý DDos attack
  • Tầng Puma/Unicorn
    • App server là thứ thật sự “chạy” app của bạn, chạy ở đây tức là load code và lưu nó ở trong memory.
    • Trong khi nginx không cần thiết ở môi trường development, application server bắt buộc ở cả môi trường dev/prod

Rack là gì

Tại sao cần Rack

Nói 1 cách đơn giản, Rack là bridge giữa App server và Rails app. Vì mỗi app server có thể nói 1 ngôn ngữ khác nhau. Nên cần 1 thằng ở giữa để dịch.

Ví dụ:

Puma gửi request object (Array) -> Rack -> Rails -> Rack nhận response object, parse thành Array -> Puma

Unicorn gửi request object (Hash) -> Rack -> Rails -> Rack nhận response object, parse thành Hash -> Unicorn

Viết 1 Rack app đơn giản

class ComplimentApp
  def call(request)
    [200, {'Content-Type' => 'text/plain'}, ["I like your shoes"]]
  end
end
  • Rack app bắt buộc phải có method call
  • ở ví dụ này return 1 Array có 3 phần từ (phù hợp với format của Puma, mỗi App server có những quy định riêng về kiểu trả về, format của kiểu trả về).
    • Puma quy định phần tử thứ nhất là HTTP status code
    • Phần tử thứ 2 là 1 hash, giá trị của HTTP headers
    • Phần tử thứ 3 là 1 object response_to method each, chính là body của response

Rack endpoint

Endpoint (đích đến) của Rack, tức là Rails Application được định nghĩa ở file config.ru

require_relative 'config/environment'
run Rails.application

Rails.application return instance của Rails App class, Rails App class lại được định nghĩa ở application.rb

module PLServer
  class Application < Rails::Application
  end
end

trường hợp này là return instance của PLServer::Application class. PLServer::Application kế thừa từ Rails::Application, một rack App. Rails::Application implement phương thức #call (nói ở trên)

def call(env)
  req = build_request env
  app.call req.env
end

env ở đây không phải biến môi trường ENV, nó chính là request object

Rack Middleware

Là một thành phần trong Rack. Có 2 nhiệm vụ:

  • Sửa env object trước khi được pass vào endpoint tiếp theo
  • Sửa response object trước được pass vào endpoint tiếp theo

Sau khi request đi qua khoảng 200 middleware objects, nó sẽ vào Router

Routing

router dispatch request vào controller và action tương ứng ở bước này thì đã có 1 empty response object được tạo ra rồi

controller_class.dispatch(action, request, response)

Controllers

  • chạy before_action
  • logic trong action
  • view rendering, stored in response.body,
  • after_action

Rời controllers

  • chạy after_action
  • response.to_a convert response object thành một array mà Rack hiểu được

Rời routing

Routing dơn giản là sẽ pass response của controller vào Middleware, trừ khi response.headers include X-Cascade: pass. Khi đó Routing sẽ dispatch response object này sang một App khác chẳng hạn, Vì dụ NodeJS app

Rời Middleware

Middleware có thể sửa status code, headers, body ở phase này. Ví dụ:

  • xóa toàn bộ response body nếu có HTTP caching headers
  • set/edit cookie in response header

Rails as backend, JS framework as frontend

Gỉa sử ta có 2 server, 1 server chạy Rails, serve APIs, một server chạy JS Framwork như là React, VueJS, v.v,

Rails app: api.example.com, Front-end app: front.example.com

Tại frontend server, phải fetch dữ liệu từ backend server thông qua API. Khi đó ta sẽ gặp phải vấn đề Cross-Origin Resource Sharing (CORS)

Để giải quyết vấn đề này, đơn giản nhất là dùng gem:

gem "rack-cors", require: "rack/cors"

Bản chất của gem này là insert một middleware vào đầu tiên trong danh sách middleware stack để modify header của các request từ JS front server.

module MyApp
  class Application < Rails::Application
    config.middleware.insert_before 0, "Rack::Cors" do
      allow do
        origins 'front.example.com'
        resource '/api/v1',
        :headers => :any,
        :expose => ['X-User-Authentication-Token', 'X-User-Id'],
        :methods => [:get, :post, :options, :patch, :delete]
      end
    end
  end
end

Inheritance in Rails

Trong OOP có 4 khái niệm là:

  • Abstraction (trừu tượng)
  • Polymorphism (đa hình)
  • Inheritance (kế thừa)
  • Encapsulation (Bao đóng)

Cách nhớ là: 4 kí tự đầu tiên tạo thành chữ: A PIE

Bài này nói về Tính kế thừa: Inheritance

Trong OOP nói chung

  • Class Con sẽ kế thừa những thuộc tính, phương thức, constants của class Cha, class Ông
  • có thể override
  • super trong method Con sẽ gọi method cùng tên trong class Cha, không tìm thấy thì sẽ tìm đến Ông, v.v

Trong Rails nói riêng

Model kế thừa từ ApplicationRecord ApplicationRecord kế thừa từ ActiveRecord::Base

Encapsulation in Rails

Trong OOP có 4 khái niệm là:

  • Abstraction (trừu tượng)
  • Polymorphism (đa hình)
  • Inheritance (kế thừa)
  • Encapsulation (Bao đóng)

Cách nhớ là: 4 kí tự đầu tiên tạo thành chữ: A PIE

Bài này nói về Tính bao đóng: Encapsulation

Trong OOP nói chung

  • giữ tất cả thuộc tính, giá trị của object nằm trong object
  • viết các phương thức setter, getter cho từng thuộc tính, giá trị chỉ khi có nhu cầu sử dụng

Trong Rails nói riêng

Ứng dụng 1 trong Rails

  • sử dụng attr_reader, attr_writer, and attr_accessor để access vào phương thức, giá trị của object
  • khi chỉ cần read thì không nên sử dụng attr_accessor (có cả quyền read và write)

Ứng dụng 2 trong Rails: Strong params.

private

def dog_params
  params.require(:dog).permit(:name, :weight)
end
  • Không cho phép các params khác được tham gia vào để init object dog, ví dụ color
  • việc private cho method dog_params có thể coi như một phương thức bao đóng

Abstraction in Rails

Trong OOP có 4 khái niệm là:

  • Abstraction (trừu tượng)
  • Polymorphism (đa hình)
  • Inheritance (kế thừa)
  • Encapsulation (Bao đóng)

Cách nhớ là: 4 kí tự đầu tiên tạo thành chữ: A PIE

Bài này nói về Tính trừu tượng: Abstraction

Trong OOP nói chung

Trừu tượng hóa điểm tương đồng của các đối tượng để tạo Class là Base Ví dụ, class Ô tô, Xe máy, Xe đạp kế thừa từ Class Phương Tiện class Chó, Mèo kế thừa từ class Động Vật

Trong Rails nói riêng

class SomeAbstractModel < ApplicationRecord
  self.abstract_class = true

  # some heritable methods here
end

AbstractModel không có table tương ứng, nên không tạo object được

SomeAbstractClass.create!
# => NotImplementedError: (Vehicle is an abstract class and cannot be instantiated)

Cách dễ hình dung nhất về Abstract Class trong Rails là ActiveRecord::Base, nó không có bảng, nó được sử dụng để các Model kế thừa nó, nên các Model có những phương thức nó định nghĩa, như find, where

class AbstractModel < ActiveRecord::Base  
  self.abstract_class = true
end

class Foo < AbstractModel
end

class Bar < AbstractModel
end

# File activerecord/lib/active_record/base.rb, line 610
def find(*args)
  options = args.extract_options!
  validate_find_options(options)
  set_readonly_option!(options)

  case args.first
    when :first then find_initial(options)
    when :last  then find_last(options)
    when :all   then find_every(options)
    else             find_from_ids(args, options)
  end
end

Abstract Class và Module

Xét bài toán sau, có class Car, class Airplain v.v, có chung attribute weight, method convert_weight Nhưng cũng có attribute khác nhau, Car thì có bánh, Airplain thì không có bánh, mà có cánh quạt Thiết kế như nào?

  • Vấn đề của STI: sẽ có cột empty
class Vehicle < ApplicationRecord
  with_options presence: true, allow_blank: false do
    validates :weight
    validates :color
  end

  def convert_weight(unit)
    case unit
    when :lbs
      weight * 2.20462
    when :g
      weight * 1000.0
    end
  end
end

class Car < Vehicle
  validates :number_of_wheels, presence: true, allow_blank: false
end

class Airplane < Vehicle
  validates :number_of_wings, presence: true, allow_blank: false
end

migration cho bảng vehicles

create_table :vehicles do |t|
  # columns required by some children
  t.integer :number_of_wheels
  t.integer :number_of_wings
  # columns required by all children
  with_options null: false do
    t.string  :type # required for inheritence
    t.integer :weight
    t.string  :color
  end
end

Như vậy thì thằng Car thì number_of_wings null, thằng Airplane thì number_of_wheels null. Khi càng có nhiều attribute khác biệt thì càng có nhiều cột null

  • Abstract Class xử lý vấn đề đó như thế nào
class Vehicle < ApplicationRecord
  self.abstract_class = true
  with_options presence: true, allow_blank: false do
    validates :weight
    validates :color
  end
  def convert_weight(unit)
    case unit
    when :lbs
      weight * 2.20462
    when :g
      weight * 1000.0
    end
  end
end

Khác ví dụ trên thì dữ liệu của Car, Airplain sẽ lưu ở bảng cars, airplains tương ứng. Không có bảng vehicles

class CreateVehicles < ActiveRecord::Migration[5.2]
  def change
    create_table :cars do |t|
      with_options null: false do
        t.integer :weight
        t.string  :color
        t.integer :number_of_wheels
      end
    end
    create_table :airplanes do |t|
      with_options null: false do
        t.integer :weight
        t.string  :color
        t.integer :number_of_wings
      end
    end
  end
end

Lưu ý 1: Ở abstract model thì đôi khi chỉ khai báo prototype của method, logic cụ thể được định nghĩa ở Class con

Lưu ý 2: Các phương thức ở Abstract class có thể được đóng gói vào Module

Polymorphism in Rails

Trong OOP có 4 khái niệm là:

  • Abstraction (trừu tượng)
  • Polymorphism (đa hình)
  • Inheritance (kế thừa)
  • Encapsulation (Bao đóng)

Cách nhớ là: 4 kí tự đầu tiên tạo thành chữ: A PIE

Bài này nói về Tính đa hình: Polymorphism

Trong OOP nói chung

Đa hình nghĩa là khi mà 1 phương thức có những hành vi khác nhau

Trong Rails nói riêng

Các giải thích dễ hiểu như sau:

Có một class là Manager, một class là nhân viên Employee

Manager thì đi ô tô. Nhân viên thì đi xe máy. Về bản chất thì đây đều là xe/phương tiện đi lại. Sẽ có những phương thức chung như là: start, drive, stop v.v. Và tất nhiên cách start của xe máy và oto sẽ khác nhau rồi.

Nên về thiết kế thì mình sẽ làm như sau

# table managers
class Manager
  has_many :xes, as: :chu_phuong_tien
end

# table employees
class Employee
  has_many :xes, as: :chu_phuong_tien
end

# table xes
class Xe
  belongs_to :chu_phuong_tien, polymorphic: :true

  def start
    # ở đây ta có thể định nghĩa tuỳ theo đây là oto hay xe máy =))
  end
  def drive; end
  def stop; end
end

khi đó bảng xes thì ta sẽ có các cột:

  t.string  :name
  t.integer :chu_phuong_tien_id
  t.string  :chu_phuong_tien_type

ta cũng có những phương thức tương ứng:

  m = Manager.new
  e = Employee.new

  m.xes
  e.xes

  xe = Xe.new(name: "Teslaaaaa", chu_phuong_tien_id: 1, chu_phuong_tien_type: "Manager")
  xe.chu_phuong_tien # => Manager with id=1

Ruby Public, Private, Protected methods

Public

Public method có thể được gọi bên trong hay bên ngoài class

class Animal
  def intro_animal
    "I am a #{self.class}"
  end
end

Animal.new.intro_animal
# => "I am a Animal"

Private

  • Private method chỉ có thể được gọi bên trong class
  • Khi có sự kế thừa, behaviour không có gì thay đổi
class Animal
  def intro_animal
    class_name
  end
  private
  def class_name
    "I am a #{self.class}"
  end
end

n = Animal.new
n.intro_animal #=>I am a Animal
n.class_name #=>error: private method `class_name' called

Protected vs Private

  • Protected method chỉ có thể được gọi bên trong class, giống Private
  • không gọi được qua self.private_method, nhưng có thể gọi self.protected_method
  • Có sự khác biệt khi có sự kế thừa
class Animal
  def animal_call
    protect_me
  end
  protected
  def protect_me
    p "protect_me called from #{self.class}"
  end  
end

# giống Private
n= Animal.new
n.animal_call #=> protect_me called from Animal
n.protect_me #=>error: protected method `protect_me' called

class Mammal < Animal
  def mammal_call
    protect_me
  end
end

# giống Private
n= Mammal.new
n.mammal_call #=> protect_me called from Mammal


class Amphibian < Animal
  def amphi_call
    Mammal.new.protect_me #Receiver same as self
    self.protect_me  #Receiver is self
  end   
end

# Khác Private, có thể gọi thông qua self, lưu ý: Mammal.new same as self
n= Amphibian.new
n.amphi_call #=> protect_me called from Mammal
             #=> protect_me called from Amphibian  


class Tree
 def tree_call
   Mammal.new.protect_me #Receiver is not same as self
 end
end

# Vì Tree không kế thừa nên Mammal.new khác self
n= Tree.new
n.tree_call #=>error: protected method `protect_me' called for #<Mammal:0x13410c0>

Send methods

private, protected methods có thể gọi thông qua method send

class Animal
  def intro_animal
    class_name
  end
  private
  def class_name
    "I am a #{self.class}"
  end
end

n = Animal.new
n.intro_animal #=>I am a Animal
n.class_name #=>error: private method `class_name' called
n.send("class_name") #=> I am a Animal
n.public_send("class_name") #error: private method `class_name' called

Trong Rails

  • Ở controller, chỉ các action (index, new, create v.v) nên là public methods, tất cả nên là private
  • Ở model, protected methods hữu ích khi class B, class C đều kế thừa từ class A. Ở class C bạn muốn gọi method protected với receiver là class B

Ruby Strings vs Symbols

Định nghĩa

"hello" # String
:hello # Symbol

"hello world!" # String with space and bang!
"hello world!".to_sym # String can convert to Symbol and vice versa
# => :"hello world!"
:"hello world!".class
# => Symbol
# Vậy là Symbol cũng có thể chưa kí tự đặc biệt

Symbol can’t change while mutable String can

a = "hello"
# => "hello"
b = :hello
# => :hello
a.object_id
# => 47213907316480
b.object_id
# => 1150428
a << "w"
# => "hellow"
a.object_id
# => 47213907316480
b << "w"
# NoMethodError: undefined method `<<' for :hello:Symbol

Frozen Strings: immutable String

example = "hello world"
example.upcase!

puts example

example.freeze
example.downcase!

# => "HELLO WORLD"
# => *.rb:7:in `downcase!': can't modify frozen string (TypeError)

Object ID

puts "hello world".object_id
puts "hello world".object_id
puts "hello world".object_id
puts "hello world".object_id
puts "hello world".object_id

# => 3102960
# => 3098410
# => 3093860
# => 3089330
# => 3084800

puts :"hello world".object_id
puts :"hello world".object_id
puts :"hello world".object_id
puts :"hello world".object_id
puts :"hello world".object_id

# => 239518
# => 239518
# => 239518
# => 239518
# => 239518

Performance

  • Khai báo Symbol nhanh hơn 40% so với khai báo String
  • So sánh Symbol vs Symbol nhanh hơn 40% so với so sánh String vs String

Ruby Constants

Định nghĩa

  • Viết hoa chữ cái đầu (nhưng thường được viết hoa tất cả)
  • Không thể định nghĩa trong method, chỉ bên trong Class, Module
class RubyBlog
  URL    = "rubyguides.com"
  AUTHOR = "Jesus Castello"

  def the_method
    # ABC = 1 => Lỗi "dynamic constant assignment"
  end

  p RubyBlog::AUTHOR # "Jesus Castello"
end

Tính bất biến

giá trị của constants có thể thay đổi, nhưng sẽ hiện cảnh báo tốt nhất là nên freeze object trước khi assign freeze khiến bạn không thể thay đổi giá trị của ô nhớ, hữu ích khi object là String, Array, Hash

ABC = 1
ABC = 2
# 2: warning: already initialized constant ABC
ABC = 2.freeze
ABC = 1

puts ABC
# => 1

Minh = "Minh".freeze
Minh << "dep trai" # RuntimeError: can't modify frozen String
#

Scope

# Ví dụ về kế thừa
class A
  FOO = 1
end

class B < A
  def foo
    puts FOO
  end
end

B.constants # [:FOO]
B::FOO # 1

# Ví dụ 
module Food
  STORE_ADDRESS = "The moon"
  class Bacon
    def foo; puts STORE_ADDRESS; end
  end
end
fb = Food::Bacon.new
fb.foo # "The moon"

Food::STORE_ADDRESS
# => "The moon"

# STORE_ADDRESS không phải constant của Food::Bacon
Food::Bacon::STORE_ADDRESS
# NameError: uninitialized constant Food::Bacon::STORE_ADDRESS

module Mixin
  A = 123
end
class Product
  include Mixin
  puts A
end
# 123

class OtherProduct
  extend Mixin
  puts A
end

#  uninitialized constant OtherProduct::A

module Parent
  VALUE = "Parent"
  def print_value
    VALUE
  end
end

class Child
  include Parent
  VALUE = "Child"
end
# warning: already initialized constant Child::VALUE
# warning: previous definition of VALUE was here

Child::VALUE
# => "Child"
Child.new.print_value
# => "Parent"


class A
  FOO = 1
end
class A::B
  class C
    puts FOO
  end
end
# NameError: uninitialized constant A::B::C::FOO

class A
  class B
    class C
      puts FOO
    end
  end
end
# 1

Happy 420

#happy420 #happybirthday Happy 420

Hash, Array, Set in Ruby

  Array Hash Set
index dùng interger để indexing
ví dụ 0,1,2
-1 là last item
-2 là item kế item -1
dùng object_id của key để indexing  
key index: -2,-1,0,1,2 key là cái đéo gì cũng được
từ int, string, symbol, object, class v.v
không có key
access element array[index]
array[0]
hash[key] hash[“key_name”] hash[:key_name] không thể access được 1 object cụ thể trong set
vì không có key
key unique? tất nhiên tất nhiên không có key
nếu key không tồn tại nil nil
có thể custom default value
ví dụ:
a = Hash.new(0) a[:some_key] # => 0
không có key
value unique? không không , không tồn tại set mà chứa (1, 1)
lookup slow fast fastest
       

Modules in Ruby: include, extend, prepend

  • include, prepend các hàm định nghĩa trong module trở thành instance method
  • extend các hàm định nghĩa trong module trở thành class method
module A
  def say
    puts "A"
  end
end

module B
  def say
    puts "B"
  end
end

module C
  def say
    puts "C"
  end
end

module D
  def say
    puts "D"
  end
end

module E
  def speak
    puts "speak in E"
  end

  def talk
    puts "talk in E"
  end
end


class Test
  include A # đưa module A vào ngay bên phải class Test trong danh sách ancestors
  include B # đưa module B vào ngay bên phải class Test trong danh sách ancestors
  prepend C # đưa module C vào đầu tiên trong danh sách ancestors
  prepend D # đưa module D vào đầu tiên trong danh sách ancestors
  extend E # dduwa module E vào ngay bên phải singleton class Test

  def self.talk
    puts "talk in Test"
  end
end

Test.ancestors # [D, C, Test, B, A, Object, Kernel, BasicObject]

test = Test.new
test.say # D
# Lý do là tìm hàm theo thứ tự từ trái qua phải trong [D, C, Test, B, A, Object, Kernel, BasicObject]

test.speak
# undefined method

Test.speak # speak in E
Test.talk # talk in Test

Test.singleton_class
# => #<Class:Test>
Test.singleton_class.ancestors
# => [#<Class:Test>, E, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

Instance variable in Rails

Các loại biến

Loại biến ví dụ độ phủ sóng default
global variable $name tất cả mọi nơi nil
class variable @@name all object, class con undefined
instance variable @name object self nil
local variable name method undefined

Định nghĩa instance variable

  • Tên biến: bắt đầu bằng @, đi sau là tên ví dụ @total_income
  • Phạm vi: bất kể đâu trong class, bất kì object nào được tạo từ class
  • Intance variable không public, không dùng được ngoài object
  • Giá trị mặc định nếu chưa được init là nil

Nơi sử dụng

Module vs Class in Ruby

  class module
có thể tạo object? không
dùng để?? để tạo ra object
để class khác kế thừa
gộp các method lại, để tái sử dụng cho class làm namespace cho class
superclass là gì? Object không có
có hàm gì? class method instance method module method instance method
kế thừa được không? không
có thể include? không có, include module trong class
có thể extend? không có, extend module trong class
  • class include module thì object sẽ có các methods được định nghĩa trong module, instance method
  • class extend module thì methods sẽ đc extend như một class method

cụ thể hơn thì xem thêm ở bài Modules in Ruby: include, extend, prepend

module A
  def a_method
    puts "a_method"
  end
end

module B
  def b_method
    puts "b_method"
  end
end

class Human
  include A
  extend B
end

Human.new.a_method
# a_method

Human.new.b_method
# NoMethodError: undefined method `b_method' for #<Human:0x00559a416bc4f8>

Human.a_method
# NoMethodError: undefined method `a_method' for Human:Class

Human.b_method
# b_method

Setter & Getter in Ruby

Setter và Getter là gì

  • Setter là method dùng để gán giá trị cho instance variable
  • Getter là method dùng để lấy giá trị của instance variable

Setter và Getter tự định nghĩa

class Cat
  def initialize(name)
    @name = name
  end
end

cat = Cat.new("Mimi")
cat.name #=> undefined method `name'
cat.name = "Mymy"
cat.name #=> undefined method `name='
class Cat
  def initialize(name)
    @name = name
  end

  def name #getter method
    @name
  end

  def name=(name) #setter method
    @name = name
  end
end

cat = Cat.new("Mimi")
cat.name #=> "Mimi"
cat.name = "Mymy"
cat.name #=> "Mymy"

Setter và Getter sử dụng accessors

class Cat
  attr_reader: :name # creates the getter methods
  attr_writer: :name # creates the setter methods
  attr_accessor: :name # creates both setter & getter methods

  def initialize(name)
    @name = name
  end
end

cat = Cat.new("Mimi")
cat.name #=> "Mimi"
cat.name = "Mymy"
cat.name #=> "Mymy"
  attr_reader attr_writer attr_accessor
methods được tạo ra name name= name
name=