20201028のRubyに関する記事は27件です。

Rails+herokuでLINEbotを作ってみた

作ったもの

毎朝ゴミ出しの通知をしてくれるBOTがあったら便利だなーと思い、
勉強がてら作ってみました。

スクリーンショット 2020-10-28 23.01.21.png

使用言語

Ruby
Ruby On Rails6
Heroku
LINE Messager API

STEP 1 メッセージをオウム返しするLINEBOTの作成

こちらについては他の方が詳しく投稿されているので割愛します。

参考記事

https://qiita.com/y428_b/items/d2b1a376f5900aea30dc

https://qiita.com/natsukingdom-yamaguchi/items/e84dffdd90d7f5ef8224

STEP 2 PUSHメッセージ

タスクを追加し、決まった時間になったらメッセージを送信します。

タスクの作成

$rails g task reminder_task

次にコントローラーを生成

$rails g controller webhook trash

descriptionとtaskを記述

lib/tasks/reminder_task.rake
namespace :reminder_task do
  desc "ここにタスクのお題"
  task :trash => :environment do
    webhook = WebhookController.new
    puts webhook.trash
  end
end

次にWebhookcontrollerのアクションを定義

app/controllers/webhook_controller.rb
  def trash
    puts "メッセージ"
  end

ここでrakeタスクに追加されているか確認

$ rake -T
rake reminder_task:trash  タスクのお題  

追加されているか確認が出来たら

$ rake reminder_task:trash

【メッセージ】が送信されたらOK

環境変数の設定

PUSHメッセージを利用する場合は環境変数を設定しなければなりません。

環境変数については以下を参照しました。
https://qiita.com/yuichir43705457/items/7cfcae6546876086b849
https://qiita.com/noraworld/items/bfa80811c9e30b4474af

設定方法

dotenvをgemに追加します

gem 'dotenv-rails'

.envファイルをアプリケーションディレクトリのルートディレクトリに作成
(appやgemfileがある場所に作成)

LINEチャネルの諸々を設定

/.env
LINE_CHANNEL_SECRET='xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
LINE_CHANNEL_TOKEN='yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
LINE_USER_ID='zzzzzzzzzzzzzzzzzzzzzz'

Webhookcontrollerのtrashアクションを書き換える

app/controller/webhook_controller.rb
class WebhookController < ApplicationController
  require 'line/bot'

def trash
  message = {
            "type": "text",
            "text": WebhookController.contents
           }
            client = Line::Bot::Client.new { |config|
              config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
              config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
           }
            response = client.push_message(ENV["LINE_USER_ID"], message)
  end

  def self.contents

      date = Date.today

      case date.strftime('%a')
        when "Mon"
            "今日は月曜日、燃えるゴミの日!"
        when "Tue"
            "無し!"
        when "Wed"
            "今日は水曜日、燃えないゴミ・段ボールの日"
        when "Thu"
            "今日は木曜日、普通ゴミの日だわ!"
        when "Fri"
            "今日は金曜日、缶・瓶・ペットボトルの日!"
        when "Sat"
            "一週間お疲れ!今日は無し!"
        else
            ""
        end
    end
  end

HerokuにPushしスケジューラーを設定すれば、メッセージが届くはずです。

最後に

rubyを勉強して初めて作ったサービス?になりますので、間違ってる点などが多々あるかと思われます。
その際は、ご指摘いただけると嬉しいです!

(参考記事の方々、ありがとうございました。)

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

【Ruby On Rails】FactoryBotを用いてRSpecでモデル単体テストを行うと外部キーが入力されていないことでエラーになる

初投稿
備忘録です。

エラー背景

RSpecでテストを実行する際に、FactoryBot内に記述すべき外部キーについて未入力であった。

FactoryBot.define do
  factory :order_form do
    zipcode       {'123-4567'}
    prefecture_id {1}
    city          {'亜嗚呼市あああ区'}
    address       {'亜嗚呼1-1-1'}
    phone_number  {'09012345678'}
  end
end

エラー内容

OrderForm
  商品購入
    商品購入がうまくいく時
      全項目が存在すれば購入できる (FAILED - 1)

Failures:

  1) OrderForm 商品購入 商品購入がうまくいく時 全項目が存在すれば購入できる
     Failure/Error: expect(@order_form).to be_valid
       expected #<OrderForm:0x00007fe44961c1e8 @zipcode="123-4567", @prefecture_id=1, @city="亜嗚呼市あああ区", @address="亜嗚呼1-1-1", @phone_number="09012345678", @validation_context=nil, @errors=#<ActiveModel::Errors:0x00007fe448aeeaa0 @base=#<OrderForm:0x00007fe44961c1e8 ...>, @messages={:user_id=>["can't be blank"], :item_id=>["can't be blank"]}, @details={:user_id=>[{:error=>:blank}], :item_id=>[{:error=>:blank}]}>> to be valid, but got errors: User can't be blank, Item can't be blank
     # ./spec/models/order_form_spec.rb:10:in `block (4 levels) in <top (required)>'

