- 投稿日:2020-04-14T21:54:58+09:00
HTML、CSSで忘れそうだけど押さえておきたいこと
CSS
HTMLと別のファイルで書くのが基本。
大きさの単位
絶対的な大きさとしてpxが主流。しかしスマホの普及により、rem(html要素のフォントを1とした時の相対的な単位)を使用される
色
色は#FFFFFFやrgb(255,255,255)などの方法で表される。
カラーコードでぐぐるとわかるidとclass
idは一度しか定義できず、CSSで読みだすときは#をつける。
classは複数回定義できるが、CSSで読みだすときは.をつける。またclassは複数所属可能なので、class = aaa bbbとしてやるとaaaとbbbに所属できる。優先度
親要素でCSSの内容を記述し、子要素でもCSSの内容を記述したとき子要素の内容が優先される
同じ要素のidのCSSの内容とclassのCSSの内容ならば、idのCSSの内容が優先される詳しくはCSSセレクタの優先度に関してググると良い
ボックスモデル
HTMLの要素はpadding border marginで囲まれている。
HTMLの要素自体の幅、高さをwidth、heightで表すまたその要素に
box-sizing: border-box;
と指定すると、widthやheightをmarignまで含んだ時の大きさに設定することになるインラインとブロック
ブロック要素
ブロックは画面の横幅全体を使い、次の要素は改行された状態になる。
例 h1~h6 p form table ul ol dl li divtext-alignやmarginをいじるやインラインブロックへの変更をすることでブロック要素は配置設定に使用できる(インライン要素は配置設定をしないのは基本。)
インライン要素
インラインは画面の横幅全体を使わず、次の要素は隣にくるようになる。
例 br a img input select textarea strong span上述したがインライン要素は配置設定をしない。
そもそも下記なので配置設定できない。
・大きさ制限できない(width,height)
・marginできない(ホントは左右はできる)
・text-alignできない
・floatできないまたインライン要素の配置を変えたいならば、その要素に親要素を作り、そいつのtext-alignやmarginをいじるやインラインブロックへの変更をすることで配置設定をする。そのためインライン要素から別の要素への変換(display)をする機会はない。
インラインブロック要素
インラインブロックは基本的にブロック要素的な面が強いが、次の要素は隣にくるようになる。ブロック要素だけでは配置設定のバラエティーがなくなるので、ブロック要素からインラインブロック要素に変更することがある。ブロック→インラインブロックのパターンしかない。
重要
以上のことからわかることは
配置変更をしたければ、ブロック要素をいじることで行う。例えば、text-alignやmargin変更、インラインブロックへの変更で横に大きいの並べるとかtext-alignとmargin
私はtext-alignとmargin 0 autoが同じだと思っていた。でも違うのだ。
text-alignはその要素の幅の範囲内(width内)での文字の位置を指定している。つまり文字の位置を指定しているのである。
marginはその要素のmargin(余白)の部分を変更しているので、要素の幅(width)や高さ(height)の範囲には影響を与えない。
要するにtext-alignは要素の中での指定、marginは要素の外側での指定。
marginのあれこれ
同じ方面でmarginがかぶると、marginの相殺(margin同士の足し算ではなく、marginの大きい方を採用する)。marginの相殺と呼ぶ
margin 0 autoで中央ぞろえ
margin-left autoでベタッリ右要素の真ん中に文字をおきたければ
要素の大きさ(幅や高さ)のwidthやheightで行う。要素の大きさを変えても要素内での文字の位置は変わらない。
↓
要素内での文字の位置を変えたければ、text-align: center;で文字を幅でも高さでも真ん中に置くのかを決める。またline-heightで文字自体の高さを決めれるので、要素のheightとline-heightを同じにすることで、完璧に要素の真ん中にもじが来るようになる。範囲指定
範囲を指定するためだけの要素
ブロックで範囲を指定するのがdiv(text-alignやmarginでいじって、指定範囲の位置指定)
インラインで範囲をしてするのがspan(特定の文字の色変更とかに使用)
background
backgroundを使用すると背景を変えれる。backgroundでの範囲はその要素のborderまでの範囲なので、余白をつけたければ,marginを使う。またrepeatを指定すると、永遠にその背景ってことになる。
img
imgはインライン要素なので、pで囲って無理やりブロックにして、配置指定をする。
HTML
<>で囲まれている部分を要素idやnameなどは属性という。
h1やpで囲んで記事を書くが、なくても文字は表示される
imgのalt属性は画像が表示されない時に出る文字、最近はSEOに必要な要素にもなっている?
ulは箇条書き(順序なし)、olは箇条書き(順序あり)、dlは説明って感じ(dlの中にdt(用語)、ddが用語の詳細説明)
表に関して。trで行をざっくり作る、trの中にthやtdを入れて行にマスを作っていくイメージ。thは見出し、tdは左寄せ。
bodyの構造。headerとfooterを頭とお尻に。その間の空間をarticleとして、その中に複数のsection(切りの良いところで、次のセクションに次の内容を書く)を置く。最近はSEO的にも重要だとか
form_tagとform_forの違い。モデルに対して操作を行うならば、form_forを使い、そうでないならばform_tagを使用する。詳しくはrailsで学ぶサーバーサイド
またform_tagでmethodを指定するが、postは見えない状態で、paramsの中に入れて運ぶ感じ。getではURLに?でクエリ(条件)を置いて、送信する。詳しくはrailsで学ぶサーバーサイド。
<a>は外部のURLにアクセスするための要素
<%= link_to %>はアプリ内部のrootingにアクセスするために作られた関数viewでの注意
・お作法としてapplication.htmlには統一レイアウトを置き
yeildという場所を書き、そこに全てのviewの内容が反映される。app/views/layouts/application.html.erb<html> <head> <title><%= full_title(yield(:title)) %></title> <%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> <%= render 'layouts/shim' %> </head> <body> <%= render 'layouts/header' %> <div class="container"> <%= yield %> <%= render 'layouts/footer' %> </div> </body> </html>同様に全てのviewは、別のviewファイルから読みだして自身のviewに反映させてもいい
<%= render '反映させたいviewの場所' %><%= render @microposts %> #これをするとviewの中のmicroposts/_micropost.html.erbを追加することになるこれをパーシャルを追加するという。他にも
<%= render 'shared/error_messages', object: f.object %> #これを行うとshared/error_messagesの方で、f.object(つまりf自身、入力内容自身)を変数objectと設定してパーシャリ川で使用できるという事↓
_error_messages.html.erb<% if object.errors.any? %> #このようにして使用する . . .これも同じような意味
this.erb<%= render partial: "list_footer", locals: {username: @username, listname: @listname} %>この意味はlist_footerパーシャル(view)の中でthis.erbで定義した@usernameをusername、 @listnameをlistnameとして使用できるという事。しかしloacalsを使わずに、@usernameをそのまま使用することも可能。しかしlocalsを使った方がパーシャルを再利用しやすいため使用される。参考文献は部分テンプレートにlocals:を使って変数を渡す時は partial:もつけないとエラー
・erb(rubyの埋め込まれたHTML)
erbでは<% 中身 %>で中身の処理をして、<%= 中身 %>では中身の処理をして結果をHTMLに出す。例
app/views/static_pages/home.html.erb<% provide(:title, "Home") %> #ここで:titleにHomeを代入 <!DOCTYPE html> <html> <head> #ここで読み出して、HTMLに出す <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> </head>provideもyieldも関数で、変数に値を入れる、読み出すの処理をしてくれているということ。
- 投稿日:2020-04-14T21:44:49+09:00
【Rails】商品出品時にカテゴリテーブルのIDと紐づける方法
はじめに
某プログラミングスクールで、フリマアプリのクローンサイトを作成中につまづきました。
内容としては、出品のタイミングでカテゴリを紐づけて投稿したいけど、そのidがうまく機能しないといったもの。困ったもんだ。やりたいこと
商品を登録するitemテーブルに、categoryテーブルの紐付けを行いたい。
categoryテーブルはancestry
というgemを使って作成しています。
ざっくり言うと、categoryテーブルの中で、親カテゴリ、子カテゴリ孫カテゴリが管理されてます。テーブルの構成としてはこんな感じ。
ちょっとわかりにくいかも
nameカラム ancestryカラム 親カテゴリ メンズ、レディースなど null 子カテゴリ アウター、ボトムスなど 1、2など 孫カテゴリ Tシャツ、スラックスなど 1/y、2/yなど
ancestry
カラムがnullのものが親カテゴリ
ancestry
カラムが1だったり2だったりするものが子カテゴリ
ancestry
カラム が1/1だったり、1/2だったり、2/1だったり、2/2だったりするのが孫カテゴリ
こんな風に管理しています。現状
非同期通信でカテゴリーの選択肢は表示できている。
けど・・・送られてきたデータみたらなんかitemのハッシュからcategory_idがハブられてるじゃないのっ!!
おめぇの席ねぇから。状態です。確かに、これじゃ登録できない。
きっとこの子がしっかり皆に受け入れられればうまくいくはず。
解決前の実装
解決する前の状態です。
商品登録画面で、親カテゴリを選択すると、子カテゴリのプルダウンを表示。
さらに、子カテゴリを選択すると、孫カテゴリのプルダウンが表示されるように非同期通信で対応しています。
ここまで頑張ったんだし良い感じに登録してくれないものかね( ꇐ₃ꇐ )解決前のソースコード
itemコントローラー
結論、ここに問題はなかったです。
item_controller.rb#〜〜〜省略〜〜〜 def create @item = Item.new(item_params) if @item.save! # 商品の投稿に成功したらindexに飛ばす処理 redirect_to root_path else # 商品の投稿に失敗したらnewアクションを再度実行new.html.hamlを表示 render :new end end #〜〜〜中略〜〜〜 # 親カテゴリーが選択された後に動くアクション def get_category_children #選択された親カテゴリーに紐付く子カテゴリーの配列を取得 @category_children = Category.find_by(name: "#{params[:parent_name]}", ancestry: nil).children end # 子カテゴリーが選択された後に動くアクション。 ajaxからハッシュで子要素のIDを受け取る{child_id: childId} def get_category_grandchildren #選択された子カテゴリーに紐付く孫カテゴリーの配列を取得 @category_grandchildren = Category.find("#{params[:child_id]}").children binding.pry end private #プライベートメソッドにしたいので、private配下に記述 def item_params binding.pry params.require(:item).permit(:product_name, :price, :condition,:description, :delivery_fee, :shipping_origin, :days_to_ship,:buyer_id, images_attributes: [:image]).merge(user_id: current_user.id, seller_id: current_user.id) end endソースコード上に、binding.pryが書かれているあたり、すごい検証しまくった形跡が残ってますね。
恥ずかしい(笑jsファイル
結論、この子がいじめっこの犯人でした。
category.js$(document).ready(function() { // カテゴリーセレクトボックスのオプションを作成 function appendOption(category){ var html = `<option value="${category.name}" data-category="${category.id}">${category.name}</option>`; return html; } // 子カテゴリーの表示作成 function appendChidrenBox(insertHTML){ var childSelectHtml = ''; childSelectHtml = `<div id="children_wrapper"> <select class= 'SellPage__Information__Box__Inner__Form', id= 'child_category' name="category_id"> <option value="---" data-category="---">---</option> ${insertHTML} </div>`; $('#PullDownCategory').append(childSelectHtml); } // 孫カテゴリーの表示作成 function appendGrandchidrenBox(insertHTML){ var grandchildSelectHtml = ''; grandchildSelectHtml = `<div id="grandchildren_wrapper"> <select class= 'SellPage__Information__Box__Inner__Form', id= 'grandchild_category' name="category_id"> <option value="---" data-category="---">---</option> ${insertHTML} </div>`; $('#PullDownCategory').append(grandchildSelectHtml); } $('#parent_category').on('change', function(){ // 変数"parentCategory"に、プルダウンで選択した値を代入 var parentCategory = document.getElementById('parent_category').value; if(parentCategory != "---"){ $.ajax({ url: 'get_category_children', type: 'GET', data: { parent_name: parentCategory }, dataType: 'json' }) .done(function(children){ $('#children_wrapper').remove(); //親が変更された時、子以下を削除するする $('#grandchildren_wrapper').remove(); var insertHTML = ''; children.forEach(function(child){ insertHTML += appendOption(child); }); appendChidrenBox(insertHTML); }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) }else{ $('#children_wrapper').remove(); //親カテゴリーが初期値になった時、子以下を削除するする $('#grandchildren_wrapper').remove(); } }); // 子カテゴリー選択後のイベント $('#PullDownCategory').on('change', '#child_category', function(){ // カテゴリーの子要素に紐づくIDを取得して、そのIDに紐づく孫要素を取得する。 // option:selected を指定する事で、プルダウンで選択したものの情報を取得できる事になる。 var childId = $('#child_category option:selected').data('category'); if (childId != "---"){ // 自身で作成したget_category_grandchildrenのルーティングへ飛ばす。その際、プルダウンで選択されている子要素のIDも渡す。 $.ajax({ url: 'get_category_grandchildren', type: 'GET', data: { child_id: childId }, dataType: 'json' }) .done(function(grandchildren){ if (grandchildren.length != 0) { $('#grandchildren_wrapper').remove(); //子が変更された時、孫以下を削除するする var insertHTML = ''; grandchildren.forEach(function(grandchild){ insertHTML += appendOption(grandchild); }); appendGrandchidrenBox(insertHTML); } }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) }else{ $('#grandchildren_wrapper').remove(); } }); });Viewファイル
この子もなんも悪い子ではなかったです。
最初から疑ってなかったよ。うん。new.html.haml-# ~~~省略~~~ .SellPage__Information__Box .SellPage__Information__Box__About 商品の詳細 #PullDownCategory.SellPage__Information__Box__Inner .SellPage__Information__Box__Inner__SellText カテゴリー .SellPage__Information__Box__Inner__SellBox 必須 = f.select :category, @category_parent_array, {}, {class: 'SellPage__Information__Box__Inner__Form', id: 'parent_category'}
data-category="${category.id}
を付与しているので、各選択肢のidは付与できている状態。
このdata-category="${category.id}の値をどうにかitem={}の仲間入りをさせたい。
そうすればcategory_id君はいじめから開放されるはず・・・!解決&方法
やっぱり、jsが悪さをしていました
ソースコード書いたのお前じゃん。とか言われたら言い返せませんが、jsが悪さをしてたんです。修正後.js// 子カテゴリーの表示作成 function appendChidrenBox(insertHTML){ var childSelectHtml = ''; childSelectHtml = `<div id="children_wrapper"> <select class= 'SellPage__Information__Box__Inner__Form', id= 'child_category' name="item[category_id]"> <option value="---" data-category="---">---</option> ${insertHTML} </div>`; $('#PullDownCategory').append(childSelectHtml); } // 孫カテゴリーの表示作成 function appendGrandchidrenBox(insertHTML){ var grandchildSelectHtml = ''; grandchildSelectHtml = `<div id="grandchildren_wrapper"> <select class= 'SellPage__Information__Box__Inner__Form', id= 'grandchild_category' name="item[category_id]"> <option value="---" data-category="---">---</option> ${insertHTML} </div>`; $('#PullDownCategory').append(grandchildSelectHtml); }5行目、16行目の記述を変えています。
変更Before/Afterはこんな感じ↓比較.js// After <select class= 'SellPage__Information__Box__Inner__Form', id= 'child_category' name="item[category_id]"> // Before <select class= 'SellPage__Information__Box__Inner__Form', id= 'grandchild_category' name="category_id">
name
の記述を変更しています。
正直理解仕切れているか?というとそうではないです。
item[category_id]
とすることでインスタンス変数@itemの要素の一つとして認識してくれるようです。
正直推測です。ですので、良い子の皆はこの記事を鵜呑みにしないようにしましょう(汗
もし、解説していただける方いたらコメント頂けますと幸いです・・・!ちゃんとitem{ }の中に入れてもらえました。
やっぱり仲良くするのが一番です。さいごに
(しっかり理屈で理解しきれていないのに)記事にして大丈夫なのかな。という内容でした・・・。
有識者の方が解説してくださる事を願っています。最後までお付き合いくださりありがとうございました!
- 投稿日:2020-04-14T20:52:08+09:00
【Rails】単一テーブル継承(STI)について
執筆中
単一テーブル継承(STI)とは
STI(Single Table Inheritance)
同じカラム設計のテーブルを、一つのテーブルにまとめて、継承することで余計なテーブルを増やさず、DRYなテーブル設計にするというもの。
(テーブルが多いですねw)考え方はクラスの継承と同じ!!
例
悪い例
良い例
図にも書いてある通り、authors、category、tagsは擬似テーブルであり、実在はしないテーブルになります。
実在はしないので、データは全てtaxonomiesに保管されます。
Model
article.rbclass Article < ApplicationRecord belongs_to :category belongs_to :author has_many :article_tags, dependent: :destroy has_many :tags, through: :article_tags endtaxonomies.rbclass Taxonomy < ApplicationRecord endauthor.rbclass Author < Taxonomy has_many :articles endcategory.rbclass Category < Taxonomy has_many :articles endtag.rbclass Tag < Taxonomy has_many :article_tags has_many :articles, through: :article_tags end擬似テーブル3つは継承元がTaxonomyになっている点に注意!!
Active Record
[1] pry(main)> Article.joins(:tags).where(tags: { id: 1 }).first Article Load (3.5ms) SELECT `articles`.* FROM `articles` INNER JOIN `article_tags` ON `article_tags`.`article_id` = `articles`.`id` INNER JOIN `taxonomies` ON `taxo nomies`.`id` = `article_tags`.`tag_id` AND `taxonomies`.`type` IN ('Tag') WHERE `tags`.`id` = 1 ORDER BY `articles`.`id` ASC LIMIT 1 ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'tags.id' in 'where clause': SELECT `articles`.* FROM `articles` INNER JOIN `article_tags` ON `article_ta gs`.`article_id` = `articles`.`id` INNER JOIN `taxonomies` ON `taxonomies`.`id` = `article_tags`.`tag_id` AND `taxonomies`.`type` IN ('Tag') WHERE `tags`.`id` = 1 ORDER BY `articles`.`id` ASC LIMIT 1[3] pry(main)> Article.joins(:tags).where(tags: { id: 1 }) Article Load (2.0ms) SELECT `articles`.* FROM `articles` INNER JOIN `article_tags` ON `article_tags`.`article_id` = `articles`.`id` INNER JOIN `taxonomies` ON `taxon omies`.`id` = `article_tags`.`tag_id` AND `taxonomies`.`type` IN ('Tag') WHERE `tags`.`id` = 1 Article Load (0.6ms) SELECT `articles`.* FROM `articles` INNER JOIN `article_tags` ON `article_tags`.`article_id` = `articles`.`id` INNER JOIN `taxonomies` ON `taxo nomies`.`id` = `article_tags`.`tag_id` AND `taxonomies`.`type` IN ('Tag') WHERE `tags`.`id` = 1 LIMIT 11 => #<Article::ActiveRecord_Relation:0x3fe90b9879dc>ここでのポイントはエラーが出力されているかということ。
なぜかというと、
where
は条件通りに絞り込んではくれても使用されるまでは実行されないため、エラーが出力されない。エラー文にWHERE 'tags'の文字がある。
これはtagsテーブルからという条件のもと検索をかけているのだが、単一テーブルを継承したテーブルは擬似テーブルであり、実在しない。
実在しないテーブルから検索するのは無理なのでエラーが出ている。[6] pry(main)> Article.joins(:tags).where(taxonomies: { id: 1 }) Article Load (9.0ms) SELECT `articles`.* FROM `articles` INNER JOIN `article_tags` ON `article_tags`.`article_id` = `articles`.`id` INNER JOIN `taxonomies` ON `taxon omies`.`id` = `article_tags`.`tag_id` AND `taxonomies`.`type` IN ('Tag') WHERE `taxonomies`.`id` = 1 => []これでok
ちなみにどういうSQLが走るかだけ見たい時は
to.sql
を使う。[7] pry(main)> Article.joins(:tags).where(tags: { id: 1 }).to_sql => "SELECT `articles`.* FROM `articles` INNER JOIN `article_tags` ON `article_tags`.`article_id` = `articles`.`id` INNER JOIN `taxonomies` ON `taxonomies`.`id` = `artic le_tags`.`tag_id` AND `taxonomies`.`type` IN ('Tag') WHERE `tags`.`id` = 1"テーブル周り弱すぎなのでSQLに励みます。
- 投稿日:2020-04-14T20:04:57+09:00
AWS 課金回避方法
AWS無料期間について
AWSのアカウントを作成してから12ヶ月間、以下のアプリケーションは無料で利用できます。
※12ヶ月が過ぎてしまうと課金が始まってしまうので注意が必要。
Amazon EC2
750時間/月 (t2.microインスタンスの使用もこれに含む)まで
Amazon S3:
5GBの標準ストレージ、20,000件のGETリクエスト、2,000件のPUTリクエスト
Elastic IPアドレス
実行中のインスタンスに関連づけられたElastic IPアドレスを1つだけ
インスタンスに紐付いていないElastic IPアドレスは全て課金対象
IAM
IAMユーザーの一時的なセキュリティ認証情報を使用して他のAWSサービスにアクセスするときのみ料金が発生2つ目以降のインスタンスを作成する際の課金回避法
インスタンスを作成し直す際や、別のアプリケーションをデプロイするためには別インスタンスを立ち上げることになります。以下の手順を踏めば課金は解除できるでしょう。
手順
1.Elastic IPアドレスを解放する
2.(すでに作成済みで、これ以上必要の無い場合)紐付いているS3バケットを削除する
3.インスタンスを削除する1.Elastic IPアドレスを解放する
1.AWSアカウントのルートユーザーにログインする
※IMAユーザーの場合、以下のようなエラーが出ると思います。
2.Amazon EC2 コンソールを開く
3.Elastic IP アドレスを選択し、[Actions]、[Release addresses] の順に選択します。
※もし、解放ができない場合は、アドレスの関連付けの解除を押してからアドレスの解放を押してください。
以上で、1.Elastic IPアドレスを解放は完了です。2.(すでに作成済みで、これ以上必要の無い場合)紐付いているS3バケットを削除する
1.AWS マネジメントコンソールにサインインし、Amazon S3 コンソールを開きます。
2.[バケット名] リストで、削除するバケットの名前の横にあるバケットアイコンを選択し、[バケットを削除する] を選択します。※バケットにオブジェクトが含まれている場合は、[This bucket is not empty (このバケットは空ではありません)] というエラーアラートの [バケットを空にする設定] リンクを選択し、[バケットを空にする] ページの指示に従って、バケットを空にしてから削除します。次に、[バケットを削除する] ページに戻り、バケットを削除します。
以下のような状態になれば2.に戻って削除に入ります。
3.[バケットを削除する] ダイアログボックスで、削除するバケットの名前を確認のために入力し、[確認] を選択します。3.インスタンスを削除する
インスタンスを停止するとEIastic IPアドレスは関連づけられなくなるので、あらかじめEIastic IPアドレスを解放し課金されないように気をつけましょう。
1.ナビゲーションペインで [Instances] を選択し、インスタンスを選択します。2.[Actions] を選択して [Instance State] を選択し、[Stop] を選択します。
[Stop] が無効になっている場合は、インスタンスが既に停止しているか、またはルートボリュームがインスタンスストアボリュームです。(インスタンスを停止すると、インスタンスストアボリューム上のデータは消去されます。インスタンスストアボリュームのデータを保持するには、このデータを永続的ストレージに必ずバックアップしてください。)
停止、終了など選択できると思うので削除したい場合は終了を押してください
停止の場合はEIastic IP(住所のようなもの)を削除していないと課金が発生してしまいます。3.確認ダイアログボックスで [Yes, Stop] を選択します。
※インスタンスが停止するまで、数分かかる場合があります。
- 投稿日:2020-04-14T19:29:25+09:00
[rails]ストロングパラメータ受け取り時の400 bad requestの例と理由考察
どんな事例
パラメータは投げ込めているのに受け取ることができないといったエラーでした。下記に詳細を示します。
エラー文(ターミナル上)の様子
Completed 400 Bad Request in 70ms (ActiveRecord: 32.8ms) ActionController::ParameterMissing (param is missing or the value is empty: delete_images_ids): app/controllers/items_controller.rb:163:in `delete_image_params' app/controllers/items_controller.rb:62:in `update'原因の記述とパラメータが投げ込まれている様子
app/assets/javascript/item_edit.js$('#edit_item').on('submit', function(e){ // 通常のsubmitイベントを止める e.preventDefault(); // images以外のform情報をformDataに追加 var formData = new FormData($(this).get(0)); var url = $(this).attr('action') // 削除画像がない場合は便宜的に0を入れる if (clickdelete_registered_images_ids == gon.imageids) { formData.append("delete_images_ids[ids][]", 0) console.log(formData); // 削除画像で、のidをformDataに追加していく } else { clickdelete_registered_images_ids.forEach(function(delete_image_id){ formData.append("delete_images_ids[ids][]", delete_image_id) console.log(formData); }); } // 新しく追加したimagesがない場合は便宜的に空の文字列を入れる if (new_image_files.length == 0) { formData.append("new_images[images][]", " ") // 新しく追加したimagesがある場合はformDataに追加する } else { new_image_files.forEach(function(file){ formData.append("new_images[images][]", file) }); } $.ajax({ url: url, type: "PATCH", data: formData, contentType: false, processData: false, }) });パラメータを受け取れていない様子
app/controllers/items_controllerdef delete_image_params #binding.pryをした結果は下記に示します params.require(:delete_images_ids).permit(ids:[]) endFrom: /Users/kontatomoya/projects/freemarket_sample_62d/app/controllers/items_controller.rb:162 ItemsController#delete_image_params: 161: def delete_image_params => 162: binding.pry 163: params.require(:delete_images_ids).permit({ids:[]}) 164: end [1] pry(#<ItemsController>)> params => <ActionController::Parameters {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"8Ld4lh2Zdf9r6wXZ9judJrqSMedhtgIdq2BqSJUtLurJqoHxVEWYWUiCjceulnnyXyl+4h99mEsAh213iYXtfQ==", "item"=>{"name"=>"お供", "text"=>"kkkkk", "category_id"=>"799", "condition_id"=>"4", "deliverycost_id"=>"1", "pref_id"=>"2", "delivery_days_id"=>"2", "price"=>"20000"}, "delete_images_ids"=>{"ids"=>["20", "22"]}, "new_images"=>{"images"=>[" "]}, "controller"=>"items", "action"=>"update", "id"=>"2"} permitted: false>上記のように欲しい値は
"delete_images_ids"=>{"ids"=>["20", "22"]}
という形で投げ込まれていましたが受け取ることができませんでした解決方法
最終的な決着としてどうなったかと言いますと、問題の根本は結局何かわからず、次の方法で迂回して解決を行いました。
①受け取り方をrequire(:item)に含めること
②ajax通信をやめるて通常のhtml通信のsubmitで送信すること(javascriptからではなくhtmlからデータを拾う形とすること)理由考察
最終的には原因究明までは至りませんでしたが、原因の可能性は4つ感じており、今後の自分と、同じエラーで困っている方のお力になれればと思い記述させていただきます。結局は解決できていないため、可能性として目を通していただければと思います。
1.投げ入れた値が許可済みのものではなかった
ajax通信でjavascriptのformDataに配列名とその中身をappend(追記)して投げ込みませんでしたがRailsガイドの該当箇所(4.5.1 許可済みスカラー値)の中にformDataはないので弾かれてしまった。
(参考)Railsガイドの該当箇所(4.5 Strong Parametersの箇所をご覧ください)
https://railsguides.jp/action_controller_overview.html2.一つのform_withに対して、送るモデルの数が2つ以上になってしまった
params.require(:item).permit(そのほかのもの,ids:[])
にまとめたら動いたため3.ajax通信で送る時に余計なデータが入った時に400エラーが起きるらしい
これに関しては手がかりはないのですが、検索すると出てくるらしいです
4.
.require()
の中に入るシンボルがモデルに定義されていないと動かないこれは1番のRailsガイドを見ると
.require()で投げ込むことができるモデルは〜
という記述があるのでこれも一つ可能性があります。今の例も一応該当はしています最後に
とりとめない記事になってしまったのですが、何かのきっかけにしていただければ幸いです。
- 投稿日:2020-04-14T19:02:56+09:00
heroku apps は 5個までらしいです。
Ruby on Rails チュートリアル 5.1(第4版)を学習中。
学習途中におきた出来事をつらつら備忘録として書いているチラシの裏のようなもの。
Qiita見切り発車。たぶん動くからリリースしようぜって偉い人が言ってた。
見にくさも技術も良くなっていくと思います。たぶん。知らんけど。$heroku createcreateコマンドを実行したら以下のとおり言われてしまった。
▸ You've reached the limit of 5 apps for unverified accounts. Delete some apps or ▸ add a credit card to verify your account.(訳)
ver未確認のアカウントのアプリ数の上限5に達しました。
一部のアプリを削除するかaccountクレジットカードを追加して、アカウントを確認します。無料のうちはアプリを5つ以上は作れないらしい。
$heroku apps上記コマンドで、現在作成したappを表示できた。
hmbrsnmhrgk mysterious-atoll-47707 pure-hollows-45904 radiant-harbor-62133 rails-tutorial-nmhrgk……確かに5個作成していた。
$heroku destroy --app hmbrsnmhrgk(アプリ名)古いappを1つ削除してみる。
▸ WARNING: This will delete ⬢ hmbrsnmhrgk including all add-ons. ▸ To proceed, type hmbrsnmhrgk or re-run this command with ▸ --confirm hmbrsnmhrgk(訳)
▸警告:これにより、すべてのアドオンを含む⬢hmbrsnmhrgkが削除されます。
proceed続行するには、hmbrsnmhrgkと入力するか、次のコマンドでこのコマンドを再実行します
▸-hmbrsnmhrgkを確認しますhmbrsnmhrgkを入力してみた。
$heroku appsmysterious-atoll-47707 pure-hollows-45904 radiant-harbor-62133 rails-tutorial-nmhrgk5個あったappが4個に減っている。
削除に成功したようだ。$heroku create無事新しいappを作成できたよ。
ちゃんちゃん。
- 投稿日:2020-04-14T18:49:58+09:00
【Rails】order
取得した値を並び替えるメソッドになります。
orderとは
create_table "posts", force: :cascade do |t| t.string "content" t.datetime "created_at", null: false end通常、上記のような
Postモデル
だとデータベースに格納される値は昇順になります。:id => 1 :created_at =>3.days.ago :id =>2 :created_at =>1.days.ago :id =>3 :created_at =>Time.zone.nowただし、実際のアプリケーションであれば投稿を行う際に時間などは降順で表示されます。
そこでorder
を使用します。以下は
order
を使用したサンプルになります。class Post < ActiveRecord::Base default_scope -> { order(created_at: :desc) } enddefault_scopeは読んだままデフォルトのスコープを定義するメソッドです。ここで定義されたスコープは全てのクエリに適用されます。
※ クエリ:データベースさんに対する命令文のこと。
スコープ:影響範囲created_atの後の:descはdescendingの略で降順という意味です。
- 投稿日:2020-04-14T14:26:29+09:00
【Rspec】エラー文 undefined local variable or method `response' for RSpecの解決法
以下のようにテストを書いたところ、次のエラー。
gyms_conotoller_spec.rbrequire 'rails_helper' describe "indexメソッドが動く" do expect(response).to render_template :index endエラー文
undefined local variable or method `response' for RSpec::ExampleGroups::Gyms::Index:Class原因
そもそもfeature_specにはresponseオブジェクトがないから。
解決法
controller_specでrenderオブジェクトを確認するテストをすればOK.
responceメソッドはcontroller_specのためだけにあります。
- 投稿日:2020-04-14T14:24:47+09:00
active_hashを用いて都道府県データを表示してみた
なぜactive_hashを使うのか?
都道府県のデータなど、変更される可能性が低いものをハッシュとして管理できるため、わざわざテーブルを作成する必要が無い
というメリットがあります。
今回の内容はフリマアプリでactive_hashを利用した際に、つまずいた点があったので、
備忘録としてまとめたものです。環境
Rails 5.2.3
Ruby 2.5.1導入方法
gem active_hashをインストール
gem 'active_hash'bundle installを実行。
active_hashのインストールが完了しましたので
続いてモデルの作成に移ります。
モデルを作成
まずはaddress.rbを作成します。
rails g model adderss作成したアドレス内に以下の内容のように編集する
class Address < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to_active_hash :prefecture endこの記述により、後ほど作成するprefecture.rbとアソシエーションを組むことができ、prefecture.rbの内容を利用することができます。
rails db:migrateを行った後、prefecture.rbを作成します。
内容は以下のように編集しましょう
class Prefecture < 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: '埼玉県'}, {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: '沖縄県'} ] endこれで、モデル内の準備は完了です。
次はコントローラー内を編集します。
コントローラーを編集
今回はフリマアプリなので、products_controller.rb内のnewアクション内に記述を行います。
def @address = Prefecture.all endこれにより@addressでprefectureを利用できるようになりました。
rails cでも確認してみると
下は見切れてしまっていますが、取り出せているようです。
続いてviewファイル内のformの記述を編集し、ユーザーが都道府県を選択できるようにします。
viewファイル内を編集、都道府県データが表示されるか確認
= form_for @product do |f|の記述から、= f.text_field, = f.text_areaといった形で入力フォームを作成しているので
f.collection_select :prefecture_id, Prefecture.all, :id, :nameフォームを設置したい箇所に上記を記述し、投稿してみる。
binding.pryを用いて、prefectureが取り出せているか確認してみることに
しっかり取り出せている...
formの記述に誤りがあるのかもしれないとのことで、以下に変更
= f.select :prefecture_id, Prefecture.all, :id, :nameまたしても、エラー...
form以外に怪しい点は無いか探してみると
なんとproductテーブル内にprefectureの入力を反映させるprefecture_idが存在していなかった。
これでは、中身があっても入れ物が無いようなもので、エラーが起きてしまうのも無理はない
no method errorは文字通りmethodが定義されていないというエラーでもあれば、もっと単純にコンピュータ側から「これがなんだかわからない」というメッセージでもあったことを知る。
無事、実装完了。
終わりに
初めてactive_hash実装にあたって苦労する箇所は多かったが、使いこなすメリットは大きいと感じました。
ただエラー内容は単純なものだったこともあり、もう少し早く解決できた内容だったかなと...
まだまだ初学者の粋を脱することは難しいようだ
- 投稿日:2020-04-14T12:09:56+09:00
#Rails + rake で複数行のDescriptionを設定する、表示する ( display multiple lines description / describe with heredoc newlines )
rake
lib/tasks/some_job.rake
desc <<~DESC This job is so Great job DESC task some_job: :environment do # do something enddescribe
descript オプションで全行表示される
bin/rails --describe some_job rails some_job This job is so Great jobtasks
一覧だと一行だけ表示される
bin/rails --tasks ... rails some_job # This job ...Original by Github issue
- 投稿日:2020-04-14T11:59:01+09:00
コーディング未経験のPO/PdMのためのRails on Dockerハンズオン、Rails on Dockerハンズオン vol.15 - Deploy to Heroku
はじめに
こんにちは!
今回は実際にここまで作ったアプリケーションをデプロイして公開してみます!
Herokuというサービスを使って、今まで開発してきたアプリケーションをデプロイしてみます。ちなみに今回のハンズオンではGitコマンドを使用します(Herokuにソースコードをアップロードするときに使う)。各自の環境に合わせてGitコマンドをインストールしてから進んでください!(ググって!)
前回のソースコード
今回使うソースコードです(今まで作ってきたアプリです)。今回はこれをデプロイしていきますので、お手元にアプリがない方はこちらからダウンロードしてください。
Heroku
アプリを公開するには、サーバーが必要ですね。今ではサーバーも物理的にサーバーを用意するのではなくAWS、Azure、GCPなどのクラウドサービスのIaaSを利用して用意するのが一般的になってきているかと思います。
HerokuはIaaSのもう一段上のPaaSに位置付けられるサービスです。
IaaS的なサーバーの利用はもちろん、アドオンとしてデータベースやCDNなどのサービスを簡易に利用できたり、ソースコードからビルド・デプロイを簡単に行なってくれたり(ビルドパック)、IaaS+αなことを提供してくれます。
時間などの制約などはありますが、個人の開発者でも無料でアプリケーションを公開できたりするのもオススメポイントです。
Herokuに関しては公式ドキュメントも整っていますし、色々な方が記事を書いたりしているので、この記事での説明はこのくらいにしておきましょう。では実際にHerokuにアプリケーションをデプロイしていきます!
システム構成
HerokuではDynoと呼ばれるコンテナにアプリケーションをデプロイすることができます。
コンテナといっても今まで開発してきたようにDockerfile
を書いてdocker-compose.yml
を書いてというようなことは必要ありません。(似たようなファイルを書くことで意図した通りのデプロイをさせる方法などはありますが、今回はスコープ外とします。)
今まで作ってきたRailsアプリケーションは、Dyno上で動作させましょう。また、ここまでPostgreSQLをDockerコンテナで起動させてきました。
こちらはHerokuのアドオンとして、Heroku Postgresが用意されているのでこちらを利用しましょう。
Heroku Postgresも容量の制約はありますが無料でも利用が可能です。Herokuアカウントを作成する
さて、では実際にデプロイを始めていきましょう。
まずはHerokuアカウントを作成します。サインアップページからアカウントを作成してみてください。Heroku CLIをインストールする
コマンドラインからHeroku操作をするために公式インストールサイトからHeroku CLIをインストールしましょう。
Heroku CLIでログインする
$ heroku login heroku: Press any key to open up the browser to login or q to exit:でサインインページがブラウザで開くと思うので、先ほど作ったアカウントでサインインしてください。
コマンドライン側に以下の表示が出ていればサインイン成功です!Logged in as [アカウントのメールアドレス]Gitを使えるようにしておく
Herokuにデプロイする際はHerokuで用意してくれるGitレポジトリにアプリをpushします。
なので、Gitを使える準備をしておく必要があります。もし現段階でGitリポジトリを作成していない場合は、ローカルレポジトリを作っておきましょう。$ git initHeroku Appを作成する
まずHerokuでAppを作ります。ワークスペースみたいなものですね。
Appの名前はサブドメインにも使われるので、お好みの名前を指定してあげるといいです。ただし、他のアカウントも含めて重複は許されないのでかぶっちゃった時は諦めましょう。$ heroku create test-app
test-app
がアプリ名です。アプリ名をつけないとHerokuが適当な名前をつけちゃうのでちゃんとつけてあげてくださいね。Heroku Add-onsのPostgresを追加する
Railsアプリケーションが使うPostgresをHeroku Add-onsから追加します。
今回は無料で済ませたいのでhobby-dev
プランを使います。$ heroku addons:create heroku-postgresql:hobby-devHerokuにアプリをデプロイする
先ほどもお話した通り、HerokuへのデプロイはHerokuのGitリポジトリにソースコードをpushするだけです。
リモートリポジトリの情報は先ほどheroku create
をした時にheroku
という名前で登録されています。前回のソースコードからソースコードをダウンロードしている場合は、ローカルブランチが
vol.14
になっている思います。(これはgit branch
コマンドで確認できます。)
Herokuのmasterブランチにpushするために、ローカルブランチもmaster
に切り替えてあげましょう。$ git checkout -b masterそして、pushします。
$ git add . $ git commit -m "First Commit" $ git push heroku master Enumerating objects: 300, done. Counting objects: 100% (300/300), done. Delta compression using up to 8 threads Compressing objects: 100% (191/191), done. Writing objects: 100% (300/300), 305.11 KiB | 30.51 MiB/s, done. Total 300 (delta 108), reused 241 (delta 82) remote: Compressing source files... done. remote: Building source: remote: remote: ! Warning: Multiple default buildpacks reported the ability to handle this app. The first buildpack in the list below will be used. remote: Detected buildpacks: Ruby,Node.js remote: See https://devcenter.heroku.com/articles/buildpacks#buildpack-detect-order remote: -----> Ruby app detected remote: -----> Installing bundler 1.17.3 remote: -----> Removing BUNDLED WITH version in the Gemfile.lock remote: -----> Compiling Ruby/Rails remote: -----> Using Ruby version: ruby-2.6.5 remote: -----> Installing dependencies using bundler 1.17.3 remote: Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment remote: The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. remote: Fetching gem metadata from https://rubygems.org/............ remote: Fetching rake 13.0.1 remote: Installing rake 13.0.1 remote: Fetching concurrent-ruby 1.1.6 ... remote: -----> Compressing... remote: Done: 92.2M remote: -----> Launching... remote: Released v6 remote: https://test-app.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/test-app.git * [new branch] master -> masterなんかいろいろログが出てきますが、注目は『
Ruby app detected
』や『Compiling Ruby/Rails
』ですね。
HerokuのGitレポジトリにpushした段階でビルドパックが作動しています。
ソースコードから勝手に「Ruby使ってますよね?Railsですよね?ビルドしてデプロイしときますね!」という感じで勝手にビルド&デプロイしてくれます。最後の方に「
https://test-app.herokuapp.com/ deployed to Heroku
」とありますね。
このURLでデプロイしたよ、ということです。デプロイされたところでDBをマイグレートしてあげます。
$ heroku run rails db:migrateこのように
heroku run
コマンドをつけることでpushしたアプリを起動してコマンドを発行します。
正確にはOne-Off Dynosが使われているみたいなので、今WebからアクセスできるDynosとは別にコマンド発行用に一時的に起動されているみたいです。(なのでログファイルとかを見るようではない)ここまで完了したら先ほどデプロイしたよと言われたURL(
https://test-app.herokuapp.com/
)にアクセスしてみましょう。お、デプロイされとる。動く、動くぞ。
ちょっといろいろと触ってみましょう。今までコーディングしてきた機能がちゃんと動くなっているはずです。デプロイできました。クソほど簡単でした。
まとめ
今回はここまでで終了です!
いままで作ってきたアプリをHerokuにデプロイすることができました!そういえば、HerokuはGitHubとの連携がいい感じらしいです。GitHubにpushしたらHerokuに自動でデプロイするように設定したりできるってことですね。
GitHubにはCIツールのGitHub Actionsがあったりもするので、テストコードをパスしたらHerokuにデプロイみたいなことも意外と簡単にできるのかも(やったことない)。
もちろん他のGitサービスやCI/CDサービスでもできます。僕はGitlab CI使ってやってましたー。ここまででDocker上でRailsアプリを開発する環境を作って、TDDでコーディングをして、Herokuにデプロイする、という一連の流れを体験できましたね!
あとは自分の作りたいものを作ってみるのが一番勉強になります!走り出す準備はできたはず!あとはひたすらに作って調べて作って調べてたまに記事とか書いてアウトプットして!開発を楽しみましょう!本業も忘れずに!次回はチャレンジング!僕も勉強中なんですが、せっかくDockerをやってきたのでKubernetesだとどうやってデプロイ・公開できるの?というのを体験してみたいと思います!「こいつ、動くぞ!」
ではまた次回!
後片付け
今回はHerokuアプリをデプロイしたままになっちゃっているのでちゃんと後片付けしましょう。
まずHeroku Appを削除しちゃいます。
$ heroku apps:destroy --app test-app --confirm test-appこれでAdd-onsも含めて削除されました。
Herokuアカウントもいらない場合はWebサイトのダッシュボードから削除できます。詳しくは公式サイトを。本日のソースコード
本日は特にソースコードを更新していないので、ないです。
Other Hands-on Links
- 投稿日:2020-04-14T11:38:21+09:00
RailsアプリにCircleCI2.1でCI/CDを導入する設定ファイル【初心者向け】
こんにちは、ペーパーエンジニアのよしこです。
自作RailsアプリにCircleCIを使ってCI/CDを導入してから数週間が経過して、導入当初と比較して動作が安定しました。
私の場合、CircleCI公式ドキュメントを基本として自分の環境を構築しましたが、
当時はCircleCI version: 2.1の新機能※ に対応した他環境の設定ファイルを参考にしたいと思ってました。※ CicleCI vertsion: 2.1の新機能
orbs / commands / executors機能の説明は次の連載記事が分かりやすかったです。
「エンジニアのためのCI/CD再入門」連載一覧そこで今回、CI/CDを導入したい初心者エンジニア向けに、CircleCI2.1に対応した設定ファイルを公開したいと思います。
環境
Ruby 2.6.3 Rails 5.1.6 PostgreSQL 12.2 CircleCI 2.1 デプロイ先はHerokuCI/CDの全体像
$ git push origin HEAD
でCircleCI作動
- bundle(依存関係)のインストール・リストア
- 静的コード解析 RuboCop
- テスト実行 RSpec
- Herokuにデプロイ(masterブランチのみ)
設定ファイル(コメントなし)
./.circleci/config.yml
に記述。
次項にコメント(個人メモ)ありver.config.ymlversion: 2.1 orbs: ruby-orbs: sue445/ruby-orbs@1.6.0 heroku: circleci/heroku@1.0.1 workflows: build_test_and_deploy: jobs: - build - rubocop_job: requires: - build - rspec_job: requires: - build - deploy: requires: - rubocop_job - rspec_job filters: branches: only: - master executors: default: working_directory: ~/repository docker: - image: circleci/ruby:2.6.3-stretch-node environment: BUNDLE_PATH: vendor/bundle RAILS_ENV: test extended: working_directory: ~/repository docker: - image: circleci/ruby:2.6.3-stretch-node environment: BUNDLE_PATH: vendor/bundle PGHOST: 127.0.0.1 PGUSER: postgres RAILS_ENV: test - image: circleci/postgres:12-alpine environment: POSTGRES_USER: postgres POSTGRES_DB: app_name_test jobs: build: executor: default steps: - checkout - bundle-install rubocop_job: executor: default steps: - preparate - run: name: 静的コード解析を実行(RuboCop) command: bundle exec rubocop rspec_job: executor: extended steps: - preparate - run: name: DBの起動まで待機 command: dockerize -wait tcp://localhost:5432 -timeout 1m - run: name: DBをセットアップ command: bin/rails db:schema:load --trace - run: name: テストを実行(RSpec) command: | mkdir /tmp/test-results TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \ circleci tests split --split-by=timings)" bundle exec rspec --profile 10 \ --format RspecJunitFormatter \ --out test_results/rspec.xml \ --format progress \ $TEST_FILES - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-results deploy: executor: heroku/default steps: - checkout - heroku/install - heroku/deploy-via-git commands: bundle-install: steps: - ruby-orbs/bundle-install: bundle_clean: true bundle_extra_args: '' bundle_gemfile: Gemfile bundle_jobs: 4 bundle_path: vendor/bundle bundle_retry: 3 cache_key_prefix: v1-bundle-dependencies restore_bundled_with: true preparate: steps: - checkout - bundle-install設定ファイル(個人メモあり)
config.yml# CircleCI 2.1 を使用 version: 2.1 # 公開されているCircleCI設定を読み込む。version: 2.1以上 orbs: ruby-orbs: sue445/ruby-orbs@1.6.0 heroku: circleci/heroku@1.0.1 # CI/CD工程の全体像 workflows: build_test_and_deploy: jobs: - build - rubocop_job: requires: - build - rspec_job: requires: - build - deploy: requires: - rubocop_job - rspec_job filters: branches: only: - master # 実行環境 executors: default: # コマンドを実行するディレクトリ(Gitリポジトリ名)を指定 working_directory: ~/repository docker: - image: circleci/ruby:2.6.3-stretch-node environment: # デフォルトはBUNDLE_PATH=/usr/local/bundleで設定。上書きが必要 BUNDLE_PATH: vendor/bundle RAILS_ENV: test extended: working_directory: ~/repository docker: - image: circleci/ruby:2.6.3-stretch-node environment: BUNDLE_PATH: vendor/bundle PGHOST: 127.0.0.1 # config/database.ymlの内容と一致させる PGUSER: postgres RAILS_ENV: test # $ psql -V で確認したバージョンと合わせる。-alpineが軽量版のため望ましい - image: circleci/postgres:12-alpine environment: # config/database.ymlの内容と一致させる POSTGRES_USER: postgres POSTGRES_DB: app_name_test # 各工程の定義 jobs: build: executor: default steps: # ソースコードを作業ディレクトリにチェックアウトする特別なステップ - checkout # 依存関係とバンドルの処理 - bundle-install rubocop_job: executor: default steps: - preparate # 静的コード解析を実行 - run: name: 静的コード解析を実行(RuboCop) command: bundle exec rubocop rspec_job: executor: extended steps: - preparate # DBの起動まで待機 - run: name: DBの起動まで待機 command: dockerize -wait tcp://localhost:5432 -timeout 1m # DBをセットアップ - run: name: DBをセットアップ command: bin/rails db:schema:load --trace # テストを実行 - run: name: テストを実行(RSpec) command: | mkdir /tmp/test-results TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \ circleci tests split --split-by=timings)" bundle exec rspec --profile 10 \ --format RspecJunitFormatter \ --out test_results/rspec.xml \ --format progress \ $TEST_FILES # テスト結果を保存 - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-results deploy: executor: heroku/default steps: - checkout - heroku/install - heroku/deploy-via-git # 処理など。version: 2.1以上 commands: # 依存関係の処理(Orbsを利用) bundle-install: steps: - ruby-orbs/bundle-install: bundle_clean: true bundle_extra_args: '' bundle_gemfile: Gemfile bundle_jobs: 4 bundle_path: vendor/bundle bundle_retry: 3 cache_key_prefix: v1-bundle-dependencies restore_bundled_with: true # 各jobの準備工程 preparate: steps: - checkout - bundle-install参考ドキュメント(日本語)
構築から少なくとも導入初期までは公式ドキュメントがオススメです。
CircleCIの用語やCI/CDの全体像を把握するのに役に立ったネット記事です。参考程度に。
Orbsの導入は、CircleCIドキュメントやGitHubなどを参考にするほうがオススメです。
参考までに私の記事もどうぞ。
既存RailsアプリにCircleCIを導入した手順
RailsアプリにDockerとCircleCIを導入した際、DB周りでエラーになったときの対処法
- 投稿日:2020-04-14T11:19:17+09:00
RESTとRESTfulについてまとめてみた
はじめに
Railsの技術書終盤に差し迫っているのですが、ついにラスボスと出会いました。
そのラスボスとは、、、『RESTfulなインターフェイス』とはRESTの特徴を備えたルートの事を指す。
という一文。
頭の中ハテナだらけになり、コテンパンにされました、、、??なので、今回は〜
『RESTfulなインターフェイス』と『REST』の理解できるようになりラスボスを倒すのが目的です!??RESTを理解するために
この後説明していきますが、RESTはWebサービスの設計概念のため、具体的にこう!というよりかは、抽象度高く理解しておいた方が、他の言語などでも適用しやすくなるので良いみたいです??
なので、自分の中でしっかり落とし込むより、ほわ〜っと理解できればオッケ〜?♀️?♂️
RESTとは?
まずはRESTから倒していきます?
RESTは「リソース」を扱うための考え方
なに(リソース)をどうする(HTTP)を表現するための考え方
※リソースはWebページや記事などの情報のことを指します。
そして、リソースはそれぞれのURIを持っていて、そのURIとHTTPメソッドの組み合わせで、どんなCRUD処理をするのかをあらかじめ紐づけています!
RESTfulとは?
ラスボス退治まであと少し!?
『RESTfulなインターフェイス』とはRESTの特徴を備えたルートの事を指す。
簡単に言うとRESTのルールに基づいて設計されたインターフェイス(接続部分)のこと。
RESTfulなインターフェイスを使うと、統一感があってわかりやすいURLをつくることができます!?
簡単にまとめると『このURIとHTTPだったら、このCRUD処理』(=REST)というセットを決めておけば使いやすくできるからこのルールにしたがってルートの設計しましょう~!という考え方で設定されたルートのこと??
RESTfulなインターフェイスと定義するためには?
Railsではresourcesメソッドを使用するとRESTfulなインターフェイスと定義することができます!しかも自動で!??
routes.rbRails.application.routes.draw do resources :users endこのようにresourcesメソッドを使うと、、、
Prefix Verb URI Pattern Controller#Action users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy上記のようなRESTfulなインターフェイス(ルート)が完成します!
※Verb = HTTPのことを指す。
※updateするときのHTTPがなぜ2つあるのか気になった方はこちらへどうぞ!まとめ
REST
RESTは「リソース」を扱うための考え方の1つ。HTTPメソッドとURIのセットでCRUD処理を表す。
RESTful
RESTのルールに基づいて設計されたインターフェイス(接続部分/ルート)のこと。
resourcesメソッド
RailsでRESTfulなインターフェイスと定義するためのメソッド
以上です!最後までみていただきありがとうございました!
今回は正直めっちゃまとめるの苦労しました〜?
- 投稿日:2020-04-14T11:00:45+09:00
#Rails ( ActiveRecord ) で alias / alias_method がエラーを起こすので alias_attribute を使う
Class
class User < ApplicationRecord alias_attributes :some_name, :name endrun example
User.last.name # Alice User.last.alias_name # AliceError case ( alias / alias method )
alias foo_alias_name name # NameError: undefined method `name' for class `User (call 'User.connection' to establish a connection)' alias_method :bar_alias_name, :name # NameError: undefined method `name' for class `User (call 'User.connection' to establish a connection)'Why?
- alias / alias_method が使えないのは評価順、ActiveRercord的なメソッド生成タイミングの問題と思われる
- ActiveRecord によってメソッド生成されたあとにOpenClass すると alias をつけることも出来る様子
Open class
Before ActiveRecord Model methods occurclass User alias alias_name name end # NameError: undefined method `name' for class `User'Open class
After ActiveRecord Model methods occurUser.first # Maybe ActiveRecord method occurs in this timing class User alias alias_name name end User.first.alias_name # => "Alice"Original by Github issue
- 投稿日:2020-04-14T10:37:23+09:00
【Rails】date_selectを使ってみた
学習メモです
タスクに期限を設定したくてカレンダーだと面倒なので日付選択が簡単なdate_selectを使ってみた。
型
html.slim
= f.date_select(:カラム名, use_month_numbers: true, start_year: Date型で最初の日付, end_year: Date型で終了の日付(interger型なので+5などでつければOK), default: 何も入力しなかった場合の値, date_separator: '区切りかた')実際に書いたコード
= f.date_select(:start_at, use_month_numbers: true, start_year: Date.today.year, end_year: Date.today.year + 5, default: Date.today, date_separator: '/')今paramsになにが来ているのかbinding.pryで見てみる
<ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"e12hjdLpjd/M5iuOGCkPaK5gZE6oR5US4HLybq1zKMNP178svzeGhjg6y2a3Fu6XnrlFuIKnkKtJgvIFqs6rkQ==", "task"=>{"name"=>"", "description"=>"", "start_at(1i)"=>"2020", "start_at(2i)"=>"2", "start_at(3i)"=>"30", "priority"=>"low", "completed"=>"doing"}, "commit"=>"Create Task", "controller"=>"tasks", "action"=>"create"} permitted: false>ちゃんと"start_at(1i)"=>"2020", "start_at(2i)"=>"2", "start_at(3i)"=>"30"がきていたのでこれをsaveする。
controllerのcreateアクションで
time = Date.new(params[:task]["start_at(1i)"].to_i,params[:task]["start_at(2i)"].to_i,params[:task]["start_at(3i)"].to_i) if taime.save redirect_to :index else render :new endこれはparamsに入っているhashから要素を取り出しDateクラスの引数に入力してインスタンスを生成している。
- 投稿日:2020-04-14T10:13:20+09:00
railsのversion確認でエラーが出た時
$gem install rails -v 5.2.3
でinstallして
$rails -v
でversion確認するとRails is not currently installed on this system. To get the latest version, simply type: $ sudo gem install rails You can then rerun your "rails" command.んんんん、さっきインストールしたよな?
ま、この通りやってみるか
$sudo gem install rails
実行また同じエラーが出ました
色々試してみました
http://tomoprog.hatenablog.com/entry/2017/02/03/015936
https://qiita.com/arashida/items/ae982df5e534fd4bc97a
上を参考にさせてもらって試してみました
けど全く同じエラー
そうだ、passを確認
$which rails
~/.rbenv/shims/rails
$which ruby
~/.rbenv/shims/rails
うん、全く同じ
rubyのversionを確認
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]
出るやんどういう事?解決策
色々調べた結果、アプリを再起動してみた
そして
$rails -v
すると
Rails 5.2.4.1
結構しょうもなかったが、色々勉強させてもらった
- 投稿日:2020-04-14T04:18:02+09:00
LINE BOTで位置情報を使って天気予報を取得する
railsで位置情報で天気予報を取得できるBOTを作成したので、APIの搭載の仕方をご紹介します。今回はXML形式で取得した場合です。
LINEBOTの作り方はこちらを参照いたしました。
https://qiita.com/takashico/items/edb6050a8e54dd137148apiはこちらから取得いたしました。
https://openweathermap.org/apiの搭載はこちらの記事を参考にいたしました。
https://yoheikoga.github.io/2016/08/14/weather-in-the-area-now-on-by-gps-module/
http://tokin-kame.hatenablog.com/entry/2015/05/31/105245ライブラリーを記載
controller.rb
require 'line/bot' require 'open-uri' require 'kconv' require 'rexml/document'XMLファイルから必要なものを取得
URLからXMLを取得しパースしていきます。
OpenWeatherMapは属性の中に必要な情報が記載されていたため、情報の取得に苦労しました。controller.rb
# 省略 when Line::Bot::Event::Message case event.type when Line::Bot::Event::MessageType::Location # LINEの位置情報から緯度経度を取得 latitude = event.message['latitude'] longitude = event.message['longitude'] appId = "取得したAPI KEY" url= "http://api.openweathermap.org/data/2.5/forecast?lon=#{longitude}&lat=#{latitude}&APPID=#{appId}&units=metric&mode=xml" # XMLをパースしていく xml = open( url ).read.toutf8 doc = REXML::Document.new(xml) xpath = 'weatherdata/forecast/time[1]/' nowWearther = doc.elements[xpath + 'symbol'].attributes['name'] nowTemp = doc.elements[xpath + 'temperature'].attributes['value'] case nowWearther # 条件が一致した場合、メッセージを返す処理。絵文字も入れています。 when /.*(clear sky|few clouds).*/ push = "現在地の天気は晴れです\u{2600}\n\n現在の気温は#{nowTemp}℃です\u{1F321}" when /.*(scattered clouds|broken clouds|overcast clouds).*/ push = "現在地の天気は曇りです\u{2601}\n\n現在の気温は#{nowTemp}℃です\u{1F321}" when /.*(rain|thunderstorm|drizzle).*/ push = "現在地の天気は雨です\u{2614}\n\n現在の気温は#{nowTemp}℃です\u{1F321}" when /.*(snow).*/ push = "現在地の天気は雪です\u{2744}\n\n現在の気温は#{nowTemp}℃です\u{1F321}" when /.*(fog|mist|Haze).*/ push = "現在地では霧が発生しています\u{1F32B}\n\n現在の気温は#{nowTemp}℃です\u{1F321}" else push = "現在地では何かが発生していますが、\nご自身でお確かめください。\u{1F605}\n\n現在の気温は#{nowTemp}℃です\u{1F321}" end } # 省略最後に
OpenWeahterMapは降水確率がないので、簡易的な天気予報しか作れませんでした。もう少し精度の高い天気予報APIが欲しいですが、おそらく有料になってくると思います。
LINE BOTは一度形ができてしまえば、意外と簡単に作ることができました。
ぜひ、皆さんもオリジナルボット作り試してみてください。
- 投稿日:2020-04-14T02:45:50+09:00
画像有り投稿を検索する(画像有フィルター)
実装したいこと
画像が投稿されている投稿に絞って一覧表示できるようにします。(imageカラムの中身が存在するレコード一覧を取ってくる)
indexページ
indexページの適当な場所に以下を追記します。
tweets/index.html.erb<%= form_tag({controller:"tweets",action:"index"}, method: :get) do %> 画像有りフィルター<%= radio_button_tag("image", "image") %> <%= submit_tag '?' %> <% end %>ラジオボタンをクリックして、送信すると、tweetsコントローラーのindexアクションに「image」というパラメーターが送られます。それを、コントローラーにて後述のような処理をすることで、画像有り投稿の一覧を取得し表示できます。
コントローラー
tweetsコントローラーのindexアクションに以下のように書きます。
tweets_controller.rbdef index if params[:image] @tweets = Tweet.where.not(image: nil) else @tweets = Tweet.all end endややこしいですが、imageカラムの中身が存在しないもの以外のレコードを取ってこいという命令になってます。(カラムの中身が存在するものを取ってこいというストレートな書き方の情報が、意外と見当たらないんですよね~)
また、検索機能などと組み合わせる場合は、if文が入れ子になります。もしくは、あまりおすすめしませんが、検索機能付きのif文の下に追記する形でも問題なく動くっちゃ動きます。
以下参考tweets_contoroller.rbdef index if params[:search] @tweets = Tweet.where("content LIKE ? ",'%' + params[:search] + '%') else @tweets= Tweet.all end if params[:image] @tweets = Tweet.where.not(image: nil) end end
- 投稿日:2020-04-14T02:17:32+09:00
kaminariをカスタマイズ
ページネーションはkaminariというgemを用いることが多いかと思います。
参考
https://qiita.com/residenti/items/1ae1e5ceb59c0729c0b9機能面は実装済みという前提で話します。
kaminariを実装すると初めは以下のように見にくいデザインになっているかと思います。
上記リンクのように、bootstrapを適用するという方法もありますが、リンクホバーなど自分でカスタマイズしたいですよね。
kaminariのviewファイルを作成
ターミナルにて以下を打ち込んでください。
rails g kaminari:views defaultこれによりviewフォルダの中にkaminariのview一覧が作成されました!これはそのままにして次に進んでください!
日本語化
kaminariのgemのデフォが英語になっているので、これを日本語環境に変える必要があります。まず、config/applicationファイルをに以下を追記します。
config/application.rbconfig.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]一行目は、日本語環境を適用、二行目は複数のlacaleファイル(後述)が適用されるコードになります。
全体としては以下のようになっているかと思います。
config/application.rbrequire_relative 'boot' require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module Geektwitter class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.1 config.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s] # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. end endこれで日本語化が適用されます。
次にconfig/localesフォルダーにkaminari_ja.ymlを作成してください。(ja_ymlファイル中に例えばcreated_atの日本語化が既に書いてある場合、他の日本語化は本ページのように別のファイルを作成する必要があります。)
その中に以下をコピペします。
config/locales/kaminari_ja.ymlja: views: pagination: first: "« 最初" last: "最後 »" previous: "‹ 前へ" next: "次へ ›" truncate: "..."英語を日本語に変えました!
カスタマイズ
次に該当のcssファイルに以下を追記しましょう。
tweets.css// paginate .pagination{ margin: auto; width: 50%; display: flex; justify-content: flex-start; } .pagination span{ background-color: rgba(158, 158, 158, 0.4); text-align: center; width: 50px; border: solid 1px #344963; color: rgb(197, 20, 159); transition: .3s; -webkit-transform: scale(1); transform: scale(1); } .pagination span:hover{ transition: .3s; -webkit-transform: scale(1.1); transform: scale(1.1); } .pagination span a:hover{ transition: .3s; -webkit-transform: scale(1.1); transform: scale(1.1); }※注意※
日本語環境に変えたあと、一度rails sを再起動して下さい。これで良い感じになったかと思います!色合いや位置の調整(width)は、各自変えてみてください!
- 投稿日:2020-04-14T00:22:06+09:00
返信機能実装時、工夫した点 -newアクション-
投稿に対する返信機能を作成する際に、newアクションの実装を少し工夫することで便利にすることができました。
●実装コード
Q&Aサイトを実装しているため、投稿=Question 返信=Answerとなっています。
app/contorollers/answers_controlelr.rbdef new @question = Question.find(params[:id]) @answer = @question.answers.build endconfig/routes.rbget "/answers/:id", to: "answers#new", as: :new_answer resources :answers, except: %i(new)●解説
1.アソシエーションでquestionを取得することができる。
app/contorollers/answers_controlelr.rbdef new @question = Question.find(params[:id]) @answer = @question.answers.build endこうすることで、@answer = @question.answersとすることで、@answer.questionのような形で、questionを取得することができます。例えば下記のように活用したり。
app/contorollers/answers_controlelr.rbdef create redirect_to question_path(@answer.question)2.@questionを取得することで、"answers/new" ページに質問の内容を記載することができる。
get "/answers/:id", to: "answers#new", as: :new_answer resources :answers, except: %i(new)こうすることで、/new/:question_id とすることができます。
この記述がないと、/new.question_id となってしまい、パラメーターを渡すことができません。●まとめ
あくまで自分はポートフォリオを作成している段階のレベル感です。
もっと良い実装方法や誤りがありましたら、指摘して頂けますと大変助かります。