Software engineering notes

Ruby Basics

變數

以此例為例子

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

t = Var.new
t.print

替換以下變數

結論 : 定義在 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}

定義 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

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]

Data process for Array or Hash

Extract columns from an array of objects and make them an array

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

only preserve specific columns from an array of objects and make them an array

example

[{id: 1, name: 'xxx', ….}, {id: 2, name: 'ccc', ….}]
=>
[{id: 1, name: 'xxx'}, {id: 2, name: 'ccc'}]

code

@rooms.map { |room| [room.id, room.plan] }

only preserve specific columns from objects of a hash by key

example

{
  "AAA": [{id: 1, name: 'one', age: 25}, {id:2, name: 'one', age: 25}],
  "BBB": [{id: 3, name: 'three', age: 30}, {id:4, name: 'three', age: 30}]
}
=>
{
  "AAA": [{id: 1}, {id:2}],
  "BBB": [{id: 3}, {id:4}]
}

code

h.transform_values { |arr| arr.map { |room| {id: room.id} }  }

Count items for each key of a hash

example

{"AAA": [RoomA, RoomB], "BBB": [RoomA]}
=>
{"AAA": 2, "BBB": 1}

code

data.transform_values { |value| value.size }

json

string to json

JSON.parse(params[:JSONData])

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

Date format

Symbol

語法

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 分鐘

yield

example

class TT
  attr_accessor :name

  def initialize(name)
    @name = name
  end
end

def test(tt)
  qq(tt) do |c|
    puts 'c'
    tt.name = '456'
    puts 'd'
  end
end

def qq(tt)
  puts 'a'
  puts tt.name
  yield tt
  puts 'yield'
  puts tt.name
  puts 'b'
end

tt = TT.new('123')
test(tt)

result

a
123
c
d
yield
456
b

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!"

Rake

Rake 是像 C 語言的 Make 工具, 但是用 Ruby 寫的, 主要是用來執行預先寫好的腳本語言

依不同環境跑 tasks

lib/tasks/tasks.rake :

namespace :tasks do
  namespace :create do
    desc "Create all survey templates"
    task :all => [:task1, :task2]

    desc "desc1"
    task :task1 => :environment do
        Rails.logger.info(1)
    end

    desc "desc2"
    task :task2 => :environment do

    end
  end
end

lib/tasks/dev.rake :

namespace :dev do
  desc "Rebuild system"
  task :build => ["tmp:clear", "log:clear", "db:drop", "db:create", "db:migrate"]
  task :rebuild => [ "dev:build", "db:seed" ]
end

ref: 參考這裡