Finished in 0.17606 seconds (files took 1.9 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/order_form_spec.rb:9 # OrderForm 商品購入 商品購入がうまくいく時 全項目が存在すれば購入できる

注目すべき点はこちらです。

@errors=#<ActiveModel::Errors:0x00007fe448aeeaa0 @base=#<OrderForm:0x00007fe44961c1e8 ...>, @messages={:user_id=>["can't be blank"], :item_id=>["can't be blank"]}, @details={:user_id=>[{:error=>:blank}], :item_id=>[{:error=>:blank}]}>> to be valid, but got errors: User can't be blank, Item can't be blank

外部キーとしていたuser_id、item_idが空欄になっているということを教えてくれました。

エラー修正

エラー内容より、FactoryBot内の記述を修正すれば良いことがわかりました。
あとは指示された通りuser_idとitem_idをFactoryBotに追加してあげれば大丈夫です。

    user_id { FactoryBot.create(:user).id }
    item_id { FactoryBot.create(:item).id }

修正後のターミナル

OrderForm
  商品購入
    商品購入がうまくいく時
      全項目が存在すれば購入できる

Finished in 0.3807 seconds (files took 3.22 seconds to load)
1 example, 0 failures

成功です。

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

rails + postgres sqlでポートフォリオ作成

記事の概要

私が作成したポートフォリオの解説です。
作った背景、機能、苦労した点、工夫したところ、今後の課題をまとめました。

実際に作成したサイトやソースコードは下記のリンクからご覧いただけます。
https://github.com/rotosiri-zu/pcparts
https://pcparts-staging.herokuapp.com/

作った背景

趣味で自作PCを作成したりしているのですが自分が知らないPCパーツのスペックをしりたい他のユーザーはどんなPCパーツを使っているのか気になり、自作PCを作成している他のユーザーからの紹介で使用しているPCパーツや購入したもので作成するときのパーツ選びに参考になればと思いWebアプリを作りました。

また、現職を退職し、Web系開発企業のバックエンドエンジニアになろうと考えていたので、
このアプリを転職活動のポートフォリオとすることに決めました。

スペック

言語

Ruby 2.6.6

フレームワーク

ruby on rails 5.2.4.3

cssフレームワーク

bootstrap4

データベース

postgres sql

バージョン管理

github

本番環境

heroku + Amazon S3

主な機能

ログイン・ログアウト機能

登録したユーザーやゲストログインでログインできます。ホーム画面からもゲストログインでログインすることができます。

circleanimationmuvie

投稿機能

会員登録することで自由に投稿することができます。
入力に誤りがあれば、投稿はされずエラ-メッセージが表示されます。

2020-10-2619 42 59

マイページ機能

ユーザーが投稿したアイテム一覧を確認することができます。

224a16330e9b45b78577fffe23533062

商品名検索機能

投稿時に記入したタイトル名で検索できます。

d32d3f013b5898f6588425a0f95a3f51

カテゴリー検索機能

投稿時に選択肢したカテゴリーで検索できます。

1b3fedbfed2ba3dca6fd34f10a117d3f

口コミ投稿機能

PCパーツの感想を投稿できます。
入力に誤りがあれば、投稿はされずエラ-メッセージが表示されます。

2020-10-2720 33 12

アイテム更新・削除機能

投稿したアイテムを更新・削除ができます。

2020-10-2620 18 54

口コミ更新・削除機能

投稿した口コミを更新・削除ができます。

2020-10-2620 24 43

苦労した点

カテゴリー機能の機能追加でIDエラーの解決に時間がかかったところです。
解決した方法は検証と仮説を立てその後に検索して調べ見つけた記事の通りやってみたところ解決しました。

工夫した点

ホーム画面からゲストログインできるようにしホーム画面に機能の説明を記載したり説明とアイテム投稿一覧の境目に黒線を追加、詳細画面の口コミ投稿のフォームに黒線を追加してわかりやすくしました。

なぜruby on railsの言語を選んだのか

ruby on railsを選んだ理由は初心者が入りやすく参考にする記事やコミニティが多いからruby on railsを選択しました。

今後の課題

今後の課題はテストコードがかけていないのでテストの実装やパンくずリストの実装を追加しようと考えております。

参考文献

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

railsでapp名を変更する方法

環境

Rails 6.0.3.4

流れ

1.gemを入れる
2.dbを削除(不要なら)
3.app名を変更
4.db作成

手順

1.gemを入れる

Gemfileに以下を追記

gem 'rename'

bundleする

$ bundle

2.dbを削除(不要なら)

特になければdbを削除

$ rake db:drop

3.app名を変更

$ rails g rename:into {{新しいapp名}}

作成したapp名のフォルダへ移動

$ cd ../{{新しいapp名}}

4.db作成

dbを作成して

$ rake db:create

マイグレーションする

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

東西南北の緯度経度から任意の数に分割する

# 縦横何分割するか
DEVIDE_COUNT = 7 

# 配列の中央値
cneter_index = (DEVIDE_COUNT / 2) + (DEVIDE_COUNT % 2)

# 東西南北
origin_n = 0
origin_s = 50
origin_w = 100
origin_e = 200

north_south_unit = (origin_s - origin_n).fdiv(DEVIDE_COUNT)
west_east_unit = (origin_e - origin_w).fdiv(DEVIDE_COUNT)

# 北から南、西から東へ分割していく distance_rankは中心から遠いほど大きくなる
Mesh = Struct.new(:nl, :sl, :el, :wl, :distance_rank)
results = []
(1..DEVIDE_COUNT).each do |ns_count|
  (1..DEVIDE_COUNT).each do |ew_count|
    results << Mesh.new(
      origin_n + (north_south_unit * (ns_count -1)),
      origin_n + (north_south_unit * ns_count),
      origin_w + (west_east_unit * (ew_count -1)),
      origin_w + (west_east_unit * ew_count),
      (cneter_index  - ns_count).abs + (cneter_index - ew_count).abs
    )
  end
end

pp results.sort_by(&:distance_rank)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【忘備録】Rubyをやってみて何度も調べたコマンド一覧

はじめに

Rubyを半年程さわってみてなんとなく調べる頻度が多かったコマンドをここでまとめてます。初学者兼初投稿なのでコメントやLGTMなどでフィードバック頂けると励みになります。よろしくお願いします。

Capistrano関連

デプロイ実行
$ bundle exec cap production deploy
1つ前の状態にロールバック
$ bundle exec cap production deploy:rollback

unicorn関連

起動状態を確認する
$ ps auxwww | grep unicorn
プロセスを終了する
$ kill -9 ○○○○○○
すべてのログを確認する ($ cd /var/www/app/log にて)
$ less unicorn.staderr.log
直近10件のログを確認する ($ cd /var/www/app/log にて)
$ tail unicorn.staderr.log

nginx関連

再起動する
$ sudo service nginx restart
すべてのログを確認する (本番環境のホームディレクトリにて)
$ sudo less /var/log/nginx/error.log

MySQL関連

起動状況を確認する
$ sudo service mysqld status
起動する
$ sudo service mysqld start

本番環境まわりのコマンドばかりになってしまいましたが、過不足があれば随時更新していきます。

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

【Ruby on Rails】try(:[ ], :key)を読み解く

はじめに

user.try(:[],:name)

初めてこのコードを見た時、
「なんで空の配列をメソッドの部分に入れているんだ、、、?」
と思いました。
Ruby(Rails)の文法をすっ飛ばしてきた弊害ですね、、、

そもそもRailsのtryとは?

Active Supportに搭載されているメソッド (Rubyだけでは使えない)
対象のオブジェクトがnilでない時のみ、指定したメソッドを使う
第一引数に使いたいメソッド、第二引数にその引数(必要であれば)を与えます

user = {name: "test"}

# user[:group]がnilであるため、メソッドエラーが起きる
user[:group].replace("A")
# => NoMethodError: undefined method 'replace' for nil:NilClass

# nilな対象に対してtryを使うとエラーは起きず、nilが帰ってくるだけ
user[:group].try(:replace,"A")
=> nil

空の配列がメソッドに?

それでは本題ですが、rubyのHashには[]のメソッドがあります。
引数にkey名を取ってそのvalueを返します。

user.[] :name
=> "test"
# これはuser[:name]と同じ

ちなみに、「+」などもメソッドとして使えます。

user[:name].+ 2
=> "test2"


今回の場合はtryとこのメソッドの組み合わせなので

user.try(:[],:name)
=> "test"

という結果が返ってきます。

まとめ

[]についてなかなかヒットしなかったので少し理解に苦しみました。
ruby/railsの文法を勉強し直します、、、

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

【Rails】星★の評価を実装する(入力、保存、表示)

はじめに

開発しているメモアプリにランク付けをしていきたいと思います。
今回は三段階で評価していこうと思います。
それに伴い、星★による評価の入力・保存・表示について学習していきます。

参考

jQuery Raty

Railsで星の評価機能をつける

jQuery Ratyの使い方

マイグレーションファイルにカラムを追加

notesテーブル(メモテーブル)のrateカラム(型:float)に評価値を保存する
すでに、テーブルとカラムは作成されていることを前提に進めます。

カラムは、半分の星による評価を行う場合、「0.5」や「1.5」という値を保存することになるため、float型にしておく必要があります。

もし、integerやstringの型として作成してしまっていた場合は、データベースによって変更方法は異なりますが、型の変更が必要になります。下記の記事でこの型の変更の沼にハマってしまった時の解決策を書いています。

class CreateNotes < ActiveRecord::Migration[5.2]
  def change
    create_table :notes do |t|
      t.text :title
      t.integer :user_id
      t.integer :category_id
      t.text :explanation
      t.float :rate

      t.timestamps
    end
  end
end
rails db:migrate:reset

javascriptの読み込み、★画像の読み込み

上記参考リンクを確認しながら、javascriptの読み込みと星の画像の読み込みを行います。

やり方(2種類)

①jQuery Ratyを使用するには、https://github.com/wbotelhos/raty からjquery.raty.jsをダウンロードする。

ダウンロードしたスクリプトをウェブサイトの任意の場所に配置する。

app/javascriptsのフォルダの中にダウンロードしたjquery.raty.jsファイルを配置する

②jQuery Ratyを使用するHTMLの中でJavaScriptを読み込む。RatyはjQueryのプラグインなので、jQueryのスクリプトも必要である。

    <script src="/js/jquery.min.js"></script>
    <script src="/js/jquery.raty.js"></script>

今回は①のやり方で実装していきます。

*ここでJavaScriptも定義していきます。
javascripts/application.jsファイルに追加する

//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery       #追加
//= require jquery_ujs  #追加
//= require_tree .

Gemファイル追加

gem 'jquery-rails'
budle install

モデルの記述(紐付け)

note.rb
class Note < ApplicationRecord
  # ユーザーとの紐付け
  belongs_to :user,optional: true

 # バリデーション
  validates :title, presence: true
  validates :explanation, presence: true

  # カテゴリーと紐付け
  belongs_to :category

  validates :rate, presence: true
  validates :rate, numericality: {
    # only_integer: true,
    less_than_or_equal_to: 3,
    greater_than_or_equal_to: 1,
  }

 # numericality=空を許可する
    numericalityは、デフォルトでは小数も許容してしまいます。rateカラムでは整数のみ許可したいので、 only_integerを。
 
   validates :param, :numericality => { :less_than_or_equal_to => }
   # 数字が3以下であるか
   validates :param3, :numericality => { :greater_than_or_equal_to =>  }
   # 数字が1以上であるか

end

コントローラーの記述

notes_controller.rb
class NotesController < ApplicationController
  before_action :authenticate_user!
  def new
    @note = Note.new
  end

  def create
    # @note = Note.new(note_params)
    @note = current_user.notes.build(note_params)
    @note.save
    redirect_to notes_path
  end

  def index
    # is_validがマッチするレコードを全て取得
    @categorys = Category.where(is_valid: true)
    @q = Note.all.ransack(params[:q])
    @notes = @q.result(distinct: true)
  end

  def show
    @note = Note.find(params[:id])
  end

  def edit
    @note = Note.find(params[:id])
  end

  def update
    @note = Note.find(params[:id])
    @note.update(note_params)
    redirect_to note_path
  end

  def destroy
    @note = Note.find(params[:id])
    @note.destroy
    redirect_to notes_path
  end

  def search
    @categorys = Category.where(is_valid: true)
    @category = Category.find(params[:id])
    @q = @category.notes.all.ransack(params[:q])
    @notes = @q.result(distinct: true).page(params[:page])
    @title = @category.name
    render 'notes/index'
  end

  private

  def note_params
    params.require(:note).permit(:title, :category_id, :explanation,:user_id,:rate)
  end
end

ここではnote_paramsメソッドにrateカラムを追加します。

メモ登録フォームを作る

_form.html.erb
<%= form_with model:note, local: true do |f| %>
  <div class='form-group'>
    <%= f.label :タイトル %>
    <%= f.text_field :title, class: 'form-control', id: 'note_title' %>
  </div>
  <div class='form-group'>
    <%= f.label :カテゴリー %>
    <%= f.collection_select :category_id, Category.all, :id, :name %>
  </div>
  <!-- 評価 -->
  <div class="form-group row" id="star">
    <%= f.label :rate,'重要度 ', class:'col-md-1 col-form-label' %>
    <%= f.hidden_field :rate, id: :review_star %>
  </div>
  <div class='form-group'>
    <div id='editor'>
      <%= f.label :内容 %>
      <%= f.text_area :explanation, rows: 10, class: 'form-control', id: 'note_explanation', "v-model" => "input", name: "note[explanation]" %>
    <h2><i class="fas fa-eye"></i> Preview</h2>
    <div id="preview-field" v-html='input | marked'>
    </div>
    <div ></div>
  </div>
  <%= f.submit '登録', class: 'btn btn-success' %>
  </div>
  <% end %>

  <!-- リアルタイムプレビュー -->
  <script type="text/javascript">
    window.onload = function() {
      new Vue({
      el: '#editor',
      data: {
        input: '<%== j @note.explanation %>',
      },
      filters: {
      marked: marked,
      },
      });
    };

   <!-- 評価 -->
    $('#star').raty({
      size     : 36,
      starOff:  '<%= asset_path('star-off.png') %>',
      starOn : '<%= asset_path('star-on.png') %>',
      starHalf: '<%= asset_path('star-half.png') %>',
      scoreName: 'note[rate]',
      half: true,
    });
  </script>

ポイントは下記

# ★の半分の入力ができるようにするため
  half: true,

星による評価の表示

メモ一覧にて、★を表示したい。
上記の「★の入力、保存」と異なる点は、1.入力はせず表示する 、 2.繰り返し処理をするということになります

_notes_index.html.erb
<div class='row'>
  <table class='table'>
    <thead>
      <tr>
        <th>タイトル</th>
        <th>カテゴリー</th>
        <th>重要度</th>
      </tr>
    </thead>
    <tbody>
      <% @notes.each do |note| %>
      <% if user_signed_in? && current_user.id == note.user_id %>
      <tr>
        <td>
          <%= link_to note_path(note) do %>
            <%= note.title %>
          <% end %>
        </td>
        <td><%= note.category.name %></td>
        <!-- 評価 -->
     <td>
         <div id="star-rate-<%= note.id %>"></div>
          <script>
            // 繰り返し処理でもidをidを一意にできるようにnote_idを入れる
          $('#star-rate-<%= note.id %>').raty({
            size: 36,
            starOff:  '<%= asset_path('star-off.png') %>',
            starOn : '<%= asset_path('star-on.png') %>',
            starHalf: '<%= asset_path('star-half.png') %>',
            half: true,
            // 読み込みだけで入力できない
            readOnly: true,
            score: <%= note.rate %>,
          });
          </script>
        </td>
      </tr>
      <% end %>
      <% end %>
    </tbody>
  </table>
</div>

ポイントは下記

# 繰り返し処理を実行してもidを一意に保てるようにreview.idを埋め込む
<div id="note-rate-<%= note.id %>"></div>

# 読み取り専用(入力できない)
readOnly: true,

# 星の入力値を読み込む
score: <%= note.rate %>,

これで完成!

最後に

説明が分かりづらかったりすると思いますがご了承ください。
また、間違っているところがあればご教授いただけると幸いです。

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

Railsで多言語化対応する!

自分が開発しているwebアプリを英語に対応しようとしたときにやったことを書いていきます。

詳しくは>

インストール

Gemfile
gem 'rails-i18n'

インストール

bundle

config/application.rbに設定

config/application.rb
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]

    # 対応する言語。今回は日本語(ja)と英語(en)だけ
    config.i18n.available_locales = %i(ja en)

    # 上記の対応言語以外の言語が指定された場合、エラーとするかの設定
    config.i18n.enforce_available_locales = true

    # デフォルトの言語
    config.i18n.default_locale = :ja

controller

controller/application_controller.rb
    before_action :set_locale

    def set_locale
        I18n.locale = locale
    end

    def locale
        @locale ||= params[:locale] ||= I18n.default_locale
    end

    def default_url_options(options={})
        options.merge(locale: locale)
    end

routes.rb

routes.rb
  scope '(:locale)', locale: /#{I18n.available_locales.map(&:to_s).join('|')}/ do
    root "posts#index"
    resources :posts
  end

scopeで囲んだ部分が多言語対応するページです。

多言語化

en.ymlとja.ymlを作ります。
多言語化したい文に

views
<%= t(:hello) %>

と書きます。helloのところは自由に変えてください

ja.yml
  ja:
    hello: こんにちは
      # モデルは全て activerecord を起点にする。
      # これにより、User.model_name.human / User.human_attribute_name({attr_name})でアクセス可能。
    activerecord:
      models:
        user: 'ユーザー情報'
      attributes:
        user:
          name: '名前'
          mail: 'メールアドレス'
          url:  'ウェブページ'
en.yml
  en:
     hello: "hello"
    # モデルは全て activerecord を起点にする。
      # これにより、User.model_name.human / User.human_attribute_name({attr_name})でアクセス可能。
    activerecord:
      models:
        user: 'User'
      attributes:
        user:
          name: 'Name'
          mail: 'Email'
          url:  'URL'

こんな感じでやっていきます。

参考
http://alfa.hatenablog.jp/entry/2013/12/03/221308

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

bundler

Mac OS X-10.15.7 ruby-2.7.1p83

動作のメモ

動くようになったら使えるようにしましょう.shebangというのを設定して,さらにexecutableに直します.

  • mv hello.rb hello
  • #!/usr/bin/env ruby
  • chmod a+x hello

