20210514のRubyに関する記事は19件です。

Rails バリデーションの日本語化、レイアウト痔に起きた現象

某プログラミングスクールに通う転職生の、日々悩める日記 初めまして!某プログラミングスクールに通う転職生です。今回初投稿なのですが、投稿しようと思ったきっかけとしては エラーにイラつきすぎて、それを同じく初心者プログラマーの方に繰り返して欲しくないからです。 それでは初投稿、書いていきます!! 今回の現象 現象としては、Railsに入れたDeviseに、バリデーションを追加したのち、レイアウトと日本語を整えたいと思い 色々いじった後、Sign_up画面で何故かルーティングエラーが発生しました。 Sign_up画面のみです。更新ボタンや、その他のボタンを押しても発生します。 ちなみにLog_in画面やその他の画面では発生しません。 はぁぁん!!!???? 初めて出るエラーなので、本気でイラつきました笑 原因はなんなのか!とりあえず日本語化の文言を消してみたり 整えたレイアウトを元に戻したりしてみましたが、エラーは解消せず。 バリデーションエラーメッセージの日本語化は以下の記事を参考にしてください config/locales/models/ja.ymlの部分は、インデントに注意!! インデントで挙動が変わります。 結局の解決方法 Devise追加するとき、routes.rbにDeviseのルーティングって勝手に追加されますよね?? あれに僕は追加で以下の内容を変更を加えています。 routes.rb devise_for :users, :controllers => { //ここの右矢印から :sessions => 'users/sessions', :registrations => 'users/registrations', //ここまで } んで、さっきのエラーに、「ルーティングは上から順に読みます。」って書いてるのを翻訳に当てて思い出し 「あっ、そっか上から読むのか。んじゃあ新規登録(registration)の方が優先度高そうだから 描き直したらいけんじゃね???」と思って、順番を変えて以下のようにしました。 routes.rb devise_for :users, :controllers => { :registrations => 'users/registrations',  //下と入れ替えた :sessions => 'users/sessions', } するとあら不思議。エラー出ません!!!!!!!!!! やっぱ新規登録(registration)の方が優先度高いから、変にルーティングエラーかかってたのかな。。。。。。。。と思い、 イラつきは空の彼方に飛んで行きました。 初投稿に関して 今回初投稿ですが、同じ初心者プログラマーさんの役に立てばなぁと思います! もし間違って入ればご指摘ください! 以上です。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails バリデーションの日本語化、レイアウト時に起きた現象

某プログラミングスクールに通う転職生の、日々悩める日記 初めまして!某プログラミングスクールに通う転職生です。今回初投稿なのですが、投稿しようと思ったきっかけとしては エラーにイラつきすぎて、それを同じく初心者プログラマーの方に繰り返して欲しくないからです。 それでは初投稿、書いていきます!! 今回の現象 現象としては、Railsに入れたDeviseに、バリデーションを追加したのち、レイアウトと日本語を整えたいと思い 色々いじった後、Sign_up画面で何故かルーティングエラーが発生しました。 Sign_up画面のみです。更新ボタンや、その他のボタンを押しても発生します。 ちなみにLog_in画面やその他の画面では発生しません。 はぁぁん!!!???? 初めて出るエラーなので、本気でイラつきました笑 原因はなんなのか!とりあえず日本語化の文言を消してみたり 整えたレイアウトを元に戻したりしてみましたが、エラーは解消せず。 バリデーションエラーメッセージの日本語化は以下の記事を参考にしてください config/locales/models/ja.ymlの部分は、インデントに注意!! インデントで挙動が変わります。 結局の解決方法 Devise追加するとき、routes.rbにDeviseのルーティングって勝手に追加されますよね?? あれに僕は追加で以下の内容を変更を加えています。 routes.rb devise_for :users, :controllers => { //ここの右矢印から :sessions => 'users/sessions', :registrations => 'users/registrations', //ここまで } んで、さっきのエラーに、「ルーティングは上から順に読みます。」って書いてるのを翻訳に当てて思い出し 「あっ、そっか上から読むのか。んじゃあ新規登録(registration)の方が優先度高そうだから 描き直したらいけんじゃね???」と思って、順番を変えて以下のようにしました。 routes.rb devise_for :users, :controllers => { :registrations => 'users/registrations',  //下と入れ替えた :sessions => 'users/sessions', } するとあら不思議。エラー出ません!!!!!!!!!! やっぱ新規登録(registration)の方が優先度高いから、変にルーティングエラーかかってたのかな。。。。。。。。と思い、 イラつきは空の彼方に飛んで行きました。 初投稿に関して 今回初投稿ですが、同じ初心者プログラマーさんの役に立てばなぁと思います! もし間違って入ればご指摘ください! 以上です。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ストロングパラメーターで指定した情報だけ保存

はじめに 今回は学習中に作成した簡易版twitterでストロングパラメーターをアウトプットしていきます。 必要な情報だけをDBに保存していきます 仮に全てのデータ(パラメーター)を受け取る仕様の場合、悪意のあるパラメーターによって第三者にデータを改竄されてしまうなど、セキュリティ上に問題が起きてしまう恐れがあります。 回避するためには、ストロングパラメーターを使用して受け取るパラメーターを制限することが大切です。 実際にどんな情報が送られてきているか このアプリは写真のURLとつぶやきを投稿できるものです。 モデルやビュー等は割愛させていただき下記がコントローラーになります。 実際に投稿ボタンを押したら何が送られてくるのかbinding.pryで確認します。 tweet_controller.rb def new @tweet = Tweet.new end def create binding.pry #一度binding.pryで止めて中身を見るための記述 Tweet.create(tweet_params) end [1] pry(#<TweetsController>)> params => <ActionController::Parameters {"authenticity_token"=>"dGa9JyZphq/J2xJrdx7ZE47LlxWeBimigLTW5GgkU+MuYuCa/qDCnCJ3L5bdMCOVg+IfB+qyDnZdGa3sNHtEJw==", "tweet"=>{"image"=>"https://tech-master.s3.amazonaws.com/uploads/curriculums/images/Rails1-4/sample.jpg", "text"=>"スカイツリーが写った綺麗な景色!"}, "commit"=>"SEND", "controller"=>"tweets", "action"=>"create"} permitted: false> "tweet"の以下の{}に入っているデータだけ欲しいのですが、余計なものまで入っています。 image(写真のURL)とtext(つぶやき)だけのデータが欲しいのでここでストロングパラメーターを使います。 ストロングパラメーター 門番みたいな役割だと思っています。 「なんでも通すわけにはいかんぞ!!」 という感じで記述して許されたparamsしか通しません。 まずはrequireメソッド params.require(:モデル名) 必要とする情報を指定します。主にモデルを指定します。 今回では3行目に記述されているtweetです。 次にpermitメソッド params.require(:モデル名).permit(:キー名, :キー名) してしたキー名をDBに保存することを許可すると覚えます。 今回ではimageとtextを指定します。 params.require(:tweet).permit(:image, :text) このような形になります。 私が覚えやすいなと思った例えを紹介します。 まずはAmazonでイヤホンを頼んだとします。 家に商品届き、Amazonの箱の中に入っています。 その箱を開けるためにrequireメソッドで開封をし、今度はイヤホンの箱が入っています。 イヤホンの箱を開けるためにpermitで開けやっとイヤホン現物となります。笑 def create Tweet.create(tweet_params) end private def tweet_params params.require(:tweet).permit(:image, :text) end end tweet_paramsというストロングパラメーターを定義し、createメソッドの引数に使用して、tweetsテーブルへ保存できます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

system_specでjqueryをテストする

導入 前回の記事で、:rack_test → :seluniumの以降が成功し、無事jqueryなどのDOM操作をrspecでテストできるようになった、わけではありませんでした。 今回はこちらの記事を参考に、正式にDOM操作をrspecでテストできるようになりました。(ありがとうございました!) 結論 結論としましては、前回の記事の設定でDOM操作をテストする為には、以下の3操作が必要でした。 chromedriverのDockerイメージを導入する Dockerfile テスト実行環境をchromedriverに設定 ← 前回の記事の内容 system_spec.rbでjs: trueにする 補足:自分の場合はポート番号が重複するエラーが発生したので(多分そう)、テスト環境のポート番号を変更しました。 rails_helper.rb Dockerfile RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && curl -sL https://deb.nodesource.com/setup_10.x | bash - \ && apt-get install -y nodejs \ && mkdir /myapp +RUN apt-get update && apt-get install -y unzip && \ + CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \ + wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \ + unzip ~/chromedriver_linux64.zip -d ~/ && \ + rm ~/chromedriver_linux64.zip && \ + chown root:root ~/chromedriver && \ + chmod 755 ~/chromedriver && \ + mv ~/chromedriver /usr/bin/chromedriver && \ + sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \ + sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \ + apt-get update && apt-get install -y google-chrome-stable WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock rails_helper.rb RSpec.configure do |config| config.before(:each, type: :system, js: true) do driven_by :remote_chrome Capybara.server_host = IPSocket.getaddress(Socket.gethostname) - Capybara.server_port = 3000 + Capybara.server_port = 3010 Capybara.app_host = "http://#{Capybara.server_host}:#{Capybara.server_port}" end config.include FactoryBot::Syntax::Methods spec/system/site_layouts_spec.rb require 'rails_helper' -RSpec.describe 'Layouts', type: :system do +RSpec.describe 'Layouts', js: true, type: :system do let(:user) { create(:user) } context "user signed in" do : expect(page).to have_link href: notifications_path expect(page).to have_selector "i.fa-bell" expect(page).to have_link href: search_works_path, class: "fa-search" + within("li.dropdown") do + expect(page).to have_link class: "dropdown-toggle" + click_on "#{user.username}" + end expect(page).to have_link "ログアウト", href: destroy_user_session_path expect(page).to have_link "プロフィール", href: user_path(user) expect(page).to have_link "アカウント編集", href: edit_user_registration_path おわりに 最後まで目を通して頂きありがとうございました! ただ、正直まだ全然理解が追いついていなくて恥ずかしいくらいです。将来的には、もっと根本的な理解ができているようなエンジニアになりたい。(本当に
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]おすすめ標準添付ライブラリ!

はじめに Rubyには、標準添付ライブラリというものがあります。これは、Rubyをより便利に活用出来るツールのことです。 これらを活用出来ることで、自分のプログラミングスキルの幅が広がり、メリットしかありませんので、日々の開発のぜひ参考にしてみてください! ファイル/ディレクトリ操作を極める Pathname ファイルパスを表現するクラス。 ※Fileクラスと同様のメソッドが使用可能。(こちらの記事を参照) require 'pathname' # Pathnameクラスの生成 Pathname.new('/tmp') #=> #<Pathname:/tmp> Pathname('/tmp') #=> #<Pathname:/tmp> home = Pathname.new('~') # 連結する foo = home.join('foo.txt') #=> #<Pathname:~/foo.txt> # 部分的なパスを得る foo.dirname #=> #<Pathname:~> foo.basename #=> #<Pathname:foo.txt> foo.expand_path #=> #<Pathname:/Users/tanaka/foo.txt> foo.expand_path.parent #=> #<Pathname:/Users/tanaka> foo.expand_path.parent.parent #=> #<Pathname:/Users> # 文字列に変換する foo.to_path #=> "~/foo.txt" foo.to_s #=> "~/foo.txt" fileutils ファイル操作を行うための処理を集めたモジュール。 require 'fileutils' # ファイルを再帰的にコピーする(第1引数にコピー元のファイル、第2引数にコピー先のディレクトリ) FileUtils.cp_r('src/dir', 'dest/dir') # コピー元を配列で指定 FileUtils.cp_r(%w(one two three), 'dest/dir') # ファイル所有者、グループの変更 FileUtils.chown('tanaka', 'staff', 'dir') FileUtils.chown_R('tanaka', 'staff', 'dir') # パーミッションの変更 FileUtils.chmod(0700, 'dir') FileUtils.chmod_R(0700, 'dir') # ディレクトリの作成 FileUtils.mkdir('foo') FileUtils.mkdir_p('foo/bar/baz') # ファイルやディレクトリの移動 FileUtils.mv('foo', 'bar') # ファイルやディレクトリの削除 FileUtils.rm_r('dir') # ハードリンクの作成 FileUtils.ln('src', 'dest') # シンボリックリンクの作成 FileUtils.ln_s('src', 'dest') FileUtilsのメソッドで使用されるオプション一覧 オプション名 意味 :dereference_root trueだとシンボリックの指す内容コピー、falseはシンボリック自体コピー :force trueだとStandardErrorを無視する :nocreate trueだとファイルを作成しない :noop trueだと実際の処理を行わない :mtime 最終時刻をTimeか秒数で指定 :mode パーミッションを8進数で指定 :preserve trueだと更新時刻もコピー :remove_destination trueだとコピーを行う前に、コピー先を削除 :secure trueだとファイルの削除にFileUtils.#remove_entry_secureを使用 :verbose trueだと詳細を出力 tmpdir 一時的な作業ディレクトリを作成出来ます。 require 'tmpdir' # 一時ディレクトリの絶対パスを取得 Dir.tmpdir #=> "/var/folders/4h/..." # 一時ディレクトリを作成してファイルパスを取得 dir = Dir.mktmpdir do |dir| File.directory?(dir) #=> true dir #=> "/var/folders/4h/..." end # 第1引数にディレクトリ名のプリフィックス、第2引数に親ディレクトリを指定出来る dir = Dir.mktmpdir('prefix', '/tmp/sample') do |dir| File.directory? #=> true dir #=> "/tmp/sample/prefix..." end tmpfile 一時ファイルを作成して操作する事ができます。 ※Fileクラスと同様のメソッドが使用可能。(こちらの記事を参照) require 'tmpfile' # 第1引数にディレクトリ名のプリフィックス、第2引数に親ディレクトリを指定出来る path = Tempfile.open('prefix', '/tmp/sample') do |f| f.puts 'foobar' f.path end path #=> "/tmp/sample/..." File.read(path) #=> "foobar\n" # Tempfileをopenする tmpfile = Tempfile.open('prefix', '/tmp/sample') #=> #<File:/tmp/sample/...> tmpfile.path #=> "/tmp/sample/..." tmpfile.close tmpfile.close! # 再オープンできない 特定のフォーマットのテキストデータを扱う YAML 構造化されたデータをプレーンな文字列で表現するためのデータ形式の1つです。 文字列・整数・浮動小数点・真偽値・nil・配列・ハッシュなどを表現できます! require 'yaml' yaml_string = <<EOS --- remote: host:localhost port: 22 ssh: true username: user password: null EOS # YAMLをオブジェクトとしてロード p setting = YAML.load(yaml_string) #=> {"remote"=>{"host"=>"localhost", ...} # 内容の変更 setting['remote'].update(host: 'example.com' # オブジェクトをYAMLとしてダンプ p YAML.dump(setting) #=> "---\nremote:\n host: example.com\n ..." 内容をYAMLとしてダンプ p setting.to_yaml #=> "---\nremote:\n host: example.com\n ..." JSON YAML同様、人気のあるデータのダンプ形式の1つです。 こちらは、文字列・整数・浮動小数点・真偽値・nil・ハッシュなどを表現できます! require 'json' data = [1, 2.0, {key: 'value'}, nil, true, false] # JSONをダンプする json = data.to_json #=> "[1,2.0,{\"key\": \"value\"},null,true,false]" # ロードする p JSON.load(json) #=> [1, 2.0, {key: 'value'}, nil, true, false] # TimeオブジェクトをJSONにダンプ Time.now.to_json #=> "\"2021-05-14 12:34:56 +0900\"" # RegexpオブジェクトをJSONにダンプ /ruby[0-9]/.to_json #=> ""\"(?-mix:ruby[0-9])\"" # ObjectオブジェクトをJSONにダンプ Object.new.to_json #=> "{}" # RangeオブジェクトをJSONにダンプ (1..10).to_json #=> "\"1..10\"" # SymbolオブジェクトをJSONにダンプ :symbol.to_json #=> "\"symbol\"" CSV コンマと改行で区切ることで表現する形式です。 require 'csv' # 配列をCSV形式の文字列に変換 ['foo', 'bar', 'baz'].to_csv #=> "foo,bar,baz" # CSV形式の文字列を配列に変換 'foo,bar,baz'.parse_csv #=> ["foo", "bar", "baz"] # 複数行のCSVをparseする data = <<EOS foo,bar,baz hoge,fuga,hoge EOS CSV.parse(data) do |row| puts row.join('|') #=> "foo|bar|..." end # ヘッダを指定する data = <<EOS name,age,gender tanaka,21,man yoko,32,woman sato,44,man EOS CSV.parse(data, headers: :first_row) do |row| # ヘッダ名をキーにしてアクセス可能 name = row['name'] age, gender = row.values_at('age', 'gender') puts "#{name} is #{gender} (#{age})" #=> "tanaka is man (21)", ... end # CSVを書き出し headers = %w(name age gender) people = [ ['tanaka', 21, 'man'], ['yoko', 32, 'woman'], ['sato', 44, 'man'] ] csv_str = CSV.generate('', write_headers: true, headers: headers) {|csv| people.each do |person| csv << person end } puts csv_str #=> name,age,gender,\ntanaka,21,man,... CSV.parseに指定出来るオプション オプション名 意味 :col_sep フィールド間のセパレータとして使用する文字列 :row_sep レコード間のセパレータとして使用する文字列 :quote_char シングルクォーとでもフォイールドを囲めるようになる :encoding エンコーディングを指定 :unconverted_fields 変換前の行を配列で得ることができる :headers 1行目をヘッダーとみなす :return_headers ヘッダとみなした1行目もCSVとして読み込む :headers_converters ヘッダ専用のコンバータをProcオブジェクトまたはシンボルを指定 :skip_blanks 空行を飛ばす :force_quotes フィールドの生成時に必ずクォートする :write_headers ヘッダを出力 上級者のための文字列オブジェクト 本来IOではない文字列をIOオブジェクトの代わりに用いることができます。 require 'stringio' def read_upcase(io) io.read.upcase end io = open('|uname') stringio = StringIO.new('tanaka') p read_upcase(io) #=> "DARWIN\n" p read_upcase(stringio) #=> "TANAKA" 集合を扱う 重複の内容そのコレクションを扱うことができるクラスです。 Setを用いることで、集合の比較や要素の追加などを高速で行うことができます。 require 'set' # Enumerable#to_set (1..5)..to_set #=> #<Set: {1,2,3,4,5}> # Set.new Set[1, 3, 5, 7] #=> #<Set: {1, 3, 5, 7}> Set.new(%w(tanaka sato suzuki)) #=> #<Set: {"tanaka", "sato", "suzuki"}> # 長さを返す set = Set[1, 3, 5, 7] set.length #=> 3 # 空にする set = Set[1, 3, 5, 7] set.clear #=> #<Set: {}> # 空か確認 set = Set[1, 3, 5, 7] set.empty? #=> false # 要素の追加 set = Set[1, 3, 5, 7] set << 10 #=> #<Set: {1, 3, 5, 7, 10}> # 要素の削除 set = Set[1, 3, 5, 7, 10] set.delete(10) #=> #<Set: {1, 3, 5, 7}> # 要素が存在しなければ追加し、あれば何もしない set = Set[1, 3, 5, 7] set.add?(9) #=> #<Set: {1, 3, 5, 7, 9}> set.add?(1) #=> nil # 要素が存在すれば削除し、なければ何もしない set = Set[1, 3, 5, 7] set.delete(1) #=> #<Set: {3, 5, 7, 9}> set.delete(100) #=> nil # Setの連結 set = Set[1, 3, 5, 7] set.merge(Set[11, 12, 13]) #=> #<Set: {1, 3, 5, 7, 9, 11, 12, 13}> # 要素をまとめて削除 set = Set[ 1, 3, 5, 7] set.subtract(1, 3) #=> #<Set: {5, 7}> # 真となった要素を削除 ## 何か削除されれば自信を返す set = Set[0, 1, 2, 3, 4, 5, 6, 7] set.reject! {|v| v.zero? } #=> #<Set{1, 2, 3, 4, 5, 6, 7}> ## 常に自身を返す set.delete_if {|v| v.even? } #=> #<Set{1, 3, 5, 7}> # 新たな集合を作る a = %w(a b c).to_set b = %w(c d e f).to_set ## どちらかに含まれる要素の集合(和集合) p a | b #=> #<Set: {"a", "b", "c", "d", "e", "f"}> ## 両方に含まれる要素のみ集合(積集合) p a & b #=> #<Set: {"c"}> ## 片方だけに含まれている集合(対称差) p a ^ b #=> #<Set: {"a", "b", "d". "e", "f"}> ## aの中でbに含まれない要素の集合(差集合) p a - b #=> #<Set: {"a", "b"}> 日付と時刻をもっと詳しく! Time require 'time' # 時刻を表す文字列をTimeオブジェクトに変換 time = Time.parse('2021-05-14 12:34') #=> 2021-05-14 12:34:00 +9000 # 各種フォーマットで表現された文字列を返す time.iso8601 #=> "2021-05-14T12:34:00+09:00" time.httpdate #=> "Fri, 14 May 2021 03:34:00 GMT" time.rfc2822 #=> "Fri, 14 May 2021 12:34:00 +0900" # 第1引数に時刻の文字列、第2引数にフォーマットを指定して、Timeオブジェクトを生成 Time.strptime('2021年5月14日', '%Y年%m月%d日') #=> 2021-05-14 00:00:00 +0900 Date/DateTime 日付を表現するクラス。 require 'date' # 年月日からDateオブジェクトを得る date = Date.new(2021, 5, 14) # 日付を指定した書式の文字列で表現 date.strftime('%Y年%m月%d日') #=> "2021年05月14日" # 妥当な日付か真偽値で確認 Date.valid_date?(2000, 1, 1) #=> false # うるう年か真偽値で確認 Date.leap?(2004) #=> true # 今日の日付を取得 Date.today #=> Fri, 14 May 2021 # 年月日を確認 date.year #=> 2021 date.month #=> 5 date.day #=> 14 # TImeオブジェクトからDateTimeオブジェクトを作成 Time.new.to_date #=> Fri, 14 May 2021 おわりに いくつかのライブラリを紹介しましたが、かなり便利な機能があったんではないでしょうか? 標準添付ライブラリを味方につけてRubyマスターになりましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]Fiberとはなんなのさ!

Fiberとは Fiberを利用することで複数のプログラム間で実行の中断や再開を相互に行わせることができます。 Fiberを用いると「ある処理を途中まで実行して、その後任意のタイミングで、前回の続きから処理を行う」みたいな処理の流れを扱うことが出来ます! 基本的な使い方 Fiberの作成 生成されたファイバは、Fiber#resumeメソッドにより実行を開始する必要があります。 ファイバの切り替えは自動的に行われないので、自分で切り替える必要があります。 fiber = Fiber.new { puts 'Hello, Fiber!' # 実行されたファイバから親に切り替える Fiber.yield puts 'Hello, (again)' } fiber.resume #=> "Hello, Fiber!" fiber.resume #=> "Hello, (again)" fiber.resume #=> FiberError: dead fiber called 引数と戻り値について fiber = Fiber.new {|first| puts 'Hello, Fiber!' second = Fiber.yield('goodbye') puts second puts 'Hello, (again)' } puts fiber.resume('Hello, Fiber!') #=> "Hello, Fiber!" puts fiber.resume(Hello, (again)) #=> "goodbye", "Hello, (agein)", "goodbye, (again)" ジェネレーター ジェネレーターとは、部分的な結果だけを計算して返し、続きを次回に計算することです。 これにより、実際の計算を最小限で済ませて、大きいデータを少ないリソースで扱うことが出来ます。 fiber = Fiber.new { a, b = 0, 1 loop do a, b = b, a + b Fiber.yield(a) end } 10.times.map { fiber.resume } #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] puts fiber.resume #=> "89" puts feiber.resume #=> "144" 標準添付ライブラリを利用してより手軽に! 標準添付ライブラリでは、親子関係のないコルーチンとして扱うことが出来ます。 新たにFiber#transfer使えるようになるメソッドになります。 このメソッドは、ファイバの処理を移した際に、その後自動的に呼び出し元に戻らないということです。 require 'fiber' f1 = Fiber.new { puts 'from f1' } f2 = Fiber.new { f1.transfer puts 'from f2' } # 生成されてからスタートしていないorブロックが終了していないときにtrue f1.alive? #=> true f2.resume #=> "from f1"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

$ bundle exec rake db:migrate RAILS_ENV=production時のエラーに関して

はじめに 作成したアプリをAWSにデプロイしようと作業しており、「$ bundle exec rake db:migrate RAILS_ENV=productionコマンドを入力した際に発生したエラーを解決するまでの道のりです。 同じエラー内容が出ている方の一助になれれば幸いです。 エラー解決に向けて unicornをインストールして、「$ bundle exec rake db:migrate RAILS_ENV=production」コマンドを入力すると、 ec2-user@ip-10-0-0-42 アプリ名]$ bundle exec rake db:migrate RAILS_ENV=production rake aborted! ActiveRecord::AdapterNotSpecified: 'production' database is not configured. Available: ["default", "development", "test"] とエラー表示がされました。 どうも'production' databaseに問題がありそうです。そこでdatabase.ymlを確認してみます。 config/database.yml 〜省略〜 production: <<: *default database: <%= Rails.application.credentials.db[:database] %> username: <%= Rails.application.credentials.db[:username] %> password: <%= Rails.application.credentials.db[:password] %> socket: <%= Rails.application.credentials.db[:socket] %> production databaseあるじゃん!と思い、別の要因を探っていました。 しかし原因はymlファイルのインデントの付け方にありました。 正しくは以下のようになります。 config/database.yml 〜省略〜 production: <<: *default database: <%= Rails.application.credentials.db[:database] %> username: <%= Rails.application.credentials.db[:username] %> password: <%= Rails.application.credentials.db[:password] %> socket: <%= Rails.application.credentials.db[:socket] %> ここでの注意点は、インデントはタブではなく、半角スペースを指定してあげてください。 もしここが上手くいっていないと、「Please note that YAML must be consistently indented using spaces. Tabs are not allowed. 」とエラーになってしまいます。 これで再度「bundle exec rake db:load_config RAILS_ENV=production」コマンドを入力したところ、またエラー表示が…。 == 20210320002528 CreateItems: migrating ====================================== -- create_table(:items) rake aborted! StandardError: An error has occurred, all later migrations canceled: Mysql2::Error: Failed to open the referenced table 'categories': CREATE TABLE `items` (`id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(255), `image` varchar(255), `category_id` bigint, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, INDEX `index_items_on_category_id` (`category_id`), CONSTRAINT `fk_rails_89fb86dc8b` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ) /var/www/rails/men-skincare/db/migrate/20210320002528_create_items.rb:3:in `change' /home/ec2-user/.rbenv/versions/2.5.3/bin/bundle:23:in `load' /home/ec2-user/.rbenv/versions/2.5.3/bin/bundle:23:in `<main>' Caused by: ActiveRecord::StatementInvalid: Mysql2::Error: Failed to open the referenced table 'categories': CREATE TABLE `items` (`id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(255), `image` varchar(255), `category_id` bigint, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, INDEX `index_items_on_category_id` (`category_id`), CONSTRAINT `fk_rails_89fb86dc8b` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ) /var/www/rails/men-skincare/db/migrate/20210320002528_create_items.rb:3:in `change' /home/ec2-user/.rbenv/versions/2.5.3/bin/bundle:23:in `load' /home/ec2-user/.rbenv/versions/2.5.3/bin/bundle:23:in `<main>' Caused by: Mysql2::Error: Failed to open the referenced table 'categories' /var/www/rails/men-skincare/db/migrate/20210320002528_create_items.rb:3:in `change' /home/ec2-user/.rbenv/versions/2.5.3/bin/bundle:23:in `load' /home/ec2-user/.rbenv/versions/2.5.3/bin/bundle:23:in `<main>' Tasks: TOP => db:migrate (See full trace by running task with --trace) うーん、itemsテーブルの作成時にエラーが発生しているようです。 もっと詳しくエラー内容を確認してみると、itemsテーブルを作ろうとしている時に、categoriesテーブルの参照がないよと言われています。 itemsテーブルとcategoriesテーブルの作成順番がうまくいっていないようなので、一度categoryモデルごと削除、再度マイグレーションして、無事エラー解消することができました。 終わりに ymlファイルのインデント規約に関しては知らなかったので良い勉強となりました。 もっとLinuxの知識をつける必要があると感じました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]Threadとはなんなのさ!

