Ruby on Rails
为用Ruby
程序语言写的开源网页框架,Rails
的发明者DHH
挑选了Ruby
做为Rails
的程序语言。Rails
在2004年发布以後的短短的时间内就迅速获得很多开发人员欢迎,这都归功於MVC
架构,以及Rails
惯例优於设定的特性(使用Rails
应用程序的开发者不用了解太多Knowhow
,只需遵循着惯例即可开发网站)。直到其他MVC
网页框架出现以前,Ruby on Rails
以第一个拥有MVC
框架红极一时。
由於Ruby
为直译语言的缘故,有速度比较慢的诟病,又加上不像Python
後期发展成多领域的语言。Ruby
发展至今仅作为网页、外挂、韧体用途,近几年逐渐式微。不过我并不这麽觉得Ruby on Rails
会那麽快的凋零,如人饮水;冷暖自知,只有写过Ruby
的人才知道Ruby
的好,加上Ruby
一直有广大的社群,加上Rails
至今仍不断地进步。
在这个月的IT铁人比赛进行的同时,DHH
同时发表了Rails7
,以及全新Javascript
载入方式esbuild
。明年的IT铁人赛
也想要用全新的Rails7
介绍我的新专案。
以下为9月与Rails7
的发布影片
我们在Rails以前,会先花时间介绍Ruby
程序语言。Day1-Day17
会着重介绍Ruby
程序语言,在Day18
以後才会从画面开始切入Rails
应用程序。
在正式切入主题以前,首先想要跟读者们介绍Ruby
程序语言的特色。
Ruby
发明的宗旨就是希望程序设计师能够用人类的语言写程序。比起一般的程序语言,Ruby
的写法更加风雅,举凡下面的例子来的写法都很风雅,也不失死板,汉汉老师自己也是看上这一点才决定入坑Rails
。
1.day.from_now # 从现在开始往前推1天
Datetime.now.tomorrow # 明天
Order.first # 第一张订单
Product.last # 最後一个商品
2.even? # 2是偶数吗?
nil.nil? # nil是nil吗?
由於大家对於Ruby
的刻板印象就是写法很奔放,就会误会Ruby
微弱型别的语言,但其实Ruby
是一个强型别的语言。我们拿Javascript
的某例与Ruby
做对比。
在Javascript
的世界中,不同型别的2个东西可以互加!而由於Javascript
弱型别的特性,使得开发过程中常会延伸出难以找错的问题,因此後来衍伸了Typescript
严格定义了型别。不过TypeScript
最後也会被编译成Javascript
,仍不会改变JS
为弱型别语言的事实。
null + 1 // 1
undefined + 1 // NaN
在Ruby
的世界中,可不允许两个不同型别的物件互加。举例来说,当我打 1 + nil
,Rails
会 告诉我不同型别的不能加起来。
1 + nil
Traceback (most recent call last):
TypeError (nil can't be coerced into Integer)
不过,在Ruby
的世界中,下列的写法是合法的。若太常使用型别的强制转换,一样会造成日後侦错的困难。
汉汉老师就很常因此深受其困扰。
price = nil
1 + price.to_i #=> 1
题外话,写程序久了发现,不是每一个会引发错误的地方都要写Rescue/Exception
,有时候该让它坏的地方还是要让它坏掉,才能够告诉使用者不能这样操作。
当网站上了正式站以後,专案还是会有一些方式,能够将原本会跑坏掉的程序码救回来,例如说跳转到500
错误状态等等。
多重继承会使物件复杂度上升,然而这些状况在Ruby
程序语言不会遇到。
Ruby
为单一继承的语言,若要实现多重继承,可以使用mixin
的方式来达成多模组的继承,而我们会在Day14讲解关於mixin
的继承方式。
Ruby
的世界里面,除了block
以外,所有的东西都是物件。 包括nil
也是在NilClass
产生的物件。
Block
为Ruby
语言的一大特色。因为Block
的缘故,使得Ruby
容易发展出DSL
语言,例如Rails
的routes
, RSpec
,以及Sinatra
,都是因为Ruby
特有的block
特性衍生出其DSL
语言。由於这系列的文章的对象是针对初阶工程师,因此不会讲关於DSL
的细节,读者们若有兴趣可以参阅这篇Toptal发表的文章
我们会在 Day8 正式介绍block
。block
是汉汉老师非常喜欢的主题,也是许多工程师拿来做面试或讨论的议题。
在详细的介绍Ruby
程序语言以前,先介绍Ruby
的一些概念和基本语法
我们要介绍的第一个用法是用来判断类别的方法。首先我们先讲is_a?
, kind_of?
。
is_a?
和 kind_of?
的用法一样,都是判断是哪一种类别。举例来说,若A
类别继承阵列,则 A.is_a?(Array)
会回传true
。被include
的mixin
也会被 is_a?
判断为true
我们举一个实际的例子来作为使用is_a?
的 demo,以下为简单判断型别,并依据型别来判断使用何种结果的方法。
def analyse_data(data)
return unless data.is_a? Array || data.is_a? String
data.is_a?(Array) ? "用阵列的处理方法" : "用字串的处理方法"
end
analyse_data([1, 2, 3]) #=> "用阵列的处理方法"
另外,instance_of?
则不会判断上层被继承或mixin
为true
,举例来说,如果A
继承了Array
,拥有了阵列的特性,但instance_of?
不会判断A
为阵列
A.is_a? A # true
A.is_a? Array # true
A.instance_of? Array # false
以下用实例介绍基本的 ||
and &&
的用法。
false || 1 #=> 1
false && 1 #=> false
nil || 1 #=> 1
nil && 1 #=> nil
true || 1 #=> true
true && 1 #=> 1
需注意,若回传值的话,我们需要多留意使用&&
或||
回传的结果为何。除此之外,逻辑运算子很好用,有些情况甚至可以代替if else
。接着我们用第二个例子来介绍 ||
and &&
的用法
def foo
'foo'
end
# nil 或 false
nil && foo #=> nil
nil || foo #=> "foo"
false && foo #=> false
false || foo #=> "foo"
# true 或存在 instance
true && foo #=> "foo"
true || foo #=> true
'abc' && foo #=> "foo"
'abc' || foo #=> "abc"
看完以上例子之後,我们来说明 ||
and &&
的使用情境
⭐️||
的使用情境:
我的房间的衣架上有挂毛巾、门口也又挂毛巾、柜子旁也有挂毛巾,我的习惯是会优先拿衣架上的毛巾,没有的话会去门口拿,门口再没有的话我才会拿柜子旁的毛巾。上述的情境用程序码可以表示成
tower = from_hanger || from_door || from_cabinet
实际上会用的情境可以是在多元登入,当我们用帐号搜寻不到帐号,改用电子信箱搜寻,若用电子信箱搜寻不到则改成使用电话号码之类
⭐️ &&
的使用情境:
初会学分拿到了才能修中会,中会修完才能修高会。所以如果elementary_accounting
为没有资料( nil
),系统就被被挡修。
elementary_accounting && medium_accounting && advanced_accounting
实务上,汉汉老师比较少使用 blank?
, empty?
,大部分的问题用nil?
, present?
就可以应付,这边先不做介绍了。我们举下列来说明,并且举例以前,先假设parse_data
为某一种解析资料的方法,这里强调的是nil?
, present?
的使用方式,所以我们先不理会parse_data
的功能是在做什麽。
以下三种的写法相等
# 反向的写法语意较不好
unless instance.nil?
instance.parse_data
end
# 清楚明了
if instance.present?
instance.parse_data
end
# 清楚明了
instance.present? && instance.parse_data
接着我们讨论上面三者用法的差别
# present? 只会回传 true/false
'a'.present? #=> true
nil.present? #=> false
[].present? #=> false
''.present? #=> false
# presence 会将 false, [], nil, '' 转为 nil,因此可以拿来回传值。
false.presence #=> nil
[].presence #=> nil
nil.presence #=> nil
''.presence #=> nil
⭐️presence
於画面上的应用:
➡️ 假设退货单没有填写退货原因,则不显示。若用present?
则会显示false
= tag.div return_order.failed_reason.presence
➡️ 下列例子是使用 presence
调整css,若亲自取货按钮不在,则将列印明细按钮写入margin-left: auto;
。
我们会在Day19介绍margin: auto
的用法。使用presence
搭配&&
判断的在於若显示!sub_order.can_pickup?
为true
,则class
上面也会显示ml-auto
,反之如果!sub_order.can_pickup?
为false
,则会显示nil
,并且['btn btn-outline-primary mr-2', nil].compact
为['btn btn-outline-primary mr-2']
,再透过join(' ')
会变为'btn btn-outline-primary mr-2'
- if sub_order.can_pickup?
= link_to '亲自取货', '#',
class: 'btn ml-auto btn-warning mr-2',
data: { confirm: "确定吗?" }
= link_to '列印明细', '#',
class: ['btn btn-outline-primary mr-2', (!sub_order.can_pickup?.presence && 'ml-auto')].join(' ')
⭐️ presence_in
的用法为筛选阵列的值
'q'.presence_in %w(q w e) #=> "q"
'a'.presence_in %w(q w e) #=> nil
搭配||
可以设定预设值
'a'.presence_in %w(q w e) || 'b' #=> b
&
在Ruby的用途很多,以下列出三种含有&符号的使用用法
Proc
与 Block
➡️ Day8介绍nil.name# Traceback (most recent call last):# NoMethodError (undefined method `name' for nil:NilClass)nil&.name#=> nil
我们也可以使用 try
用法来写,不过try
表示法比较冗就是了。
nil.try(:name)#=> nilnil&.name#=> nil
有趣的是,javascript
後来衍伸了很像的写法,表示法为?.
null.name// Uncaught TypeError: Cannot read property 'name' of nullnull?.name// undefined
⭐️ 与 &&
不一样,a && b
的意思为,若a
为不为nil, false
,则回传b
值
[1,3] & [1,2]#=> [1][1,3] && [1,2]#=> [1, 2]
object_id
在Ruby
语言为重要概念,虽然没那麽实用,但object_id
可以用来解释Ruby
存取记忆体的位置。
a = "a"#=> "a"b = a#=> "a"b.object_id#=> 70126864284020a.object_id#=> 70126864284020a.object_id == b.object_id#=> true
利用object_id
,我们可以发现symbol
在Ruby
程序语言中,占着同个记忆体位置,而String
不是。
# Symbol 在记忆体为同个位置:hello.object_id == :hello.object_id# String 在记忆体不同位置"hello".object_id == "hello".object_idfalse
在提到 symbol
的同时,我们也讲讲freeze
的原理
a = "a".freeze(1..3).map { "a".object_id } #=> [70240285526560, 70240285526420, 70240285526400](1..3).map { a.object_id } #=> [70240172465500, 70240172465500, 70240172465500]
先看没有被结冻的字串对应的object_id
,我们发现记忆体位置不一样。使用freeze
过後,所指向的object_id
相同,因此使用freeze
占去的记忆体会比较小,效能会比较高。
由上述介绍的freeze
和 symbol
,我们可以发现freeze
的原理跟 symbol
很像!
虽然在後面的Rails
版本似乎已经不用写freeze
方法了,但如果要面试Rails
工程师的读者们,记得要看freeze
的概念。
Day2介绍了Ruby
的概念以及基本用法。在Day3-7的四天里面,会介绍其他关於Ruby
的基本语法
>>: @Day2 | C# WixToolset + WPF 帅到不行的安装包 [使用参考专案打包的方式]
Day05 时 , 我们制作了一个可传入参数的 neuomorphic-button <neu...
最近有银行在更新 似乎有灾情 来看看C#是否可以写出 定义银行帐户类型 您可以从建立能定义该行为之类...
本次的事件起因是一名教育部委外厂商在搬移资料的过程中误删档案,导致虚拟硬碟被重新设定,重开机後,学生...
tags: OC 30 day NSObject 是什麽? 是Foundation 框架中的类,在这...
函式(function) 可参考:Day08 - 函数(01) 重复的内容会以函式来定义,来减少重工...