置き場所を工夫すれば,とても便利です.

  1. . # very local
  2. ./bin # project
  3. ~/bin # user
  4. gem # all users

tight bindingという考え方があります.これは,必要なファイルを出来るだけ必要な場所に近いところに置いておくことを意図しています.そうすると可搬性が増すから.

execなどの種類で整理したり,~/binなどに置いておくとそのうち爆発します.localはlocalに必要なprojectで実行できるように.さらに,チョコチョコ直すのも気兼ねなくできるようになります.ここでしか使わないから.

でも,便利なものや,いろいろな場面で使う共通のアプリはいろいろなところから使えると便利ですよねそういう時にはgemにしちまいましょう.

bundler

  • gem install bundler
  • bundle gem hogehoge -b

です.これでOKです.あとは,少し修正です.

  • rm -rf hogehoge/.git

bundleはdefaultでgitでのbackup管理をします.なので,その設定をする必要がありますが,今は,それを外します.


  • source ~/git_hub/ruby_docs/chart_style_ruby/./c0_bundler.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】遷移前のURLのパス名を取得してリンク先を変更する

環境

rails 6.0.3

説明

遷移前のページのURL内のパス名によって、ページに表示するリンク先を分岐させる方法。

補足

パス名とは?
https:///www.example.com/user
だった場合は、パス名は/user

該当のviewファイル
<% if URI(request.referer).path == 'パス名' %>
  #前のページが指定したパスだった場合のリンク先
<% else %>
  #その他のリンク先
<% end %>
  1. request.refererは文字列を返す。
  2. RubyURIモジュールを使用して、request.refererをラップすると、パスだけを要求することができる。

参考記事

URLすべてを取得して、ページに表示するリンク先を分岐させる方法
https://qiita.com/taka_571/items/9b1c82d8fcc602df8a1a

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

Railsで遷移前のURLのパス名を取得してリンク先を変更する

環境

rails 6.0.3

説明

遷移前のページのURL内のパス名によって、ページに表示するリンク先を分岐させる方法。

該当のviewファイル
<% if URI(request.referer).path == 'パス名' %>
  #前のページが指定したパスだった場合のリンク先
<% else %>
  #その他のリンク先
<% end %>

補足

例えば、URL
https:///www.example.com/user
だった場合は、パス名は/user になる。

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

Expected exactly 2 elements matching "a[href="/"]", found 0.. のエラーメッセージ

Expected exactly 2 elements matching "a[href="/"]", found 0..のエラーメッセージ



Railsチュートリアルをやっている時にエラーが起きたので、解決した方法を書きます。やっていたのは5章の「5.3.4 リンクのテスト」の所です。
エラーメッセージは

 FAIL["test_layout_links", SiteLayoutTest, 2.2938910419998138]
 test_layout_links#SiteLayoutTest (2.29s)
        Expected exactly 2 elements matching "a[href="/"]", found 1..
        Expected: 2
          Actual: 1
        test/integration/site_layout_test.rb:10:in `block in <class:SiteLayoutTest>'

  1/1: [===================================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.29667s
1 tests, 2 assertions, 1 failures, 0 errors, 0 skips

}



私の場合はこのファイルに異常がありました。
app/views/layouts/_header.html.erb

<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "sample app", root_path, id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "Home",   'root_path' %></li>
        <li><%= link_to "Help",   'help_path' %></li>
        <li><%= link_to "Log in", '#' %></li>
      </ul>
    </nav>
  </div>
</header>



この中でも今回は、上記のroot_pathとhelp_pathに' 'がついていたことが原因みたいでした。このエラーが出た原因は、もともとあった'#'にroot_pathなどを入れたことで外し忘れていたみたいでした。
正しくはこちらになります。

        <li><%= link_to "Home",   root_path %></li>
        <li><%= link_to "Help",   help_path %></li>



因みに、app/views/layouts/_fotter.html.erbにも同じようなミスがあったので、上記のエラーが起こった方は確認してみると良いかもしれません。

参照元

https://teratail.com/questions/189008

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

〇〇env系ツールをanyenvで一括管理する

はじめに

この記事は フューチャーAdvent Calendar 2020 の20日目の記事です。

環境構築に便利な〇〇env系ツールを、もっと便利に扱える anyenv に感動したので、ブログ化しました。

開発言語のバージョン管理

複数プロジェクトに関わる場合、開発言語のバージョン管理は必須スキルです。
他の開発メンバーと利用バージョンを一致させなければ、フォーマッタ適用 → PRでの差分爆発が生じてレビュアーに袋叩きにされます。

なので、各エンジニアはそれぞれの創意工夫により、開発言語のバージョン管理問題を解決しています。

私の場合、環境構築ポリシーとして
- ローカル環境への直ダウンロードは厳禁
- 環境構築は基本的にDockerを使う

という方針を、ずっと貫いています。

...

スミマセン。強がりです。
貫けていませんでした。

〇〇env の多用

コロナ影響で在宅勤務へ移行した結果、NWの問題によりDockerがうまく使えない状況が増え、仕方なく〇〇env系ツールの利用を開始しました。

インストールした〇〇env系ツールは
- PJ開発はメインでgoを使う → goenv
- 環境Aはpythonのみ動く → pyenv
- 環境Bはrubyのみ動く → rbenv
- AWSのMFA(多要素認証)でet-otpを使う → jenv

というもので、開発作業で新しい言語が必要になるたびに、対応するツール1つ1つ追加していました。

これらは同じコマンド操作で設定可能なため、ストレスになるのはパッケージのダウンロード時間くらいだと思っていましたが、知らぬ間に開発環境汚染が進んでいました。

bash_profileが汚れていた

担当業務で Vue.js のコードに触れる機会があり、新たに nodenv をインストールしようとしたところ
- 〇〇env系ツールの環境構築って、いっつも同じような環境変数を追加しているな
- bash_profileのメンテしてたっけ?

ということに気づき、cat ~/.bash_profile すると、↓こんな感じになっていました

...
# goenv
export GOENV_ROOT=$HOME/.goenv
export PATH=$GOENV_ROOT/bin:$PATH
eval "$(goenv init -)"

# pyenv
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

# jenv
export JENV_ROOT=$HOME/.jenv
export PATH="$HOME/.jenv/bin:$PATH"
eval "$(jenv init -)"

(rbenv, nodenv などの設定が続く...)
...

あれ?

開発環境が汚れないよう注意していたはずなのに
所々で微妙な違いがあり、綺麗な状態とは言い難い...

〇〇env系ツールが増えるごとに
bash_profileに追記し続けるのを止めたい

...

anyenv が便利そう!

anyenvを使って綺麗にする

anyenv自体の説明前に、このツールを使うと何が綺麗になるか?から説明します。

すでにインストール済みのgoenv, pyenvなどはanyenv経由での再設定が必要になりますが、それを乗り越えれば、bash_profile に必要な 〇〇env系ツールの記述 は、以下のみで十分となります。将来的に新しい 〇〇env が必要になった場合でも、bash_profile への追記は不要です。

...
# anyenv
eval "$(anyenv init -)"
...

goenv, jenvごとにPATHを設定 & initする煩わしい記述から解放され、かつ、操作方法は従来の〇〇env系コマンドを利用可能なので、開発環境汚染を少しでも減らしたいエンジニアであれば、このツールを使わない理由は無いと思います。

anyenvとは?

anyenv - All in one for **env

This is a simple wrapper for rbenv style environment managers. You don't have to git clone or modify your shell profile for each **env anymore if you install anyenv.

https://github.com/anyenv/anyenv#anyenv---all-in-one-for-env

端的に言うと、複数の〇〇envを一括管理できる便利ツールです。

執筆時点(2020.12.10)では、以下〇〇envパッケージがanyenvでは利用可能です。
有名どころは網羅しているので、多くの開発プロジェクトで導入可能だと思います。

$ anyenv install -l
  Renv       #R
  crenv      #Crystal
  denv       #D
  erlenv     #Erlang
  exenv      #Elixir
  goenv      #Go
  hsenv      #Haskell
  jenv       #Java
  jlenv      #Julia
  luaenv     #Lua
  nodenv     #Node.js
  phpenv     #PHP
  plenv      #Perl
  pyenv      #Python
  rbenv      #Ruby
  sbtenv     #Sbt
  scalaenv   #Scala
  swiftenv   #Swift
  tfenv      #terraform

上記から欲しい言語をインストールすれば、あとはいつも通りの〇〇env操作コマンドで環境構築可能です。

anyenvのインストール手順

macの場合

Homebrewを利用します。

$ brew install anyenv
$ echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
$ exec $SHELL -l

$ anyenv install --init # enter "y"

Linux環境の場合

GitHubから直接引っ張ってきます。

$ git clone https://github.com/anyenv/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
$ exec $SHELL -l

$ anyenv install --init # enter "y"

最後に $ anyenv install 〇〇env で欲しいパッケージを取得すれば、これまで通りの〇〇envコマンドが利用できます。

ex) node.jsのv15.0.1が欲しい場合

anyenvを利用し、nodenvを取得します。

$ anyenv install nodenv
$ exec $SHELL -l

$ anyenv versions
nodenv:
Warning: no Node detected on the system

次は、nodenvでv15.0.1を取得します。

$ nodenv install 15.0.1
$ nodenv global(local) 15.0.1

$ node -v
v15.0.1

以上により、Node.js:v15.0.1 が利用可能となりました。

私の開発では goenv, pyenv, jenv, nodenv が必須なので、以下の設定を入れています。
$ anyenv versions により、各envの設定状況が一覧できます。

$ anyenv versions
goenv:
  system
  1.XX.0
* 1.YY.0 (set by /Users/<username>/.anyenv/envs/goenv/version)
jenv:
  system
* XX.0 (set by /Users/<username>/.anyenv/envs/jenv/version)
nodenv:
  system
* XX.0 (set by /Users/<username>/.anyenv/envs/nodenv/version)
pyenv:
  system
  XX.0
* YY.0 (set by /Users/<username>/.anyenv/envs/pyenv/version)

anyenvインストール以前に設定したgoenvやpyenvの環境変数が残っている場合は、bash_profileから削除することも忘れないようご注意ください。

まとめ

  • 環境変数、echo 'export hogehoge"' >> ~/.bash_profileで追加したまま放置していませんか
  • 複数の〇〇env系ツールを使っているなら、anyenvの利用がオススメ
  • anyenvでbash_profileがスッキリ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

variable and method

Mac OS X-10.15.7 ruby-2.7.1p83

variable

お題

受け取った引数ARGV[0]をnameという変数(variable)に代入してそれを打ち出すcodeを書け.

> ruby name_variable.rb Rudy
Rudy

簡単な解説

rubyの変数は型宣言の必要性がありません.文脈に合わせて適当に型が定められます.イコールで代入されます.

name = 'Rudy'

なんかです.

詳しい解法

> ruby name_variable.rb Rudy

としましょう.

ruby: No such file or directory -- name_variable.rb (LoadError)

というエラーが返ってきますよね.そんなファイルないよって.なら作ります.

> emacs name_variable.rb

としてemacsを起動して

p "Rudy"

と打ち込んでsaveしましょう.

> ruby name_variable.rb Rudy
"Rudy"

 

類題

さてこの後,

  • nameに代入してRudyと打ち出すようにrefactoringしてください.
  • ARGV[0]を受け取って,自分の名前を返すようにrefactoringしてください

p "Rudy"

method

お題

> ruby hello_method.rb Rudy
Hell Rudy.

と返すhello methodを作りなさい.

解説

Rubyではfunctionやprocedureなどのまとまりはmethodを定義(definition)することになります.methodは0個以上の引数(argument)を取ることができます.例えば次の通りです.

def hello(name)
  p name
end

解法

> ruby hello_method.rb Rudy

として見ましょう.もちろん動きませんよね.emacsでhello_method.rbを編集します.

> emacs hello_method.rb

何ですが,methodの中身ではなく呼び出す方から考えます.普通に,

hello(name)

と打ち込んで動かして見てください.

> ruby hello_method.rb Rudy
hello_method.rb:1:in `<main>': undefined local variable or method `name' for main:Object (NameError)