Threadとは スレッド(Thread)とはメモリ空間を共有して同時に実行される制御の流れのことで、 スレッド(Thread)という処理を使えば、複数の処理を並行して実施することが可能です。 RubyではThreadクラスを使用します。 基本的な使い方 Threadの作成 Threadの作成には、Thread#forkもしくはThread#newなどを使う files = %w(.xinitrc .zshrc .vimrc) threads = files.map {|file| Thread.fork { num = File.readlines(file).length "#{file}: #{num}" } } # スレッドの終了後に戻り値を返す threads.map(&:value) #=> [".xinitrc: 58", ".zshrc: 51", ".vimrc: 209"] # 単にスレッドの終了を待つ threads.join Threadの取得 # メインスレッドの取得 Thread.main #=> <Thread:...> # 現在実行中のスレッドを取得 Thread.current #=> <Thread:...> # 全てのスレッドの配列を取得 Thread.list #=> [<Thread:...>, #=> <Thread:...>, ...] Threadでの変数の扱い方 他のスレッドと共有したくない値は、ブロックに引数を渡すことができます。 items = %w(foo bar baz) Thread.fork(items) do |item| sleep 1 puts item #=> "foo", "bar", ... end # 子スレッドの終了を待つ (Thread.list - [Thread.current]).each(&:join) スレッド固有のデータを格納 threads = Thread { Thread[:status] = 'in progress' sleep 1 } # スレッド固有のデータにアクセス thread[:status] #=> "in progress" ライフサイクル スレッドの状態はライフサイクルの中で変化していきます。 t = Thread.fork { sleep 1 } # 状態を取得 t.status #=> "sleep" # スレッドが生きているか t.alive? #=> true # スレッドが終了or停止しているか t.stop #=> true スレッドの状態 状態 意味 run 実行可能、実行中 sleep 停止 aborting 終了処理中 false 正常終了 nil 異常終了 Threadの操作 t = Thread.fork { # 自スレッドを停止 Thread.stop # 停止しているスレッドを実行可能にする Thread.wakeup puts 'Thread!' } # 即座にスレッドの処理を実行 t.run #=> "Thread!" # スレッドを終了させる t.kill Threadの優先順位 優先順位を数値で設定することが出来て、値が大きいほど優先度は高くなります。 current = Thread.current current.priority #=> 0 current.priority = 3 current.priority #=> 3 ThreadGroup スレッドを1つのグループにまとめるためにはThreadGroupを利用します。 デフォルトでは、全てのスレッドはThreadGroup::Defaultに所属しています。 group = ThreadGroup.new thread = Thread.fork { sleep 1 } # グループにスレッドを追加 group.add(thread) Mutex 複数のスレッドが同時に1つのデータを参照・更新する処理には適切なはいた処理が必要です。 Mutexを利用することで、相互排他処理を行うためのロックを提供します。 def countup File.open('counter', File::RDWR | File::CREAT) do |f| last_count = f.read.to_i f.rewind f.write last_count + 1 end end mutex = Mutex.new 10.times.map { Thread.fork { # mutexのロックを取得してブロックを実行後に開放する mutex.synchronize { countup } } }.map(&:join) # 正しく10回カウントアップされる puts File.read('counter').to_i thread.rb 標準添付ライブラリであるthread.rbを利用することで、組み込み機能に加えて、キューや状態変数を使えるようになります。 Queue Queueとは、スレッドセーフなFIFOキューです。 スレッドセーフとは、複数のスレッドから同時にアクセスしてのプログラムが壊れないような性質を持っていることです。 require 'thread' queue = Queue.new # ワーカースレッドを3つ用意する workers = 3.times.map {|t| Thread.fork { while req = queue.deq # キューが空になると待機する puts "Worker#{t} processing..." req.call end } } # 10個のリクエストをenqueueする 10.times do |t| # キューを末尾に追加 queue.enq -> { sleep 1 } end # 全てのキューが処理されるまで待機する sleep until queue.empty? # キューが空になったので全スレッドがqueueを待っている p workers.map(&*status) #=> ["sleep", "sleep", "sleep"] ConditionVariable ConditionVariableは状態変数を表現するクラスです。 状態変数とは、排他領域で処理を行っているスレッド同士がお互いに通信し合う手段を提供するための仕組みのことです。 require 'thread' class Bucket def initialize(limit = 5) @appendable = ConditionVariable.new @flushable = ConditionVariable.new @lock = Mutex.new @limit = limit @out = '' end def append(str) @lock.syncronize { # ロックを一時的に開放してカレンとスレッドを停止 @appendable.wait(@lock) unless appendable? @out << str     # waitしているスレッドがあれば、そのうち1つを再開させて再開させたスレッドの配列を返す @flushable.signal if flashable? } end def flush @lock.syncronize { # ロックを一時的に開放してカレンとスレッドを停止 @flashable.wait(@lock) unless flasable? puts '-' * 10 puts @out @out = ''     # waitしているスレッドがあれば、そのうち1つを再開させて再開させたスレッドの配列を返す @appendable.signal if appendable? } end private def appendable? @out.lines.count < @limit end def flasable? !appendable? end end require_relative 'list11.2' bucket = Bucket.new # bucketに結果を書き込む t1 = Thread.fork { 25.times do |t| sleep rand bucket.append("line: #{t}\n") end }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[rails] DateTime#agoがRails5.2まで1.dayといった引数を受け付けていたが6.0からダメになった原因を調べた

