20200906のRubyに関する記事は24件です。

[ruby]標準入力からの値を受け取るには?

概要

2つ以上の入力の取得方法が分からず、調べていると解決したので、記録しておきます。
今回は1行に複数の要素が入力されている場合です。

本題

以下の入力結果を整数型に変換し、出力します。

標準入力
1999 2000
input_line = gets.split(' ').map(&:to_i)
puts input_line
出力結果
[1999, 2000]

「split」は要素を分解し、配列に格納します。

「map」はブロック内を評価し、配列に置き換えます。今回の場合、整数型に置き換えているということだと。

間違っていれば、ご指摘お願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアル テストについて

はじめに

Railsチュートリアルを学習すると、第3章にいかに「自動化テスト」が重要であるか書いてあります。テストの書き方については色々議論の余地があるそうですが、本記事ではテストについて自分なりに抑えていたほうが良いと思われることをまとめました。

テストフレームワークについて

テストフレームワークにもいくつか種類があり、それぞれコード書き方等の特徴があります、ここでは主だった2種類のフレームワークを紹介します。

参考にしました →Railsの人気テストフレームワーク6選!
         RSpecとMinitest、使うならどっち?

 Minitest

 ・Railsのデフォルトのフレームワーク。
 ・学習コストは比較的低い。
 ・Railsチュートリアルでも使用されている。
 ・処理速度は比較的早いといわれている。
 ・デフォルトの機能は必要最小限。
 ・コード量は少なく、シンプル

 Rspec

 ・自然言語で近い方たちで書かれている。
 ・利用率が高い。
 ・慣れるまでが時間がかかる。
 ・Minitestに比べると処理速度は遅いといわれている。
 ・デフォルトの機能は豊富。
 ・コード量は多くて、複雑。

どちらも一長一短みたいですね。初心者はMinitestから入って、その後Rspecも学ぶのが一番ですかね。

テストの種類について

参考する文献によって2種類だったり、それ以上だったりしますが今回は2種類のテストについて紹介します。

参考にしました →Rails テスティングガイド
         ソフトウェアテスト -wilki

 単体テスト

 関数やメソッドなど小さな単位で行うテスト。モデルやビューヘルパー単体の動作をチェックします。

例:

test/helpers/application_helper_test
  test "full title helper" do
    assert_equal full_title,         "Ruby on Rails Tutorial Sample App"
  end

assertとはオブジェクトまたは式を評価して、期待された結果(true)が得られるかどうかをチェックするコードです。
上記はassert_equalがfull_titleが"Ruby on Rails Tutorial Sample App"と完全一致しているかどうか検証しています。

test/models/user_test.rb
  test "name should not be too long" do
    @user.name = "a" * 51
    assert_not @user.valid?
  end

assert_notとは、assertとは逆にfalseが得られるかどうかをチェックするコードです。
上記はユーザーの名前が51文字の場合、ユーザーが有効でないことを検証しています。

 統合テスト

 プログラムを組み合わせて行うテスト。個々の機能が正しく連動しているか、ユーザーの実際の操作を想定してチェックします。

例:

test/integration/users_login_test
  test "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_path

2. ログインページのテンプレートが選択されているか検証する。
  →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?
  end

5. ログインページのテンプレートが選択されているか検証する。
  →assert_template 'sessions/new'

6. エラーメッセージが表示されていることを検証する。
  →assert_not flash.empty?

7. getメソッドでホームページ(root_path)を生成する。
  →get root_path

8. エラーメッセージが表示されていないことを検証する。
  →assert flash.empty?

という流れになっています。

まとめ

正直Railsチュートリアル終えても自力でテストをかけるか自信がないです。また、テストはどこまで厳密にやるかセンスが問われそうですね。数をこなしてなれていくのが大事だと思いました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】タグ管理機能(acts-as-taggable-on使用)

  • タグ付け機能を実装時の記録。エラーで戸惑ったので、備忘録として。
  • acts-as-taggable-on(gem)、UI に Tag-it( jQueryのプラグイン )を使用。

Alt text

環境

  • macOS Catalina
  • ruby 2.5.1
  • rails 5.0.7.2
  • acts-as-taggable-on 6.5.0 ( ※ ActiveRecord のモデルにタグ付けしてくれるGem)
  • DB mySQL

