- 投稿日:2020-05-14T23:53:01+09:00
2020年最新【EC2】 postgresql9.6、10、11をインストールする方法
$ yum update $ yum install postgresql noを選択する。すると下記の文字が現れる。 postgresql is available in Amazon Linux Extra topics "postgresql9.6" and "postgresql10" and "postgresql11" To use, run # sudo amazon-linux-extras install :topic: Learn more at https://aws.amazon.com/amazon-linux-2/faqs/#Amazon_Linux_Extras で、下記を入力すればインストールできる。 $ sudo amazon-linux-extras install postgresql9.6 $ yum install postgresql-server
- 投稿日:2020-05-14T22:41:18+09:00
RailsにBULMAでビューを作成するまでの手順
どうも!!
Railsで、オリジナルアプリ開発を始めた、プログラミング学習歴3ヶ月の初心者です。BULMAを導入するまでの経緯
CSSフレームワークのBULMAをRailsで使いたい!!!
「今のところJSを使わないので、BootstrapではなくてBULMAを使いたいな。」と思い立ち、
ビューで使用するまでの手順を模索していました。BULMAの概要は以下を参照ください。
BULMA具体的な手順
gemをGemfileに記載する
Gemfilegem "bulma-rails"bundle install
ターミナル$ bundle installapplication.scssでimport
application.scss@import "bluma";app/assets/stylesheets/application.scss(application.cssから変更してます。)
にBULMAをインポートする。application.html.erbを編集する
application.html.erb<!DOCTYPE html> <html> <head> <title>ShitsumonWa</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <#以下の行を忘れずに追加する > <%= stylesheet_pack_tag 'application' %> </head> <body> <%= yield %> </body> </html>app/views/layouts/application.html.erbに
<%= stylesheet_pack_tag 'application' %>を追加Booystarapからの乗換の際に注意事項
application.html.erb<!DOCTYPE html> <html> <head> <title>ShitsumonWa</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= yield %> </body> </html>app/views/layouts/application.html.erb
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>ここの2行のままだと、
Sprockets::FileNotFound in コントーラー名#アクション名
というエラーが出て怒られるので、修正するのを忘れずに!!!!
原因が何かわからず、3時間ぐらいググり続けました...(本来ならこの時間でviewを作成できていた
)まとめ
BULMAをRailsで利用するまでの手順は、単純です。
Rails6以降、Bootstrapが少しめんどくさくなりました。
それでも、RailsにBootstrapを導入したい人もたくさんいると思います。
以下の記事が大変参考になりました。感謝です。
Rails 6にjQueryとBootstrapを入れるRails6以降の変更点はこちらの記事が参考になりました。ありがたやです。
Ruby on Rails 6の主要な新機能・機能追加・変更点個人的には、
①フロントで時間をかけるよりもしっかりバックエンドの機能実装に時間を使いたい
②実際に現役のバックエンドエンジニアの方が、現場で使用している上記の理由でCSSフレームワークにBULMAを導入することにしました。(あと使ってて純粋に楽しい。)
アプリ開発の際に、BULMAを導入している方の参考になれば、嬉しいです。公式: >BULMA start
- 投稿日:2020-05-14T22:11:54+09:00
Railsのポリモーフィック関連付けのテストを考えてみた。
なぜ今回の記事を書いたか。
Railsの場合、ポリモーフィック関連付けを行なった際に紐づくモデルに対して実装しなければいけないメソッドの強制が行われません。そのため、メソッドの定義を忘れた場合エラーが発生します。
ただ、実装する際に忘れるメソッドのテストを書くのは難しいのでは? と個人的に思いました。
そのため、良し悪しは別として僕が実装したテストを記載します。他に良い方法があれば是非教えてください!
実装したモデル
今回の記述に関係ない箇所に関しては省略しています。
app/models/notification.rbclass Notification < ApplicationRecord belongs_to :event, polymorphic: true ・ ・ ・ endapp/models/comment.rbclass Comment < ApplicationRecord has_many :notifications, as: :event, dependent: :destroy ・ ・ ・ def notification_message() "#{self.user.name}によりコメントされました。" end end実装したテスト
ポリモーフィック関連付けに紐づくテストなので、Notificationのモデルテストに記載いたしました。
spec/models/notification_spec.rbrequire 'rails_helper' RSpec.describe Notification, type: :model do it 'Notificationと関連づいているクラスに対して必要なメソッドが定義されているか' do # 定数オブジェクトの一覧を取得。 constants = Object.constants.map do |name| Object.const_get(name) end # Notificationモデルと紐づいているモデルを取得する models = constants.select do |c| # モデルクラスを絞り込む next unless c.class == Class && c < ActiveRecord::Base && !c.abstract_class? # Notificationモデルが紐づいているか絞り込む c.reflect_on_all_associations.map(&:name).include?(:notifications) end models.each do |model| # 定義されたメソッドがあるかを確認する expect(model.method_defined?(:notification_message)).to be true end end endこの実装を行う事で、自動的にポリモーフィック関連付けされたモデルに必要なメソッドが存在しているかを確認してくれる。これでポリモーフィック関連付けを行なった際にメソッド忘れは無くなります!
懸念点
- モデルの数が多くなった時にテスト時間が増えそう
- モデルテストで、他のモデルにメソッドがあるか確認するのは良いのか?
- テストでここまで処理を書くのは良いのか?
- ポリモーフィック関連付けで関連づいたモデルに対してメソッドの強制を行う方法が他にあるのではないか?
- 継承やmoduleを使用した方が良いのではないか?
参考にしたサイト
- 投稿日:2020-05-14T21:54:20+09:00
ActiveRecordでnew => build => save! するとどうなる
関連付けのあるモデルにおいて、親レコードをnew => 子レコードをbuild => 親レコードをsave! したときの挙動が複雑な気がしたのでメモ。
(理解力が不足しているだけかもしれない。)
検証環境: ActiveRecord 6.0.2.1
結論を言葉で
- has_oneでは親レコードと子レコードのvalidityは独立している。
- has_manyでは子レコードが1つでもinvalidなら親レコードもinvalidになる。
親レコードをsave!したとき、
- 親レコードがinvalidならraiseする
- 親レコードも子レコードもvalidな場合、insertがtransactionで囲まれて実行される
- 親レコードがvalidで子レコードがinvalidな場合(上記の性質によりhas_oneでのみありうる)、親レコードのinsert処理のみが実行される
実験
サンプルは以下。
migrations:
class CreateTables < ActiveRecord::Migration[6.0] def change create_table :customers do |t| end create_table :banks do |t| end create_table :merchants do |t| end create_table :bank_accounts do |t| t.references :customer, foreign_key: true, null: false t.references :bank, foreign_key: true, null: false t.string :account_number, null: false end create_table :orders do |t| t.references :customer, foreign_key: true, null: false t.references :merchant, foreign_key: true, null: false t.integer :price, null: false end end endmodels:
class Customer < ApplicationRecord has_one :bank_account has_many :orders end class Bank < ApplicationRecord end class Merchant < ApplicationRecord end class BankAccount < ApplicationRecord belongs_to :customer validates :account_number, presence: true end class Order < ApplicationRecord belongs_to :customer validates :price, presence: true end実験するケースは以下。
- 1 has_one
- 1-1 invalid
- 1-2 valid & insertable
- 1-3 valid & uninsertable
- 2 has_many
- 2-1 all invalid
- 2-2 all valid & insertable
- 2-3 all valid & uninsertable
- 2-4 valid&savable + valid & uninsertable
- 2-5 invalid + valid & uninsertable
- 2-6 invalid + valid & insertable
1-1 invalid
irb(main):118:0> customer = Customer.new irb(main):119:0> customer.build_bank_account => #<BankAccount id: nil, customer_id: nil, bank_id: nil, account_number: nil, created_at: nil, updated_at: nil> irb(main):120:0> customer.valid? => true irb(main):121:0> customer.bank_account.valid? => false irb(main):122:0> customer.bank_account.errors.full_messages => ["Account number can't be blank"] irb(main):123:0> customer.save! => true irb(main):125:0> customer.persisted? => true irb(main):126:0> customer.bank_account.persisted? => false1-2 valid & insertable
irb(main):156:0> customer = Customer.new irb(main):157:0> customer.build_bank_account(account_number: "1234", bank_id: 1) => #<BankAccount id: nil, customer_id: nil, bank_id: 1, account_number: "1234", created_at: nil, updated_at: nil> irb(main):158:0> customer.valid? => true irb(main):159:0> customer.bank_account.valid? => true irb(main):160:0> customer.save! => true irb(main):161:0> customer.persisted? => true irb(main):162:0> customer.bank_account.persisted? => true1-3 valid & uninsertable
irb(main):138:0> customer = Customer.new irb(main):139:0> customer.build_bank_account(account_number: "1234") => #<BankAccount id: nil, customer_id: nil, bank_id: nil, account_number: "1234", created_at: nil, updated_at: nil> irb(main):140:0> customer.valid? => true irb(main):141:0> customer.bank_account.valid? => true irb(main):142:0> customer.save! ActiveRecord::NotNullViolation (Mysql2::Error: Field 'bank_id' doesn't have a default value) irb(main):143:0> customer.persisted? => false irb(main):144:0> customer.bank_account.persisted? => false2-1 all invalid
irb(main):176:0> customer = Customer.new irb(main):177:0> customer.orders.build => #<Order id: nil, customer_id: nil, bank_id: nil, price: nil, created_at: nil, updated_at: nil> irb(main):178:0> customer.orders.build => #<Order id: nil, customer_id: nil, bank_id: nil, price: nil, created_at: nil, updated_at: nil> irb(main):187:0> customer.valid? => false irb(main):188:0> customer.errors.full_messages => ["Orders is invalid"] irb(main):191:0> customer.orders.map {|order| order.valid? } => [false, false] irb(main):193:0> customer.orders.map {|order| order.errors.full_messages } => [["Price can't be blank"], ["Price can't be blank"]] irb(main):196:0> customer.save! ActiveRecord::RecordInvalid (Validation failed: Orders is invalid)2-2 all valid & insertable
irb(main):001:0> customer = Customer.new irb(main):002:0> customer.orders.build(price: 100, merchant_id: 1) => #<Order id: nil, customer_id: nil, merchant_id: 1, price: 100, created_at: nil, updated_at: nil> irb(main):004:0> customer.orders.build(price: 200, merchant_id: 2) => #<Order id: nil, customer_id: nil, merchant_id: 2, price: 200, created_at: nil, updated_at: nil> irb(main):005:0> customer.valid? => true irb(main):006:0> customer.orders.map {|order| order.valid? } => [true, true] irb(main):007:0> customer.save! => true irb(main):008:0> customer.persisted? => true irb(main):009:0> customer.orders.map {|order| order.persisted? } => [true, true]2-3 all valid & uninsertable
irb(main):016:0> customer = Customer.new irb(main):017:0> customer.orders.build(price: 100) => #<Order id: nil, customer_id: nil, merchant_id: nil, price: 100, created_at: nil, updated_at: nil> irb(main):018:0> customer.orders.build(price: 200) => #<Order id: nil, customer_id: nil, merchant_id: nil, price: 200, created_at: nil, updated_at: nil> irb(main):019:0> customer.valid? => true irb(main):020:0> customer.orders.map {|order| order.valid? } => [true, true] irb(main):021:0> customer.save! ActiveRecord::NotNullViolation (Mysql2::Error: Field 'merchant_id' doesn't have a default value) irb(main):024:0> customer.persisted? => false irb(main):025:0> customer.orders.map {|order| order.persisted? } => [false, false]2-4 valid&savable + valid & uninsertable
irb(main):035:0> customer = Customer.new irb(main):036:0> customer.orders.build(price: 100, merchant_id: 1) => #<Order id: nil, customer_id: nil, merchant_id: 1, price: 100, created_at: nil, updated_at: nil> irb(main):037:0> customer.orders.build(price: 200) => #<Order id: nil, customer_id: nil, merchant_id: nil, price: 200, created_at: nil, updated_at: nil> irb(main):038:0> customer.valid? => true irb(main):039:0> customer.orders.map {|order| order.valid? } => [true, true] irb(main):040:0> customer.save! ActiveRecord::NotNullViolation (Mysql2::Error: Field 'merchant_id' doesn't have a default value) irb(main):041:0> customer.persisted? => false irb(main):042:0> customer.orders.map {|order| order.persisted? } => [false, false]2-5 invalid + valid & uninsertable
irb(main):044:0> customer = Customer.new irb(main):045:0> customer.orders.build => #<Order id: nil, customer_id: nil, merchant_id: nil, price: nil, created_at: nil, updated_at: nil> irb(main):046:0> customer.orders.build(price: 100) => #<Order id: nil, customer_id: nil, merchant_id: nil, price: 100, created_at: nil, updated_at: nil> irb(main):047:0> customer.valid? => false irb(main):048:0> customer.errors.full_messages => ["Orders is invalid"] irb(main):049:0> customer.orders.map {|order| order.valid? } => [false, true] irb(main):050:0> customer.orders.map {|order| order.errors.full_messages } => [["Price can't be blank"], []] irb(main):051:0> customer.save! ActiveRecord::RecordInvalid (Validation failed: Orders is invalid)2-6 invalid + valid & insertable
irb(main):059:0> customer = Customer.new irb(main):060:0> customer.orders.build => #<Order id: nil, customer_id: nil, merchant_id: nil, price: nil, created_at: nil, updated_at: nil> irb(main):061:0> customer.orders.build(price: 100, merchant_id: 1) => #<Order id: nil, customer_id: nil, merchant_id: 1, price: 100, created_at: nil, updated_at: nil> irb(main):062:0> customer.valid? => false irb(main):063:0> customer.errors.full_messages => ["Orders is invalid"] irb(main):064:0> customer.orders.map {|order| order.valid? } => [false, true] irb(main):065:0> customer.orders.map {|order| order.errors.full_messages } => [["Price can't be blank"], []] irb(main):066:0> customer.save! ActiveRecord::RecordInvalid (Validation failed: Orders is invalid)
- 投稿日:2020-05-14T20:58:08+09:00
【FactoryBot】外部参照キーのカラムデータの作成方法
【FactoryBot】外部参照キーのカラムデータの作成方法
FactoryBotで外部参照キーとなるカラムデータを作成する方法を調べたので内容をまとめます.
例えばPostモデルでファクトリデータを作成する際に一緒に投稿者となるUserのファクトリデータを作成するような状況です。目次
状況
Messageモデルのテストを行うためにConversationモデルとUserモデルのデータが必要です。
下記が今回のER図です。Messageモデルは
belongs_to :user, belongs_to :conversation
というアソシエーションを持ちます。すなわちMessageモデルのFactoryBotを実行した際にConversationモデルとUserモデルのデータを生成する必要があります。
message.rbclass Message < ApplicationRecord belongs_to :conversation belongs_to :user enduser.rbclass User < ApplicationRecord has_many :senders, class_name: 'Conversation', foreign_key: :sender_id has_many :recipients, class_name: 'Conversation', foreign_key: :recipient_id has_many :messages, dependent: :destroy endconversation.rbclass Conversation < ApplicationRecord belongs_to :sender, class_name: 'User', foreign_key: :sender_id belongs_to :recipient, class_name: 'User', foreign_key: :recipient_id has_many :messages, dependent: :destroy end
動作環境
OS : macOS Mojave 10.14.6
ruby : 2.6.3p62
rails : 5.2.4
factory_bot_rails : 5.2.0
手順
イメージとして以下の流れを実現するようにします。
STEP1. UserのFactoryBot呼び出し
STEP2. ConversationのFactoryBot呼び出し
STEP3. MessageのFactoryBot呼び出し
STEP 1. UserモデルのFactoryBotを定義
users.rbFactoryBot.define do #名前が重複しないようにシーケンスを利用 factory :user do sequence :name do |n| "user_#{n}" end end endSTEP 2. ConversationモデルのFactoryBotを定義
associationを記載することでUserのFactoryBotが呼び出されsenderに代入されます.
ポイントassociation(senderやrecipient)
とuser
は異なる名前のためfactory: :user
でどのfactoryを利用するのか明示していますconversations.rbFactoryBot.define do factory :conversation do association :sender, factory: :user association :recipient, factory: :user end end実際にConversaionモデルのFactoryBotを動かしてみました。
conversation.senderとconversation.recipientにFactoryBot :userで作成されたデータが入っています。STEP 3. MessageモデルのFactoryBotを定義
messages.rbFactoryBot.define do factory :message do sequence :body do |n| "message_#{n}" end #アソシエーション名とファクトリー名が異なる場合はconv factory: :conversationのように #記述.今回はアソシエーション名とファクトリ名が同じためconversationのみで問題ない. conversation #conversationファクトリで作成したuserデータをmessage.userに代入 user { conversation.sender } end end結果
message_spec.rbrequire 'rails_helper' RSpec.describe Message, type: :model do it 'バリデーションが通る' do message = build(:message, body:'test message' ) binding.pry expect(message).to be_valid end endmessage内を確認するとuserとconversationにデータが入っており、
テストをPassしている。当初つまづいたところ
当初はSTEP 3. FactoryBot:messageのコードでblankエラーが発生していた
原因
そこでMessagemモデルのファイルを確認すると
user_id
とconversation_id
にnullチェックがあったためBlankエラーになっていた。つまりこのようなバリデーションがある場合はcreateメソッドを利用する必要があります。
messages.rbFactoryBot.define do factory :message do sequence :body do |n| "message_#{n}" end after(:build) do |message| message.conversation = create(:conversation) message.user = message.conversation.sender end end endインスタンスが保存されるためidが表示される. その結果,nullチェックのバリデーションもPassできる.
おわりに
今回の件で以下のことを学びました。
1. associationメソッドを利用することで外部参照キーのデータを作成できる
2. associationメソッドはbuildのため、idのnullチェックがある場合はcreateを利用する
- 投稿日:2020-05-14T18:10:48+09:00
Rails モデル作成時 ファイルアップロードエラー
問題
Sample.create({name: "hoge", icon: File::open('db/dummy-001.png')}) rails aborted! Errno::EXDEV: Invalid cross-device link @ rb_file_s_link - (db/dummy-001.png, /tmp/7408f7ac8f26bae8efad1f94360df7e420200514-12545-30ex00.png) /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/io_adapters/abstract_adapter.rb:62:in `link_or_copy_file' /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/io_adapters/abstract_adapter.rb:55:in `copy_to_tempfile' /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/io_adapters/file_adapter.rb:21:in `cache_current_values' /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/io_adapters/file_adapter.rb:11:in `initialize' /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/io_adapters/registry.rb:33:in `new' /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/io_adapters/registry.rb:33:in `for' /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/attachment.rb:100:in `assign' /work/vendor/bundle/ruby/2.4.0/gems/paperclip-6.1.0/lib/paperclip/has_attached_file.rb:66:in `block in define_setter' /work/vendor/bundle/ruby/2.4.0/gems/activemodel-5.2.1/lib/active_model/attribute_assignment.rb:51:in `public_send' /work/vendor/bundle/ruby/2.4.0/gems/activemodel-5.2.1/lib/active_model/attribute_assignment.rb:51:in `_assign_attribute' /work/vendor/bundle/ruby/2.4.0/gems/activemodel-5.2.1/lib/active_model/attribute_assignment.rb:44:in `block in _assign_attributes' /work/vendor/bundle/ruby/2.4.0/gems/activemodel-5.2.1/lib/active_model/attribute_assignment.rb:43:in `each' /work/vendor/bundle/ruby/2.4.0/gems/activemodel-5.2.1/lib/active_model/attribute_assignment.rb:43:in `_assign_attributes' /work/vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.1/lib/active_record/attribute_assignment.rb:23:in `_assign_attributes' /work/vendor/bundle/ruby/2.4.0/gems/activemodel-5.2.1/lib/active_model/attribute_assignment.rb:35:in `assign_attributes' /work/vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.1/lib/active_record/core.rb:314:in `initialize' /work/vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.1/lib/active_record/inheritance.rb:66:in `new' /work/vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.1/lib/active_record/inheritance.rb:66:in `new' /work/vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.1/lib/active_record/persistence.rb:35:in `create' /work/db/seeds.rb:41:in `block in <main>' /work/db/seeds.rb:41:in `map' /work/db/seeds.rb:41:in `<main>' /work/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load' /work/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load' /work/vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `block in load' /work/vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:253:in `load_dependency' /work/vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `load' /work/vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/engine.rb:551:in `load_seed' /work/vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.1/lib/active_record/tasks/database_tasks.rb:281:in `load_seed' /work/vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.1/lib/active_record/railties/databases.rake:194:in `block (2 levels) in <main>' /work/vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/commands/rake/rake_command.rb:23:in `block in perform' /work/vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/commands/rake/rake_command.rb:20:in `perform' /work/vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/command.rb:48:in `invoke' /work/vendor/bundle/ruby/2.4.0/gems/railties-5.2.1/lib/rails/commands.rb:18:in `<main>' /work/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require' /work/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `block in require_with_bootsnap_lfi' /work/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/loaded_features_index.rb:65:in `register' /work/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:20:in `require_with_bootsnap_lfi' /work/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:29:in `require' /work/vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `block in require' /work/vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:253:in `load_dependency' /work/vendor/bundle/ruby/2.4.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `require' bin/rails:4:in `<top (required)>' /usr/local/bundle/bin/bundle:23:in `load' /usr/local/bundle/bin/bundle:23:in `<main>' Tasks: TOP => db:seed (See full trace by running task with --trace) make: *** [seed] Error 1解決
vendor
配下が何らかの理由で壊れていたようです...。$ rm -rf vendor/ $ bundle install --path vendor/bundle
- 投稿日:2020-05-14T17:51:08+09:00
[Ruby]GraphQLで認証を実装する
はじめに
最近、graphql-rubyを勉強中です。認証周りを実装してみたので忘備録です。
ただ、この方法が正解かどうかは模索中です。やりたいこと
あるmutationなりqueryはログインユーザしか発行できないけど、ユーザに関係ないquery等はログインしてなくても発行できるようにしたい。でも全てのqueryとかでログインしてるかどうかのコードを追加するのはめんどくさい。
まずは通常の認証
graphql_controller.rbのexcute内でuserの認証を行い、contextにcurrent_userを保持させます。
graphql_controller.rbdef excute current_user = User.auth_user(params) context: { current_user: current_user } # その後の処理↓ ... end上の例だと、auth_userメソッドで認証して、userオブジェクトをもらっています。
contextに引き渡すと、resolver内等でcontext[:current_user]で取得できます。ひと工夫
さて、この場合だと、resolver内でいちいちcontext[:current_user]が存在するかどうかを確認しないといけません。かといって、excute内で弾くと全てのqueryがログインしていないと発行できません。
そこで、base_query.rbに以下のような処理を書いてやることにしました。base_query.rbclass BaseQuery < GraphQL::Schema::Resolver def current_user context[:current_user].presence || raise(GraphQL::ExecutionError, 'Permissions deny, Please login again') end endqueryはbase_query.rbを継承していますので、current_userを使うときにこのメソッドを呼び出してやれば良いですね。
もう少し細かくやる場合
今回はuserと紐づくかどうかで、判断しましたがもう少し細かく設定する場合には返すObjectタイプごとに指定できるようです。こっちも試してみたら記事を書こうと思います。
https://graphql-ruby.org/authorization/authorization.html
他にもっと良い方法あるよって方は教えてもらえると幸いです。
- 投稿日:2020-05-14T17:32:46+09:00
【簡潔】Brakeman導入方法-Rails-
Brakeman導入方法
Brakemanとは
Brakemanとはアプリケーションを開発した際にセキュリティ検査ができるgemの一つです。
Railsでは元からセキュリティについては比較的維持できるものですが、私が開発した際に補助的にこのようなツールを使ってみました。ちなみにBrakemanは静的なツールです。導入方法
作成中のプロジェクトの
Gemfile
内のgroup :development do
とend
の間にgem 'brakeman', require: false
を追記します。group :development do gem 'brakeman', require: false end次にターミナルの作成中アプリのディレクトリで
brakeman
と叩けばBrakemanでのセキュリティチェックが始まります。% brakeman出力はこんな感じです。
== Brakeman Report == Application Path: /Users/UserName/FolderName/AppName Rails Version: 5.2.4.2 Brakeman Version: 4.8.2 Scan Date: 0000-00-00 00:00:0 +0000 Duration: 0.290167 seconds Checks Run: BasicAuth, BasicAuthTimingAttack, ContentTag, CookieSerialization, CreateWith, CrossSiteScripting, DefaultRoutes, Deserialize, DetailedExceptions, DigestDoS, DynamicFinders, EscapeFunction, Evaluation, Execute, FileAccess, FileDisclosure, FilterSkipping, ForgerySetting, HeaderDoS, I18nXSS, JRubyXML, JSONEncoding, JSONEntityEscape, JSONParsing, LinkTo, LinkToHref, MailTo, MassAssignment, MimeTypeDoS, ModelAttrAccessible, ModelAttributes, ModelSerialize, NestedAttributes, NestedAttributesBypass, NumberToCurrency, PageCachingCVE, PermitAttributes, QuoteTableName, Redirect, RegexDoS, Render, RenderDoS, RenderInline, ResponseSplitting, RouteDoS, SQL, SQLCVEs, SSLVerify, SafeBufferManipulation, SanitizeMethods, SelectTag, SelectVulnerability, Send, SendFile, SessionManipulation, SessionSettings, SimpleFormat, SingleQuotes, SkipBeforeFilter, SprocketsPathTraversal, StripTags, SymbolDoSCVE, TranslateBug, UnsafeReflection, ValidationRegex, WithoutProtection, XMLDoS, YAMLParsing == Overview == Controllers: 2 Models: 3 Templates: 34 Errors: 0 Security Warnings: 0 == Warning Types == No warnings foundセキュリティチェック完了です。
この記事を読んでいただきありがとうございました。
- 投稿日:2020-05-14T16:38:03+09:00
【rails】active_hash セレクトボックス作り方
目的
active_hashを使うと
セレクトボックスの選択肢を作る上で…
DBを使わずに、使うことができる。
準備
1.Gemfileに以下を記述する。
gem 'active_hash'下記と合わせてサーバの再起動も行う
$ bundle install準備はこれだけ
実装例
itemsテーブルにregion_idカラムを用意して、itemの登録の時にregion(発送元の地域)をアクティブハッシュから選択する。(下のgifのようなイメージ)
1.早速下記コマンドでitemsテーブルにregion_idカラムを用意
コンソールrails g model Item region_id:integer name:string rake db:migrate2.Active_Hash::Baseを継承しているモデルを作成する。(app/modelsの中にファイル作成)
app/models/region.rbclass Region < ActiveHash::Base include ActiveHash::Associations has_many :items self.data = [ {id: 1, name: '北海道'}, {id: 2, name: '青森県'}, {id: 3, name: '岩手県'}, {id: 4, name: '宮城県'}, {id: 5, name: '秋田県'}, {id: 6, name: '山形県'}, {id: 7, name: '福島県'}, {id: 8, name: '茨城県'}, {id: 9, name: '栃木県'}, {id: 10, name: '群馬県'}, {id: 11, name: '埼玉県'}, {id: 12, name: '千葉県'}, {id: 13, name: '東京都'}, {id: 14, name: '神奈川県'}, {id: 15, name: '新潟県'}, {id: 16, name: '富山県'}, {id: 17, name: '石川県'}, {id: 18, name: '福井県'}, {id: 19, name: '山梨県'}, {id: 20, name: '長野県'}, {id: 21, name: '岐阜県'}, {id: 22, name: '静岡県'}, {id: 23, name: '愛知県'}, {id: 24, name: '三重県'}, {id: 25, name: '滋賀県'}, {id: 26, name: '京都府'}, {id: 27, name: '大阪府'}, {id: 28, name: '兵庫県'}, {id: 29, name: '奈良県'}, {id: 30, name: '和歌山県'}, {id: 31, name: '鳥取県'}, {id: 32, name: '島根県'}, {id: 33, name: '岡山県'}, {id: 34, name: '広島県'}, {id: 35, name: '山口県'}, {id: 36, name: '徳島県'}, {id: 37, name: '香川県'}, {id: 38, name: '愛媛県'}, {id: 39, name: '高知県'}, {id: 40, name: '福岡県'}, {id: 41, name: '佐賀県'}, {id: 42, name: '長崎県'}, {id: 43, name: '熊本県'}, {id: 44, name: '大分県'}, {id: 45, name: '宮崎県'}, {id: 46, name: '鹿児島県'}, {id: 47, name: '沖縄県'} ] end3.Itemモデルに記述
item.rbclass Item < ApplicationRecord # アクティブハッシュ extend ActiveHash::Associations::ActiveRecordExtensions belongs_to_active_hash :region end4.フォーム例(ビュー作成)
_form.html.haml= form_for @item do |f| .new-wrapper__main__title.spacing 発送元の地域 %span.require 必須 = f.collection_select :region_id, Region.all, :id, :name,{prompt: "選択してください"}, {class: "new-wrapper__main__input-select"}参考文献
https://qiita.com/YJ2222/items/c2eaada06c99c051e151
https://qiita.com/Toman1223/items/8633142312bfa886d50b
- 投稿日:2020-05-14T16:22:03+09:00
railsでsidekiq-schedulerを動かす
公式のUsageはrailsで動かす前提じゃなかったので、railsで動かす場合のUsage的な何か。
- 0. ばーじょん
Gemfile.lockrails (6.0.1) sidekiq (6.0.7) sidekiq-scheduler (3.0.1)
- 1. Gemfile書く
Gemfilegem 'sidekiq-scheduler' gem 'sinatra', require: false # ダッシュボードいらない場合はいらない
- 2. 初期設定を書く
herokuで動かす場合は環境変数に設定しておくこともできるので、その場合はなくても構わない、多分。
config/initializers/sidekiq.rbSidekiq.configure_server do |config| config.redis = { url: ENV.fetch('REDIS_URL') { 'redis://localhost:6379' } } end Sidekiq.configure_client do |config| config.redis = { url: ENV.fetch('REDIS_URL') { 'redis://localhost:6379' } } end
- 3. ジョブを作る
app/jobs/
内に作る。ApplicationJob
を継承していれば自動的にsidekiq-scheduler
がジョブとして認識してくれる。ここ参照app/jobs/hello_world_job.rbclass HelloWorldJob < ApplicationJob def perform puts 'test' end end
- 4. ジョブを登録する
こっちの拡張子は
yml
ですよ。config/sidekiq.yml:schedule: hello_world: every: '1m' class: HelloWorldJob
- 5. ダッシュボード見れるようにする
config/routes.rbrequire 'sidekiq/web' require 'sidekiq-scheduler/web' Rails.application.routes.draw do mount Sidekiq::Web, at: "/sidekiq" endこれで http://localhost:3000/sidekiq のURLでsidekiqのダッシュボードが見れるようになる。
- 6. sidekiq動かす
rails s
とは別プロセスで動かす必要がある。$ bundle exec sidekiqこんな感じで見れたら成功。
- 投稿日:2020-05-14T15:48:11+09:00
【Rails】後からカラムを追加して外部キーを張る際に、add_referenceを使う場合の注意点。
始めに
忘れがちになるため、備忘録。
環境
Ruby 2.6.5
Rails 5.2.4.2アソシエーション対象
投稿用のpostsテーブルと、ユーザーのusersテーブルを関連付け。
postsテーブルに外部キー制約を張ったuser_idカラムを追加するカラム追加用のマイグレーションを作成
1.マイグレーション作成
$ rails g migration AddUserIdToPosts2.マイグレーションファイルへの記述。foreign_key: trueを忘れずに記述すること。
db/migrate/[timestamps]_add_user_id_to_posts.rbclass AddUserIdToPosts < ActiveRecord::Migration[5.2] def change add_reference :posts, :user, foreign_key: true end end※上手く外部キー制約が追加されない記述。
db/migrate/[timestamps]_add_user_id_to_posts.rbclass AddUserIdToPosts < ActiveRecord::Migration[5.2] def change add_reference :posts, :user, index: true end end3.マイグレーションファイルの保存
$ rails db:migrate
何故、foreign_keyが必要なのか
Ruby on Rails APIより、
add_reference(table_name, ref_name, **options)
:foreign_key
Add an appropriate foreign key constraint. Defaults to false.Create a supplier_id column and appropriate foreign key
add_reference(:products, :supplier, foreign_key: true)外部キー制約を張るためのforeign_keyはデフォルトではfalseになるため、ただ単にadd_referenceカラムを追加しただけでは外部キー制約は張られることがないとのこと。
つまり、「user.posts」のようにアソシエーションを利用してオブジェクトを取得しようとしても、外部キーが存在しないためエラーになるので注意が必要。
参考
Railsで外部キー制約のついたカラムを作る時のmigrationの書き方
後からreferencesカラムを追加しようとするとdb:migrateできない
- 投稿日:2020-05-14T15:48:11+09:00
【Rails】add_referenceで、カラムへ後から外部キー制約を追加する場合の注意点。
始めに
忘れがちになるため、備忘録。
アソシエーション対象
投稿用のpostsテーブルと、ユーザーのusersテーブルを関連付け。
postsテーブルに外部キー制約を張ったuser_idカラムを追加するカラム追加用のマイグレーションを作成
1.マイグレーション作成
$ rails g migration AddUserIdToPosts2.マイグレーションファイルへの記述
db/migrate/[timestamps]_add_user_id_to_posts.rbclass AddUserIdToPosts < ActiveRecord::Migration[5.2] def change add_reference :posts, :user, foreign_key: true end end※上手く外部キー制約が追加されない記述。
db/migrate/[timestamps]_add_user_id_to_posts.rbclass AddUserIdToPosts < ActiveRecord::Migration[5.2] def change add_reference :posts, :user, index: true end end3.マイグレーションファイルの保存
$ rails db:migrate
何故、foreign_keyが必要なのか
Ruby on Rails APIより、
add_reference(table_name, ref_name, **options)
:foreign_key
Add an appropriate foreign key constraint. Defaults to false.Create a supplier_id column and appropriate foreign key
add_reference(:products, :supplier, foreign_key: true)外部キー制約を張るためのforeign_keyはデフォルトではfalseになるため、ただ単にadd_referenceカラムを追加しただけでは外部キー制約は張られることがないとのこと。
つまり、「user.posts」のようにアソシエーションを利用してオブジェクトを取得しようとしても、外部キーが存在しないためエラーになるので注意が必要。
参考
Railsで外部キー制約のついたカラムを作る時のmigrationの書き方
後からreferencesカラムを追加しようとするとdb:migrateできない
- 投稿日:2020-05-14T15:37:51+09:00
サジェスト機能を実装
サジェスト機能を実装
機能を追加したので学んだことをoutputしておく。
理解が深まればもう少しわかりやすく改善していく予定。実装内容
以下の写真のように文字を入力すると候補が表示されるようにしたい。
まず
gem 'jquery-ui-rails'
をGemfileに記載し、
bundle install
を実行
jQuery UIは、jQueryを拡張するライブラリ(プラグイン)の一種
これにより「autocomplete」が使えるようになり、候補がリストで表示される。早速、
search.rb
を作成し、以下のように配置する。(今回は新たに作成したが既存のものに書いても良い。)
軽く説明
1行目はjQuery記載のためのテンプレ
9行目で以下のようなhtmlファイルに記載されたid="suggest"のiuputにユーザーが入力した時に「autocomplete」が反応する。<input type="text" id="suggest">
source:data
によりデータの元を定義する
今回であればl2~l9で定義されたdataを呼び出しユーザーが入力した値に対して該当する値が検出される。
autoFocus:true
このページの最初の写真のように、「a」を入力するとリストが表示されて、「apple」の部分背景がgrayになっている。trueをfalseに変えると背景はgrayにならない。
delay:300
入力してから0.3秒後に表示される。
minLength:1
1文字以上入力された時に実行される。参考サイト
- 投稿日:2020-05-14T15:04:55+09:00
Railsプロジェクトの *_spec.rb の中で xmpfilter を使う方法
要点
rspec/autorun
を読み込むことで rspec コマンドで実行したのと同じ状況にするrequire "rails_helper"
などが失敗しないように-I spec
を指定- カレントディレクトリを Rails.root に固定するため
--cd
に .git のあるディレクトリを指定例
(defun rubyxmp () (interactive) (let ((s "xmpfilter --dev --fork --no-warnings --poetry") (rspec-p (string-match "_spec" (buffer-file-name))) (git-root (locate-dominating-file default-directory ".git")) xmpfilter-command-name) (when rspec-p (setq s (concat s " -r rspec/autorun -I spec")) (when git-root (setq s (concat s " --cd " git-root)))) (setq xmpfilter-command-name s) (call-interactively 'xmp)))
- 投稿日:2020-05-14T14:56:16+09:00
[Rails]form_for/form_withの書き方(haml含む)
はじめに
自分が初めてform_with/form_forの文法を学んだ時に「???」となったので、
初学者にも分かるような説明記載と自分の備忘録として記載します。まず、はじめに「form_for」はRails5.1で非推奨となっており、将来的にform_withに置き換えられる予定です。
「Rails5.1以上」と「form_with」の使用が推奨とされております。(2020/5/14現在)「form_for」と「form_with」
1.form_for
- Railsでフォームタグを簡単に作成するためのメソッド
- モデルに基づくformに使用される
form_forは以下のように書きます。
<%= form_for モデル do |form| %> <%# フォームの部品 %> <% end %>具体的なコードで書くと、
※モデルはUserとする<%= form_for @user do |form| %> <%= form.text_field :email %> <%= form.submit %> <% end %>Rails特有のhamlで書くと、
= form_for @user do |f| = f.text_field :email = f.submit2.form_with
- 自動でパスを選択してくれ、HTTPメソッド(getやpostなど)を指定する必要が無い
- コントローラから渡された、ActiveRecordを継承するモデルのインスタンスが利用できる
- モデルに基づかないフォームも生成できる
form_withは以下のように書きます。
<%= form_with model:'モデル名' local: true do |form| %> <%# フォームの部品 %> <% end %>上記の説明で取り上げた通り、form_withは、モデルに基づかないファームも生成できます。
具体的なコードで書くと、
※モデルはUserとする#関連するモデルがない場合 → urlの指定のみで、modelの記述がない <%= form_with url: users_path do |form| %> <%= form.text_field :email %> <%= form.submit %> <% end %>#関連するモデルがある場合 → modelの記述のみで、urlの指定は不要 <%= form_with model: @user do |form| %> <%= form.text_field :email %> <%= form.submit %> <% end %>Rails特有のhamlで書くと、
#関連するモデルがない場合 → urlの指定のみで、modelの記述がない = form_with url: users_path do |f| = f.text_field :email = form.submit#関連するモデルがある場合 → modelの記述のみで、urlの指定は不要 = form_with model: @user do |f| = form.text_field :email = form.submit「form_for」と「form_with」の違い
1.localは、記載の必要あり。
2.remote(Ajax)は、基本的にtrue設定で記載の必要なし。
3.form_withではinputタグは用いない
上記の1と2に関しましては、以下の記事が参考になりますので、興味がございましたらご参照下さい。
https://qiita.com/Tatsu88/items/8ea9b944681a48cce589#comment-85443e109252b344989b最後に
これからは form_with を使うことを強くお勧めします。
もしこの記事がみなさんの役に立てば幸いです。以上となります。最後までご覧いただき、ありがとうございました!
記述に何か誤りなどございましたら、お手数ですが、ご連絡いただけますと幸いです。参考記事
【Rails 5】(新) form_with と (旧) form_tag, form_for の違い
form_for/form_tag/form_withについて【概要+使い方】
【Rails入門説明書】form_withについて解説
- 投稿日:2020-05-14T14:30:15+09:00
【jQuery】【rails】使えない!? 動かない
解消したいこと
jQueryが上手く動かない
しかしリロードをすれば、動くようになる
なぜ??原因
結論、Turbolinksが邪魔している
デフォルトgemであるturbolinks(ajaxとajaxとhistoryAPIを使ってページ遷移しやすくするもの)が動作しているため、そもそも$(document).ready()が動かない
つまりページが変わっているように見えて、上記のことが起きておりjqueryは起動しない。Turbolinksとは
ページ上のリンクをクリックした時に、ページ全体をリロードさせるのではなく、bodyタグの中身とheadの中のtitleを同一ページ上で書き換える方法。
.jsとか.cssとか処理し直さないので、ページの読み込みがかなり早くなる。使い方
gemfileに以下を記述
gem 'jquery-turbolinks'bundel install と サーバ再起動を行う
解消例
該当のjqueryをこの記述に入れることで解消された。
$(document).on('turbolinks:load',(function(){ }))参考文献
https://workabroad.jp/posts/1133
https://qiita.com/hiroyayamamo/items/b258acbaa089d9482c8a
- 投稿日:2020-05-14T13:53:45+09:00
Railsをやって、最近まなんだ事をまとめてみる
はじめに
自分が最近Railsで、制作していて忘れそうなことをメモ感覚で残したものなので、
頭おかしいこと書いてあるかもしれないのでご了承ください。STI(単一テーブル継承)
1つのテーブルを継承したクラスを作成することができる。
継承することによって、子クラスは親クラスと同じカラムを使うことができるので、似たようなクラスを作って増やしたいときに、カラムの定義を1つずつ行わなくてよくなるので、手間も少なくなるので便利です。カラムを追加することもできます。※ 親クラスには、
string型
のtype
をカラムとして追加することを忘れないように、気を付けましょう!モデルの関連付けについて
1対1とか、1対多などの関係定義などを行うときに、いろんなファイル内に設定を書き込まないといけないので、結構大変です。
簡単に、こんな時にこう書く的なのをまとめます。
- 1対1のつながり(従属)
belongs_to
を従属するモデルに書く- 1対1のつながり(含む、所有など)
has_one
を所有するモデルに書く- 1対多のつながり
has_many
を所有するモデルに書く- 従属する(マイグレーションファイル)
references :従属されるモデル(単数形)
を従属する側のモデルのマイグレーションファイルに書く大体こんな感じだと思います。
Railsガイド- Active Recordの関連付けバリデーションなど
よく使うやつと個人的に思うものをまとめます。
バリデーションで、データを確認することにより、保存したいデータのみを保存することができる。
presence
- 指定した値が空かどうかを確認
format
- 正規表現などを利用して、データの内容(文字など)を検証する
length
- 長さを検証
maximum
: 最大値を指定minimum
: 最小値を指定in
: 値の範囲を指定できる(以上、以下の指定ができる)is
: 等しくないといけないように指定できる最後に
Railsで開発するときに、すごい悩まされたのがデータベース設計でした。今までは、本に書いてあることをそのまま作ればよいだけでしたが、自分で開発する際には、データベースのカラムに何が必要か、どんな制約を付けるか。などとても悩む部分が多かったです。でも、いろんなことにチャレンジすることはいつか自分のためになると思うので、皆さんも頑張ってください!!
- 投稿日:2020-05-14T11:14:29+09:00
Railsでgoogleapisを使い、gmailでメール送信
事前準備
https://developers.google.com/gmail/api/quickstart/ruby
とりあえずこの通りにして、ラベルが表示されるまでやってみるスコープの変更
サンプルコードだとreadonlyだけなので、メール送信出来るようにスコープを変える
SCOPE = Google::Apis::GmailV1::AUTH_GMAIL_READONLYSCOPE = [ Google::Apis::GmailV1::AUTH_GMAIL_READONLY, Google::Apis::GmailV1::AUTH_GMAIL_SEND ]* ただメールを送るだけなら
AUTH_GMAIL_READONLY
は要らないが、後に説明するプロファイルの参照時に必要既に出来上がっているtoken.ymlはスコープが最初のままなので、このファイルを消す
メールの作成
メールは素のテキストで書くか、何らかのライブラリを使って組み立てるかする。
APIとしてはBase64でエンコードしたものを渡す必要があるが、google/apis/gmail_v1
で勝手にやってくれるので、素のテキストのままで渡せば問題ない。# Railsに標準で入っている[mail gem][5] mail = Mail.new do from "from@domain.local" # credentialsを取ったgmailのアカウントから送るなら、多分必要ない get_user_profile("me") で確定するっぽい to "to@domain.local" subject "test" body "body" end message_object = Google::Apis::GmailV1::Message.new message_object.raw = mail.to_s GoogleApi.gmail_service.send_user_message("me", message_object)自分のメールアドレスを動的に取る
fromアドレスをハードコードしたくない場合
response = GoogleApi.gmail_service.get_user_profile("me") data = { "from" => response.email_address } Rails.root.join("tmp/gmail_profile.yml").write YAML.dump(data)
- 投稿日:2020-05-14T10:59:13+09:00
Chartkickで入力していない日付の表示をしたくない
- 投稿日:2020-05-14T10:54:52+09:00
【brew】bundle installでmysql2エラーが出てしまった時の解決策
homebrewを使ってrailsをAPIモードで使用する、 bundle install で以下のエラーが出てしまいました。
console.user-no-MacBook-Pro:app user$ bundle install An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling.解決策
そういえば、brewでmysqlを使うのが初めてだったので、まずbrewでmysqlをインストールが必要でした。
console.user-no-MacBook-Pro:app user$ brew install mysql ... To have launchd start mysql now and restart at login: brew services start mysql Or, if you don't want/need a background service you can just run: mysql.server startmysql.server.startが出力されたらmysqlインストールに成功しています。
console.user-no-MacBook-Pro:app user$ brew info mysql mysql: stable 8.0.19 (bottled)ここで、bundle install 再実行
console.user-no-MacBook-Pro:app user$ bundle install Installing mysql2 0.5.3 with native extensions Using puma 4.3.3 Fetching rack-cors 1.1.1 Installing rack-cors 1.1.1 Using thor 1.0.1 Using railties 6.0.3 Using sprockets 4.0.0 Using sprockets-rails 3.2.1 Using rails 6.0.3 Using spring 2.1.0 Using spring-watcher-listen 2.0.1 Bundle complete! 10 Gemfile dependencies, 55 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed.先ほどまで、installing mysql2 で引っかかっていたのが、今度はスムーズにいけました。
railsをAPIモードで使うことを前提としているので、コレで作業が進みそうです。おまけ
Ruby on Rails+ReactでCRUDを実装してみた
https://qiita.com/yoshimo123/items/9aa8dae1d40d523d7e5d#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E6%88%90こちらを進めているのですが、
console.user-no-MacBook-Pro:app user$ rake db:create Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) Couldn't create 'app_development' database. Please check your configuration. rake aborted! Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)と出てしまいました。
rake aborted!
cant connect to local MySQL serverとあるため、mysql側にエラーがあるようです。今回の場合、mysqlサーバーが動いていないよ!ということだったので、
console.user-no-MacBook-Pro:app user$ mysql.server start Starting MySQL .. SUCCESS! user-no-MacBook-Pro:app user$ rake db:create Created database 'app_development' Created database 'app_test'mysqlを起動することで、無事に解決しました。
今後もエラーログを見て、何がエラーになっているのかを理解してスキルを高めていきたいですね。参考
【Rails/MySQL】RailsにMySQLを導入する方法【プログラミング学習149日目】
https://qiita.com/fuku_tech/items/a380ebb1fd156c14c25b
- 投稿日:2020-05-14T04:06:00+09:00
[Rails]ネストしたコメントの削除機能の作成
コメントの削除機能の作成
Railsで投稿サイトのコメント削除機能を作成した際に、ネストのルーティング関連で少し詰まってしまったのでログを残しておきます。
作りたいもの
本の内容を投稿(book)して、その投稿1つ1つに対してコメント(book_comment)できる。
そのコメントを消すことができるようにする。routes.rbRails.application.routes.draw do devise_for :users resources :users,only: [:show,:index,:edit,:update] resources :books do resources :book_comments, only: [:create, :destroy] end root 'home#top' get 'home/about' enddestroyアクションにlink_toする書き方について
ここで、viewファイルのlink_toの書き方が分からず、詰まってしまいました。
comment
はbook
に紐づいているので、comment
を削除(destroy)するときは以下のように引数を2つ指定してあげるといいそうです。※
book_book_comment_path(@book, book_comment)
の部分。
book_comment
となっているのは、@book.book_comments
をeach
で1つ1つ回しているためです。show.html<% @book.book_comments.each do |book_comment| %> <%= link_to "#{book_comment.user.name}", user_path(book_comment.user) %> <span><%= book_comment.created_at.strftime('%Y/%m/%d') %></span> <%= link_to "Destroy", book_book_comment_path(@book, book_comment), method: :delete, data:{confirm: "削除しますか?"}, class: "btn-sm btn-danger"%> <div class="comment-entry"><%= book_comment.comment %></div> <% end %>コントローラーファイルについて
コントローラーファイルは以下の通りです。
URLにはidが2つある状態(
/books/:book_id/book_comments/:id(.:format)
)なので、book_id
とコメントのid
が2つ分かっている状態にすると、きちんと動きました。book_comments_controller.rbclass BookCommentsController < ApplicationController before_action :authenticate_user! def create @book = Book.find(params[:book_id]) @book_new = Book.new @book_comment = @book.book_comments.new(book_comment_params) @book_comment.user_id = current_user.id @book_comment.save redirect_to book_path(@book) end def destroy @book = Book.find(params[:book_id]) book_comment = @book.book_comments.find(params[:id]) if book_comment.user != current_user redirect_to request.referer end book_comment.destroy redirect_to request.referer end private def book_comment_params params.require(:book_comment).permit(:comment) end end
- 投稿日:2020-05-14T00:30:04+09:00
【DBエラー解決】Error: Duplicate column name ''"
はじめに
$ rails db:migrate した際にDBには反映されているがエラーが起こっている解決策について調べたのでまとめました。
今回の状況
$ rails db:migrate
⬇︎
エラー発生
⬇︎
エラーなのにDBにはカラムが反映されているエラー内容
andardError: An error has occurred, all later migrations canceled: Mysql2::Error: Duplicate column name '' ・・・・・ ・ ・ ・ ・これはカラムが重複していますと言うエラーだそうです。
解決策
command + f でmigrationファイルの中身を空にする。
class AddCategoryToItems < ActiveRecord::Migration[5.2] def change ここを空にする end end空の状態で $ rails db:migrate すると通る。
command + f で切り取ったコードをペーストしてもとに戻しておく。
以降 $ rails db:migrate してもエラーになりません!
まとめ
Mysql2::Error: Duplicate column name ''
このエラーはカラムが重複していますよと言うエラー内容でした
migrationファイルを空にしてから $ rails db:migrate
その後migrationファイルの中身はもとに戻しておく。
- 投稿日:2020-05-14T00:09:53+09:00
【開発ログ⑩】Railsで法定付与日と勤続年数を計算したよ
前提について
はじめまして、 プログラミングスクールに通ういりふねと申します。この記事は、スクールの課題である個人アプリの開発の記録を書くことで、自身のアウトプットに利用しています。もし、読んでいただけた方がいましたら、フィードバックをしていただけたら嬉しいです。
開発するのは「有給休暇管理ツール」です。仕様は過去記事をどうぞ。
アプリはデプロイまで行いますが、サービスとして提供するものではありません。あくまでも自学習の一環ですので、ご理解下さい。では本題へどうぞ。
今回の実施内容
前回までで、社員登録の実装が完了しました。社員登録時に「入社日(カラム名は、hire_date)」を入力してもらいます。そこで、この入社日を使用して、法定付与日と勤続年数を計算させます。具体的には以下の手順で進めます。今回は書くこと少なめ。
- 法定付与日の計算方法を確認
- 法定付与日の実装
- 勤続年数の計算方法を確認
- 勤続年数の実装
法定付与日の「とは?」と「計算方法」
大見出しを使用するほどではないのですが、法定付与日について確認しておきます。法定付与日は、毎年有給休暇が付与される日のことです。これは、入社日から6ヵ月後となります。詳細は厚生労働省のページ等で確認できますが、あまりここでは関係ないので省きます。
2019年04月01日に入社した人は、毎年10月01日が法定付与日。
2019年01月30日に入社した人は、毎年07月30日が法定付与日。
つまり、入社日に6ヵ月を足し算すれば法定付与日は求められるわけです。もちろん、会社が作成する就業規則によって法定付与日は、変更されている場合があります。一律、4月1日と定めているところもあるようです。今回は、基本的にこの考え方で進めます。
法定付与日の実装
日付計算に際しては、こちらの記事を参考にさせていただきました。今回は、ルーティングとコントローラーの編集は行わないので、モデルとビューを掲載します。
モデルには、入社日(hire_date)に「 >> 6 」を記述して、月数に6を足す計算メソッドcalculate_grant_dateを定義しました。models/employee.rbclass Employee < ApplicationRecord 〜中略〜 def calculate_grant_date grant_day = hire_date >> 6 grant_day.strftime("%m/%d") end endモデルファイルに定義したcalculate_grant_dateをeach文の変数employeeに対して実行しています。これでOKでした。
branches/_main.html.haml.main =render 'branches/mainheader' 〜中略〜 - @employees.each do |employee| %tr 〜中略〜 %th{id: "short"} = employee.calculate_grant_date 〜以下省略〜勤続年数の計算方法
今回、勤続年数は「何年何ヵ月」という表示をさせたいと考えています。大まかな考え方としては、現在の日付(rubyでは、Date.todayで表示)から、入社日(hire_date)を引き算すれば求められます。
仮に今日の日付が「2020年05月」で、入社日が「2019年3月」とするならば、「1年2ヵ月」と表示させることができればよいということになります。年と月をバラバラにとりだして、それぞれ引き算すれば良さそうです。具体的には以下のとおりです。
●勤務年数の計算●
今日の年「2020」 - 入社の年「2019」 = 1年
今日の月「05」 - 入社の月「03」 = 2ヵ月
計算結果を合体させて「1年2ヵ月」となります。これならイケそう!!計算がヘンテコになるパターンもありました。今日の日付が「2020年05月」で、入社日が「2015年08月」であれば、「4年9ヵ月」にならなければいけませんが、先程の計算を適用するとこうなります。
●ヘンテコになる勤務年数の計算●
今日の年「2020」 - 入社の年「2015」 = 5年
今日の月「05」 - 入社の月「08」 = -3ヵ月
計算結果を合体させて「5年-3ヵ月」となります。っておい!!!!つまり、月の計算結果がマイナスになるときは、下のように年の計算結果に「-1」を月の計算結果に「+12」を足せば正しい勤続年数が算出されるというわけです。
●勤務年数の計算●
今日の年「2020」 - 入社の年「2015」 -1 = 4年
今日の月「05」 - 入社の月「08」 +12 = 9ヵ月
計算結果を合体させて「4年9ヵ月」となります。できました!!ちなみに月の計算結果が、0のときはどうでしょうか?今日の日付が「2020年05月」で、入社日が「2015年05月」とであれば、「5年0ヵ月」になります。
●勤務年数の計算●
今日の年「2020」 - 入社の年「2015」 = 5年
今日の月「05」 - 入社の月「05」 = 0ヵ月
計算結果を合体させて「5年0ヵ月」となります。こっちは問題なさそう。勤続年数の実装
前置きが長くなりましたが、実装します。今回もモデルとビューのみの編集になります。
同じくモデルに勤続年数を計算させる「calculate_year_of_service」メソッドを定義します。
先程の計算結果から、月の計算結果が「0以上」か「0未満」かで条件分岐をさせています。「0以上」ならそのまま年と月の引き算を行い、それ以外(月の計算結果がマイナスになった場合)は、計算結果の最後に年に「-1」を月に「+12」を付け加えればOK!!model/employee.rbclass Employee < ApplicationRecord 〜中略〜 def calculate_year_of_service if Date.today.month - hire_date.month > 0 year = Date.today.year - hire_date.year month = Date.today.month - hire_date.month else year = Date.today.year - hire_date.year - 1 month = Date.today.month - hire_date.month + 12 end "#{year}年#{month}ヵ月" end endbranches/_main.html.haml.main =render 'branches/mainheader' 〜中略〜 - @employees.each do |employee| 〜中略〜 %th{id: "short"} = employee.calculate_year_of_service 〜以下省略〜ビューの確認
無事に実装できました。ビュー確認時点は、2020年05月です。
今日の積み上げ
計算系のメソッドを作るときは、紙に手書きで計算を書いてから論理を組み立てることが大切だと感じました。この記事も計算式と説明している内容が合っているかドキドキしながら書きました〜。
あと、数字を際立たせるために文中に「カギカッコ」を使いすぎ。。。
- 投稿日:2020-05-14T00:00:41+09:00
【Rails】 5分でExcel出力を実装する方法
はじめに
社内アプリケーションを作成したときに管理部へ提出するようの資料をExcel出力するとき調べたことをまとめました。
Axlsx というライブラリを使用すると、Rails で簡単にExcel出力ができるようになります。
利点なのかわかりませんが、複数のシートを作成することができて便利だなーと感じました。
コマンド
// Rails アプリケーション作成 $ rails _5.2.4.2_ new five_min_axlsx_rails $ cd five_min_axlsx_rails // scaffold で Excel モデルを作成 $ rails g scaffold excel title:string body:string $ rails db:migrate RAILS_ENV=developmentGemfile
Gemfilegem 'axlsx_rails'$ bundle installサンプルデータ作成
サンプルデータを作成するために、数も少ないのでコンソール
rails c
を開いてコマンドを実行します。$ rails c > Excel.create(title: "sample_1", body: "body_sample_1") > Excel.create(title: "sample_2", body: "body_sample_2") > Excel.create(title: "sample_3", body: "body_sample_3")Controller
index(一覧)のデータをExcelに出力したいケースを想定します。
controllerに追記します。app/controllers/excels_controller.rbdef index # Excelに出力したいデータをインスタンス変数に格納する @excels = Excel.all # 以下、追記 respond_to do |format| format.html format.xlsx do # ファイル名をここで指定する(動的にファイル名を変更できる) response.headers['Content-Disposition'] = "attachment; filename=#{Date.today}.xlsx" end end endaxlsx
viewのexcelフォルダに
index.xlsx.axlsx
を作成します。
rubyの拡張子ではないため注意!!
- ポイント
- 複数のシート(excel用語)に分けてデータを出力するために
add_worksheet
メソッドを繰り返ししている
add_worksheet
とは、新しいシート(excel用語)を作成します。add_worksheet
メソッドの引数(name)に出力するファイル名を指定することができるadd_row
メソッドで1行目に追加する内容を指定することができるapp/views/excels/index.xlsx.axlsxwb = xlsx_package.workbook @excels.each_with_index do |excel, index| wb.add_worksheet(name: excel.title) do |sheet| sheet.add_row [ "No.", "title", "body" ] sheet.add_row [ index, excel.title, excel.body ] end endview
以下の1行を追記します。
app/views/excels/index.html.erb<h1>Excels</h1> <!-- 以下の1行を追記 --> <%= link_to "Excel出力", excels_path(@excels, format: :xlsx) %> <table> <thead> <tr> <th>Title</th> <th>Body</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @excels.each do |excel| %> <tr> <td><%= excel.title %></td> <td><%= excel.body %></td> <td><%= link_to 'Show', excel %></td> <td><%= link_to 'Edit', edit_excel_path(excel) %></td> <td><%= link_to 'Destroy', excel, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New Excel', new_excel_path %>確認
ここまでできたら、実際にExcel出力ができるかどうかを確認します。
$ rails sリンク
http://localhost:3000/excels
へアクセス。Excel出力のリンク先をクリックするとダウンロードが開始されます。
ダウンロードしたExcelファイルを開くと以下のように出力されていることが確認できます。
まとめ
いかがでしたでしょうか。
最近、自分が書いた記事を参考にしてくださっているのを見て、時間をかけて書いた甲斐があるなぁとしみじみ感じております。参考
- RailsでAxlsxを使ってxlsxを生成
- Github randym/axlsx