Rails6.0への更新中、理由が不明だった為調べました # DateTime#agoの実装 def ago(seconds) since(-seconds) end # rails 5.2 DateTime.current => Fri, 14 May 2021 16:20:00 +0900 DateTime.current.ago(1.day) => Thu, 13 May 2021 16:14:26 +0900 # rails 6.0 DateTime.current => Fri, 14 May 2021 16:31:41 +0900 DateTime.current.ago(1.day) TypeError: not an integer from /bundle/gems/activesupport-6.0.3.7/lib/active_support/core_ext/date_time/calculations.rb:113:in `Rational' 5.2ではagoで呼ばれているDateTime#sinceの実装で、agoの引数をroundしてintegerとなっていたのが、roundしなくなりRationalの引数として不適切になったから出たエラーのようです。 PRを見た感じ、昔はroundをする意味があったが今は無いのでしなくなったという事かな? そもそも、secondsを要求されているので1.dayといった値を渡していたのがおかしくて、たまたまroundされていただけで意図しない使い方していた気がする...! 素直にこう書いておきます DateTime.current - 1.day => Thu, 13 May 2021 16:34:06 +0900 修正PR 5.2 実装 6.0 実装
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ruby 似た文字を除外したランダムな英数字

この記事について 今回のネタのようなことを書いてる記事があまりなかったから、エセファーストペンギンになるがために投稿してやる。 やりたいこと アルファベット大文字(A-Z)、アルファベット小文字(a-z)、数字(0-9)から構成されるランダムな4文字を生成させるコードの発表会。 ただし、1とlのような紛らわしい文字列は除外させる。 生成するやで similar_str = "1lI9q0Oo" SecureRandom.alphanumeric(20).delete("1lI9q0Oo")[0..3] # => "4hA8" SecureRandom.alphanumeric(生成する文字数) A-Z, a-z, 0-9 の合計62種類から成る、ランダムな英数字を生成する。 delete(消したい文字列) 等しい要素が見つかった場合にその要素を削除する。 [0..3] 指定の位置から指定の長さの文字列を取得する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】実務未経験の僕が共同開発に参加して学んだこと

