arql 0.3.30 → 0.3.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README-zh_CN.org +6 -0
- data/README.org +3 -0
- data/lib/arql/commands/models.rb +39 -9
- data/lib/arql/version.rb +1 -1
- data/ruby-guides-for-java-developer-zh_CN.org +766 -0
- data/simple-active-record-guide-zh_CN.org +83 -0
- data/simple-pry-guides-zh_CN.org +114 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fda37174020852318fc454680c6927e4fd01ef354779e782e596d0af43807e9
|
4
|
+
data.tar.gz: b3794fd222fa005419c03b514c3a2d8a4c0ec2dc8db7ea3a4e5ed6304a4146c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 293b549fdbbbd97bd43d3a457325cf88fa8af414f1866918a96bd8b313884c5f7c39a30701571c20e20617e7c68b03b431d8b633a7e2dc09a2a0f6a7db3fc82a
|
7
|
+
data.tar.gz: dc4992b8f330b4b1598669cf05987091176d70b56befb70bfffc00ecd94c69d052a3ca4d0c245b748fca02ccfac8776c68fa9bb4f1d3aea1726d1143db6e4ece
|
data/Gemfile.lock
CHANGED
data/README-zh_CN.org
CHANGED
@@ -166,6 +166,7 @@
|
|
166
166
|
6. =ssh.password= : ssh 密码
|
167
167
|
7. =ssh.local_port= : ssh 本地端口
|
168
168
|
8. =singularized_table_names=: 是否使用单数表名,默认为 =false=, 如果为 =false=, 则 =students= 表将定义为 =Student= 模型,如果为 =true=, 则 =students= 表将定义为 =Students= 模型
|
169
|
+
9. =table_name_prefixes=: 表名前缀数组,默认为空数组,如果指定了此项,在生成模型时将忽略这些前缀,例如,如果指定了 =["t_"]=, 则 =t_students= 表将定义为 =Student= 模型
|
169
170
|
|
170
171
|
**** 配置文件示例
|
171
172
|
|
@@ -182,6 +183,7 @@
|
|
182
183
|
username: root
|
183
184
|
database: blog
|
184
185
|
password:
|
186
|
+
table_name_prefixes: ["t_"]
|
185
187
|
socket: /tmp/mysql.sock
|
186
188
|
|
187
189
|
dev:
|
@@ -191,6 +193,7 @@
|
|
191
193
|
username: root
|
192
194
|
password: 123456
|
193
195
|
database: blog
|
196
|
+
table_name_prefixes: ["t_"]
|
194
197
|
ssh:
|
195
198
|
host: dev.mycompany.com
|
196
199
|
port: 22
|
@@ -1075,6 +1078,9 @@
|
|
1075
1078
|
# Table: sub_order, Column: entry_price (金额)
|
1076
1079
|
#+END_SRC
|
1077
1080
|
|
1081
|
+
*** [[./ruby-guides-for-java-developer-zh_CN.org][给 Java 开发者的 Ruby 入门简明教程]]
|
1082
|
+
*** [[./simple-pry-guides-zh_CN.org][简明 Pry 使用指南]]
|
1083
|
+
*** [[./simple-active-record-guide-zh_CN.org][简明 ActiveRecord 使用指南]]
|
1078
1084
|
** 开发
|
1079
1085
|
|
1080
1086
|
检出代码后,运行 =bin/setup= 安装依赖。你也可以运行 =bin/console= 进入交互式控制台。
|
data/README.org
CHANGED
@@ -175,6 +175,7 @@
|
|
175
175
|
6. =ssh.password= : ssh password
|
176
176
|
7. =ssh.local_port= : ssh local port
|
177
177
|
8. =singularized_table_names=: Whether to use singular table names, default is =false=, if =false=, the =students= table will be defined as the =Student= model, if =true=, the =students= table will be defined as the =Students= model
|
178
|
+
9. =table_name_prefixes=: An array of table name prefixes, defaults to an empty array. If specified, these prefixes will be ignored when generating models. For example, if =["t_"]= is specified, then the =t_students= table will be defined as the =Student= model
|
178
179
|
|
179
180
|
**** Example
|
180
181
|
|
@@ -190,6 +191,7 @@
|
|
190
191
|
<<: *default
|
191
192
|
username: root
|
192
193
|
database: blog
|
194
|
+
table_name_prefixes: ["t_"]
|
193
195
|
password:
|
194
196
|
socket: /tmp/mysql.sock
|
195
197
|
|
@@ -200,6 +202,7 @@
|
|
200
202
|
username: root
|
201
203
|
password: 123456
|
202
204
|
database: blog
|
205
|
+
table_name_prefixes: ["t_"]
|
203
206
|
ssh:
|
204
207
|
host: dev.mycompany.com
|
205
208
|
port: 22
|
data/lib/arql/commands/models.rb
CHANGED
@@ -13,21 +13,51 @@ module Arql::Commands
|
|
13
13
|
t
|
14
14
|
end
|
15
15
|
|
16
|
-
def models_table(
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
def models_table(table_regexp=nil, column_regexp=nil)
|
17
|
+
if column_regexp.nil?
|
18
|
+
Terminal::Table.new do |t|
|
19
|
+
models.each_with_index { |row, idx| t << (row || :separator) if row.nil? ||
|
20
|
+
table_regexp.nil? ||
|
21
|
+
idx.zero? ||
|
22
|
+
row.any? { |e| e =~ table_regexp }
|
23
|
+
}
|
24
|
+
end
|
25
|
+
else
|
26
|
+
connection = ::ActiveRecord::Base.connection
|
27
|
+
table = Terminal::Table.new do |t|
|
28
|
+
t << ['PK', 'Table', 'Model', 'Name', 'SQL Type', 'Ruby Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
|
29
|
+
t << :separator
|
30
|
+
Arql::Definition.models.each do |definition|
|
31
|
+
model_class = definition[:model]
|
32
|
+
matched_columns = model_class.columns.select { |column| column.name =~ column_regexp || column.comment =~ column_regexp }
|
33
|
+
next if matched_columns.empty?
|
34
|
+
matched_columns.each do |column|
|
35
|
+
pk = if [connection.primary_key(definition[:table])].flatten.include?(column.name)
|
36
|
+
'Y'
|
37
|
+
else
|
38
|
+
''
|
39
|
+
end
|
40
|
+
t << [pk, definition[:table], model_class.name, column.name, column.sql_type,
|
41
|
+
column.sql_type_metadata.type, column.sql_type_metadata.limit || '',
|
42
|
+
column.sql_type_metadata.precision || '', column.sql_type_metadata.scale || '', column.default || '',
|
43
|
+
column.null, "#{definition[:comment]} - #{column.comment}"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
puts table
|
23
48
|
end
|
24
49
|
end
|
25
50
|
end
|
26
51
|
end
|
27
52
|
|
28
|
-
Pry.commands.block_command 'm' do |
|
53
|
+
Pry.commands.block_command 'm' do |arg|
|
29
54
|
puts
|
30
|
-
|
55
|
+
if arg.start_with?('c=') or arg.start_with?('column=')
|
56
|
+
column_regexp = arg.sub(/^c(olumn)?=/, '')
|
57
|
+
Models::models_table(nil, column_regexp.try { |e| e.start_with?('/') ? eval(e) : Regexp.new(e) })
|
58
|
+
else
|
59
|
+
puts Models::models_table(arg.try { |e| e.start_with?('/') ? eval(e) : Regexp.new(e) }, nil)
|
60
|
+
end
|
31
61
|
end
|
32
62
|
|
33
63
|
Pry.commands.alias_command 'l', 'm'
|
data/lib/arql/version.rb
CHANGED
@@ -0,0 +1,766 @@
|
|
1
|
+
* Java 开发者的 Ruby 入门简明教程
|
2
|
+
|
3
|
+
Java 非常成熟,久经检验,且非常快(与那些反对java的人可能还在声称的相反)。但它也非常啰嗦。从 Java 到Ruby,可以预见的
|
4
|
+
是代码规模将大大缩小。你也可以期望使用相对少的时间快速出产原型。
|
5
|
+
|
6
|
+
** 相似点 Ruby 与 Java 一样的地方
|
7
|
+
|
8
|
+
- 垃圾回收器帮你管理内存。
|
9
|
+
- 强类型对象。
|
10
|
+
- 有 public、 private 和 protected 方法。
|
11
|
+
- 拥有嵌入式文档工具(Ruby 的工具叫 rdoc)。rdoc 生成的文档与 javadoc 非常相似。
|
12
|
+
|
13
|
+
** 相异点 Ruby 与 Java 不同的地方
|
14
|
+
|
15
|
+
一些简单总结:
|
16
|
+
|
17
|
+
- 你不需要编译你的代码。你只需要直接运行它。
|
18
|
+
- 定义像类这样的东西时,可以使用 =end= 关键字,而不使用花括号包裹代码块。
|
19
|
+
- 使用 =require= 代替 =import= 。
|
20
|
+
- 所有成员变量为私有。在外部,使用方法获取所有你需要的一切。
|
21
|
+
- 方法调用的括号通常是可选的,经常被省略。
|
22
|
+
- 一切皆对象,包括像 2 和 3.14159 这样的数字。
|
23
|
+
- 没有静态类型检查。
|
24
|
+
- 变量名只是标签。它们没有相应的类型。
|
25
|
+
- 没有类型声明。按需分配变量名,及时可用(如: ~a = [1,2,3]~ 而不是 ~int[] a = {1,2,3};~ )。
|
26
|
+
- 没有显式转换。只需要调用方法。代码运行之前,单元测试应该告诉你出现异常。
|
27
|
+
- 使用 ~foo = Foo.new("hi")~ 创建新对象,而非 ~Foo foo = new Foo("hi")~ 。
|
28
|
+
- 构造器总是命名为“initialize” 而不是类名称。
|
29
|
+
- 作为接口的替代,你将获得“混入(mixins)”。
|
30
|
+
- 相比 XML,倾向于使用 YAML。
|
31
|
+
- =nil= 替代 =null= 。
|
32
|
+
- Ruby 对 ~==~ 和 ~equals()~ 的处理方式与 Java 不一样。测试相等性使用 ~==~ (Java 中是 ~equals()~ )。测试是否为同一
|
33
|
+
对象使用 ~equals?()~ (Java 中是 ~==~ )。
|
34
|
+
- Ruby 里面调用一个函数或方法,参数两边的圆括号可以省略。如:
|
35
|
+
- ~p.say_hello("Hi", "Jack")~ 可以写成 ~p.say_hello "Hi", "Jack"~
|
36
|
+
- ~f.close()~ 可以写成 ~f.close~
|
37
|
+
- 定义函数或方法时,参数两边的圆括号也可以省略。
|
38
|
+
- Ruby 的类可以重新「打开」,即在原有的类定义上追加新的方法或属性、覆盖已有的方法、属性。如:
|
39
|
+
#+BEGIN_SRC ruby
|
40
|
+
class Person < ActiveRecord::Base # 定义了一个 Person 类,它是 ActiveRecord::Base 的子类
|
41
|
+
def say_hello(message, name)
|
42
|
+
puts "#{name}, #{message}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
class Person # 重新打开 Person 类,这里添加了一个 sleep 方法,并且 Person 仍然有 say_hello 方法,并且它仍然是 ActiveRecord::Base 的子类
|
48
|
+
has_many :books # has_many 是从 ActiveRecord::Base 继承来的类方法(静态方法), 类方法可以直接在类体中调用(就是和定义方法的同一层级),而实例方法则不可以在类体中调用
|
49
|
+
def sleep(time_secs)
|
50
|
+
...
|
51
|
+
end
|
52
|
+
end
|
53
|
+
#+END_SRC
|
54
|
+
|
55
|
+
下面是更详细的差异的介绍:
|
56
|
+
|
57
|
+
** 快速简明教程
|
58
|
+
|
59
|
+
*** 代码块以 end 关键字结束
|
60
|
+
|
61
|
+
不同于 Java,Ruby 使用 =end= 关键字来结束代码块,而不是使用花括号。如:
|
62
|
+
|
63
|
+
**** 类定义
|
64
|
+
#+BEGIN_SRC ruby
|
65
|
+
class Person
|
66
|
+
def initialize(name)
|
67
|
+
@name = name
|
68
|
+
end
|
69
|
+
end
|
70
|
+
#+END_SRC
|
71
|
+
**** 函数方法定义
|
72
|
+
#+BEGIN_SRC ruby
|
73
|
+
def say_hello
|
74
|
+
puts "Hello"
|
75
|
+
end
|
76
|
+
#+END_SRC
|
77
|
+
*** 一切皆对象
|
78
|
+
|
79
|
+
Ruby 中的数字、字符串、数组等都是对象,它们都有自己的方法。如:
|
80
|
+
|
81
|
+
#+BEGIN_SRC ruby
|
82
|
+
3.times { puts "Hello" } # 输出三次 Hello
|
83
|
+
"Hello".length
|
84
|
+
[1, 2, 3].reverse
|
85
|
+
#+END_SRC
|
86
|
+
|
87
|
+
*** 基本数据类型
|
88
|
+
**** 数值
|
89
|
+
#+BEGIN_SRC ruby
|
90
|
+
1 + 2
|
91
|
+
2 * 3
|
92
|
+
10 / 5
|
93
|
+
10 % 3
|
94
|
+
#+END_SRC
|
95
|
+
**** 字符串
|
96
|
+
#+BEGIN_SRC ruby
|
97
|
+
"Hello, " + "World"
|
98
|
+
"Hello" * 3
|
99
|
+
"Hello".length
|
100
|
+
"Hello".reverse
|
101
|
+
#+END_SRC
|
102
|
+
|
103
|
+
+ 单引号字符串中的特殊字符不会被转义,而双引号字符串中的特殊字符会被转义。如:
|
104
|
+
#+BEGIN_SRC ruby
|
105
|
+
puts 'Hello\nWorld' # 输出 Hello\nWorld
|
106
|
+
puts "Hello\nWorld" # 输出 Hello
|
107
|
+
# World
|
108
|
+
#+END_SRC
|
109
|
+
+ 双引号字符串中可以使用 #{} 来插入变量或表达式。如:
|
110
|
+
#+BEGIN_SRC ruby
|
111
|
+
name = "Jack"
|
112
|
+
puts "Hello, #{name}" # 输出 Hello, Jack
|
113
|
+
#+END_SRC
|
114
|
+
|
115
|
+
**** nil
|
116
|
+
|
117
|
+
Ruby 中的 =nil= 相当于 Java 中的 =null= 。
|
118
|
+
|
119
|
+
**** Symbol
|
120
|
+
|
121
|
+
#+BEGIN_SRC ruby
|
122
|
+
:name
|
123
|
+
#+END_SRC
|
124
|
+
|
125
|
+
Symbol 是一种特殊的字符串(但Symbok 类的和表示字符串的 String 类没有直接关系),它的值是唯一的。Symbol 通常用来表示一个名字或标识符。
|
126
|
+
|
127
|
+
**** boolean
|
128
|
+
|
129
|
+
Ruby 中的 true 和 false 都是对象,它们都是 TrueClass 和 FalseClass 的实例。
|
130
|
+
|
131
|
+
在 Ruby 中,除了 false 和 nil 为假,其他值都为真。
|
132
|
+
|
133
|
+
在 Ruby 代码中,还经常看到 =if obj.present?= 等方法,这些方法是 Rails 提供的,它们是对 Ruby 的扩
|
134
|
+
展。其中,=obj.present?= 方法会判断 obj 是否为 nil 或空字符串或空数组、空散列
|
135
|
+
|
136
|
+
**** 数组
|
137
|
+
|
138
|
+
#+BEGIN_SRC ruby
|
139
|
+
[1, 2, 3]
|
140
|
+
[1, 2, 3].length
|
141
|
+
[1, 2, 3].reverse
|
142
|
+
[1, 2, 3] << 4
|
143
|
+
#+END_SRC
|
144
|
+
|
145
|
+
+ 数组中的元素可以是不同类型的对象。
|
146
|
+
+ 数组中的元素可以通过索引访问,索引从 0 开始。
|
147
|
+
+ 数组中的元素可以通过 << 方法添加到数组的末尾。
|
148
|
+
***** 数组的常用方法
|
149
|
+
****** each
|
150
|
+
each 方法用于遍历数组中的元素。如:
|
151
|
+
#+BEGIN_SRC ruby
|
152
|
+
[1, 2, 3].each { |i| puts i }
|
153
|
+
#+END_SRC
|
154
|
+
****** map
|
155
|
+
map 方法用于对数组中的每个元素执行块中的操作,返回一个新的数组。如:
|
156
|
+
#+BEGIN_SRC ruby
|
157
|
+
[1, 2, 3].map { |i| i * 2 }
|
158
|
+
#+END_SRC
|
159
|
+
****** select
|
160
|
+
select 方法用于从数组中选择满足条件的元素,返回一个新的数组。如:
|
161
|
+
#+BEGIN_SRC ruby
|
162
|
+
[1, 2, 3].select { |i| i > 1 }
|
163
|
+
#+END_SRC
|
164
|
+
****** reduce
|
165
|
+
reduce 方法用于对数组中的元素进行累加。如:
|
166
|
+
#+BEGIN_SRC ruby
|
167
|
+
[1, 2, 3].reduce { |sum, i| sum + i }
|
168
|
+
#+END_SRC
|
169
|
+
****** each_with_index
|
170
|
+
each_with_index 方法用于遍历数组中的元素,同时获取元素的索引。如:
|
171
|
+
#+BEGIN_SRC ruby
|
172
|
+
[1, 2, 3].each_with_index { |i, index| puts "#{index}: #{i}" }
|
173
|
+
#+END_SRC
|
174
|
+
|
175
|
+
****** each_with_object
|
176
|
+
each_with_object 方法用于遍历数组中的元素,同时传递一个对象。如:
|
177
|
+
#+BEGIN_SRC ruby
|
178
|
+
[person1, person2].each_with_object({}) { |person, hash| hash[person.name] = person.age }
|
179
|
+
#+END_SRC
|
180
|
+
****** group_by
|
181
|
+
group_by 方法用于根据块中的条件对数组中的元素进行分组。如:
|
182
|
+
#+BEGIN_SRC ruby
|
183
|
+
[person1, person2].group_by { |person| person.gender }
|
184
|
+
#+END_SRC
|
185
|
+
****** in_groups
|
186
|
+
in_groups 方法用于将数组分成若干组。如:
|
187
|
+
#+BEGIN_SRC ruby
|
188
|
+
[1, 2, 3, 4, 5].in_groups(2)
|
189
|
+
#+END_SRC
|
190
|
+
****** in_groups_of
|
191
|
+
in_groups_of 方法用于将数组分成若干组,每组包含指定个数的元素。如:
|
192
|
+
#+BEGIN_SRC ruby
|
193
|
+
[1, 2, 3, 4, 5].in_groups_of(2)
|
194
|
+
#+END_SRC
|
195
|
+
**** 哈希
|
196
|
+
|
197
|
+
哈希是一种键值对的数据结构,类似于 Java 中的 Map。如:
|
198
|
+
|
199
|
+
#+BEGIN_SRC ruby
|
200
|
+
{ "name" => "Jack", "age" => 20 }
|
201
|
+
{ :name => "Jack", :age => 20 }
|
202
|
+
{ name: "Jack", age: 20 }
|
203
|
+
#+END_SRC
|
204
|
+
|
205
|
+
上述代码中:
|
206
|
+
1. 第一行的哈希中的键和值都是字符串。
|
207
|
+
2. 第二行的哈希中的键是 Symbol,值是字符串。
|
208
|
+
3. 第三行的哈希中的键是 Symbol,值是字符串,也就是说在一个哈希中, =key: value= 的形式等价于 ~:key => value~ 的形式。
|
209
|
+
|
210
|
+
4. 哈希是一种键值对的集合。
|
211
|
+
5. 哈希中的键和值可以是任意类型的对象。
|
212
|
+
6. 哈希中的键是唯一的。
|
213
|
+
***** 哈希的常用方法
|
214
|
+
****** each
|
215
|
+
each 方法用于遍历哈希中的键值对。如:
|
216
|
+
#+BEGIN_SRC ruby
|
217
|
+
{ name: "Jack", age: 20 }.each { |key, value| puts "#{key}: #{value}" }
|
218
|
+
#+END_SRC
|
219
|
+
****** map
|
220
|
+
map 方法用于对哈希中的每个键值对执行块中的操作,返回一个新的数组。如:
|
221
|
+
#+BEGIN_SRC ruby
|
222
|
+
{ name: "Jack", age: 20 }.map { |key, value| value }
|
223
|
+
#+END_SRC
|
224
|
+
****** select
|
225
|
+
select 方法用于从哈希中选择满足条件的键值对,返回一个新的哈希。如:
|
226
|
+
#+BEGIN_SRC ruby
|
227
|
+
{ name: "Jack", age: 20 }.select { |key, value| value > 18 }
|
228
|
+
#+END_SRC
|
229
|
+
****** keys
|
230
|
+
keys 方法用于获取哈希中的所有键。如:
|
231
|
+
#+BEGIN_SRC ruby
|
232
|
+
{ name: "Jack", age: 20 }.keys
|
233
|
+
#+END_SRC
|
234
|
+
****** values
|
235
|
+
values 方法用于获取哈希中的所有值。如:
|
236
|
+
#+BEGIN_SRC ruby
|
237
|
+
{ name: "Jack", age: 20 }.values
|
238
|
+
#+END_SRC
|
239
|
+
****** merge
|
240
|
+
merge 方法用于合并两个哈希。如:
|
241
|
+
#+BEGIN_SRC ruby
|
242
|
+
{ name: "Jack" }.merge({ age: 20 })
|
243
|
+
#+END_SRC
|
244
|
+
****** merge!
|
245
|
+
merge! 方法用于将另一个哈希合并到当前哈希中。如:
|
246
|
+
#+BEGIN_SRC ruby
|
247
|
+
{ name: "Jack" }.merge!({ age: 20 })
|
248
|
+
#+END_SRC
|
249
|
+
*** 类的基础知识
|
250
|
+
**** 初始化函数
|
251
|
+
|
252
|
+
初始化函数相当于 Java 中的构造函数,它是在创建对象时自动调用的函数。在 Ruby 中,初始化函数的名字是 =initialize=
|
253
|
+
|
254
|
+
#+BEGIN_SRC ruby
|
255
|
+
class Person
|
256
|
+
def initialize(name, age)
|
257
|
+
@name = name
|
258
|
+
@age = age
|
259
|
+
end
|
260
|
+
end
|
261
|
+
#+END_SRC
|
262
|
+
**** 属性和 getter/setter
|
263
|
+
|
264
|
+
上例中的 =@name= 和 =@age= 是 Person 类的属性,它们是实例变量,只能在类的内部访问。如果要在类的外部访问这两个属性,需要提供 getter 和 setter 方法。如:
|
265
|
+
|
266
|
+
#+BEGIN_SRC ruby
|
267
|
+
class Person
|
268
|
+
def initialize(name, age)
|
269
|
+
@name = name
|
270
|
+
@age = age
|
271
|
+
end
|
272
|
+
|
273
|
+
def name
|
274
|
+
@name
|
275
|
+
end
|
276
|
+
|
277
|
+
def name=(name)
|
278
|
+
@name = name
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
p = Person.new("Jack", 20)
|
283
|
+
puts p.name # 这是调用 getter 方法, 而不是直接访问实例变量
|
284
|
+
p.name = "Tom" # 这是调用 setter 方法, 而不是直接设置实例变量
|
285
|
+
#+END_SRC
|
286
|
+
|
287
|
+
上述代码中, =name= 方法是 getter 方法, =name= 方法是 setter 方法。Ruby 提供了一种更简洁的方式来定义 getter 和 setter 方法,如:
|
288
|
+
|
289
|
+
#+BEGIN_SRC ruby
|
290
|
+
class Person
|
291
|
+
attr_accessor :name
|
292
|
+
|
293
|
+
def initialize(name, age)
|
294
|
+
@name = name
|
295
|
+
@age = age
|
296
|
+
end
|
297
|
+
end
|
298
|
+
#+END_SRC
|
299
|
+
|
300
|
+
如果只需要 getter 方法,可以使用 =attr= 活 =attr_reader= 方法;如果只需要 setter 方法,可以使用 =attr_writer= 方法。
|
301
|
+
|
302
|
+
#+BEGIN_SRC ruby
|
303
|
+
class Person
|
304
|
+
attr :name
|
305
|
+
attr_writer :name
|
306
|
+
|
307
|
+
def initialize(name, age)
|
308
|
+
@name = name
|
309
|
+
@age = age
|
310
|
+
end
|
311
|
+
end
|
312
|
+
#+END_SRC
|
313
|
+
|
314
|
+
**** 继承
|
315
|
+
|
316
|
+
和 Java 一样,Ruby 也是只支持单继承的。Ruby 使用 =<= 操作符来表示继承关系。如:
|
317
|
+
|
318
|
+
#+BEGIN_SRC ruby
|
319
|
+
class Person
|
320
|
+
def initialize(name, age)
|
321
|
+
@name = name
|
322
|
+
@age = age
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
class Student < Person
|
327
|
+
def initialize(name, age, school)
|
328
|
+
super(name, age)
|
329
|
+
@school = school
|
330
|
+
end
|
331
|
+
end
|
332
|
+
#+END_SRC
|
333
|
+
|
334
|
+
**** 静态方法
|
335
|
+
|
336
|
+
Ruby 中的静态方法使用 =self= 关键字来定义。如:
|
337
|
+
|
338
|
+
#+BEGIN_SRC ruby
|
339
|
+
class Person
|
340
|
+
def initialize(name, age)
|
341
|
+
@name = name
|
342
|
+
@age = age
|
343
|
+
end
|
344
|
+
|
345
|
+
def self.say_hello
|
346
|
+
puts "Hello"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
#+END_SRC
|
350
|
+
|
351
|
+
上述代码中, =say_hello= 是一个静态方法,可以通过 Person 类直接调用。 还有另一种常用的定义静态方法的方式,如:
|
352
|
+
|
353
|
+
#+BEGIN_SRC ruby
|
354
|
+
class Person
|
355
|
+
def initialize(name, age)
|
356
|
+
@name = name
|
357
|
+
@age = age
|
358
|
+
end
|
359
|
+
|
360
|
+
class << self
|
361
|
+
def say_hello
|
362
|
+
puts "Hello"
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
#+END_SRC
|
367
|
+
|
368
|
+
*** 类可以重新打开
|
369
|
+
|
370
|
+
Ruby 的类可以重新「打开」,即在原有的类定义上追加新的方法或属性、覆盖已有的方法、属性。如:
|
371
|
+
|
372
|
+
#+BEGIN_SRC ruby
|
373
|
+
class Person < ActiveRecord::Base # 定义了一个 Person 类,它是 ActiveRecord::Base 的子类
|
374
|
+
def say_hello(message, name)
|
375
|
+
puts "#{name}, #{message}"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
|
380
|
+
class Person # 重新打开 Person 类,这里添加了一个 sleep 方法,并且 Person 仍然有 say_hello 方法,并且它仍然是 ActiveRecord::Base 的子类
|
381
|
+
has_many :books # has_many 是从 ActiveRecord::Base 继承来的类方法(静态方法), 类方法可以直接在类体中调用(就是和定义方法的同一层级),而实例方法则不可以在类体中调用
|
382
|
+
def sleep(time_secs)
|
383
|
+
...
|
384
|
+
end
|
385
|
+
end
|
386
|
+
#+END_SRC
|
387
|
+
|
388
|
+
*** 混入和 Enumerable
|
389
|
+
|
390
|
+
Ruby 虽然和 Java 一样只支持单继承,但是 Ruby 提供了一种叫做「混入」的机制,可以在类中引入模块(module),从而实现多继承的效果。如:
|
391
|
+
|
392
|
+
#+BEGIN_SRC ruby
|
393
|
+
module MyModule
|
394
|
+
def say_hello
|
395
|
+
puts "Hello"
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
class Person
|
400
|
+
def xxx
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
class Student < Person
|
405
|
+
include MyModule
|
406
|
+
end
|
407
|
+
#+END_SRC
|
408
|
+
|
409
|
+
现在 Student 类既继承了 Person 类的 xxx 方法,又引入了 MyModule 模块的 say_hello 方法。
|
410
|
+
|
411
|
+
因为模块(module关键字定义的部分)和类不同的是,模块不能被实例化,也就是不能创建模块的对象。所以混入模块,只会继承模块的方法,而不会继承模块的属性,从而避免了多继承的问题。
|
412
|
+
|
413
|
+
Ruby 标准库中的 Enumerable 模块就是一个很好的例子。Enumerable 模块提供了很多方法,如 =each=、=map=、=select=、=reject=、=detect=、=sort= 等,这些方法可以被任何实现了 =each= 方法的类包含进来,从而实现了类似于 Java 中的 Collection 接口的效果。如:
|
414
|
+
|
415
|
+
#+BEGIN_SRC ruby
|
416
|
+
class MyCollection
|
417
|
+
include Enumerable
|
418
|
+
def each
|
419
|
+
...
|
420
|
+
end
|
421
|
+
end
|
422
|
+
#+END_SRC
|
423
|
+
|
424
|
+
现在 MyCollection 类就可以像数组一样使用 =map=、=select=、=reject=、=detect=、=sort= 等方法了。而数组实际上也是实现了 =each= 方法并混入了 Enumerable 模块的类。
|
425
|
+
|
426
|
+
*** Kernel 模块
|
427
|
+
|
428
|
+
Ruby 中的 Kernel 模块是一个特殊的模块,它包含了很多常用的方法,这些方法可以直接在任何地方调用,而不需要通过模块名或类名来调用。如:
|
429
|
+
|
430
|
+
#+BEGIN_SRC ruby
|
431
|
+
puts "Hello"
|
432
|
+
sleep 1
|
433
|
+
#+END_SRC
|
434
|
+
|
435
|
+
上述代码中的 =puts= 和 =sleep= 方法都是 Kernel 模块中的方法,可以直接调用。
|
436
|
+
|
437
|
+
事实上,Ruby 中的很多方法都是定义在 Kernel 模块中的,这是因为 Ruby 中的所有类都是 Object 类的子类,而 Object 类又混入了 Kernel 模块,所以所有的类都可以直接调用 Kernel 模块中的方法。
|
438
|
+
|
439
|
+
如果你希望定义一个全局方法,可以直接定义在 Kernel 模块中,这样就可以在任何地方调用了。如:
|
440
|
+
|
441
|
+
#+BEGIN_SRC ruby
|
442
|
+
module Kernel
|
443
|
+
def say_hello
|
444
|
+
puts "Hello"
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
say_hello
|
449
|
+
#+END_SRC
|
450
|
+
|
451
|
+
*** Block 和 Proc
|
452
|
+
|
453
|
+
Java 8 中引入了 Lambda 表达式,Ruby 中的 Block 和 Proc 与之类似,都是相当于一个函数对象。
|
454
|
+
|
455
|
+
**** Block
|
456
|
+
|
457
|
+
Block 并不是一个对象,而是一段代码块,是原生的语法元素、可以在方法调用时传递给方法。如:
|
458
|
+
|
459
|
+
#+BEGIN_SRC ruby
|
460
|
+
def say_hello
|
461
|
+
puts "Hello"
|
462
|
+
yield if block_given? # block_given? 用于判断在调用这个函数时,是否使用了 block, yield 会执行传递给 say_hello 方法的 Block
|
463
|
+
end
|
464
|
+
|
465
|
+
say_hello # 输出 "Hello"
|
466
|
+
|
467
|
+
say_hello do
|
468
|
+
puts "World"
|
469
|
+
end # 输出 "Hello" 和 "World"
|
470
|
+
#+END_SRC
|
471
|
+
|
472
|
+
也可以使用 花括号 代替 do/end:
|
473
|
+
|
474
|
+
#+BEGIN_SRC ruby
|
475
|
+
say_hello { puts "World" } # 输出 "Hello" 和 "World"
|
476
|
+
#+END_SRC
|
477
|
+
|
478
|
+
**** Proc
|
479
|
+
|
480
|
+
proc 看起来和 Block 类似,但是 proc 是一个对象,可以赋值给变量,也可以作为参数传递给方法。如:
|
481
|
+
|
482
|
+
#+BEGIN_SRC ruby
|
483
|
+
my_proc = Proc.new { puts "Hello" }
|
484
|
+
my_proc.call # 输出 "Hello"
|
485
|
+
|
486
|
+
def say_hello(block)
|
487
|
+
block.call
|
488
|
+
end
|
489
|
+
|
490
|
+
say_hello(my_proc) # 输出 "Hello"
|
491
|
+
#+END_SRC
|
492
|
+
|
493
|
+
***** 将 Block 转换为 Proc
|
494
|
+
|
495
|
+
有时候我们需要将 Block 转换为 Proc 对象,可以使用 & 符号:
|
496
|
+
|
497
|
+
#+BEGIN_SRC ruby
|
498
|
+
def say_hello(&blk)
|
499
|
+
blk.call
|
500
|
+
end
|
501
|
+
|
502
|
+
say_hello { puts "Hello" } # 输出 "Hello"
|
503
|
+
#+END_SRC
|
504
|
+
|
505
|
+
上面的代码中,&blk 会将传递给 say_hello 方法的 Block 转换为 Proc 对象。
|
506
|
+
|
507
|
+
|
508
|
+
***** 其他定义 Proc 的方式
|
509
|
+
****** lambda
|
510
|
+
|
511
|
+
lambda 是 Proc 的一种特殊形式,可以使用 lambda 方法定义一个 Proc 对象:
|
512
|
+
|
513
|
+
#+BEGIN_SRC ruby
|
514
|
+
my_proc = lambda { puts "Hello" }
|
515
|
+
my_proc.call # 输出 "Hello"
|
516
|
+
#+END_SRC
|
517
|
+
|
518
|
+
lambda 定义的 Proc 和普通的 Proc 的区别在于,lambda 会检查传递给它的参数个数是否正确,而普通的 Proc 不会。如:
|
519
|
+
|
520
|
+
#+BEGIN_SRC ruby
|
521
|
+
my_proc = Proc.new { |a, b| puts a + b }
|
522
|
+
my_proc.call(1) # 输出 1
|
523
|
+
|
524
|
+
my_proc = lambda { |a, b| puts a + b }
|
525
|
+
my_proc.call(1) # 报错
|
526
|
+
****** ->
|
527
|
+
|
528
|
+
-> 是 lambda 的一种简写形式,可以用来定义一个 Proc 对象:
|
529
|
+
|
530
|
+
#+BEGIN_SRC ruby
|
531
|
+
my_proc = -> { puts "Hello" }
|
532
|
+
my_proc.call # 输出 "Hello"
|
533
|
+
#+END_SRC
|
534
|
+
***** =&:my_method=
|
535
|
+
|
536
|
+
有时候我们会看到这样的写法:
|
537
|
+
|
538
|
+
#+BEGIN_SRC ruby
|
539
|
+
p1 = Person.new
|
540
|
+
p2 = Person.new
|
541
|
+
|
542
|
+
[p1, p2].each(&:say_hello)
|
543
|
+
#+END_SRC
|
544
|
+
|
545
|
+
这里的 =&:say_hello= 实际上是将 =:say_hello= Symbol对象转换为 Proc 对象,然后传递给 each 方法。这种写法等价于:
|
546
|
+
|
547
|
+
#+BEGIN_SRC ruby
|
548
|
+
[p1, p2].each { |p| p.say_hello }
|
549
|
+
#+END_SRC
|
550
|
+
|
551
|
+
这种用法看起来很像 Java 8 中的方法引用,但实际上是 Ruby 的一个语法糖。
|
552
|
+
|
553
|
+
那么这是如何将 Symbol 对象转换为 Proc 对象呢?实际上 =&= 后面跟一个对象,就是调用这个对象的 =to_proc= 方法,将其转换为 Proc 对象。如:
|
554
|
+
|
555
|
+
=&:say_hello= 就是将调用 =:say_hello= 这个 Symbol 对象的 to_proc 方法,而 Symbol 的 =to_proc= 方法是这样定义的:
|
556
|
+
|
557
|
+
#+BEGIN_SRC ruby
|
558
|
+
class Symbol
|
559
|
+
def to_proc
|
560
|
+
Proc.new { |obj, *args| obj.send(self, *args) }
|
561
|
+
end
|
562
|
+
end
|
563
|
+
#+END_SRC
|
564
|
+
|
565
|
+
*** 元编程
|
566
|
+
|
567
|
+
元编程是指在运行时动态地创建类和方法,或者修改现有类和方法的技术。Ruby 是一种动态语言,非常适合进行元编程。
|
568
|
+
|
569
|
+
可以理解为有点像 Java 中的反射机制,但是 Ruby 的元编程更加强大和灵活。
|
570
|
+
|
571
|
+
Ruby 提供了一些方法来动态地定义类和方法,如 =method_missing=, =define_method=, =class_eval=, =instance_eval= 等。
|
572
|
+
|
573
|
+
通过元编程,我们可以实现很多功能,如动态地创建类和方法,动态地修改类和方法,动态地调用方法等。
|
574
|
+
|
575
|
+
虽然你在编码时有可能不会用到元编程,但是了解元编程的原理和技术,可以帮助你更好地理解 Rails 等 Ruby 库或者框架的特性和机制。
|
576
|
+
|
577
|
+
更多关于元编程的内容,可以参考 「Ruby 元编程」 这本书。
|
578
|
+
|
579
|
+
**** =method_missing=
|
580
|
+
|
581
|
+
=method_missing= 是 Ruby 中一个非常重要的方法,当调用一个对象不存在的方法时,Ruby 会调用这个对象的 =method_missing= 方法。
|
582
|
+
|
583
|
+
通过重写 =method_missing= 方法,我们可以实现很多功能,如动态地创建方法,动态地调用方法等。
|
584
|
+
|
585
|
+
例如,我们可以定义一个类,当调用这个类的不存在的方法时,输出方法名:
|
586
|
+
|
587
|
+
#+BEGIN_SRC ruby
|
588
|
+
class MyClass
|
589
|
+
def method_missing(name, *args)
|
590
|
+
puts "You called #{name} with #{args.inspect}"
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
obj = MyClass.new
|
595
|
+
obj.hello(1, 2, 3) # 输出 "You called hello with [1, 2, 3]"
|
596
|
+
#+END_SRC
|
597
|
+
|
598
|
+
那么 =method_missing= 方法有哪些实际用途呢?考虑我们定义一个 ActiveRecord 模型:
|
599
|
+
|
600
|
+
#+BEGIN_SRC ruby
|
601
|
+
class User < ActiveRecord::Base
|
602
|
+
end
|
603
|
+
#+END_SRC
|
604
|
+
|
605
|
+
ActiveRecord 库会根据约定的命名规则,将 User 类和数据库中的 users 表对应起来,当我们调用 User 类的一些方法:
|
606
|
+
|
607
|
+
#+BEGIN_SRC ruby
|
608
|
+
user = User.find(1)
|
609
|
+
user.username = "Tom"
|
610
|
+
#+END_SRC
|
611
|
+
|
612
|
+
那么 =user.username= setter 方法是如何实现的呢?如果我们是 ActiveRecord 库的作者,我们就可以使用 =method_missing= 方法来实现这些方法:
|
613
|
+
|
614
|
+
#+BEGIN_SRC ruby
|
615
|
+
class ActiveRecord::Base
|
616
|
+
def method_missing(name, *args)
|
617
|
+
if db_columns.include_by_name?(name) # 判断数据库中是否有这个字段
|
618
|
+
db_columns.write_by_column_name(name, args.first) # 写入数据库
|
619
|
+
else
|
620
|
+
super # 调用父类的 method_missing 方法
|
621
|
+
end
|
622
|
+
end
|
623
|
+
end
|
624
|
+
#+END_SRC
|
625
|
+
|
626
|
+
**** =define_method=
|
627
|
+
|
628
|
+
=define_method= 方法可以动态地定义方法,它接受一个方法名和一个块,然后定义一个方法。
|
629
|
+
|
630
|
+
例如,我们可以定义一个类,动态地定义一个方法:
|
631
|
+
|
632
|
+
#+BEGIN_SRC ruby
|
633
|
+
class MyClass
|
634
|
+
define_method :hello do |name|
|
635
|
+
puts "Hello, #{name}!"
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
obj = MyClass.new
|
640
|
+
obj.hello("Tom") # 输出 "Hello, Tom!"
|
641
|
+
#+END_SRC
|
642
|
+
|
643
|
+
和 =method_missing= 方法不同, =define_method= 方法是在类对象上调用的,而且定义好之后,这个方法就会一直存在,直到这个类被销毁。而 =method_missing= 方法是在实例对象上调用的,
|
644
|
+
并且每次调用不存在的方法时,都会调用 =method_missing= 方法。
|
645
|
+
|
646
|
+
在 =method_missing= 的那个示例中,考虑到性能问题,不希望每次调用 ~user.username=~ 的时候都调用 =method_missing= 方法,我们可以使用 =define_method= 方法来定义这个方法:
|
647
|
+
|
648
|
+
#+BEGIN_SRC ruby
|
649
|
+
class ActiveRecord::Base
|
650
|
+
def method_missing(name, *args)
|
651
|
+
if db_columns.include_by_name?(name) # 判断数据库中是否有这个字段
|
652
|
+
self.class.send(:define_method, name) do |value|
|
653
|
+
db_columns.write_by_column_name(name, value) # 写入数据库
|
654
|
+
end
|
655
|
+
send(name, args.first) # 调用刚刚定义的方法, 而且这样一来,下次调用这个方法就不会再调用 method_missing 方法了
|
656
|
+
else
|
657
|
+
super # 调用父类的 method_missing 方法
|
658
|
+
end
|
659
|
+
end
|
660
|
+
end
|
661
|
+
#+END_SRC
|
662
|
+
|
663
|
+
**** =class_eval=
|
664
|
+
|
665
|
+
=class_eval= 方法可以动态地定义类的方法,它接受一个字符串作为参数,然后定义一个方法。
|
666
|
+
|
667
|
+
例如,我们可以定义一个类,动态地定义一个方法:
|
668
|
+
|
669
|
+
#+BEGIN_SRC ruby
|
670
|
+
class MyClass
|
671
|
+
class_eval %{
|
672
|
+
def hello(name)
|
673
|
+
puts "Hello, \#{name}!"
|
674
|
+
end
|
675
|
+
}
|
676
|
+
end
|
677
|
+
|
678
|
+
obj = MyClass.new
|
679
|
+
obj.hello("Tom") # 输出 "Hello, Tom!"
|
680
|
+
#+END_SRC
|
681
|
+
|
682
|
+
**** =instance_eval=
|
683
|
+
|
684
|
+
=instance_eval= 方法可以动态地定义实例对象的方法,它接受一个字符串作为参数,然后定义一个方法。
|
685
|
+
|
686
|
+
例如,我们可以定义一个类,动态地定义一个方法:
|
687
|
+
|
688
|
+
#+BEGIN_SRC ruby
|
689
|
+
class MyClass
|
690
|
+
def initialize(name)
|
691
|
+
@name = name
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
obj = MyClass.new("Tom")
|
696
|
+
obj.instance_eval %{
|
697
|
+
def hello
|
698
|
+
puts "Hello, \#{@name}!"
|
699
|
+
end
|
700
|
+
}
|
701
|
+
|
702
|
+
obj.hello # 输出 "Hello, Tom!"
|
703
|
+
|
704
|
+
obj2 = MyClass.new("Jerry")
|
705
|
+
obj2.hello # 报错, 因为 obj2 没有定义 hello 方法, 请自行搜索学习 Ruby 中的 singleton class 的概念
|
706
|
+
#+END_SRC
|
707
|
+
|
708
|
+
**** =instance_exec=
|
709
|
+
|
710
|
+
=instance_exec= 方法和 =instance_eval= 方法类似,但是它可以接受块作为参数。
|
711
|
+
|
712
|
+
例如,我们可以定义一个类,动态地定义一个方法:
|
713
|
+
|
714
|
+
#+BEGIN_SRC ruby
|
715
|
+
class MyClass
|
716
|
+
def initialize(name)
|
717
|
+
@name = name
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
obj = MyClass.new("Tom")
|
722
|
+
obj.instance_exec("Jerry") do |name|
|
723
|
+
puts "Hello, #{name}!"
|
724
|
+
end # 输出 "Hello, Jerry!", 这里定义的仍然是一个 singleton method
|
725
|
+
#+end_src
|
726
|
+
|
727
|
+
|
728
|
+
还可以借助 =instance_exec= 方法来动态定义一个普通的实例方法:
|
729
|
+
|
730
|
+
#+BEGIN_SRC ruby
|
731
|
+
class MyClass
|
732
|
+
def initialize(name)
|
733
|
+
@name = name
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
MyClass.instance_exec do
|
738
|
+
define_method :hello do
|
739
|
+
puts "Hello, #{@name}!"
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
obj = MyClass.new("Tom")
|
744
|
+
obj.hello # 输出 "Hello, Tom!"
|
745
|
+
#+END_SRC
|
746
|
+
|
747
|
+
|
748
|
+
|
749
|
+
|
750
|
+
**** =Class.new=
|
751
|
+
|
752
|
+
示例:
|
753
|
+
|
754
|
+
#+BEGIN_SRC ruby
|
755
|
+
Employee = Class.new(Person) do
|
756
|
+
def hello
|
757
|
+
...
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
e = Employee.new
|
762
|
+
e.hello
|
763
|
+
#+END_SRC
|
764
|
+
|
765
|
+
Person 成为 Employee 的父类
|
766
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
* ActiveRecord 简明教程
|
2
|
+
|
3
|
+
ActiveRecord 是 Rails 中的 ORM 框架,它将数据库表映射到 Ruby 对象,让我们可以通过 Ruby 对象来操作数据库。
|
4
|
+
|
5
|
+
|
6
|
+
** 定义模型类
|
7
|
+
|
8
|
+
模型类是继承自 ActiveRecord::Base 的类,它们是数据库中的表在 Ruby 语言中的映射。
|
9
|
+
|
10
|
+
#+BEGIN_SRC ruby
|
11
|
+
class User < ActiveRecord::Base
|
12
|
+
end
|
13
|
+
#+END_SRC
|
14
|
+
|
15
|
+
而 User 对象的属性和数据库表的字段的对应关系,是 ActiveRecord 根据数据库中的表信息,自动完成的。
|
16
|
+
|
17
|
+
在 Arql 中,模型类的定义是 Arql 根据数据库表的表信息自动生成的,所以我们不需要像这里一样手动定义模型类。
|
18
|
+
|
19
|
+
** 定义关联关系
|
20
|
+
|
21
|
+
在 ActiveRecord 中,我们可以通过 has_many, belongs_to, has_one 等方法来定义模型类之间的关联关系。
|
22
|
+
|
23
|
+
- =has_many= 表明此表是一对多关系的“一”方
|
24
|
+
- =has_one= 表明此表是一对一关系的属主
|
25
|
+
- =belongs_to= 表明此表是一对多或一对一关系的从属方
|
26
|
+
- =has_and_belongs_to_many= 表明此表是多对多关系的其中一方
|
27
|
+
|
28
|
+
|
29
|
+
使用 Arql 查询数据库时,我们希望也可以通过定义好的关联关系来查询数据。
|
30
|
+
|
31
|
+
例如,我们有两个模型类 User 和 Post,User 有多个 Post,Post 属于一个 User, 如果我们希望查询张三的所有文章,
|
32
|
+
|
33
|
+
在不使用关联关系的情况下,我们需要这样查询:
|
34
|
+
|
35
|
+
#+BEGIN_SRC ruby
|
36
|
+
user = User.find_by(name: '张三')
|
37
|
+
posts = Post.where(user_id: user.id)
|
38
|
+
#+END_SRC
|
39
|
+
|
40
|
+
而如果我们定义了关联关系,我们可以这样查询:
|
41
|
+
|
42
|
+
#+BEGIN_SRC ruby
|
43
|
+
user = User.find_by(name: '张三')
|
44
|
+
posts = user.posts
|
45
|
+
#+END_SRC
|
46
|
+
|
47
|
+
关联关系是在模型类中定义的,而 Arql 中模型类是 Arql 替我们自动生成的,那么我们要在哪里定义关联关系呢?
|
48
|
+
|
49
|
+
别忘了 Ruby 的类是可以重新打开的,我们可以在 Arql 中重新打开模型类,定义关联关系:
|
50
|
+
|
51
|
+
#+BEGIN_SRC ruby
|
52
|
+
class User < ActiveRecord::Base
|
53
|
+
has_many :posts
|
54
|
+
end
|
55
|
+
#+END_SRC
|
56
|
+
|
57
|
+
像 has_many, belongs_to, has_one 这样的方法,ActiveRecord 会根据默认的规则,来关联关系关联到的是哪个表的哪个字段。这也就是 Rails 中所谓的约定大于配置的体现。
|
58
|
+
|
59
|
+
使用 Arql 查询既有系统的数据库时,数据库中的表、字段名称往往不符合 Rails 的规则约定,这时我们可以通过传递参数来指定关联关系的关联字段:
|
60
|
+
|
61
|
+
#+BEGIN_SRC ruby
|
62
|
+
class User < ActiveRecord::Base
|
63
|
+
has_many :posts, foreign_key: 'author_id', class_name: 'Article', primary_key: 'uid'
|
64
|
+
end
|
65
|
+
#+END_SRC
|
66
|
+
|
67
|
+
=has_many=, =belongs_to=, =has_one= 方法常用的参数如下:
|
68
|
+
|
69
|
+
- =class_name=: 表明此关联关系对应的对方的 Model 类名
|
70
|
+
- =foreign_key=: 表明此关联关系中,从属表一侧的关联字段名
|
71
|
+
- =primary_key=: 表明此关联关系中,属主表一侧的关联字段名
|
72
|
+
- =join_table=: 在多对多关系中,表明关联两个表的中间表名
|
73
|
+
- =association_foreign_key=: 在多对多关系中,表明对方 Model 在中间表中的关联字段名
|
74
|
+
|
75
|
+
** 简单 CRUD
|
76
|
+
|
77
|
+
参考:https://guides.rubyonrails.org/active_record_querying.html
|
78
|
+
|
79
|
+
** 参考
|
80
|
+
|
81
|
+
- https://guides.rubyonrails.org/active_record_basics.html
|
82
|
+
- https://guides.rubyonrails.org/active_record_querying.html
|
83
|
+
- https://guides.rubyonrails.org/association_basics.html
|
@@ -0,0 +1,114 @@
|
|
1
|
+
* Pry 简明教程
|
2
|
+
|
3
|
+
** Pry 是什么?
|
4
|
+
|
5
|
+
和 Python 一样,Ruby 也有自己的 REPL 工具,叫做 irb(Interactive Ruby)。
|
6
|
+
|
7
|
+
Pry 是另外一个功能更加强大的 Ruby REPL 工具,它可以让你在 Ruby REPL 中做很多事情,比如查看源码、查看文档、调试等等。
|
8
|
+
|
9
|
+
Arql 的主要功能就是基于 Pry 实现的,所以你可以把 Pry 当做 Arql 的一个子集。Pry 的命令和功能在 Arql 中都是可以使用的。而且 Arql 还提供了一些额外的 Pry 命令。
|
10
|
+
|
11
|
+
|
12
|
+
当然也可以单独安装和使用 Pry。
|
13
|
+
|
14
|
+
|
15
|
+
** 单独安装 Pry
|
16
|
+
|
17
|
+
#+BEGIN_EXAMPLE
|
18
|
+
$ gem install pry
|
19
|
+
#+END_EXAMPLE
|
20
|
+
|
21
|
+
Pry 本身也支持扩展,你可以安装一些 Pry 的插件,比如 pry-doc、pry-byebug 等等。
|
22
|
+
|
23
|
+
#+BEGIN_EXAMPLE
|
24
|
+
$ gem install pry-doc
|
25
|
+
$ gem install pry-byebug
|
26
|
+
#+END_EXAMPLE
|
27
|
+
|
28
|
+
|
29
|
+
** 单独使用 Pry
|
30
|
+
|
31
|
+
#+BEGIN_EXAMPLE
|
32
|
+
$ pry
|
33
|
+
#+END_EXAMPLE
|
34
|
+
|
35
|
+
|
36
|
+
** 常用的 Pry 命令
|
37
|
+
|
38
|
+
|
39
|
+
*** 查看帮助
|
40
|
+
|
41
|
+
#+BEGIN_EXAMPLE
|
42
|
+
[1] pry(main)> help
|
43
|
+
#+END_EXAMPLE
|
44
|
+
|
45
|
+
|
46
|
+
*** 查看变量
|
47
|
+
|
48
|
+
#+BEGIN_EXAMPLE
|
49
|
+
[2] pry(main)> ls
|
50
|
+
#+END_EXAMPLE
|
51
|
+
|
52
|
+
|
53
|
+
*** 查看一个实例方法的源码
|
54
|
+
|
55
|
+
#+BEGIN_EXAMPLE
|
56
|
+
[3] pry(main)> show-source ActiveRecord::Base#save
|
57
|
+
#+END_EXAMPLE
|
58
|
+
|
59
|
+
|
60
|
+
*** 查看一个实例方法的文档
|
61
|
+
|
62
|
+
#+BEGIN_EXAMPLE
|
63
|
+
[4] pry(main)> show-doc ActiveRecord::Base#save
|
64
|
+
#+END_EXAMPLE
|
65
|
+
|
66
|
+
|
67
|
+
*** 查看一个类的的源码
|
68
|
+
|
69
|
+
#+BEGIN_EXAMPLE
|
70
|
+
[5] pry(main)> show-source ActiveRecord::Base
|
71
|
+
#+END_EXAMPLE
|
72
|
+
|
73
|
+
|
74
|
+
*** 查看一个类的文档
|
75
|
+
|
76
|
+
#+BEGIN_EXAMPLE
|
77
|
+
[6] pry(main)> show-doc ActiveRecord::Base
|
78
|
+
#+END_EXAMPLE
|
79
|
+
|
80
|
+
|
81
|
+
*** 在 Pry 中直接修改代码
|
82
|
+
|
83
|
+
你甚至可以在 Pry 中用 edit 命令直接修改代码,然后 Pry 会自动保存修改后的代码到一个临时文件中,然后你可以在 Pry 中直接调用修改后的代码。
|
84
|
+
|
85
|
+
#+BEGIN_EXAMPLE
|
86
|
+
[7] pry(main)> edit ActiveRecord::Base#save
|
87
|
+
#+END_EXAMPLE
|
88
|
+
|
89
|
+
** 分号
|
90
|
+
|
91
|
+
在 Ruby 语言中, 行尾的分号是可以省略的。
|
92
|
+
|
93
|
+
|
94
|
+
Pry 中每次执行一个 Ruby 表达式,都会自动打印出这个表达式的值:
|
95
|
+
|
96
|
+
#+BEGIN_EXAMPLE
|
97
|
+
[7] pry(main)> user.posts = Post.all
|
98
|
+
=> [#<Post id: 1, title: "Hello World", content: "Hello World", created_at: "2016 -12-12 12:12:12", updated_at: "2016-12-12 12:12:12">,
|
99
|
+
#<Post id: 2, title: "Hello Ruby", content: "Hello Ruby", created_at: "2016-12-12 12:12:12", updated_at: "2016-12-12 12:12:12">,
|
100
|
+
... ...]
|
101
|
+
#+END_EXAMPLE
|
102
|
+
|
103
|
+
这是通过调用 Ruby 的 表达式的值对象的 inspect 方法 (Object#inspect 方法)实现的。如果你不想打印出这个值,可以在表达式后面加上分号:
|
104
|
+
|
105
|
+
#+BEGIN_EXAMPLE
|
106
|
+
[8] pry(main)> user.posts = Post.all;
|
107
|
+
#+END_EXAMPLE
|
108
|
+
|
109
|
+
我们知道在 ActiveRecord 中,像 =User.where(gender: 'Male')= 这样的表达式,返回结果是一个 ActiveRecord::Relation 对象,而不是一个数组。
|
110
|
+
这样设计的目的是为了支持 Lazy Loading,只有在需要的时候才会去执行 SQL 查询。但是当我们在 Pry 中直接对 =User.where(gender: 'Male')= 的时候,却发现
|
111
|
+
它立即执行了 SQL 查询,并且输出的是一个数组对象;这就是因为 Pry 在打印对象的时候,会调用对象的 inspect 方法,而 ActiveRecord::Relation 对象的 inspect 方法
|
112
|
+
会立即执行 SQL 查询并返回一个数组对象。如果你不想立即执行 SQL 查询,就可以在表达式后面加上分号。
|
113
|
+
|
114
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.31
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Liu Xiang
|
@@ -360,6 +360,9 @@ files:
|
|
360
360
|
- lib/arql/version.rb
|
361
361
|
- oss-files-zh_CN.org
|
362
362
|
- oss-files.org
|
363
|
+
- ruby-guides-for-java-developer-zh_CN.org
|
364
|
+
- simple-active-record-guide-zh_CN.org
|
365
|
+
- simple-pry-guides-zh_CN.org
|
363
366
|
- sql-log-zh_CN.org
|
364
367
|
- sql-log.org
|
365
368
|
homepage: https://github.com/lululau/arql
|