というエラーが返ってきたはずです.nameという変数やmethodがundefineやと怒られています.そこで今まで学んできたのを思い出して,nameにARGV[0]を代入します.

name = ARGV[0]
hello(name)

次のerrorは

> ruby hello_method.rb Rudy
hello_method.rb:2:in `<main>': undefined method `hello' for main:Object (NoMethodError)

でhelloっていうmethodがないよですね.

類題

先の解法を参照してhello メソッドを完成させてください.

name = ARGV[0]
hello(name)

ここで学んだこと

  • ruby
    • variable
    • method
    • argument

TDD

codingの最終目標

こういうcodingの仕方はTDD(Test Driven Development:テスト駆動開発)と呼ばれています.

  • テストを作る
  • エラーを出す(red)
  • エラーをなくす(green)
  • codeを綺麗にする(refactoring)

です.codingの最終目標は

clean code that works

動く綺麗なコードを作ることです.順番は,まず動かして,それからこそこそと綺麗にするです.えっと,もし,あなたが,

  • codeを書くまえに設計すべき
  • コピペは悪

なんていう,マントラを信じているならこのprogramming styleを提唱し始めたKent Beckの文章を載せておきます.よーーく心に刻んでください.

もしあなたがプログラミング嫌いならば,少し心が晴れると思いますよ.俺のせいじゃないって.

clean code that works

TDD by Example (Kent Beck) p.13

目指すのは,動作するきれいなコードだ(このRon Jeffriesの簡潔な表現は素晴らしい).動作するきれいなコードは,偉大なプログラマでもすぐには書けないことがあるし,普通のプログラマならなおさらだ(私もそうだ).=ここは分割統治しよう.=各個撃破だぜ,ベイビー.最初に「動作する」に取り組み,その後で「きれいな」に取り組む.この方法はアーキテクチャ駆動とは正反対だ.アーキテクチャ駆動は「きれいなコード」に最初に取り組み,そのうえで苦心してあちこち設計の辻褄を合わせながらどうにか「動作する」を実現させる.

TDD by Example (Kent Beck) p.11

The goal is clean code that works(thenks to Ron Jeffries for this pithy summary).Clean code that works is out of the reach of even the best programmerssome of the time,and out of the reach of most programmers(like me)most of the time.Divide and conquer, baby.First we'll solve the "that works" part of the problem.Then we'll solve the "clean code" part.This is the opposite of architecture-driven development,where you solve "clean code" first,then scramble around trying to integrate into the design the things you learn as you solve the "that works" problem.

phases and speed

TDD by Example (Kent Beck) p.30

グリーンバーを出す小さなステップはどんなものだろうか.Dollarクラスを丸ごとコピーして,DollarをFrancに書き換えるのはどうだろう.

ちょっと待った.これで唾棄すべき糞コードの出来上がりだとあざ笑う声が聞こえる.その声は続けて言う.コピー&ペーストによる再利用は抽象化の敗北であり,きれいな設計を殺すと.

イラっとしただろうか.なら深呼吸だ.鼻から息を吸い込んで,3秒止めて口から吐き出す.オーケー?さて,サイクルにはフェーズがあることを思い出してほしい(中には数秒で通り過ぎるものもあるが,フェーズはフェーズだ).

  1. テストを書く.
  2. コンパイラを通す.
  3. テストを走らせ,失敗を確認する.
  4. テストを通す.
  5. 重複を排除する.

各フェーズにはフェーズなりの目的と解決法があり,価値観も異なっている.最初の3つのフェーズはなるべく速く通過して,新しい機能がどの状態にあるのかわかるところまで行きたい.そこにたどり着くためには,どのような罪をおかしてもよい.その短い時間だけは,速度が設計よりも重要だからだ.

1つ心配なのは,私がここに書いていることが,良い設計の原則を無視してよいという免罪符になってしまうことだ.チームに対して「Kent Beckが設計なんてどうでもいいって書いてたよ」などと言い始めるのは,ちょっと待ってほしい.サイクルはまだ終わっていない.4つ足のアーロンチェアは倒れる.最初の4つのステップは,5つ目がなければ無意味だ.正しい設計を,正しいタイミングで行う.動かしてから,正しくする.

ここまで言っておけば安心だろう.これで読者の皆さんは重複を排除するまで誰にもコードを見せないはずだ(ただし,ペアプロ相手は除く)

TDD by Example (Kent Beck) p.23

What short step will get us to a green bar?Copying the Dollar code and replacing Dollar with Franc.

Stop. Hold on.I can hear the aesthetically inclined among you sneering and spitting.Copy-and-paste reuse?The death of abstraction?The killer of clean design?

If you're upset, take a cleanthing breath.In through the nose … hold it 1, 2, 3… out through the mouth.There. Remember, our cycle has different phases(they go by quickly, often in seconds, but they are phases.):

  1. Write a test.
    1. Make it compile.
    2. Run it to see that it fails.
  2. Make it run.
  3. Remove duplication.(Make it right.)

The different phases have different purposes.They call for different styles of solution, different aesthetic viewpoints.The first three phases need to go by quickly,so we get to a known state with the new functionality.We can commit any number of sins to get there,because speed trumps design,just for that brief moment.

Now I'm worried.I've given you a license to abandon all the principles of good design.Off you go to your teams –"Kent says all that design stuff doesn't matter."Halt. The cycle is not complete.A four-legged Aeron chair falls over.The first four steps of the cycle won't work without the fifth.Good design at good times.Make it run, make it right.

There, I feel better.Now I'm sure you won't show anyone except your partner your codeuntil you've removed the duplication.Where were we?Ah, yes.Violating all the tenets of good design in the interest of speed(penance for our sin will occupy the next several chapters).

おまけ

言語は英語から

これをmethodにしたら,

def func(hoge)
  puts "Hello #{hoge}."
end

hoho = ARGV[0]
func(hoho)

これを動かしてみましょう.

> ruby hello.rb bob

とちゃんと動きます.これでは何をしているのかわからないので,コメントをつけましょう.

# method: func
# input:  hoge is a name
# output: puts greetings
# return: nil
def func(hoge)
  puts "Hello #{hoge}."
end

honya = ARGV[0] # get hoho from argv
func(honya) # call hello with a name honya

英語でしっかりとコメントが書ければ世界中に通用するcodeが書ける…んでしょうかね?コメントをぐちゃぐちゃ書くんじゃなくて,もっとわかりやすい名前(method名,変数名)にしたくなるでしょう?

def puts_hello name
  puts "Hello #{name}."
end

name = ARGV[0]
puts_hello name

こっちの方が短くてスッキリしていて圧倒的にいいでしょ? 適切な英単語を使えば,要らん説明が省けます.これが言語は英語からという意味です.

長くなったら,隠せ

もしoptionのnameを打つのを忘れたら,問い合わせてくれると,user interfaceという感じがもっとするでしょう?

name = ARGV[0]
if name == nil
  puts "What\'s your name?"
  name = gets.chomp
end

というのをname=の下につけましょう[1].ちょっと動かして見ましょう.

> ruby hello.rb

意図通りに動いていますか? でもmain loopが長くなってきちゃいました.そこで,これもmethodに切り出します.領域を指定して(C-spaceで先頭を指定して移動),C-wでcut, 移動してC-yでyankです.

def gets_name
  name = ARGV[0]
  if name == nil
    puts "What\'s your name?"
    name = gets.chomp
  end
  return name
end

name = gets_name

この時,領域を指定してM-x indent-regionとしてください.あるいはC-x TAB.これだけでもだいぶ読みやすくなったでしょ?

もっとできます.main loopにcursorを持って行って,'C-c @ C-l'と打ってみてください.

def puts_hello name...end

def gets_name...end

name = gets_name
puts_hello name

これがoutlineによるtoggle表示からきたすごいメタファなんですよ.何をしているのか知らなくていい部分は隠してしまおうというのが,methodに切り分ける理由なんです.

今日の教え- editor -

ここで示した優秀なeditorのmeritは納得いただけましたでしょうか?必須操作を少しだけリストアップしておくと,

  1. editのkey-bind
  2. syntax high_lighting
  3. code hiding, show
  4. block copy, 移動
  5. query-replace
  6. shellとの連携

  • source ~/git_hub/ruby_docs/chart_style_ruby/c2_name_method_hello.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チャート式ruby-II(variable and method)

Mac OS X-10.15.7 ruby-2.7.1p83

variable

お題

受け取った引数ARGV[0]をnameという変数(variable)に代入してそれを打ち出すcodeを書け.

> ruby name_variable.rb Rudy
Rudy

簡単な解説

rubyの変数は型宣言の必要性がありません.文脈に合わせて適当に型が定められます.イコールで代入されます.

name = 'Rudy'

なんかです.

詳しい解法

> ruby name_variable.rb Rudy

としましょう.

ruby: No such file or directory -- name_variable.rb (LoadError)

というエラーが返ってきますよね.そんなファイルないよって.なら作ります.

> emacs name_variable.rb

としてemacsを起動して

p "Rudy"

と打ち込んでsaveしましょう.

> ruby name_variable.rb Rudy
"Rudy"

 

類題

さてこの後,

  • nameに代入してRudyと打ち出すようにrefactoringしてください.
  • ARGV[0]を受け取って,自分の名前を返すようにrefactoringしてください

p "Rudy"

method

お題

> ruby hello_method.rb Rudy
Hell Rudy.

と返すhello methodを作りなさい.

解説

Rubyではfunctionやprocedureなどのまとまりはmethodを定義(definition)することになります.methodは0個以上の引数(argument)を取ることができます.例えば次の通りです.

def hello(name)
  p name
end

解法

> ruby hello_method.rb Rudy

として見ましょう.もちろん動きませんよね.emacsでhello_method.rbを編集します.

> emacs hello_method.rb

何ですが,methodの中身ではなく呼び出す方から考えます.普通に,

hello(name)

と打ち込んで動かして見てください.

> ruby hello_method.rb Rudy
hello_method.rb:1:in `<main>': undefined local variable or method `name' for main:Object (NameError)

というエラーが返ってきたはずです.nameという変数やmethodがundefineやと怒られています.そこで今まで学んできたのを思い出して,nameにARGV[0]を代入します.

name = ARGV[0]
hello(name)

次のerrorは

> ruby hello_method.rb Rudy
hello_method.rb:2:in `<main>': undefined method `hello' for main:Object (NoMethodError)

でhelloっていうmethodがないよですね.

類題

先の解法を参照してhello メソッドを完成させてください.

name = ARGV[0]
hello(name)

ここで学んだこと

  • ruby
    • variable
    • method
    • argument

TDD

codingの最終目標

こういうcodingの仕方はTDD(Test Driven Development:テスト駆動開発)と呼ばれています.

  • テストを作る
  • エラーを出す(red)
  • エラーをなくす(green)
  • codeを綺麗にする(refactoring)

です.codingの最終目標は

clean code that works

動く綺麗なコードを作ることです.順番は,まず動かして,それからこそこそと綺麗にするです.えっと,もし,あなたが,

  • codeを書くまえに設計すべき
  • コピペは悪

なんていう,マントラを信じているならこのprogramming styleを提唱し始めたKent Beckの文章を載せておきます.よーーく心に刻んでください.

もしあなたがプログラミング嫌いならば,少し心が晴れると思いますよ.俺のせいじゃないって.

clean code that works

TDD by Example (Kent Beck) p.13