0. はじめに はじめまして! この記事では、「実務未経験の私が Rails 共同開発に挑戦し、そこで学んだことや躓いたこと、反省した点」などについてまとめています。 チームでの開発現場経験を積むことで、「コミュニケーションの適切な取り方」や「わからない部分の質問の仕方」、「コンフリクトの解消の仕方」など、一人での学習では決して学ぶことのできない貴重な経験を積ませていただくことができました。 今回はそのような共同開発の現場で実際に学んだことをまとめて、今後の自分の成長につなげることができたらなと考えております! 自己紹介                                           まずは簡単な自己紹介からさせて頂きます! スペック 24歳 エンジニアを目指している駆け出しエンジニア 学習中の言語:Ruby, Ruby on Rails 今後学習予定の言語や知識:Javascrict, AWS HTML・CSS・Sass・も出来ます。 趣味 勉強 : 好きでやっているので、もう趣味ですw 読書 : 一番好きな本は、SF小説、「千九八四年」です。 TVゲーム : 最近は勉強のため封印中。スマブラしたい。 もうちょっとコロナが落ち着いたら、旅行を趣味に加えたいです。 性格 割と慎重派だと思います。前職で絶対にダブルチェックを欠かさなかったです。 温厚だと思ってます。怒った記憶がない。怒りは無駄だと考えてます。 2. 今回の共同開発に参加しようと思った動機 まず、「なぜ今回の共同開発に参加しようと思ったのか?」の根本的な動機の部分を述べさせて頂きます。端的に言うと、以下のものを「得たい!」と思ったので、共同開発に参加させて頂きました。 コミュニケーションスキルの向上 チームでの開発経験 問題にぶち当たった時に自分で原因を特定し、解決を図るための「自走力」 試行錯誤した上でどうしてもわからない場合、素直に質問できる柔軟性 これらのものを「共同開発」に参加することを通して獲得したいなと思いました。 結果的に、特に「コミュニケーション」の部分に関してはスキルの向上ができたのと、以前よりもより自信を持つことができた気がします。 ここについての具体的なやり取りに関しては後述させて頂きます! 3. Rails 共同開発で取り組んだ内容 共同開発で取り組んだ内容を簡単にご説明していきます! 成果物 今回の共同開発では「ECサイト」の購入者視点の画面の実装に取り組みました。 概要 開発期間:5/1 ~ 6/ 30(2ヶ月間) 週1回のチームミーティングを行い、進捗状況の確認を行う 週2~3回の作業会を行い、それぞれわからないところを質問し合う 構成メンバー 講師・TA:現役エンジニア2名 共同開発メンバー:未経験2名, 現役インターン生1名 使用した言語・技術 バックエンド Ruby(バージョン:2.7.1) Ruby on Rails(バージョン:6.0.3.5) フロントエンド HTML5 CSS3 Bootstrap(5.0.0.beta2) インフラ Docker docker-compose データベース MySQL2(0.5.3) 使用したツール・環境 Slack:レビュー依頼、進捗状況の確認、日報の共有などをしました。 GitHub:タスク管理・確認、プルリクエストを出す、コードレビューを受ける、受けた指摘に対応し、コミットを出すなどをしました。 Googleドライブ:モックアップ、基本設計(ER図/画面遷移図)、画面定義書などのファイルを管理・共有のために使用しました。 Git / Sourcetree:コミットやプッシュなどの git 操作をするために利用しました。 DeepL:英語対策のための翻訳アプリ 使用したエディター:Visual Studio Code 使用したPC:Mac ER図 実装した(する予定)機能 新規ユーザ登録機能 ログイン / ログアウト処理 商品検索 商品詳細へ カートへ カートを開く 購入確定 Top画面に戻る 履歴を開く 注文検索 注文キャンセル ユーザ情報を開く ユーザ情報修正 / 退会処理 修正確定 4. Rails 共同開発で僕が担当した箇所 ①products 関連の DB構築 ER図を見て、必要なカラムや属性を考え、migrationファイルを作成しました。 ↓こういった感じのmigrateファイルを作成 class CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| t.string :password, limit: 64 t.string :last_name, limit: 16 t.string :first_name, limit: 16 t.string :zipcode, limit: 16 t.string :prefecture, limit: 16 t.string :municipality, limit: 16 t.string :address, limit: 16 t.string :apartments, limit: 32 t.string :email, limit: 128 t.string :phone_number, limit: 16 t.references :user_classification, null: false, foreign_key: true t.string :company_name, limit: 128 t.boolean :delete_flag t.timestamps end end end ②products 関連の seedファイル作成 seeds.rb を記述。 別Qiitaにメモしました。 ③users 関連の validate作成 models/users.rb にvalidateをかけました 別Qiitaにメモしました。 ④商品詳細画面 を作成 商品名・カテゴリ・商品説明・値段 をDBから引っ張ってきて表示するように実装しました。 ⑤商品詳細画面(存在しないID) を作成 存在しない商品が指定された際には、この画面が出てくるように、 if分岐させました。 ⑥ユーザー情報更新画面 を作成 ユーザー情報の更新を行う画面の見た目とroutes を作成しました。 <%= form_with %>, <form.label>, <form.text_fiels> を使用して作成しています。 ⑦ユーザー情報更新機能 を実装 ⑥で見た目を実装し、ここで実際の動作を実装しました。 flashを利用したアラートを採用し、更新成功時には、user詳細画面にとび、「更新に成功しました」というメッセージを、失敗時には、user情報更新画面にrenderして、「更新に失敗しました」というメッセージを表示するように実装しています。 ※今後も実装完了次第、追加していきます! 5. 【必ず身に付けておきたい】 講師に学んだプルリクエストの出し方 共同開発では、開発業務以外にも Git や GitHub の操作方法がとても学びになりましたが、その中でも特に「 Pull Request(プルリクエスト )の出し方」が個人的には参考になりました! 今後の開発を進めていく上での基礎として、必ず身に付けておきたいと思いました。 ▼ プルリクエストを出す際の雛形となるテンプレート 以下は講師が提供してくださったテンプレートを元に僕が作成したプルリクエストの一例です。 プルリクエスト出す際は、以下のようなフォーマットをもとに作成しています。 プルリクエストの最終的な確認項目 このプルリクエストで実行したこと(概要) 対象 issue 重点的に見て欲しいところ(不安なところ) 実装できなくて後回しにしたところ チェックリスト(動作確認・ rubocop の実行など) その他の参考情報(参考にした記事のリンクなど) このようなフォーマットを元に書くことによって、レビュアーの方にとってわかりやすいプルリクエストの作成に繋がると感じました。 特に「重点的に見て欲しいところ(不安なところ)」の項目では、「問題に躓いた時にうまく人に頼れるスキル」も大事になって来ると思うので、不安だったことについては小さなことであっても、必ず書くようにしました。 ▼ 1指摘1コミット また、コードレビューを受ける際は「1指摘1コミット」でやり取りをするようにしました。つまり、複数の指摘を同時に受けたとしても、対応する際は一つずつコミットを出して個別に対応していくということ。 これは講師の方から「意外と知らない人が多いけど、大事なこと」として教わりました。 以下のような感じです↓ 基本的なことかもしれませんが、だからこそ、この機会で知ることができた意義は大きい!と考えています。 6. Rails 共同開発で躓いた点・大変だったこと 共同開発において躓いたことや大変だったこと、またそれに対する僕の対応です! 主に、以下の2点でつまずきました。 ① コンフリクトの発生 ※ 追加あるかもです。(無いことを願うw) ① コンフリクトの発生 コンフリクト発生に関しては、共同開発が始まった初っ端から躓いてしまい、かなり焦りました? しかし、練習段階で失敗することに意味があるんだろう。 と考え、自分を奮い立たせて対処しました。 何度か、コンフリクトを発生させているうちに、 「あぁ、多分、ここが原因だろ、一回、developをmergeして…。よし!」 ぐらいの気軽さで対処できるようになりました。 Gitの基礎は抑えられたのでは? と考えています。 7. Rails 共同開発に参加する経緯&身についたこと 2021年2月頃までは僕は主に一人でプログラミング学習に取り組んでいました。 しかし、エンジニアなるためには、実務に近い、チーム単位での経験が必要なのではないか、お互いの進捗状況を確認しあったり、わからないことを相談しあったりするなどと言った「協調性」が必ず必要になってくるのではないかと考えていました。 今回の共同開発で、早めにそのスキルを身につけておくことによって、技術的なサポートをして頂く際の「質問力」であったり、スムーズやりとりを行うための「コミュニケーション能力」の面においては、他の未経験者よりも前に立てたのではないか、と考えています。 この経験は、これからエンジニアになっていく上で、この上なく大事な経験だと思っていますし、この経験があれば、きっとエンジニアになれる!という自信にも繋がりました。 技術的にまだまだ未熟な自覚はあります。 しかし、それ以外の、エンジニアにとって大切な、「協調性」を身につけられたという点において、この共同開発への参加はとても有意義だったなぁ。と感じています。 8. さいごに【まとめ】 ここは、共同開発終了後に書こうと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyで?以降の文字列を消す

