Jex’s Note

Ruby Basic

變數

以此例為例子

class Var
  def print
    puts $hh
  end
  $hh = "hh"
end

t = Var.new
t.print

替換以下變數

  • $name : 全域變數. 結果: 正常
  • @name : 實例變數, 作用僅限於 self 指示的物件. 結果: 相當於輸出 nil, 什麼東沒有
  • @@name : 類別變數, 在 class 內使用, 如果另個物件繼承它的物件, 也可以使用 @@name. 結果: 正常
  • name : 區域變數 (小寫字母或底線開頭, 初始化前並沒有 nil 值). 結果: undefined local variable or method hh'
  • Name : 常數 (大寫開頭, 不可重覆定義). 結果: 正常

結論 : 定義在 class 內的變數必須是 全域, 類別或常數

宣告

陣列

%w(i1 b2 c3 j4)                 # ["i1", "b2", "c3", "j4"]

arr = {                         # {:A => 1, :B => 2 }
    'A' : 1,
    'B' : 2,
}

基本 class 觀念

class Foo

  def instance_method                           # instance method   (可以用 self 取 instance 的值)
    'instance_method 要先 new 才能使用'
  end

  def self.class_method                         # class method (無法用 self 取內部的值)
    'slef.class_method 不能 new, 可直接使用'
  end

  def call_method
    inside_method
  end

  protected

  def inside_method
    'Call 內部的 inside_method 不需加 `self.` prefix'
  end

end

a = Foo.new
puts a.instance_method      # instance_method 要先 new 才能使用
puts Foo.class_method       # slef.class_method 不能 new, 可直接使用
puts a.call_method          # Call 內部的 inside_method 不需加 `self.` prefix

判斷

0 及 empty 都是 TRUE, 只有 false 與 nil 才是 FALSE

是否為數值

9.9.integer?

數值是否在範圍裡面

(11..15).include? 15
15.between?(11, 15)

檔案相關

Check directory in existence, Create folder

require 'fileutils'
if File.directory?('/tmp/layer1')
  puts "Folder exist !"
else
  puts "Create folder ..."
  FileUtils.mkpath('/tmp/layer1/layer2')
end

Check if a file exists

File.exist?('/tmp/ruby_test/send/sorry.mp3')

Getting path from full file path

2.0.0-p247 :001 > File.dirname("/tmp/ruby_test/send/qq.php")
 => "/tmp/ruby_test/send"

Getting filename from path

2.0.0-p247 :003 > File.basename("/tmp/ruby_test/send/qq.php")
 => "qq.php"

Integer & Float

小數第一位四捨五入

a = 633.633
a.round(1)     # 633.6

次方

2 ** 3

絕對值

-5.abs      # 5

取最近的整數/四捨五入

5.6.round   # 6

取整數/無條件捨去

9.9.floor   # 9

取整數/無條件進位

2312.22.ceil    # 2313

下一個數

2.next      # 3

二元運算

n & num
n | num
n ^ num (XOR)
n << num (向左位移)
n >> num (向右位移)

Rand

rand(1..10)
[true, false].sample

亂數

SecureRandom.random_number(99999999999)             # 9997979524

建立 UUID

require 'securerandom'                  # 原生
SecureRandom.hex(20)                    # ac5d23f916dd83dcc495dc5f0b8b602942b635fa    長度會是你傳值的 2 倍
SecureRandom.base64(20)                 # WcLJKJzgibphbiXHKIeCsP8jtN8=

SecureRandom : This library is an interface for secure random number generator which is suitable for generating session key in HTTP cookies

字串處理

gsub

"hello".gsub(/[aeiou]/, '*')                    # "h*ll*"
"hello".gsub(/([aeiou])/, '<\1>')               # "h<e>ll<o>"
"hello".gsub(/./) {|s| s.ord.to_s + ' '}        # "104 101 108 108 111 "
"hello".gsub(/(?<foo>[aeiou])/, '{\k<foo>}')    # "h{e}ll{o}"
'hello'.gsub(/[eo]/, 'e' => 3, 'o' => '*')      # "h3ll*"

