Ruby在其标准库中有很多有用的东西。
学习其中的内容以及如何使用它是非常有帮助的,特别是在编写小脚本时,你不想拉着bundler开始制作Gemfile等等。
这些库很强大,由核心团队维护,应该是非常可靠的,比你遇到的大多数Ruby gem都要长。
1.Net/HTTP和open-uri
我想从我建立的大多数应用程序中需要的东西开始,那就是执行HTTP请求的能力。
在过去,我选择使用HTTP.rbgem,但对于简单的任务,学习Net/http甚至open-uri对于简单的GET请求真的很有用。
require "net/http"
require "json"
# Net/HTTP GET request
uri = URI("https://swapi.dev/api/films/1/")
resp = Net::HTTP.get(uri)
json = JSON.parse(resp)
puts json["title"] # => "A New Hope"
# Net/HTTP POST request
uri = URI("https://example.org/users")
params = {
"name" => "Bob",
"email" => "bob@example.org",
}
res = Net::HTTP.post_form(uri, params)
puts res.body
# Net/HTTP Full form: delete/put/patch requests
uri = URI("http://example.org/some_path")
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Delete.new uri
# or request = Net::HTTP::Patch.new uri
# or request = Net::HTTP::Put.new uri
response = http.request(request) # Net::HTTPResponse object
puts response.body
end
如果你只需要一个GET请求,用open-uri ,事情就更简单了。
require "open-uri"
require "json"
URI.open("https://swapi.dev/api/films/1/") do |io|
json = JSON.parse(io.read)
puts json["title"] # => "A New Hope"
end
# or
uri = URI("https://swapi.dev/api/films/1/")
puts uri.open.read
2.CSV
当我创建一个管理部分时,我经常需要输出某种形式的报告来衡量一个应用程序的表现。Ruby内置了这个功能,要把一些数据放到CSV中是非常简单的。
require "csv"
module CustomerExport
module_function
def generate(customers)
CSV.generate do |csv|
# Define the headers
csv << %w[id name email country_code created_at updated_at]
# Add a new row for each customer record
customers.each do |customer|
csv <<
[
customer.id,
customer.name,
customer.email,
customer.country_code,
customer.created_at,
customer.updated_at
]
end
end
end
end
CustomerExport.generate(Customer.all)
如果你想做一个Rails路线来下载这个...
module Admin
class ExportsController < AdminBaseController
def customers
customers = Customer.all
respond_to do |format|
format.csv do
filename = "customers-#{Time.zone.now.strftime("%Y-%m-%d")}"
filename += ".csv"
headers["Content-Type"] ||= "text/csv"
headers["Content-Disposition"] =
"attachment; filename=\"#{filename}\""
render plain: CustomerExport.generate(customers)
end
end
rescue => e
flash[:error] = "Failed to generate export: #{e.message}"
redirect_to admin_path
end
end
end
解析CSV(例如:从用户那里导入CSV文件)也是内置的,你可以阅读文档了解更多。
3.SimpleDelegator
我喜欢使用SimpleDelegator来为ActiveRecord对象添加额外的行为,例如将Stripe的特定代码包装成StripeWrappedUser 或类似的东西,这样可以避免在ActiveRecord对象上放置大量的方法,但可以用额外的功能来增强该类。
require "delegate"
require "digest"
class User < Struct.new(:id, :email)
end
class BillingUser < SimpleDelegator
def billing_system_id
Digest::SHA1.hexdigest(id.to_s + email)
end
end
bob = User.new(1, "bob@example.com")
bob = BillingUser.new(bob)
puts bob.email # => bob@example.com
puts bob.billing_system_id # => 186da93f0b39990e034a80cc4b45c8ec253f2a1a
4.Struct和OpenStruct
Struct包含在Ruby核心中,它不需要,而且是这两个对象中更严格的一个,而且性能也更强。
我发现它对两件事特别有用。
- 作为一个值对象,给一些数据片断起一个名字,否则就是一个Hash。
- 在测试中,可以创建模拟对象,用来表示假的东西,比如FakeUser。
User = Struct.new(:id, :email)
alice = User.new(1, "alice@example.org")
puts alice.id # => 1
puts alice.email # => alice@example.org
# Can also be defined with additional methods
class User < Struct.new(:id, :email)
def send_welcome_email
UserMailer.welcome(self).deliver_later
end
end
同时,OpenStruct更加灵活,可以作为Hash或来自API的JSON的一个整洁的包装。
require "ostruct"
data = OpenStruct.new(id: 5, name: "Jeffrey")
# New attributes can be set at runtime:
data.email = "jeff@example.org"
puts data.id # => 5
puts data.name # => "Jeffrey"
puts data.email # => "jeff@example.org"
5.PStore
PStore是一个非常简单的键值数据存储,当你需要在一个非生产系统中存储数据的地方时,PStore会很有帮助(也就是说,这不是ActiveRecord的好替代品)。
require "pstore"
store = PStore.new("my_db.pstore")
# Write some data
store.transaction do
store["person-1"] = { name: "Bob", email: "bob@example.org" }
end
# Read some data
store.transaction(true) do
puts store["person-1"] # => {:name=>"Bob", :email=>"bob@example.org"}
end
6.Minitest
在处理较小的脚本时,我喜欢拉入一个测试框架,在测试驱动一些代码时做一些断言。
在使用Rails时,我通常更喜欢RSpec,但它比Minitest更费力,因为它包含在Ruby中。
Minitest的好处是,它只是Ruby--没有什么神奇的事情发生,而且非常容易理解。
require "minitest/test"
require "minitest/autorun"
class APIResponse < Struct.new(:body, :error)
def success?
error.nil?
end
end
class APIResponseTest < Minitest::Test
def test_successful_responses
resp = APIResponse.new("Well done!", nil)
assert resp.success?
end
def test_error_responses
resp = APIResponse.new(nil, StandardError.new("400 bad request"))
refute resp.success?
end
end
# Running:
# ..
# Finished in 0.000957s, 2089.8640 runs/s, 2089.8640 assertions/s.
# 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
7.记录器
使用logger可以在编写脚本时清理大量的调试输出,然后你可以控制日志级别来显示更多/更少的输出。注意:在Rails中使用Rails.logger 。
require "logger"
logger = Logger.new(STDOUT)
# Setting :warn will only show warning, error and fatal.
logger.level = :warn
# Can tweak log output to be less verbose
logger.formatter = proc do |severity, datetime, progname, msg|
"#{severity[0...1]} #{datetime.strftime('%H:%M %b %d %Y')}: #{msg}\n"
end
logger.debug("Created logger") # wont be displayed in current log level
logger.info("Program started") # wont be displayed in current log level
logger.warn("Nothing to do!")
logger.error("Something really bad!")
# =>
# W 14:56 Oct 22 2021: Nothing to do!
# E 14:56 Oct 22 2021: Something really bad!
在Ruby标准库中还有很多需要熟悉的东西,但这7个是我一次又一次回来的东西。