【 タグ付け機能 : 登録/削除/表示 】

1. gem導入

Gemfile
gem '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
end
xxxxxxxxxx_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)
end

4. ビュー

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. タグの表示

  • モデルで追加した:tagsがPostモデルに紐づいてる。
  • tag_listは配列なので、each文で取得。
    Alt text
タグの表示(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) 】

1. 導入

  1. tag-it のGitHubから、CloneDownload ZIP をクリック。
  2. ファイルを解凍し、JSとCSSディレクトリ内に、格納。
    cssフォルダ内の jquery.tagit.css 、 tagit.ui-zendesk.css → app/assets/stylesheets
    jsフォルダ内の tag-it.js、tag-it.min.js → assets/javascripts
  3. gem導入。
Gemfile
gem '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の表示を変更する↓↓ Alt text
    ※ ヘルプメッセージが表示された( .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. タグのインクリメンタルサーチ

  • Ajax通信で、DBのデータをTag-itオプションのavailableTagsに渡してあげる。 Alt text

4-1. ルーティング

  • 新規登録(id情報なし)、編集ページ(id情報あり)で、Ajax通信したい。
  • htmlで取得する予定がないので、jsonをフォーマットにしとく。
routes.rb
  resources :posts, expect: [:index] do
    get 'get_tag_search', on: :collection, defaults: { format: 'json' }
    get 'get_tag_search', on: :member, defaults: { format: 'json' }
  end

4-2. コントローラー

  • nameカラムがparams[:key]から始まる、Tagsテーブルのレコードを全取得(※ :keyは、jQueryで定義した入力値。Ajaxで送ってるモノを取得)。
postsコントローラー
def get_tag_search
  @tags = Post.tag_counts_on(:tags).where('name LIKE(?)', "%#{params[:key]}%")
end

4-3. jbuilder

  • コントローラーで定義した、@tagsのnameカラムのみ取得。
views/posts/get_tag_search.json.jbuilder
json.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
      });
    }
  })
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby on Rails】投稿機能の非同期通信、ajax化

目標

ajax.gif

開発環境

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'を導入

Gemfile
gem 'jquery-rails'
ターミナル
$ bundle install
app/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>

補足
render 'posts/new', posts: @posts は
app/views/posts/_new.html.erbの部分テンプレートを読み込みます。
そしてローカル変数に変更したpostsに@postsを代入します。
※その際、createとdestroyに@postsを定義する必要があります。
これは後ほど記述していきます。

部分テンプレートは以下のファイルを作成し、@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.rb
    def 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.rb
    def 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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.2

Gem
シリアライザ:active_model_serializers
認証:devise_token_auth
認可:pundit
ダミーデータ:Faker
静的コード解析:rubocop
テスト:RSpec, FactoryBot
(記事執筆中なので、今後増減の可能性あり)

Cloud9環境の構築

まずはAWSアカウントをお持ちでない場合、アカウントを取得しましょう。
本記事ではアカウントを所持していること前提として解説をします。

AWSマネジメントコンソールでCloud9を探します。
s1.png

Create environmentを押します。
s2.png

分かりやすい名前と説明を入れ作成します。
s3.png
2ページ目は基本的に全てデフォルトで問題ないでしょう。

構築に数分かかるので放置します。
完了したらこんなIDE画面が表示されます。

s4.png

Rubyのバージョンアップ

最初からRubyもRailsも入っていますが、バージョンが古いですね。

$ ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
$ rails -v
Rails 5.0.0

s5.png

記事執筆当時の安定バージョンは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

簡単ですね。

参考:Cloud9でRailsのバージョンを変更する

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から新しいターミナルを立ち上げます。

s6.png

ローカル等では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

して閉じます。

edit.png

ホストを許可する

Rails6から、ホストを許可しないとエラーでページが表示されません。
Cloud9で実行しているので、AWSのhostsを追加します。
保存したらテストサーバを一度Ctrl+Cで止め、再起動してください。そうしないとconfigが反映されません。

config/application.rb
...
module Bbs
  class Application < Rails::Application
...

+    config.hosts << '.amazonaws.com'