目指すのは,動作するきれいなコードだ(このRon Jeffriesの簡潔な表現は素晴らしい).動作するきれいなコードは,偉大なプログラマでもすぐには書けないことがあるし,普通のプログラマならなおさらだ(私もそうだ).=ここは分割統治しよう.=各個撃破だぜ,ベイビー.最初に「動作する」に取り組み,その後で「きれいな」に取り組む.この方法はアーキテクチャ駆動とは正反対だ.アーキテクチャ駆動は「きれいなコード」に最初に取り組み,そのうえで苦心してあちこち設計の辻褄を合わせながらどうにか「動作する」を実現させる.

TDD by Example (Kent Beck) p.11

The goal is clean code that works(thenks to Ron Jeffries for this pithy summary).Clean code that works is out of the reach of even the best programmerssome of the time,and out of the reach of most programmers(like me)most of the time.Divide and conquer, baby.First we'll solve the "that works" part of the problem.Then we'll solve the "clean code" part.This is the opposite of architecture-driven development,where you solve "clean code" first,then scramble around trying to integrate into the design the things you learn as you solve the "that works" problem.

phases and speed

TDD by Example (Kent Beck) p.30

グリーンバーを出す小さなステップはどんなものだろうか.Dollarクラスを丸ごとコピーして,DollarをFrancに書き換えるのはどうだろう.

ちょっと待った.これで唾棄すべき糞コードの出来上がりだとあざ笑う声が聞こえる.その声は続けて言う.コピー&ペーストによる再利用は抽象化の敗北であり,きれいな設計を殺すと.

イラっとしただろうか.なら深呼吸だ.鼻から息を吸い込んで,3秒止めて口から吐き出す.オーケー?さて,サイクルにはフェーズがあることを思い出してほしい(中には数秒で通り過ぎるものもあるが,フェーズはフェーズだ).

  1. テストを書く.
  2. コンパイラを通す.
  3. テストを走らせ,失敗を確認する.
  4. テストを通す.
  5. 重複を排除する.

各フェーズにはフェーズなりの目的と解決法があり,価値観も異なっている.最初の3つのフェーズはなるべく速く通過して,新しい機能がどの状態にあるのかわかるところまで行きたい.そこにたどり着くためには,どのような罪をおかしてもよい.その短い時間だけは,速度が設計よりも重要だからだ.

1つ心配なのは,私がここに書いていることが,良い設計の原則を無視してよいという免罪符になってしまうことだ.チームに対して「Kent Beckが設計なんてどうでもいいって書いてたよ」などと言い始めるのは,ちょっと待ってほしい.サイクルはまだ終わっていない.4つ足のアーロンチェアは倒れる.最初の4つのステップは,5つ目がなければ無意味だ.正しい設計を,正しいタイミングで行う.動かしてから,正しくする.

ここまで言っておけば安心だろう.これで読者の皆さんは重複を排除するまで誰にもコードを見せないはずだ(ただし,ペアプロ相手は除く)

TDD by Example (Kent Beck) p.23

What short step will get us to a green bar?Copying the Dollar code and replacing Dollar with Franc.

Stop. Hold on.I can hear the aesthetically inclined among you sneering and spitting.Copy-and-paste reuse?The death of abstraction?The killer of clean design?

If you're upset, take a cleanthing breath.In through the nose … hold it 1, 2, 3… out through the mouth.There. Remember, our cycle has different phases(they go by quickly, often in seconds, but they are phases.):

  1. Write a test.
    1. Make it compile.
    2. Run it to see that it fails.
  2. Make it run.
  3. Remove duplication.(Make it right.)

The different phases have different purposes.They call for different styles of solution, different aesthetic viewpoints.The first three phases need to go by quickly,so we get to a known state with the new functionality.We can commit any number of sins to get there,because speed trumps design,just for that brief moment.

Now I'm worried.I've given you a license to abandon all the principles of good design.Off you go to your teams –"Kent says all that design stuff doesn't matter."Halt. The cycle is not complete.A four-legged Aeron chair falls over.The first four steps of the cycle won't work without the fifth.Good design at good times.Make it run, make it right.

There, I feel better.Now I'm sure you won't show anyone except your partner your codeuntil you've removed the duplication.Where were we?Ah, yes.Violating all the tenets of good design in the interest of speed(penance for our sin will occupy the next several chapters).

おまけ

言語は英語から

これをmethodにしたら,

def func(hoge)
  puts "Hello #{hoge}."
end

hoho = ARGV[0]
func(hoho)

これを動かしてみましょう.

> ruby hello.rb bob

とちゃんと動きます.これでは何をしているのかわからないので,コメントをつけましょう.

# method: func
# input:  hoge is a name
# output: puts greetings
# return: nil
def func(hoge)
  puts "Hello #{hoge}."
end

honya = ARGV[0] # get hoho from argv
func(honya) # call hello with a name honya

英語でしっかりとコメントが書ければ世界中に通用するcodeが書ける…んでしょうかね?コメントをぐちゃぐちゃ書くんじゃなくて,もっとわかりやすい名前(method名,変数名)にしたくなるでしょう?

def puts_hello name
  puts "Hello #{name}."
end

name = ARGV[0]
puts_hello name

こっちの方が短くてスッキリしていて圧倒的にいいでしょ? 適切な英単語を使えば,要らん説明が省けます.これが言語は英語からという意味です.

長くなったら,隠せ

もしoptionのnameを打つのを忘れたら,問い合わせてくれると,user interfaceという感じがもっとするでしょう?

name = ARGV[0]
if name == nil
  puts "What\'s your name?"
  name = gets.chomp
end

というのをname=の下につけましょう[1].ちょっと動かして見ましょう.

> ruby hello.rb

意図通りに動いていますか? でもmain loopが長くなってきちゃいました.そこで,これもmethodに切り出します.領域を指定して(C-spaceで先頭を指定して移動),C-wでcut, 移動してC-yでyankです.

def gets_name
  name = ARGV[0]
  if name == nil
    puts "What\'s your name?"
    name = gets.chomp
  end
  return name
end

name = gets_name

この時,領域を指定してM-x indent-regionとしてください.あるいはC-x TAB.これだけでもだいぶ読みやすくなったでしょ?

もっとできます.main loopにcursorを持って行って,'C-c @ C-l'と打ってみてください.

def puts_hello name...end

def gets_name...end

name = gets_name
puts_hello name

これがoutlineによるtoggle表示からきたすごいメタファなんですよ.何をしているのか知らなくていい部分は隠してしまおうというのが,methodに切り分ける理由なんです.

今日の教え- editor -

ここで示した優秀なeditorのmeritは納得いただけましたでしょうか?必須操作を少しだけリストアップしておくと,

  1. editのkey-bind
  2. syntax high_lighting
  3. code hiding, show
  4. block copy, 移動
  5. query-replace
  6. shellとの連携

  • source ~/git_hub/ruby_docs/chart_style_ruby/./c2_name_method_hello.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby設定3 Rubocop

RuboCop

PATH

(m・_・bp) 18:16 ~/Desktop/vsnote2.0/ruby/lib/rubybook_2 % which rubocop
/Users/uekiyoshihiro/.rbenv/shims/rubocop

RuboCopの導入

ruby-style-guide/README.ja.md at japanese · fortissimo1997/ruby-style-guide

# グローバルインストール
$ gem install rubocop

# インストール確認
$ rubocop -v

# インストール場所確認
$ which rubocop
/Users/myname/.rbenv/shims/rubocop

Rubocopの使用とオプション

$ rubocop

# 自動修正(--auto-correctでもOK)
$ rubocop -a

# 違反した項目をすべて無効とする設定ファイル(.rubocop_todo.yml)が生成される やらなくてもいいかな
$ rubocop --auto-gen-config

Railsの設定に対応する

Gemのインストール

$ cd <プロジェクトルート> #プロジェクトルートに移動
$ gem install rubocop-performance #2つをインストール
$ gem install rubocop-rails

設定ファイルを新しく作成する

$ cd <プロジェクトルート> #プロジェクトルートに移動
$ touch .rubocop.yml  #設定ファイルを新規作成
$ curl https://raw.githubusercontent.com/rails/rails/master/.rubocop.yml > .rubocop.yml #rails本家のコード規約をダウンロードして.rubocop.ymlファイルへ出力

.rubocop.ymlの中身

require:
  - rubocop-performance
  - rubocop-rails

AllCops:
  TargetRubyVersion: 2.5
  # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
  # to ignore them, so only the ones explicitly set in this file are enabled.
  DisabledByDefault: true
  Exclude:
    - '**/tmp/**/*'
    - '**/templates/**/*'
    - '**/vendor/**/*'
    - 'actionpack/lib/action_dispatch/journey/parser.rb'
    - 'railties/test/fixtures/tmp/**/*'
    - 'actionmailbox/test/dummy/**/*'
    - 'actiontext/test/dummy/**/*'
    - '**/node_modules/**/*'

Performance:
  Exclude:
    - '**/test/**/*'

# Prefer assert_not over assert !
Rails/AssertNot:
  Include:
    - '**/test/**/*'

# Prefer assert_not_x over refute_x
Rails/RefuteMethods:
  Include:
    - '**/test/**/*'

Rails/IndexBy:
  Enabled: true

Rails/IndexWith:
  Enabled: true

# Prefer &&/|| over and/or.
Style/AndOr:
  Enabled: true

# Align `when` with `case`.
Layout/CaseIndentation:
  Enabled: true

Layout/ClosingHeredocIndentation:
  Enabled: true

# Align comments with method definitions.
Layout/CommentIndentation:
  Enabled: true

Layout/ElseAlignment:
  Enabled: true

# Align `end` with the matching keyword or starting expression except for
# assignments, where it should be aligned with the LHS.
Layout/EndAlignment:
  Enabled: true
  EnforcedStyleAlignWith: variable
  AutoCorrect: true

Layout/EmptyLineAfterMagicComment:
  Enabled: true

Layout/EmptyLinesAroundAccessModifier:
  Enabled: true
  EnforcedStyle: only_before

Layout/EmptyLinesAroundBlockBody:
  Enabled: true

# In a regular class definition, no empty lines around the body.
Layout/EmptyLinesAroundClassBody:
  Enabled: true

# In a regular method definition, no empty lines around the body.
Layout/EmptyLinesAroundMethodBody:
  Enabled: true

# In a regular module definition, no empty lines around the body.
Layout/EmptyLinesAroundModuleBody:
  Enabled: true

# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
Style/HashSyntax:
  Enabled: true

Layout/FirstArgumentIndentation:
  Enabled: true

# Method definitions after `private` or `protected` isolated calls need one
# extra level of indentation.
Layout/IndentationConsistency:
  Enabled: true
  EnforcedStyle: indented_internal_methods

# Two spaces, no tabs (for indentation).
Layout/IndentationWidth:
  Enabled: true

Layout/LeadingCommentSpace:
  Enabled: true

Layout/SpaceAfterColon:
  Enabled: true

Layout/SpaceAfterComma:
  Enabled: true

Layout/SpaceAfterSemicolon:
  Enabled: true

Layout/SpaceAroundEqualsInParameterDefault:
  Enabled: true

Layout/SpaceAroundKeyword:
  Enabled: true

Layout/SpaceBeforeComma:
  Enabled: true

Layout/SpaceBeforeComment:
  Enabled: true

Layout/SpaceBeforeFirstArg:
  Enabled: true

Style/DefWithParentheses:
  Enabled: true

# Defining a method with parameters needs parentheses.
Style/MethodDefParentheses:
  Enabled: true

Style/FrozenStringLiteralComment:
  Enabled: true
  EnforcedStyle: always
  Exclude:
    - 'actionview/test/**/*.builder'
    - 'actionview/test/**/*.ruby'
    - 'actionpack/test/**/*.builder'
    - 'actionpack/test/**/*.ruby'
    - 'activestorage/db/migrate/**/*.rb'
    - 'activestorage/db/update_migrate/**/*.rb'
    - 'actionmailbox/db/migrate/**/*.rb'
    - 'actiontext/db/migrate/**/*.rb'

