如何处理 Crystal 中的冗余类型?
How to handle redundant types in Crystal?
我正在使用 crystal language,到目前为止效果很好。不幸的是,我觉得 my 代码到处都是类型,变得有点太乱了。
例如:
# ---------=====----++---
# Grab characters
# ---------=====----++---
def handle_character_list(msg, client)
result = {} of String => Array(Tuple(Int64, String, String, Int16, Int8)) | Int32 | Int16 | Int64 | String
result["characters"] = db.query_all "select character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc from rpg_characters where user_id = ? ", client.user_id,
as: {Int64, String, String, Int16, Int8}
result["max_char_slots"] = client.user_data["max_char_slots"]
puts result
end
在查看 db.query_all method 时,它说:
returns an array where the value of each row is read as the given type
根据上述内容,如果要返回这些类型,为什么我需要再次将这些类型显式设置为我的 result
变量?
我觉得我做错了什么,任何advice/insight不胜感激。
我首先想到的是您的散列类型的大小。您使用 Hash
的方式似乎与 Ruby 相同。不要。
在 Ruby 或其他动态语言中,Hash
es 或对象用作通用数据容器,几乎类似于未命名的 classes。在 Crystal 中,哈希具有单一类型的键和单一类型的值,这使得它们不适合这项任务。您想告诉 Crystal 更多关于数据结构的信息,因此您不必一直重复。
首先要做的是查看 result
对象。它可以简单地转换成 record Result
:
record Result,
characters: Array({Int64, String, String, Int16, Int8}),
max_char_slots: Int32
方法就变成了
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
characters = db.query_all sql, client.user_id, as: {Int64, String, String, Int16, Int8}
max_char_slots = client.user_data["max_char_slots"]
Result.new(characters, max_char_slots)
end
但是,通过查看该方法,这条 Result
记录可能只用在一个地方 - 来自该方法的 return 数据。在那种情况下,您不太可能想给它一个更正式的名称。在这种情况下,您可以使用 NamedTuple
。它们有点像匿名记录。
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
{
characters: db.query_all(sql, client.user_id, as: {Int64, String, String, Int16, Int8}),
max_char_slots: client.user_data["max_char_slots"]
}
end
更进一步,我们可以看到"Character"也是一个类型:
class Character
getter id : Int64
getter name : String
getter created : Time
getter level : Int16
getter cc : Int8
def initialize(@id, @name, @created, @level, @cc)
end
end
然后我们可以使用 DB.mapping
来定义 Character
class 在数据库中的外观。
class Character
DB.mapping({
id: Int64,
name: String.
created: Time,
level: Int16,
cc: Int8
})
def initialize(@id, @name, @created, @level, @cc)
end
end
这个定义和前面的定义是等价的,因为DB.mapping
自动为我们生成getter。
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
{
characters: db.query_all(sql, client.user_id, as: Character),
max_char_slots: client.user_data["max_char_slots"]
}
end
更进一步,我将把它提取到两个方法中,每个方法只做一件事,我可能会使 client.user_data
类型更安全:
def characters_for_user(user_id)
sql = <<-SQL
SELECT character_id, character_name, created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
db.query_all(sql, user_id, as: Character)
end
def handle_character_list(msg, client)
{
characters: characters_for_user(client.user_id),
max_character_slots: client.user_data.max_char_slots
}
end
这只是我关于如何编写您显示的代码的思考过程。我对您的代码和数据库做了很多假设,这些假设可能是错误的(即 "created" 是 mysql 中的 DATETIME)。我试图展示一个思考过程,而不是一个最终的解决方案。希望对您有所帮助。
我正在使用 crystal language,到目前为止效果很好。不幸的是,我觉得 my 代码到处都是类型,变得有点太乱了。
例如:
# ---------=====----++---
# Grab characters
# ---------=====----++---
def handle_character_list(msg, client)
result = {} of String => Array(Tuple(Int64, String, String, Int16, Int8)) | Int32 | Int16 | Int64 | String
result["characters"] = db.query_all "select character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc from rpg_characters where user_id = ? ", client.user_id,
as: {Int64, String, String, Int16, Int8}
result["max_char_slots"] = client.user_data["max_char_slots"]
puts result
end
在查看 db.query_all method 时,它说:
returns an array where the value of each row is read as the given type
根据上述内容,如果要返回这些类型,为什么我需要再次将这些类型显式设置为我的 result
变量?
我觉得我做错了什么,任何advice/insight不胜感激。
我首先想到的是您的散列类型的大小。您使用 Hash
的方式似乎与 Ruby 相同。不要。
在 Ruby 或其他动态语言中,Hash
es 或对象用作通用数据容器,几乎类似于未命名的 classes。在 Crystal 中,哈希具有单一类型的键和单一类型的值,这使得它们不适合这项任务。您想告诉 Crystal 更多关于数据结构的信息,因此您不必一直重复。
首先要做的是查看 result
对象。它可以简单地转换成 record Result
:
record Result,
characters: Array({Int64, String, String, Int16, Int8}),
max_char_slots: Int32
方法就变成了
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
characters = db.query_all sql, client.user_id, as: {Int64, String, String, Int16, Int8}
max_char_slots = client.user_data["max_char_slots"]
Result.new(characters, max_char_slots)
end
但是,通过查看该方法,这条 Result
记录可能只用在一个地方 - 来自该方法的 return 数据。在那种情况下,您不太可能想给它一个更正式的名称。在这种情况下,您可以使用 NamedTuple
。它们有点像匿名记录。
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
{
characters: db.query_all(sql, client.user_id, as: {Int64, String, String, Int16, Int8}),
max_char_slots: client.user_data["max_char_slots"]
}
end
更进一步,我们可以看到"Character"也是一个类型:
class Character
getter id : Int64
getter name : String
getter created : Time
getter level : Int16
getter cc : Int8
def initialize(@id, @name, @created, @level, @cc)
end
end
然后我们可以使用 DB.mapping
来定义 Character
class 在数据库中的外观。
class Character
DB.mapping({
id: Int64,
name: String.
created: Time,
level: Int16,
cc: Int8
})
def initialize(@id, @name, @created, @level, @cc)
end
end
这个定义和前面的定义是等价的,因为DB.mapping
自动为我们生成getter。
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
{
characters: db.query_all(sql, client.user_id, as: Character),
max_char_slots: client.user_data["max_char_slots"]
}
end
更进一步,我将把它提取到两个方法中,每个方法只做一件事,我可能会使 client.user_data
类型更安全:
def characters_for_user(user_id)
sql = <<-SQL
SELECT character_id, character_name, created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
db.query_all(sql, user_id, as: Character)
end
def handle_character_list(msg, client)
{
characters: characters_for_user(client.user_id),
max_character_slots: client.user_data.max_char_slots
}
end
这只是我关于如何编写您显示的代码的思考过程。我对您的代码和数据库做了很多假设,这些假设可能是错误的(即 "created" 是 mysql 中的 DATETIME)。我试图展示一个思考过程,而不是一个最终的解决方案。希望对您有所帮助。