niki12260714の日記

フリーランスのITエンジニアの呟き。

AWS Cloud9でMySQLに乗り換える

諸事情により、ポスグレからMySQLにDBを変更することになり、開発環境のDBを変えることになりました。
その手順です。

※2018/7/30現在、AWS Cloud9には、MySQLが標準でインストールされているので、インストール作業は飛ばします

MySQLにデータベースを作る】

参考にしたのはこちら。

qiita.com1.文字コードを修正する
「sudo vi /etc/my.cnf」で、my.confに「character-set-server=utf8mb4」を追加
※sudoでやらないと権限足りなくて書き込みできませんでした
2.開発用、テスト用、本番用のデータベースを作る
mysql -uroot」でmysqlに入り、「create database DB名;」でそれぞれデータベースを作成
3.接続用のユーザーを作成し、作成したデータベースへの権限を不要
「create user 'ユーザー名'@'localhost' identified by 'パスワード';」でユーザー作成
「grant all on DB名.* to 'ユーザー名'@'localhost' identified by '接続パスワード';」

MySQLのgemを入れる】

Gemfileに「gem 'mysql2'」を追加、「bundle install」
→失敗する。
 「Gem::Ext::BuildError: ERROR: Failed to build gem native extension.」の表示
ぐぐってみると、こちらの記事がヒット。

kzy52.com

「sudo yum -y install mysql-devel」を実施後、「bundle install」「bundle update」でエラーが発生しなくなる

【database.ymlを修正】

こちらの記事の設定をそのまま使わせていただきました。

www.rubylife.jp

【DB作成しなおし】