str = "https://www.hogehoge.com/articles/DA3S14897493.html?=nantoka_kantoka_article" puts str.sub(/\?(.*)/) {''}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyXLで詰まった部分の解消方法と基本動作について

はじめに 本記事は基本的にRubyXL ver.3.4.0を想定して記載を行っています ver3.4.0からリソースの無駄を抑えられるように用途に応じたメソッドが呼び出せるようになりました どちらかと言えば、呼び出さないと使えなくなったのでundefined method Errorが出る場合は確認してください 基本的には以下の通りです require 'rubyXL/convenience_methods/cell' require 'rubyXL/convenience_methods/color' require 'rubyXL/convenience_methods/font' require 'rubyXL/convenience_methods/workbook' require 'rubyXL/convenience_methods/worksheet' 面倒なら下記コードですべてを呼び出すことができます require 'rubyXL/convenience_methods' 発生した課題とその対応 テキストの折り返しがsheet.add_cellではできない sheet.add_cell(col, row, 'text').change_text_wrap によって実装可能 テキストを折り返して表示してくれるようになる change_contentsが使えない 3.4.0以降はconvenience_methodsを呼び出さないと利用できないため、要確認 シートのコピーができない 参考サイト Marshal.loadなども試したが、どうにも上手くいかなかったところ上記サイトの方法で解決 かなり力技だと思うのでより良い案があったら教えてください。 シートコピー後、コピーしたシートを編集したら他のシートの内容も一緒に変わってしまう シートの書式等を丸々コピー ↓ change_contentsで書式設定を保持したまま内容を変更 ↓ 該当するコピーシートすべての中身が書き換わる という流れ 恐らくchange_contents()の仕様と上記のシートコピーによって生じている現象だと思われる add_cell()による値の挿入だとそういった現象は発生しなかった この場合には書式設定等はRubyXL側で再現するしかない 動的な配置でエクセル数式を設定したい 正確には、「insert_rowによる行の挿入量が動的に変化する中、エクセルの数式によって値の算出が行いたい」 insert_row()は公式でも書かれているが扱いに慎重になるべきメソッドで 結合したセルや数式が挿入した行の下にあった場合にエクセルを破損させることが頻発する 解決策として add_cell()の書式が add_cell(row_index = 0, column_index = 0, data = '', formula = nil, overwrite = true) となっていたことを確認 data部分に数式を書くのではなく、formula部分に数式を記述することで解決した グラフの作成ができない xlsx、xlsmともに不可能 RubyXL側にグラフに関するメソッドは存在しないため、こればかりはどうしようもない(はず テンプレートシートに人数分のシートとグラフを用意して マクロを用いてファイルを開いた際にグラフを描画するようにして対応した 用意した人数未満であれば、シートの状態を非表示にすることで一見正常に動作しているようにした 読み込み時に数式を再計算させるようにしても動いてくれない 下記の「基本的な動作」に記載された エクセルで強制的に再計算させるプログラムを書いてもグラフの表示が変わらない問題が発生した 結論から書くとエクセル側の仕様であることが判明 ネット上からダウンロードしてきたファイルは保護ビュー関連で再計算前に必ず止まってしまう そのため、エクセルの設定から保護ビューなどの設定を無効化することで対処できる 印刷方向を縦向きから横向きにしたい 基本はテンプレートファイルの印刷設定を修正するだけで問題ない テンプレートファイル内のテンプレートシートをコピーして作成、作成完了後テンプレートシートを削除する といった処理の場合は要注意(上記参照) worksheet.page_setup = template.page_setup 以下、page_setup内の属性 paperSize(unsigned int型) ## 用紙サイズ scale(unsigned int型) ## 印刷スケール(すべての行を1シート内に含める、などの設定を指す) firstPageNumber(unsigned int型) ## 最初のページ番号 fitToWidth(unsigned int型) ## 幅に合わせる fitToHeight(unsigned int型) ## 高さに合わせる horizonDpi(unsigned int型) ## 水平DPI verticalDpi(unsigned int型) ## 垂直DPI copies(unsigned int型) ## 印刷する部数 usePrinterDefaults(boolean型) ## デフォルトのプリンタを利用するかどうか blackAndWhite(boolean型) ## 白黒印刷をするかどうか draft(boolean型) ## 簡易印刷するかどうか(罫線や塗りつぶしの色(網掛け)などの書式やグラフや図形などのオブジェクトを省略して印刷) useFirstPageNumber(boolean型) ## 最初のページ番号を使用するかどうか pageOrder(downThenOver・overThenDownの2択) ## ページの順序.昇順か降順か orientaion(default・portrait・landscapeの3択) ##オリエンテーション(印刷時の向き.デフォルト・縦・横) cellComments(none・asDisplayed・atEndの3択) ##セルコメントを印刷する errors(displayed・blank・dash・NAの4択) ## 印刷エラー処理 上記のような形で印刷時のページ設定をコピーしてくれば問題ない(もしくはpage_setupに直接値を入力するか) RubyDocによると、RubyXL::PrintOptionsというのが存在し、 Worksheet内にも同様のものが存在するが基本的にnilになっている クラスとして存在し、属性に値を入れることもできるので何かしらできることがあると思われるが... もしできるのであればそちらで設定するのが適切かもしれない RubyXLでは実装できないパターンについて シート名を動的に変更しつつグラフの描画を行う エクセル上だとグラフのデータ範囲はシートに基づいており シート名を変更してしまうと変更前のシート名を参照してしまいエラーが発生してしまう DISTINCT()などを用いてもグラフのデータ範囲はファイル読み込み時の エクセルの関数よりも早くに行われるためどうしてもエラーが発生する また、シート名の動的な取得はxlsmでマクロを利用するしかなく RubyXL側では何もすることができないこととなる 同様の理由で、グラフのデータ範囲が動的に変化する(insert_row()などを用いて抽出するデータ範囲が変動する)場合もRubyXL側では何もできないことになる こちらについては行の挿入を行っても位置が変化しない部分を非表示にし それぞれの値を非表示セル内に記載してデータ範囲は非表示セル内のデータとすれば解決が可能だと思われる グラフの生成(グラフ設定の編集含む) RubyXL側にグラフ描画・サポート機能がないためそもそも不可能 テンプレートファイルに直接描画してのがベスト 基本的な動作 主に公式のGithubページやQiita、RubyDocを基に結構使いそうなものを列挙してみました ファイル(ブック)の新規作成 workbook = RubyXL::Workbook.new テンプレート等のファイル(ブック)読み込み xlsx、xlsm以外の読み書きは不可 workbook = RubyXL::Parser.parse('app/assets/template/template.xlsx') 見つからなかったらZip::Error: XLSX file format errorが返ってくるため注意 ファイルの書き出し workbook.write('data/sen/template.xlsx') File.openなどを利用する必要はない 数式の再計算 4つすべて必要 workbook.calc_pr.full_calc_on_load = true workbook.calc_pr.calc_completed = true workbook.calc_pr.calc_on_save = true workbook.calc_pr.force_full_calc = true シート指定 1枚目のシートはworkbook[0]、2枚目はworkbook[1] 2枚目のシートが存在しない状態でworkbook[1]を指定した場合はnilが返ってくる sheet = workbook[0] sheet = workbook.first sheet = workbook['sheet_name'] シートの名前を変更 日本語名可 sheet.sheet_name = 'sheet_name' シートの追加(新規作成) sheet = workbook.add_worksheet('sheet_name2')などと 変数に代入して作成したシートの参照ができる workbook.add_worksheet('sheet_name2') シートの削除 workbookからの指定で行わないとできない 指定したシートが存在しなかったらnilが返ってくる workbook.worksheets.delete(workbook[0]) 行指定 指定した位置の行全体を取得できる row = sheet[sheet_row] row = sheet.sheet_data[sheet_row] セル指定 cell = sheet[row][col] cell = sheet.sheet_data[row][col] 下記コードでもセルの指定が可能 値が存在したら元データを、存在しなかったら上書きする 値の抽出はcell.valueで抽出できる他、sheet[row][col].value、add_cell(row, col, 'hoge').valueでも可能 (上記の場合はhogeをvalueとして取得する) cell = sheet.add_cell(row, col, '', nil, false) 以下書式 data部分を空にしてformula部分に'SUM(A1, B1)'などと書くとエクセルの数式を作ってくれる この時、=(イコール)は不要なので注意 add_cell(row_index = 0, column_index = 0, data = '', formula = nil, overwrite = true) 値の挿入(新規追加・書式設定の保持は不可) sheet.add_cell(row, col, 'text') 値の挿入(上書き・書式設定を保持する) sheet[row][col].change_contents('text') ただし、指定したセルに値が存在しない(nilであった)場合はエラーが発生するため注意 以下書式 data部分を空にしてformula部分に'SUM(A1, B1)'などと書くとエクセルの数式を作ってくれる この時、=(イコール)は不要なので注意 change_contents(data, formula_expression = nil) 行の挿入 挿入箇所よりも下に結合したセルや数式が存在する場合、エクセルが破損するため注意 挿入箇所よりも上にある場合は問題無い sheet.insert_row(row) 下記のように書くと値を勝手に入れてくれる(らしい?動作未確認) sheet.insert_row(row, array) //arrayは値が格納された配列 行の削除 挿入箇所よりも下に結合したセルや数式が存在する場合、エクセルが破損するため注意 挿入箇所よりも上にある場合は問題無い worksheet.delete_row(1) 列の挿入 挿入箇所よりも右に結合したセルや数式が存在する場合、エクセルが破損するため注意 挿入箇所よりも左にある場合は問題無い sheet.insert_column(col) 下記のように書くと値を勝手に入れてくれる(らしい?動作未確認) sheet.insert_row(row, array) //arrayは値が格納された配列 列の削除 挿入箇所よりも右に結合したセルや数式が存在する場合、エクセルが破損するため注意 挿入箇所よりも左にある場合は問題無い sheet.delete_column(col) セルの挿入 ずらす方向はright/left/top/bottomの4種類を設定可能 worksheet.insert_cell(row, col, 'text')と書いた場合はadd_cellと同じ動きになる(セルはずれない) ずらした方向に結合したセルや数式が存在する場合、エクセルが破損するため注意 worksheet.insert_cell(row, col, 'text', nil, :right) セルの削除 削除後にずらす方向はright/left/top/bottomの4種類を設定可能 worksheet.delete_cell(row, col, 'text')と書いた場合はadd_cellと同じ動きになる ずらした方向に結合したセルや数式が存在する場合、エクセルが破損するため注意 実際のところ、add_cellやchange_contentsで空文字を挿入しても変わらないので 安全に利用できるadd_cellなどで上書きした方が良い sheet.delete_cell(row, col, :right) セルの文字色の変更 16進数で指定する #(シャープ)は不要 行・列指定のようにsheet.change_row_font_color(row, 'FFFFFF')とすれば 指定した行全体を変えることができる sheet.add_cell(~).change_font_color('FFFFFF') セル指定の場合、cell = sheet.add_cell(~)後にcell.change_font_color('FFFFFF') change_contents()の後ろにつけても実装可能 セルの背景色を変更 16進数で指定する #(シャープ)は不要 行・列指定のようにsheet.change_row_fill(row, 'FFFFFF')とすれば 指定した行全体を変えることができる sheet.add_cell(~).change_fill('FFFFFF') セル指定の場合、cell = sheet.add_cell(~)後にcell.change_fill('FFFFFF') change_contents()の後ろにつけても実装可能 セルの結合 選択した範囲内のセルを結合する(A1からB2まで、とするとA1・A2・B1・B2セルを結合する) sheet.merge_cells(row1, col1, row2, col2) 罫線の付与 線の太さはhairline/thin/medium/thickの4種類が選択可能 sheet.add_cell(~).change_border(:left, 'medium') セル指定の場合、cell = sheet.add_cell(~)後にcell.change_border(:left, 'medium') change_contents()の後ろにつけても実装可能 複数指定の場合は以下のように行う %i[left right bottom top].each do |e| cell_index.change_border(e, 'medium') end その他、可能なことはGithubやRubyDocのご確認を 参考サイト 公式Github RubyDoc 利用できるメソッドや型の説明が細かく載っているので迷ったら確認するべき場所 参考Qiita1:rubyXlでエクセルを壊さないように丁重に扱いながら、内容を変えていただくための注意事項 参考Qiita2:rubyXL で Excel 生成したいときの Tips 集 参考Qiita3:Railsで既存のエクセルファイルをテンプレートにできる魔法のヘルパー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsにおけるnew, build, save, createの違い

new インスタンスをメモリ上に作成する。 user = User.new(name: "Mike") build newのエイリアス。 newと全く同じ動作をする。 save メモリ上に存在するインスタンスをデータベースに保存する。 user = User.new(name: "Mike") user.save create newとsaveをまとめて実行する。 user = User.create(name: "Mike")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyのdeleteメソッドで複数の文字列を削除

deleteメソッドについて 最近paizaで勉強中なので、メモ用に残します。 deleteメソッド deleteメソッド、delete!メソッドは文字列から、指定の文字列を削除するメソッド。 使用例 str = 'abcde' puts str.delete('a') # bcde puts str.delete('abc') # de みたいな感じですね。。 そんなの言われなくてもわかってるよ!という方がほとんどですね しかし、一つの文字列から複数の文字を削除したい時はどうでしょうか str = 'abcde' puts str.delete('a' 'c') # bde こうします。 カンマがいらないんですね〜 なんでなのかはわかりませんが..笑 もし知ってる方がいれば教えてください! 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初心者向け]SystemSpecの基本的な使い方とサポートモジュールについて

はじめに 初めまして、takumi(@takumi73)と申します。 今回、RspecのSystemSpecについて学習したので、簡単ではありますが、基本的な使い方についてまとめていこうと思います。 内容に誤りがありましたら、ご指摘頂けると嬉しいです。 SystemSpecでできること 従来の統合テストはFeatureSpecであり、それをより便利かつスピーディーに開発できるようにしたものがSystemSpecになる。 利点としては以下が挙げられる ・ブラウザ上で実行することがデフォルトで可能になった統合(E2E)テスト ・エラー発生時スクリーンショットで画面を確認できる 環境 capyrbaraとfactory_botを使用しています。 ruby 2.6.3 Rails 6.1.3.2 factory_bot 6.1.0 factory_bot_rails 6.1.0 capybara 3.35.3 SystemSpecの基本的な使い方 ※事前準備に関してはわかりやすい記事がたくさんあるので今回は省略させて戴きます。 【Rails】はじめてのSystemSpec(RSpec) 基本的な書き方は以下の通りになり、今回はログインを例に挙げています。 ※わかりやすくするためにあえてdescrideなどは記述していません。 [spec/system/users_spec.rb] require 'rails_helper' RSpec.describe 'Users', type: :system do it '入力が全て正しい時、ログイン完了のフラッシュが表示されていること' do visit login_path fill_in 'email', with: @user.email fill_in 'password', with: @user.password click_button 'ログイン' expect(page).to have_content 'ログインしました!' end end これでログインが正常に完了することのテストができます。 visitでテストしたいパスに移動する fill_in 'email'でname属性を指定し、with:で、入力値を再現できます。 <input type="email" name="email" class="form-control" value="<%= @email %>"> click_button 'ログイン'でログインというボタンタグを実行。 <button type="submit" class="btn btn-primary">ログイン</button> ・リンク(aタグ)の場合は、click_link ・どちらか気にせずクリックを実装したいときはclick_on ・値が存在せずアイコンなどの場合は、find('.クラス名').clickでクラス名を指定する   expect(page).to have_content 'ログインしました!'の部分が、実行結果の期待する振舞いを書く。 今回であれば、ログインが成功した場合成功のフラッシュが表示されるようになっているのでそれを検証しました。 再現に少し苦労したもの 画像アップロードの実装 attach_file 'image_icon', 'app/assets/images/default.jpeg' 画像アップロードを再現するには、attach_fileを使用します。 <%= form.file_field :image_icon %> image_iconには、属性が入ります。 'app/assets/images/default.jpeg'で、再現に使用する画像のパスを指定します。 SystemSpecの便利な機能 スクリーンショット テストを実行しエラーが発生すると、実行後の画面をスクリーンショットしてくれる。 [Screenshot Image]:~と表示されるのでその後のURLをブラウザで表示させることができる。 テストのデバック うまくテストが通らない場合に、テストが失敗する時の画面を確認する方法があります。 スクリーンショット機能と似ていて、テストが失敗するであろう箇所の画面を確認することができる。 1gem 'launchy'をインストール 2デバックしたい箇所にsave_and_open_pageに追記する。 visit login_path fill_in 'email', with: @user.email fill_in 'password', with: @user.password save_and_open_page click_button 'ログイン' expect(page).to have_content 'ログインしました!' この場合、入力値が再現できているかなどを確認することができる。 エラーが発生していなくても、入力値の確認など利用することもできる。 DRYなテストにする方法(サポートモジュール) コードの重複を減らす方法として、サポートモジュールを使用する方法がある。 ※他にも方法はある。 サポートモジュール 特性としては、重複しているワークフローをモジュール化し、DRYなコードを実現することができる。 方法 ①有効化 spec/rails_helper.rbの以下の記述をコメント解除 Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } ②ファイルの作成 spec/support/任意のモジュール名_support.rbを作成 ③モジュールの作成 先ほどのログイン実装部分をlogin_un_as(user)というメソッドで定義。 module LoginSupport def login_in_as(user) visit login_path fill_in 'email', with: @user.email fill_in 'password', with: @user.password click_button 'ログイン' end end RSpec.configure do |config| config.include LoginSupport end ④書き換え login_in_as @user expect(page).to have_content 'ログインしました' 先ほど記述していた行を削除し、login_in_as @userで呼び出すことができる。 これでかなりスッキリした記述にすることができる。 また、loginの実装は、何度も使用することがあるため、この1行で実装できるのはかなり便利。 まとめ ・SystemSpecを使用することで、アプリケーションの一連の流れをテストすることができる。 ・処理が複雑になってくるため、必要に応じてDRYなコードを意識する必要がある。 最後まで読んでいただきありがとうございました! 参考文献 rspec-rails 3.7の新機能!System Specを使ってみた RSpec公式 【Rails】はじめてのSystemSpec(RSpec)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Herokuを使用したデプロイの方法