...
  end
end

Welcome画面を表示する

画面上部からPreview Running Applicationを押すと、小さくアプリケーションウィンドウが立ち上がります。

s7.png

ですがこの小さいウィンドウでは正常に実行されないため、Pop Out Into New Windowを押して別ウィンドウを立ち上げます。

s8.png

するとついに、正常にWelcome画面が表示されます。

welcome.png

続き

Rails 6で認証認可入り掲示板APIを構築する #2 gitとrubocop導入

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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のバージョンを変更したとき
のようなパターンでこのエラーが頻発しているように感じます。

同じエラーが発生した方へ届きますように!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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
end

1.親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]と記述すれば
親から情報を抜き出して子のビュー表示することができるという内容でした。

うまくまとめることができないのは私の力不足です・・・。
具体的な画像とテーブル情報が添付していると分かりやすくなったかも。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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
end

1.親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]と記述すれば
親から情報を抜き出して子のビュー表示することができるという内容でした。

うまくまとめることができないのは私の力不足です・・・。
具体的な画像とテーブル情報が添付していると分かりやすくなったかも。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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.yml
default: &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.yml
default: &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

これで無事完了。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

simple_formを使ってみる/子モデルまで編集する

概要

simple_formは高機能なフォームを自動作成してくれるgemです。https://github.com/heartcombo/simple_form
フォームを作ってくれるだけでなく、エラーメッセージや項目名なども自動で表示します。

今回はテーブルが2つのシンプルなrailsプロジェクトで、
simple_formを使ってみました

※bootstrapやCSSは書いていません

image.png

実装

まずは一番シンプルな実装userモデルのみ実装します

gemfile
gem 'simple_form'
user.rb
class User < ApplicationRecord
  validates :name, presence: true
  validates :email, presence: true
  validates :age, presence: true
end
routes.rb
  resources :users
users_controller.rb
class 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:migrate

db/migrate/20200906_create_users.rb
class 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
end

viewファイル(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にアクセスした結果が以下です。
image.png

presenceのバリデーションを設定している項目にはcan't be blank と自動で表示されています。

また、emailとurlはカラム名からsimple_formが判断して、各々最適なバリデーションをしてくれます。
image.png

image.png

吹き出しのデザインも勝手に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.rb
class UserProperty < ApplicationRecord
  belongs_to :user

  validates :nickname, presence: true
  validates :hobby, presence: true
end
user.rb
class User < ApplicationRecord
  validates :name, presence: true
  validates :email, presence: true
  validates :age, presence: true

 has_one :user_property
  accepts_nested_attributes_for :user_property
end
users_controller.rb
class 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:migrate

db/migrate/20200906_create_user_property.rb
class 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
end

simple_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の項目が二つ追加されています。これで完了です。
image.png

userと同様に、バリデーションエラーが表示されます
image.png

以上です

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(ギリ)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
=> nil

3. 上記の文字列の間にある半角スペースをタブに置き換えてみてください。(ヒント: 改行文字と同じで、タブも特殊文字です)
→ 下記

>> puts prefecture +  "\t" + city         
hokkaido        sapporo
=> nil

4. タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?
→ 下記

>> puts prefecture +  '\t' + city         
hokkaido\tsapporo
=> nil

 

【4.2.3 オブジェクトとメッセージ受け渡し 演習】

1. "racecar" の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。
→ 下記

>> s = "racecar"
=> "racecar"
>> s.length
=> 7

2. reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
→ 下記

>> s.reverse
=> "racecar"

3. 変数sに "racecar" を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
→ もう入れとったわ。下記

>> s == s.reverse
=> true

4. リスト 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_tester

2. 上で定義したメソッドを使って “racecar” と “onomatopoeia” が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文でない、という結果になれば成功です。
→ 下記

>> palindrome_tester("racecar")
It's a palindrome!
=> nil
>> palindrome_tester("onomatopoeia")
It's not a palindrome.
=> nil

3. 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!
=> nil

4. 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.rb
class 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}"
  end

3. 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)
 オブジェクト指向プログラミングで、オブジェクト内部のメンバ変数(属性、プロパティ)に外部からアクセスするために用意されたメソッド。メンバ変数をオブジェクト内部に隠蔽し、外部から直接参照させないようにするために用意される。