split

'C3-0803986E423F0C66DA56'.split('-')            # ["C3", "0803986E423F0C66DA56"]

last word

'example'.last                                  # e

取固定位置及長度的字串

a = "1234567890"
a[1..3]  # 234

Hash

key 是否存在

genders.has_key?(:male)

value 是否存在

genders.value?(2)

刪除某個 key

a.delete('es')

刪除某個 value

hash.delete_if{|_,v| v == "2"}

Reverse key <-> value

Hash[h.to_a.collect(&:reverse)]

Find value and return key

h.key(value)    # return key

Get all keys from Hash

h.keys

Sort

hash = {f: 4, a: 2, r: 1 }
hash.sort # => [[:a, 2], [:f, 4], [:r, 1]]          # 這不是我們要的結果
hash.sort.to_h                                      # => {:a=>2, :f=>4, :r=>1} , 這才是

兩個 hash 合併另種寫法

a = {"zh"=>1, "es"=>5}
b = {"zh"=>1, "qq"=>5}
a.merge(b)                  # {"zh"=>1, "es"=>5, "qq"=>5}

# 這個範例可以看的出它只比對 key,如果 key 一樣值不一樣,則會被後者的值覆蓋
a = {"zh"=>1, "es"=>5}
b = {"zh"=>1, "es"=>7}
a.merge(b)                  # {"zh"=>1, "es"=>7}

只取得 hash 裡面某些 key -> value