Heroku CLIをインストール 下記のコマンドを実行するとherokuというコマンドが使用できるようになり、ターミナルからHerokuへアクセス可能になります。 % brew tap heroku/brew && brew install heroku インストールが完了したら下記のコマンドを実行してバージョンを確認します。 % heroku --version heroku/7.40.0 darwin-x64 node-v12.16.2 上記のようにバージョンが出力されれば成功です。(バージョンの値は実際と異なる場合があります。) Herokuにログイン 下記のコマンドを実行し、メールアドレスとパスワードを入力します。 % heroku login --interactive Heroku上にアプリケーションを作成 下記のコマンドを実行。 % heroku create アプリ名 下記のコマンドを実行し、正しく設定できたことを確認 % git config --list | grep heroku Heroku上でMySQLを使えるように設定 下記のコマンド実行でデーターベースをMySQLに設定します。 % heroku addons:add cleardb Ruby on Railsを使う場合は、MySQLに対応するGemについて考慮する必要があり、そちらの設定もします。 まず、ClearDBデータベースのURLを変数heroku_cleardbに格納するため下記のコマンドを実行します。 % heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL` 続いて、データベースのURLを再設定するため下記のコマンドを実行します。 % heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5} Heroku上にマスターキーを設定 Heroku上に環境変数を設定します。下記のコマンドを実行。 % heroku config:set RAILS_MASTER_KEY=`cat config/master.key` 設定が正しくできているか、Herokuの環境変数一覧を表示。 % heroku config Herokuで利用する環境を指定 Rubyのバージョン2.6.5が動作するStackを指定しました。下記を実行。(注:ダウングレードは極力避けた方が良いそうです。) % heroku stack:set heroku-18 -a アプリ名 Herokuへアプリケーションを追加 Gitで管理しているリポジトリをHerokuに反映させます。下記のコマンドを実行。 % git push heroku master Heroku上でマイグレーションファイルを実行 % heroku run rails db:migrate 公開を確認 下記のコマンドを実行すると公開されたアプリケーションの詳細を見ることができます。 % heroku apps:info 表示されたURLへアクセスすると、公開されたページを見ることができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

while true do の使い方

whileの使い方 ex1.条件によって繰り返し処理を行う場合 ruby while 条件式 do 処理するアクション end ex2.プログラムを終了させず同じ処理を繰り返すループ処理を行う場合 ruby while 条件式 do 処理するアクション end 問題.1 以下の仕様を満たすアプリケーションを作成してください。 仕様 プログラムの実行を行うと、 ①「[0]:体重を表示する、[1]:終了する」という選択肢が表示され、数字を入力することができる ② 0を入力すると「80kg」と表示され、上記条件①が繰り返し実行される。 ③ 1を入力するとアプリケーションが終了する while true do puts "[0]:体重を表示する" puts "[1]:終了する" input = gets.to_i if input == 0 puts "80kg" elsif input == 1 exit end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

草野球の出欠確認Webアプリを作ろう! part.6

これから作っていく簡単なWebアプリの作成メモ(自分の備忘)です。 自分用なのであまり凝りすぎないように書いていきたい。 <<前回の記事 ※前回導入していたRspecですが、なぜかchromedriverが動作しなくて原因も不明だったので、いったんTDDは凍結とします(そのままずっと凍結の可能性も...)。 今回やったこと スケジュールの編集 schedulesコントローラーのeditとupdateのアクションを整える。 app/controllers/schedules_controller.rb def edit @schedules = Schedule.find(params[:id]) end def update @schedules = Schedule.find(params[:id]) if @schedules.update(schedules_params) redirect_to schedule_path(@schedules), notice: "予定を編集しました。" else render :edit end end viewのほうも整える。 views/schedules/edit.html.erb <h1>予定の編集</h1> <div class="row_line"> <%= link_to '予定一覧へ', schedules_path, class: 'btn primary-btn' %> </div> <%= form_for @schedules, url: {action: "update", id: @schedules.id} do |f| %> <div class="row_line"> <label>件名:</label> <%= f.text_field :title %> </div> <div class="row_line"> <label>予定の日付:</label> <%= raw sprintf( f.date_select(:date_of, use_month_numbers: true, order: [:year,:month,:day], selected: Time.zone.now, start_year: Time.zone.now.year + 5, end_year: Time.zone.now.year, date_separator: '%s' ),'年','月') + '日' %> </div> <div class="row_line"> <label>開始時間:</label> <%= f.time_select :start_time %> &nbsp;~&nbsp; <label>終了時間:</label> <%= f.time_select :end_time %> &nbsp;( <label>集合時間:</label> <%= f.time_select :meeting_time %> ) </div> <div class="row_line"> <%= f.submit "保存する" %> </div> <% end %> スケジュールの編集画面に遷移できないと動作確認できないので、遷移できるようにしておく。 以下の記事を参考にした。 【jQuery】HTMLのtableの行にリンクを設定する Rails6でjQueryの導入方法 $ yarn add jquery config/webpacker/environment.js const { environment } = require('@rails/webpacker') const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ $: 'jquery/src/jquery', jQuery: 'jquery/src/jquery' }) ) module.exports = environment app/javascript/packs/application.js (略) import "jquery" (略) ※jqueryを導入した際にrailsサーバーを起動中だったら、一度サーバーを停止して再度起動しなおすほうがよい(過去バージョンでの経験則) jqueryを使えるようにした後は、そのjqueryを使用して一覧画面のテーブルの1行をクリックしたときに、編集画面に遷移するように準備する。 views/schedules/index.html.erb (略) <tbody> <% @schedules.each do |lst| %> <tr class="clickable-row" data-href="<%= edit_schedule_path lst.id %>"> <td><%= lst.title %></td> <td><%= lst.date_of %></td> <td><%= lst.start_time.strftime('%H:%M') %> ~ <%= lst.end_time.strftime('%H:%M') %></td> </tr> <% end %> </tbody> (略) 以下のjsファイルを新規に作成した。 app/javascript/packs/schedules.js $(function($) { $(".clickable-row").css("cursor","pointer").click(function() { location.href = $(this).data("href"); }); }); 作成したjsファイルを読み込むようにする。 (各モデルの名前付きのjsはもっと個別機能の処理を記述するほうが良いのではと思ったのは、ファイルを作成してからだった。今後余裕があればリファクタリングできるといいポイント。) views/layouts/application.html.erb (略) <head> <title>YaQue</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'schedules', 'data-turbolinks-track': 'reload' %> </head> (略) ↓テーブルの行をクリックすると以下のようになる。 Railsの5.2あたりとJqueryの導入方法がすこし違って戸惑った。 今回はここまで。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む