- 投稿日:2020-09-06T23:32:22+09:00
[ruby]標準入力からの値を受け取るには?
- 投稿日:2020-09-06T23:30:15+09:00
Railsチュートリアル テストについて
はじめに
Railsチュートリアルを学習すると、第3章にいかに「自動化テスト」が重要であるか書いてあります。テストの書き方については色々議論の余地があるそうですが、本記事ではテストについて自分なりに抑えていたほうが良いと思われることをまとめました。
テストフレームワークについて
テストフレームワークにもいくつか種類があり、それぞれコード書き方等の特徴があります、ここでは主だった2種類のフレームワークを紹介します。
参考にしました →Railsの人気テストフレームワーク6選!
RSpecとMinitest、使うならどっち?Minitest
・Railsのデフォルトのフレームワーク。
・学習コストは比較的低い。
・Railsチュートリアルでも使用されている。
・処理速度は比較的早いといわれている。
・デフォルトの機能は必要最小限。
・コード量は少なく、シンプルRspec
・自然言語で近い方たちで書かれている。
・利用率が高い。
・慣れるまでが時間がかかる。
・Minitestに比べると処理速度は遅いといわれている。
・デフォルトの機能は豊富。
・コード量は多くて、複雑。どちらも一長一短みたいですね。初心者はMinitestから入って、その後Rspecも学ぶのが一番ですかね。
テストの種類について
参考する文献によって2種類だったり、それ以上だったりしますが今回は2種類のテストについて紹介します。
参考にしました →Rails テスティングガイド
ソフトウェアテスト -wilki単体テスト
関数やメソッドなど小さな単位で行うテスト。モデルやビューヘルパー単体の動作をチェックします。
例:
test/helpers/application_helper_testtest "full title helper" do assert_equal full_title, "Ruby on Rails Tutorial Sample App" endassertとはオブジェクトまたは式を評価して、期待された結果(true)が得られるかどうかをチェックするコードです。
上記はassert_equalがfull_titleが"Ruby on Rails Tutorial Sample App"と完全一致しているかどうか検証しています。test/models/user_test.rbtest "name should not be too long" do @user.name = "a" * 51 assert_not @user.valid? endassert_notとは、assertとは逆にfalseが得られるかどうかをチェックするコードです。
上記はユーザーの名前が51文字の場合、ユーザーが有効でないことを検証しています。統合テスト
プログラムを組み合わせて行うテスト。個々の機能が正しく連動しているか、ユーザーの実際の操作を想定してチェックします。
例:
test/integration/users_login_testtest "login with valid email/invalid password" do get login_path assert_template 'sessions/new' post login_path,params: {session: { email: @user.email, password: "invalid"}} assert_not is_logged_in? assert_template 'sessions/new' assert_not flash.empty? get root_path assert flash.empty? end上記は、有効でないパスワードを入力したユーザーに対するテストを行っています。
1. getメソッドでログインページ(login_path)を生成する。
→get login_path2. ログインページのテンプレートが選択されているか検証する。
→assert_template 'sessions/new'3. login_pathへ有効なメールアドレスと有効でないパスワードを投稿する。
→post login_path,params: {session: { email: @user.email,password: "invalid"}}4. ユーザーがログインしていないことを検証する。
→assert_not is_logged_in?is_logged_in?メソッドは下記により定義されています。
test/test_helper.rb#テストユーザーがログイン中の場合にtrueを返す def is_logged_in? !session[:user_id].nil? end5. ログインページのテンプレートが選択されているか検証する。
→assert_template 'sessions/new'6. エラーメッセージが表示されていることを検証する。
→assert_not flash.empty?7. getメソッドでホームページ(root_path)を生成する。
→get root_path8. エラーメッセージが表示されていないことを検証する。
→assert flash.empty?という流れになっています。
まとめ
正直Railsチュートリアル終えても自力でテストをかけるか自信がないです。また、テストはどこまで厳密にやるかセンスが問われそうですね。数をこなしてなれていくのが大事だと思いました。
- 投稿日:2020-09-06T23:06:56+09:00
【Rails】タグ管理機能(acts-as-taggable-on使用)
- タグ付け機能を実装時の記録。エラーで戸惑ったので、備忘録として。
- acts-as-taggable-on(gem)、UI に Tag-it( jQueryのプラグイン )を使用。
環境
- macOS Catalina
- ruby 2.5.1
- rails 5.0.7.2
- acts-as-taggable-on 6.5.0 ( ※ ActiveRecord のモデルにタグ付けしてくれるGem)
- DB mySQL
【 タグ付け機能 : 登録/削除/表示 】
1. gem導入
- acts-as-taggable-onのGitHub 通りやると、マイグレーションエラー発生。
Gemfilegem 'acts-as-taggable-on', '~> 6.0'ターミナル% bundle install : # マイグレーションファイルをインストールしてね!というメッセージが表示されるので、従う ↓ % rake acts_as_taggable_on_engine:install:migrations # 6個のマイグレーションファイルが作成される Copied migration 20200905143628_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine Copied migration 20200905143629_add_missing_unique_indices.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine Copied migration 20200905143630_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine Copied migration 20200905143631_add_missing_taggable_index.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine Copied migration 20200905143632_change_collation_for_tag_names.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine Copied migration 20200905143633_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine # mySQLの場合は、マイグレーション実行前に、↓ を実行が必要(初期設定のため) % rake acts_as_taggable_on_engine:tag_names:collate_bin % rails db:migrate # それでも、エラー発生(gemのバグ?)
- マイグレーションファイルを修正。
※ 外部キーを削除せずに、インデックスを削除しよーとしてるのが原因みたいなので、インデックス削除前に外部キー部分をコメントアウトで対応。xxxxxxxxx_add_missing_unique_indices.acts_as_taggable_on_engine.rb: AddMissingUniqueIndices.class_eval do def self.up # add_index ActsAsTaggableOn.tags_table, :name, unique: true # remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) # remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx' # add_index ActsAsTaggableOn.taggings_table, # [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type], # unique: true, name: 'taggings_idx' end def self.down # remove_index ActsAsTaggableOn.tags_table, :name # remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_idx' # add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) # add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx' end endxxxxxxxxxx_add_missing_taggable_index.acts_as_taggable_on_engine.rb: AddMissingTaggableIndex.class_eval do def self.up # add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx' end def self.down # remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx' end end【 作成されたテーブルとカラム 】
tags テーブル taggings テーブル name(タグ名) tag_id(tagsテーブルのid) taggings_count(タグの登録数) taggable_type taggable_id tagger_type tagger_id content ※ tagsテーブルは、unique: true(一意性)がかかってる。
例 : acts-as-taggable-onのメソッド
※ 詳細は、リファレンス参照。
【 取得や検索 】
メソッドなど 意味 tags_on(:tags) 全 Article のタグ一覧取得 most_used 登録数が多いタグ least_used 登録数が少ないタグ most_used(10) 登録数が多いタグから取得。デフォルトは20 least_used(10) 登録数が少ないタグから取得。デフォルトは20 tag_counts 全タグのデータ named("タグ名") 完全一致 named_any(["タグ名1", "タグ名2",..]) 完全一致(and) named_like("タグ名") 部分一致 named_like_any(["タグ名1", "タグ名2",..]) 部分一致(or) 例)タグ検索class User < ActiveRecord::Base acts_as_taggable_on :tags, :skills scope :by_join_date, order("created_at DESC") end User.tagged_with(params[:tag]) # タグに紐づくUserのデータを取得 User.tagged_with("タグ1")[0].id # 1 User.tagged_with("タグ1, タグ2")[0].id # 配列ではなく、コンマ区切りの文字列でも可。 # 含むか? User.tagged_with("タグ1").by_join_date User.tagged_with("タグ1").by_join_date.paginate(page: params[:page], per_page: 20) # 完全一致(AND検索) User.tagged_with(["タグ1", "タグ2"], match_all: true) # 条件一致(OR検索) User.tagged_with(["タグ1", "タグ2"], any: true) # 除外(含まないモノを検索) User.tagged_with(["タグ1", "タグ2"], exclude: true)【 登録や削除 】
メソッドなど 意味 tag_list.add("タグ1", "タグ2", ..) 追加 tag_list = 'タグ1, タグ2, ..' 上書き tag_list.remove("タグ1", "タグ2", ..) 削除 save 保存 ([id: 1, name: "タグ1", taggings_count: 1],[id: 2, name: "タグ2", taggings_count: 1]) 2. モデル(アソシエーション)
- タグ付けしたいモデルに、アソシエーションを追加。
Postモデルacts_as_taggable # acts_as_taggable_on :tags の省略 # 参)複数設定も可能↓ acts_as_taggable_on :skills, :interests # @post.skill_list とかが使えるようになる3. コントローラー
- タグ登録のため、ストロングパラメーターに、
:tag_listを追加。- タグ表示のため、アクションも追加。
postsコントローラーdef index @posts = Post.all @tags = Post.tag_counts_on(:tags).most_used(20) # タグ一覧表示 end def show @post = Post.find(params[:id]) @tags = @post.tag_counts_on(:tags) # 投稿に紐付くタグの表示 end : private : def post_params params.require(:post).permit(:title, :content, :tag_list) end4. ビュー
4-1. タグ付け用フォーム
,(デフォルト)で区切ると、複数タグに分割してくれる。投稿ページにタグ付け用のフォーム設置(haml)- form_for @post do |f| : = f.label :tag_list = f.text_field :tag_list, value: @post.tag_list.join(',') -# 参)タグのチェックボックスで選択させたい時 - @tags.each do |tag| = f.check_box :tag_list, { multiple: true }, "#{tag.name}", nil = f.label " #{tag.name}(#{tag.taggings_count})"4-2. タグの表示
タグの表示(haml)- if @tags.present? - @tags.each do |tag| # コントローラーで、登録数の順で20個取得(@tags) = link_to "#{tag.name}(#{tag.taggings_count})", tags_path(tag.name) - else %p 登録されているタグはありません【 タグ検索 】
- タグ(リンク)をクリックすると、posts#indexページに、関連する投稿一覧を表示する。
postsコントローラーdef index : @tags = Post.tag_counts_on(:tags).order('count DESC') # 全タグ(Postモデルからtagsカラムを降順で取得) if @tag = params[:tag] # タグ検索用 @post = Post.tagged_with(params[:tag]) # タグに紐付く投稿 end end
tagged_with("タグ名"): 絞り込み検索するメソッド。
クリックされたtag情報を取得し、tagged_with("タグ名")で検索。同じタグを持つ投稿を取得できる。リンク付きのタグ(haml)- @tags.each do |tag| = link_to "#{tag.name}(#{tag.taggings_count})", posts_path(tag: tag.name)タグに紐付く投稿一覧の表示(haml)- if @post.present? %h1 #{@tag} に関連する投稿 - @post.each do |post| = post.user.name = post.name【 UIを整える (Tag-it) 】
- Tag-itは、タグ付け用UIを提供する jQueryのプラグイン。
- GitHub:jquery-ui-rails、tag-it-rails、tag-it
1. 導入
- tag-it のGitHubから、
Clone→Download ZIPをクリック。- ファイルを解凍し、JSとCSSディレクトリ内に、格納。
・ cssフォルダ内の jquery.tagit.css 、 tagit.ui-zendesk.css → app/assets/stylesheets
・ jsフォルダ内の tag-it.js、tag-it.min.js → assets/javascripts- gem導入。
Gemfilegem 'jquery-ui-rails' # Tag-itは、 jQuery UI を使う2. 設定
- Tag-it 、 jQuery UI を読み込むための設定。
application.js//= require jquery //= require jquery_ujs //= require jquery-ui //= require tag-it //= require_tree . // turbolinks(ページ読み込みの高速化の役割)は、ページリロードしないとjQueryが発火しないので、削除(無効化)。application.scss@import "reset"; @import "font-awesome-sprockets"; @import "font-awesome"; @import "jquery.tagit"; // 記述の順番に注意 @import "tagit.ui-zendesk"; // こっちを後に書かないと、タグ削除( x )が表示されない :3. tag-itを読み込む(jQuery)
- ページ更新で、tag-itイベントを発火させる。
- 入力ごとに、placeholderの表示を変更する↓↓
※ ヘルプメッセージが表示された( .ui-helper-hidden-accessible クラスが メッセージ表示置き場に設定されてる模様 )ので、ひとまず、display: none;で、非表示にした。tag-itの読み込み(jQuery)// ページ更新でtag-it発火 $(document).ready(function() { $(".tag_form").tagit({ // 指定のセレクタ( 今回は、:tag_list の text_field )に、tag-itを反映 tagLimit:10, // タグの最大数 singleField: true, // タグの一意性 // availableTags: ['ruby', 'rails', ..] 自動補完する一覧を設定できる(※ 配列ならok)。今回は、Ajax通信でDBの値を渡す(後述)。 }); let tag_count = 10 - $(".tagit-choice").length // 登録済みのタグを数える $(".ui-widget-content.ui-autocomplete-input").attr( 'placeholder','あと' + tag_count + '個登録できます'); }) // タグ入力で、placeholder を変更 $(document).on("keyup", '.tagit', function() { let tag_count = 10 - $(".tagit-choice").length // ↑ と同じなので、まとめた方がいい。 $(".ui-widget-content.ui-autocomplete-input").attr( 'placeholder','あと' + tag_count + '個登録できます'); }); // 参:placeholderの書き換え方法 $(".input").attr('placeholder','書き換え後のテキスト'); // 参:placeholderの削除方法 $(".input").removeAttr('placeholder');
$(セレクタ).tagit()(jQuery)で、イベント発火すると、haml上は、
・ タグ入力フォーム(text_field)に、name属性: post[tag_list] が追加される( ※ post_params(コントローラー)で、:tag_list を許可してるので、タグ登録できるようになる )。id名、class名も追加される。
・ 入力フォーム内に、ul、li が追加される。tagitイベント発火によるタグ入力フォームの変化(haml).input_form = f.text_field :tag_list, value: @post.tag_list.join(","), class: "tag_form tagit-hidden-field" name: "post[tag_list]" id: "post_tag_list" # tagitイベントで、class名、name、id名が追加される -# tagitイベントで、追加される↓↓ %ul.tagit.ui-widget.ui-widget-content.ui-corner-all %li.tagit-new = f.text_field, class: "ui-widget-content ui-autocomplete-input", autocomplete: "off", placeholder: "あと10個登録できます" # autocomplete="off" 自動入力の無効化4. タグのインクリメンタルサーチ
4-1. ルーティング
- 新規登録(id情報なし)、編集ページ(id情報あり)で、Ajax通信したい。
- htmlで取得する予定がないので、jsonをフォーマットにしとく。
routes.rbresources :posts, expect: [:index] do get 'get_tag_search', on: :collection, defaults: { format: 'json' } get 'get_tag_search', on: :member, defaults: { format: 'json' } end4-2. コントローラー
- nameカラムが
params[:key]から始まる、Tagsテーブルのレコードを全取得(※ :keyは、jQueryで定義した入力値。Ajaxで送ってるモノを取得)。postsコントローラーdef get_tag_search @tags = Post.tag_counts_on(:tags).where('name LIKE(?)', "%#{params[:key]}%") end4-3. jbuilder
- コントローラーで定義した、@tagsのnameカラムのみ取得。
views/posts/get_tag_search.json.jbuilderjson.array! @tags do |tag| json.name tag.name end # [{name: "タグ名1"}, {name: "タグ名2"}, ..] の型で取得してる4-4. jQuery
- タグフォームの入力値を取得し、Ajaxで送信 → コントローラーでDB検索 → jbuilderで所望データを取得 → jQueryでTag-itの availableTags に渡す。
jQuery(Ajax通信部分)$(document).on("keyup", '.tagit', function() { : // Ajaxで、タグ一覧を取得 let input = $(".ui-widget-content.ui-autocomplete-input").val(); // 変数inputに、入力値を格納 $.ajax({ type: 'GET', url: 'get_tag_search', // ルーティングで設定したurl data: { key: input }, // 入力値を:keyとして、コントローラーに渡す dataType: 'json' }) .done(function(data){ if(input.length) { // 入力値がある時のみ let tag_list = []; // 空の配列を準備 data.forEach(function(tag) { // 取得したdataのnameを配列に格納 tag_list.push(tag.name); // 1つずつ追加。 tag_list = ["タグ名1", "タグ名2", ..] }); $(".tag_form").tagit({ availableTags: tag_list }); } }) });
- 投稿日:2020-09-06T22:38:00+09:00
【Ruby on Rails】投稿機能の非同期通信、ajax化
目標
開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina前提
流れ
1 gem 'jquery-rails'を導入
2 非同期通信にしたい箇所を部分テンプレート化
3 remote: trueを記述
4 js.erbファイルを作成
5 controllerのリダイレクト先を削除うまくいかない場合は、
ターミナルでActionView::Template::Errorが表示される場合が多く、
大体は変数の定義忘れが多いと思います。
あとはid名やrenderの記述を間違わなければ、うまくいくはずです。gem 'jquery-rails'を導入
Gemfilegem 'jquery-rails'ターミナル$ bundle installapp/assets/javascript/sapplication.js//= require rails-ujs //= require activestorage //= require jquery <--追加 //= require turbolinks //= require_tree .部分テンプレート化、 remote: trueの追加
今回は新規投稿画面の非同期通信を行っていきます、
app/views/posts/new.html.erb<h1>Posts#new</h1> <span>現在ログイン中のユーザー:<%= current_user.name %></span> <--- form_for を form_withに変更し、, data: {remote: true}を記述 ---> <%= form_for(@post, url: posts_path) do |f| %> <--- ---> <div> <%= f.label :タイトル %><br> <%= f.text_field :title, autofocus: true %> <-- idを追加 </div> <div> <%= f.label :中身 %><br> <%= f.text_area :body %> </div> <div><%= f.submit "投稿する" %></div> <% end %> <table> <thead> <tr> <th>投稿者名</th> <th>タイトル</th> <th>本文</th> <th></th> <th></th> <th></th> </tr> </thead> <tbody> <-- idを追加 <--- ここから ---> <% @posts.each do |post| %> <tr> <td><%= post.user.name %></td> <td><%= post.title %></td> <td><%= post.body %></td> <td><%= link_to "詳細", post_path(post) %></td> <td><%= link_to "編集", edit_post_path(post) %></td> <td><%= link_to "削除", post_path(post), method: :delete %></td> </tr> <% end %> <--- ここまでを部分テンプレート化 ---> </tbody> </table>修正した記述が以下の通り。
app/views/posts/new.html.erb<h1>Posts#new</h1> <span>現在ログイン中のユーザー:<%= current_user.name %></span> <%= form_with model:[@post], data: {remote: true} do |f| %> <div> <%= f.label :タイトル %><br> <%= f.text_field :title, autofocus: true, id: "text" %> </div> <div> <%= f.label :中身 %><br> <%= f.text_area :body %> </div> <div><%= f.submit "投稿する" %></div> <% end %> <table> <thead> <tr> <th>投稿者名</th> <th>タイトル</th> <th>本文</th> <th></th> <th></th> <th></th> </tr> </thead> <tbody id="post"> <%= render 'posts/new', posts: @posts %> </tbody> </table>
部分テンプレートは以下のファイルを作成し、@postsをローカル変数に変更。
削除ボタンにもremote:trueを記述。app/views/posts/_new.html.erb<% posts.each do |post| %> <tr> <td><%= post.user.name %></td> <td><%= post.title %></td> <td><%= post.body %></td> <td><%= link_to "詳細", post_path(post) %></td> <td><%= link_to "編集", edit_post_path(post) %></td> <td><%= link_to "削除", post_path(post), method: :delete, remote: true %></td> </tr> <% end %>js.erbファイルを作成
app/views/create.js.erb$('#post').html("<%= j(render 'posts/new', posts: @posts) %>"); $("text_area").val(""); $("#text").val("");app/views/destroy.js.erb$('#post').html("<%= j(render 'posts/new', posts: @posts) %>");
補足
$('#post').html("<%= j(render 'posts/new', posts: @posts) %>"); は
render部分のhtmlを更新するという意味です。
$("text_area").val(""); はform_withのtext_areaを空白に
$("#text").val(""); はform_withのtext_fild(idで指定済み)を空白に
html以外にもappendなども使えるのでおすすめです
controllersのリダイレクト先を削除
app/controllers/posts_controller.rbdef create @posts = Post.all <---追加 @post = Post.new(post_params) @post.user_id = current_user.id if @post.save redirect_to new_post_path <---削除 else render :new end end def destroy @posts = Post.all <---追加 @post = Post.find(params[:id]) @post.destroy redirect_to request.referer <---削除 end修正後。
app/controllers/posts_controller.rbdef create @posts = Post.all @post = Post.new(post_params) @post.user_id = current_user.id if @post.save else render :new end end def destroy @posts = Post.all @post = Post.find(params[:id]) @post.destroy end
- 投稿日:2020-09-06T22:10:07+09:00
Rails 6で認証認可入り掲示板APIを構築する #1 環境構築
はじめに
Rails APIに関して、チュートリアルがあまり見当たらなかったため作成しました。
なお、筆者自身もRails API触り始めて半年程度なので、誤っている点などあればご指摘ください。Rails APIモードを使い、掲示板APIを構築してみようと思います。
なお、フロントエンドは本チュートリアルに含みません。
API完成後、ご自由なものを選択してください。対象読者
- Ruby on Railsチュートリアルを1周し、Railsアプリケーションの実装方法や用語がなんとなく分かっている方
- モノリシックなRailsではなく、RailsのAPIサーバを構築したい方
RailsのCRUD操作に関する最低限の知識はないと、追いつくのは難しいかもしれません。
最終的な環境・Gem
AWS Cloud9
PostgreSQL 9.5.15
Ruby 2.7.1
Ruby on Rails 6.0.3.2Gem
シリアライザ:active_model_serializers
認証:devise_token_auth
認可:pundit
ダミーデータ:Faker
静的コード解析:rubocop
テスト:RSpec, FactoryBot
(記事執筆中なので、今後増減の可能性あり)Cloud9環境の構築
まずはAWSアカウントをお持ちでない場合、アカウントを取得しましょう。
本記事ではアカウントを所持していること前提として解説をします。分かりやすい名前と説明を入れ作成します。
2ページ目は基本的に全てデフォルトで問題ないでしょう。構築に数分かかるので放置します。
完了したらこんなIDE画面が表示されます。Rubyのバージョンアップ
最初からRubyもRailsも入っていますが、バージョンが古いですね。
$ ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux] $ rails -v Rails 5.0.0記事執筆当時の安定バージョンは2.7.1なので上げましょう。
$ rvm install 2.7.1 ... $ rvm use 2.7.1 $ ruby -v ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]無事上がりました。
参考:【AWS】Cloud9でRubyのバージョンをアップデートする方法(rvm使用)
Railsのバージョンを上げる
こちらも、記事執筆当時最新が6.0.3に対し、インストールされているのが5.0.0とだいぶ古いので上げましょう。
$ gem install rails ... $ rails -v Rails 6.0.3.2簡単ですね。
postgreSQLを入れる
なぜpostgresをこのタイミングで入れるか?
Railsのデフォルト開発DBはSQLiteですが、Amazon LinuxのCloud9環境だとSQLiteのバージョンが低くアプリケーションが作れません。バージョン上げがなかなかしんどいので、今回はPostgreSQLを使います。
また、SQLiteはその名の通り機能的にもliteなので、他のRDBMSに比べてできないことも多く、無駄に詰まることがあるので、それを回避するのも要因の一つです。
まずインストールします。
$ sudo yum install postgresql95-devel postgresql95-server postgresql95-contrib $ psql --version psql (PostgreSQL) 9.5.15次に初期化と起動、ユーザー作成。Permission deniedは無視でOKです。
$ sudo service postgresql95 initdb Initializing database: sudo service postgresql95 start [ OK ] $ sudo service postgresql95 start Starting postgresql95 service: [ OK ] $ sudo -u postgres createuser -s ec2-user could not change directory to "/home/ec2-user/environment": Permission denied参考:[Rails6]AWS Cloud9(Amazon Linux)で動かしてみる
APIモードでrails newする
$ rails new bbs -d postgresql --api--apiと付けることでRails APIモードとなります。
APIに不要なファイルが生成されない状態で作られます。Railsのテストサーバを立ち上げる
New Terminalから新しいターミナルを立ち上げます。
ローカル等では
rails sだけでテストサーバが立ち上がるのですが、Cloud9はオプションが必要です。$ cd bbs/ $ rails s -b $IP -p $PORT ... * Listening on tcp://127.0.0.1:8080 Use Ctrl-C to stopこれでサーバが立ち上がります。
なお、書いてあるとおりCtrl+Cで止めることができます。
port8080で動いていることが分かりますね。先程のターミナルに戻り、ちゃんと立ち上がったかcurlで確認してみましょう。
テストサーバを立ち上げたターミナルウィンドウは、止めずに放置してください。$ cd bbs/ $ curl localhost:8080 {"status":500,"error":"Internal Server Error","exception":"#\u003cActiveRecord::NoDatabaseError: FATAL: database \"bbs_development\" does not existどうやらDBが無くて怒られてます。
railsでDBの初期化をしましょう。$ rails db:create ... Created database 'bbs_development' Created database 'bbs_test' $ curl localhost:8080なんかhtmlっぽい大量の文字列が出てきたら、とりあえず500系エラーは起きていないので大丈夫です。
IDEの初期設定をする
インデントはspace2にしたいので、画面右端にある歯車マークから設定画面へ。
Code Editor (Ace)を選択し、Soft Tabsを4から2に偏向
On Save, Strip Whitespaceして閉じます。
ホストを許可する
Rails6から、ホストを許可しないとエラーでページが表示されません。
Cloud9で実行しているので、AWSのhostsを追加します。
保存したらテストサーバを一度Ctrl+Cで止め、再起動してください。そうしないとconfigが反映されません。config/application.rb... module Bbs class Application < Rails::Application ... + config.hosts << '.amazonaws.com' ... end endWelcome画面を表示する
画面上部からPreview Running Applicationを押すと、小さくアプリケーションウィンドウが立ち上がります。
ですがこの小さいウィンドウでは正常に実行されないため、Pop Out Into New Windowを押して別ウィンドウを立ち上げます。
するとついに、正常にWelcome画面が表示されます。
続き
- 投稿日:2020-09-06T21:23:52+09:00
can't find gem bundler (>= 0.a) with executable bundleの対処法
既存のRailsプロジェクトをcloneし、
% bundle installをした際にこのエラーが発生。% bundle install Traceback (most recent call last): 2: from /Users/trilingual/.rbenv/versions/2.5.1/bin/bundle:23:in `<main>' 1: from /Users/trilingual/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:308:in `activate_bin_path' /Users/triringual/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:289:in `find_spec_for_exe': can't find gem bundler (>= 0.a) with executable bundle (Gem::GemNotFoundException)原因
Gemfile.lock最下部に記載の
BUNDLED WITH2.0.2の記述と自身がインストールしたbundlerのバージョンと異なってしまっていることが原因。Gemfile.lock<省略> BUNDLED WITH 2.1.4これと
% bundler -v Bundler version 1.17.1これが違うということです。
解決策
①アプリケーションのGemfile.lock最下部に記載の
BUNDLED WITH○○の記載を確認Gemfile.lock<省略> BUNDLED WITH 2.1.4②Gemfile.lockに記載のバージョンをインストールする(今回は
2.1.4)% gem install bundler -v 2.1.4まとめ
アプリケーションで想定している環境と自身のPCの環境
を合わせてあげるようなイメージです。
・PCを買い替えてアプリを開いたとき
・アプリをgit cloneしたとき
・Rubyのバージョンを変更したとき
のようなパターンでこのエラーが頻発しているように感じます。同じエラーが発生した方へ届きますように!
- 投稿日:2020-09-06T20:55:36+09:00
[rails]ネストした関係でどうやって親情報を表示させるか
やりたいこと
前提・Itemの情報がないとOrderの情報は成り立たない関係
(Orderにitem_idカラムがある(どのitem情報を指しているのか?))
(親)itemテーブル>(子)orderテーブルになる。orderコントローラでindexアクションを起動させ(index.html.erb)に
(親)のitem情報を表示させたかったらどうやって記述するの?
※例えば
itemテーブル
id:1
ぬいぐるみ4000円orderテーブル
item_id:1
user_id:3
ぬいぐるみ4000円とuser_idが結びつくんだな・・・手順
config/routes.rbの記述内容 Rails.application.routes.draw do devise_for :users root 'items#index' resources :items do resources :orders, only: [:create, :index] end end1.親items>子ordersの関係をroutes.rbで記述する
resources :items do resources :orders, only: [:create, :index]これで(親)item>(子)orderの関係を指定することができる。ネストすることができる
2.ordersコントローラーで@item = Item.find(params[item_id])と記述する
def index @item = Item.find(params[:item_id]) end@itemの箱の中にitemテーブルの情報を入れる。
どのitemテーブルの情報を指定するか[:item_id]で指定できる
ユーザーが選択したitem情報を@itemに代入する3.@itemをorder#indexのビューファイルに記述する
orderコントローラーのindex.html.erbに記述 <%= @item.item_name %>これで(親)itemテーブルからデーターを抜き出して
(子)orderコントローラーのindex.html.erbのビューに表示することができる。まとめ
分かりにくいまとめですが
要は親テーブルの情報を抜き出して、子のビューに表示させたかったら
ネストを使って関係を示し、子コントローラーでparams[:親_id]と記述すれば
親から情報を抜き出して子のビュー表示することができるという内容でした。うまくまとめることができないのは私の力不足です・・・。
具体的な画像とテーブル情報が添付していると分かりやすくなったかも。
- 投稿日:2020-09-06T20:55:36+09:00
[rails]ネストした関係で親情報を子ビューに表示する方法
やりたいこと
前提・Itemの情報がないとOrderの情報は成り立たない関係
(Orderにitem_idカラムがある(どのitem情報を指しているのか?))
(親)itemテーブル>(子)orderテーブルになる。orderコントローラでindexアクションを起動させ(index.html.erb)に
(親)のitem情報を表示させたかったらどうやって記述するの?
※例えば
itemテーブル
id:1
ぬいぐるみ4000円orderテーブル
item_id:1
user_id:3
ぬいぐるみ4000円とuser_idが結びつくんだな・・・手順
config/routes.rbの記述内容 Rails.application.routes.draw do devise_for :users root 'items#index' resources :items do resources :orders, only: [:create, :index] end end1.親items>子ordersの関係をroutes.rbで記述する
resources :items do resources :orders, only: [:create, :index]これで(親)item>(子)orderの関係を指定することができる。ネストすることができる
2.ordersコントローラーで@item = Item.find(params[item_id])と記述する
def index @item = Item.find(params[:item_id]) end@itemの箱の中にitemテーブルの情報を入れる。
どのitemテーブルの情報を指定するか[:item_id]で指定できる
ユーザーが選択したitem情報を@itemに代入する3.@itemをorder#indexのビューファイルに記述する
orderコントローラーのindex.html.erbに記述 <%= @item.item_name %>これで(親)itemテーブルからデーターを抜き出して
(子)orderコントローラーのindex.html.erbのビューに表示することができる。まとめ
分かりにくいまとめですが
要は親テーブルの情報を抜き出して、子のビューに表示させたかったら
ネストを使って関係を示し、子コントローラーでparams[:親_id]と記述すれば
親から情報を抜き出して子のビュー表示することができるという内容でした。うまくまとめることができないのは私の力不足です・・・。
具体的な画像とテーブル情報が添付していると分かりやすくなったかも。
- 投稿日:2020-09-06T18:43:09+09:00
【Rails】db:migrateでNo database selectedとUnknown databaseというエラーが出た時の対処
はじめに
思い付いたアイデアを形にしたくてWebサービスの作成を本日より始めました。
早速、エラーにハマったので忘備録として残します。環境Rails : 6.0.3.2 ruby : 2.6.3エラー内容
deviseでユーザーログイン画面の作成を行なってます。
下記コマンドでエラーが発生しました。ターミナル$ rake db:migrate rake aborted! ActiveRecord::StatementInvalid: Mysql2::Error: No database selectedどうやら、データベースが選択されていないとのこと。
database.ymlを確認すると、、、database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: 5 username: <%= ENV['DATABASE_DEV_USER'] %> password: <%= ENV['DATABASE_DEV_PASSWORD'] %> host: <%= ENV['DATABASE_DEV_HOST'] %> development: <<: *default database: <%= ENV['DATABASE_DEV_NAME'] %> test: <<: *default database: <%= ENV['DATABASE_DEV_NAME'] %> production: <<: *default database: <%= ENV['DATABASE_DEV_NAME'] %>
databaseが環境変数によって指定されているが、
.envファイルには何も記述がない為、エラーが発生したらしい。database.ymlの
databaseを、環境変数では無く直接記述する。database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: 5 username: <%= ENV['DATABASE_DEV_USER'] %> password: <%= ENV['DATABASE_DEV_PASSWORD'] %> host: <%= ENV['DATABASE_DEV_HOST'] %> development: <<: *default database: development test: <<: *default database: test production: <<: *default database: production再度
rake db:migrateを実行。ターミナルrake aborted! ActiveRecord::NoDatabaseError: Unknown database 'development'と表示される。
どうも、データベースが見つからないらしい。railsでデータベース作成&マイグレート(MySQL)
上記記事を参考にさせて頂きました。###データベース作成 rake db:create:all ###マイグレーション rake db:migrateこれで無事完了。
- 投稿日:2020-09-06T18:39:26+09:00
simple_formを使ってみる/子モデルまで編集する
概要
simple_formは高機能なフォームを自動作成してくれるgemです。https://github.com/heartcombo/simple_form
フォームを作ってくれるだけでなく、エラーメッセージや項目名なども自動で表示します。今回はテーブルが2つのシンプルなrailsプロジェクトで、
simple_formを使ってみました※bootstrapやCSSは書いていません
実装
まずは一番シンプルな実装userモデルのみ実装します
gemfilegem 'simple_form'user.rbclass User < ApplicationRecord validates :name, presence: true validates :email, presence: true validates :age, presence: true endroutes.rbresources :usersusers_controller.rbclass UsersController < ApplicationController def new @user = User.new end def create @user = User.new(user_params) if @user.save redirect_to user_path(@user) else render 'users/new' end end private def user_params params.require(:user).permit( :name, :email, :age, :date ) end下記migrationファイルを実行します。
rails db:migratedb/migrate/20200906_create_users.rbclass CreateUsers < ActiveRecord::Migration[5.1] def change create_table :users do |t| t.string :name t.string :email t.integer :age t.datetime :date t.string :url t.timestamps end end endviewファイル(new)にsimple_formを書きます。ほぼreadmeのコピペです
app/views/users/new.html.erb<%= simple_form_for user do |f| %> <%= f.input :name %> <%= f.input :email %> <%= f.input :age, collection: 18..60 %> <%= f.input :date %> <%= f.input :url %> <%= f.button :submit %> <% end %>これでフォームは表示されます。localhost:3000/users/newにアクセスした結果が以下です。
presenceのバリデーションを設定している項目にはcan't be blank と自動で表示されています。
また、emailとurlはカラム名からsimple_formが判断して、各々最適なバリデーションをしてくれます。
吹き出しのデザインも勝手にsimple_formが生成してくれたものです。
また、dateとageの項目を見れば分かるように、型に応じて入力形式を勝手に変えてくれます
ここに書いたのは一例で、
その他 colorやcountryなど、様々な入力パターンが用意されています。(reademeに書いてあります)
simple_formの使い方はこんな感じです。子モデルを一緒に編集したい場合
userに紐ついている子モデルを、userモデルから編集することができます。
userモデルが主であるようなページで同時に編集したい場合に使います。今回は user_propertyという子モデルを用意します。
user_properties_controller のcreateアクションではなく、
users_controllerのcreate で編集をできるようにします。(updateも同様ですまたそれによってsubmitボタンを一つにできます。
下に先ほどから変化のあっるファイルを示します。
user_property.rbclass UserProperty < ApplicationRecord belongs_to :user validates :nickname, presence: true validates :hobby, presence: true enduser.rbclass User < ApplicationRecord validates :name, presence: true validates :email, presence: true validates :age, presence: true has_one :user_property accepts_nested_attributes_for :user_property endusers_controller.rbclass UsersController < ApplicationController def new @user = User.new @user.create_user_property(nickname: "takashi", hobby: "yamamoto") end private def user_params params.require(:user).permit( :name, :email, :age, :date, user_property_attributes: %i[ nickname hobby ] ) end下記migrationファイルを実行します。
rails db:migratedb/migrate/20200906_create_user_property.rbclass CreateUserProperties < ActiveRecord::Migration[5.1] def change create_table :user_properties do |t| t.integer :user_id t.string :nickname t.string :hobby t.timestamps end end endsimple_formは以下のようにかきます。
----user_proiperty-----以下が追加した項目です。
simple_field_forを使います。app/views/users/new.html.erb<%= simple_form_for user do |f| %> <%= f.input :name %> <%= f.input :email %> <%= f.input :age, collection: 18..60 %> <%= f.input :date %> <%= f.input :url %> -------user_property---------- <%= f.simple_fields_for :user_property, user.user_property do |ff| %> <%= ff.input :nickname %> <%= ff.input :hobby %> <% end %> <%= f.button :submit %> <% end %>これでできたのが以下です
user_propertyの項目が二つ追加されています。これで完了です。
以上です
- 投稿日:2020-09-06T18:05:29+09:00
(ギリ)20代の地方公務員がRailsチュートリアルに取り組みます【第4章】
前提
・Railsチュートリアルは第4版
・今回の学習は3周目(9章以降は2周目)
・著者はProgate一通りやったぐらいの初学者基本方針
・読んだら分かることは端折る。
・意味がわからない用語は調べてまとめる(記事最下段・用語集)。
・理解できない内容を掘り下げる。
・演習はすべて取り組む。
・コードコピペは極力しない。第4章です。この章は冗長な内容だった記憶が…。つべこべ言わずやっていきます。
本日のBGMはこちら。
matryoshka "zatracenie[full album]"
【4.1.2 カスタムヘルパー メモ】
・新しく作ったメソッド=カスタムヘルパー
・全てのページで使うヘルパー:app/helpers/application_helper.rb に設置
・特定のコントローラだけが使うヘルパー:app/helpers/(該当のコントローラ名).rb に設置
【4.2.2 文字列 メモと演習】
・putsメソッドは戻り値にnilを返す。改行文字である\nが出力の末尾に追加される。
・printメメソッドは改行文字を追加しない。
・シングルクォート内では式展開できない。入力した文字をエスケープせずに、そのまま保持するときに便利1. city変数に適当な市区町村を、prefecture変数に適当な都道府県を代入してください。
→ city = "sapporo" prefecture = "hokkaido"(今コンサドーレ札幌の試合観ながらやってるので)2. 先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力にはputsを使ってください。
→ 下記>> puts prefecture + " " + city hokkaido sapporo => nil3. 上記の文字列の間にある半角スペースをタブに置き換えてみてください。(ヒント: 改行文字と同じで、タブも特殊文字です)
→ 下記>> puts prefecture + "\t" + city hokkaido sapporo => nil4. タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?
→ 下記>> puts prefecture + '\t' + city hokkaido\tsapporo => nil
【4.2.3 オブジェクトとメッセージ受け渡し 演習】
1. "racecar" の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。
→ 下記>> s = "racecar" => "racecar" >> s.length => 72. reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
→ 下記>> s.reverse => "racecar"3. 変数sに "racecar" を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
→ もう入れとったわ。下記>> s == s.reverse => true4. リスト 4.9を実行すると、どんな結果になるでしょうか? 変数sに "onomatopoeia" という文字列を代入するとどうなるでしょうか? ヒント: 上矢印 (またはCtrl-Pコマンド) を使って以前に使ったコマンドを再利用すると一からコマンドを全部打ち込む必要がなくて便利ですよ。)
→ 下記>> puts "It's a palindrome!" if s == s.reverse It's a palindrome! => nil>> s = "onomatopoeia" => "onomatopoeia" >> puts "It's a palindrome!" if s == s.reverse => nil
【4.2.4 メソッドの定義 演習】
1. リスト 4.10のFILL_INの部分を適切なコードに置き換え、回文かどうかをチェックするメソッドを定義してみてください。ヒント: リスト 4.9の比較方法を参考にしてください。
→ 下記>> def palindrome_tester(s) >> if s == s.reverse >> puts "It's a palindrome!" >> else >> puts "It's not a palindrome." >> end >> end => :palindrome_tester2. 上で定義したメソッドを使って “racecar” と “onomatopoeia” が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文でない、という結果になれば成功です。
→ 下記>> palindrome_tester("racecar") It's a palindrome! => nil >> palindrome_tester("onomatopoeia") It's not a palindrome. => nil3. palindrome_tester("racecar")に対してnil?メソッドを呼び出し、戻り値がnilであるかどうかを確認してみてください (つまりnil?を呼び出した結果がtrueであることを確認してください)。このメソッドチェーンは、nil?メソッドがリスト 4.10の戻り値を受け取り、その結果を返しているという意味になります。
→ 下記>> palindrome_tester("racecar").nil? It's a palindrome! => true
【4.3.1 配列と範囲演算子 演習】
1. 文字列「A man, a plan, a canal, Panama」を ", " で分割して配列にし、変数aに代入してみてください。
→ 下記>> a = "A man, a plan, a canal, Panama".split(',') => ["A man", " a plan", " a canal", " Panama"]
2. 今度は、変数aの要素を連結した結果 (文字列) を、変数sに代入してみてください。
→ 下記>> s = a.join => "A man a plan a canal Panama"3. 変数sを半角スペースで分割した後、もう一度連結して文字列にしてください (ヒント: メソッドチェーンを使うと1行でもできます)。リスト 4.10で使った回文をチェックするメソッドを使って、(現状ではまだ) 変数sが回文ではないことを確認してください。downcaseメソッドを使って、s.downcaseは回文であることを確認してください。
→ ひとまとめにするとこうなる(一回コンソール落としてたからメソッドの再定義めんどくさかったやん!)>> palindrome_tester(s.split(' ').join.downcase) It's a palindrome! => nil4. aからzまでの範囲オブジェクトを作成し、7番目の要素を取り出してみてください。同様にして、後ろから7番目の要素を取り出してみてください。(ヒント: 範囲オブジェクトを配列に変換するのを忘れないでください)
→ 最初は0から始まるから6ですね。>> A = ("a".."z").to_a => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] >> A[6] => "g" >> A[-7] => "t"
【4.3.2 ブロック 演習】
1. 範囲オブジェクト0..16を使って、各要素の2乗を出力してください。
→ 下記>> (0..16).each { |i| puts i**2 } 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 => 0..16
2. yeller (大声で叫ぶ) というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller(['o', 'l', 'd'])と実行したとき、"OLD"という結果が返ってくれば成功です。ヒント: mapとupcaseとjoinメソッドを使ってみましょう。
→ 下記。これもっと賢いやり方ないかな。引数に配列じゃなくて文字列入れるだけでいいようにするとか。>> def yeller(s) >> s.map(&:upcase).join >> end => :yeller >> yeller(['o','l','d']) => "OLD"3. random_subdomainというメソッドを定義してください。このメソッドはランダムな8文字を生成し、文字列として返します。ヒント: サブドメインを作るときに使ったRubyコードをメソッド化したものです。
→ 下記>> def random_subdomain >> ("a".."z").to_a.shuffle[0..7].join >> end => :random_subdomain >> random_subdomain => "unwdemsp"4. リスト 4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。ヒント:split、shuffle、joinメソッドを組み合わせると、メソッドに渡された文字列 (引数) をシャッフルさせることができます。
→ 下記>> def string_shuffle(s) >> s.split('').shuffle.join >> end => :string_shuffle >> string_shuffle("foobar") => "arbfoo"
【4.3.3 ハッシュとシンボル 演習】
1. キーが'one'、'two'、'three'となっていて、それぞれの値が'uno'、'dos'、'tres'となっているハッシュを作ってみてください。その後、ハッシュの各要素をみて、それぞれのキーと値を"'#{key}'のスペイン語は'#{value}'"といった形で出力してみてください。
→ 下記>> n = { one: 'uno', two: 'dos', three: 'tres' } => {:one=>"uno", :two=>"dos", :three=>"tres"} >> n.each do |key, value| ?> puts "#{key}のスペイン語は#{value}" >> end oneのスペイン語はuno twoのスペイン語はdos threeのスペイン語はtres => {:one=>"uno", :two=>"dos", :three=>"tres"}
2. person1、person2、person3という3つのハッシュを作成し、それぞれのハッシュに:firstと:lastキーを追加し、適当な値 (名前など) を入力してください。その後、次のようなparamsというハッシュのハッシュを作ってみてください。1.) キーparams[:father]の値にperson1を代入、2). キーparams[:mother]の値にperson2を代入、3). キーparams[:child]の値にperson3を代入。最後に、ハッシュのハッシュを調べていき、正しい値になっているか確かめてみてください。(例えばparams[:father][:first]がperson1[:first]と一致しているか確かめてみてください)
→ 下記。それぞれの名前とキーの意味が分かるアナタはガンバサポーター。(2020シーズン第14節のスタメンより)>> person1 = { first: "Yuki", last: "Yamamoto" } => {:first=>"Yuki", :last=>"Yamamoto"} >> person2 = { first: "Yosuke", last: "Ideguchi" } => {:first=>"Yosuke", :last=>"Ideguchi"} >> person3 = { first: "Shu", last: "Kurata" } => {:first=>"Shu", :last=>"Kurata"} >> params = { anchor: person1, rih: person2, lih: person3 } => {:anchor=>{:first=>"Yuki", :last=>"Yamamoto"}, :rih=>{:first=>"Yosuke", :last=>"Ideguchi"}, :lih=>{:first=>"Shu", :last=>"Kurata"}} >> params[:anchor][:first] == person1[:first] => true
3. userというハッシュを定義してみてください。このハッシュは3つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています。
→ 下記>> user = { name: "tk", email: "tk@mail.com", password_digest: ("a".."z").to_a.shuffle[0..15].join } => {:name=>"tk", :email=>"tk@mail.com", :password_digest=>"socxlgerjatyinbw"}
4. Ruby API (訳注: もしくはるりまサーチ) を使って、Hashクラスのmergeメソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか? 推測できたら、実際にコードを実行して推測があっていたか確認してみましょう。{ "a" => 100, "b" => 200 }.merge({ "b" => 300 })→ 正直、るりまサーチで調べてもよく分かんないんですよね。日本語が日本語じゃない。なので普通にグーグル先生に頼ると、mergeメソッドは複数のハッシュを結合させるメソッドとのこと。そして、merge「前」のハッシュに、merge「後」のハッシュを結合するのですが、重複するハッシュがある場合は、「後」のハッシュが上書きされます。ということは、上の結果はbが300になるはず。実際の結果は下記。合ってますね。
>> { "a" => 100, "b" => 200 }.merge({ "b" => 300 }) => {"a"=>100, "b"=>300}
【4.4.1 コンストラクタ 演習】
1. 1から10の範囲オブジェクトを生成するリテラルコンストラクタは何でしたか? (復習です)
→ 下記>> r = 1..10 => 1..10
2. 今度はRangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作ってみてください。ヒント: newメソッドに2つの引数を渡す必要があります。
→ 下記>> r2 = Range.new(1,10) => 1..10
3. 比較演算子==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください。
→ 下記>> r == r2 => true
【4.4.2 クラス継承 演習】
1. Rangeクラスの継承階層を調べてみてください。同様にして、HashとSymbolクラスの継承階層も調べてみてください。
→ まずはRangeから。>> r = Range.new(1,3) => 1..3 >> r.class => Range >> r.class.superclass => Object >> r.class.superclass.superclass => BasicObject >> r.class.superclass.superclass.superclass => nil次にHash
>> h = {} => {} >> h.class => Hash >> h.class.superclass => Object >> h.class.superclass.superclass => BasicObject >> h.class.superclass.superclass.superclass => nil最後にSymbol
>> s = :symbol => :symbol >> s.class => Symbol >> s.class.superclass => Object >> s.class.superclass.superclass => BasicObject >> s.class.superclass.superclass.superclass => nil
2. リスト 4.15にあるself.reverseのselfを省略し、reverseと書いてもうまく動くことを確認してみてください。
→ 下記>> class Word < String >> def palindrome? >> self == reverse >> end >> end => :palindrome? >> s = Word.new("level") => "level" >> s.palindrome? => true
【4.4.3 組み込みクラスの変更 演習】
1. palindrome?メソッドを使って、“racecar”が回文であり、“onomatopoeia”が回文でないことを確認してみてください。南インドの言葉「Malayalam」は回文でしょうか? ヒント: downcaseメソッドで小文字にすることを忘れないで。
→ 4.4.2の演習の流れでいきましょう。s = Word.new("racecar") => "racecar" >> s.palindrome? => true >> s = Word.new("onomatopoeia") => "onomatopoeia" >> s.palindrome? => false >> s.downcase.palindrome? => true
2. リスト 4.16を参考に、Stringクラスにshuffleメソッドを追加してみてください。ヒント: リスト 4.12も参考になります。3. 比較演算子==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください。
→ まとめて下記>> def shuffle >> self.split('').shuffle.join >> end >> end => :shuffle >> "foobar".shuffle => "fobaro" >> class String >> def shuffle >> split('').shuffle.join >> end >> end => :shuffle >> "foobar".shuffle => "boroaf"
【4.4.4 コントローラクラス 演習】
1. 第2章で作ったToyアプリケーションのディレクトリでRailsコンソールを開き、User.newと実行することでuserオブジェクトが生成できることを確認してみましょう。
→ toy_app消してますやん…ということで、git cloneを実行。また一つ賢くなりました。usernameとpasswordを聞かれましたが、Githubのアカウント名とログインパスワードを入力すれば実行できました。$ git clone 該当のリモートリポジトリのhttpsそして本題に入ろうとしたら、各種エラーが…。budle install --without productionして、rails db:migrateして解決。無事userオブジェクトを作成できました。
>> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
2. 生成したuserオブジェクトのクラスの継承階層を調べてみてください。
→ 下記>> user.class => User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime) >> user.class.superclass => ApplicationRecord(abstract) >> user.class.superclass.superclass => ActiveRecord::Base >> user.class.superclass.superclass.superclass => Object >> user.class.superclass.superclass.superclass.superclass => BasicObject >> user.class.superclass.superclass.superclass.superclass.superclass => nil
【4.4.5 ユーザークラス 演習】
1. Userクラスで定義されているname属性を修正して、first_name属性とlast_name属性に分割してみましょう。また、それらの属性を使って "Michael Hartl" といった文字列を返すfull_nameメソッドを定義してみてください。最後に、formatted_emailメソッドのnameの部分を、full_nameに置き換えてみましょう (元々の結果と同じになっていれば成功です)
→ 下記example_user.rbclass User attr_accessor :first_name, :last_name, :email def initialize(attributes = {}) @first_name = attributes[:first_name] @last_name = attributes[:last_name] @email = attributes[:email] end def full_name "#{@first_name} #{@last_name}" end def formatted_email "#{self.full_name} <#{@email}>" end endコンソール上で、
>> require './example_user' => true user = User.new(first_name: "t" ,last_name: "k", email: "tk@mail.com") => #<User:0x00000000030715e0 @first_name="t", @last_name="k", @email="tk@mail.com"> >> user.formatted_email=> "t k <tk@mail.com>"
2. "Hartl, Michael" といったフォーマット (苗字と名前がカンマ+半角スペースで区切られている文字列) で返すalphabetical_nameメソッドを定義してみましょう。
→ 下記def full_name "#{@first_name} #{@last_name}" end3. full_name.splitとalphabetical_name.split(', ').reverseの結果を比較し、同じ結果になるかどうか確認してみましょう。
→ 上の演習2を記入してるのでコンソールに読み込み直し。上の入力値も短すぎるので入れ直します。(ヤットさんのaが抜けてる、、、)>> require './example_user'=> true >> user = User.new(first_name: "Ysuhito", last_name: "Endo", email: "ye@ye.com") => #<User:0x0000000003059968 @first_name="Ysuhito", @last_name="Endo", @email="ye@ye.com"> >> user.full_name.split=> ["Ysuhito", "Endo"] >> user.alphabetical_name.split(', ').reverse => ["Ysuhito", "Endo"] >> user.full_name.split == user.alphabetical_name.split(', ').reverse => true
第4章まとめ
・全体で使うのか、特定のコントローラで使うのか、それらによってヘルパーの定義ファイルも使い分ける。
・オブジェクト指向を感覚に刷り込んでいけ。すべてがオブジェクトだ。
・クラスは継承できる。必ず大元のクラスがある。継承しているから色々な機能が使える。
・クラスに定義するのがクラスメソッド、インスタンスで定義するのがインスタンスメソッド。
・( )とか{ }省略できる。引数の最後のブロックの{ }など。
・:nameはシンボル。ハッシュ記法は ~~: "mojiretu" の書き方が一般的。
・ぼちぼち分からない単語があったので、用語集にまとめています。
この章は作業多かったな〜。でもコードリーディングには省略記法とかの知識が大事かも。次の5章から、再びアプリ開発に戻ります!
⇦第3章はこちら
学習にあたっての前提・著者ステータスはこちら
なんとなくイメージを掴む用語集
・組み込み関数
プログラミング言語などの仕様にあらかじめ用意され、標準で使用できる関数のこと。これに対し、プログラマがコード上で定義・実装した関数を「ユーザー定義関数」(user-defined function)という。・API(Application Programming Interface)
あるコンピュータプログラム(ソフトウェア)の機能や管理するデータなどを、外部の他のプログラムから呼び出して利用するための手順やデータ形式などを定めた規約のこと。APIの利用には、ソフトウェア開発の効率化・セキュリティの向上・最新情報を簡単に取得可能 といったメリットがある。・エスケープ(エスケープ文字・処理)
意味を持つ文字列をただの文字列にしたり、その逆で意味を与えたりすること、その働きを持つ文字(\など)のこと。・リテラル (literal)
ソースコードに書いた文字とか数字のこと。・ネスト(nest)
あるものの中に、それと同じ形や種類の(一回り小さい)ものが入っている状態や構造のこと。・アクセサー(accessir)
オブジェクト指向プログラミングで、オブジェクト内部のメンバ変数(属性、プロパティ)に外部からアクセスするために用意されたメソッド。メンバ変数をオブジェクト内部に隠蔽し、外部から直接参照させないようにするために用意される。・メンバ変数
インスタンス変数のこと。
- 投稿日:2020-09-06T17:59:25+09:00
遭遇したerror達と対策
備忘録の最たる例として、遭遇したエラーと解決方法を書いていく。
①syntax error, unexpected tIDENTIFIER
syntax error = 構文エラーなので、記述したコードの何処かが間違っていると怒られる。
下記コードはファイルをターミナルで出力した結果。main2.rb:81: syntax error, unexpected tIDENTIFIER, expecting ')' hp: params[:hp] main2.rb:82: syntax error, unexpected ':', expecting end offense: params[:offense] main2.rb:83: syntax error, unexpected ':', expecting end defense: params[:defense] main2.rb:84: syntax error, unexpected ')', expecting end )main2.rb(間違ってるコード)super( name: params[:name] hp: params[:hp] offense: params[:offense] defense: params[:defense] )main2.rb(修正したコード)super( name: params[:name], hp: params[:hp], offense: params[:offense], defense: params[:defense] )原因はsuperメソッド内の各項目が「,」で区切られておらず、全て同じ行扱いになっていたが為に怒られた。
尚、superメソッドは、実行しているメソッドがオーバーライドしているメソッドを呼び出します。オーバーライドとは「親クラスのメソッドを子クラスで上書きすること」です。
initializeメソッド中でsuperを使うことにより、指定クラスのinitializeメソッドを実行することが可能。結果、「共通している部分」と「共通していない部分」で処理を振り分けることができる。②uninitialized constant (NameError)
main.rbTraceback (most recent call last): main.rb:1:in `<main>': uninitialized constant <class名> (NameError)main.rbファイルの中にその様なclass名は無いと怒られています。
もし、別に切り分けているファイルを呼び出したくて起きたエラーならば、
下記の様にrequireを記述して別ファイルを呼び出せる様にしてあげましょう。require './<class名>'以下はエラーに遭遇、解決したら追記していく。
- 投稿日:2020-09-06T17:59:25+09:00
Rubyの文法エラーと対策
備忘録の最たる例として、遭遇したエラーと解決方法を書いていく。
①構文がおかしいよ?[syntax error, unexpected tIDENTIFIER]
syntax error = 構文エラーなので、記述したコードの何処かが間違っていると怒られる。
下記コードはファイルをターミナルで出力した結果。main2.rb:81: syntax error, unexpected tIDENTIFIER, expecting ')' hp: params[:hp] main2.rb:82: syntax error, unexpected ':', expecting end offense: params[:offense] main2.rb:83: syntax error, unexpected ':', expecting end defense: params[:defense] main2.rb:84: syntax error, unexpected ')', expecting end )main2.rb(間違ってるコード)super( name: params[:name] hp: params[:hp] offense: params[:offense] defense: params[:defense] )main2.rb(修正したコード)super( name: params[:name], hp: params[:hp], offense: params[:offense], defense: params[:defense] )原因はsuperメソッド内の各項目が「,」で区切られておらず、全て同じ行扱いになっていたが為に怒られた。
尚、superメソッドは、実行しているメソッドがオーバーライドしているメソッドを呼び出します。オーバーライドとは「親クラスのメソッドを子クラスで上書きすること」です。
initializeメソッド中でsuperを使うことにより、指定クラスのinitializeメソッドを実行することが可能。結果、「共通している部分」と「共通していない部分」で処理を振り分けることができる。②そんな名前は存じません!uninitialized constant (NameError)
main.rbTraceback (most recent call last): main.rb:1:in `<main>': uninitialized constant <class名> (NameError)main.rbファイルの中にその様なclass名は無いと怒られています。
もし、別に切り分けているファイルを呼び出したくて起きたエラーならば、
下記の様にrequireを記述して別ファイルを呼び出せる様にしてあげましょう。require './<class名>'③必要な値を渡すんだぞ!ArgumentError
wrong number of arguments (given 1, expected 0) (ArgumentError)引数エラーの場合に発生。
(given 1, expected 0) とあるように必要な値を一個渡してるけど、受け取り手がいないと怒られている。
そもそもの引数の記述し忘れてる可能性が高いから記述したメソッドのチェックをしてみよう。④メソッドを定義しましょう!NoMethodError
monster.rb:51:in `transform': undefined method `transform_message' for #<Monster:0x00007fea761c2b28> (NoMethodError)文字通り、メソッドが定義されてない事によるエラー。
単純にタイポが原因の可能性があるが、どこかのメソッドがdef〜endの閉じ忘れの可能性もあり。インデントの位置も注意してみよう。
以下はエラーに遭遇、解決したら追記していく。
- 投稿日:2020-09-06T17:37:56+09:00
ruby資格試験ポイントまとめ
Ruby Silver 試験ポイントまとめ(自分用)
自分用メモで汚いですが間違えやすいところをまとめてますぜひ
- 12問間違えてもいい。38問正解スルヒツヨウガアル
- Ruby 2.0以降では、デフォルトのスクリプトエンコーディングはUTF-8
- Hash#eachのブロックパラメータはArray。
- Hash#size はhashの要素の数を返す
- Enumerable#select selfからブロックの条件に該当する要素を集めて返す。
- Enumerable#find selfからブロックの条件に該当する最初の要素を返す。
Array#delete selfから引数の要素を削除。削除した要素を返す。破壊的メソッド。
例外処理について
- Rescue 節で省略するとstandard error とそのサブクラスを拾う
- Ensure節は必ず実行される
- raiseメソッドは引数を指定していなければ、RuntimeError例外 を発生 - elseもある。rescueが実行されないときに実行。resucueの下に書く。
八進数表記
- 0から始まるもの
- Puts 090 などでエラー
演算子の優先順位は*よりも**が高い
Ruby定数は大文字で始める
- 変更可能だが警告が出る
- << で追加するときは警告なし
Ruby変数名
- 1文字目はアルファベット小文字か_で始める必要がある。
- 2文字目以降はアルファベットもしくは数字を使用
可変長変数
- *a などで宣言
- 任意の数の引数が取れ、aに配列が格納される
Initializeメソッドは何回でも読み出され書き直される
Super の記述場所は任意の位置でおk。同名のメソッドをオーバライドしている場合はスーパーのほうが呼ばれる
外部モジュール読み込みについて
- 数学PIなどをしようする場合Mathモジュールを読みこむ必要がある
- 読み込み方法例
- Include Math
- Math::PI
クラス拡張のメソッドより特異メソッドが優先して実行される
.. , … 演算子について
- a = [1,2,3,4]
- a[0..3] => [1,2,3,4]
- a[0..-1] => [1,2,3,4]
- a[0,3] => [1,2,3]
find_all == select
map == collect
find == detect
updade! == merge
method
- stringクラスのdeleteは消した結果を返す。非破壊メソッド
正規表現記号
- *は直前の文字列の0回以上の繰り返しを表す
- I
- iオプションで大文字と小文字の区別しない
- . は任意の文字を表す
- {m}は直前の正規表現のm回の繰り返しを意味
- Mオプションで.が改行にもマッチする
- \d+などの+は連続を表す
- Matchは一度しか行わない
- Scanは繰り返し行われる
- \wは単語構成文字の省略記号
65を”A”にしたい時
- 65.chr
- 逆は”A”.ord
ヒアドキュメント
- <<-識別子' のように-' を付けて書くことで終端行をインデントすることは可能
- 通常は空白を開けるとエラー
Chompメソッドで\r\n全て消す
chopメソッドで末尾の文字を消す
IO#rewindはファイルポインターを先頭に移動する
Splitメソッドは引数で特定の文字列を区切り文字として指定できる。また第二引数で生成される配列の個数を指定することができる。
“0-5”は範囲指定になるが、”8-”では範囲指定にならず8と-を削除します
eql は 文字列が同じ場合true
equal は オブジェクトが同じかどうかを見る
inject はブロックに要素を割り当てる…|i,j|
IO
- IO#getsとIO#readlineはファイルオブジェクトから一行読み込んで、読み込みに成功した時にはその文字列を返す。
- IO#getsとIO#readlineの違いはEOFに到達した時の振る舞いのみ。IO#getsはnil, IO#readlineはEOFErrorを返す
foo = [1,2,3] bar = foo p foo === bar # => trueModule Foo def foo //インスタンスメソッドとして定義 puts("foo") end end Class Bar extend Foo //FooモジュールのインスタンスメソッドをBarクラスの特異メソッドとして定義 end Bar.foo #=> foo という使い方ができる
::Fooのような::演算子から始まる定数は、トップレベルで定義されている定数
slice!はスライス後を参照している
ファイルの末尾(EOF)への反応の仕方
- getsの場合、nilが帰る
- Readlineの場合、EOFError例外が発生する
File#mtimeメソッドは更新が保存されるがFile::Stat#mtimeメソッドは更新が保存されない
- File::Statクラスはnewメソッドのみクラスメソッド
ファイル名と拡張子が".“で区切られているファイル名から任意の拡張子を取り除く時に、第2引数として”.*"を指定してFile.basenameを呼び出します
"%2d%s"の"%2d"は出力したい数値が2桁より少なければ、空白スペースを入れるようにする指定
(1..5).each_cons(3) {|arr| p arr } # <実行結果> # [1, 2, 3] # [2, 3, 4] # [3, 4, 5] (1..10).each_slice(3) {|arr| p arr } # <実行結果> # [1, 2, 3] # [4, 5, 6] # [7, 8, 9] # [10]arr = [1,2].zip([3,4]) p arr # <実行結果> [[1, 3], [2, 4]]a1 = [1,2,3] a2 = [4,2,3] p a1 - a2 # <実行結果> [1]p ({a: 100, b: 100}).invert # <実行結果> # {100 => :b}1: s = ["one", "two", "three"] 2: s.shift 3: s.shift 4: s.unshift 5: s.push "four" 6: p s # <実行結果> # 1: ["one", "two", "three"] # 2: ["two", "three"] # 3: ["three"] # 4: ["three"] # 5: ["three", "four"]p "Ruby on Rails".delete("Rails") # <実行結果> # "uby on "a, b = 0 # a => 0, b => nil c, a = 1 # c => 1, a => nil a, d = 1, 2 # a => 1, d => 2 b, c = 3 # b => 3, c => nilselfがわからんからこれ覚える
class Blog def foo # インスタンスメソッド end def self.foo # クラスメソッド(このselfはクラス自身(Blog)を指す) end def bar # インスタンスメソッド self.foo # インスタンスメソッドのfooが呼ばれる(このselfはインスタンス自身(Blog.new)を指す) foo # インスタンスメソッドのfooが呼ばれる self.class.foo # クラスメソッドのfooが呼ばれる(このような記述でインスタンスメソッド内でもクラス自身を指すことができる) end def self.bar # クラスメソッド(このselfはクラス自身を指す) self.foo #クラスメソッドのfooが呼ばれる foo #クラスメソッドのfooが呼ばれる end endself="My job is a sales"である
class String def change_job self.replace("I will become an engineer!") end end s = "My job is a sales" s.change_job p s一文字マッチするものがマッチ
a = ['3p', '913', 'Zx', 'ssr', 'M7', 'W'] a.grep(/[A-Z0-9]/) ["3p", "913", "Zx", "M7", "W"]keyのソートsort1、非破壊
h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort [["abc", 3], ["def", 2], ["ghi", 1]] h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort.reverse [["ghi", 1], ["def", 2], ["abc", 3]]vauleのsort、非破壊
昇順 h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort{ | a, b | a[1] <=> b[1] } p h [["ghi", 1], ["def", 2], ["abc", 3]] 降順 h = { "def" => 2, "ghi" => 1, "abc" => 3, "ddd" => 5 } p h.sort{ | a, b | b[1] <=> a[1] } [["ddd", 5], ["abc", 3], ["def", 2], ["ghi", 1]] a,bに-がつくパターンもある意味が分からん
- 投稿日:2020-09-06T17:35:12+09:00
GitHubActionsでRuby on RailsのRSpecテストを実行する
Ruby on RailsのRSpecテストをGitHubActionsで実行しようとしたら、そこそこ大変だったのでメモに残しておきます。
GitHubActionsとは
GitHub上で動作するサーバレスな実行環境です。
リポジトリ内のファイルに設定を記述することで、GitHubの各種操作にトリガして任意のアクションを実行できる仕組みです。
パブリックリポジトリであれば無料で使えます。今回はPull Requestの作成に紐づけてRSpecを実行することで、Rails用のCIにしようと思います。
結論
Railsを実行できるDockerイメージをDockerHubにアップロードして、DockerPullしてテストを実行するのが良さそうです。
環境
- Ubuntu 16.04 LTS
- Ruby on Rails 6.0
- Ruby 2.6.6
OS以外は違っても簡単に対応できます。
Dockerイメージを作ってDockerHubにアップロードする
Dockerfile
ベーシックな環境です。
必要なライブラリがあれば追記してください。tools/ci/DockerfileFROM ubuntu:16.04 SHELL ["/bin/bash", "-c"] ENV RUBY_VERSION="2.6.6" ENV BUNDLER_VERSION="2.1.4" ENV DEBIAN_FRONTEND="noninteractive" ENV PATH=/root/.rbenv/bin:/root/.rbenv/shims:$PATH WORKDIR /app COPY Gemfile . COPY Gemfile.lock . RUN set -x \ && apt update \ && apt install -y \ build-essential \ curl \ git \ libssl-dev \ libreadline-dev \ libmysqlclient-dev \ mysql-client \ mysql-server \ tzdata \ zlib1g-dev \ # Install rbenv and ruby && git clone https://github.com/sstephenson/rbenv.git ~/.rbenv \ && git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build \ && CONFIGURE_OPTS='--disable-install-rdoc' /root/.rbenv/bin/rbenv install ${RUBY_VERSION} -s \ && /root/.rbenv/bin/rbenv global ${RUBY_VERSION} \ # Install bundler && echo 'gem: --no-rdoc --no-ri' > /root/.gemrc \ && /root/.rbenv/shims/gem install bundler -v ${BUNDLER_VERSION} \ # bundle install && /root/.rbenv/shims/bundle config set without development \ && /root/.rbenv/shims/bundle install \ # delete unused file && rm -rf /var/lib/apt/lists/* \ && rm Gemfile Gemfile.lockRUNを複数書いてDockerのレイヤを重ねるとMySQL周りで実行時にエラーが出るので1つのコマンドで全環境を構築します。
Dockerのビルド
docker build -t utyosu/build-rails:latest . -f tools/ci/DockerfileDockerイメージのアップロード
DockerHubのアカウントが必要になります。
はじめてのDockerHubリポジトリ登録あたりが参考になりそうです。docker push utyosu/build-rails:latestGitHubActionsのトリガを設定する
プロジェクトのリポジトリ内にGitHubAction用のファイルを作成します。
.github\workflows\build.ymlname: build # Pull Requestをトリガにする on: [pull_request] jobs: build: # Ubuntuの最新版をベースにする runs-on: ubuntu-latest # DockerHubからイメージをPullする container: image: utyosu/build-rails:latest # このブランチをcheckoutする steps: - uses: actions/checkout@v1 # test.shを実行する - name: run test run: . tools/ci/test.shテストスクリプト
テストしたいことを記述します。
このスクリプトが失敗するとビルド失敗になります。tools/ci/test.sh#!/bin/bash # プロセスが何も起動してないので、まずはMySQLを立ち上げておく service mysql start # MySQLユーザの作成と権限の付与 mysql -e 'create user "user_name";' mysql -e 'grant all on *.* to "user_name";' export RAILS_ENV=test bundle config set without development # Gemをインストールする。 # 何もない状態から bundle install # Capistranoが bundle exec cap -T # Rubocopによる静的解析 bundle exec rubocop # railsで定義されているデータベースを作成する bundle exec rails db:create # ridgepoleによるデータベースのスキーマ作成 # ridgepoleを使っていない人は代わりに bundle exec rails db:schema:load を実行する bundle exec ridgepole -c config/database.yml --apply -f db/schema -E test # RSpecの実行 bundle exec rspec実際にやってみる
PullRequestを作成すると
Some checks haven't completed yetと表示されます。DetailsをクリックするとCIの進捗状況を確認できます。
少し待っているとコンソール出力が表示されます。
しばらくすると完了します。
PullRequestに戻ると
All checks have passedと表示されて、問題なくテストが通ったことが分かります。経緯
GitHub標準のRuby実行環境が用意されているので、最初はそれをベースにして試していました。
しかし、RailsやGemのインストールで30分以上かかってしまうので、軽めのプロジェクトにおけるCIには向いてないと思いました。
DockerHubからイメージをPullして実行する方法であれば、40秒くらいでテストの実行が開始できることが分かったのでこの方法を用いています。メモ
別コンテナにMySQLとか用意してコンテナ同士でやり取りする方法もあるようですが仕組みが難しすぎて実現できませんでした。
Rails環境で上手く実現できたらいいなーって思ってます。
- 投稿日:2020-09-06T16:18:47+09:00
milkode の導入
はじめに
複数パッケージのソースコードを対象にgrepするのはめんどくさい。
手早くテキストやソースコードにgrepかけてくれるものとしてmilkodeを入れてみる。env
$ uname -r 4.19.104-microsoft-standard $ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=20.04 DISTRIB_CODENAME=focal DISTRIB_DESCRIPTION="Ubuntu 20.04 LTS"install
$ sudo apt install ruby ruby-dev $ sudo gem install milkodeinitialize
$ milk init --default Deprecation warning: Expected string default value for '--port'; got 9292 (numeric). This will be rejected in the future unless you explicitly pass the options `check_default_type: false` or call `allow_incompatible_default_type!` in your code You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION. create : /home/tkhshmsy/.milkode/milkode.yaml create : /home/tkhshmsy/.milkode/db/milkode.db created.add/remove
すでに4つほどpackageを追加済み。
$ git clone https://github.com/tkhshmsy/hogehoge.git Cloning into 'hogehoge'... remote: Enumerating objects: 87, done. remote: Total 87 (delta 0), reused 0 (delta 0), pack-reused 87 Unpacking objects: 100% (87/87), 599.91 KiB | 777.00 KiB/s, done. $ milk add hogehoge/ Deprecation warning: Expected string default value for '--port'; got 9292 (numeric). This will be rejected in the future unless you explicitly pass the options `check_default_type: false` or call `allow_incompatible_default_type!` in your code You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION. package : hogehoge github : tkhshmsy/hogehoge result : 1 packages, 73 records, 73 add. (0.98sec) *milkode* : 5 packages, 976 records in /home/tkhshmsy/.milkode/db/milkode.db. $ milk rm hogehoge/ Deprecation warning: Expected string default value for '--port'; got 9292 (numeric). This will be rejected in the future unless you explicitly pass the options `check_default_type: false` or call `allow_incompatible_default_type!` in your code You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION. rm_package : hogehoge result : 1 packages, 73 records. (0.97sec) *milkode* : 4 packages, 903 records in /home/tkhshmsy/.milkode/db/milkode.db.update
$ milk update --all Deprecation warning: Expected string default value for '--port'; got 9292 (numeric). This will be rejected in the future unless you explicitly pass the options `check_default_type: false` or call `allow_incompatible_default_type!` in your code You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION. package : package1 package : package2 package : package3 package : package4 result : 4 packages, 903 records. (0.18sec) *milkode* : 4 packages, 903 records in /home/tkhshmsy/.milkode/db/milkode.db.settings
- コマンドラインからの検索には
gmilkを使う- 色付き、全パッケージ検索に設定
.bashrcalias gmilk='gmilk -a --color'Web UI
WebUIを起動する。デフォルトの公開ポートは9292。
$ milk web -o `hostname -I` -nWebUIをWindows側から起動できるようにする
Windowsでショートカットを作る
- デスクトップの何もないところで右クリック=>ショートカットの作成
- wsl.exeを実行するように設定
- 項目にwslとだけ書いて次へ(参照をする必要なし)
- ショートカット名を編集、完了
- できたショートカットを右クリック、プロパティ
- リンク先にコマンドラインを書くwsl.exe - u <User> -- milk web -o `hostname -I` -nできたショートカットをダブルクリックして実行するとでmilk webが実行される。
IPアドレスとポート番号は標準出力に出ているのでそれをブラウザで開く。止めるときは、
- このウインドウをAlt+F4してもプロセスは止まらない
- プロセスを止めたいときはCTRL+Cで終了する、とウインドウも閉じる
- 別口からkillでもいい
どうなった?
単一のパッケージ内ならあまり必要性はないが、
- 複数パッケージにまたがる処理を追いかける時
- 他のパッケージのコードを参考に書く時
などには重宝する。
- 投稿日:2020-09-06T16:02:12+09:00
[rails]binding.pryで処理内容を確認する
やりたいこと
@itemに入っている中身valueを知りたい
items#show def show @item = Item.find(params[:id]) end#show.html.erbの記述 <%= link_to '購入画面に進む',item_orders_path(@item.id)%>処理内容
"購入画面に進む"ボタンを押すとorders#index(/items/:item_id/orders(.:format))に画面遷移する。
購入ボタンを押すと@itemの内容を参照するようになるがどのような情報を抜き出しているのか見たい処理内容の確認方法
1.
gem 'pry-rails'をGemfileに記述しインストールする
2.items#showにbinding.pryと記述するdef show @item = Item.find(params[:id]) binding.pry endbinding.pryによってshowアクションが実行された時処理を止めている
3.実際にブラウザを開きshow.hrml.erbのページを開くshowアクションを実行する。
4.処理が止まるのでコンソールのサーバーを見る
app/controllers/items_controller.rb:41 ItemsController#show: 40: def show => 41: binding.pry 42: end [1] pry(#<ItemsController>)>showアクションで処理が止まっていることがわかる
5[1] pry(#)>に@itemと記述する
[1] pry(#<ItemsController>)> @item => #<Item:0x00007f9ac9e01300 id: 8, item_name: "ラーメン", info: "いい味出てる!", category_id: 3, status_id: 3, shipping_id: 3, area_id: 5, schedule_id: 4, price: 1000, user_id: 1, created_at: Fri, 04 Sep 2020 01:29:20 UTC +00:00, updated_at: Fri, 04 Sep 2020 01:29:20 UTC +00:00>これで@itemに中身valueがわかった!
補足
[1] pry(#)>に@item.idとうつと
[2] pry(#<ItemsController>)> @item.id => 8@item.id の内容が”8”ということがわかった
まとめ
<%= link_to '購入画面に進む',item_orders_path(@item.id)%>
item_orders_pathに画面遷移したかったらitem_idが指定できないと遷移できない。item_orders /items/:item_id/orders(.:format) orders#index<%= link_to '購入画面に進む',item_orders_path(@item.id)%>(@item.id)と指定することでの
:item_idに必要な情報を抜き出すことができた!
- 投稿日:2020-09-06T15:21:23+09:00
VSCodeでファイル保存時に rubocopのauto-correct
掲題の通り。設定だけならそれほど大したことはなかったが、一点謎の不具合があってそこそこハマった。
プロジェクトにrubocopを導入。
この辺は公式のReadme等読みつつ雑に実施。
gemfileにrubocopを入れて、.rubocop.ymlを用意するだけ。
参考: https://github.com/rubocop-hq/rubocop元ファイルはRailsのRubocopをベースに作成。
rubocop-railsやrubocop-performanceを使っている様子なので、こちらもGemfile等に入れておく。
参考: https://github.com/rails/rails/blob/master/.rubocop.ymlrubocopの対象外にしたいファイルは除外する。
参考: https://qiita.com/necojackarc/items/8bc16092bbc69f17a16d#%E5%AF%BE%E8%B1%A1%E3%83%95%E3%82%A1%E3%82%A4%E3%83%ABあとは
bundle exec rubocopでrubocopが想定通り動作すればOKVSCode 上でrubocop の auto-correct
Prettierみたいなことがしたいだけだったが苦労した。なぜか。。。
基本設定
基本的には以下をVSCodeのsetting.jsonに入れるだけでおおよそ実現する
"ruby.lint": { "rubocop": true }, "[ruby]": { "editor.formatOnSave": false }, "ruby.format": "rubocop"参考: https://qiita.com/yumikokh/items/98be01df144c41d60e1e#formatting
上の参考サイトでは
"editor.formatOnSaveTimeout": 5000が必要とされていたが、私の環境では必要なかった。ハマり箇所
ただ、私の環境だとこの設定をした際、日本語が何故かユニコードに変換されてしまうという現象が起きた。コマンドラインからrubocop auto-correctを実施した際は全く問題無いが、VSCodeの自動整形でのみ発生する。
例えば、
context '未ログインの場合' do
が
context "\u672A\u30ED\u30B0\u30A4\u30F3\u306E\u5834\u5408" do
こうなる。ファイルはutf8になっており、文字コード周りの設定は問題ないとは思うのだが、、、
この辺りの話が関連しているのだろうか?わからん。
https://techracho.bpsinc.jp/hachi8833/2016_10_13/26969またこちらも理由は全くわからないが .rubocop.ymlの設定を以下のように変えたら上手くいった。
Style/StringLiterals: Enabled: true EnforcedStyle: double_quotes↓
Style/StringLiterals: Enabled: true EnforcedStyle: single_quotesdoubleをsingleに変えただけ。謎だ。。。
formatterとしてrubocop以外を利用した場合も日本語周りで問題が起きる。
おそらく何かしら問題があるだろうと思う。【今日の部会】
— みのも (@RNd8136) November 20, 2018
VScodeでRubyが自動整形されない
→Rufo拡張を入れる
それでも整形されない
→cmdでRufoをインストール
まだ整形されない
→日本語コメントが問題
→encoding: utf-8を最初に挿入
解決実際に私もやってみた結果、同じ結果に遭遇し、同じ対応で乗り越えることができた。
が、encoding: utf8 と書くのがなんとなく引っかかり、上記の対応を取ることにした。最後に
double_quotesが強制の環境だとキツイことになるな...どうしようか?
またもっと良い解決策をご存知の方がいましたら教えてください m(_ _)m
(しかしそもそもこれ再現性あるのだろうか。。。?)
- 投稿日:2020-09-06T14:03:29+09:00
google vision apiをhocalhostでリファラーの制限を設定する
google vision api を開発環境で実行すると↓のエラーになります。
```ruby'"message"=>Requests from referer are blocked.' '"status"=>"PERMISSION_DENIED",
```APIのリファラーの制限をしている
開発環境でリファラーの制限しても動作させる方法header にRefererを設定するとhocalhostでも動作する
https://qiita.com/kasajei/items/78f934b943ebdff7ef08
headers = {
'Content-Type': 'application/json',
'Referer': 'your domain here'
}
- 投稿日:2020-09-06T12:24:24+09:00
Ruby injectメソッドの使い方
- 投稿日:2020-09-06T11:45:12+09:00
bundle execが必要な理由
なぜbundle execが必要なのか
$bundle exec rails g controller userのようにすると、Gemfile.lockに記載されているものがrequireされる。
簡単に言うと、bundle exec をつけないで開発を行い、アップデートを行うという場合、動かなくなることがある。だから、bundle execを付けて開発を行うのが安牌なのかなという理解で、とりあえずは良いのかなと思いました。
参照
https://qiita.com/dawn_628/items/1821d4eef22b9f45eea8
この記事とかとても詳しく書かれていました。
- 投稿日:2020-09-06T10:15:06+09:00
Gitっぽい自作コマンドラインツールのTab補完をするやつ
はじめに
あー、ブラウザ立ち上げるのめんどくさい。またもやコマンドラインツールを作りたい熱がでてきた。なんかこんなやつ。
gitみたいに階層的にサブコマンドがあるやつTABで自動補完するやつ- シェルが立ち上がる
REPLっぽい雰囲気のやつ1- コマンド入力時にサーバの
REST APIコールするやつ- 結果をいい感じに整形してだしてくれるやつ
今回は、コマンドを定義して
TABで保管できるようにするところ。いいサンプルがみつからないので、調べながら作ってみました2デモ
今回はこんな感じのものを作るまでのところ
コマンドの階層が一つだけの場合
テンプレは基本的に
Readline使ってこんな感じです。TABで①が呼ばれて、リターンで②が回る一階層のコマンドrequire "readline" # 補完候補のコマンド pet_store = [ "pet", "store", "user"] # ①TAB押すと呼ばれて select で補完しようとする Readline.completion_proc = proc do |input| pet_store.select { |name| name.start_with?(input) } end # ②リターン押すと回る while input = Readline.readline("$ ", false) if input break if input == "q" p Readline.line_buffer.split if !input.empty? end end一階層の場合はコマンド候補が固定なので簡単です
コマンドが多階層の場合
多階層のコマンドというのは、さっきの例の補完候補のコマンドを現在のコンテキスト(前に選ばれたコマンド)に応じて変化すればいいんだと思います。
要は、「今選ばれているコマンド」に応じて「次の候補」を動的に入れ替えればいいんでしょう。ということでコマンドのツリーのようなものを事前に定義してみます。ツリー構造なので、
YAMLを使ってみます。petstore.rbrequire "readline" require "yaml" command_tree = <<-YAML pet: buy: dog: cat: sell: bird: fox: list: all: filter_by: store: find: by_name: by_tag: by_address: list: user: login: loout: sign_up: YAML command_tree = YAML.load(command_tree) #pp command_tree # 「今選ばれているコマンド」に応じて「次の候補」を動的に入れ替え(ツリーを降りていく) def current_option(command_tree, line_buffer) current_option = command_tree.keys line_buffer.split.each do |command| command_tree = command_tree[command] if command_tree.nil? current_option else current_option = command_tree.keys end end current_option end comp = proc do |input| current_option(command_tree, Readline.line_buffer).select { |name| name.to_s.start_with?(input) } end Readline.completion_proc = comp while input = Readline.readline("$ ", true) break if input == "q" p Readline.line_buffer.split if !input.empty? endやってることは:
- コマンドの階層を
YAMLで定義しておくReadlineでキーボード入力を受け取ったのがinputへTABをヒットしたときに、procの処理が動き、その時点のinputを元に、現在のコマンド候補の配列を入れ替える(階層を降る)- その配列に対して
selectで候補をマッチする候補を探すみたいな感じです
細かい修正
マイナーなケースかもしれませんが、上記のやり方だと、例えばコマンド候補が
petとpetGroupのようにpetまで補完してもまだ候補が複数マッチする時に、正しい候補が表示されません。これは、petまで補完した時に、petかpetGroupの候補をだしたいのですが、Hashのキーpetにマッチして、一階層降りてしまうので、petの下階層が候補となってしまうためです。これを避けるために、以下の1. のようにしてみました。def get_current_option(command_tree, line_buffer) current_option = command_tree.keys commands = line_buffer.split commands.each_with_index do |command, i| # 1. Don't go down to the lower level(and return current_option) in case current command matches multiple candidates such as "pet" and "petGroup" return current_option if i == commands.size-1 and !line_buffer.end_with?("\s") # 2. Go down if command_tree.has_key?(command) if command_tree[command].nil? # invalid command or key at leaf current_option = [] else command_tree = command_tree[command] current_option = command_tree.keys end end end current_option endこれで期待した動作になって、心地よくタイプできるようになりました
今後
ひとまず動きましたので、コマンドのパースや処理のところに行きます。補完についてもっといい実装方法があったらアドバイスを!
参考にしたサイト
- https://stackoverflow.com/questions/13876024/how-to-write-a-ruby-command-line-app-that-supports-tab-completion
- https://thoughtbot.com/blog/tab-completion-in-gnu-readline-ruby-edition
- https://thoughtbot.com/blog/tab-completion-in-gnu-readline
- http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/151732
- http://bogojoker.com/readline/
REPL (Read-Eval-Print Loop) とは、入力・評価・出力のループのこと。 インタプリタにおいて、ユーザーとインタプリタが対話的にコード片を実行できるもの。個人的に Cisco っぽいやつ、とも呼んでいる。 ↩
その昔、自作コマンドラインツールでオートコンプリートするのような記事を書きましたが、その
rubyバージョンです。前回と微妙に違うところとして、今回はREPL1 風にしています ↩
- 投稿日:2020-09-06T00:33:55+09:00
Rubyで値の取得をしたい
Rubyでの値の取得の一覧をメモ書き
値の取得
■ 値を一行で取得する
value = getsただこのままだと出力するときに改行されてしまうので chompメソッドを使う
■ 改行コードを切り落として取得する
value = gets.chomp取得した数値を計算をしたいときは to_i メソッドが便利
■ 値を数値として取得する
value = gets.to_ito_i メソッドは文字列を数値に変えることができる
逆に数値を文字列に変えたいときはto_sメソッドを使う■ 一行から複数要素を取得
a b c
このように値を1行でスペース区切りに入力されたときは split メソッド
value = gets.split(" ")split メソッドを使うことで値を配列の形で取得できる
value[0] や value[1] のように配列と同じ形で中身を確認できる■ 一行で取得した値を配列で取得
value = gets.chars
a b c
このように値を1行でスペース区切りに入力されたときに
value = ["a", " ", "b", " ", "c"]
という配列として値を取得します
value = gets.chomp.split(" ")このように記述することで改行コードも切り落とせるので便利
一般的にはこれの方が使うと思う
value = gets.split(" ").map(&:to_i)
値が全て数字の時はこのように記述すると配列の形で取得と数値に変換を同時にできる■ 値を複数行で取得する
value = readlinesredalinesは、複数行の文字列を1行ごとの配列として取得できる。
getsと違い、改行を入力してもキーボードからの入力は終了しない。
■ 複数行から複数要素を取得
値を複数行でスペース区切りに入力されたときは split メソッド
value = readlines.split.map(&:to_i)値を全て数値に変換して取得
以上です!
間違えている若しくはもっと簡単な方法があるときは教えて欲しいです!
- 投稿日:2020-09-06T00:10:46+09:00
[git hub]gitignoreを使ってファイルを除外する
概要
.gitignoreファイルを使い、指定のファイルをgit hubにアップしないようにする方法です。
rubyでbitcoinの自動売買システムを作成しています。ただ、bitflyerのAPI_KEYをgit hubにアップすると、セキュリティ上まずいです。色々調べると、.gitignoreファイルで対処可能であることが分かったので、記録しておこうと思います。
ruby on railsだと、環境変数でgit hubの管轄外にできることは知っていたのですが、今回は別の方法で。
手順
手順はざっとこんな感じです。
1 .gitignoreファイルをアプリケーション直下に作成
2 git statusで状況確認
3 .gitignoreファイルに除外したいファイルを記述1.gitignoreファイルをアプリケーション直下に作成
アプリケーションの直下に「.gitignore」ファイルを作成します。
拡張子と勘違いされて、保存する前に警告が出ますが、「.gitignore.」で保存すればいいみたいです。それと合わせて、bitflyerのAPI_KEYを記述するkey.rbの作成します。2 git statusで状況確認
git statusはcommitされていないファイルを確認するみたいのものです。
(間違っていたらすみません笑)
すると、先ほど作成した2つのファイルが確認できます。これをこのままリポジトリにpushするとAPI_KEYが公開されてしますので、「key.rb」を隠したいと思います。$ git statusInitial commit Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore key.rb nothing added to commit but untracked files present (use "git add" to track)3 .gitignoreファイルに除外したいファイルを記述
API_KEYの書いているkey.rbファイルを.gitignoreに記述すれば、git hubからの除外に成功です。
key.rbAPI_KEY = "ここにkeyが書いています" API_SECRET ="secret keyが書いています"gitignore.key.rb























