arql 0.3.30 → 0.3.31
Sign up to get free protection for your applications and to get access to all the features.
- 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
|