{a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
{:a=>1, :b=>2}

Array

是否為 array

array.is_a?(Array)

去除相同的值 :

[2,4,2,5,1].uniq            # => [2, 4, 5, 1]

Array to Hash

a = ['apple', 'banana', 'mongo']
c = Hash[a.map { |e| [e.to_sym, e + ' !'] }]
 => {:apple=>"apple !", :banana=>"banana !", :mongo=>"mongo !"}

a = [["Bob", 1], ["Jex", 2], ["Jxx", 3]]
Hash[a.map { |e| [e[0].to_sym, e[1]]}
 => {:Bob=>1, :Jex=>2, :Jxx=>3}

刪除 each 判斷

a.delete_if { |x| x >= 3 }

a.delete_if do |v|
  if v >= 3
    true                # Make sure the if statement returns true, so it gets marked for deletion
  end
end

array’s value to symbol

array.map { |x| x.to_sym }
或
array.map &:to_sym

值是否存在

a = [1,2,3]
a.include?(2)

多項是否存在, 只要其中一項存在就是 true
([2, 6, 13, 99, 27] & [5,6]).any?

找出值旳 index

array.find_index(90)

兩個 array 合併

a.zip(s).flatten.compact
或
s.inject(a, :<<)

add

會改變原值
a.push("d", "e", "f")
a << 'x'

不會改變原值
a + [3]

delete

會改變原值
a = [2,4,6,3,8]
a.delete(3)

不會改變原值
a - [3]

remove duplicate elements

array = array.uniq

join array (無值就 delete value)

my_array.delete_if(&:empty?).join(',')

ordre ASC

my_array.sort

order DESC

my_array.sort.reverse

order 某個欄位

my_array.sort_by &:lastname

associations 關聯,如果想刪除其中某一筆

items.each do |i|
  if i.product.nil?
    needed_remove << i  # 先放進待刪除區
  end
end
items - needed_remove

# 以下看起來會 work ,但實際上最後的 items 還是跟原始的一樣,也就是 delete 沒有在原本的 items 移除該項目
items.each do |i|
  if i.product.nil?
    items.delete(i)
  end
end
items

insert 一筆到最前面

[1,3,6].unshift(5)              # [5, 1, 3, 6]

json

string to json

JSON.parse(params[:JSONData])

each

一行

cats.each(&:name)
cat_names = cats.map(&:name)            # 相當於 each cat.name
cats.each do |cat| cat.name end
cats.each {|cat| cat.name }

Call Dynamic method name

Sometimes you might need a dynamic method, you can use send

send

def test
  puts 'test~'
end

send('test')

前面也可以接 instance 以及可以多個 method 一起執行

User.send('smart').send('honest')           # 等於 User.smart.honest

傳遞參數

User.send('smart', iq: 130)

只 call public method

User.public_send(:name, "Jex")

method call

程式碼比較多而且不能串多個 method

> a = "my_string"
> meth = a.method("size")
> meth.call() # call the size method
=> 9

型態

Print variable type

3.class
 => Fixnum
3.class.superclass
 => Integer

加密

Digest::SHA256.digest 'message'         # "\xABS\n\x13\xE4Y\x14\x98+y\xF9\xB7\xE3\xFB\xA9\x94\xCF\xD1\xF3\xFB\"\xF7\x1C\xEA\x1A\xFB\xF0+F\fm\x1D"

Digest::SHA256.hexdigest('message')     # ab530a13e45914982b79f9b7e3fba994cfd1f3fb22f71cea1afbf02b460c6d1d

Progress bar

簡易

10.times{|i| STDOUT.write "\r#{i}"; sleep 1}

1~100%

progress = 'Progress [ '
1000.times do |i|
  j = i + 1
  if j % 10 == 0
    # 將 = 加到 progress string
    progress << "="

    # 將游標移到這行的最前面
    print "\r"

    # 取代目前這一行, 並輸出進度
    print progress + " #{j / 10} % ]"

    # flush buffer 立即顯示
    $stdout.flush
    sleep 0.05
  end
end
puts "\nDone!"

定義 hash parameters

def instance_method(hash = {})
Call : a.instance_method aa: 'aa', bb: 'bb'

hash key 使用 integer 時注意 :

hash = {1: 'one'} # will not work
hash = {1 => 'one'} # will work

Date format

Symbol

  • %Y: 2017
    • %C : 20 (year / 100)
    • %y : 17 (year % 100)
  • %B : January
    • %^b : JANUARY
  • %b : Jan 也等於 %h
    • %^b : JAN
  • %m : 01..12 with zero-padding
    • %_m : 1..12 with blank-padding
    • %-m : 1..12 without balnk-padding
  • %d : 01..31 with zero-padding
    • %e : 1..31 with blank-padding
    • %-d : 1..31 without balnk-padding
  • %j : 001..336 day of the year
  • %H : 00..23 with zero-padding
  • %k : 0..23 with blank-padding
  • %I : 01..12 with zero-padding
  • %l : 1..12 with blank-padding
  • %P : am / pm
  • %p : AM / PM
  • %M : 00..59 minute
  • %S : 00..59 second
  • %L : 000..999 millisecond
  • %N = 9 digits nanosecond
    • %3N = 3 digits millisecond
    • %6N = 6 digits microsecond
    • %9N = 9 digits nanosecond
    • %12N = 12 digits picosecond
  • %z : +0900
    • %:z : +09:00
    • %::z : +09:00:00
    • %:::z : +09:30:30
    • %Z : CST time zone abbreviation name
  • %A : Sunday
    • %^A : SUNDAY
    • %a : Sun
    • %^a : SUN
    • %u : 1..7 day of the week (monday is 1)
    • %w : 0..6 day of the week (sunday is 0)
  • %G : 2016 the week-based year
    • %g : 16 the last 2 digits of the week-based year
    • %V : 52 Week number of the week-based year
  • %U : 00..53 week number of the year. starts with sunday
    • %W : 00..53 starts with mondy
  • %s : 1483249934 timestamp, number of seconds since 1970-01-01 00:00:00 UTC
  • %n : \n
    • %t : \t
    • %% : %
  • %c : Sun Jan 1 13:54:29 2017 date and time (%a %b %e %T %Y)
    • %D = %x : 01/01/17 Date (%m/%d/%y)
    • %F : 2017-01-01 The ISO 8601 date format (%Y-%m-%d)
    • %v : 1-JAN-2017 VMS date (%e-%b-%Y)
    • %r : 01:56:03 PM 12-hour time (%I:%M:%S %p)
    • %R : 13:56 24-hour time (%H:%M)
    • %T = %X : 13:55:44 24-hour time (%H:%M:%S)

語法

Time.now                                        # 2016-04-07 11:04:09 +0800
Time.now.getutc                                 # 2016-04-07 03:05:00 UTC
Time.now.strftime('%F %T')                      # 2015-08-10 21:02:04
Time.now.strftime('%F %R')                      # 2015-09-21 01:04
Time.now.strftime("%Y-%m-%d %H:%M:%S")          # 2015-08-10 21:01:06
Time.now.in_time_zone                           # Tue, 22 Sep 2015 09:27:56 CST +08:00    如果 config 有設定 timezone
Time.now.to_i                                   # 1483249426 (timestamp)

String 轉回 Time

Time.parse("2015-09-21 12:00")
或
"2015-09-21 12:00".to_time

2 天前 (rails)

2.days.ago

2 天後 (rails)

2.days.since

現在 DateTime 減 5 天

Post.find(12).update(created_at: Time.now.strftime('%F %R').to_time - 5.days)

5 天後

Date.today + 5
Date.today + 5.days

10.days.from_now

date 相減

require 'date'
> now = Date.today
> before = Date.today + 2.days
> difference_in_days = (before - now).to_i

-----
start_date = Date.parse "2012-03-02 14:46:21 +0100"
end_date =  Date.parse "2012-04-02 14:46:21 +0200"
(end_date - start_date).to_i

Add time to date

d = Date.new(2012, 8, 29)
t = Time.now
dt = DateTime.new(d.year, d.month, d.day, t.hour, t.min, t.sec, t.zone)
或
Date.new(2015, 2, 10).to_datetime + Time.parse("16:30").seconds_since_midnight.seconds

Today

Date.today

Year

Time.now.year

顯示時間語意

# console 要引入 include ActionView::Helpers::DateHelper
time_ago_in_words(3.minutes.from_now)                       # => 3 minutes
time_ago_in_words(Time.now - 15.hours)                      # => 15 hours
time_ago_in_words(Time.now)                                 # => less than a minute
from_time = Time.now - 3.days - 14.minutes - 25.seconds     # => 3 days
distance_of_time_in_words('2015-09-16'.to_date, Date.today) # => 不到 1 分鐘

Gem error

Gem::FilePermissionError

$ gem install sitemap_generator
ERROR:  While executing gem ... (Gem::FilePermissionError)
    You don't have write permissions for the /usr/local/rvm/gems/ruby-2.2.1 directory.

使用 sudo 安裝也不行

sudo: gem: command not found

解決方法 : 切換到 sudo 加上 gem 相關的 PATH

$ $PATH
-bash: /usr/local/rvm/gems/ruby-2.2.1/bin:/usr/local/rvm/gems/ruby-2.2.1@global/bin:/usr/local/rvm/rubies/ruby-2.2.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

切換到 sudo, 在 .bashrc 最後一行加上

export PATH=$PATH:/usr/local/rvm/gems/ruby-2.2.1/bin:/usr/local/rvm/gems/ruby-2.2.1@global/bin:/usr/local/rvm/rubies/ruby-2.2.1/bin

再重新登入, 完成!

Gem::LoadError: You have already activated rake ...

Gem::LoadError: You have already activated rake 12.0.0, but your Gemfile requires rake 11.3.0.

1) Check your Gemfile to see if rake exists. If yes, upgrade it.

2) If no, execute bundle update rake

Comments