Style/RedundantFreeze:
  Enabled: true

# Use `foo {}` not `foo{}`.
Layout/SpaceBeforeBlockBraces:
  Enabled: true

# Use `foo { bar }` not `foo {bar}`.
Layout/SpaceInsideBlockBraces:
  Enabled: true
  EnforcedStyleForEmptyBraces: space

# Use `{ a: 1 }` not `{a:1}`.
Layout/SpaceInsideHashLiteralBraces:
  Enabled: true

Layout/SpaceInsideParens:
  Enabled: true

# Check quotes usage according to lint rule below.
Style/StringLiterals:
  Enabled: true
  EnforcedStyle: double_quotes

# Detect hard tabs, no hard tabs.
Layout/IndentationStyle:
  Enabled: true

# Empty lines should not have any spaces.
Layout/TrailingEmptyLines:
  Enabled: true

# No trailing whitespace.
Layout/TrailingWhitespace:
  Enabled: true

# Use quotes for string literals when they are enough.
Style/RedundantPercentQ:
  Enabled: true

Lint/AmbiguousOperator:
  Enabled: true

Lint/AmbiguousRegexpLiteral:
  Enabled: true

Lint/ErbNewArguments:
  Enabled: true

# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
Lint/RequireParentheses:
  Enabled: true

Lint/ShadowingOuterLocalVariable:
  Enabled: true

Lint/RedundantStringCoercion:
  Enabled: true

Lint/UriEscapeUnescape:
  Enabled: true

Lint/UselessAssignment:
  Enabled: true

Lint/DeprecatedClassMethods:
  Enabled: true

Style/ParenthesesAroundCondition:
  Enabled: true

Style/HashTransformKeys:
  Enabled: true

Style/HashTransformValues:
  Enabled: true

Style/RedundantBegin:
  Enabled: true

Style/RedundantReturn:
  Enabled: true
  AllowMultipleReturnValues: true

Style/Semicolon:
  Enabled: true
  AllowAsExpressionSeparator: true

# Prefer Foo.method over Foo::method
Style/ColonMethodCall:
  Enabled: true

Style/TrivialAccessors:
  Enabled: true

Performance/FlatMap:
  Enabled: true

Performance/RedundantMerge:
  Enabled: true

Performance/StartWith:
  Enabled: true

Performance/EndWith:
  Enabled: true

Performance/RegexpMatch:
  Enabled: true

Performance/ReverseEach:
  Enabled: true

Performance/UnfreezeString:
  Enabled: true

Performance/DeletePrefix:
  Enabled: true

Performance/DeleteSuffix:
  Enabled: true

githooksの設定

【Ruby on Rails】rubocop と pre-commit を利用して git commit 時にコーディングチェックを行う - きゃまなかのブログ

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

Ruby設定2 

gemとは?

  • 決まったフォーマットで提供されるRuby言語用のライブラリを「gem」と呼びます。
  • 「gem」には例えば以下のようなものがある。
    • Rails(Webフレームワーク)
    • Sinatra(Webフレームワーク)
    • omniauth(認証機能)
    • unicorn(アプリケーションサーバ)
    • Rspec(テストフレームワーク) ...etc
  • PerlでいうCPAN、phpでいうpear等にあたる

Rubygemsとは

  • yumやaptと似た、gem専用のパッケージ管理システム。
  • 以下サイトにてgemの情報公開、提供を行っている。 https://rubygems.org/

Bundlerとはなんぞや

  • gemは以下のように個別に手動インストールする事が可能です。
    • gem install "sinatra"
    • gem install "unicorn"
    • gem install "omniauth"
  • しかし、様々なgem(ライブラリ)を組み合わせて使っていると「gemAのver1とgemBのVer2はうまく動くけど、最新バージョン同士だとうまく動かない。」という互換性の問題が出てくる場合があります。
  • また、複数人、複数環境で開発を行う場合は、各環境で使うライブラリの名前、バージョンを合わせる必要があります。
  • こういった場合に、gem同士の互換性を保ちながら各gemの導入、管理を行ってくれるのがBundlerです。
  • Gemfileというファイルにインストールしたいgemを記述し、それを元にbundlerを使ってインストールを行います。
  • bundler本体以外のgemは基本的にbundler経由でインストールを行うのがおすすめです。

Bundlerのインストールの仕方

  • Bundlerのインストール
    • Bundlerもgemの1つです。手動ダウンロードしましょう
      • gem install bundler
    • 導入後はバージョンが表示される事を確認してください。
      • bundler -v

Bundlerの使い方

  1. Gemfileの作成
    • bundle init
    • 上記実行するとGemfileという雛形ファイルが作成されます
  2. 使用したいgemをGemfileに記述する
    • Gemfileを編集する
      • vi Gemfile
    • Gemfileの先頭に、インストール元はrubygemsですよ、と宣言を行います。
      • source "rubygems"
    • 続けてインストールしたいgemを記述します
      • gem "sinatra"
      • gem "unicorn"
      • gem "omniauth"
    • gemの指定の際に、バージョンの指定も可能です
      • gem 'rails', "~> 4.2.4"
  3. Gemfileにリスト化したgemを一括インストール
    • 一括インストール
      • bundle install --path vendor/bundle
    • --pathでインストール先を指定可能。一度でも--path指定でインストールしたら、次回以降はpath指定無しでbundle installを行っても同じpathが選択されます。
    • この時、インストールしたgemの名前とバージョンを記載した「gemfile.lock」というファイルが作成されます。
  4. インストールしたgemをプログラムソース上でロード
    • まずソース上でrubygemsをrequireします。
      • require "rubygems"
    • 以下のように記述するとインストールした内容からrequireを行うように(指定無しだとシステム最新のgemを使ってしまいます)
      • require "bundler/setup"
  5. プログラムを実行する際は、最初にbundle execを実行します。(指定無しだと互換性を考慮せずにシステム最新のgemを使ってしまう)
    • bundle exec ruby foo.rb

参考

GemコマンドとBundlerの基礎 - BlueTechNote

Gemコマンドを使用

$ gem install
$ gem update
$ gem uninstall

bundler

  • Bundler をインストールする
  • Gemfile にインストールするGem を書く
  • bundle installコマンドでインストールする
  • bundle updateコマンドでGemをバージョンアップする ```console - Bundler をインストールする (m・_・bp) 17:50 ~/Desktop/vsnote2.0/ruby % gem install bundler Fetching bundler-2.1.4.gem Successfully installed bundler-2.1.4 Parsing documentation for bundler-2.1.4 Installing ri documentation for bundler-2.1.4 Done installing documentation for bundler after 2 seconds 1 gem installed
  • Gemfile にインストールするGem を書く

(m・_・bp) 17:51 ~/Desktop/vsnote2.0/ruby % bundle init
Writing new Gemfile to /Users/uekiyoshihiro/Desktop/vsnote2.0/ruby/Gemfile

  • bundle installコマンドでインストールする

(m・・bp) 17:53 ~/Desktop/vsnote2.0/ruby % bundle install
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Using bundler 2.1.4
Fetching coderay 1.1.2
Installing coderay 1.1.2
Fetching method
source 1.0.0
Installing method_source 1.0.0
Fetching pry 0.13.1
Installing pry 0.13.1
Bundle complete! 1 Gemfile dependency, 4 gems now installed.
Use bundle info [gemname] to see where a bundled gem is installed.

  • bundle updateコマンドでGemをバージョンアップする

(m・・bp) 17:58 ~/Desktop/vsnote2.0/ruby % bundle update
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Using bundler 2.1.4
Using coderay 1.1.2
Using method
source 1.0.0
Using pry 0.13.1
Bundle updated!
```

よく使うコマンド集

$ bundle init #Gemfileを生成
$ bundle install    #Gemfileに書かれたgemパッケージをインストール
$ bundle exec   #Bundlerでインストールされたgemパッケージを使用してコマンドを実行
$ bundle list   #インストール済みのgemパッケージの一覧を表示
$ bundle update #インストール済みのgemパッケージのバージョンを更新


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

Ruby環境設定

初めに

Bundlerのインストールの仕方

  • Bundlerのインストール
    • Bundlerもgemの1つです。手動ダウンロードしましょう
      • gem install bundler
    • 導入後はバージョンが表示される事を確認してください。
      • bundler -v

Bundlerの使い方

  1. Gemfileの作成
    • bundle init
    • 上記実行するとGemfileという雛形ファイルが作成されます
  2. 使用したいgemをGemfileに記述する
    • Gemfileを編集する
      • vi Gemfile
    • Gemfileの先頭に、インストール元はrubygemsですよ、と宣言を行います。
      • source "rubygems"
    • 続けてインストールしたいgemを記述します
      • gem "sinatra"
      • gem "unicorn"
      • gem "omniauth"
    • gemの指定の際に、バージョンの指定も可能です
      • gem 'rails', "~> 4.2.4"
  3. Gemfileにリスト化したgemを一括インストール
    • 一括インストール
      • bundle install --path vendor/bundle
    • --pathでインストール先を指定可能。一度でも--path指定でインストールしたら、次回以降はpath指定無しでbundle installを行っても同じpathが選択されます。
    • この時、インストールしたgemの名前とバージョンを記載した「gemfile.lock」というファイルが作成されます。
  4. インストールしたgemをプログラムソース上でロード
    • まずソース上でrubygemsをrequireします。
      • require "rubygems"
    • 以下のように記述するとインストールした内容からrequireを行うように(指定無しだとシステム最新のgemを使ってしまいます)
      • require "bundler/setup"
  5. プログラムを実行する際は、最初にbundle execを実行します。(指定無しだと互換性を考慮せずにシステム最新のgemを使ってしまう)
    • bundle exec ruby foo.rb

参考

確認

(m・_・bp) mbp-2 17:04 ~ % ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]

色々インストール

$ brew install openssl
$ brew install readline
$ brew install libyaml
$ brew install autoconf

ruby-build

ruby-buildはrbenvのプラグインで、自動でrubyをビルドする。

$ brew update
$ brew upgrade ruby-build

$ ruby-build --version

現在のバージョン確認