・メンバ変数
 インスタンス変数のこと。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

遭遇した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.rb
Traceback (most recent call last):
main.rb:1:in `<main>': uninitialized constant <class名> (NameError)

main.rbファイルの中にその様なclass名は無いと怒られています。
もし、別に切り分けているファイルを呼び出したくて起きたエラーならば、
下記の様にrequireを記述して別ファイルを呼び出せる様にしてあげましょう。

require './<class名>'

以下はエラーに遭遇、解決したら追記していく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb
Traceback (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の閉じ忘れの可能性もあり。インデントの位置も注意してみよう。

以下はエラーに遭遇、解決したら追記していく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 # => true
Module 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 => nil

selfがわからんからこれ覚える

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
end

self="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に-がつくパターンもある意味が分からん
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/Dockerfile
FROM 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.lock

RUNを複数書いてDockerのレイヤを重ねるとMySQL周りで実行時にエラーが出るので1つのコマンドで全環境を構築します。

Dockerのビルド

docker build -t utyosu/build-rails:latest . -f tools/ci/Dockerfile

Dockerイメージのアップロード

DockerHubのアカウントが必要になります。
はじめてのDockerHubリポジトリ登録あたりが参考になりそうです。

docker push utyosu/build-rails:latest

GitHubActionsのトリガを設定する

プロジェクトのリポジトリ内にGitHubAction用のファイルを作成します。

.github\workflows\build.yml
name: 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 と表示されます。

1.png

DetailsをクリックするとCIの進捗状況を確認できます。

3.png

少し待っているとコンソール出力が表示されます。

4.png

しばらくすると完了します。

5.png

PullRequestに戻ると All checks have passed と表示されて、問題なくテストが通ったことが分かります。

6.png

経緯

GitHub標準のRuby実行環境が用意されているので、最初はそれをベースにして試していました。
しかし、RailsやGemのインストールで30分以上かかってしまうので、軽めのプロジェクトにおけるCIには向いてないと思いました。
DockerHubからイメージをPullして実行する方法であれば、40秒くらいでテストの実行が開始できることが分かったのでこの方法を用いています。

メモ

別コンテナにMySQLとか用意してコンテナ同士でやり取りする方法もあるようですが仕組みが難しすぎて実現できませんでした。
Rails環境で上手く実現できたらいいなーって思ってます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 milkode

initialize

$ 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を使う
  • 色付き、全パッケージ検索に設定
.bashrc
alias gmilk='gmilk -a --color'

Web UI

WebUIを起動する。デフォルトの公開ポートは9292。

$ milk web -o `hostname -I` -n

WebUIをWindows側から起動できるようにする

Windowsでショートカットを作る
- デスクトップの何もないところで右クリック=>ショートカットの作成
- wsl.exeを実行するように設定
- 項目にwslとだけ書いて次へ(参照をする必要なし)
- ショートカット名を編集、完了
- できたショートカットを右クリック、プロパティ
- リンク先にコマンドラインを書く

wsl.exe - u <User> -- milk web -o `hostname -I` -n

できたショートカットをダブルクリックして実行するとでmilk webが実行される。

IPアドレスとポート番号は標準出力に出ているのでそれをブラウザで開く。

止めるときは、

  • このウインドウをAlt+F4してもプロセスは止まらない
  • プロセスを止めたいときはCTRL+Cで終了する、とウインドウも閉じる
    • 別口からkillでもいい

どうなった?

単一のパッケージ内ならあまり必要性はないが、

  • 複数パッケージにまたがる処理を追いかける時
  • 他のパッケージのコードを参考に書く時

などには重宝する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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
  end

binding.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に必要な情報を抜き出すことができた!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.yml

rubocopの対象外にしたいファイルは除外する。
参考: 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が想定通り動作すればOK

VSCode 上で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_quotes

doubleをsingleに変えただけ。謎だ。。。

formatterとしてrubocop以外を利用した場合も日本語周りで問題が起きる。
おそらく何かしら問題があるだろうと思う。

実際に私もやってみた結果、同じ結果に遭遇し、同じ対応で乗り越えることができた。
が、encoding: utf8 と書くのがなんとなく引っかかり、上記の対応を取ることにした。

最後に

double_quotesが強制の環境だとキツイことになるな...どうしようか?
またもっと良い解決策をご存知の方がいましたら教えてください m(_ _)m
(しかしそもそもこれ再現性あるのだろうか。。。?)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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'
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby injectメソッドの使い方

injectメソッドとは

ブロックを使用して繰り返し処理を行うメソッド
具体的には以下のように記述する

配列.inject {|初期値, 要素| 処理内容 }

使用例

  • injectメソッドの引数にsumの初期値を入れる

サンプルコード

[1,2,3].inject (0){ |sum, array_num| 
   p "array_num=#{array_num}"
   p sum+=array_num
}

実行結果

"array_num=1"
1
"array_num=2"
3
"array_num=3"
6
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bundle execが必要な理由

なぜbundle execが必要なのか

$bundle exec rails g controller user

のようにすると、Gemfile.lockに記載されているものがrequireされる。

簡単に言うと、bundle exec をつけないで開発を行い、アップデートを行うという場合、動かなくなることがある。だから、bundle execを付けて開発を行うのが安牌なのかなという理解で、とりあえずは良いのかなと思いました。

参照
https://qiita.com/dawn_628/items/1821d4eef22b9f45eea8
この記事とかとても詳しく書かれていました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gitっぽい自作コマンドラインツールのTab補完をするやつ

はじめに

あー、ブラウザ立ち上げるのめんどくさい。またもやコマンドラインツールを作りたい熱がでてきた。なんかこんなやつ。

  • git みたいに階層的にサブコマンドがあるやつ
  • TAB で自動補完するやつ
  • シェルが立ち上がる REPL っぽい雰囲気のやつ1
  • コマンド入力時にサーバの REST API コールするやつ
  • 結果をいい感じに整形してだしてくれるやつ

今回は、コマンドを定義して TAB で保管できるようにするところ。いいサンプルがみつからないので、調べながら作ってみました2

デモ

今回はこんな感じのものを作るまでのところ

ac.gif

コマンドの階層が一つだけの場合

テンプレは基本的に 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.rb
require "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 で候補をマッチする候補を探す

みたいな感じです

細かい修正

マイナーなケースかもしれませんが、上記のやり方だと、例えばコマンド候補が petpetGroup のように pet まで補完してもまだ候補が複数マッチする時に、正しい候補が表示されません。これは、pet まで補完した時に、petpetGroup の候補をだしたいのですが、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

これで期待した動作になって、心地よくタイプできるようになりました

今後

ひとまず動きましたので、コマンドのパースや処理のところに行きます。補完についてもっといい実装方法があったらアドバイスを!

参考にしたサイト


  1. REPL (Read-Eval-Print Loop) とは、入力・評価・出力のループのこと。 インタプリタにおいて、ユーザーとインタプリタが対話的にコード片を実行できるもの。個人的に Cisco っぽいやつ、とも呼んでいる。 

  2. その昔、自作コマンドラインツールでオートコンプリートするのような記事を書きましたが、その ruby バージョンです。前回と微妙に違うところとして、今回は REPL1 風にしています 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyで値の取得をしたい

Rubyでの値の取得の一覧をメモ書き

値の取得

■ 値を一行で取得する

value = gets

ただこのままだと出力するときに改行されてしまうので chompメソッドを使う

■ 改行コードを切り落として取得する

value = gets.chomp

取得した数値を計算をしたいときは to_i メソッドが便利

■ 値を数値として取得する

value = gets.to_i

to_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 = readlines

redalinesは、複数行の文字列を1行ごとの配列として取得できる。

getsと違い、改行を入力してもキーボードからの入力は終了しない。

■ 複数行から複数要素を取得

値を複数行でスペース区切りに入力されたときは split メソッド
value = readlines.split.map(&:to_i)

値を全て数値に変換して取得

以上です!
間違えている若しくはもっと簡単な方法があるときは教えて欲しいです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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 status
Initial 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.rb
API_KEY = "ここにkeyが書いています"
API_SECRET ="secret keyが書いています"
gitignore.
key.rb
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む