「bundle exec rake db:migrate」を実行。
→エラー発生、「NoMethodError: undefined method `inet' for #<ActiveRecord(略」が発生
ぐぐると、こちらの記事がヒット。

programming-beginner-zeroichi.jpまったく同じ状態だったので、inetをstringに修正、再度「bundle exec rake db:migrate」を実施、成功。

ここまで来たら、「rails s -b $IP -p $PORT」でrails serverを起動し、ログイン画面を表示させ、ログインを試しました。
当然ユーザーがいないので蹴られます。
DBにユーザーを問い合わせられる(接続が確立している)、ユーザーがいない(新しいDBを見に行っている)から、成功していると判断。
以上終了です。

deviceでtwitter連携時に「raise OAuth::Unauthorized, response 403」となった時の対応方法

deviceの使い方はこちら参照。

qiita.com

で、この通りに進めて、サインアップページの下に出てくる「Sign in with Twitter」をクリックすると、

「raise OAuth::Unauthorized, response 403」

となってしまいました。
ぐぐると、「Twitterの設定で、Callback URLが空だと駄目」とあります。
ですが、自分はちゃんと埋めてある。
とすると、Callbackが正しくないということで、「rails routes」でルート確認。

f:id:niki12260714:20180718093640j:plain

deviceが自分で作ってくれたルートの中に、twitter関連のものと思われるものを発見。
これをそれぞれ、TwitterのCallback URLに追加。

https://(自分の環境)/users/auth/twitter
https://(自分の環境)/users/auth/twitter/callback」

これで自分はエラーは出なくなりました。

独自定義のモデルを元にしたformのvalidatesを日本語化

変にハマったので、メモ。
内容はここの続きです。
あと、大前提として、「i18n」を入れて、日本語化の準備が出来ていることが必須です。

niki12260714.hatenablog.com

この方法で戻ってくるエラーメッセージを見ると
「col aは必須です」
のように、モデルに定義したattributesは英語になってます。
これを日本語にするには、「config/locals/models」の中に「ja.yml」を作成し、以下のように記述します。

ja:
 activemodel:
  attributes:
   item_add:
    col_a: "カラムA"
    col_b: "カラムB"
    col_c: "カラムC"

ネットでぐぐって最初にヒットするのは、大体がDBと結びついたモデルのカラム名を日本語化する話だと思います。
自分の場合、独自モデルで、Active Modelを継承したものなので、activemodelと記述しないと駄目です。
気付けば、そういうことか! ってなる話でした。

※これで日本語化されてないとなると、多分パスが通ってない気がするので、こちらを参考にパスを通すと良いかと。

qiita.com

Active Modelを定義し、form_forを使用、validatesでエラー処理までの流れ

画面の入力項目が一つのテーブルだけではないというのはよくあるパターン。
そうすると、form_forが使えないので、form_tagを使うことになります。
が、そうするとvalidatesの処理とかセオリー通りにいかないし、form_forの方がBootstrapでごにょごにょするのに便利なわけですし。
なんで、こういう場合は「そのformで使うモデルを独自に定義し、それを使う」という技があります。
※あと、検索画面で「検索条件欄に入力が無かったらエラーにしたい」というのをvalidatesで定義しておきたいとか。

というわけで、独自モデルの定義。
railsが5以上の場合は、app/modelの下にconcernsというフォルダが出来ているので、ここを使うらしい?
※ファイルの配置位置は、私の解釈なんで、間違っていたらご指摘ください。
「item_add.rb」というファイルを追加し、以下を記述

【model】
class ItemAdd
 include ActiveModel::Model
 attr_accessor :col_a, :col_b, :col_c

 validates :col_a
  presence: true,
  length: {minimum: 1, maximum: 100}
end

ポイントは、赤字にした「include」のところで、ActiveModelを継承すること。
で、attr_accessorで、そのformで使う要素を列挙すること。
attr_accessorに書いた要素に対し、validatesを定義しておきます。
これは通常のModelに対する書き方と一緒。

で、この独自に定義したmodelをcontrollerでnewしてあげて、viewで使うわけです。

【controller】
def init
 #独自定義のモデルをロードする
 @add_form = ItemAdd.new()
end

【view】
<%= form_for @add_form, url: {controller: :xxx, action: :xxx} do |form| %>
 <%= form.label :col_a, "要素A" %>
 <%= form.text_field :col_a, id: :col_a%><br />
 <%= form.label :col_b, "要素B" %>
 <%= form.text_field :col_b, id: :col_b%><br />
 <%= form.label :col_c, "要素C" %>
 <%= form.text_field :col_c, id: :col_c%><br />
 <br />
 <%= form.submit "追加", class: :"btn btn-default" %>
<% end %>

これでview側で、form_forが使えます!
さて、これでsubmitされたら、validatesを通してチェックをしたいわけで、それはcontrollerでこんな感じで書きます。

【controller】
def xxx
 #入力値を元にモデルオブジェクトを生成
 add_form = ItemAdd.new(col_a: params[:item_add][:col_a], col_b: params[:item_add][:col_b], col_c: params[:item_add][:col_c])
 if !add_form.valid?
  #validatesでエラーになった場合の記述
  flash[:danger] = add_form.errors.full_messages[0]
 else
  #validatesに問題がない場合の記述
 end
end

ポイントは、赤字のnewしているところと、newしたオブジェクトに対し、valid?と判定をかけているところ。
newする時に、formでpostされたデータを入れてあげます。
その結果が正しいか(valid?)を確認し、falseが戻ってきたらエラーってことで、エラー処理を書けばよいです。
エラーのメッセージは、通常のmodelのvalidatesで戻されるのと同一です。

 

→エラーメッセージの日本語化について、追加の記事があります。

niki12260714.hatenablog.com

railsで二つのセレクトボックスを連動させる

都道府県のセレクトボックス選ぶと、隣にある市町村のセレクトボックスの中身が変わる。
そういう奴をrailsで作る方法です。
ネットに沢山情報がありますが、自分が分かりやすかった方法がこちら。

【プログラムの動き】
1.セレクトボックスにonchangeイベントを設定し、coffeescriptに記述した関数を呼び出す
2.呼び出された関数で、セレクトボックスの選択値を取得、Ajax通信を行い、データを取得
3.取得したデータを、coffeescriptでセレクトボックスの要素に設定する

というわけで、1から順番に。

【View】
<%= select_tag 'parent_select', options_for_select(@date_array), onchange: 'change_data()' %>

赤字のところで、「セレクトボックスが選択されたら、change_dataという関数を呼び出す」と設定します。

coffeescript

f:id:niki12260714:20180620175754j:plain

coffeescriptはインデントが大事なので、画像でコードを表示
app/assets/javascriptの中に配置されるcoffeescriptに記述します。
自分が詰まった場所について、それぞれ解説。
「4行目:$ ->」は、「jQueryを使用します」という宣言。
「5行目:@change_data =() ->」は、先ほどViewで設定した呼び出し関数ですが、ここでポイントは「@」と「()」。
coffeescriptのサイトでコンパイルすると分かりますが、@はthisを表します。
これがいないと、View側から見えないので、セレクトボックスを選択するとエラーになっちゃいます。
()も、呼び出される時には必要な設定。
「7行目:val = $('[name=parent_select]').val()」は、jQueryを使ってセレクトボックスで選択された値を取ってきます。
9~18行目はAjax通信部分で、json形式の戻り値を処理します。
処理は別関数に切り出してます、21行目以下。
jsonをfor文で回し、中身をoptionの中に入れてセレクトボックスの末尾に追加。

 

【controller】
def
 data = Item.where(id: params[:id])
 render json: group.select("id AS id, item_name AS name")
end

 Ajax通信の個所。
render jasonで、json形式にデータを展開します。

以上!

AWS Cloud9でpumaを再起動

参考にしたのはこちら。

qiita.com

ターミナルで「ps ax」を打つと、これが出てきました。

f:id:niki12260714:20180613171258j:plain

AWSの場合、「puma x.xx.xx~」を殺せばよい模様。
「kill -9 6109」でpumaが終了するので、「rails s -b $IP -p $PORT」を打てば再起動する。

※「server.pid」ファイルをrmする必要があるかは不明。多分rmしなくても良い気がする……。

【調査中】railsでleft_joinsするモデルに対して条件を追加する方法

こちらと同じことをしたくて調べています。

teratail.com

自分の場合、まさに外部結合する方のテーブルに条件を追加するSQLを発行したいのです。

SELECT A.id, B.id
FROM A
LEFT OUTER JOIN B ON A.id = B.a_id AND B.col = (動的な値)
WHERE A.col = (なんかの値)

赤字のところを追加したいんですが、これが分からん。
あんまりモデルにデフォルトスコープ持たせたくないのと、後でメンテするときに分かりやすい記述にしておくべきと思うので、今はfind_by_sqlSQLべた書きにしています。
これを折々調べようと思うんで、ここにメモとして残しておきます。