## .rbenvのinstall
```bash
(m・_・bp) mbp-2 17:10 ~ % brew install rbenv ruby-build

/etc/zprofileへの追記

(m・_・bp) mbp-2 17:12 ~ % cd /etc
(m・_・bp) mbp-2 17:16 /etc % sudo cp zprofile zprofile.org
/etc % sudo vi zprofile
############################

rbenv PATH

if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

(m・_・bp) mbp-2 18:06 ~ % source /etc/zprofile

rbenvのinstall確認

(m・_・bp) mbp-2 17:22 ~ % rbenv -v
rbenv 1.1.2

ruby-buildインストール,,,既にインストールしてた

(m・_・bp) mbp-2 17:35 ~ % brew install ruby-build
Warning: ruby-build 20200401 is already installed and up-to-date
To reinstall 20200401, run `brew reinstall ruby-build`

rbenvで最新versionをinstall

(m・_・bp) mbp-2 17:22 ~ % rbenv versions
* system (set by /Users/uekiyoshihiro/.rbenv/version)

rbenvでインストール

Rubyの公式サイト
こちらのサイトをみて安定版の最新バージョンをインストール

 ~ % rbenv install -l

 ~ % rbenv install 2.7.1

 ~ % ruby -v

 ~ % rbenv install 2.6.5

rubyのバージョンを切り替える

バージョン切り替えには、ディレクトリのみの設定orデフォ設定の2種類がある。

$ rbenv local <バージョン>
# 実行したディレクトリ配下で使うrubyのバージョンを変更
# 指定バージョンの .ruby-version が作成される

$ rbenv global <バージョン>
# デフォルトで使うrubyのバージョンを変更

$ rbenv local --unset
# ローカルバージョン指定の取消し

上記の通り、アプリごとに異なるバージョンのrubyを利用したい場合は、各ディレクトリでrbenv localを実行して、rubyのバージョンを指定。

ちなみにこれらのコマンドを実行すると、現在のディレクトリに 「.ruby-version」 というファイルが作成される。これは自動で使用するrubyのバージョンを変えるもの。

その他:openssl

opensslはhttpsを使用するためのパッケージ。

$ which openssl
$ brew list openssl
$ openssl version

参考:Mountain Lion に rbenv を導入

(m・_・bp) mbp-2 19:48 ~ % rbenv versions
  system
  2.6.5
  2.6.6
* 2.7.1 (set by /Users/uekiyoshihiro/.ruby-version)



(m・_・bp) 14:57 ~/Desktop/vsnote2.0/ruby/lib/rubybook_2 % gem install minitest -v 5.10.1
Fetching minitest-5.10.1.gem
Successfully installed minitest-5.10.1
Parsing documentation for minitest-5.10.1
Installing ri documentation for minitest-5.10.1
Done installing documentation for minitest after 0 seconds
1 gem installed

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

Ruby設定1

初めに

確認

(m・_・bp) mbp-2 17:04 ~ % ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]

色々インストール

$ brew install openssl
$ brew install readline
$ brew install libyaml
$ brew install autoconf

ruby-build

ruby-buildはrbenvのプラグインで、自動でrubyをビルドする。

$ brew update
$ brew upgrade ruby-build

$ ruby-build --version

現在のバージョン確認

## .rbenvのinstall
```bash
(m・_・bp) mbp-2 17:10 ~ % brew install rbenv ruby-build

/etc/zprofileへの追記するために移動

(m・_・bp) mbp-2 17:12 ~ % cd /etc
(m・_・bp) mbp-2 17:16 /etc % sudo cp zprofile zprofile.org #念のため予備を作っておく
(m・_・bp) mbp-2 17:16 /etc % sudo vi zprofile

rbenv PATH zprofileに下記の事を追記

if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

(m・_・bp) mbp-2 18:06 ~ % source /etc/zprofile

rbenvのinstall確認

(m・_・bp) mbp-2 17:22 ~ % rbenv -v
rbenv 1.1.2

ruby-buildインストール,,,既にインストールしてた

(m・_・bp) mbp-2 17:35 ~ % brew install ruby-build
Warning: ruby-build 20200401 is already installed and up-to-date
To reinstall 20200401, run `brew reinstall ruby-build`

rbenvで最新versionをinstall

(m・_・bp) mbp-2 17:22 ~ % rbenv versions
* system (set by /Users/uekiyoshihiro/.rbenv/version)

rbenvでインストール

Rubyの公式サイト
こちらのサイトをみて安定版の最新バージョンをインストール

 ~ % rbenv install -l

 ~ % rbenv install 2.7.1

 ~ % ruby -v

 ~ % rbenv install 2.6.5

rubyのバージョンを切り替える

バージョン切り替えには、ディレクトリのみの設定orデフォ設定の2種類がある。

$ rbenv local <バージョン>
# 実行したディレクトリ配下で使うrubyのバージョンを変更
# 指定バージョンの .ruby-version が作成される

$ rbenv global <バージョン>
# デフォルトで使うrubyのバージョンを変更

$ rbenv local --unset
# ローカルバージョン指定の取消し

上記の通り、アプリごとに異なるバージョンのrubyを利用したい場合は、各ディレクトリでrbenv localを実行して、rubyのバージョンを指定。

ちなみにこれらのコマンドを実行すると、現在のディレクトリに 「.ruby-version」 というファイルが作成される。これは自動で使用するrubyのバージョンを変えるもの。

その他:openssl

opensslはhttpsを使用するためのパッケージ。

$ which openssl
$ brew list openssl
$ openssl version

参考:Mountain Lion に rbenv を導入

(m・_・bp) mbp-2 19:48 ~ % rbenv versions
  system
  2.6.5
  2.6.6
* 2.7.1 (set by /Users/uekiyoshihiro/.ruby-version)



(m・_・bp) 14:57 ~/Desktop/vsnote2.0/ruby/lib/rubybook_2 % gem install minitest -v 5.10.1
Fetching minitest-5.10.1.gem
Successfully installed minitest-5.10.1
Parsing documentation for minitest-5.10.1
Installing ri documentation for minitest-5.10.1
Done installing documentation for minitest after 0 seconds
1 gem installed

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

Rails Tutorial 第4章学習編

Rails Tutorial 第4章で(。´・ω・)ん?って思ったところ

前提知識と復習でアウトプットしているからだと思いますが、
この章は特に(。´・ω・)ん?って思ったところはないです。
動画もテキストも非常に分かりやすかったです。

Rails 第4章 簡単な内容のおさらい

第四章はRails風味のRubyです。こんなに分かりやすいオブジェクト指向は初めてです。
ProgateのPython、TECH::CAMPのRubyでも学習しましたが。。。
(゜-゜)う~ん オブジェクト指向それって美味しいの?って感じでした。

よく説明に・・・全てのモノはオブジェクトです。
人も一つのオブジェクト、車も一つのオブジェクト
ジョンという人がいる。ジョンは人に属している。
人は職業・性別・年齢などの要素を持っている。。。
こんな内容に沿ってコードを書かされて(。´・ω・)ん?って感じになります。

私の言葉でのオブジェクト指向を説明すると・・・

"文字列"があります。
オブジェクト指向で記述されていると、
"12345".length #文字をカウントするメソッドが使える。
"12345".to_i   #文字列を整数に変換するメソッドが使える。

.lengthメソッド・.to_iメソッドを定義していないのに何で使えるの?
 ⇒ 答えはオブジェクト指向で書かれているからです。

まだ、(。´・ω・)?って感じですよね

実は"文字列"と書けば文字列と認識されていますが、
String.new("文字列")というコードの省略形です。
文字列(String)クラスのオブジェクト"文字列"を作りなさいという意味なんです。

予め文字列(String)クラスに様々なメソッドが用意されています。
String.new("")で生成したオブジェクトは文字列オブジェクトなので定義済みメソッドが使えるのです。

文字列クラスで.to_iメソッドが用意されているので、
新しく生成した文字列も.to_iなどの様々なメソッドが使えるという事です。

