- 投稿日:2021-01-13T23:52:34+09:00
Railsで内容を更新しても反映されない?
★内容を更新したのに反映されない。。。
springが悪さをしているかも?
springはrails sをした時に
同時に立ち上がり読み込むもので古い情報を読み込んでいる可能性がある❶rails sをして別タブでspring stop
再度springをして挙動を確認すると悪さをなくせるかも?
$ spring stop Spring is not running❶プロセスを見てみる
ここで下記以外のプロセスがあるようならKILL
ps aux | grep spring$ ps aux | grep spring で、KILLを実行後、KILLできているか確認
$ kill -9 ◯◯◯◯ $ ps aux | grep spring
- 投稿日:2021-01-13T23:28:24+09:00
Rubyでスプレッドシートからデータを取得するAPIを作成してみた(with サービスアカウント)
概要
スプレッドシートからデータを取得してjsonに整形するみたいなAPIを作成してみました。
意外とサービスアカウントを使っている記事が少なかったので少しでも参考になればと思います。環境
Ruby: 2.5.7
Ruby on Jets: 2.3.18
(Railsライクなフレームワークなので、Railsでもできます)サービスアカウントについて
ユーザではなくアプリケーションレベルで使用するアカウント。ユーザで認証する場合はそのユーザがアプリケーションの管理からはずれた場合などに認証情報を変えなければならなくなるため不便。
サービスアカウントはユーザに依存しないのでユーザなど関係なくアプリケーションからAPIを叩く場合にはこちらが便利。
ちなみに今回はバックエンドからユーザなど関係なくAPIを叩くために使うのでサービスアカウントを利用しました。
サービスアカウントの作成
作成していない場合は以下を参考に作成してください。ここでは省きます
https://support.google.com/a/answer/7378726?hl=ja
サービスアカウントからcredentialファイルをjsonファイルで取得
認証情報 > サービスアカウントを管理 > 作成したアカウントの操作 > 鍵を作成
json形式で鍵を作成して、
ダウンロードしたらアプリのトップディレクトリにgoogle-sa-credential.json
という名前で保存APIを有効にする
https://console.developers.google.com/apis/dashboard
GCPコンソールの「APIとサービス」から
- Google Drive API
- Google Sheets API
を検索してそれぞれ有効にする。
サービスアカウントでフォルダ、スプレッドシートを作成する
サービスアカウントでスプレッドシートを操作する際には、以下のいずれかの必要がある。
- サービスアカウントによって作成されている
- サービスアカウントが編集者として共有されている
- 公開されている
スプレッドシートの「共有」でサービスアカウントを許可できるならば、共有した時点でこのタスクを必要としないので飛ばしてもOKです。
以下に2つのrakeタスクを記述しました
- 指定したフォルダID配下にフォルダを作成する(指定しなければ最上階にフォルダを作成する)
- 指定したフォルダID配下にスプレッドシートを作成する
# lib/tasks/google_drive.rake namespace :google_drive do desc 'Create folder with Service account' task :create_folder, [:title, :email_address, :collection_id] => :environment do |_, args| session = create_session folder = if args[:collection_id] session.file_by_id(args[:collection_id]).create_subcollection(args[:title]) else session.root_collection.create_subcollection(args[:title]) end folder.acl.push(type: 'user', email_address: args[:email_address], role: 'writer') p "Created folder: #{folder.human_url}" end desc 'Create sheet with Service account' task :create_sheet, [:title, :email_address, :collection_id] => :environment do |_, args| session = create_session sheet = session.file_by_id(args[:collection_id]).create_spreadsheet(args[:title]) sheet.acl.push(type: 'user', email_address: args[:email_address], role: 'writer') p "Created sheet: #{sheet.human_url}" end def create_session ::GoogleDrive::Session.from_service_account_key('google-sa-credential.json') end end$ bundle exec rake 'google_drive:create_folder[<フォルダタイトル>,<共有したいユーザのメールアドレス>]' $ bundle exec rake 'google_drive:create_sheet[<シートタイトル>,<共有したいユーザのメールアドレス>]'補足
collection: folderの意味
ACL: アクセス制御リスト。IAMと似て非なるもの。バケットやオブジェクトに柔軟に権限を与えるっぽい。
その後の実装方針
- gem google_driveを使用する
- spreadsheet → worksheet → json
- gem google_api_clientを使用する
- 柔軟性がない(jsonで出力できてもどちらにせよしたい形に変換しなければならない)
json構造を柔軟に変更できるように1の方針で実装しました。
所々moduleに切り分けていますが必須ではないです
また、routingは省略しますGemfile
gem 'google_drive'controller
# controller session = GoogleDriveSession.create_session service = SpreadSheetToHashService.new(session) service.run! render json: JSON.dump(service.records)session作成module
# google_drive_session.rb module GoogleDriveSession CREDENTIAL_PATH = 'google-sa-credential.json' def self.create_session return ::GoogleDrive::Session.from_service_account_key(CREDENTIAL_PATH) end endちなみに自分の場合は開発環境以外ではSecrets Managerから取得する(gem 'aws-sdk-secretsmanager'を使用)
# google_drive_session.rb module GoogleDriveSession CREDENTIAL_PATH = 'google-sa-credential.json' def self.create_session return ::GoogleDrive::Session.from_service_account_key(CREDENTIAL_PATH) if Jets.env.development? credential_json = RequestSecretsManager.request('/<project_name>/google-sa-credential') credential_hash = JSON.parse(credential_json) File.open("/tmp/#{CREDENTIAL_PATH}", 'w') do |f| JSON.dump(credential_hash, f) end ::GoogleDrive::Session.from_service_account_key("/tmp/#{CREDENTIAL_PATH}") end end # request_secrets_manager.rb require 'aws-sdk-secretsmanager' module RequestSecretsManager def self.request(secret_name) client = Aws::SecretsManager::Client.new(region: Jets.aws.region) get_secret_value_response = client.get_secret_value(secret_id: secret_name) get_secret_value_response.secret_string end endservice
# spread_sheet_to_hash_service.rb class SpreadSheetToHashService attr_reader :records SPREADSHEET_ID = '<スプレッドシートのID>' WORKSHEET_ID = '<ワークシートのID(1ページ目は0)>' HEADER_COUNT = 1 def initialize(session) @session = session end def run! worksheet = SpreadSheet.identify_worksheet_by_id(@session, SPREADSHEET_ID, WORKSHEET_ID) convert_worksheet_to_hash(worksheet) end private def convert_worksheet_to_hash(worksheet) # ヘッダをスキップする @records = worksheet.rows(HEADER_COUNT).map { |row| # ここでセル単位で好きな形にする # rowは行のこと、一行ずつのイテレータ # row[0]: 1列目の値 # row[1]: 2列目の値 } end endspread sheet操作用module
# spread_sheet.rb module SpreadSheet def self.identify_worksheet_by_id(session, spread_sheet_id, work_sheet_id) spreadsheet = session.file_by_id(spread_sheet_id) spreadsheet.worksheet_by_sheet_id(work_sheet_id) end endまとめ
これでスプレッドシートの内容を柔軟にjsonへ変更してレスポンスとして返すことができます!
参考
- 投稿日:2021-01-13T22:38:19+09:00
Railsでtext_areaに入力した文章を改行込で表示させる方法
はじめに
Ruby on Railsにて投稿機能のあるアプリを作成しました。
投稿画面にはRailsのヘルパーメソッドであるform_withメソッドを用いてフォームを実装。その際、text_areaで、複数行の入力が可能な入力欄を作成したので、改行を含めた文章を入力し、それを表示させようとしたところ、改行がされていない文章が表示されてしまいました。
今回はその問題を解決する方法を調べたので記事に残しておきます。環境
macOS Catalina バージョン 10.15.7
Ruby 2.6.5
Ruby on Rails 6.0.3.4前提として
フォームのコードは以下の通り。
app/views/shared/_form.html.erb<%= form_with(model: word, local: true) do |f| %> <%= f.text_area :remarks, placeholder: "備考", rows: "7" %> <%= f.submit "Register" %> <% end %>念の為、このフォームで送られてくるデータに改行が含まれているか、Railsのデバックツールであるpry-railsを用いて確認してみました。
まず、以下の改行を含めた文章をフォームで送信しました。おはよう こんにちはここで、コントローラーに記述したbinding.pryのところで一旦処理が止まるので、コンソールでparamsを実行し、送られてきたパラメーターを確認したところ、
このように、改行した部分に改行コードである「\r\n」が含まれており、ちゃんとデータとして送られてきていることが分かります。そして、送られてきたデータを表示するためのコードが以下なのですが、
app/views/show.html.erb<%= @word.remarks %>このままだと改行が含まれず、以下のような表示になってしまいます。
おはよう こんにちはこの問題を解決する方法を、以下2通りまとめました!
①simple_formatメソッド
以下のサイトを参考にしました。
- https://railsdoc.com/page/simple_format
- https://www.ryotaku.com/entry/2019/06/25/%E3%80%8Csimple_format%E3%80%8D%E3%82%84%E3%80%8Csafe_join%E3%80%8D%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%80%81%E6%AD%A3%E5%B8%B8%E3%81%AB%E6%94%B9%E8%A1%8C%E8%A1%A8%E7%A4%BA%E3%81%95%E3%81%9B
- https://beryus.hatenablog.com/entry/2020/10/06/004936
simple_formatはRailsのヘルパーメソッドで、下記の機能を有します。
- 文字列を<p>で括る
- 改行は<br>を付与
- 連続した改行は</p><p>を付与
では、show.html.erbにsimple_formatを追記します。
app/views/show.html.erb<%= simple_format(@word.remarks) %>そして、ブラウザを更新し改めて確認してみると、
おはよう こんにちはこのように改行がされたまま表示がされました!
ChromeのデベロッパーツールのElementsパネルを確認すると、
たしかに、文字列がpタグで括られており、改行にはbrタグが付与されています。そこで、試しに以下のようにいくつか連続した改行の場合を調べてみました。
おはよう こんにちはしかし、表示を確かめると、
おはよう こんにちはこのように、連続した改行は実際に入力した通りには表示されませんでした。
ちなみに、Elementsパネルを確認すると、以下のようにpタグが付与されていました。
なので、この場合は改行ではなく、「おはよう」と「こんにちは」それぞれが1つの段落として表示されている事になります。②safe_joinメソッド
以下のサイトを参考にしました。
- https://www.ryotaku.com/entry/2019/06/25/%E3%80%8Csimple_format%E3%80%8D%E3%82%84%E3%80%8Csafe_join%E3%80%8D%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%80%81%E6%AD%A3%E5%B8%B8%E3%81%AB%E6%94%B9%E8%A1%8C%E8%A1%A8%E7%A4%BA%E3%81%95%E3%81%9B
- https://qiita.com/kamotetu/items/1aa94994985c720668e4
safe_joinメソッドもRailsのヘルパーメソッドの一つです。
simple_formatと違い、pタグで括らず、また連続した改行を表示させることができます。まず、先ほどの記述を以下のように書き換え。
app/views/show.html.erb<%= safe_join(@word.remarks.split("\n"),tag(:br)) %>※ splitは文字列を分割するメソッド。引数に区切り文字を指定することで、その区切り文字のところで文字列を区切る。
あとは、同じように複数の連続した改行を含む文章を入力してみると、おはよう こんにちはきちんと入力した通りの表示がされました!!
Elementsパネルを確認すると、
brタグで改行されているのが分かります。最後に
色々調べる中で、HTMLやタグ、エスケープ処理などの記事を目にしました。これらの理解が深まったと同時に、知らない知識に触れる機会も多かった為、さらに派生させ知識をより深めていきたいと思います。
誤った箇所などありましたら、ご指摘いただけると幸いです。
- 投稿日:2021-01-13T22:38:19+09:00
text_areaに入力した文章を改行込で表示させる方法
はじめに
Ruby on Railsにて投稿機能のあるアプリを作成しました。
投稿画面にはRailsのヘルパーメソッドであるform_withメソッドを用いてフォームを実装。その際、text_areaタグで、複数行の入力が可能な入力欄を作成したので、改行を含めた文章を入力し、それを表示させようとしたところ、改行がされていない文章が表示されてしまいました。
今回はその問題を解決する方法を調べたので記事に残しておきます。環境
macOS Catalina バージョン 10.15.7
Ruby 2.6.5
Ruby on Rails 6.0.3.4前提として
フォームのコードは以下の通り。
app/views/shared/_form.html.erb<%= form_with(model: word, local: true) do |f| %> <%= f.text_area :remarks, placeholder: "備考", rows: "7" %> <%= f.submit "Register" %> <% end %>念の為、このフォームで送られてくるデータに改行が含まれているか、binding.pryを用いて確認してみました。
まず、以下の改行を含めた文章をフォームで送信しました。おはよう こんにちはここでbinding.pryを用いて、コンソールで「params」を実行し、パラメーターを確認したところ、
このように、改行した部分に改行コードである「\r\n」が含まれており、ちゃんとデータとして送られてきていることが分かります。そして、送られてきたデータを表示するためのコードが以下なのですが、
app/views/show.html.erb<%= @word.remarks %>このままだと改行が含まれず、以下のような表示になってしまいます。
おはよう こんにちはこの問題を解決する方法を、以下2通りまとめました!
①simple_formatメソッド
以下のサイトを参考にしました。
- https://railsdoc.com/page/simple_format
- https://www.ryotaku.com/entry/2019/06/25/%E3%80%8Csimple_format%E3%80%8D%E3%82%84%E3%80%8Csafe_join%E3%80%8D%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%80%81%E6%AD%A3%E5%B8%B8%E3%81%AB%E6%94%B9%E8%A1%8C%E8%A1%A8%E7%A4%BA%E3%81%95%E3%81%9B
- https://beryus.hatenablog.com/entry/2020/10/06/004936
simple_formatはRailsのヘルパーメソッドで、下記の機能を有します。
- 文字列を<p>で括る
- 改行は<br>を付与
- 連続した改行は</p><p>を付与
では、show.html.erbにsimple_formatを追記します。
app/views/show.html.erb<%= simple_format(@word.remarks) %>そして、ブラウザで更新し改めて確認してみると、
おはよう こんにちはこのように改行がされたまま表示がされました!
ChromeのデベロッパーツールのElementsパネルを確認すると、
たしかに、文字列がpタグで括られており、改行にはbrタグが付与されています。そこで、試しに以下のようにいくつか連続した改行の場合を調べてみました。
おはよう こんにちはしかし、表示を確かめると、
おはよう こんにちはこのように、連続した改行は実際に入力した通りには表示されませんでした。
ちなみに、Elementsパネルを確認すると、以下のようにpタグが付与されていました。
なので、この場合は改行ではなく、「おはよう」と「こんにちは」それぞれが1つの段落として表示されている事になります。②safe_joinメソッド
以下のサイトを参考にしました。
- https://www.ryotaku.com/entry/2019/06/25/%E3%80%8Csimple_format%E3%80%8D%E3%82%84%E3%80%8Csafe_join%E3%80%8D%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%80%81%E6%AD%A3%E5%B8%B8%E3%81%AB%E6%94%B9%E8%A1%8C%E8%A1%A8%E7%A4%BA%E3%81%95%E3%81%9B
- https://qiita.com/kamotetu/items/1aa94994985c720668e4
safe_joinメソッドもRailsのヘルパーメソッドの一つです。
simple_formatと違い、pタグで括らず、また連続した改行を表示させることができます。まず、先ほどの記述を以下のように書き換え。
app/views/show.html.erb<%= safe_join(@word.remarks.split("\n"),tag(:br)) %>※ splitは文字列を分割するメソッド。引数に区切り文字を指定することで、その区切り文字のところで文字列を区切る。
あとは、同じように複数の連続した改行を含む文章を入力してみると、おはよう こんにちはきちんと入力した通りの表示がされました!!
Elementsパネルを確認すると、
brタグで改行されているのが分かります。最後に
色々調べる中で、HTMLやタグ、エスケープ処理などの記事を目にしました。これらの理解が深まったと同時に、知らない知識に触れる機会も多かった為、さらに派生させ、知識をより深めていきたいと思います。
誤った箇所などありましたら、ご指摘いただけると幸いです。
- 投稿日:2021-01-13T21:39:58+09:00
Rails の Carrierwave を使用した画像複数アップロード機能
備忘録のため記述しています。
Carrierwave という gem の導入
carrierwaveuploader/carrierwave
Gemfile に以下を記述
gem "carrierwave", "~> 2.0"終わったらターミナルにて bi
bundle installUploader の作成
bundle exec rails g uploader Images下記が作成される。
images_uploader.rbアップロードされるファイルの設定を色々弄れるけど今回は見送り。
MVC 設定
Post モデルに対して Memory というモデルで画像登録をできるようにしている
Model
post.rb
post.rbclass Post < ApplicationRecord has_many :memories accepts_nested_attributes_for :memories, allow_destroy: true validates :title, presence: true endaccepts_nested_attributes_for :memories の設定で posts_controller 上で memories を登録できるようにしている。
memory.rb
class Memory < ApplicationRecord belongs_to :post mount_uploader :image, ImagesUploader endmount_uploader は carrierwave の設定。これで簡単にアップロードできるようになる。
Controller
posts_controller.rb
class PostsController < ApplicationController def new @post = Post.new # @post.memories.build とすることで post に紐づいた memories を保存する準備が整う @post_memory = @post.memories.build end def create post = Post.new(post_params) if post.save! # 以下は memories に保存する処理。 each 文で複数の画像を保存可能。 params[:memories][:image].each do |image| post.memories.create(image: image, post_id: post.id) end end redirect_to root_path end private def post_params params.require(:post).permit(:title, memories_attributes: [:image]).merge(user_id: current_user.id) end endView
new.html.erb
<%= form_for @post, local: true, html: {class: "form_area"} do |f| %> <div class="form_area__field"> <%= f.text_area :title, id: "post_text", placeholder: "投稿内容を入力", rows: 10%> <div class="form_area__image_field"> <%= f.fields_for :memories do |m| %> <%= m.label :image, "画像" %> <%= m.file_field :image, multiple: true, name: "memories[image][]" %> <%= hidden_field :memories, :post_id, value: @post.id %> <% end %> </div> <div class="form_area__hidden_field"> <%= hidden_field :post, :user_id, value: current_user.id %> </div> <div class="form_area__action"> <%= f.submit "投稿", class: "form_area__action__btn" %> </div> </div> <% end %>fields_for で1回の投稿で複数のクラスを保存できる。
multiple: true で複数の画像を投稿できるように設定している。
表示するには?
<% @post.memories.each do |m|%> <%= image_tag m.image.url %> <% end %>上記で表示できる。
- 投稿日:2021-01-13T21:12:11+09:00
"Hello World"でみるプログラミング言語
はじめに
プログラミングを学ぶものなら誰しも通る道、Hello world。今回はそんなHello worldを出力するためのプログラムで、5つのプログラミング言語を比較していきます。皆さんが使っていなそうな言語を選びました。が、あくまでも独断と偏見によって決定されています。
1.C
いわずと知れた言語ですね。
#include <stdio.h> int main(void) { printf("Hello world\n"); }2.Python
簡潔に書けることが利点ですね。
print("Hello world")3.Ruby
日本生まれの言語ですね。
puts "Hello world"4.FORTRAN
最初の高水準言語として有名ですね。まだ息してたのか...
program hello print *, 'Hello world' end program hello5. PHP
HTML/CSSと一緒に使われたりしますね。
<?php echo "Hello world\n"; ?>終わりに
以上です。いかがでしたでしょうか?
Ruby
はやっぱり短かったですね。それが長所ですよね。PHP
のechoてエコーって読むんですね。初めて知りました。若輩者ゆえ、もしかしたら間違いがあるかもしれないので、その時はコメントなんかで教えてください。
- 投稿日:2021-01-13T20:42:39+09:00
Ruby:正規表現まとめ※コードサンプルあり
初心者向けに主な正規表現のパターンを整理しました。
主な正規表現のパターン一覧
パターン 意味 [a-z] 角括弧で囲まれた文字のいずれか1個にマッチ(この場合はaからzのいずれかの文字にマッチ) \d 数字にマッチ \D 数字以外にマッチ {n} 直前のパターンを n 回繰り返すものにマッチ {n, m} 直前の文字が少なくともn回、多くてもm回出現するものにマッチ . 改行以外のどの1文字にもマッチ * 直前の部分式の0回以上の繰り返しにマッチ + 直前の部分式の1回以上の繰り返しにマッチ \A 直後の文字列が先頭にある文字列にマッチ \z 直前の文字列が末尾にある文字列にマッチ ?= 直後に設定した文字が続く文字列にマッチ *? 直前に設定した文字が0回以上続く文字列をチェックし、?の直後の文字が出た段階でその1文字を返す 修飾子
パターン 意味 /i 大文字と小文字を区別しない 「*」の意味を深ぼる
パターン 意味 * 直前の文字の0回以上の繰り返しにマッチ 「*」の「0回以上の繰り返しにマッチ」がちょっとわかりにくいので、「+」と比較しながら確認します。
パターン 意味 マッチする文字列の例 a.*z aから始まってzで終わる2桁以上の文字列 az, abz,abczなど a.+z aから始まってzで終わる3桁以上の文字列 abz,abczなど 「*」自体は1桁以上の文字列を求めないので、上記の例のように2桁の文字列でもマッチします。「.」は改行以外のどの文字にもマッチしますが、「*」が後に付くことで「.」にあたる文字がなくてもマッチします。
一方で「+」は1桁以上の文字列を求めるので、2桁の文字列にはマッチしません。そのまま使える正規表現の主なパターン一覧
パターン 意味 /\A[ぁ-んァ-ン一-龥]/ 全角ひらがな、全角カタカナ、漢字 /\A[ァ-ヶー-]+\z/ 全角カタカナ /\A[a-zA-Z0-9]+\z/ 半角英数 /\A\d{3}[-]\d{4}\z/ 郵便番号(「-」を含む且つ7桁) greater_than_or_equal_to: 〇〇 〇〇以上の数値 less_than_or_equal_to: 〇〇 〇〇以下の数値 参考資料
- 投稿日:2021-01-13T18:47:40+09:00
Rails must be exist系のエラー(@saveが実行されない)
- 投稿日:2021-01-13T18:14:04+09:00
Mysql2::Error: Incorrect string valueのエラーの対処
はじめに
データベースを作成してマイグレーションも実行し、「いざ、データ(日本語)を入れて動かそう!」としたところで、以下のようなエラーが。。。
Mysql2::Error: Incorrect string value: '\xE3\x82\xB2\xE3\x82\xB9...' for column 'name' at row 1どうやら文字コードが不適切らしい。。。
ということで、本記事では、このエラーについて、僕が試したことや解決策を書いていこうと思います!技術・環境
Docker/docker-compose
ruby 2.7.2
rails 6.0.2.3試したこと
①テーブルの文字コードを調べてみました。
まずは、MySQLに接続する。
$ docker-compose run web rails db次に、データベースの文字コードを調べてみる。
> show variables like '%char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+やはり、データベースの文字コードが「latin1」だった。。。
※MySQLはdefaultで「latin1」が文字コードに設定されています。②データベースの文字コードをutf8mb4に修正
僕はDockerを使用していたので、以下のようにdocker-compose.ymlを修正。
※以下の公式ドキュメントを参考にしました。
https://hub.docker.com/_/mysqldocker-compose.ymlversion: "3" services: db: image: mysql:5.7 # この下の一行を追記しました command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci environment: MYSQL_ROOT_PASSWORD: password MYSQL_USER: root ports: - "3306:3306" volumes: - ./db/mysql/volumes:/var/lib/mysql web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/share-read ports: - "3000:3000" depends_on: - db volumes: mysql-data: driver: localデータベースの文字コードを再度調べてみる。
> show variables like '%char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+「よし!修正完了!」と思ったが、まだエラーが出てしまう。
もしかしたらテーブル自体の文字コードが「latin1」のまま?と思い調べてみると。。。> SHOW CREATE TABLE users; | users | CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, # 中略 PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |やはり、テーブルの文字コードが変わっていなかった。。。
③テーブルの文字コードをutf8mb4に修正
ということで、テーブルの文字コードを修正するのですが、10個くらいテーブルがあって一つ一つ修正するのは大変!
また、まだテーブルにデータを格納してなかったこともあり、今回はマイグレーションをやり直そうことにしました。$ docker-compose run web rails db:reset $ docker-compose run web rails db:migrateということで無事解決しました。
もし同じようなエラーが出てしまった方は、参考にしてみてください。
- 投稿日:2021-01-13T18:09:28+09:00
Cognito と API Gateway と Lambda と Dynamo
はじめに
前回のものにユーザ認証を足すために Cognito を Authorizer として使う。出来上がりはこんな感じ。User は Cognito で認証されて、APIをコールできるようになったり、自分でサインナップできるようになったりする。
だいたいこの記事の通りでよい
プログラミングせずにCognitoで新規ユーザー登録&サインインを試してみる というわかりやすい記事があるので、この通り実行するのがよい
Cognito の多くのサンプルは WebUI を書くところから始まるものだが、この記事はひとまず AWS CLI を使って、アプリコードなしに基本的な設定方法が理解できるのがよい
やってみて、記事との微妙な差分は下の画面の
Cognito
>ユーザープール
>アプリクライアント
>認証フローの設定
のところが以下のように少し変わっているくらい。認証フローの設定は以下で動きましたあと、一番最初にユーザープールを作るところで「クライアントシークレットを作成」にデフォルトで入っているチェックを外すべし。
外さないと CLI でユーザーをサインアップする時に以下のエラーになった
An error occurred (NotAuthorizedException) when calling the SignUp operation: Unable to verify secret hash for client <CLIENT_ID>テストしてみる
先のページの通り、
$TOKEN
にトークンを入れておいてcurl -H "Authorization: $TOKEN" https://****.execute-api.us-east-2.amazonaws.com/dev_1/projects | jqcurl -X POST -d '{"project-id": 200, "name": "Typing", "is-default": false}' -H "Authorization: $TOKEN" https://****.execute-api.us-east-2.amazonaws.com/dev_1/projects | jqcurl -X DELETE -d '{"project-id": 200}' -H "Authorization: $TOKEN" https://****.execute-api.us-east-2.amazonaws.com/dev_1/projects | jqcurl -X PUT -d '{"project-id": 200, "name": "Yoga"}' -H "Authorization: $TOKEN" https://****.execute-api.us-east-2.amazonaws.com/dev_1/projects | jq次回予告
CLI では動いたので、いよいよ次回はフロントエンドのクライアントをかいてみようか
- 投稿日:2021-01-13T14:27:51+09:00
belongs_to, has_many 関連付けの :foreign_keyオプションのデフォルト設定
- 投稿日:2021-01-13T13:46:54+09:00
ビット操作メソッド三題(Ruby)
正統的なビット操作の話ではありません。
Ruby を使っているとビット操作はあまり必要なのだが、競技プログラミングをやっていたりすると時にはビット操作したくなることもある。で、Ruby をあらためて見てみると(当り前だが)ビット操作も一通り揃っているわけだ。競プロで便利だなと思うのは例えば「ビットの立っている」ものを数える、つまり整数を二進数表現に直して "1" の数を数えるというもので、Ruby では
to_s(2).chars.count { _1 == "1" }と簡潔に書ける。
また、いわゆる「ビット全探索」は、Ruby ではビットを使わなくても簡単に書ける。「ビット全探索」でよく使われるのが部分集合の列挙で、例えば、[0, 1, 2, 3, 4]
のすべての部分集合を列挙する場合は、ary = [0, 1, 2, 3, 4] co = 0 (0..ary.size).each do |i| ary.combination(i) do |sub_set| p [co, sub_set] co += 1 end endなどと書ける。結果は
[0, []] [1, [0]] [2, [1]] [3, [2]] [4, [3]] [5, [4]] [6, [0, 1]] [7, [0, 2]] [8, [0, 3]] [9, [0, 4]] [10, [1, 2]] [11, [1, 3]] [12, [1, 4]] [13, [2, 3]] [14, [2, 4]] [15, [3, 4]] [16, [0, 1, 2]] [17, [0, 1, 3]] [18, [0, 1, 4]] [19, [0, 2, 3]] [20, [0, 2, 4]] [21, [0, 3, 4]] [22, [1, 2, 3]] [23, [1, 2, 4]] [24, [1, 3, 4]] [25, [2, 3, 4]] [26, [0, 1, 2, 3]] [27, [0, 1, 2, 4]] [28, [0, 1, 3, 4]] [29, [0, 2, 3, 4]] [30, [1, 2, 3, 4]] [31, [0, 1, 2, 3, 4]]となって、確かにすべての部分集合が列挙されている(空集合を含む場合)。Ruby の組み込みメソッドにないのが不思議なくらいだが、これはだから
class Array def each_subset(empty_set=true) if block_given? n = empty_set ? 0 : 1 (n..size).each do |i| combination(i) { yield _1 } end else to_enum(__method__, empty_set) end end endとでもして Array クラスに追加してやったらよい。で、上のと同じ結果を得ようと思えば
[0, 1, 2, 3, 4].each_subset.with_index do |ary, i| p [i, ary] endとでもすればよいのである。これがあったら競プロに便利だろうね。組み込みに追加されないかね。
本題っぽいもの
で、以下が本題というほどでもないのだが、Ruby にビット操作メソッドを三つ追加してみた。いずれも実用性はないものと考えられ、たんなる遊び。
こんな感じである。class Integer def each_bit if block_given? to_s(2).chars.reverse.map { _1 == "1" }.each { yield _1 } else to_enum(__method__) end end def bit_reverse to_s(2).chars.reverse.join.to_i(2) end end class Array def to_i map { _1 ? "1" : "0" }.reverse.join.to_i(2) end endまず、
Integer#each_bit
はビットごとにブロックを実行し、ブロック変数にはビットが 1 ならtrue
、0 ならfalse
が入るというもの。0b10101101.each_bit { p _1 }を実行すると
true false true true false true false trueが出力となる。下位ビットから実行されていくのに注意。だから最後は必ず
true
になる。173.each_bit.map(&:itself) #=>[true, false, true, true, false, true, false, true] 0.each_bit.map(&:itself) #=>[false] 173.to_s(2) #=>"10101101"など。
Array#to_i
はこれの反対。配列の中身で真のものを 1、偽のものを 0 として二進表現と見做し、Integer に変換する。これも配列の左から下位ビットに詰められていくのに注意。[true, false, true, true, false, true, false, true].to_i #=>173 [:a, nil, 1, -1, 1 == 0, 1.integer?, 0.1 < 0, true].to_i #=>173 173.each_bit.map(&:itself).to_i #=>173
Integer#bit_reverse
はテキトーに思いついたもの。Integer のビットを左右反転させた Integer を作る。まぎらわしいが、ビット反転(否定、Integer#~
)とは全然ちがうのです。173.bit_reverse #=>181 173.to_s(2) #=>"10101101" 181.to_s(2) #=>"10110101"なお、
bit_reverse.bit_reverse
は必ずしも元に戻らないので注意(わかりますね?)。
おそまつ様でした。
- 投稿日:2021-01-13T13:40:32+09:00
belongs_to関連付けのforeign_keyオプション(Rails Guide自分用訳)
Railsの慣例では、相手のモデルを指す外部キーを保持しているjoinテーブル上のカラム名については、その
モデル名にサフィックス_idを追加した関連付け名
が使われることを前提とします。:foreign_keyオプションを使えば、外部キーの名前を直接指定できます。
By convention, Rails assumes that the column used to hold the foreign key on this model is
the name of the association with the suffix _id added
. The :foreign_key option lets you set the name of the foreign key directly:
- 自分用訳
Railsの慣例では、このモデルの外部キーを保持するカラム名は、
関連付け名に接尾辞の_idを付けたもの
と、想定している。:foreign_keyオプションを使えば、外部キーを保持するカラム名を指定することができる。
Railsが慣例で想定しているforeign_key
class Book < ApplicationRecord belongs_to :author(, foreign_key: author_id) end
関連付け名に_idをつけたforeign_keyをRailsは想定しているので、下の例では :foreignkeyオプションが必要となる。
class Book < ApplicationRecord belongs_to :author, class_name: "Patron", foreign_key: "patron_id" end
- 投稿日:2021-01-13T13:24:06+09:00
belongs_to関連付けのforeign_keyオプション(Rails Guide自分用訳)
Railsの慣例では、相手のモデルを指す外部キーを保持しているjoinテーブル上のカラム名については、そのモデル名にサフィックスidを追加した関連付け名が使われることを前提とします。:foreignkeyオプションを使えば、外部キーの名前を直接指定できます。
By convention, Rails assumes that the column used to hold the foreign key on this model is the name of the association with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly:
- 自分用訳
Railsの慣例では、このモデルの外部キーを保持するカラム名は、関連付け名に接尾辞の_idを付けたものと、想定している。:foreign_keyオプションを使えば、外部キーを保持するカラム名を指定することができる。
- 投稿日:2021-01-13T12:48:21+09:00
【環境構築】localからrailsのアンインストール
はじめに
railsを誤ってlocalにインストールしてしまったので、アンインストールした時の手順を記事にしとこうと思います。
環境
macOS Catalina バージョン 10.15.7
Homebrew 2.7.1
rbenv 1.1.2
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin19]
gem 3.2.4
Bundler version 2.1.4手順
先ず
cd
でホームディレクトリに移動します。
続いてgem list
でgemをインストール状況を確認します。gem list *** LOCAL GEMS *** . . . rails (6.1.0, 6.0.3.4, 6.0.0) rails-dom-testing (2.0.3) rails-html-sanitizer (1.3.0) rails-i18n (6.0.0) railties (6.1.0, 6.0.3.4, 6.0.0) rake (13.0.3, 13.0.1, 12.3.2) . . .
rails (6.1.0, 6.0.3.4, 6.0.0)
rails6.0.0のみ使用するので、他の2つをアンイストールします。アンインストール
gem uninstall rails
だけでいいのかと思いきや、調べるとrailties
も削除する必要があるらしい。gem uninstall rails -v '6.0.3.4' gem uninstall railties -v '6.0.3.4'上のコマンドを打つと、下記が表示される。
You have requested to uninstall the gem: railties-6.0.3.4 rails-6.0.3.4 depends on railties (= 6.0.3.4) If you remove this gem, these dependencies will not be met. Continue with Uninstall? [yN] y Successfully uninstalled railties-6.0.3.4「railsアンインストールしてもいい?」と聞かられてるので、
y
を入力します。
[yN]は[yes or No]という意味です。続いて
6.1.0
をアンインストールします。gem uninstall rails -v '6.1.0' gem uninstall railties -v '6.1.0' You have requested to uninstall the gem: railties-6.1.0 rails-6.1.0 depends on railties (= 6.1.0) If you remove this gem, these dependencies will not be met. Continue with Uninstall? [yN] yアンインストールできてるか確認します。
rails -v Rails 6.0.0できてました!!
最後、
bundle inatall
で読み込んで、変更を反映します。bundle inatallこれで完了です。
最後に
同じ様に悩んでる方々の助けになればと思い、記事を投稿しております。
それでは、また次回お会いしましょう〜参考
- 投稿日:2021-01-13T12:45:27+09:00
iso-2022-jpの文字列をutf-8に変換する
メールを受け取った際にiso-2022-jpで送られたために文字化けしてしまっていました。
rubyのencodeを使ってutf-8に変換します
検証するためにiso-2022-jpでエンコードされた文字列を生成します
puts "ほげ".encode('iso-2022-jp') # => $B$[$2(Bencodeを以下のように記述します
str.encode(変換先, 変換元, 変換オプション)今回はiso-2022-jpの文字列をutf-8に変換したいので次のようになります
str = "ほげ".encode('iso-2022-jp') puts str # => $B$[$2(B puts str.encode('utf-8', 'iso-2022-jp', invalid: :replace, undef: :replace, replace: '')今回は変換できない文字を空文字に置き換えるオプションを指定しています
変換元がわかっている場合でないとこの方法で変換することはできませんが、
今回のようにメールが文字化けしている場合などでは有効に使えそうです参考
https://docs.ruby-lang.org/ja/latest/method/String/i/encode.html
- 投稿日:2021-01-13T12:03:24+09:00
Formオブジェクトを用いて作成したデータを、特定のデータのみ削除する方法
要点
- メルカリクローンのアプリを作るときの参考に
- dependent: :destroy を用いて外部キーの削除制限を外す
- 下記のエラーを解消する方法
ActiveRecord::InvalidForeignKey (Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (`-アプリ名-_development`.`-中間テーブル名-`, CONSTRAINT `fk_rails_~~~~~` FOREIGN KEY (`-商品のテーブル名-_id`) REFERENCES `-商品のテーブル名-` (`id`))):はじめに
メルカリのようなフリマアプリを作成中で、Formオブジェクトを用いて商品にタグ付けして出品できる機能を実装する所まで行いました
各モデルとコントローラーは以下のようになります
- Item/商品
- Tag/タグ
- TagItemRelation/商品とタグの中間テーブル
- TagsItem/ ItemとTagを同時に保存するためのFormオブジェクト
/app/model/item.rbclass Item < ApplicationRecord has_many :tag_item_relations has_many :tags, through: :tag_item_relations end/app/model/tag.rbclass Tag < ApplicationRecord has_many :tag_item_relations has_many :items, through: :tag_item_relations validates :tag_name, uniqueness: true end/app/model/tag_item_relation.rbclass TagItemRelation < ApplicationRecord belongs_to :item belongs_to :tag end/app/form/tags_item.rbclass TagsItem include ActiveModel::Model attr_accessor :item_name, :tag_name with_options presence: true do validates :item_name end def save item = Item.create(item_name: item_name) tag = Tag.where(tag_name: tag_name).first_or_initialize tag.save TagItemRelation.create(item_id: item.id, tag_id: tag.id) end endコントローラー
/app/controller/items_controller.rbclass ItemsController < ApplicationController def index @items = Item.all.order('created_at ASC') end def new @item = TagsItem.new end def create @item = TagsItem.new(items_params) if @item.valid? @item.save redirect_to root_path else render :new end end private def items_params params.require(:tags_item).permit( :item_name, :tag_name ) end endその後商品閲覧機能と
問題の商品削除機能を実装した/app/controller/items_controller.rbclass ItemsController < ApplicationController before_action :set_item, only: [:show, :destroy] ### 中略 def show @tags_item = TagItemRelation.find(params[:id]) end def destroy if current_user.id == @item.user_id @item.destroy redirect_to root_path else render :show end end private ### 中略 def set_item @item = Item.find(params[:id]) @tag = Tag.find(params[:id]) end endしかし実際に出品した商品を削除してみると、、、
ActiveRecord::InvalidForeignKey (Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (`-アプリ名-_development`.`tag_item_relations`, CONSTRAINT `fk_rails_~~~~~~` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`))):のエラーが出てしまい、商品の削除ができませんでした
調べたこと
エラー内容をよく読んでみると、商品idは中間テーブルの外部キー(foreign key)に含まれて
デリートやアップデートができないよ!と書かれているのがわかりますそこで外部キーについて調べてみると、、、
外部キー(外部制約キーとも)は、よくマイグレーションファイルなどに書いてるデータベースのキーで
データベースの特定の項目に好き勝手な値を入れることを防ぎ、外部の項目から選んで入れる制約を持ち
主に外部キーのテーブルから主キーのテーブルのデータに対して、変更を制限するものだそうです
今回の場合外部キーのテーブルはtag_item_relations、主キーのテーブルはitemになることが分かりますね外部キーの設定変更
今回のエラーの原因はマイグレーションファイルに記載した外部キー(foreign_key: trueのやつ)でした
ですがマイグレーションファイルごと書き換えてしまうと別の不具合が起きてしまう恐れがあります
今回はdestoryで外部キーとして設定している項目を削除させるように、dependent: :destroyを設定しようと思います
この簡単な設定により不具合なく商品の削除ができるようになります/app/model/item.rbclass Item < ApplicationRecord has_many :tag_item_relations, foreign_key: :item_id, dependent: :destroy has_many :tags, through: :tag_item_relations end参考にしたサイト
関連記事
- 投稿日:2021-01-13T11:53:09+09:00
RSpec エラー 解決 undefined method 'name' in 'user' factory
rspecコマンドを打った時に出たエラー。
結論
FactoryBotの定義の書き方が違う。
コード
間違った書き方
require 'rails_helper' FactoryBot.define do factory :user do name "Example" sequence(:email) { |n| "tester#{n}@example.com" } password "password" password_confirmation "password" year "1年" bio "hello!" end end正しい書き方
require 'rails_helper' FactoryBot.define do factory :user do name {"Example"} sequence(:email) { |n| "tester#{n}@example.com" } password {"password"} password_confirmation {"password"} year {"1年"} bio {"hello!"} end end一応gemfile
group :test do #rspecには、以下の3つのgemが必要。 gem 'rspec-rails' gem 'factory_bot_rails' gem 'rails-controller-testing' #rspecのfeatureで必要。 gem 'capybara', '~> 2.13' #Capybaraでテスト中に、現在どのページを開いているのか確認するため gem 'launchy' #便利。validationが一行くらいでかける。 gem 'shoulda-matchers', git: 'https://github.com/thoughtbot/shoulda-matchers.git', branch: 'rails-5' end group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'spring-commands-rspec' end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem 'web-console', '>= 4.1.0' # Display performance information such as SQL time and flame graphs for each request in your browser. # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md gem 'rack-mini-profiler', '~> 2.0' gem 'listen', '~> 3.3' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
- 投稿日:2021-01-13T11:46:54+09:00
RSpec エラー undefined method `feature' for RSpec:Module
rspecコマンドを打った時に出たエラー。
結論
capybaraを読み込んでいなかった。
コード
エラーコード
require 'rails_helper' RSpec.feature "Users", type: :feature do describe "Signup page" do before do visit signup_path end it "display Signup contents, title properly" do expect(page).to have_css('h1', text: 'ユーザー登録') expect(page).to have_title 'Signup hoge' end end end解決したコード capybaraを読み込む
require 'rails_helper' + require 'capybara/rspec' RSpec.feature "Users", type: :feature do describe "Signup page" do before do visit signup_path end it "display Signup contents, title properly" do expect(page).to have_css('h1', text: 'ユーザー登録') expect(page).to have_title 'Signup hoge' end end end一応gemfile
#rspecのprogressbarを表示してくれる。実行のコマンド% bin/rspec spec/ --format Fuubar gem 'fuubar' group :test do #rspecには、以下の3つのgemが必要。 gem 'rspec-rails' gem 'factory_bot_rails' gem 'rails-controller-testing' #rspecのfeatureで必要。 gem 'capybara', '~> 2.13' #Capybaraでテスト中に、現在どのページを開いているのか確認するため gem 'launchy' #便利。validationが一行くらいでかける。 gem 'shoulda-matchers', git: 'https://github.com/thoughtbot/shoulda-matchers.git', branch: 'rails-5' end group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'spring-commands-rspec' end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem 'web-console', '>= 4.1.0' # Display performance information such as SQL time and flame graphs for each request in your browser. # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md gem 'rack-mini-profiler', '~> 2.0' gem 'listen', '~> 3.3' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]参考:
https://stackoverflow.com/questions/26217184/rspec-3-1-undefined-method-feature-for-mainobject
- 投稿日:2021-01-13T11:46:54+09:00
RSpec エラー 解決 undefined method `feature' for RSpec:Module
rspecコマンドを打った時に出たエラー。
結論
capybaraを読み込んでいなかった。
コード
エラーコード
require 'rails_helper' RSpec.feature "Users", type: :feature do describe "Signup page" do before do visit signup_path end it "display Signup contents, title properly" do expect(page).to have_css('h1', text: 'ユーザー登録') expect(page).to have_title 'Signup hoge' end end end解決したコード capybaraを読み込む
require 'rails_helper' + require 'capybara/rspec' RSpec.feature "Users", type: :feature do describe "Signup page" do before do visit signup_path end it "display Signup contents, title properly" do expect(page).to have_css('h1', text: 'ユーザー登録') expect(page).to have_title 'Signup hoge' end end end一応gemfile
#rspecのprogressbarを表示してくれる。実行のコマンド% bin/rspec spec/ --format Fuubar gem 'fuubar' group :test do #rspecには、以下の3つのgemが必要。 gem 'rspec-rails' gem 'factory_bot_rails' gem 'rails-controller-testing' #rspecのfeatureで必要。 gem 'capybara', '~> 2.13' #Capybaraでテスト中に、現在どのページを開いているのか確認するため gem 'launchy' #便利。validationが一行くらいでかける。 gem 'shoulda-matchers', git: 'https://github.com/thoughtbot/shoulda-matchers.git', branch: 'rails-5' end group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'spring-commands-rspec' end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem 'web-console', '>= 4.1.0' # Display performance information such as SQL time and flame graphs for each request in your browser. # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md gem 'rack-mini-profiler', '~> 2.0' gem 'listen', '~> 3.3' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]参考:
https://stackoverflow.com/questions/26217184/rspec-3-1-undefined-method-feature-for-mainobject
- 投稿日:2021-01-13T09:40:46+09:00
ハッシュ
ハッシュとは
データとそれに対応する名前をつけて複数の値を管理するものです。
データの部分を「バリュー」、名前の部分を「キー」とよびます。このようなキーとバリューで管理する考え方を「キーバリューストア」と呼びます。
ハッシュは { } (波括弧)を使って生成します。
ハッシュロケット => を用いてキーとバリューをセットにして書きます。
変数 = { キー1 => バリュー1, キー2 => バリュー2, キー3 => バリュー3 }ハッシュの書き方には複数あり、シンボルという物を使って書かれる書き方もあります。
シンボルは、見た目は文字列のようですが、実際の中身は数値になっています。
シンボルの宣言は、文字列の先頭にコロン:をつけます。
同じ文字列であれば、文字列にクォーテーションをつけてもつけなくても中身は同じ値になります。:"Hello" :Hello #どちらも同じ値コンピューターは、文字列を扱うよりも、数値を扱うほうが処理速度は速くなるので、処理速度を速くすることと、文字列としての役割も果たせることから、ハッシュのキーには、文字列よりもシンボルを用いることが多いのです。
実際にハッシュを生成してみましょう。
son = { "name" => "Nick", "age" => 12 } father = { name: "George", age: 38 }ハッシュへ要素を追加したい場合
ハッシュ[追加するキー] = 値 とします。
son = { "name" => "Nick", "age" => 12 } father = { name: "George", age: 38 } father[:hobby] = "fishing" #キー hobby バリュー fishing を追加ハッシュの値を取得
ハッシュ[取得したい値のキー] とします。
son = { "name" => "Nick", "age" => 12 } father = { name: "George", age: 38, hobby: "fishing" } puts father[:age] # 38 と出力されるハッシュの値を変更
ハッシュ[変更したい値のキー] = 値 とします。
son = { "name" => "Nick", "age" => 12 } father = { name: "George", age: 38, hobby: "fishing" } father[:age] = 40 puts father[:age] #40と出力される以上です。
- 投稿日:2021-01-13T09:37:31+09:00
TailwindUIのFormの見た目がサンプル通りにならない
困ったこと
同じスタイルクラスを当ててるはずなのに、見た目が違う。
理想の見た目
実際の見た目
対応内容
たぶん次のどっちかやればええんちゃうか
https://github.com/tailwindlabs/tailwindcss-forms
https://tailwindcss-custom-forms.netlify.app/1個目のほうをインストールしたけど怒られた
terminalERROR in ./node_modules/tippy.js/animations/perspective.css (./node_modules/css-loader/dist/cjs.js??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./node_modules/tippy.js/animations/perspective.css) Module build failed (from ./node_modules/postcss-loader/src/index.js): TypeError: Cannot read property 'none' of undefined at /Users/XXXXXXX/projects/XXXXX/node_modules/@tailwindcss/forms/src/index.js:38:26 at /Users/XXXXXXX/projects/XXXXX/node_modules/tailwindcss/lib/util/processPlugins.js:66:5 at Array.forEach (<anonymous>) at _default (/Users/XXXXXXX/projects/XXXXX/node_modules/tailwindcss/lib/util/processPlugins.js:60:11) at /Users/XXXXXXX/projects/XXXXX/node_modules/tailwindcss/lib/processTailwindFeatures.js:56:54 at LazyResult.run (/Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:288:14) at LazyResult.asyncTick (/Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:212:26) at LazyResult.asyncTick (/Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:225:14) at /Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:217:17 ERROR in ./app/javascript/css/tailwind.css (./node_modules/css-loader/dist/cjs.js??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./app/javascript/css/tailwind.css) Module build failed (from ./node_modules/postcss-loader/src/index.js): TypeError: getProcessedPlugins is not a function at /Users/XXXXXXX/projects/XXXXX/node_modules/tailwindcss/lib/processTailwindFeatures.js:67:83 at LazyResult.run (/Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:288:14) at LazyResult.asyncTick (/Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:212:26) at LazyResult.asyncTick (/Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:225:14) at /Users/XXXXXXX/projects/XXXXX/node_modules/postcss/lib/lazy-result.js:217:17 ℹ 「wdm」: Failed to compile.Tailwind CSS v2.0. 用だからかな??
Tailwind CSS v2.0インストール
下記を元に2.0を入れる
https://tailwindcss.com/docs/installationTerminalnpm install tailwindcss@latest postcss@latest autoprefixer@latest
下記怒られる。
Terminal> % bin/webpack-dev-server ℹ 「wds」: Project is running at http://localhost:3035/ ℹ 「wds」: webpack output is served from /packs/ ℹ 「wds」: Content not from webpack is served from /Users/XXX/projects/XXX/public/packs ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: wait until bundle finished: /packs/js/application-83db7df6641884880765.js ✖ 「wdm」: Hash: f6dfc3f28099b484dcf6 Version: webpack 4.44.2 Time: 3149ms Built at: 2021-01-13 7:49:08 Asset Size Chunks Chunk Names js/application-83db7df6641884880765.js 1.88 MiB application [emitted] [immutable] application js/application-83db7df6641884880765.js.map 1.92 MiB application [emitted] [dev] application manifest.json 364 bytes [emitted] ERROR in ./app/javascript/css/tailwind.css (./node_modules/css-loader/dist/cjs.js??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./app/javascript/css/tailwind.css) Module build failed (from ./node_modules/postcss-loader/src/index.js): Error: PostCSS plugin tailwindcss requires PostCSS 8. Update PostCSS or downgrade this plugin. at Processor.normalize (/Users/XXX/projects/XXX/node_modules/postcss-loader/node_modules/postcss/lib/processor.js:153:15) at new Processor (/Users/XXX/projects/XXX/node_modules/postcss-loader/node_modules/postcss/lib/processor.js:56:25) at postcss (/Users/XXX/projects/XXX/node_modules/postcss-loader/node_modules/postcss/lib/postcss.js:55:10) at /Users/XXX/projects/XXX/node_modules/postcss-loader/src/index.js:140:12 ERROR in ./node_modules/tippy.js/animations/perspective.css (./node_modules/css-loader/dist/cjs.js??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./node_modules/tippy.js/animations/perspective.css) Module build failed (from ./node_modules/postcss-loader/src/index.js): Error: PostCSS plugin tailwindcss requires PostCSS 8. Update PostCSS or downgrade this plugin. at Processor.normalize (/Users/XXX/projects/XXX/node_modules/postcss-loader/node_modules/postcss/lib/processor.js:153:15) at new Processor (/Users/XXX/projects/XXX/node_modules/postcss-loader/node_modules/postcss/lib/processor.js:56:25) at postcss (/Users/XXX/projects/XXX/node_modules/postcss-loader/node_modules/postcss/lib/postcss.js:55:10) at /Users/XXX/projects/XXX/node_modules/postcss-loader/src/index.js:140:12インストール方法にも書いてるが、こちらを参考にする。
Terminalnpm uninstall tailwindcss postcss autoprefixer npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
postcss.config.jsmodule.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, } }terminal-> % npx tailwindcss init @tailwindcss/postcss7-compat 2.0.2 ? tailwind.config.js already exists.tailwind.config.jsmodule.exports = { purge: [], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: {}, plugins: [], }既に下記だった
app/javascript/css/tailwind.css@import "tailwindcss/base"; @import "tailwindcss/components"; @import "tailwindcss/utilities";一行目追加
app/javascript/src/js/application.jsimport 'tailwindcss/tailwind.css' import './dropdown.js'もう一度、tailwindcss-formsインストールする
今見直したら、2つとも叩いたが、片方でいいと思う。
Terminal-> % npm install @tailwindcss/forms + @tailwindcss/forms@0.2.1 added 2 packages from 1 contributor, removed 7 packages and audited 1824 packages in 8.861s found 0 vulnerabilities -> % yarn add @tailwindcss/forms yarn add v1.22.10 warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json. [1/4] ? Resolving packages... [2/4] ? Fetching packages... [3/4] ? Linking dependencies... warning " > webpack-dev-server@3.11.1" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0". warning "webpack-dev-server > webpack-dev-middleware@3.7.2" has unmet peer dependency "webpack@^4.0.0". [4/4] ? Building fresh packages... success Saved lockfile. success Saved 4 new dependencies. info Direct dependencies ├─ @tailwindcss/forms@0.2.1 ├─ @tailwindcss/postcss7-compat@2.0.2 └─ tailwindcss@2.0.2 info All dependencies ├─ @tailwindcss/forms@0.2.1 ├─ @tailwindcss/postcss7-compat@2.0.2 ├─ mini-svg-data-uri@1.2.3 └─ tailwindcss@2.0.2 ✨ Done in 19.76s.tailwind.config.jsmodule.exports = { purge: [], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: {}, plugins: [ require('@tailwindcss/forms'), ], }結果
いい感じになった。
- 投稿日:2021-01-13T08:24:30+09:00
C#で書くものはRubyでこう書く
注意
個人的によく使う書き方のみ記述します。
まだ未完成です。暇だった時に更新します。標準ストリーム
標準出力
C#Console.Write("改行なし"); Console.WriteLine("改行あり");Rubyprint "改行なし" puts "改行あり"標準入力
C#// 値がひとつだけの場合 var single = Console.ReadLine(); // 複数ある場合 var multiple = Console.ReadLine().Split(",");Ruby# 値がひとつだけの場合 single = gets # 複数ある場合 multiple = gets.split(",")空白で区切られてるだけの場合、こういう書き方もできるらしい。
multiple = gets.split
Rubyって凄いね。ループ処理
for文
C#for (var i = 0; i < 10; i++) { Console.WriteLine(i); }Rubyfor i in 0..9 puts i endforeach文
C#var array = new int[5] {1, 2, 3, 4, 5}; foreach (var i in array) { Console.WriteLine(i); }Rubyarray = (1..5) array.each do |i| puts i endWhile文
C#var num = 0; while (num < 5) { Console.WriteLine(num); num++; }Rubynum = 0 while num < 5 puts num num += 1 endRubyにはインクリメントとデクリメントがないみたい。
その代わり?1を追加するのとは別で、succとnextてのがあるらしい。succ
Rubynum = 1 apt = "a" letter = "z" puts num.succ # 出力: 2 puts apt.succ # 出力: b puts letter.succ # 出力: aaこれはこれで便利。zの次がaaってのも面白い(何処で使うかわからないけど……)
ただ、デクリメントに対応するものはないっぽい。
近いものがあるみたい……pred
Rubynum = 1 puts num.pred # 出力: 0ただ、アルファベットでやるとエラーが出た。
まぁそんな問題はないはず!条件分岐
if文
C#var name = "たけし"; if (name == "たけし") { Console.WriteLine("ガタガタガタガタガタガタガタ"); } else if (name == "たくろう") { Console.WriteLine("残念なイケメン"); } else { Console.WriteLine("みか"); }Rubyname = "たけし" if name == "たけし" puts "ガタガタガタガタガタガタガタ" elsif name == "たくろう" puts "残念なイケメン" else puts "みか" end個人的にRubyはちょっと見にくいかも……
Switch文
C#var energyDrink = "Monster"; switch (energyDrink) { case "Monster": Console.WriteLine("いっぱい味がある"); break; case "Zone": Console.WriteLine("安くてデカい"); break; default: Console.WriteLine("レッドブルが値下げするらしい"); break; }RubyenergyDrink = "Monster" case energyDrink when "Monster" puts "いっぱい味がある" when "Zone" puts "安くてデカい" else puts "レッドブルが値下げするらしい" endいちいちbreak書くのめんどくさかったからありがたい。
三項演算子
C#var num = 10; var results = (num == 10) ? "true" : "false";Rubynum = 10; results = (num == 10) ? "true" : "false";こいつは流石に変わらなかった。
- 投稿日:2021-01-13T00:46:52+09:00
【Rails】RSpec キホンのキ
はじめに
RSpecはRailsで定番のテストフレームワーク。
minitestと比べると必要なGemが多い。
とはいえ一度に全てのGemを入れると、どのGemがどこでどういう働きをするのかが曖昧になってしまう。
そこで、RSpecの学習に至っては、テストの内容に応じてGemを逐次追加していく手法を取ることにした。RSpecとFactoryBot
Gemの導入と設定
RSpec
と、テスト用データの生成用となるFactoryBot
を導入する。
FactoryBotはminitestにおけるfixture的な役割を担う。
ここでspring-commands-rspec
を入れておくと、RSpecをbinstub
から実行し、spring
を使ってテスト開始にかかる時間を短縮できるとのこと。Gemfile.rbgroup :development, :test do gem "rspec-rails" gem 'spring-commands-rspec' gem "factory_bot_rails" end
bundle install
したら以下のコマンドでRSpec用のデータを生成する。$ bundle exec rails generate rspec:installRSpecの設定を追加する。
設定はconfig/application.rb
に書く。
ここではgenerate
コマンド実行時にRSpec用のファイルを生成するかを設定している。
true
かfalse
かはプロジェクトによって適宜変更する。config/application.rbmodule SampleApp class Application < Rails::Application . . . config.generators do |g| g.test_framework :rspec, controller_specs: true, fixtures: true, helper_specs: true, model_specs: true, request_spec: true, routing_specs: false, view_specs: false end end end次にFactoryBotの設定。
以下のように記述することで、FactoryBotクラスの呼び出しを簡略化できる。spec/rails_helper.rbRSpec.configure do |config| config.include FactoryBot::Syntax::Methods end# FactoryBotクラスの呼び出し user = FactoryBot.create(:user) #省略版 user = create(:user)テスト用データの設定をもう一つ。
以下の一行のコメントアウトを外しておく。
spec/support/
配下のファイルを読み込めるようになる。
ここにはテスト用のヘルパーを記述するファイルなどが置かれる。spec/rails_helper.rb# The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }最後に、RSpecのbinstubを導入する。
これで$ bundle exec rspec spec/
に加えて$ bin/rspec spec/
でテストを実行できるようになる。$ bundle exec spring binstub rspec # 実行コマンド $ bin/rspec spec/specファイルの生成
RSpecで使用されるファイルは全て
*_spec.rb
というファイル名になっている。
これをコントローラやモデルに合わせて各種用意していく。
例えばusers_controller用のspecファイルは以下のように生成する。$ rails g rspec:controller users create spec/controllers/users_controller_spec.rbテスト用データの用意
FactoryBotを使って、各種クラスのテスト用データ(インスタンス)を定義する。
例えばUserクラスなら、/spec/factories/users.rb
に定義していく。/spec/factories/users.rbFactoryBot.define do # Userモデルのテストデータmichaelを定義 factory :michael, class: User do name 'michael' email 'michael@example.com' end # 汎用データをたくさん用意する factory :user do sequence(:name) { |n| "name-#{n}"} sequence(:email) { |n| "test-#{n}@example.com"} end end定義したテストデータを呼び出してインスタンス変数に格納し、テストで使用する。
/spec/models/users_spec.rbRSpec.describe User, type: :model do before do # モデルのみの作成 @michael = build(:michael) # DBへレコード生成 @michael = create(:michael) # 汎用データの活用 @user1 = build(:user) @user2 = build(:user) @user3 = build(:user) end . . . end続く!!!
- 投稿日:2021-01-13T00:05:31+09:00
Ruby製REST-like APIフレームワーク「grape」の最小構成 (RSpec入り / Heroku対応)
GET /ping
で{"response": "pong"}
を返すAPIサーバを、Ruby製REST-like APIフレームワーク「grape」で作成する最小構成。ファイル/ディレクトリ構成
. ├── Gemfile ├── Procfile ├── config.ru ├── api.rb └── spec ├── spec_helper.rb └── api_spec.rbユニットテスト
bundle exec rspec spec/api_spec.rbローカル開発
bundle exec rackup ## もしくは heroku local:startファイル解説
Gemfile
bundle init
で作成した後、以下を行う。
- Ruby のバージョン指定 (Heroku向け)
- grape、rspec、puma の指定
- rack-test はユニットテスト(RSpec)向け
Gemfile# frozen_string_literal: true source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } ruby "~> 3.0.0" gem "grape" gem "rspec" gem "puma" group :development do gem "rack-test" endProcfile
Procfile は Heroku 向け。Herokuを使わない場合は不要。
Procfileweb: bundle exec rackup config.ru -p $PORTconfig.ru
rackup 向け。
config.rurequire_relative File.join("api") run APIserverapi.rb
API本体。
api.rbrequire "grape" class APIserver < Grape::API format :json get "/ping" do {"response": "pong"} end endspec/spec_helper.rb
RSpec向け。
require_relative
は config.ru と同じものを指定していく。
require "rack/test"
以下は grape/RSpec に書いてある通り、おまじない的。app
が API のインスタンスを返すようにする必要がある。spec/spec_helper.rbrequire_relative File.join("..", "api") require "rack/test" include Rack::Test::Methods def app APIserver endspec/api_spec.rb
ユニットテスト本体。
spec/api_spec.rbrequire "spec_helper" context "GET ping" do it "return pong" do get "/ping" expect(last_response.status).to eq(200) expect(JSON.parse(last_response.body)).to eq({"response" => "pong"}) end end今後の拡張ポイント
rackup(webサーバ)のエントリーポイントは
config.ru
、RSpec(ユニットテスト)のエントリーポイントはspec_helper.rb
です。require等はここに集中する(もしくは共通化)するようにしましょう。APIのデータ構造を実装したければ、grape-entityが使えます。Nestedな1:N構造も実装可能です。RESTful APIにおいて一対多関係のデータ構造をGrape::Entityを使って表現する
複雑なJSONの検証には rspec-json_matcher が使えます。複雑なケースの例は rspec-json_matcherでJSONの検証を自由自在に行う をご覧ください。
EoT