- 投稿日:2020-08-24T23:32:50+09:00
【Railsチュートリアル 4章】Rails風味のRuby
引数のデフォルト値
メソッドを呼び出した時に指定した引数は、メソッド側で先頭から順に代入されていきます。
この時、代入するオブジェクトが無かった場合(呼び出し側で指定した引数の数が、メソッド定義側で指定した引数よりも少なかった場合)、
デフォルト値が設定されていればその値が代わりに代入されます。def printHello(msg="No msg", name="No name") print(msg + "," + name + "¥n") end printHello("こんにちは", "佐藤") #=>こんにちには,佐藤 printHello("お元気ですか") #=> お元気ですか,No name printHello() #=> No msg, No namedef printHello(msg="No msg", name="No name") print(Kconv.tosjis(msg + "," + name + "¥n")) endカスタムヘルパー
Railsのビューでは膨大な数の組み込み関数が使えるが、それだけでなく、新しく作成することもできる。
単なるRubyのコードを書くのであれば、モジュールを作成するたびに明示的に読み込んで使うのが普通。
Railsでは自動的にヘルパーモジュールを読み込んでくれるので、
include行をわざわざ書く必要がない。app/helpers/application_helper.rbmodule ApplicationHelper # ページごとの完全なタイトルを返します。 def full_title(page_title = '') base_title = "Ruby on Rails Tutorial Sample App" if page_title.empty? base_title else page_title + " | " + base_title end end endapp/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title><%= full_title(yield(:title)) %></title> <!-- full_tileメソッドの使用ができている --> <%= 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>
- 投稿日:2020-08-24T23:29:54+09:00
operator does not exist: timestamp without time zone ~~ unknownというエラーの対処
環境
・Rails 6.0.3.2
・mysql Ver 14.14 Distrib 5.6.47
・osx10.15
・herokuへデプロイあらすじ
railsでWebアプリを作成しherokuへデプロイするときにエラーが発生した。
heroku log でエラーを確認すると以下のような表示を発見
operator does not exist: timestamp without time zone ~~ unknownエラー文は初めはよくわからなかったがtimestampとはカラムの型なので型のところで何かエラーが起こっていることがわかりさらに調べて見ました。
そうするとDBの検索の条件文に問題点があることが判明。
もうすこし詳しくいうと、データの作成日時で検索をかけていたロジックが以下みたいに文字列(string型)で検索していたため問題が発生。where('created_at: LIKE(?)', "%%-%%-%%") #左記は問題のコード原因
原因は端的にいえばherokuのDBは『PostgreSQL』であり自分の環境(MySQL)と異なっていたことです。
『MySQL』ではtimestamp型のカラムをstring型で検索しても自動で変換してくれる(寛容)みたいです。
『PostgreSQL』ではSQL文を厳密にしないと動作しないみたいです。(厳しい・・・)対策
xxx.in_time_zone.というメソッドを使うことで解決しました!
例えば自分の場合は月単位でデータを取りたくて2020年8月のデータを取得したい場合(以下)search_time = "2020-08-01" (モデル名).where(created_at: search_time.in_time_zone.all_month)とすることでうまくtimestamp型でデータ検索ができるので『PostgreSQL』でもうまく動きました!
また記述も少なくなりスッキリしました!
最後のall_monthをall_dayなどに変更可能でかなり使えます!詳しくはリンクがあるのでそちらがとてもわかりやすく書いてあります!参照
エラー文について
・https://nobuneko.com/blog/archives/2010/05/postgresql83operator_does_not.html
TimeクラスとDateTimeクラスについて
・https://qiita.com/jnchito/items/cae89ee43c30f5d6fa2c
- 投稿日:2020-08-24T22:08:51+09:00
rails g controller コマンドで作成されるファイルを制限
開発環境
rails 6.0.3
(Minitest
を使用していないプロジェクトを想定しています)説明
rails g controller <コントローラー名> <アクション名>
を実行すると、controller
とview
以外に、特に必要のないファイルが作成される。これを回避するために、
rails
プロジェクト内に、次のファイルを作成すれば,
controller
とview
以外のファイルが作成されないようになる。config/initializers/generators.rbRails.application.config.generators do |g| g.assets false g.helper false g.skip_routes true end上記ファイルの作成が面倒なときは、
rails g controller
コマンドを実行時に下記3つのオプションを追加して実行する。ターミナルrails g controller <コントローラー名> <アクション名> --no-assets --no-helper --skip-routes
上記によって、下記のメリットがある。
・app/assets/
配下にstylesheetsのファイルが作成されない。
・app/helpers/
配下にhelperファイルが作成されない。
・config/routes.rb
にルーティングが追加されない。
- 投稿日:2020-08-24T21:59:36+09:00
テーブル設計(Active Hashを利用する)
概要
今回は、フリマアプリのクローンサイトを作る上でテーブル設計をしていきました。
初めて自作で作るのでテーブル設計のクオリティはまだまだですが、とりあえずアウトプットということで見逃してください、、、ER図
ポイントとしては、既存のデータから選択させるような箇所はActive Hashを用いてファイルを用意しておくことです。
商品のコンディションや、購入時の届け先の都道府県など、、、
都道府県を表すprefectures(active_hash)テーブルに関しては発送元と届け先で二箇所利用しています。Active Hashの使い方
インストール
Gemfileに以下を記載してbundle installする。
Gemfilegem 'active_hash'あとはActive Hash用のモデルを作成します。
⚠︎ターミナルでのコマンドで作らず、ActiveHash::Baseを継承したモデルを自作すること⚠︎ファイル記述
例として、カテゴリーのファイルをあげておきます。
app/models/category.rbclass Category < ActiveHash::Base 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: 'その他'} ]README
また、今回設計したテーブル、アソシエーションのREADMEは次のようになりました。
README.md# テーブル設計 ## users テーブル | Column | Type | Options | | ----------- | ------ | ----------- | | nickname | string | null: false | | email | string | null: false | | password | string | null: false | | first_name | string | null: false | | family_name | string | null: false | | read_first | string | null: false | | read_family | string | null: false | | birth | date | null: false | ### Association - has_many :products - has_many :item_purchases - has_many :comments ## products テーブル | Column | Type | Options | | ------------------- | ---------- | ----------- | | photo | text | null: false | | name | string | null: false | | explanation | text | null: false | | category | integer | null: false | | condition | integer | null: false | | postage_type | integer | null: false | | prefectures | integer | null: false | | preparation_days | integer | null: false | | value | integar | null: false | | user | references | null: false | ### Association - belongs_to :user - has_one :item_purchase - has_many :comments - belongs_to_active_hash :category - belongs_to_active_hash :condition - belongs_to_active_hash :postage_type - belongs_to_active_hash :prefectures - belongs_to_active_hash :preparation_days - belongs_to :seller, class_name: "User" ## item_purchases テーブル | Column | Type | Options | | ------------- | ------- | ------------------------------ | | product | integer | null: false, foreign_key: true | | user | integer | null: false, foreign_key: true | | purchase_info | integer | null: false, foreign_key: true | ### Association - belongs_to :user - belongs_to :product - belongs_to :purchase_info ## comments テーブル | Column | Type | Options | | ------- | ---------- | ------------------------------ | | content | string | null: false | | user | integer | null: false, foreign_key: true | | product | integer | null: false, foreign_key: true | ### Association - belongs_to :product - belongs_to :user ## purchase_info テーブル | Column | Type | Options | | ------------- | ---------- | ------------------------------ | | postal_code | string | null: false | | prefectures | integer | null: false, foreign_key: true | | city | string | null: false | | address | string | null: false | | building_name | string | | | phone_number | string | null: false | | item_purchase | integer | null: false, foreign_key: true | ### Association - has_one_active_hash :prefectures - has_one :item_purchaseここでは、has_oneとbelongs_toの従属関係に注意しましょう。
外部キーが必要な場合はbelongs_toでアソシエーションが組まれていることが必要です。
最後に少しだけ感想ですが、初めての自力実装はめちゃくちゃ時間がかかりますが、楽しくてしかたなかったです。ただ、今日これをやるだけでも10時間ほどかかってしまったので挫折しないように頑張ります、、、
- 投稿日:2020-08-24T21:46:58+09:00
(´-`).。oO(標準出力の『Hello』をお手軽に探したい。
Ruby初心者です。
今日は「Hello World」を出力するだけのrakeタスクを作りました。
# rake hoge:hello "Hello World"結果に「Hello」が含まれていることを確認するテストを書いてて、簡潔に文字列指定で探せる方法があれば良いと思ったのですが。。。
コード
hoge_spec.rbrequire 'rails_helper' require 'rake' RSpec.describe 'Hoge', type: :task do # 中略 describe 'rake hoge:hello' do let(:task) { 'hoge:hello' } context '標準出力のHelloを探せ' do it '標準出力でHello #1 outputマッチャーの引数に文字列' do # ※理想型。ただし完全一致しないので失敗する。。。 expect{ @rake[task].invoke() }.to output('Hello').to_stdout end it '標準出力でHello #2 outputマッチャーの引数に正規表現' do # ※うまくいくけど正規表現のマッチ使いにくい。。。 expect{ @rake[task].invoke() }.to output(/Hello/).to_stdout end it '標準出力でHello #3 includeで部分一致' do # ※うまくいくけどコードが長い。。。 $stdout = StringIO.new @rake[task].invoke() output_text = $stdout.string $stdout = STDOUT expect(output_text).to include 'Hello' end end end end実行結果
・・・文字列指定(#1)でできたら良いなと思ってたのですがエラー。
目的を実現するには正規表現(#2)にするか標準出力を変数に代入する(#3)しか方法が見つからず。。。
何か良い方法がないものかと思ってます。
- 投稿日:2020-08-24T18:35:03+09:00
実装するにあたり、テストを記述したのち、処理をコーディングする
背景
現場でのあるあるですが、普段は以下の流れで開発を進めることが多いと思います。
(1)設計→(2)処理のコーディング→(3)テストのコーディング→(4)単体テストなど…しかし、Railsチュートリアルでは、
(1)設計→(2)テストのコーディング→(3)処理のコーディング…
という流れで解説されていました。自分も少し勉強になったので、記事として掲載しようと思います。環境
項目 内容 OS aws #35-Ubuntu SMP(Cloud9) Ruby ruby 2.6.3p62 (2019-04-16 revision 67580) Ruby On Rails Rails 6.0.3 対応
以下は、ルーティング、コントローラが設定されているアプリに、新たにabout(概要)ページを追加する場合、テストソースからコーディングする例です。
config/route.rbRails.application.routes.draw do get 'static_pages/home' get 'static_pages/help' root 'static_pages#home' ※概要ページのルーティングはまだ設定されていません。 endapp/controller/static_pages_controller.rbclass StaticPagesController < ApplicationController def home end def help end ※概要ページのアクションはまだ設定されていません。 end手順1)ここで、新たに追加するページに対するテストを予め記述しておきます。
test/static_pages_controller_test.rbrequire 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest 1 2 test "should get home" do 3 get static_pages_home_url 4 assert_response :success 5 end 6 7 test "should get help" do 8 get static_pages_help_url 9 assert_response :success 10 end 11 12 ※~~~~ ここから想定で概要ページのテストを記入する。~~~~ 13 test "should get about" do 14 get static_pages_about_url 15 assert_response :success 16 end end手順2)テストを実行してみる(1回目=まだ処理を記述していないのでエラーになる)
$rails test Error: StaticPagesControllerTest#test_should_get_about: NameError: undefined local variable or method `static_pages_about_url' .. Finished in 2.157942s, 1.3902 runs/s, 0.9268 assertions/s. 3 runs, 2 assertions, 0 failures, 1 errors, 0 skipsエラーが出てしまいました。
手順3)上記から、パスが通っていないので、ルーティングを見直します。
config/route.rbRails.application.routes.draw do get 'static_pages/home' get 'static_pages/help' get 'static_pages/about' ←ここを追加した☆ root 'static_pages#home' end手順4)テストを実行する(2回目=想定ではエラーになる)
$ rails test Error: StaticPagesControllerTest#test_should_get_about: AbstractController::ActionNotFound: The action 'about' could not be found for StaticPagesController… 3 runs, 2 assertions, 0 failures, 1 errors, 0 skipsまたエラーが出てしまいました。
手順5)上記エラーから、アクションが原因なのでコントローラを見直します。
app/controller/static_pages_controller.rbclass StaticPagesController < ApplicationController def home end def help end def about ←ここを追加した☆ end end手順6)さらにテストを実行してみる(3回目=どうなるか…)
$ rails test Error: StaticPagesControllerTest#test_should_get_about: ActionController::MissingExactTemplate: StaticPagesController#about is missing a template for request formats: .. 3 runs, 2 assertions, 0 failures, 1 errors, 0 skipsまたエラーが出てしまいました。次はまた文言が違うようです…
手順7)上記エラーから、テンプレートが原因のようなのでビューを追加します。
touch app/views/static_pages/about.html.erb手順8)テストを実行する(4回目=正常テストパス)
$ rails test ... 3 runs, 3 assertions, 0 failures, 0 errors, 0上手くいきました…基本中の基本ですが、リファクタリングを含めておさえておくべき内容だと感じました。
参考文献
- 投稿日:2020-08-24T17:54:53+09:00
データベース(MySQL)の主キーを任意のカラムに変更する。
初めに…
データベースでテーブルを作成すると必ず作成される主キー(
:id
カラム)ですが、デフォルトではレコードに登録された順番に1番から登録されていきます。ところが、既存のサービスを見てみるとhttps://www.uniqlo.com/jp/ja/products/E428313-000
のように商品番号が URL に入っていたり、ユーザー情報を閲覧できるページではユーザー名がパラメータとなっていたりします。
そこで、本投稿ではデータベース(MySQL)で主キーを任意のカラムに変更する方法をご紹介します。データベース (MySQL) の主キーを任意のカラムに変更する方法
db/migrate/_マイグレーションファイル.rbclass ● ● ● < ActiveRecord::Migration[5.2] def change create_table :テーブル名, id: false, primary_key: :主キーにするカラム名 do |t| # `id: false` は、このテーブルでは `:id` カラムを作成しない。という記述です。 # `primary_key:` に主キーにするカラム名を指定する。 t.timestamps end end endapp/models/モデル名.rbclass モデル名 < ApplicationRecord self.primary_key = "主キーにするカラム名" # モデルファイルに この記述をすることで主キー(プライマリーキー)となるカラムを指定する。 end
- モデルファイルに主キーとなるカラムを記述しておくことで、コントローラーから
モデル名.find(params[:id])
により:id
のレコードを呼び出すと…サーバーログParameters: {"id"=>"値"} SELECT `テーブル名`.* FROM `テーブル名` WHERE `テーブル名`.`主キーとするカラム名` = 値 LIMIT 1`となり、ちゃんと主キーとした値が読み込まれてることが分かります。
- 投稿日:2020-08-24T17:27:02+09:00
【Rails】Amazon PA API v5.0 で書籍検索(非同期通信)
こちらの記事の続きです。
前回はAPI連携後にviewファイルを表示させていましたが、今回はajax通信でデータを取得して書籍検索結果を表示します。
(理由はこの方がかっこいいからです笑)完成イメージ
(cssで見た目を少し整えたので、前回の記事と少し見た目は変わってます)
処理の流れ
検索フォームに入力して検索ボタンを押す(submitする)と以下のように処理されます
1. 検索フォームのキーワードを取得
2. json形式でデータ(キーワード)を持たせて、指定のURLにリクエスト(ajax通信)
3. コントローラでAPI連携の処理をし、jsonでデータを渡す
4. 返ってきたデータを使ってhtmlを書き換えて検索結果を表示各ファイル
コントローラ
コントローラにjsonでレスポンスを返すという記述が必要です。
respond_to 〜
のところsearches_controllerclass SearchesController < ApplicationController before_action :call_client, only: :index def index si = @client.search_items(keywords: keyword_params, SearchIndex: "Books") @items = si.items respond_to do |format| format.html format.json end end private def call_client require 'paapi' @client = Paapi::Client.new(access_key: Rails.application.credentials[:pa_api][:access_key_id], secret_key: Rails.application.credentials[:pa_api][:secret_key], market: :jp, partner_tag: Rails.application.credentials[:pa_api][:associate_tag]) end def keyword_params params.require(:keyword) end endjbuilder
views/[コントローラ名と同じ名前のディレクトリ]
の中にindex.json.jbuilder
を作成します。
今回はcontrollers
直下にsearches_controller.rb
を作っているので、views/searches/index.json.jbuilder
となります。今回jsonデータは配列になっているので、各要素をどのように加工するか書く必要があります。取り出すデータは画像、タイトル、著者、出版社の情報にしています。
index.json.jbuilderjson.array! @items do |item| json.image_url item.image_url json.title item.title json.authors item.authors json.publisher item.publisher endjsファイル
api-search.js// jQueryを使って記述します $(function() { // 検索結果を表示する関数 let search_list = $("#books") function appendBook(image_url, title, author, publisher) { const html = `<div class="search-book-content"> <div class="book-image"> <img class="book-image" src="${image_url}"> </div> <div class="right-content"> <div class="book-info"> <div class="book-info__title"> ${title} </div> <div class="book-info__author"> ${author} </div> <div class="book-info__publisher"> ${publisher} </div> </div> </div> </div>` search_list.append(html); } // 検索中の表示関数 function dispLoading(msg){ let dispMsg = "<div class='loadingMsg'>" + msg + "</div>"; $("body").append("<div id='loading'>" + dispMsg + "</div>"); } // 検索中の表示を消す関数 function removeLoading(){ $("#loading").remove(); } // 検索ボタンを押した時にイベント発火 $("#book-search-form").on("submit", function(e) { e.preventDefault(); const keyword = $("#keyword").val(); dispLoading("検索中..."); // ajax通信の詳細 $.ajax({ url: '/searches', type: 'GET', data: {'keywords': keyword}, dataType: 'json', timeout: 10000 }) // ajaxがうまくいったとき .done(function(items){ $(".search-book-content").remove(); items.forEach(function(item){ let image_url; let author; let publisher; if (item.image_url == null) { image_url = `/assets/no_image-267acfcb976ba4942183409c682b62a768afb48c328b6ba60de7b57fd83c3b56.png` } else { image_url = item.image_url } if (item.authors.length == 0) { author = '不明な作者' } else { author = `${item.authors[0]}` } if (item.publisher == null) { publisher = '不明な出版社' } else { publisher = item.publisher } let title = item.title appendBook(image_url, title, author, publisher); }) }) // ajaxが失敗した時 .fail(function(){ alert("検索に失敗しました"); }) // ajaxが成功しても失敗しても共通の処理 .always( function(data) { // Lading 画像を消す removeLoading(); }); }); })コードを読めばわかると思いますが、処理の流れを書いてみます。
■ 関数の定義
- appendBook
本のデータを元にhtml作成する関数- dispLoading
検索中のgifファイルを表示させる関数- removeLoading
検索中のgifファイルを非表示にする関数■ イベント
- submitアクションでイベント発火
- フォームに入力されたキーワードを取得
- 検索中の画面を表示
- ajax通信のリクエスト(urlや送信データを指定)
- ajax通信が成功したときの処理
各データを変数にして、appendBook関数に食わせる (データが取得できない場合は不明な作者などのデータが入るようにしておく)- ajax通信が失敗したときの処理
- 共通処理として検索中の画像を消す
検索に少し時間がかかるので、検索中であることがわかりやすいように、ajax通信中はgif画像を表示させるようにしています。
また、検索に時間がかかりすぎた場合にエラーになるようにしています。(10秒)
ずっと検索中になっているとユーザー側が不安になるので。検索中の画面(cssファイル)
cssファイル#loading { display: table; width: 100%; height: 100%; position: fixed; top: 0; left: 0; background-color: #fff; opacity: 0.8; } #loading .loadingMsg { display: table-cell; text-align: center; vertical-align: middle; padding-top: 140px; background: image-url("loading.gif") center center no-repeat; }gifファイルは無料で作れるサイトがいくつかあるので作ってみましょう。
僕はこのサイトでつくりました。メモ
うまくいかなかった部分をメモに残しておきます。
(詳しい方は教えていただけると嬉しいです…)
本当はkeyword_paramsだけを引数にしたい
コントローラでkeywordを元にAPIを叩く際に、search_itemsの引数にはkeyword_params
としてハッシュ形式で渡せばスッキリするのですが、なぜかうまくいきませんでした。(activehashだと受け取ってくれない感じ…)turbolinksを消すとうまく作動しない
turbolinksが入っているとajax通信する際にうまくいかないことがあるらしく、いつも使わない設定にしていたのですが、逆にturbolinksがないせいでajax通信がうまく行かないという事態になりました笑。この辺の仕組みはいまいち理解できてません…
- 投稿日:2020-08-24T16:11:28+09:00
Ruby 預金システム、アルゴリズム問題
問題.1
以下の条件を達成するプログラムを実装しなさい
銀行口座に10万円の預金残高があり、お金を引き出すプログラムを作成します。
・お金を引き出すwithdrawメソッドを作成する
・お金を引き出すと手数料110円かかり、「◯◯円引き落としました。残高は◯◯円です」と表示する(残高は手数料を引いた額を表示します)
・もし預金残高より多く引き落としたら「残高不足です」と表示する
・以下にヒントを用意するので参考にしてくださいdef withdraw(balance, amount) fee = 110 # 手数料 # 引き落とし額と残高を表示する、もしくは残高より多く引き落としたら残高不足と表示 end balance = 100000 # 残高 puts "いくら引き落としますか?(手数料110円かかります)" money = gets.to_i withdraw(balance, money)比較的、わかりやすい問題でした。
以下が回答です。
def withdraw(balance, amount) fee = 110 if balance >= (amount + fee) balance -= (amount + fee) puts "#{amount}円引き落としました。残高は#{balance}円です" else puts "残高不足です" end end balance = 100000 puts "いくら引き落としますか?(手数料110円かかります)" money = gets.to_i withdraw(balance, money)考え方を順で追っていくと、
①puts "いくら引き落としますか?(手数料110円かかります)"
②money = gets.to_i で、引き落とし金額をコマンドで入力、moneyに代入
③withdrawの引数が定義され、def end に飛ぶ
④残高不足かどうかで出力される内容が異なるので、条件式if else を作成
⑤条件式はbalance >= (amount + fee)とすることで、残高に対して引き落とし金額が少ないかどうかが
表される
⑥balance = balance - (amount + fee)と書くことで、残高金額が表されてbalanceに代入される
⑦コードをすっきりさせる為にbalance -= (amount + fee)と書く。
⑧残高不足の場合はelse以降にputs "残高不足です"を表示。僕は、以下のようにコードを書いたけど、これはちょっと汚そう。
一応、同じ内容が出力されるけどね。if balance < amount - 110 puts "残高不足です" else puts "#{amount}円引き落としました。残高は#{balance - amount - 110}円です" end
- 投稿日:2020-08-24T15:45:45+09:00
半角スペースで区切られた2つの正の整数を足し算して出力する
paizaDランク問題で、標準入力に半角スペースで入力して計算する方法が分からなかったのでメモ
ruby.rbs = gets.split(' ').map(&:to_i) #splitで半角空けて配列を作り、map(&:to_i)で数値に変更 result = s[0] + s[1] #配列内の数値を足す puts resultこれまで改行で区切られた問題しか解いていなかったので、標準入力で半角空けて2つ入力する方法を分かってなかった。
基礎固め大事。
- 投稿日:2020-08-24T15:35:09+09:00
【Ruby】基本コマンド一覧
【Ruby】基本コマンド一覧
Rubyの基本コマンドの一覧と実例。
目次
- puts, print, pの違い
- 定数
- オブジェクトのクラス一覧表示(.class)
- オブジェクトのメソッド一覧表示(.methods)
- 小数点以下の計算
- 小数点の表示桁数指定
- 文字列
- 配列
- ハッシュオブジェクト
- 条件分岐
- 繰り返し
- メソッドの作成と呼び出し
- クラスの作成とインスタンスの生成
puts, print, pの違い
それぞれに微妙な違いはあるものの、putsついで、pだけ覚えておけばいい。
それぞれの差は、改行の有無や型表現の有無。
メソッド puts p 用途 本番用 デバッグ用 pythonの名残? 改行 あり あり なし 型表現 なし あり なし putsとprintの違い(改行の有無)puts "こんにちは" puts 123 #こんにちは #123 print "こんにちは" print 123 #こんにちは123
▼「p」の場合、文字列のテキストにダブルクオテーションがつくputsとpの違い(型表現の有無)puts "こんにちは" puts 123 #こんにちは #123 p "こんにちは" p 123 #"こんにちは" #123
定数
・変数を大文字で定義
・単語を複数つなげる場合はアンスコを使用
・上書きしたり、再定義するとエラーが出るTAX_RATE = 1.0 puts TAX_RATE #上書き指示 TAX_RATE = 1.5 #warning: already initialized constant TAX_RATE #warning: previous definition of TAX_RATE was here
オブジェクトのクラス一覧表示(.class)
p オブジェクト.class
p 1.1.class #Float
オブジェクトのメソッド一覧表示(.methods)
p オブジェクト.methods
p 1.1.methods #[:-@, :**, :<=>, :<=, :>=, :==, :===, :eql?, :%, :inspect, :*, :+, :-, :/, :<, :>, :to_int, :coerce, :to_s, :divmod, :to_i, :to_f, :to_r, :fdiv, :modulo, :abs, :magnitude, :zero?, :finite?, :infinite?, :floor, :ceil, :round, :truncate, :positive?, :negative?, :numerator, :denominator, :rationalize, :arg, 以下省略...p "abc".methods #[:encode, :encode!, :unpack, :unpack1, :include?, :%, :*, :+, :count, :partition, :to_c, :sum, :next, :casecmp, :casecmp?, :insert, :bytesize, :match, :match?, :succ!, :<=>, :next!, :upto, 以下省略...
小数点以下の計算
計算する整数に「.0」をつけると小数点まで表示する。
┗ どちらか一方
┗ 桁数を指定するものではない
┗ ない場合は切り捨てp 8.0 / 3 p 8/3.0 p 8/3 #2.6666666666666665 #2.6666666666666665 #2
round, ceil, floorメソッド
.round
:四捨五入
.ceil
:切り上げ
.floor
:切り捨てp 8.0/7 p (8.0/7).round p (8.0/7).ceil p (8.0/7).floor #1.1428571428571428 #1 round #2 ceil #1 floor
小数点の表示桁数指定
format(または sprintf)
format("%.桁数f", 対象数値)
calc = 8/3.0 puts format("%.2f", calc) puts sprintf("%.2f", calc) #2.67 #2.67round, ceil, floorメソッド
オブジェクト.round(桁数)
オブジェクト.ceil(桁数)
オブジェクト.floor(桁数)
puts calc.round(2) puts calc.round(3) #2.67 #2.667 puts calc.ceil(2) puts calc.ceil(3) #2.67 #2.667 puts calc.floor(2) puts calc.floor(3) #2.66 #2.666
文字列
式展開
puts "#{数式や変数}"
┗ ダブルクオテーション
┗ シングルだとそのまま出力puts "tax #{1000 * 0.10}" #tax 100.0 puts 'tax #{1000 * 0.10}' #tax #{1000 * 0.10}
連結
puts 文字列 + 文字列
puts "Hello" + "World" #HelloWorld
文字列の繰り返し
puts 文字列 * n
puts "Hello★" * 5 #Hello★Hello★Hello★Hello★Hello★
文字数カウント(.length)
puts 文字列.length
str = "hello world" p str.length #11
文字列から数値に変換(.to_i)
puts 文字列.to_i
str = "123" p str p str.to_i #"123" #123
配列
配列の要素取得
変数 = [ , , , ]
colors = ["red", "blue", "white"] p colors[0] p colors[3] #nil p colors #"red" #nil #["red", "blue", "white"]
要素追加(push or <<)
・
オブジェクト.push(追加要素)
・オブジェクト << (追加要素)
colors = ["red", "blue", "white"] p colors.push("green") #["red", "blue", "white", "green"] p colors << ("black") #["red", "blue", "white", "green", "black"]
配列の長さ取得(size, length, count)
・
オブジェクト.size
・オブジェクト.length
・オブジェクト.count
colors = ["red", "blue", "white", "green", "black"] p colors.length p colors.size p colors.count #5 #5 #5
ソート
・
オブジェクト.sort
・オブジェクト.sort.reverse
colors = ["red", "blue", "white", "green", "black"] p colors.sort p colors.sort.reverse #["black", "blue", "green", "red", "white"] #["white", "red", "green", "blue", "black"]numbers = [5, 8, 1, 4, 9] p numbers.sort p numbers.sort.reverse #[1, 4, 5, 8, 9] #[9, 8, 5, 4, 1]
ハッシュオブジェクト
作成と呼び出し
書き方は3種類あるが、省略形のみ覚えておけばいい。
作成
変数 = {キー:値, キー:値,,,}
呼び出し
変数[:キー名]
scores = {tanaka:100, sato:80, ito:50} p scores p scores[:sato] #{:tanaka=>100, :sato=>80, :ito=>50} #80
▼他の記述方法以下どれも同じscore1 = {"tanaka" => 100, "sato"=>80, "ito"=>50 } score2 = {:tanaka =>100, :sato=>80, :ito=>50} score3 = {tanaka:100, sato:80, ito:50} p score1 p score2 p score3 #{:tanaka=>100, :sato=>80, :ito=>50} #{:tanaka=>100, :sato=>80, :ito=>50} #{:tanaka=>100, :sato=>80, :ito=>50}
要素追加
変数[:キー名] = 値
scores = {tanaka:100, sato:80, ito:50} scores[:suzuki] = 40 p scores #{:tanaka=>100, :sato=>80, :ito=>50, :suzuki=>40}
要素削除(.delete(:キー名))
変数.delete(:キー名)
scores = {tanaka:100, sato:80, ito:50, suzuki:40} scores.delete(:suzuki) p scores #{:tanaka=>100, :sato=>80, :ito=>50}
条件分岐
if
条件式で処理を分岐する。
if, elsif, else, end
stock = 5 #在庫 if i >= 10 puts '在庫:◎' elsif i > 3 puts '在庫:△' elsif i == 1 puts '在庫:ラスト1個' else puts '在庫:×' end #在庫:△
case,when
値で条件式を分岐する。
case, when, else, end
case構文の構造# case 評価したい変数 # when 数値 # 処理 # when 範囲(開始値..終値) # 処理 # when 範囲(開始値..終値) # 処理 # else # 処理 # end
▼実例age = 6 case age when 0..5 puts 800 when 6..11 puts 1800 else puts 2500 end #1800・
開始値 .. 終値
┗ 開始値〜終値までの範囲
繰り返し
for in
for 変数 in 範囲
配列x = ["A", 1, "b", 3] for i in x puts i end #A #1 #b #3連続した数値for i in 0..3 puts i end #0 #1 #2 #3
each do
eschメソッドによる繰り返し。
オブジェクト.each do |変数| 処理 end
arrays = ["A", 1, "b", 3] arrays.each do |array| puts array end #A #1 #b #3
while
条件範囲内での繰り返し。
while 条件式 処理 end
i = 0 while i < 3 puts i i += 1 # i++は使えない end #0 #1 #2
times do
繰り返し回数の指定。
数値.times do |変数|
┗ 変数にインデックス番号が入る(0始まり)3.times do |i| puts "#{i}回目の処理です" end #0回目の処理です #1回目の処理です #2回目の処理です
loop + break
無限ループと終了条件の組み合わせによる繰り返し。
loop{処理 if 条件 break end}
┗ 終了条件(break)がないとエラーになるi = 0 loop{ puts i i += 1 if i >=3 break end } #0 #1 #2 #3
スキップ(if next)
指定条件のみスキップする。
if 条件式 next end
┗ 繰り返し処理の中で使用する。3.times do |i| if i == 1 next end puts "#{i}回目の処理です" end #0回目の処理です #2回目の処理です
メソッドの作成と呼び出し
▼作成
def メソッド名() 処理 end
┗ 引数なしの場合は()不要。▼呼び出し
メソッド名()
def hello(name) puts "Hello #{name}さん" end hello("Yuta") #Hello Yutaさん
▼事例2複数個の引数def ttl_price(price, n) return (price * n * 1.10).floor #returnは省略可能 end puts ttl_price(500, 3) #1650
クラスの作成とインスタンスの生成
クラスの作成
class クラス名 end
- クラス名は頭文字大文字。
- initializeメソッド:インスタンス作成時に自動で呼び出されるメソッド。
- プロパティは@で定義。
クラスの作成class User def initialize(name) @name = name end def say_name puts "私は#{@name}です。" end end
インスタンスの生成
変数 = クラス名.new(引数)
インスタンスの生成tanaka = User.new("tanaka") tanaka.say_name #私はtanakaです。 yamada = User.new("yamada") yamada.say_name #私はyamadaです。
以上。
- 投稿日:2020-08-24T14:36:45+09:00
【Rails6】resourcesメソッドを使ってdestroy
はじめに
resourcesメソッドってRESTfulな開発には必須だと思うんですよね〜(言いたいだけ)
resourcesって実際の中身を普段目にしなくて実際どんなパスが通っているかわかりにくいなと思ったので学習しました!
今回はたまたま僕が実装しようとしているdestroyを中心に書きます!削除ボタンの実装にどうぞ!
resoucesメソッドのルーティングの確認
routes.rbRails.application.routes.draw do resources :tweets :only => [:destroy] end普通destroyだけ設定なんてなかなかないとおもいますが、
ターミナルにて、
rake routes
を実行するとルーティングが確認できます!tweets DELETE /tweets/:id(.:format) tweets#destroyこれで設定できているパスは以下のとおりです
URL アクション HTTPメソッド 名前付きパス(_path) 名前付きパス(_url) 対応するパス /tweets/:id(.:format) destroy DELETE tweet_path(id) tweet_url(id) /tweets/:id 削除ボタン実装の例
<%= link_to "削除", tweet_path(○○変数名.id), method: :delete , data: { confirm: "削除しますか?"} %>変数名のところは時と場合によると思います!(要は削除したいレコードのidが取得できたらOK)
以上です〜
- 投稿日:2020-08-24T14:30:55+09:00
Rails ActionCableでチームチャットを作る
はじめに
ポートフォリオでActionCableでチームチャット機能を作ったので、アウトプットする。
参考記事
以下の記事を参考にしました。ありがとうございました。
・ Rails 5 + ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より)
・ 【Rails6.0】ActionCableとDeviseの欲張りセットでリアルタイムチャット作成(改正版)コード
参考記事にあるコードに手を加えて、チーム毎にチャットを送るようにする。
今回は、teamコントローラーにchatアクションを作ってチャットを送れるようにする。
Action Cable以外の部分をMVC構造に従って、作成する。model
モデルは、チームモデル、ユーザーモデルの多対多の関係を記述する。
また、チャットの中身であるメッセージモデルは、ユーザーとチームの中間テーブルのモデルとして作成する。app/models/team.rbclass Team < ApplicationRecord #チームモデル belongs_to :owner, class_name: 'User', foreign_key: :owner_id has_many :team_assigns, dependent: :destroy has_many :members, through: :team_assigns, source: :user has_many :messages, dependent: :destroy endapp/models/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable has_one_attached :icon #Active Storageでユーザーアイコンをつける has_many :team_assigns, dependent: :destroy has_many :teams, through: :team_assigns, source: :team has_one :owner_team, class_name: 'Team', foreign_key: :owner_id, dependent: :destroy has_many :messages, dependent: :destroy endapp/models/team_assign.rbclass TeamAssign < ApplicationRecord belongs_to :team belongs_to :user endapp/models/message.rbclass Message < ApplicationRecord belongs_to :user belongs_to :team endcontroller
コントローラーは、チーム内にチャットアクションを作成した。
リンクからチームIDを受け取って、Teamクラスから検索、そのチームが持つチャットを取得してviewに渡す。app/controllers/teams_controller.rbdef chat @team = Team.find(params[:id]) messages = @team.messages endview
bootstrapを適用したので、container、alertなどを使っている。基本的には受け取ったチャットをメッセージパーシャルをレンダリングすることで画面に表示している。また、チャットを送るためのフォームを設置している。
app/views/teams/chat.html.erb<h1 id="chat" data-team_id="<%= @team.id %>"> <%= @team.name %>チャットルーム </h1> <div class="container"> <% if @team.members.count == 1 %> <div class="alert alert-danger" role="alert"> チャットするチームメンバーがいません。チーム画面からメンバーを追加して下さい </div> <% end %> <form class="form-group"> <label>チャット:</label> <input type="text" data-behavior="team_speaker", placeholder="内容", class="form-control"> </form> <div id="messages_<%= @team.id %>"> <ul class="list-group"> <%= render @messages %> </ul> </div> </div>app/views/messages/_message.html.erb<div class="message"> <li class="list-group-item"> <div class="row"> <div class="col-md-2"> <% if message.user.icon.attached? %> <%= image_tag message.user.icon.variant(resize:'50x50').processed %> <% end %> <p><%= message.user.name %></p> </div> <div class="col-md-10"> <small class="text-muted"><%= l message.created_at, format: :short %></small><br> <%= message.content %> </div> </div> </li> </div>ここから、Action Cableの実装をする。
まず、チームチャンネルを作成する。
チャンネル用にディレクトリとファイルが作成される。$ rails g channel teamcoffeescript
まず、ブラウザからサーバーを監視する設定を記述していく。
jQueryを活用して、viewからチームIDを取得して、購読するチャンネルを作成する。app/assets/javascripts/channels/team.coffeeApp.team = App.cable.subscriptions.create { channel: "TeamChannel", team_id: $("#chat").data("team_id")}, connected: -> # Called when the subscription is ready for use on the server disconnected: -> # Called when the subscription has been terminated by the server received: (data) -> # Channel側のspeakメソッドで受け取ったチャットを受け取る $('#messages_'+data['team_id']).prepend data['message'] speak: (message) -> team_id = $('#chat').data('team_id') @perform 'speak', message: message, team_id: team_id $(document).on 'keypress', '[data-behavior~=team_speaker]', (event) -> if event.keyCode is 13 # return = send App.team.speak event.target.value event.target.value = '' event.preventDefault()channel.rb
次に、サーバーからブラウザを監視する設定を記述していく。
ブラウザの@perform speakによって、サーバー側のspeakアクションが呼び出される。
speakアクションは、ブラウザから受け取った情報を元に、メッセージクラスのインスタンスを作成する。app/channels/team_channel.rbclass TeamChannel < ApplicationCable::Channel def subscribed stream_from "team_channel_#{params['team_id']}" #どのチームを監視しているのかを示す end def unsubscribed # Any cleanup needed when channel is unsubscribed end def speak(data) message = Message.create!( content: data['message'], team_id: data['team_id'], user_id: current_user.id ) end endチャンネル内でcurrent_userを使っているが、そのためには次のコードを追記する必要がある。
app/channels/application_cable/connection.rbmodule ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user verified_user = User.find_by(id: env['warden'].user.id) return reject_unauthorized_connection unless verified_user verified_user end end endMessageモデル
Messageモデルにインスタンスが作成された際(コミット後)にjobが走るようにします。
app/models/message.rbclass Message < ApplicationRecord belongs_to :user belongs_to :team after_create_commit { MessageBroadcastJob.perform_later self } #追記 endjob
jobから、作成されたメッセージをブラウザへ送る設定を記述します。
まず、railsコマンドからMessageBroadcastJobを作成します。$ rails g job MessageBroadcastActiveStorageを使っている場合、ApplicationController.rendererのhttp_hostを指定しないと、画像の参照先がエラーになる。http://ではなく、https://から始まるURLを本番で使う場合には、https: trueを指定する必要がある。
app/jobs/message_broadcast_job.rbclass MessageBroadcastJob < ApplicationJob queue_as :default def perform(message) ActionCable.server.broadcast( "team_channel_#{message.team_id}", message: render_message(message), team_id: message.team_id ) end private def render_message(message) renderer = ApplicationController.renderer.new( http_host: 'http_host番号', #ローカルだと、locallhost:3000。本番環境によって変更 #https: true https://から始まるURLの場合、このコードを追記する必要あり ) renderer.render(partial: 'messages/message', locals: { message: message }) end endこのjobによって、coffeescriptのreceiveにメッセージのパーシャルが渡されてリアルタイムで反映されるという仕組みになっている。
まとめ
ActionCableは複雑だと思ったが、参考文献によってデータの受け渡しを追うことができ、理解することができた。この経験から、複雑に見えても、データの流れを1つずつ追うことが大事で、サーバー側だったらbinding.pry、ブラウザ側だったらdebuggerを活用するのが一番の近道だと改めて感じた。
- 投稿日:2020-08-24T14:22:36+09:00
[Rails]Faradayのミドルウェアを使ってみる
Faradayのミドルウェアを使ってみたので簡単にまとめます。
Faradayとは?
rubyで書かれたHTTPクライアントライブラリであり、
これを利用してRailsプロジェクトから外部APIにアクセスすることができます。Faradayのミドルウェアとは?
Faradayにはリクエストやレスポンス処理についてカスタマイズできるミドルウェアがあります。
RequestミドルウェアとResponseミドルウェアの2つがあり、これを利用することによってリクエスト時の認証情報を付与したり、通信についてログに書き出すことができます。シンボルを使って以下のように使うことができます。
api_client.rbFaraday.new(url: 'https://test.com') do |builder| # ミドルウェアを使用する builder.request :url_encoded builder.response :logger, Config.logger if Config.enable_logger endRequestミドルウェアについて
Requestミドルウェアはリクエスト送信前にリクエストの詳細を設定することができます。
ここでは2つのミドルウェアを紹介します。
認証ミドルウェアはAuthorizationヘッダーの設定ができ、
UrlEncodedミドルウェアはキーと値のペアのハッシュをURLエンコードされたリクエスト本文に変換することができます。使用例
api_client.rb# 認証ミドルウェア builder.request :basic_auth, "username", "password" builder.request : authorization :Basic, "aut_key" # UrlEncodedミドルウェア builder.request :url_encodedResponseミドルウェアについて
responseミドルウェアはレスポンスを受け取る際にレスポンスの詳細を設定することができます。
ここではLoggerミドルウェアを紹介します。
Loggerミドルウェアを使うと、リクエストとレスポンスの本文およびヘッダーをログに出力することができます。
またフィルター機能もあり、正規表現を利用して機密情報をフィルタリングすることができます。使用例
api_client.rb# Loggerミドルウェア builder.response :logger # フィルター builder.response :logger do | logger | logger.filter(/(password=)(\w+)/, '\1[REMOVED]') endfaraday_middlewareを使うとさらに便利!
faraday_middlewareというgemをいれると使えるミドルウェアがさらに増えます。
例えば、ParseJsonクラスを使って、parser_optionsとcontent_typeを指定すればハッシュキーをシンボルにしてJSONパースすることもできます。
使用例
api_client.rbbuilder.response :json, parser_options: { symbolize_names: true }, content_type: 'application/json'参考ドキュメント
https://lostisland.github.io/faraday/
https://lostisland.github.io/faraday/middleware/list
- 投稿日:2020-08-24T14:22:36+09:00
[Rails]Faradyのミドルウェアを使ってみる
Faradyのミドルウェアを使ってみたので簡単にまとめます。
Faradyとは?
rubyで書かれたHTTPクライアントライブラリであり、
これを利用してRailsプロジェクトから外部APIにアクセスすることができます。Faradyのミドルウェアとは?
Faradayにはリクエストやレスポンス処理についてカスタマイズできるミドルウェアがあります。
RequestミドルウェアとResponseミドルウェアの2つがあり、これを利用することによってリクエスト時の認証情報を付与したり、通信についてログに書き出すことができます。シンボルを使って以下のように使うことができます。
api_client.rbFaraday.new(url: 'https://test.com') do |builder| # ミドルウェアを使用する builder.request :url_encoded builder.response :logger, Config.logger if Config.enable_logger endRequestミドルウェアについて
Requestミドルウェアはリクエスト送信前にリクエストの詳細を設定することができます。
ここでは2つのミドルウェアを紹介します。
認証ミドルウェアはAuthorizationヘッダーの設定ができ、
UrlEncodedミドルウェアはキーと値のペアのハッシュをURLエンコードされたリクエスト本文に変換することができます。使用例
api_client.rb# 認証ミドルウェア builder.request :basic_auth, "username", "password" builder.request : authorization :Basic, "aut_key" # UrlEncodedミドルウェア builder.request :url_encodedResponseミドルウェアについて
responseミドルウェアはレスポンスを受け取る際にレスポンスの詳細を設定することができます。
ここではLoggerミドルウェアを紹介します。
Loggerミドルウェアを使うと、リクエストとレスポンスの本文およびヘッダーをログに出力することができます。
またフィルター機能もあり、正規表現を利用して機密情報をフィルタリングすることができます。使用例
api_client.rb# Loggerミドルウェア builder.response :logger # フィルター builder.response :logger do | logger | logger.filter(/(password=)(\w+)/, '\1[REMOVED]') endfaraday_middlewareを使うとさらに便利!
faraday_middlewareというgemをいれると使えるミドルウェアがさらに増えます。
例えば、ParseJsonクラスを使って、parser_optionsとcontent_typeを指定すればハッシュキーをシンボルにしてJSONパースすることもできます。
使用例
api_client.rbbuilder.response :json, parser_options: { symbolize_names: true }, content_type: 'application/json'参考ドキュメント
https://lostisland.github.io/faraday/
https://lostisland.github.io/faraday/middleware/list
- 投稿日:2020-08-24T09:34:12+09:00
ソースコードを読むための技術(チートシート)
1 概要
聞くところによると業務の8割がソースコードを読む時間らしい。しかし、8割という規模感の割には世間でソースコードの読み方についての議論が活発にされている印象はない上に、体系的かつ順序立てたソースコードの読み方をまとめたWebサイトや書籍も少ない。疑問に思いながらもそれなりに長いことデバッガーを使った読み方・リーダブルコードの内容・Web記事を参照にしてソースコードを読んでいた。
しかし、ソースコードリーディングの方法についての情報がメモアプリ内で散らかってしまい、いつまで経ってもソースコードリーディングの技術が体系的に身についていないと感じた。そのため、本稿では本・Web記事・YouTubeなど媒体を問わず、様々な文献からソースコードを効率的に読む方法をチートシートにしてまとめた。チートシートにする目的は「見返して反復し長期記憶化しやすいようにするため」と「ソースコードリーディングについての知識を体系的にまとめ、情報を一元化することで情報の取り出しを容易にするため」である。また、本稿で想定している言語は私に馴染みのあるRubyとする。
2 コードリーディングにおける階層
コードリーディングには以下の階層があるとのこと。
それぞれ通し番号を以下のように設定する。
- ソースコードとして書かれている各行
- 基本文法
- ライブラリ・フレームワーク
- 設計パターン、データ構造・アルゴリズム、パラダイム・原理原則
- IDE(開発ツール)、デバッガ(デバッグツール)、実行環境
- チームルール
- 業務知識・業務要件・非機能要件(実現したいこと)
この「コードリーディングという行為を構成する知識群」という画像における読解は、通し番号でいうと1→7に。詳細から全体へ向かって読むことをコードリーディングと呼称している。
しかし、これはある程度プログラムを把握し終えた時の読解方法であり、エラーやプロジェクト参画1日目などの場合は7→5→6→4→3→2→1(筆者はこの順番で読みますが、スタイルは自由です)のように全体から詳細に向かって読むのがプログラムを把握するのには向いているのではないかと考える。したがって本稿では、エラーやプロジェクト参画1日目にソースコードを読む場合を想定した順序で記載していく。
余談だが「コードリーディングという行為を構成する知識群」はソースコードを読むという方法を階層化し分類している点で優秀な図だと思う。
それでは次稿からソースコードを読むための技術を詳説していく。
3 目的の設定
読むプログラムが決まった。さてどうするか。なんの方針もなくただ ただ mainから読んでいってもコードが言おうとしていることは理解できないだろう。まずコードを読む目的を明確に決め、それにだけ 集中するようにする。全てを読まなければいけないときでも、パスを分けて部分ごとに読む。(*1)
先述の画像における⑦業務知識・業務要件・非機能要件(実現したいこと)がこれにあたる。「エラーを解決するため」「プロジェクトの理解のため」などが該当し、具体的には「NoMethodErrorになったので解決したい」や「いいね機能を実装したい」などが目的として設定される。
ちなみに筆者は、早く目的を達成するために日頃から業務や自主勉強を通して得た知識を、機能要件・非機能要件に分けてチートシート形式で蓄えている。検索機能を作りたい!()と思った時には機能要件のこれを参照にするし、カラムを変更したい!と思った時には非機能要件のこれを参照にする。一度ハマったエラーなどもチートシート形式で保存をしておくと、同じエラーに遭遇した際に二度手間にならずにURLを参照できる。これは心理的安定にも繋がる。
また筆者は、とりあえずのメモ帳としてはiOS純正のメモ帳やInkDropを使ってチートシートを作成し、外部にアウトプットする時はブログやQiitaなどを使っている。OSSなど読んでよさそうだと思ったソースコードはGitHubに逐一蓄えておくと再利用が可能なのでとてもよい。
4 動的解析
大雑把に言って解析手法は静的な手法と動的な手法に分類できる。 静的な手法とは、ソースコードそれ自体を読むこと。 動的な手法とは、デバッガなどを使って実行時の動きを追うことだ。
基本的に解析は動的解析から始めるのがよい。 静的解析とは、多かれ少なかれ、プログラムの動作を予想することである。 対して動的解析で見るのは事実である。 まず事実を見ておいたほうが方向付けがしやすいし、間違いも減る。 最適化する前にプロファイルを取れ、というのと似ているだろうか。 事件解決はまず現場から、というのでもよい。(*1)らしい。よって、動的手法から解説していく。
また、本稿では「エラーを解決することを目的」として動的解析を行っているが、「プロジェクトの理解が目的」でも同様にしてソースコードを動的解析する。「エラーが起きたら〜」部分を「プロジェクトで気になるソースコードが見つかったら」と適宜、変換してしてほしい。
4.1 デバッガーを使う
Webプログラミングというのはクライアントがパラメーター付きのリクエストを送り、それをサーバーサイドで加工しクライアントにレスポンスするものだと考えている。時にはパラメーターの値を参照にデータベースから値を持ってきて、サーバーサイドで加工しクライアントにレスポンスすることもある。つまり、Webプログラミングはバケツリレー大会であり駅伝大会である。バケツの水やタスキが変数になっただけなのだ。そして、エラーは変数の値が予想と違ったり、大会のルールがロジカルではないなどの原因で発生する。
そのバケツリレーの流れの中でどの地点がエラーになったのかを分析するのがデバッガーである。先述の「1 目的の設定」で決めた目的に合致するエラーの箇所をブラウザ・URL・バックトレースから判別し、プロジェクトファイルから該当のフォルダとファイルを、付与された名前を駆使して類推・探索する。それによってそれっぽいファイル名・関数名・変数名・型名・メンバ名を特定し、特定地点にデバッガーを挿し込んで変数の流れを把握する。
静的な文字情報として脳内で認識していたプログラムをデバッガーを使用することで、一気に動的な風景として脳内で認識することができる。個人的には80年前の写真が突如動画になって動き始めるた時の感動に近いものを感じる。プログラムをイメージで把握することで右脳や空間知覚を使用することができる。記憶容量の大きい右脳に処理の流れをストックできるので、空いた左脳でソースコードの分析ができる。
デバッガーは、IDEであればEclipseやJetBrain系IDEに予め内蔵されているものを使う。そうでない場合はRubyであればbyebugやpryなどのサードパーティ製のライブラリを使うといいだろう。
4.2 ソースコードの処理の流れを日本語や図でメモに記載する
4.1ではバックトレースを読んだりデバッガーを使用し、ソースコードの処理の流れを把握した。処理の流れを把握したらその次は把握した内容を日本語や図でメモなどに記載することをする。
なぜ「ソースコードの処理の流れを日本語に置き換えメモをする」のか。理由は2つある。
1つ目は「ソースコードは抽象的だから」だ。
ソースコードを解読するということは抽象概念を具象概念に落とし込むことであると考えている。天才や業務経験の長いエンジニアであれば抽象を抽象のまま理解しそれを活用することができるが、プログラミングに関して私は凡人であり業務経験も少ない。そのため、抽象概念を具象概念に落とし込み日本語でメモをすることは業務効率、学習、思考の外部記憶装置の面で重要なファクターだと考えている。故に「ソースコードの処理の流れを日本語に置き換えメモをする」ことは重要である。
2つ目は「ソースコードは英語で記載されているから」だ。
まず我々は日本人だ。24時間日本語を使っている。日本語で記載されているものは0秒で理解できるしイメージ理解が容易である。しかし、残念なことにソースコードは英語で記載されている。母国語ではない英語には馴染みがない。英語でGiraffeと言われてもすぐに理解はできないが、日本語でキリンと言われれば0秒で動物のキリンが頭の中にイメージできる。つまり、英語で記載されている単語の意味は0秒で理解できないし、増してやイメージ記憶は日本語に比べて難解だということだ。そのため、英語で書かれた抽象的なソースコードの流れを具体的に日本語や図でメモに記載することは、業務を早くするという面で重要なファクターを持つ。
仮にソースコードを英語のまま理解したいのであれば、英語を英語で理解する習慣を意識的に作り、英語脳を養うことが必要であると考える。しかし、恐らく英語脳習得にはかなりの時間がかかると見ている。ライティングは特に。またプログラミングやマーケティングなど学ぶことも多いため、英語ばかりにリソースを割けないというのも実情だ。そのため、取り急ぎ英語で記載されたプログラムを日本語に変換し、処理の流れを順次メモし整理することは、重要なことであると考える。
以上の理由から、ソースコードの処理の流れを日本語や図で記載することは重要であると考えた。実は3つ目に「処理の流れを俯瞰で理解できる点」というものを用意していたが、詳細の文章をどう書けばいいかわからなくなったため記載を省きました。
4.3 エラー箇所の挙動を確認
4.1でエラーの箇所を、4.2で処理の流れ全体を把握することができた。
デバッグは「ある処理(行)のときに・データ状態はこうなっているはず」という仮説検証の行為だと考えている。
エラーの箇所がなぜ動かないのかはエラーメッセージを読めば大体のあたりがつくが、中にはなぜ動かないのか分からない時がある。そういう時は以下の手法を用いることでエラーを解決できる。3.3ではプロを目指す人のためのRuby入門の11章を参考にさせて頂いた。名著なのでRuby経験者なら一度は耳にしたことがあると思うが、「コードリーディングという行為を構成する知識群」の②基本文法を抑えるという意味で重要なので、気になった方は読んでみまSHOW TIME。
4.3.1 自分で解決する
①エラーメッセージを読む
エラーメッセージをコピペしてググれば大抵の問題は解決する。
②バックトレースを読む
バックトレースとは、大雑把にいうと「メソッドの呼び出し状況を表したデータ」でメソッドがどこから呼び出されたかを調べることができる。railsでいえばApplication Traceを使う。
③ログを調べる
Railsなどのフレームワークにはログを出力するものがあるらしい。
④プログラムの途中経過を確認する
概要
自分の推測と実際の処理動作が正しいのかどうか。変数の推測と実際のズレはどこにあるのかを調べたい。
方法
1. printデバッグやloggerデバッグをし変数を確認
2. メソッドチェーンにtapを仕込み、変数を確認
3. ByebugやIDE標準のデバッガーでブレークポイントを打ち、変数を確認⑤インタラクティブに試す。
メソッドの挙動が自分の想定していたものと違ったり、ライブラリの挙動を調べたりしたい時に行う。
方法
1. irbで簡単なコードを動かす
2. フレームワーク由来のエラーであればscaffoldでエラー箇所を再現する。⑥ライブラリのコードを読む
外部ライブラリの挙動がどうなっているかを知りたい時や、バージョンアップ等で外部ライブラリ自体が動かない原因になっていることがある場合、ライブラリのコードまで降りる必要がある。Rubyはスクリプト言語なので、実行に必要なライブラリはRubyのコードとして手元に存在しているはずである。あとはプロジェクトからライブラリの場所を探すためにMethodクラスのsource_locationメソッドを使うことで調べることができる。なお、戻り値がnilの場合(C言語で実装されているケース)は、Rubyの標準ライブラリのメソッドであることが多い。
また、ライブラリのソースコードはソースコードとして優秀であることが多いので、書き方を学んでGitHubなどに蓄えておくと再利用可能で技術力も向上する。
⑦テストコードを書く
メリット
1. デバッグにかける時間を節約できる
2. これまで作ったプログラムのロジックが壊れていないことを保証するドキュメントの代わりにできる
3. 自身の思考の認知外にある予期せぬエラーを事前に対策できる方法
各言語のテストフレームワークを参照。
⑧パソコンの前から離れる
トイレに行く、飲み物を買いに行く、散歩をする、運動をする、昼寝する、ご飯を食べる、お風呂に入る、早めに寝る。
ことにより、視界がひらけてくるため数時間悩んでいた問題が解決することがある。
4.3.2 他人を頼る
①社内&公式ドキュメント
どちらも1次情報なので正確かつ確実に問題を解決できるものだと考える。
②ネットの情報を参考にする
Qiitaや個人ブログを参照にする。
③issueを検索
issueトラッキングサービスなるものが存在するらしい(初めて知った)。全文英語でよく分からないのが玉に瑕だが、公式のissueなのでエラー解決の選択肢として頭の片隅に置いておくと、困った際に正確かつ確実に問題を解決できるものだと考える。
④誰かに聞く(職場の人、teratail)
個人的には、ある程度の時間調べて悩んで分からなかったらそこが現時点での自分の限界なので、要点をまとめた上で人に聞くことをしたいと思っている。しかし、苦い顔をされることも多いため抵抗がある方法でもある。
5 静的解析
5.1 ドキュメントを読む
そもそもソースコードを読まない
ソースコード読んだら負けかなと思っています。
リファレンスマニュアル、ドキュメント
設計関連の文書・資料
自然言語で書かれた素晴らしいものがたくさんあるはずなので、まずはそちらを読みましょう。そういったものがあまり頼れないとき、そういったもので解決しないときは、しょうがないのでコードを読みます。それでもコメントや識別子の命名を重視して、まずはできるだけ意味的に解釈してみます。
コメント皆無、変数名適当……というケースでは、仕方ありません。他にどうしようもないので、コードロジックを見ていきましょう。
読むのは面倒くさいので、できればやりたくないことの筆頭です。できるだけやらないで済ませる方法を考えます。
初心者だと読解力不足で捗らないということも多々あるでしょうし、よほどの必要性に迫られていなければ「コード読むのは、もう少し上達してからでいいよ」なのでは。
前任者がドキュメントを残しているはずなので、それを読む。
https://teratail.com/questions/147782これも前項と似ていて、まず仕様を知っておこうということ。 また内部構造を解説したドキュメントが付いていたらそれもぜひ見ておきたい。 「HACKING」「TOUR」などという名前のファイルがあったら要チェック。
「コードリーディングという行為を構成する知識群」における⑥チームルールにあたるものだ。
せっかく前任者が工数をかけて自然言語や図でドキュメントを残しているので、あるならドキュメントを先に読解するのがいい。5.2 構造、設計の理解
「コードリーディングという行為を構成する知識群」における④設計パターン、データ構造・アルゴリズム、パラダイム・原理原則にあたるものだ。
5.2.1 ディレクトリ構造を読む
どういう方針でディレクトリが分割されているのか見る。 そのプログラムがどういう作りになっているのか、 どういうパートがあるのか、概要を把握する。 それぞれのモジュールがどういう関係にあるのか確かめる。
5.2.2 ファイル構成を読む
ファイルの中に入っている関数(名)も合わせて見ながら、 どういう方針でファイルが分割されているのか見る。 ファイル名は衰えないコメントのようなものであり、注目すべきである。
また関数名の名前付けルールについてもあたりをつけておきたい。 C のプログラムなら extern 関数にはたいていプリフィクスを 使っているはずで、これは関数の種類を見分けるのに使える。また オブジェクト指向式のプログラムだと関数の所属情報がプリフィクスに 入っていることがあり、貴重な情報になる。(例: rb_str_push)
5.2.3 関数同士の呼び出し関係を把握する
関数名の次に重要な情報。 特に関数の数が多い場合はこれが重要である。 このへんはツールを活用したい。 図にしてくれるツールがあればそれが一番いいが、 なければ特に重要な部分だけでいいので自分で図を書いておくといい。 図に凝る必要はないので、裏紙にざっと描けば十分だろう。
ちなみにこのこの呼び出しの関係を図にしたもののことを コールグラフ (call graph) と言うことがある。 ソースコードに書いてある呼び出し関係を そのまま図にしたのが静的なコールグラフ (static call graph) で、 実際に動作させたときに呼び出した関数だけを書いた図が 動的なコールグラフ (dynamic call graph) である。
ただ、検索した感じでは、日本語の文章だと「コールグラフ」 は暗黙のうちに dynamic call graph を指し、 static call graph は「関数呼び出し関係」と言うことが多いようだ。 だが static と dynamic で対になっているほうがわかりやすいので 筆者は動的コールグラフ・静的コールグラフと呼ぶことにしている。
コールグラフはJavaをやっていた頃にEclipseの機能の1つとしてみた気がします。「Ruby コールグラフ」や「vscode コールグラフ」で調べても出てこない上にあまり詳しく内容を把握していないので、とりあえず頭の片隅に置いておきます。知りたい人は検索してみてください。
5.2.4 デザインパターン
デザインパターンとは「オブジェクト指向において、よく使われる設計をパターン化したもの」「何度も遭遇する似たような問題に関する解法」である。デザインパターンを知っておくことでソースコードを読み書きする際に以下のメリットを享受できる。
メリット
1. プログラムの再利用性が高い
2. 効率的に品質の高い構造を作れる
3. 可読性が高い故に保守性に優れる
4. デザインパターンを知っていれば設計の意図が読めるようになるので、引継ぎする際に保守しやすくなる
5. 転じてデザインパターンを知ることで開発者同士の意思疎通がスムーズになる凡人の凡人による凡人のためのデザインパターン第一幕 Public
5.2.5 アルゴリズムとデータ構造
プログラムなんて、データ構造がどうなっているのか分かれば もう半分勝ったようなものだ。コードを書くときも、 コードに逐一コメントを付けるよりデータ構造 (だけ) を 解説するほうがはるかに役に立つ。 (と、何かの本に書いてあったんだけど、なんだっけ?)
※ 追記:『プログラム書法』だった。以下、同書の p.168 より引用する。 「プログラムに解説をつけるための、もっとも効果的な方法の一つは、 単にデータの割り付けかたをくわしく説明する、というものである。 おもな変数について、その値としてはどんなものが可能かを示し、 それが変って行くようすを説明すれば、それだけでプログラムの解説は、 ずいぶん進んだといってよい。」
ちなみにこの本の原書は 1974 年に出版されている。※ 追記2:『Cプログラミング診断室』でも似たようなコメントを発見した。 以下、同書の p.78 より引用する。 「フローチャートは禁止しましょう。フローチャートは、 制御の流れを「もろ」に書けてしまうのでよくありません。 プログラムは、データを処理するためにあり、データの違いによって 制御の流れが変更されます。あくまでも、データが主体です。 変数、引数などのデータをどう定義するかで、プログラムの組みやすさは 大幅に改良されます。データ構造がどうなっているかの図のほうが、 フローチャートよりはるかに役立ちます。データの意味だけは、 しっかり書きましょう。」
閑話休題。 C でデータ構造を作るならもちろん struct か union を使うはずだ。 そういう重要構造はヘッダファイルで定義されていることが多い。 もちろん内部構造は .c で定義されることもあるし動的に構築される データ構造もあるので、最終的には関数を読んでいかないとわからない。 それでもまずはヘッダファイルを読むべきだろう。ヘッダファイルを 読むときにもやはりファイル名は重要である。例えば言語処理系で frame.h というファイルがあったら、たぶんスタックフレームの定義だ。
データ構造を予測する時は構造体メンバに注目する。構造体の定義中に next というポインタがあればリンクリストだろうと想像できる。 同様に、parent・children・sibling と言った要素があれば十中八九 ツリーだ。
アルゴリズムとデータ構造を実務で使用した経験がないため、これらをどう使うかはよくわかっていない。どうやらビックデータ分析を行う際に多用されるらしい。文献等は以下を読むと理解が深まると考えられる。
STEP1
STEP2
AtCoder:競技プログラミングコンテストを開催する国内最大のサイト
これら以外に何かおすすめがあったら教えてください!
5.3 ソースコードの意味を理解する
5.3.1 単語単位で読む
ソースコードに出現する単語はファイル・関数(メソッド)・型(クラス)・変数・メンバ変数・予約語のどれを指しているのか。どんな書き方をしているかを知る。
① ファイル
Railsなどのフレームワークなら命名規則が存在する。ファイル名でそれがソフトウェアにおいてどんな役割を担っているかをある程度類推できる。
② 関数(メソッド)の種類
標準ライブラリ産のメソッド(使用頻度が高いのでRuby,Railsなどに元々備わっているクラスやそれに付随するメソッド。殿堂入り)
外部ライブラリ産のメソッド(gem)
自作ライブラリ産のメソッド(helper,model,application)
メソッドのオプション
③ 型の種類
integer,string,arrayなど
オブジェクト志向プログラミングでは、型をクラスと呼び、クラスを自身で作成して使うことができる。
オブジェクト志向プログラミングの用語
クラス、オブジェクト、インスタンス、レシーバ、メソッド、メッセージ、状態(ステート)、属性(アトリビュート、プロパティ)
④変数(インスタンス)
変数、メンバ変数、インスタンス変数、クラスインスタンス変数
https://qiita.com/mogulla3/items/cd4d6e188c34c6819709
⑤名前空間を用いたクラスの差別化
クラス名の予期せぬ衝突を防ぐのが目的。
⑥予約語や識別子
「順次処理」「条件分岐」「繰り返し」のような構造を表した単語。
# rubyにおける予約語や識別子 BEGIN class ensure nil self when END def false not super while alias defined? for or then yield and do if redo true __LINE__ begin else in rescue undef __FILE__ break elsif module retry unless __ENCODING__ case end next return until⑦略語の調査
わかりにくい略語があればリストアップしておいて早めに調べる。 例えば「GC」と書いてあった場合、それが garbage collection なのか graphic context なのかでずいぶん話が違ってしまう。 英語だと単語の頭文字をとるとか、母音をなくすとかが多い。 特に対象プログラムの分野で有名な略語は問答無用で使われるので あらかじめチェックしておく。
筆者の記憶にある中から一つ例を挙げよう。 とある Lisp 処理系で、 プログラム全体で「blt」というプリフィクスが使われているのだが、 これが何を表しているのかわからなくて困ったことがある。 これは実は built-in function (組み込み関数) のことであった。 わかってみると単純なことだが、これがわかるのとわからないのでは ずいぶん難易度が違う。
らしい。確かにJavaの案件で略語を見た気がする。分からない場合は
- 詳しい人に聞く
- ドキュメントを調べる
- 検索する
のいずれかの戦略を取る必要があるだろう。
また、自分で変数などをコーディングする際はリーダブルコードを参照にするといい。
5.3.2 どのような目的で設計されたコードなのかを読む
RASISとは、コンピュータシステムに関する評価指標の一つで、「信頼性」「可用性」「保守性」「保全性」「安全性」の5項目をアクロニム(頭文字語)によって表現したもののことである。
業務でWebサービス開発をする際に気をつけたいこと(新卒向け)を基に作成しました。RASISに関しては明るくないため、詳細を知りたい場合は参照元から。
①Reliability(信頼性)
システム障害への強さ「壊れにくさ」システムの故障しにくさを表します。
- 例外設計
- デプロイ/ロールバックの手順を完璧にする
- 死活監視/障害検知をする
- ログファイルを適切に扱う
- バックアップを取る
②Availability(可用性)
必要なタイミングで要求サービスが利用可「サービスが利用できる」度合い。システムが継続して稼働できる能力。
- 単一障害点をなくす
③Serviceability(保守性)(拡張性)
「シンプルなシステム」を志向する。シンプルだと内容の把握やアップデートが簡単。
- テストコード
- リーダブルコードの命名規則に沿った変数名
- バージョン管理
- ドキュメントを残す
- 障害情報をログに残す
- 開発環境を簡単に作る
④Integrity(保全性)
データの一貫性や整合性をさす。データベースのデータについて言及される事が多い。Railsであればモデル層やDBで制約をかけたりする。
- トランザクション制御
トランザクションとは?【13分でわかるDBトランザクション処理】データベース入門講座#4
- DBの制約
「SQLアンチパターン」を避けるためのチェックリスト①(DB論理設計編)
- モデル設計
データモデルの設計とベストプラクティス(第2部) - Talend
モデル設計を適当にやるとどうなるのか - SlideShare
ドメイン駆動設計をわかりやすく - ドメインのモデル設計を手を動かしながら学ぼう
⑤Security(機密性)(安全性)
情報が流出しないようにする。
- SSL化
- パスワードのハッシュ化
- フレームワークの機能を使う
- ストロングパラメーターの設定
- プレースホルダーの設定
5.3.3 歴史を読む
GitHubのコミットログを参照する。開発者の意図がわかるのと、完成モジュールに関わるソースコードがどれなのかが分かりやすい。
6 ちょっと変更して動作させてみる
これは「この段階でやる」という類のものではなくて手法の一つである。 人間の頭というのは不思議なもので、できるだけ身体のいろんな場所を 使いながらやったことは記憶に残りやすい。パソコンのキーボードより 原稿用紙のほうがいい、という人が少なからずいるのは、単なる懐古趣味ではなく そういうことも関係しているのではないかと思う。
そういうわけで、単にモニタで読むというのは非常に身体に残りにくいので、 書き換えながら読む。そうするとわりと早く身体がコードに馴染んでくることが 多い。気にくわない名前やコードがあったら書き換える。わかりずらい略語は メモるだけでなく省略しない語に置換してしまってもよい。
ただし、当然のことだが書き換えるときはオリジナルのソースは別に残し、 途中で辻褄が合わないと思ったら元のソースを見て確認すること。でないと 自分の単純ミスで何時間も悩む羽目になる。
書き換えて動かす
前項と似ているが、こちらは実際にプログラムを動かしてみる。例えば 動作のわかりにくいところでパラメータやコードをちょっとだけ変えて 動かしてみる。そうすると当然動きが変わるから、コードがどういう意味 なのか類推できる。これまた言うまでもないが、オリジナルのバイナリは残しておいて 同じことを両方にやってみるべきである。
変更させて動作確認する行為は、私の経験上プログラミングスキルを高めることができるものだと考えており、特に初心者におすすめである。しかし、変更を元に戻せなくてエラーになって焦ってしまい、エラーが怖いから試さなくなってしまい成長機会を逃すというのを経験している。なので、命綱として変更を取り消すやり方について下記に記していく。
6.1 Git上で作業した内容を取り消す
DBのマイグレーションやファイルなどの作成も消せるので便利
git checkout . git clean -df .6.2 irbなどのインタラクティブな機能で試す
ライブラリやメソッドがどう動いているかを仮説検証できる
rails c --sandbox7 ソースコードを楽しく読みたい(ゲーム感覚で取り組みたい)
- 記録を付ける
- 興味を持っている分野のソースコードを読む
- 良いソースコードを読む
- 仕事でも役立つソースコードを読む
- ちょっと変更して動作させてみる
- バグがないか探してみる
プログラマーが教える、ソースコードを読むための4つの方法!
【新人なるプログラマーへ】ソースコードを読みましょう (2/2)8 終わりに
さしあたってはこれくらいです。なお、外部からのコメントや、自身の成長度合いによってこの文章は随時更新していくつもりです。
- 投稿日:2020-08-24T09:11:11+09:00
[Rails] webpackerでCSSを読み込みたい
railsのver.6以上を使うなら
asset pipeline
ではなくwebpacker
でCSSを読み込みたいと思い、実装したのでメモです。ディレクトリ構成
webpacker のREADME.mdより。
app/javascript: └── packs: # only webpack entry files here └── application.js └── application.css手順
rails new
は完了しているものとします。
rails6より下のversion使ってる人はwebpackerもインストールしましょう。新規フォルダ・ファイル作成
↑のディレクトリ構成に従う。
app/javascript/packsにapplication.css
ファイルを作成エントリーポイント
エントリーポイントは packs/application.js なので、application.cssファイルをimport。
packs/application.jsimport "packs/application.css";application.html.erbを編集
views/layouts/application.html.erb に以下を記述。
おそらくデフォルトだとstylesheets_link_tag
になっているので、そこをstylesheets_pack_tag
に変更すればいいかと。<%= javascript_pack_tag 'application' %> <%= stylesheet_pack_tag 'application' %>CSSを書く
あとはCSSを書くのみです。
次はReact、Vue.jsあたりも導入してみたい。今回は手順だけをメモした感じなので、もう少し詳しく中身を知りたい人は↓が参考になると思われます。
参考
Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)
webpackerでcssとimagesを参照したい
webpacker でページごとにスタイルを分ける
- 投稿日:2020-08-24T02:58:44+09:00
ジャンル別に投稿・チャットできるようなアプリ作ってみた〜苦労したところ〜
一つ前の記事にアプリの概要を投稿しました。
ここからアプリ作成時の苦労したところなどを、記事にしたいと思います。
大体がジャンルページから始まる
- まず、このアプリはジャンルを選択、または自分で作成します
- ジャンルを選択することで、ジャンルページが開きます
- ジャンルページの右側で投稿ができ、左側でジャンル内のチャットグループ検索ができるようになっています
つまり大体のページ移行、データ送信に置いてgenru_idが必要になるのです
これは大体のものがジャンルを親として紐づいている(ジャンル内にある)ことを意味します
![]()
これが色々と邪魔をされました
色々やり方を検索するのですが、そのページ単体だったら簡単にできたものも、ジャンルページを挟んだものだとうまくいかないということがあったのです。
最初はなぜ上手くいかないのかと考えましたが、大体genru_idが抜けていました(笑)
中間テーブルが多かった
下記ER図をご確認ください。
データベース(ER図)
- この中で中間テーブルはいくつあるでしょうか?
A => 3つ(UserGenrusテーブル/UsersGroupsテーブル/Likesテーブル)
実は、もう一つ中間テーブル(hopeテーブル)を作ろうとしたのですが、形がUsersGroupテーブルと同じだった為に、hopeテーブルはintegerを使ってレコード保存してます。なので、実質は4つになります。
特にUsersGroupsテーブルにグループメンバーのidを保存するのが大変でした(笑)
また、中間テーブルを作成するのに、ちょっと間違った作成方法をしてしまったのも大変だった理由ですね
この辺りについては別記事を作成する予定です
いいね機能がJS化しなかった
結果から言えば、JS化しました(笑)
でも、最初はどうしてもJS化しなくて、「なんでだよ〜!」ってなってしまいました
- なぜJS化できたのか
=> JS化とりあえず保留にして別機能を進めたら、そこにヒントがありました!
投稿機能の画像プレビュー化(JSにより)
投稿する前に、画像があっているか確認できるようにプレビュー化したんですが、これが曲者で...
プレビュー化できたと思ったら、枚数上限が上手くいかなかったりとかなり苦戦しました(笑)
感想
今のところ、思いつく苦労したところはこのようなところでしょうか
本当は苦戦したところを詳しくやっていこうと思ったのですが、変更して、一つ一つの機能を記事にしていこうと思います