オブジェクト指向の最大の利点は、
文字列は必ず文字列オブジェクトとして生成する事で.to_iなどメソッドを共有出来きる事です。('ω')
文字列クラスに新しいメソッドを追加すれば、全ての文字列オブジェクトで使用できるって事です。
Progateで学んだ時は(。´・ω・)?でしたが、今になって便利さが分かりました(´-ω-`)

クラスと継承

"文字列"Stringクラスに属しています。
何処に属しているか確認する為に.classメソッドが使えます。

s = "文字列"

s.class
 String

実はStringクラスもObjectクラスに属しています。
>> s.class.superclass             # superclass : 親クラスを調べる
=> Object

Objectクラスも更に上のクラスに属しています。
>> s.class.superclass.superclass 
=> BasicObject

BasicObjectは更に上のクラスに属している? 答えはNoでした。nilは存在しないという意味です。
>> s.class.superclass.superclass.superclass
=> nil

なんでこんな事をしているのかというと・・・機能を引き継ぐ為です。('ω')
Objectクラスの下には、String(文字列)Integer(整数)Array(配列)クラスなどがあります。
配列も、整数も、文字列も、.lengthメソッドが使えますよね。
3つそれぞれ追加していくのは大変ですよね。
なのでObjectに追加して下位クラスで利用出来るようにすれば手間が省けます。

この様に上位のクラスで定義されたメソッドを下位メソッドに引継ぐ事を『継承』と言います('ω')

#class <クラス名>で新しいクラスを定義できます。
>> class Word < String             # < StringでStringクラスを継承出来ます('ω')ノ
>>   # 文字列が回文であればtrueを返す
>>   def palindrome?        # defで新しいメソッドを定義できます。
>>     self == self.reverse        # selfは文字列自身を表します
>>   end
>> end
=> :palindrome?

Ruby 基礎

$ rails console

#整数の足し算
>> 17 + 42
=> 59

#文字列の結合
>> "foo" + "bar"    # 文字列の結合
=> "foobar"

#変数の代入
>> first_name = "Michael"
=> "Michael"

#文字列の式展開 これ便利('ω')ノ Railsでも使うよ。
>> "#{first_name} Hartl"
=> "Michael Hartl"

#式展開('ω')ノ
>> first_name = "Michael"
=> "Michael"
>> last_name = "Hartl"
=> "Hartl"
>> first_name + " " + last_name    # 苗字と名前の間に空白を入れた結合
=> "Michael Hartl"
>> "#{first_name} #{last_name}"    # 式展開を使って結合 (上と全く同じ)
=> "Michael Hartl"

#文字列の出力 (puts)
>> puts "foo"     # 文字列を出力する
foo
=> nil #戻り値はnilは「何にもない」

#文字列の出力 (print)
>> print "foo"    # 文字列の画面出力 (putsと同じだが改行しない)
foo=> nil

>> print "foo\n"  # \nは改行。これでputsと同じ出力になる。
foo
=> nil

#''シングルクォーテーションと""ダブルクォーテーション
>> '#{first_name} #{last_name}'    # ''は式展開できない。
=> "/#{first_name} #{last_name}"

>> "#{first_name} #{last_name}"    # ""は式展開できる。
=> "Michael Hartl"

# empty?メソッド
>> "foobar".empty?
=> false
>> "".empty?
=> true

#条件分岐 (if) ・.include?("文字列") ・.nil? メソッド
>> if s.nil?
>>   "The variable is nil"
>> elsif s.empty?
>>   "The string is empty"
>> elsif s.include?("foo")
>>   "The string includes 'foo'"
>> end
=> "The string includes 'foo'"


# バン!バン! 強制的に論理値(True/False)で出力する
>> !!nil #Rubyオブジェクトはnilだけfalse
=> false

>> !!0  #その他のあらゆるRubyのオブジェクトはtrue
=> true

これで半分程度の内容ですかね。結構ボリューム多いです。
配列やハッシュといった重要な内容がこの後に続くのですが・・・
全部書くと著作権的にどうなのかなと思うのでシンボルについて書いたら終わりッス('ω')ノ

シンボルと整数、文字列について

#これはハッシュというラベル付配列です。
user1 = { "name"=>"test_user", "email" => "test_user@example.com" }
user2 = { "name"=>"test_user", "email" => "test_user@example.com" }

user1 == user2
=> false

同じ内容なのに falseになるのはなんで?('ω')ノ 
実はobject_idというモノが違っているから、異なっているのを確かめてみてください。
user1.object_id 
user2.object_id

このobject_idってデータの格納されている場所みたいなものです。
文字列でも試してください。実行する度にobject_idが代わるんです。
"name".object_id
"name".object_id
"name".object_id

次に数字です。数字はどうでしょうか? 数字は常に同じobject_idになるんです。
1.object_id
1.object_id
1.object_id

最後に・・・シンボルです。 シンボルも数字と同じくobject_idが変化しないんです。
:name #:+文字列をシンボルって言うのですが、object_idにラベル付けたモノって思ってください。

なんでこんな事をするかというと、文字列を使うよりも処理速度が速くなるからです。('ω')
なのでハッシュのラベル名は文字列よりもシンボルの方が好まれます。

◎ user = { :name=> "test_user" , :email => "test_user@example.com" }
△ user = { "name"=>"test_user", "email" => "test_user@example.com" }

更にもっと簡単に記述もできます。
user = { :name=> "test_user" , :email => "test_user@example.com" }
user = { name: "test_user", email: "test_user@example.com" }

カスタムヘルパー

自分でメソッドを定義する場合にヘルパーというファイルに記述します('ω')ノ

app/helpers/application_helper.rb
module ApplicationHelper

  # ページごとの完全なタイトルを返します。
  def full_title(page_title = '')
    base_title = "Ruby on Rails Tutorial Sample App"
    if page_title.empty?
      base_title
    else
      page_title + " | " + base_title
    end
  end
end

感想

第四章は非常に内容が濃くて面白いです。
自分で様々なメソッドを定義する為にはRubyの知識が必要になってきますね。
いろんなメソッドが存在するけど、ほんの僅かしか覚えていないので
Railsの学習が終わったら、Rubyもきちんと学習しないと駄目ですね・・・。
はぁー、先が長い長い( ^ω^)・・・。

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

【備忘録】Fizz_Buzz問題について(RubyonRailsの動き方)

本稿について

PFを作成後、改めてFizz_Buzz問題をRubyで入力していたところ、
少し理解が深まったので、備忘録として残します。

Fizz_Buzz問題とは

プレイヤーは円状に座る。最初のプレイヤーは「1」と数字を発言する。次のプレイヤーは直前のプレイヤーの次の数字を発言していく。ただし、3で割り切れる場合は「Fizz」(Bizz Buzzの場合は「Bizz」)、5で割り切れる場合は「Buzz」、両者で割り切れる場合(すなわち15で割り切れる場合)は「Fizz Buzz」(Bizz Buzzの場合は「Bizz Buzz」)を数の代わりに発言しなければならない。発言を間違えた者や、ためらった者は脱落となる。
Wikiより転記:https://ja.wikipedia.org/wiki/Fizz_Buzz

これをプログラムで書くと、条件分岐(if文等)と繰り返し(for文等)が必要になるため、初学者の練習問題となることが多い。

fizz_buzz.rb
def fizz_buzz(number)
    if number % 15 == 0
        puts "fuzz_buzz"
    elsif number % 5 == 0
        puts "buzz"
    elsif number % 3 == 0
        puts "fuzz"
    else
        puts number.to_s
    end
end

puts "1以上の数値を入力してください"
for i in 1..5 do
  print fizz_buzz(gets.to_i)
end

RubyonRailsで言うと

controllerで下記のような、プログラムをよくみる

users_controller.rb
def show
    @user = User.find(params[:id])
end

fizz_buzz.rbで言うところのgets.to_iがviewファイルで行われ、controllerの処理が実行されている。

わかっている人にとっては、当たり前だろうが府に落ちたので

以上です

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

【Ruby on Rails】サクッとブラウザでページタイトルを表示

目標

スクリーンショット 2020-10-26 13.32.22.png

開発環境

ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

ベースとなるタイトルの編集

application_helper.rb
module ApplicationHelper
  def full_title(page_title = '')
    base_title = "アプリ名"
    if page_title.empty?
      base_title
    else
      "#{ page_title } | #{ base_title }"
    end
  end
end

head内のタイトルを編集

application.html.erb
<head>
  <title><%= full_title(yield(:title)) %></title>
</head>

各ページのタイトルを追加

今回はaboutページがあると想定します。
追加場所は一番上でOKです。

app/views/homes/about.html.erb
<%= provide(:title, 'サイトについて')%>

Qiita記事のようにカラムを使用してタイトルを表示する場合は
下記のように記述すればOKです。

app/views/homes/about.html.erb
<%= provide(:title, @genre.name)%>

まとめ

これらを実施する事により、ユーザーが現在どの位置にいるかを把握することができ、
ユーザーの離脱率改善に繋がりますので、必須の搭載機能だと言えます。

またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら https://twitter.com/japwork

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

Railsで簡単な通知機能(フォローされた時のみ)

Railsでの簡単な通知機能です。(とりあえずフォローされた時のみ)

ルーティングの設定

config/routes.rb
  resources :notifications, only: [:index]

コントローラの作成

$ rails g controller notifications

モデルの作成

$ rails g model Notification visitor_id:integer visited_id:integer
$ rails db:migrate

モデルとアソシエーションの設定

app/models/user.rb
  has_many :active_notifications, class_name: "Notification", foreign_key: "visitor_id", dependent: :destroy
  has_many :passive_notifications, class_name: "Notification", foreign_key: "visited_id", dependent: :destroy

  def create_notification_follow!(current_user)
    temp = Notification.where(["visitor_id = ? and visited_id = ? ",current_user.id, id])
    if temp.blank?
      notification = current_user.active_notifications.new(visited_id: id)
      notification.save if notification.valid?
    end
  end
app/models/notification.rb
  belongs_to :visitor, class_name: 'User', foreign_key: 'visitor_id'
  belongs_to :visited, class_name: 'User', foreign_key: 'visited_id'

コントローラの設定

app/controllers/relationships_controller.rb
  def create
    @user = User.find(params[:user_id])
    current_user.follow(params[:user_id])
    @user.create_notification_follow!(current_user) #追記部分
    redirect_to request.referer
  end
app/controllers/notifications_controller.rb
  def index
    @notifications = current_user.passive_notifications.order(created_at: :DESC)
  end

ビュー(通知一覧)の設定

app/views/notifications/index.html
<h1>おしらせ</h1>
<% if @notifications.exists? %>
  <% @notifications.each do |notification| %>
    <table>
      <tr>
        <td>
          <%= attachment_image_tag notification.visitor, :image, size: "50x50" %>
        </td>
        <td>
          <%= link_to user_path(notification.visitor) do %>
            <%= notification.visitor.name %>
          <% end %>
        </td>
        <td>
          からフォローされました。
        </td>
        <td>
          <%= " (#{time_ago_in_words(notification.created_at)}まえ)" %>
        </td>
      </tr>
    </table>
  <% end %>
<% else %>
  <p>おしらせはありません</p>
<% end %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby の Gem で ウエブフレームワーク を検索してみた

はじめに

気が向いたので、やってみた

 検索結果

gem last
Roda 3.37.0 - October 16, 2020
Ruby on Rails 6.0.3.4 - October 07, 2020
Scorched 1.0.1 - August 28, 2020
Cuba 3.9.3 - August 11, 2020
Sinatra 2.1.0 - September 04, 2020
Pakyow 1.0.6 - September 03, 2020
Rack::App 2.2.3 - June 15, 2020
Padrino 0.15.0 - May 15, 2020
Syro 3.2.0 - February 28, 2020
Hanami 2.0.0.alpha1 - January 30, 2019
Hobbit 0.6.1 - February 20, 2016
Camping 2.1.532 - March 21, 2013
Ramaze 2012.12.08 - December 08, 2012
Hyperstack -

まとめ

  • いろいろあります

参照したサイト
RubyGems.
Awesome Ruby : 素晴しい Ruby のライブラリ・ツール・フレームワーク・ソフトウェアの数々

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

to_sメソッドについて。

to_sメソッドの説明

数値が持つメソッドです。数値を文字列に変換してくれます。
ちなみに、 to_sのsの意味は「string(文字列)」です。

以下の例を見てください。

【例】irb
# 数値20にto_sメソッドをつけて実行
irb(main):001:0> 20.to_s
=> "20"

to_sメソッドによって、文字列"20"が実行結果になっています。

それでは、文字列と数値を連結させて、
"i am 20 years old."を表示しましょう。

irbで以下のコードを実行しましょう

では、実際にターミナルでto_sメソッドを試してみましょう。

ターミナル
# to_sで数値を文字列に変換し連結
irb(main):001:0> "i am " + 20.to_s + " years old."

# 続けてこのように表示されれば成功
=> i am 20 years old.

「はじめから文字列"20"を用意すればよかったのでは?」と思うかも
しれません。しかし、足し算した結果を文字列として利用したい場合など、
数値を文字列に変換するto_sメソッドを利用する場面は多くあります。
この方法は必ず覚えておきましょう。

反対に文字列を数値に変換するメソッドもあります。それがto_iメソッドです。これは文字列がもつメソッドです。
to_iメソッドについては、次回説明させていただきます。

まとめ

to_sメソッドとは、数値を文字列に変換するメソッド。

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

RSpec Setup

RSpecのセットアップ

参考文献
Everyday Rails - RSpecによるRailsテスト入門
https://leanpub.com/everydayrailsrspec-jp

環境 Mac
Ruby 2.4.9
Rails 5.1.1

Rspec導入における忘備録である。

RSpecのインストール

GemFileを開き、テスト環境と開発環境で使用可能なように
下記のグループにRSpecを追記する。

GemFile.
group :development, :test do
  #追加 バージョン指定は各環境に応じて変更してください。
  gem 'rspec-rails', '~> 3.6.0'
end

下記のコマンドを入力

ターミナル.
bin/rails g rspec:install

すると下記のフォルダが作成される。

ターミナル.
Running via Spring preloader in process 53239
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

これで準備完了。

お次に、テスト時にターミナルに流れるRSpecの出力を読みやすいドキュメント形式に変更
.rspecに下記を記載する。

#デフォルトで記載されている。
--require spec_helper
#こっちを追記
--format documentation

rspec binstubの導入

binstubとはなんぞや?
アプリケーションの起動を早くすることが可能で、bundle exec ~からbin/~と呼び出しも楽になる便利屋さん。

参考URL
https://qiita.com/tanaka51/items/bc22c1c364202d3cce4e

開発環境に追記してbundle

GemFile.
group :development do
  gem 'spring-commands-rspec'
end

続いてターミナルに下記コマンドを打ち込むと、
Railsアプリケーションディレクトリ直下にあるbinディレクトリ内にRSpecが新たに加わる。

ターミナル.
bundle exec spring binstub rspec

ここまで完了したら

ターミナル.
bin/rspec

でテストが走るか確認する。
rspecディレクトリ内のテストを一括で回してくれるので、最高に楽である。
そして、2回目の実行がめちゃくちゃ早い。

VSCode民のためのテストプラグイン

Rails Run Specs

これはテストコードを開いてcmd + shift + t でその画面のテストをVSCodeが実行してくれるという便利拡張機能!

かなりオススメ

rails g で一緒にRSpecファイルも作成してもらう

ジェネレーターでアプリケーションにコードを追加する際に一緒にRSpecのテストファイルも作ってもらう。
しかし、使用しないディレクトリ及びテストファイルまで作成されては困るので、同時に、使用しないものを作らない設定も行う。

config/application.rb内に追記していく。

config/application.rb
module Projects
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.1

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.


    #ここから下を追記
    config.generators do |g|
      #ジェネレート時にRSpecのテストファイルも作成
      g.test_framework :rspec,
      #テストデータベースにレコードを作成するファイルの作成をスキップ
      fixtures: false,
      #ビュースペックを作成しないことを指定
      view_specs: false,
      #ヘルパーファイル用のスペックを作成しないことを指定
      helper_specs: false,
      #ルーディング用のスペックファイルの作成を省略
      routing_specs: false
    end
  #ここまで
  end
end

もし手動で追加したいのであれば、
テストしたいファイルのディレクトリ名とファイル名を合わせて作成する
app/helpers/projects_helper.rb をテストするのであれば、spec/helpers/projects_helper_spec.rb を作成する。

最後に、Railsがデフォルトで用意してあるテストディレクトリを削除しておきましょう!

ちなみに、RSpecのファイルのみ作成したい場合は下記のジェネレートコマンドを入力すれば、作成される。
下記の場合は、RSpecディレクトリに、modelディレクトリが作成されて、userのRSpecファイルが作成される。

spec/models/user_spec.rb

ターミナル.
bin/rails g rspec:model user

#結果
      create  spec/models/user_spec.rb

今回はここまでにします。

間違い、誤記、ご助言等ございましたらご教授くださると幸いです。
ありがとうございました。

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