Ruby エラー - 期待された配列または文字列、ハッシュを取得しました
概要
編集 - コメントアウトされたセクションを再追加して、これを解決しようとした他の方法を含めて、プロセスについてのさらなる洞察を提供できることを願っています。現時点では、このプログラムは機能していますが、Google シートから生成して添付する必要がある「一時」ファイルの代わりに、ローカルのスプレッドシートを添付ファイルとして送信します。これは明らかに私が問題を抱えている部分であり、処理されたデータを含む一時ファイルをダウンロードして添付するために、それに応じてコードを追加する方法を知る必要があります…
こんにちは。ご協力いただきありがとうございます。シート ファイルをダウンロードして電子メールに添付しようとすると、Ruby プログラムで問題が発生します。このプログラムは、ローカルのスプレッドシートから再注文するユニットを追加し、それをシート ファイルに追加し、ダウンロードして添付ファイルとして電子メールで送信することになっています。電子メールの本文に対応する SKU と QTY をテキストとして含む電子メールを送信するプログラムを取得できます。また、指定された Google シートに SKU と QTY を追加するプログラムを取得できますが、シート ファイルをダウンロードできません添付ファイルとして電子メールに送信します。私が得ている正確なエラーは次のとおりです。
C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/signet-0.18.0/lib/signet/oauth_2/client.rb:420:in `scope=': Expected Array or String, got Hash (TypeError)
raise TypeError, "Expected Array or String, got #{new_scope.class}"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/signet-0.18.0/lib/signet/oauth_2/client.rb:193:in `update!'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/signet-0.18.0/lib/signet/oauth_2/client.rb:115:in `initialize'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/googleauth-0.17.1/lib/googleauth/service_account.rb:105:in `initialize'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/googleauth-0.17.1/lib/googleauth/service_account.rb:80:in `new'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/googleauth-0.17.1/lib/googleauth/service_account.rb:80:in `make_creds'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/google_drive-3.0.7/lib/google_drive/session.rb:89:in `from_service_account_key'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/google_drive-3.0.7/lib/google_drive/session.rb:86:in `block in from_service_account_key'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/google_drive-3.0.7/lib/google_drive/session.rb:85:in `open'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/google_drive-3.0.7/lib/google_drive/session.rb:85:in `from_service_account_key'
from ex5.rb:38:in `insert_data'
from ex5.rb:83:in `process_order'
from ex5.rb:144:in `<main>'
誰か私のコードをレビューして、何が間違っているのか教えていただけますか?
require 'bundler'
require 'google_drive'
require 'roo'
require 'google/apis/sheets_v4'
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'mail'
Bundler.require
begin
def read_spreadsheet(file_path)
spreadsheet = Roo::Excelx.new(file_path)
header = spreadsheet.row(2)
data = []
(3..spreadsheet.last_row).each do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
data << row
end
data
end
def format_data_plain_text(data)
formatted_data = ""
data.each do |row|
unless row['REORDER QTY'].to_i.zero? || row['AC SKU'].to_s.empty?
sku = row['AC SKU'].to_s
quantity = row['REORDER QTY'].to_s
formatted_data += "#{sku}\t#{quantity}\n"
end
end
formatted_data.to_s
end
def insert_data(file_path, formatted_data)
session = GoogleDrive::Session.from_service_account_key("client_secret.json")
spreadsheet_title = "Reorder Details"
spreadsheet = session.spreadsheet_by_title(spreadsheet_title)
worksheet = spreadsheet.worksheets.first
first_empty_row = 1
while !worksheet[first_empty_row, 1].empty? && first_empty_row <= worksheet.num_rows
first_empty_row += 1
end
if first_empty_row == 1
worksheet[first_empty_row, 1] = "SKU"
worksheet[first_empty_row, 2] = "QUANTITY"
first_empty_row += 1
end
puts formatted_data.class
data_lines = formatted_data.split("\n")
data_lines.each_with_index do |line, index|
columns = line.split("\t")
worksheet[first_empty_row + index, 1] = columns[0]
worksheet[first_empty_row + index, 2] = columns[1]
end
worksheet.save
end
def download_google_sheet(sheet_id)
session = GoogleDrive::Session.from_service_account_key("client_secret.json")
spreadsheet = session.spreadsheet_by_key(sheet_id)
worksheet = spreadsheet.worksheets.first
temp_file = Tempfile.new(['google_sheet', '.xlsx'], encoding: 'UTF-8')
worksheet.export_as_file(temp_file.path)
temp_file.path
temp_file.close
end
def process_order(file_path)
order_details = read_spreadsheet(file_path)
formatted_data = format_data_plain_text(order_details)
insert_data(file_path, formatted_data)
puts "Data inserted into Google Sheets."
sheet_id = 'numbersandletters'
temp_file_path = download_google_sheet(sheet_id)
send_email(file_path, temp_file_path)
end
def send_email(file_path, temp_file_path)
# Configure your email settings
options = {
address: 'smtp.gmail.com',
port: 587,
user_name: '[email protected]',
password: 'password',
domain: 'domain.com',
authentication: 'plain',
enable_starttls_auto: true
}
Mail.defaults do
delivery_method :smtp, options
end
order_details = read_spreadsheet(file_path)
formatted_data = format_data_plain_text(order_details)
#temp_file_content = File.read(temp_file_path)
#Specify the email content
mail = Mail.new do
from '[email protected]'
to '[email protected]'
subject "Order Details:"
body "Hey Tim. Hope you're doing well! I just need to place an order for the following items:\n\nThis will be using Net Terms.\n\nPlease let me know if you need anything else from me! Have a great week!"
add_file(file_path)
end
# Send the email
mail.deliver!
end
#def send_email(file_path)
# gmail = Gmail.connect("[email protected]", "password")
#
# subject = "Reorder Details"
# body = "Please find the attached Google Sheet with reorder details."
#
# gmail.deliver do
# to "[email protected]"
# subject subject
# text_part do
# body body
# end
# add_file file_path
# end
# gmail.logout
#end
file_path = 'path\to\file'
process_order(file_path)
#def export_google_sheet(file_id, export_format, export_directory)
# session = GoogleDrive::Session.from_service_account_key('path\to\cliet_secret.json')
#
# spreadsheet = session.file_by_id(file_id)
# export_filename = "AC_Order_#{Time.now.strftime('%Y-%m-%d')}.#{export_format.split('/').last}"
# export_path = File.join(export_directory, export_filename)
#
# spreadsheet.export_as_file(export_format, export_path)
#
# puts "File exported to: #{export_path}"
#end
#file_id = 'randomnumbersandletters'
#export_format = 'pdf'
#export_directory = '\path\to\export'
#export_google_sheet(file_id, export_format, export_directory)
end
データが文字列に変換されることを保証するために、「format_data_plain_text」メソッドの最後に format_data.to_s を追加してみました。 「temp_file_content = File.read(temp_file_path)」も追加しようとしましたが、これらの追加に関係なく同じエラーが返されたため、別のものである必要があります。
解決策
GoogleDrive::Session.from_service_account_key には次のメソッド シグネチャがあります。
def self.from_service_account_key(
json_key_path_or_io, scope = DEFAULT_SCOPE, client_options = nil,
request_options = nil
)
4 つの位置引数は次のとおりです。
電話をかける場合:
GoogleDrive::Session.from_service_account_key("client_secret.json", timeout_sec: 300)
timeout_sec: 300 はスコープ引数のハッシュとして扱われるため、型エラーになります。
3 番目の引数を指定するには、2 番目の引数も渡す必要があります。
GoogleDrive::Session.from_service_account_key(
"client_secret.json",
GoogleDrive::Session::DEFAULT_SCOPE,
timeout_sec: 300
)
これは位置引数の欠点です。キーワード引数を使用した方が簡単でしょう。