20200924のRubyに関する記事は21件です。

Ruby on Rails 〜MVCとルーターの基礎〜

はじめに

本記事では、MVC(Model(モデル)、View(ビュー),Controller(コントローラー)とRouter(ルーター)の関連を確認しながら、用語について解説していきます。

Router(ルーター)とは

RouterはHTTPリクエストがあったURLに対して、アプリケーション内のどの機能を結びつけるを決める役割を果たします。そして次に説明するControllerに1つのメソッドを割り当てます。

Controller(コントローラー)

"Controller"はMVCパターンの司令塔的役割を果たします。
これは、ユーザから送られてきたHTTPリクエストの処理を担当します。そのHTTPリクエストはRouterからControllerの1つメソッドと結びつきます。
そしてControllerはHTTPリクエストに対応したModelの作成や取得、取得した値をViewに渡して処理を行わせる、というようなことを行います。
つまりModelやViewに命令を出すことがControllerの仕事です。

Model(モデル)

"Model"はデータとやりとりをしてくれる存在です。
Controllerからの命令に従い、データベースから情報を取得したり、データを更新したり、書き込んだり、データを消去したりします。
データベースと直接やりとりできるのはModelだけです。

View(ビュー)

ViewではControllerから渡されたデータを、HTMLページでどのように表示するかを定義します。最終的にHTTPレスポンスとして返すWebページです。
Viewでは見た目の部分だけでなく、Controllerによって選択された適切なModelが埋め込まれます。

まとめ

これを一連の流れにしてまとめます。
RouterはHTTPリクエストを受け取り、適切なアクションとひも付けます。
Controllerはそのアクションを実行してModelやVeiwに指示を出して、ViewをHTTPレスポンスとして返すと行ったところでしょうか。

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

【Rails】ページの上部に戻るボタン

 はじめに

・Railsを使用してアプリケーションを開発
・viewsの下部地点から上部に戻るボタンを設置する

Ruby 2.5.7
Rails 5.2.4.4

流れ

gem 'jquery-rails' を導入
・アクションを起こすボタンを用意(画像でなく文字やfont-awesome等でも代用可能)
・viewにボタンを記載しcss及びjavascriptへ追記

ボタン画像

top_btn.png

適当に準備します・・・

画像ファイルをapp/assets/images配下に格納

viewsへ追加

<span id="top-btn">
  <a href="#">
    <%= image_tag asset_path('top_btn.png'), class: 'top-btn' %>
  </a>
</span>

・asset_path('~~') は、app/assets/images配下の画像を読み込む

css 及び jsファイルへ追記

.top-btn {
  position:   fixed;
  right:      30px;
  bottom:     40px;
}

.top-btn a {
  width:      50px;
  height:     50px;
}
$(function() {
  $('#top-btn a').on('click',function(event){
    $('body, html').animate({
      scrollTop:0
    }, 1000);
    event.preventDefault();
  });
});

初期状態(ページの上部)では表示したくない場合?

$(function() {
    var topBtn = $('#top-btn a');
    topBtn.hide();
    $(window).scroll(function(){
         if ($(this).scrollTop() > 100) {
             topBtn.fadeIn(1000);
         } else {
             topBtn.fadeOut();
         }
    });
    topBtn.click(function () {
        $('body, html').animate({
      scrollTop:0
    }, 1000);
    event.preventDefault();
  });
});

fadeIn 及びfadeOut のif文にてボタンの表示を設定しています・・

終わり

上記記載内容で、実装できるはず・・・
不明点及び説明がずれている(誤っている等)ございましたら、コメントにてにてお伝えいただければありがたいです。

以上、ご参考になれば幸いです。

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

再帰処理の流れについて基本的な理解を目指す

 「自分自身を呼び出す」再帰的関数に関するコード記述を読んでも、なかなか理解が追い付かずに折れかけたので、自戒を込めて「再帰処理の流れについて基本的な理解を目指す」をテーマに記事を書いてみたいと思います。
 下記のシンプルなコードを用いてどのような流れで関数が実行されているか、考えてみます。

再帰的関数

1  def recursive(n)
2     p 'a'
3     p n
4     return 1 if n == 0
5     p n
6     n * recursive(n - 1)
7     p 'b'
8     p n
9  end
10 
11  p recursive(5)

実行すると、下記のとおり。
recursive1.png

初見、なぜ数字が0から増えていくのかがまったく分からずに混乱しました。
「関数が関数を内包している」様子をイメージしながら、再帰的関数の仕組みについて、下記のように自分なりにかみ砕いてアウトプットしてみます。

おおまかな流れ

(1)引数として5が与えられて関数実行
→(2)p "a"から5行目のp nまでが実行される(※当然、後置if文returnは実行されない)
→(3)6行目で、自身の関数recursive(4)が実行される。
→(4)ループのように、上記(2)から(3)が、recursive(0)まで実行される。(※この時点で、「(省略)..."a", 1 , 1」まで処理されている」)
→(5)recursive(0)実行時、 p "a"から3行目のp nまでが実行されたのち、後置if文 returnが実行される。returnにより、recursive(0)関数処理が終了。
→(6)ここからは、巻き戻し?逆行?の様な状態になる。
実行場面がrecursive(1)に戻り、7行目の"b"、8行目のp n(※nは1)が実行され、recursive(1)終了。
→(7)実行場面がrecursive(2)へ戻り、7行目の"b"、8行目のp n(※nは2)が実行され、recursive(2)終了。
→(recursive(4)まで省略)
→(8)実行場面がrecursive(5)へ戻り、7行目の"b"、8行目のp n (※nは5)が実行され、recursive(5)が終了しすべての関数が完結。
→(9)最後に、p recursive(5)(p nと同義で)5が出力され、終了。(※仮にpメソッドを除くと、最後の5は出力されない。)

・・・個人的に再帰的関数はかなり理解が難しいです。
場数をこなして慣れていくのがいいのかなと少し頭を悩ませつつ、筆をおきます。:confused:

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

単体テスト時、ActiveRecordの暗黙的型変換でハマった話

事象

以下の単体テストコードが失敗してしまう。

item_spec.rb
    it 'priceが半角数字でなければ保存できないこと' do
      @item.price = '1000'
      @item.valid?
      expect(@item.errors.full_messages).to include('Price Half-width characters.')
    end

原因

integer型の項目「price」への文字列「1000」代入時、
暗黙的型変換が行われ、priceには数値「0」が代入されたため。

↓暗黙的型変換が行われる様子をコンソールで確認↓

irb(main):002:0> @item.price = '1000'
=> "1000"
irb(main):003:0> @item.price
=> 0

文字列「1000」を代入した後のpriceの値は数値「0」となります。

これはRails 5から追加された「ActiveRecord Attributes API」という機能によるものです。
暗黙的型変換は裏で以下のようなセッターが動いているイメージです。
to_iしているところがポイントです。

  # セッター
  def price=(price)
    @price = price.to_i
  end

結論

Itemモデルに以下の記述をすることで解決

item.rb
class Item < ApplicationRecord
  attr_accessor :price

【解説】
 attr_accessorは項目に対してセッター・ゲッターの処理を実現できるメソッドです。
 以下の記述と「attr_accessor :price」は同じ意味です。

  # セッター
  def price=(price)
    @price = price
  end

  # ゲッター
  def price
    @price
  end

つまり、これで純粋なセッター(to_iしない)が呼び出されることによって、
to_iされない文字列をpriceに代入できるようになります。

irb(main):002:0> @item.price = '1000'
=> "1000"
irb(main):003:0> @item.price
=> "1000"

【参考リンク】

「ActiveRecord Attributes API」の仕組みについては、以下の記事を参考にさせていただきました。
https://www.wantedly.com/companies/wantedly/post_articles/31132

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

Rails APIモード キーワード複数検索機能を配列と繰り返し処理を使って実装してみた。

機能として成り立つ前

def keyword
    if params[:keyword]
      keywords = params[:keyword].split(/[[:blank:]]+/).select(&:present?)
      keywords.each do |keyword|
        offices = Office.where('name LIKE ? OR address LIKE ? OR near_station LIKE ? OR introduction LIKE ? OR company LIKE ?',"%#{keyword}%", "%#           {keyword}%", "%#{keyword}%", "%#{keyword}%", "%#{keyword}%")
      end
    else
      pagy, offices = pagy(Office.all)
      pagy_headers_merge(pagy)
    end
    render json: offices, each_serializer: OfficeIndexSerializer, include: '**'
  end

このような記述で彷徨っていた。理由としては、1単語では検索可能だが、複数キーワード(例えば,「東京 福岡」のような検索方法では、福岡という文字だけでしか検索できないような状態だった。

気になっていた点

  • each文を使って繰り返し処理を書いているが、これがそもそも機能していないっぽい。
  • where句の中身の記述があっているのか自信がない・・・。

最終的なコード

  def keyword
    if params[:city_id]
      offices = Office.where(city_id: params[:city_id])
    elsif params[:keyword]
      keywords = params[:keyword].split(/[[:blank:]]+/).select(&:present?)
      offices = []
      keywords.each do |keyword|
        offices += Office.where('name LIKE (?) OR address LIKE (?) OR near_station LIKE (?) OR introduction LIKE (?) OR company LIKE (?)',"%#{keyword}%", "%#{keyword}%", "%#{keyword}%", "%#{keyword}%", "%#{keyword}%")
      end
    else
      pagy, offices = pagy(Office.all)
      pagy_headers_merge(pagy)
    end
    render json: offices
  end

変わった点としては、offices = []を加えた。キーワードを格納する箱を作るイメージ。
そして、office += Office.whereと、where句内の?に()を加えた。

これにより、「東京」というような1単語のみのキーワード検索のみならず、「東京 福岡 千葉」というような複数キーワードに関しても検索機能として成り立たせることができた。

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

[Rails][jQuery] remote:trueとjs.erbを使って非同期のいいね機能実装

概要

Railsプロジェクトで掲示板サイトを作成中、remote:truejs.erbファイルを用いた非同期のいいね機能を実装する機会がありました。
備忘録としてここに手順を残しておきます。

前提

Rails 5.0

favorites_tableの作成モデルのアソシエーションを定義は既にされているものとします。

アソシエーション:
users - has_many :posts, has_many :favorites
posts - belongs_to :user, has_many :favorites
favorites - belongs_to :user, belongs_to :post

jQueryの準備

Rails内でjQueryを使えるように準備します。
まずはgemの導入します。

Gemfile
gem 'jquery-rails'

ターミナルでbundle installします。
そしてapplication.jsに記述を追加します。

application.js
# Rails5.1より前の場合
//= require jquery
//= require jquery-ujs

# バージョンがRails5.1以降の場合
//= require jquery
//= require rails-ujs

今回は、Rails 5.0 を使っているのでjquery_ujsrequireしてあげてください。
この場合、必ずjqueryを先にrequireするようにしてください。

ちなみにrails-ujsRails5.1よりデフォルトで搭載されているので、application.jsにてrequireしてあげるだけで大丈夫です。

ルーティングの設定

createアクションをpostメソッド、destroyアクションをdeleteメソッドを使ってルーティングを記述します。
オプションでas:を記述することでPrefixの名前を指定できます。

routes.rb
  post '/favorite/:post_id' => 'favorites#create', as: 'like'
  delete '/favorite/:post_id' => 'favorites#destroy', as: 'unlike'

favorites_controllerの作成

いいねのcreateとdestroyを定義します。

app/controllers/favorites_controller.rb
class FavoritesController < ApplicationController
   before_action :set_post

   def create
      @favorite = Favorite.create(user_id: current_user.id, post_id: @post.id)
   end

   def destroy
      @favorite = Favorite.find_by(user_id: current_user.id, post_id: @post.id)
      @favorite.destroy
   end

   private

   def set_post
      @post = Post.find(params[:post_id])
   end
end

部分テンプレートでviewを作成

使い回ししやすいように、部分テンプレート化しています。

app/views/users/show.html.erb
<%= render 'posts/posts', posts: @posts %>
app/views/users/_post.html.erb
<% posts.each do |post| %>


  <div id="favorite-<%= post.id %>">
    <%= render partial: "favorites/favorite", locals: { post: post } %>
  </div>
<% end %>

jsでイベントを発火させるためには、idまたはclassでセレクタを指定しますが、今回はどのpostに対するいいねなのかを判別するために、id内にそのpostのidを含める必要があります。
上のように書くことによって、id="post-1"のようなidを指定することができます。

app/views/favorites/_favorite.html.erb
<% if Favorite.find_by(user_id: current_user.id, post_id: post.id) %>
  <%= link_to "いいね", unlike_path(post.id), method: :delete, remote: true,  class: "btn btn-default" %>
<% else %>
  <%= link_to "いいね", like_path(post.id), method: :post, remote: true, class: "btn btn-success" %>
<% end %>
<%= post.favorites.length %>

いいねボタンのlink_toにremote: trueを追加します。
この記述により、通常であれば、link_toで呼ばれるアクションに対応するhtml.erbファイルを呼び出すところ、js.erbファイルを呼び出せるようになります。
なのでページ遷移を行わず非同期で通信が行われるようになります。
最後にいいね数を<%= post.favorites.length %>で表示しています。

js.erbファイル作成

まずはcreateとdestroyそれぞれのアクションに対応するjs.erbファイルを作成します。
app/views/favorites/create.js.erb
app/views/favorites/destroy.js.erb

app/views/favorites/create.js.erb
$("#favorite-<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");
app/views/favorites/destroy.js.erb
$("#favorite-<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");

まずはセレクタの指定ですが、id="favorite-<%= post.id %>に対応するように指定します。
そして、jQueryのhtml()メソッドで、指定したセレクタのhtmlを置き換えます。

置き換える内容が
"<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>"の部分で、部分テンプレートの_favorite.html.erbを呼び出しています。
(renderの前にあるjは、escape_javascriptのエイリアスで、改行と括弧をエスケープしてくれるメソッドです。)

これで非同期のいいね機能実装完了です!

スクリーンショット 2020-09-24 21.26.48.png

スクリーンショット 2020-09-24 21.31.17.png

最後に

ここまで読んでいただきありがとうございます。
間違っている点などがありましたらご指摘いただきますよう、よろしくお願いいたします。

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

2020年で最も需要のあるプログラミング言語

本記事はMost in-demand programming languages in 2020の日本語訳です。翻訳元に報告していますが、もし苦情が来たら消します。
翻訳は不慣れなので変なところもあると思いますが、ご容赦ください。


Most-in-demand-programming-languages-in-2020.jpeg

ソフトウェア開発業界は絶えず変化しており、それは開発者の能力に対する企業のニーズも変化していることを意味します。そのため、あなたが想像できるように、Webアプリケーション、ゲーム、アルゴリズムなどのあらゆる側面の開発をカバーするために、選択できるプログラミング言語はたくさんあります。その上で、私たちは2020年で最も需要のあるプログラミング言語とその主な特徴について触れます。

  1. JavaScript (回答者の71%がこのスキルに関する求職者を探している)
  2. Java (57%)
  3. C# (53%)
  4. Python (51%)
  5. PHP (40%)
  6. Ruby (15%)

Screenshot-2020-05-03-at-22.31.53.png
2020年で最も需要のあるプログラミング言語

JavaScript

JavaScriptが2020年で最も需要のあるプログラミング言語リストのトップであることは全く不思議ではありません。

今日では何らかの方法でJavaScriptを使用することなしに開発者になることは不可能です。調査の回答者の71%以上がJavaScriptでコードを書ける開発者を探しており、JavaScriptが最も人気のあるプログラミング言語のうちの一つであることが想像できます。また、JavaScriptはWebにおける偏在性と私たちのインターネットへの重い依存のため、非常に人気があります。Twitter、Facebook、YouTubeなどの最も人気のあるサイトの多くは、JavaScriptを使ってインタラクティブなWebページを生成したり、コンテンツを動的にユーザに表示したりしています。

JavaScriptはコア言語があり、追加の開発ツールによって柔軟性が保たれています。JavaScriptは寛大で柔軟な構文を持ち、全てのメジャーなブラウザで動作するため、初学者にとって最も簡単なプログラミング言語の一つです。今日、JavaScriptは世界中で広く使用されているプログラミング言語であり、あらゆるところで動作します:コンテナ、マイクロコントローラ、モバイル端末、クラウド、ブラウザ、サーバなど。

主な特徴

  • JavaScriptはここ数年で大規模な現代化と徹底的な点検を経てきました。ES5、ES6といったJavaScriptのメジャーなリリースはいくつかのモダンな機能が追加され、今日のJavaScriptは過去10年間のJavaScriptとは完全に別物です
  • Node.jsのおかげでJavaScriptはイベント駆動なプログラミングを提供し、特にI/Oの複雑なタスクに適しています。今日では、Node.jsとJavaScriptは、サーバとモバイル端末を含めてほとんど全てのプラットフォームで動作します
  • JavaScriptはブラウザプログラミングにおいて、議論の余地のない王様です。今日、Web開発は主にVue.js、Angular、ReactといったJavaScriptベースのSPAフレームワークによって支配されています

Java

Javaは2020年で最も需要のあるプログラミング言語リストで2位の座を手にしています。

Javaは、ビジネスでは最も人気のモバイルコンピューティングプラットフォームであるAndroidのネイティブ言語であることから人気のあるプログラミング言語です。

Javaは過去数年の間に、非常にユーザに優しいモダンな言語にビジネスの一部を奪われました。

Javaは欠陥の改善に取り組んでおり、GraalVMアクションを介してクラウドにフィットさせる努力をしています。Javaはまだエンタープライズではナンバーワンのプログラミング言語です。Javaは誕生してからずっとトップクラスの需要があるプログラミング言語です。大企業の多数がバックエンドWebシステムやデスクトップアプリケーションのためにJavaを使っているため、もし開発者がJavaを知っていれば、その開発者は継続的に需要が高い状態になるでしょう。Javaは静的型付けの言語で、そのためバグが少なく、メンテナンスを速く行えて管理しやすいです。

主な特徴

  • Javaはマルチパラダイムを提供し、強力で、多機能で、柔軟な学習曲線と高い生産性を持つインタプリタ型のプログラミング言語です
  • Javaは厳格な後方互換性を持ち、これはビジネスアプリケーションにとって重要な要件です。JavaではScalaやPythonのようなメジャーな破壊的変更が取り入れられたことはありません。その結果、Javaはいまだにビジネスにとってナンバーワンの選択肢です
  • JavaのランタイムであるJVMはソフトウェア工学の結晶で、ビジネスにおいて最高の仮想マシンの一つです。何年もの技術革新とエンジニアリングの職人技により、JVMは素晴らしい機能と性能をJavaに提供します。JVMはいくつかの優秀なガベージコレクションもJavaに提供しています

C#

C言語は、移植性と、AppleやMicrosoftのような巨大なIT企業から早期に採用されたことのおかげで最も古くて最も人気のあるプログラミング言語のうちの一つとなった言語です。1
C-sharpとしても知られるC#は、2000年にMicrosoftによって開発された言語のスピンオフです。C#はオブジェクト指向言語で、アクションの代わりにオブジェクトを中心に、ロジックの代わりにデータを中心に構築されます。C#の特徴はJavaと似ており、Windowsのデスクトップアプリケーションとゲームを開発するのに特に有効です。

しかし、C#はWebアプリケーションとモバイルアプリケーションを開発するのにも使えます。C#はC++のようなC派生の言語に似た構文を使っており、あなたがCファミリーの中の別の言語から来たのであれば簡単に習得できます。C#は銀行のトランザクション処理のような大企業のアプリケーションの開発にしばしば使われます。C#は人気のUnityゲームエンジンを使った2Dや3Dのビデオゲームを作るために推奨される言語です。今日では、C#はWindowsプラットフォームにおいてだけでなくLinuxプラットフォームやiOS/Androidプラットフォームでも幅広く使用される、マルチパラダイムな言語です。

主な特徴

  • C#はプラットフォーム非依存でもあり、Linux、Windows、モバイル端末で動作します
  • Microsoftの後ろ盾があり、長年に渡り業界にいるC#はライブラリとフレームワークの大きなエコシステムがあります。ASP.NETはWebアプリケーション開発に使われます(主にWindows上での)
  • 開発者体験という面では、C#はJavaよりはるかに優れています

The-top-5-languages-tested-on-DevSkiller.png
DevSkillerでテストされた言語トップ5

Python

Pythonはおそらくこのリストの中で最もユーザに優しいプログラミング言語です。Pythonの構文は明確で、直感的で、ほとんど英語だとよく言われ、初学者にとって本当に良い選択肢です。Pythonは高レベルで、汎用性が高く、Webアプリケーションやデータ解析、アルゴリズムの開発などに使われます。Pythonは、科学計算やエンジニアリング、数学といったフィールドで頻繁に使われるSciPyやNumPyのようなパッケージも持っています。

Pythonはスクレイピングにしばしば使われ、PHPでコーディングするのに何時間もかかるものが、Pythonだと数分しかかかりません。Pythonは、あなたの時間を消費する日々のタスクを含む特定の作業を自動化するためにも使うことができます。もしバックエンドのWeb開発の例に興味があれば、オープンソースの(Pythonで書かれた)Djangoフレームワークは人気で、学ぶのが簡単で、多機能です。そしてJavaのように、Pythonには多様なアプリケーションがあり、あなたのユースケースのために最も良いプログラミング言語を選択する時に、多様で強力な選択肢となります。今日、Pythonは広く行き渡り、ソフトウェア開発の多くの分野で使用され、そしてその勢いが衰えるようには見えません。

主な特徴

  • Pythonには非常に活発なコミュニティとサポートがあります。たとえあなたがデータサイエンス、業務アプリケーション、AIのどれで働いているのだとしても、常に十分なPythonの組織2やフレームワークが見つかります
  • Pythonには第一級のC++/Cとの統合機能があり、CPU負荷の高いタスクをシームレスにC++/Cにオフロードすることができます。Pythonは、SciPy、Pandas、NumPyなど、統計、Scikit-Learn3、数学、および計算科学のための素晴らしいツールセットも提供します。 結論として、Pythonは機械学習/ディープラーニング/データサイエンスの状況やその他の科学的な領域を支配しています
  • Pythonのウリはその言語設計にあります。それは生産性が高く、エレガントで、シンプルで、その上強力です。Pythonは開発者経験という面で黄金律を設定し、Julia、Goといったモダンな言語に対して多大な影響を与えました

PHP

たとえ多くの論争があるとしても、PHPは2020年で最も需要のあるプログラミング言語リストに入っています。

PHPは幅広く使用されているオープンソースの汎用スクリプト言語で、典型的にはWebアプリケーション開発に適しています。たとえ以前ほどではないとしても、PHPは依然として世界中で最も用いられているプログラミング言語のうちの一つです。PHPはFacebookやYahoo!といった多数の大きな会社によって使用されています。PHPは汎用的で、動的な、基本的にはサーバサイドのWebアプリケーションの開発のために使用されているプログラミング言語です。

PHPはJavaScriptのような新しいWeb言語が実現するまでずっと、ほとんど全てのモダンなWebサイトを構築可能にしました。いくつかの調査によると、PHPがWebの3分の1を支えているとのことです。たとえPHPが以前ほどは注目されていないとしても、PHPは今後何年にもわたって進化を継続し、最も人気のあるプログラミング言語のうちの一つとしての地位を維持するでしょう。

主な特徴

  • 多くの大きな会社がPHPを使用しており、そのための素晴らしいツールのサポートに繋がっています
  • PHPはWebアプリケーション開発に過去25年4に渡って使用されており、強力で安定したPHPフレームワークが数多く市場に存在します
  • PHPは非常に生産性が高いサーバサイドWeb開発プログラミング言語のうちの一つです。結果として、Webアプリケーションを素早く開発するために、IT業界で広く使われています。最も有名なSNSの一つであるFacebookはPHPで開発されました

USA-PHP-versions-popularity-research.png
アメリカにおけるPHPバージョンの人気調査

Ruby

特に、Rubyは人気のあるRuby on Rails Webアプリケーションフレームワークのための基盤として使われます。RubyはC言語で実装され、ガベージコレクタがあります。Rubyは90年代半ばに作られましたが、ここ10年ほどの間に人気を獲得しました。Rubyは非常に動的で、オブジェクト指向言語で、プログラマーが使うための様々な機能を持っています。Rubyの経験が6年以上ある開発者は、現在の採用状況では2倍の面接依頼を受けることが期待できます。

Twitter、Shopify、そして多くのスタートアップがいずれかの段階でRuby on Railsを使ってWebサイトを構築しています。Rubyはまた、素晴らしいハイテク企業との関連性のために選び出すには本当に良い言語です。5
Pythonのように、Rubyは開発者の生産性と幸福を非常に重視しています。Rubyは新しい開発者にとって学習曲線がフラットになる非常に優れた言語でもあります。

主な特徴

  • RubyはTwitter、GitHub、Airbnbのような最大級のソフトウェアプロジェクトで使われ、そして素晴らしいツールとフレームワークの支援があります
  • Rubyそれ自体は破壊的ではありませんが、RubyのWeb開発フレームワークであるRuby on Railsはおそらく最も破壊的で、影響力のあるサーバサイドWeb開発フレームワークです
  • Rubyはプログラミング言語の最高の機能のうちのいくつかをうまく利用してきました: 簡潔さ、動的、ガベージコレクタのあるオブジェクト指向、そして関数型です

  1. [訳注] 一応、書き間違えではありません。なぜC言語の話が出てきたのか謎です。 

  2. [訳注]よくわからなかったのですが、コミュニティとかそういう意味でしょうか… 

  3. [訳注]機械学習のライブラリです。具体的なライブラリが数学、統計、計算科学と並ぶのは非常に違和感があるので、何か誤訳があるかもしれません。 

  4. [訳注]原文では35年となっていますが、PHPは1995年に公開された言語であるため、おそらく誤りです。 

  5. [訳注] よくわからないですが、Rubyを習得していればハイテク企業に入社しやすいとかそういう意味ですかね… 

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

【Rails】ドット(.)とか、コロン(:)とは一体

::とか、.とかよくお見かけします。
これはメソッドの呼び出しの違いで、表記が変わるという種明かしなんです。

ドット記法

class User
  def method
    puts "Hello"
  end
end
> user = User.new

> user.method
Hello
=> nil

コロン記法

class User
  def method
    puts "Welcome"
  end
end
> user = User.new
> user::method
Welcome
=> nil

ドット記法とコロン記法の違い

ドット記法とコロン記法の違いは、定数を呼び出せるかどうか。

記法 メソッドの呼び出し 定数の呼び出し
ドット記法 ×
コロン記法

Rubyでは、クラスやモジュールも定数として扱われる。

require 'uri'
require 'net/http'

url = URI.parse("http://yahoo.co.jp")
http = Net::HTTP.start(url.host, url.port)
> document = http.get(url.path)

> puts document.body
# ドキュメントを表示

まとめ

このようにコロン記法だと、指定した別ファイルのクラス内やモジュール内のアイテムにアクセスすることができる。

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

Rails6のマルチDBの自動切換(ActiveRecord::Middleware::DatabaseSelector)の実装を読んでみる

概要


Rails6.0より実装された複数DB対応に関して、自身のプロジェクトに適用できるか?という判断のために実装を追ってみます

Railsガイドでの記載

https://railsguides.jp/active_record_multiple_databases.html#コネクションの自動切り替えを有効にする

自動切り替え機能によって、アプリケーションはHTTP verbや直近の書き込みの有無に応じてprimaryからreplica、またはreplicaからprimaryへと切り替えます。
アプリケーションがPOST、PUT、DELETE、PATCHのいずれかのリクエストを受け取ると、自動的にprimaryに書き込みます。書き込み後に指定の時間が経過するまでは、アプリケーションはprimaryから読み出します。アプリケーションがGETリクエストやHEADリクエストを受け取ると、直近の書き込みがなければreplicaから読み出します。

HTTPメソッドによる判定 + 書き込みからの時間による判定 の2種類にて行われているという記載になっている
設定方法としては下記の記載

config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session

コードリーディング

まずはミドルウェアの適用部分から追ってみる

Railtieでのミドルウェアの適用部分

activerecord-6.0.3.2/lib/active_record/railtie.rb

  class Railtie < Rails::Railtie # :nodoc:
    config.active_record = ActiveSupport::OrderedOptions.new

    initializer "active_record.database_selector" do
      if options = config.active_record.delete(:database_selector)
        resolver = config.active_record.delete(:database_resolver)
        operations = config.active_record.delete(:database_resolver_context)
        config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations, options
      end
    end
  • database_selectorが設定されていれば実行される
  • database_resolverdatabase_resolver_contextはただ渡されるようになっている

続いてミドルウェア部分の実装を追っていく

[補足] OrderedOptionsとは?

https://api.rubyonrails.org/classes/ActiveSupport/OrderedOptions.html

Hashをドットでアクセスできるようにしている機構
deleteで設定されていない場合はnilが返る

Ruby2.6.5 pry(main)> x = ActiveSupport::OrderedOptions.new
=> {}
Ruby2.6.5 pry(main)> x.delete(:hoge)
=> nil

DatabaseSelectorの実装内容

activerecord-6.0.3.2/lib/active_record/middleware/database_selector.rb

module ActiveRecord
  module Middleware
    class DatabaseSelector
      def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
        @app = app
        @resolver_klass = resolver_klass || Resolver
        @context_klass = context_klass || Resolver::Session
        @options = options
      end

      attr_reader :resolver_klass, :context_klass, :options

      def call(env)
        request = ActionDispatch::Request.new(env)

        select_database(request) do
          @app.call(env)
        end
      end
  • database_resolverが設定されていない場合はDatabaseSelector::Resolverが設定される
  • database_resolver_contextが設定されていない場合はDatabaseSelector::Resolver::Sessionが設定される

続いて重要そうな#select_databaseを見てみる

DatabaseSelector#select_database

module ActiveRecord
  module Middleware
    class DatabaseSelector
      private
        def select_database(request, &blk)
          context = context_klass.call(request)
          resolver = resolver_klass.call(context, options)

          if reading_request?(request)
            resolver.read(&blk)
          else
            resolver.write(&blk)
          end
        end

        def reading_request?(request)
          request.get? || request.head?
        end
    end
  end
end

contextが先に実行され、それをもとにresolverが作られているため、まずはcontextのほうを確認してみる

Resolver::Session.call

class Resolver # :nodoc:
  class Session # :nodoc:
    def self.call(request)
      new(request.session)
    end

    def initialize(session)
      @session = session
    end

    attr_reader :session

.call#newしているだけで、ここではActionDispatch::Requestのsessionをセットしているのみ
なので続いてはResolverの.callを確認

Resolver.call

class Resolver # :nodoc:
  SEND_TO_REPLICA_DELAY = 2.seconds

  def self.call(context, options = {})
    new(context, options)
  end

  def initialize(context, options = {})
    @context = context
    @options = options
    @delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
    @instrumenter = ActiveSupport::Notifications.instrumenter
  end

  attr_reader :context, :delay, :instrumenter

ここでもnewしているだけなので、元のDatabaseSelector#select_databaseをもう少し読み進めていく
※ちなみにinstrumenterは通知の仕組み(購読すれば通知を受け取れる)

HTTPメソッドの判断部分

def select_database(request, &blk)
  context = context_klass.call(request)
  resolver = resolver_klass.call(context, options)

  if reading_request?(request)
    resolver.read(&blk)
  else
    resolver.write(&blk)
  end
end

def reading_request?(request)
  request.get? || request.head?
end

ここではGETかHEADの場合 => read
それ以外の場合 => write
としてresolverの処理を実行している(Resolverがさらに判定を行っているっぽい

まずはreadから見てみる

Resolver#read

class Resolver # :nodoc:
  def read(&blk)
    if read_from_primary?
      read_from_primary(&blk)
    else
      read_from_replica(&blk)
    end
  end

  private
    def read_from_primary(&blk)
      ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
        instrumenter.instrument("database_selector.active_record.read_from_primary") do
          yield
        end
      end
    end

    def read_from_replica(&blk)
      ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role, prevent_writes: true) do
        instrumenter.instrument("database_selector.active_record.read_from_replica") do
          yield
        end
      end
    end

roleをどちらにするかとinstrumentにより通知を行ったうえで処理を進めているよう。
では続いて#read_from_primary?にて何をどう判定しているのか?

  private
    def read_from_primary?
      !time_since_last_write_ok?
    end

    def send_to_replica_delay
      delay
    end

    def time_since_last_write_ok?
      Time.now - context.last_write_timestamp >= send_to_replica_delay
    end

contextいわゆるsessionの#last_write_timestampというものが、
最初のconfig.active_record.database_selector = { delay: 2.seconds }のdelay分だけ経過していなかった場合はprimaryにして、
delay分経過している場合はreplicaを見るようになっている

ということでsessionの#last_write_timestampを確認

class Session # :nodoc:
  # Converts milliseconds since epoch timestamp into a time object.
  def self.convert_timestamp_to_time(timestamp)
    timestamp ? Time.at(timestamp / 1000, (timestamp % 1000) * 1000) : Time.at(0)
  end

  def last_write_timestamp
    self.class.convert_timestamp_to_time(session[:last_write])
  end

ただsesion[:last_write]をtimeオブジェクトに直しているのみ
readはこれで終わりなので続いてwrite

Resolver#write

class Resolver # :nodoc:
  def write(&blk)
    write_to_primary(&blk)
  end

  private
    def write_to_primary(&blk)
      ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
        instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
          yield
        ensure
          context.update_last_write_timestamp
        end
      end
    end

これはただただ書き込みのroleにて書き込んでいるのみ

class Session # :nodoc:
  def self.convert_time_to_timestamp(time)
    time.to_i * 1000 + time.usec / 1000
  end

  def update_last_write_timestamp
    session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
  end

そして、session[:last_write]に現在のタイムスタンプを付与

まとめ

さすがRailsですね。とても分かりやすく作られていました。

結局はActionDispatch::Requestをもとに処理されているだけで、かつResolverの#read#writeを両方書き換えれば
GETとHEADに限らず好きに書くことができそうでした。

ヘッダーやIPアドレス、localhostなどによる判定も、Requestが持っている情報なので使えますし
できることはかなり多いと思いました。

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

組み合わせ爆発ハラスメントの処方箋

プログラミング初学者向けの内容です。
今のところ Golang, Ruby, Python, JavaScript, TypeScript による処方箋のみ掲載しています。

ある日のこと

知人「店長からさぁ、

   『うちはメニューの数が少ないから、
    コンビ・メニュー作ることにした』

   『とりあえず、
    今あるメニューを組み合わせて、
    単品から全部入りまで
    すべての組み合わせのリスト作ってくれ!』

   って、言われたんだけど…」

俺「え? それって、
  ???があるとしたら、
  ↓みたいなやつ?」

1:?
2:?
3:? ?
4:?
5:? ?
6:? ?
7:? ? ?

知人「そう。そう。それ!それ!」

俺「作れるけど、、
  きっとものすごい数になるよ。
  単品メニューって何種類くらいあんの?」

知人「20種類くらいかなぁ。。
   物好きな店長でしょ?!
   めんどくせぇ。。」

俺「…」

俺「あのさぁ、、
  面倒くさいとかの次元じゃないんだけど。。」

俺「0.1 mm 厚の紙を 26 回折ったら
  富士山より高くなるって知ってる?」

\begin{align}
0.1mm\times2^{26} &= 6,710,886.4 mm\\
&\fallingdotseq 6.7 km
\end{align}

知人「あ、なんか聞いたことあるかも。。」

俺「それと同じなんだけど、、」

\begin{align}
2^{20} - 1 &= 1,048,575 通り\\
&\fallingdotseq 105万通り
\end{align}

俺「105万通りは、
  さすがにメニュー充実しすぎだろ(笑)」

知人「へ〜、そんなになるんだぁ!」

俺「…」

Golang による処方箋

取り急ぎ Golang で書いてみます。

menu.go
package main

import (
        "flag"
        "fmt"
        "strings"
)

func comball(in []string) [][]string {
        n := 1 << len(in)
        out := make([][]string, n)
        for i := 0; i < n; i++ {
                ss := make([]string, 0, len(in))
                for j := 0; j < len(in); j++ {
                        if 1<<j&i != 0 {
                                ss = append(ss, in[j])
                        }
                }
                out[i] = ss
        }
        return out
}

func main() {
        flag.Parse()
        args := flag.Args()
        for i, ss := range comball(args) {
                fmt.Printf("%d:%s\n", i, strings.Join(ss, " "))
        }
}

Go のバージョンです。

version
$ go version
go version go1.15.2 linux/amd64

実行してみます。

実行
$ go run menu.go ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt

Golang はコンパイルも実行も速くていいですね。
Generics をサポートしていないので string 型専用の関数になってしまい、そこが残念ポイントですが、LL のような感覚で気軽にいろいろ試せます。
(Generics は来年サポートされるようですね)

プログラムは標準出力へ書き出すようにしましたが、そのまま出力するとたぶん大変なことになるので menu.txt という名前のファイルへリダイレクトしました。

ファイルの先頭を見てみます。

ファイルの先頭
$ head menu.txt 
0:
1:?
2:?
3:? ?
4:?
5:? ?
6:? ?
7:? ? ?
8:?
9:? ?

最初の行に「なし」を出すようにしてます。

今度は末尾を見てみます。

ファイルの末尾
$ tail menu.txt 
1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?

1行目の「なし」を除いて、ちゃんと 104 万 8,575 行あります。
ファイルサイズが 56.4 MiB もありますが、、(笑)
(圧縮して 4 Mib くらい)
ひとまず、これで大丈夫そうです。

あと、 Golang はサクッとクロスコンパイルしてシングルバイナリが作れるのがいいですね!
とりあえず、AMD64 互換 の linux と Mac と Windows 用を用意して持ち帰ってもらうことにました。

build.sh
GOOS=linux   GOARCH=amd64 go build -o ./linux-amd64/menu       menu.go
GOOS=darwin  GOARCH=amd64 go build -o ./darwin-amd64/menu      menu.go
GOOS=windows GOARCH=amd64 go build -o ./windows-amd64/menu.exe menu.go

でも、、

せっかくプログラムを書いてあげたのに、結局、彼は「店長に怒られそう…」という理由で、これを使ってくれませんでした。

遠い昔を思い出す

知人は採用してくれませんでしたが、これってテストデータの生成(フラグの組み合わせとか)にも応用できますよ。

昔、入社 1 年目のとき、まるで野球部の球拾いのごとくテスターをやらされた日々を思い出します。

ある日、明確なテスト仕様書もない中で、先輩 SE から無茶振りされました。

「可能な組み合わせを全部テストするなんて当たり前なの!お前バカなの!?」

と怒鳴られました。

遠い昔のことなので、細かいことはあまり良く覚えてませんが、同期の仲間と計算してみると、1 個 1 分でやったとしても、寝ずにやって何十年かかるとか、そんな途方もないオーダーでした。

現実的な解が思い浮かばなかったので、もっと上の先輩に相談しました。
すると、即答で「バカは相手にしなくていいから(笑)!」と言ってくれ、あっさりとこの問題は解決してしまいました。

今考えると完全にパワハラでした。
2〜3日、真剣に悩みましたから(笑)

無茶ぶりした先輩 SE はその後しばらくして会社を辞めていきました。

でも、マシンが高速化し自動テストがあたりまえになった現代では、当時できなかったいろんなことができるようになりました。

あのとき、もし今の環境が手元にあったら、この程度の簡単な処方箋であっさりと解決していたのかも。。
そう考えると感慨深いものがあります。

先輩 SE が後輩を馬鹿呼ばわりすることもなく、彼がさらに上の先輩から馬鹿呼ばわりされることもなかったかもしれません。

ということで、他の言語の例もいくつか載せておきます。

Ruby で調合する

Ruby はあまり書いたことがないので、らしいコードじゃないかもしれません。

プログラムをみる
menu.rb
def comb(arr)
  out = []
  n = 1 << arr.size
  n.times do |i|
    a = []
    arr.size.times do |j|
      if 1 << j & i != 0 
        a << arr[j]
      end
    end
    out << a
  end
  out
end

comb(ARGV).each.with_index(0) do |a, i|
  puts i.to_s + ":" + a.join(" ")
end
実行
$ ruby --version
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
$ ruby menu.rb ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt
$ tail menu.txt 
1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?

まあ、Ruby の場合は組み込み関数を使えば、↓これでもいけますね。
出力順が違いますけど。。

menu2.rb
def comball(arr)
  out = [[]]
  arr.each_with_index { |s, i| out += arr.combination(i+1).to_a }
  out
end

comball(ARGV).each.with_index(0) do |a, i|
  puts i.to_s + ":" + a.join(" ")
end

Ruby って、書く順番がなんか他の言語と違いますよね。
この感覚が気持ちよくて好きです。。

Python で調合する

プログラムをみる
menu.py
import sys

def comball(arr):
    out = []
    n = 1 << len(arr)
    for i in range(n):
        a = []
        for j in range(len(arr)):
            if 1 << j & i != 0:
                a.append(arr[j])
        out.append(a)
    return out

arr = sys.argv
arr.pop(0)
for i, a in enumerate(comball(arr)):
    s = ' '.join(a)
    print('{0}:{1}'.format(i, s))
実行
$ python3 --version
Python 3.6.8
$ python3 menu.py ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt
$ tail menu.txt 
1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?

end や } が必要ない分、関数本体が短く書けますね。

JavaScript で調合する

プログラムをみる
menu.js
function comball(arr) {
    const out = []
    const n = 1 << arr.length
    for (let i = 0; i < n; i++) {
        const a = []
        for (let j = 0; j < arr.length; j++) {
            if ((1 << j & i) != 0) {
                a.push(arr[j])
            }
        }
        out.push(a)
    }
    return out
}

const arr = process.argv.slice(2)
comball(arr).forEach((a, i) => 
    console.log(i + ":" + a.join(" "))
)
実行
$ node --version
v12.16.1
$ node menu.js ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt
$ tail menu.txt 
1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?

JavaScript って & より != の方が演算子の優先順位が高いんですよね。
だから括弧が付いてます。
なんか理由があるんですかね。。

TypeScript で調合する

プログラムをみる
menu.ts
function comball<T>(arr: T[]): T[][] {
    const out: T[][] = []
    const n = 1 << arr.length
    for (let i = 0; i < n; i++) {
        const a: T[] = [] 
        for (let j = 0; j < arr.length; j++) {
            if ((1 << j & i) != 0) {
                a.push(arr[j])
            }
        }
        out.push(a)
    }
    return out
}

const arr: string[] = process.argv.slice(2)
comball(arr).forEach((a, i) =>
    console.log(i + ":" + a.join(" "))
)
実行
$ npx ts-node --version
v8.10.1
$ npx ts-node menu.ts ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt
$ tail menu.txt 
1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?
1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?

TypeScript は関数シグネチャを見れば何をしそうか分かるところが良いですね。
あと、Generics 使っています。
でも、トランスパイルが遅いところが玉に瑕です。

あとがき

本稿で扱ったプログラミング言語は演算子の使い方もほとんど同じなので、みんな似たコードになりましたが、それでも言語の個性が出ている部分もあって面白かったです。

後で他の言語も追記するかもしれません。
要望があればプログラムの解説も付けるかもしれません。

あと、各言語のエキスパートの方で、もっとカッコいい書き方を知ってるよ! という人は是非教えてください。

それでは!

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

CodeWarでの勉強

この記事について

最近始めたCodewarを通じて学べたことを少しずつアウトプット

学び① joinメソッド

Write a function that accepts an array of 10 integers (between 0 and 9), that returns a string of those numbers in the form of a phone number.

#僕の回答
def createPhoneNumber(numbers)
  return "(#{numbers[0]}#{numbers[1]}#{numbers[2]}) #{numbers[3]}#{numbers[4]}#{numbers[5]}-#{numbers[6]}#{numbers[7]}#{numbers[8]}#{numbers[9]}"
end

#理想の回答
def createPhoneNumber(str)
  "(#{str[0..2].join}) #{str[3..5].join}-#{str[6..10].join}"
end

解説

https://docs.ruby-lang.org/ja/latest/method/Array/i/join.html

僕が書いたコードでは配列のデータをバカ丁寧に一つ一つ文字展開をしている
理想のコードではstr[0..2]のようにまとめてやって、尚且つその配列の要素を結合して一つの文字列に変換するためにjoinメソッドを使用している。

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

Railsでログイン機能をシンプルに名前とパスワードだけで実装する(3)

前回の続き

概要

前回まででログイン機能はできたものの、現在の状態ではどのユーザーがログインしているか分からず、またログインしなくても一覧ページにアクセスできるという欠陥が残っている。それに対処していく

手順

  • current_roomメソッド。現在のログインしているユーザーを表示するヘルパーメソッドを追加する
app/helpers/sessions_helper.rb
  # ログインしているユーザーがいたら、ユーザーを代入する
  def current_room
    if session[:room_id]
      @current_room ||= Room.find_by(id: session[:room_id])
    end
  end
  • 今回はページに共通した部分がないので、不要ですが、必要ならログインしているかどうかを判断するヘルパーメソッドを定義します。
app/helpers/sessions_helper.rb
  # ログインしているかどうか判断する
  def logged_in?
    !current_room.nil?
  end
  • ログアウトメソッドを 2か所に書きます。
app/helpers/sessions_helper.rb
  def log_out
    session.delete(:room_id)
    @current_room = nil
  end
app/controllers/sessions_controller.rb
  def destroy
    log_out
    redirect_to login_path
  end
  • あとは以下のようにログインユーザーの表示とログアウト用リンクを追加します。
ログイン制御したページ
~
 <div class="header-title">
    <div class="header-title">受付一覧: 
    <%= current_room.name %>
    <%= link_to "Logout", logout_path, method: :delete %>
    </div>
 </div>
~
  • 最後に一覧画面へアクセスする前にログイン制御したいので、プライベートメソッド内に定義します。
app/controllers/receptions_controller.rb
class ReceptionsController < ApplicationController
  before_action :logged_in_room, only: [:index]
~
  private
    def logged_in_room
      unless logged_in?
        flash[:alert] = "ログインが必要です"
        redirect_to login_path
      end
    end
~

これで一覧ページへのログイン機能が簡単にですが完成しました。

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

Ruby_work 基礎問題

模範解答

work.rb

##### 問題1(四則演算とif文の復習) #####
# 以下のif文の後にコードを記述して、正しい表示がされるようにしてください

number = 5

if number % 2 == 0
  puts "偶数です"
else
  puts "奇数です"
end

##### 問題2(式展開の復習) #####
# 以下の表示がされるよう、コードを記述してください

name = "小松原" # ご自身の名前を代入してください

# 「私は●●です」と表示されるよう記述してください

puts "私は#{name}です"

##### 問題3(配列の復習) #####
# 以下の①から④についてコードを記述してください

# ①"田中","佐藤","山田"という名前が3つ入ったstudentsという配列を定義してください。
students = ["田中", "佐藤", "山田"]

# ②①でつくった配列に"鈴木"を追加してください。
students << "鈴木"

# ③②でつくった配列をターミナルに出力してください
puts students

# ④配列の中から番号を指定して"佐藤"を取り出し、ターミナルに出力してください
puts students[1]

##### 問題4(ハッシュの復習) #####
# 以下の①から②についてコードを記述してください

# ①下記のsasakiというハッシュの中から、"東京"を取り出してターミナルに出力してください
sasaki = {from: "東京", age: 30}

puts sasaki[:from]

# ②sasakiというハッシュにjob:というキーで"teacher"というバリューを追加してください
sasaki[:job] = "teacher"

# ③②で追加したjobというキーのバリューをターミナルに出力してください
puts sasaki[:job]


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

未経験者が自社開発企業に就職するためのREADME書き方

はじめに

こんにちは!

先月、無事にエンジニアデビューした1年目の者です。

私は某プログラミングスクールに3ヶ月間お世話になり、卒業後、3週間で自社開発企業に転職することができました。
この経験を生かして、私が意識してきたことを伝えていければと思います。

まず、前提として転職では見せ方が最も重要だと考えています。
例えば、職務経歴書、面接対策、wantedlyのプロフィールなどなど、、
この記事では、その中でも重要なことの一つである、ポートフォリオの見せ方について共有します。

記事の対象者

・未経験からエンジニア転職を考えていて、ポートフォリオを作成している方
・ポートフォリオのREADMEにて書く内容で迷っている方

ポートフォリオの見せ方とは

エンジニアの方々は、忙しいので、書類選考の段階で、ソースを細かく見たりサイトを触って見たりしてくれるとは限りません。
ここで活躍するのが、READMEです。
私は、多くの転職成功された方々のREADMEを見て、自分なりの最適解を見つけました。
共通点は、端的でわかりやすく、視覚的に訴えていることでした。
この共通点からREADMEを使ってわかりやすく、視覚的に伝えられるようにすることが大事だと思います。
この記事では内容についてフォーカスしているので、書き方については他の方の記事を参考にしてください。

READMEの構成

⬇️私の転職活動時に使用していたREADMEを元に説明していきます。
https://github.com/sora-uzu/The-View/blob/master/README.md

書くことは、主に4つです。
1, タイトルと概要、URL
2, 使用技術
3, AWS(インフラ)構成図
4, 機能、非機能一覧

1, タイトルと概要、URL

まずは、タイトル、URLはそのままの意味なので、概要について説明します。
概要は、以下の3点を一言で書いてください。
 ・どういうサイトか
 ・何ができるのか
 ・アピールポイント

これがかけたら、あとはサイトの分かりやすい画像を載せます。
載せ方は、癖があるので、
github readme 画像
などでググってください。

image.png

2, 使用技術

ここには、使用言語や、インフラ周りで使用しているサービスを書きます。
バージョンも書いていると良いです。

3, AWS(インフラ)構成図

インフラ周りで力を入れている方や、AWSを使っている方は、draw.ioを使ってインフラ構成図を作成してください。
各サービスの画像は、ググると公式サイトなどから転載可能な画像が取得できます。
インフラ周りでアピールポイントがあればここに記載すると良いと思います。
私の場合は、CircleCIでやっていることを書いています。
image.png

4, 機能、非機能一覧

機能と使用したgemを記載します。細かく書くというよりは、主な機能とそれに付随した機能を簡単に書いてください。
次に、非機能一覧では、なにでテストをしたか、何のテストをどのファイルで行っているかを書いてください。

最後に

以上が私が転職活動時にReadmeにおいて意識していたポイントです。私自身も多くの方のポートフォリオを参考にこういった構成になったので、皆さんも一つの参考にしていただけると嬉しいです。
ご覧いただきありがとうございました!

会社の紹介

私は現在、株式会社ダイアログという物流×ITの会社に勤務しております。
2020年9月現在、エンジニアの募集はしていませんが、他にも様々な職種を募集しているので、Wantedlyのページをご覧ください。

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

Catalinaにアップグレード後のbundle installでエラー

エラー内容

$ bundle install
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

(省略)

An error occurred while installing ffi (1.13.0), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.13.0'` succeeds before bundling.

In Gemfile:
  guard-rspec was resolved to 4.7.3, which depends on
    guard was resolved to 2.16.2, which depends on
      listen was resolved to 3.2.1, which depends on
        rb-inotify was resolved to 0.10.1, which depends on
          ffi

試したこと

$ gem install ffi -v '1.13.0'  
Building native extensions. This could take a while...
ERROR:  Error installing ffi:
        ERROR: Failed to build gem native extension.
...

エラーの通りにコマンドを実行するもエラー

$ xcode-select --install                             
xcode-select: error: command line tools are already installed, use "Software Update" to install updates

既にinstallされていた。

解決策

$ sudo xcodebuild -license accept   

を実行したところ解決。

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

(Ruby on Rails6) 投稿内容を編集する機能を作る

まえがき

こちらでは、投稿ページ制作後を前提に"投稿内容を編集"の作業を忘却録として残します。

投稿内容を編集する機能を作る

編集ページの作成

routes・controllers・view の3点に、編集ページを新規作成してください
また、ページ作成には

・コントローラー名→ form
・アクション名→ Edit

で設定してます。

(例)

config/routes
Rails.application.routes.draw do
  get '/' => 'form#index'
  get '/form' => 'form#form'
  post "form/create" => "form#create"
  get "form/:id/edit" => "form#edit" ⇦これ
  get "form/:id" => "form#post"
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
app/views/任意.html.erb
<h1>編集ページ</h1>
app/controllers/任意.controller.rb
class FormController < ApplicationController
  def index
    @forms = Form.all
    @forms = Form.all.order(created_at: :desc)
  end
  def post
    @forms = Form.find_by(id: params[:id])
  end
  def form
    @forms = Form.find_by(id: params[:id])
  end
  def create
    @forms = Form.new(content: params[:content])
    @forms.save 
    @forms = Form.new(title: params[:title])
    @forms.save 
    redirect_to("/")
  end

  def edit ←ここから

  end      ←ここまで
end

routesのpostは、フォームデータの取得を行う時に使います。
新規ページ作成ではgetで設定してください。エラーの原因になってしまいます。

編集ページでの初期値設定(textarea・input)

controllers

app/views/任意.html.erb



def edit
    @forms = Form.find_by(id: params[:id]) ←ここ
end

editアクションに @forms = Form.find_by(id: params[:id]) を設定して、URLと同じidのデータを取得させます。

views

app/views/任意.html.erb
<h1>編集ページ</h1>
<div class="form">
  <div>
    <textarea><%= @forms.content %></textarea>  ←ここ
    <input type="submit" value="保存"> </div>
</div>

<%= @forms.content %> により、入力の初期値を設定しています。

編集内容の保存

コントローラーにアクションの追加

フォームから値を受け取るので、routesでは get ではなく post で実行してください。

config/routes.rb
post "form/:id/new" => "form#new"

リダイレクトの設定

アクションにフォームから値を受け取る準備をしたら、submitを押した後にリダイレクトさせる必要があります。

app/views/任意.controllers.rb
def new
    redirect_to("/")
end

redirect_to("/") を利用してリダイレクト設定を行います。 / は私が任意で設定しているので個人でアレンジしてください。

Viewsで送信先の指定する

先により フォームからの値の取得・リダイレクト を行いました。
ここでは、Viewsで送信先を指定します。

app/views/任意.controllers.erb
<h1>編集ページ</h1>
<%= form_tag("/posts/#{@post.id}/new") do %> ←ここ
<div class="form">
  <div>
    <textarea><%= @forms.title %></textarea> 
    <textarea><%= @forms.content %></textarea> 
    <input type="submit" value="保存"> </div>
</div>
<% end %> ←ここ

↓ 詳細

app/views/任意.html.erb
<%= form_tag("/posts/#{@post.id}/new") do %>
フォームタグ
<% end %>

↑により、送信先が指定されます。

投稿内容の更新を行う

データーベースからデータを取得する必要があります。
取得アクションは、アクション・name属性の追加で行えるので、実行しましょう。

Viewでname属性を指定する

app/views/任意.html.erb
<h1>編集ページ</h1>
<%= form_tag("/posts/#{@post.id}/new") do %>
<div class="form">
  <div>
    <textarea name="content"><%= @forms.content %></textarea>  ←ここ
    <input type="submit" value="保存"> </div>
</div>
<% end %>

コントローラーで params と save で指定

params では、送られてきたデータを保持するオブジェクトを作成できます。

app/controllers/任意.controller.rb
def update
    @forms = Form.find_by(id: params[:id])
    @forms.content = params[:content]
    @forms.save

    redirect_to("/")
  end

あとがき

ここまで読んでいただき、ありがとうございました。
初期値の設定などの処理が理解できるまで、少し時間がかかりそうですが地道に理解を深めたいと思います。
また、ここの紹介だけでは"投稿ページへのリンク"など詳細を省いてしまってるので、自作されるかたは設定することをおすすめします。

参考リンク

Ruby on Rails6 実践ガイド

Myリンク

また、Twitter・Portfolio のリンクがありますので、気になった方は
ぜひ繋がってください。プログラミング学習を共有できるフレンドが出来るととても嬉しいです。

Twitter
Portfolio
Github

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

Railsでログイン機能をシンプルに名前とパスワードだけで実装する(2)

前回 の続き。ここからログイン画面とログイン処理を実装していく

手順

  • Sessionコントローラを作成する
MacBook % rails g controller Sessions new
  • 以下のようにroutesファイルを編集
config/routes
Rails.application.routes.draw do
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
~
  • 次にログインフォームを作っていく。今回はBootstrapなどをつかってない直書き。
app/views/sessions/new.html.erb
<body>
  <h1>Login</h1>
  <%= form_for(:session, url: login_path) do |f| %>
    <div class="input-name">
      <%= f.label :password, '部屋の名前' %>
      <%= f.text_field :name, placeholder: "部屋の名前を入力してください" %>
    </div>
    <div class="form-group">
      <%= f.label :password, 'パスワード' %>
      <%= f.password_field :password, placeholder: 'パスワードを入力してください' %>
      <%= f.submit 'ログイン' %>
    </div>
  <% end %>
</body>
  • フォームをつくり、http://localhost:3000/login にアクセスし確認
  • 確認できたら、次にコントローラのアクションにcreateとdestroyアクションを追加する
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def new
  end

  def create
    room = Room.find_by(name: params[:session][:name])
    if room && room.authenticate(params[:session][:password])
    else
    end
  end

  def destroy
  end
end
  • createアクションのログイン失敗時の処理を書いていく。(bootstrapなど使われていない)
app/controllers/sessions_controller.rb
~
  def create
    room = Room.find_by(name: params[:session][:name])
    if room && room.authenticate(params[:session][:password])
    else
      flash[:alert] = '名前かパスワードが違います'
      redirect_to '/login'
    end
  end
~
  • ブラウザを閉じると自動的に有効期限が切れるsessionhelperの追加
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include SessionsHelper
end
  • ヘルパーメソッド、log_inメソッドを定義する
app/helpers/sessions_helper.rb
module SessionsHelper
  #ブラウザ内の一時cookiesに暗号化済みのroomIDを格納
  def log_in(room)
    session[:room_id] = room.id
  end
end
  • これによってログインできるようになったので、createアクションを定義する
/app/controllers/sessions_controller.rb
~
  def create
    room = Room.find_by(name: params[:session][:name])
    if room && room.authenticate(params[:session][:password])
      log_in room
      redirect_to receptions_path
    else
      flash[:alert] = '名前かパスワードが違います'
      redirect_to '/login'
    end
  end
~
ブラウザの画面からログインができるようになりました。

続き [Railsでログイン機能をシンプルに名前とパスワードだけで実装する(3)]

(https://qiita.com/yongjugithub/items/dbb880f085f99cc9af93)

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

Ruby on Rails チュートリアル 第一章

はじめに

自分がRailsを勉強するときに、簡単にまとめたやつがあったらいいのにと思ったので学習しながら編集しました。
自分の勉強用です。


AWSでのCloud9の設定はとばします。

1.2.2 Railsをインストールする

ターミナル
$ echo "gem: --no-document" >> ~/.gemrc

Rubyドキュメントをインストールしないよう.gemrcファイルを設定する
.gemrcって言うのはgemの設定ファイル
設定は「gemのインストールやアップデートの際にドキュメントファイル生成しない」的な感じ
ドキュメントとは、説明書のこと。
Gemを使うに当たって絶対必要ではなく、サイズも大きくなるし時間もかかるからスルーしますというやつ。

ターミナル
$ gem install rails -v 6.0.3

$ rails -v 

$ ruby -v 

Railsのインストールとバージョン確認をする。

ターミナル
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)

JavaScriptの依存関係を管理するYarnというプログラムをインストールする。

1.3 最初のアプリケーション

ターミナル
$ cd ~/environment
$ rails _6.0.3_ new hello_app 

rais newを実行する(バージョン番号を指定)
並び順は rails バージョン指定 new アプリ名

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

group :development, :test do
  gem 'sqlite3', '1.4.1'
  gem 'byebug',  '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'capybara',           '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  gem 'webdrivers',         '4.1.2'
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Gemfileを書き換える。

ターミナル
$ cd hello_app/
$ bundle install

gemをインストールする。
ここでエラーが出てしまったのでbundle uddateする。

ターミナル
$ bundle uddate

再度bundle installする。

ターミナル
$ bundle install

1.3.2 rails server

config/environments/development.rb
Rails.application.configure do
  .
  .
  .
  # Cloud9 への接続を許可する
  config.hosts.clear
end

Webサーバーへの接続を許可が必要らしいので、config/environments/development.rbを開いて2行足す。

ターミナル
$ cd ~/environment/hello_app/
$ rails server

実行したままにしたいので、もう1つターミナルを開いてRailsサーバーを実行する。
ブラウザを開いて、ヾ(´∀`)ノYay! You’re on Rails!ヾ(´∀`)ノわーい みたいのが出たらOK。

1.3.4 Hello, world!

ここからMVCモデルに入る。

ターミナル
#app/controllers/の中にある〇〇controller.rbと言うファイルは?
$ ls app/controllers/*_controller.rb
#結果
app/controllers/application_controller.rb

ファイルを調べる。まずは第一章ではまずコントローラから。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  #helloアクション
  def hello   
    render html: "hello, world!" #HTMLで表示したい:"表示したい内容"
  end
end

3行追加する。これでこの後のルーティングを設定すると"hello, world!"が表示できるようになるらしい

config/routes.rb
Rails.application.routes.draw do
  root 'application#hello'
end

#root(一番上、/)では'application_controller'のhelloアクション'を実行するという意味。
'application_controller'のhelloアクション'というのは1個前に設定したやつ。

ここでエラーが出てしまった。エラーの内容は読み込み中がすごい長いというもの。
Rails sを再起動しようと思ってRails sを実行しているターミナルを見ると、終了できなくなってしまっていた。

ターミナル
$ ps aux | grep [p]uma

$ kill -9 <出てきたプロセス番号>

これでもう一度Rails sを再起動できた。
ブラウザに、Hello,world!と表示されればOK。

1.4 Gitによるバージョン管理

ターミナル
$ git config user.name #登録されていればここに表示される

$ git config user.email #登録されていればここに表示される

#登録されていなければ下記コマンドで登録
$ git config --global user.name "Your Name"
$ git config --global user.email your.email@example.com

git configで設定。

ターミナル
$ sudo ln -sf `which nano` /usr/bin
$ git config --global alias.co checkout

Gitで使うデフォルトのエディタを設定。
git coをcheckoutのエイリアスに設定。これから'git checkout' を'git co'と省略できる。

ターミナル
$ cd hello_app #アプリのルートディレクトリに移動する
$ git init #リポジトリの初期化
$git add -A #すべての更新ファイルをステージに移動
$git status #ステージングの状況を確認 #現状ではすべての更新ファイルがステージングされて緑で表示

$git commit -m "Initialize repository" #メッセージを追加するcommitのコマンド メッセージ→"リポジトリを初期化"
$ git log #ログ表示 「q」で終了

git initでセットアップする。

1.5.1 Herokuのセットアップ

gemfileを書き換える。

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

group :development, :test do
  gem 'sqlite3', '1.4.1'
  gem 'byebug',  '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'capybara',           '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  gem 'webdrivers',         '4.1.2'
end

group :production do
  gem 'pg', '1.1.4'
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Herokuではsqlite3は使えない。PortgreSQLを使う。
本番(production)環境にpg gemをインストールしてRailsがPostgreSQLと通信できるようにする。
sqlite3 gemが本番環境に導入されないようにしておく。

ターミナル
bundle install --without production

本番用以外のgemをインストールする。

ターミナル
$ heroku --version

Herokuがインストールされているか確認する。されていなかった。

ターミナル
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)

Herokuをインストールする。

ターミナル
$ heroku --version

確認。OK。

ターミナル
$ heroku login --interactive

herokuにログインする。 --interactiveはブラウザを開かないようにするやつ。

ターミナル
$ heroku create

Herokuに新しいアプリケーションを作成する。

ターミナル
$ git push heroku master

HerokuにリポジトリをPUSHする。
heroku createの時に出てきたURLを開くとデプロイ完了が確認できる。

ターミナル
$ rails webpacker:install 

webpackerがないと怒られたとき。

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

【Ruby on Rails】デバック(binding.pry)

目標

binding.pryの使い方をマスターする

開発環境

ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

前提

※ ▶◯◯ を選択すると、説明等が出てきますので、
  よくわからない場合の参考にしていただければと思います。

今回は投稿に対するコメント機能まで実装している例を参考に解説していきます。

流れ

1 デバックとは?
2 gem 'pry-byebug'のインストール
3 controllerで使用
4 viewで使用

デバックとは?

エラーの原因(エラーが出ない場合もあります)を調査して、
理想の動作を実現させるために行うことをデバッグと言います。

アプリケーションがエラーを起こした際には、
基本的にエラーメッセージが出力されるようになっています。
そのエラーメッセージを確認し、デバッグを行い、エラー原因を解決していきます。

今回はその方法の一つでもあるbinding.pryをご紹介します。

gem 'pry-byebug'のインストール

gemfile
gem 'pry-byebug'
ターミナル
$ bundle install

補足【pry-byebug】
記述を加えた場所でプログラムの処理を止め、
その時の状態を確認することができるgemです。

controllerで使用

処理を止めたい場所で下記を記述。

binding.pry

実際の使用例①

ターミナル
  def show
    @post = Post.find(params[:id])
    @comment = Comment.new
    @comments = @post.comments
  end

上記のような記述があった場合、下記のように追加します。

ターミナル
  def show
    binding.pry
    @post = Post.find(params[:id])
    @comment = Comment.new
    @comments = @post.comments
  end

その後railsサーバーを立ち上げてください。
すると記述したページに遷移すると画面がフリーズするはずです。
その状態のまま、ターミナルに戻ってください。すると下記のようになっています。

ターミナル
    31: def show
 => 32:   binding.pry
    33:   @post = Post.find(params[:id])
    34:   @comment = Comment.new
    35:   @comments = @post.comments
    36: end

[1] pry(#<PostsController>)> 

この状態で[1] pry(#)> の後ろにparamsと入力後、Enterを押してみてください。
postのパラメーターとしてid=32が取得出来ていることがわかります。

ターミナル
    31: def show
 => 32:   binding.pry
    33:   @post = Post.find(params[:id])
    34:   @comment = Comment.new
    35:   @comments = @post.comments
    36: end

[1] pry(#<PostsController>)> params
=> <ActionController::Parameters {"controller"=>"posts", "action"=>"show", "id"=>"32"} permitted: false>
[2] pry(#<PostsController>)> 

次にcontroller通りの記述をしてみます。
するとid=32のデータを取得出来ました。

ターミナル
[1] pry(#<PostsController>)> params
=> <ActionController::Parameters {"controller"=>"posts", "action"=>"show", "id"=>"32"} permitted: false>
[2] pry(#<PostsController>)> Post.find(params[:id])
  Post Load (3.4ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 32], ["LIMIT", 1]]
   (pry):2
=> #<Post:0x00007f16b08cfeb0
 id: 32,
 user_id: 2,
 title: "test",
 body: "test",
 created_at: Tue, 08 Sep 2020 06:36:21 UTC +00:00,
 updated_at: Tue, 08 Sep 2020 06:36:21 UTC +00:00,
 post_image_id: nil>
[3] pry(#<PostsController>)> 

続けてこの投稿に対してのコメントを見る場合、下記のように記述します。
するとid=32に紐づくコメント情報を取得できました。

ターミナル
[3] pry(#<PostsController>)> Post.find(params[:id]).comments
  CACHE Post Load (0.0ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 32], ["LIMIT", 1]]
   (pry):3
  Comment Load (2.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 32]]
   app/controllers/posts_controller.rb:32
=> [#<Comment:0x00007f16b0af5398
  id: 9,
  user_id: 2,
  post_id: 32,
  comment: "",
  created_at: Thu, 17 Sep 2020 09:52:01 UTC +00:00,
  updated_at: Thu, 17 Sep 2020 09:52:01 UTC +00:00,
  score: 1>]
[4] pry(#<PostsController>)> 

求めていたパラメーターが返ってきたら成功です。
もし返ってこなければ、記述が間違えているため、見直しが必要です。
※終了する場合は exit と入力後、Enterです。

実際の使用例②

①では一番上にしましたが、今度は一番下にしてみます。

ターミナル
  def show
    @post = Post.find(params[:id])
    @comment = Comment.new
    @comments = @post.comments
    binding.pry
  end

その後railsサーバーを立ち上げてください。
すると記述したページに遷移すると画面がフリーズするはずです。
その状態のまま、ターミナルに戻ってください。すると下記のようになっています。

ターミナル
    31: def show
    32:   @post = Post.find(params[:id])
    33:   @comment = Comment.new     
    34:   @comments = @post.comments
 => 35:   binding.pry
    36: end

そこで@postを記述すると、上記Post.find(params[:id])の記述と同じ結果が返ってきます。

ターミナル
[1] pry(#<PostsController>)> @post
=> #<Post:0x00007fc2b06fc870
 id: 32,
 user_id: 2,
 title: "test",
 body: "test",
 created_at: Tue, 08 Sep 2020 06:36:21 UTC +00:00,
 updated_at: Tue, 08 Sep 2020 06:36:21 UTC +00:00,
 post_image_id: nil>
[2] pry(#<PostsController>)> 

このようにbinding.pryを置く場所によって調べたい項目を変更できるため、
色々試してみるのもおすすめです。
※終了する場合は exit と入力後、Enterです。

実際の使用例③

binding.pryはif文などでも使えます。
[2] pry(#)> if @post.nil? と入力すると
[2] pry(#)* と表示が変わるため、
1行づつif文の中身を記述していきます。

[2] pry(#<PostsController>)> if @post.nil?
[2] pry(#<PostsController>)*   redirect_to new_post_path
[2] pry(#<PostsController>)* else  
[2] pry(#<PostsController>)*   @post
[2] pry(#<PostsController>)* end  
=> #<Post:0x00007fc2b06fc870
 id: 32,
 user_id: 2,
 title: "test",
 body: "test",
 created_at: Tue, 08 Sep 2020 06:36:21 UTC +00:00,
 updated_at: Tue, 08 Sep 2020 06:36:21 UTC +00:00,
 post_image_id: nil>
[3] pry(#<PostsController>)> 

※終了する場合は exit と入力後、Enterです。
※表示が多い場合は途中まで表示されますが、Enterで取得していくことができ、
取得を終了する場合はqを押すことで終了します。

viewで使用

viewで使用する場合は、調べたい箇所で下記のように記述します。

<% binding.pry %>

使い方に関してはcontrollerと同様です。
使うタイミングはeach文の中身を確認するときなどで使います。

まとめ

binding.pryは使い方次第で開発スピードを格段に早めてくれます。
とくに初学者の方にとっては早めに学ぶことができれば、理解もしやすくなるので
早めの学習をおすすめします。

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

小数点以下を切り捨てたい

【概要】

1.結論

2.どのように使うのか

3.ここから学んだこと

1.結論

floorメソッドを使う!


2.どのように使うのか

def cal_points(point)
  if point < 6000
    cal_point = points * 0.05
  else
    cal_point = points * 0.1
  end
  puts "ポイントは#{cal_point.floor}点です"
end

上記ではif条件式の中で小数点が入った計算を#{cal_point.floor}を小数点以下を切り捨てています。
式展開にfloorメソッドを適用しています。

参考URL:
Rubyで数値の切り捨て・切り上げ・四捨五入する

3.ここから学んだこと

他にもroundメソッド(四捨五入),ceilメソッド(切り上げ)があります。また、.round(1)とすることで"小数点第1位まで四捨五入"や.round(-1)とすることで1の位を四捨五入できます。

ex)1.23.round(1) #1.2
ex)123.round(-1) #120


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

【Ruby】セキュアなパスワードポリシー設定のための正規表現

1.はじめに

 Webアプリケーション作成の際に特に意識をしないとならない箇所はセキュリティ面です。パスワードに関しては慎重に取り扱わなければいけません。
 この記事ではパスワードポリシー設定の際に正規表現を使うことが有用という話を聞きましたので、パスワードと正規表現について考察してみました。

2.正規表現とはなにか

 初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」によると

「パターンを指定して、文字列を効率よく検索・置換するためのミニ言語」

ということみたいです。
 実際にRuby用の正規表現エディタのRubularというWebサイトを使用して、正規表現がどういったものか見ていきます。
例えば下記のようなテキストがあります。

8月30日学習時間:1時間
8月31日学習時間:3時間
9月1日学習時間:2時間
9月2日学習時間:4時間
9月3日学習時間:6時間
9月4日学習時間:10時間

この中で9月の学習時間の数字だけ正規表現を使って抽出します。
正規表現を使うと下記のような文字列になります。

  ^9.*:(\d*)時間$

^.*などの正規表現で使用する特別な文字を「メタ文字」と呼びます。
ここでのメタ文字の意味は後述するとしてRubularで検証していきます。
Rubularの「Your test string」に検証したいテキストを、「Your regular expression」に正規表現を入力していきます。
そうすると下記のような結果になります。
スクリーンショット 2020-09-23 10.41.33.png
「Match result」の水色の部分が検索して見つかった部分です。
「Match groups」がキャプチャされた部分です。ここで学習時間を抽出します。
こうやって正規表現を使用して文字列を検索していきます。

3.この記事で使用する正規表現のメタ文字

参考にしました→基本的な正規表現一覧

メタ文字 意味
^ 文頭を表す
$ 文末を表す
[] いずれか1文字を表す文字クラスを作る
- []内で使われる文字の範囲を示す
. 任意の1文字を表す
? 直前の文字やパターンが1回、もしくは0回現れる
* 直前の文字やパターンが0回以上連続する。条件に合う最長の部分に一致
*? 直前の文字やパターンが0回以上連続する。条件に合う最短の部分に一致
+ 直前の文字やパターンが1回以上連続する。条件に合う最長の部分に一致
() 内部でマッチした文字列をキャプチャもしくはグループ化する
{n,m} 直前の文字やパターンがn回以上、m回以下連続する
\ メタ文字をエスケープしたり、\dや\wといった他のメタ文字の一部になったりする
\d 1個の半角数字(0123456789)
\w 半角英数字とアンダースコア'_'
(?=x) xの「直前の位置」を表す(肯定先読み)

 

4.どういったパスワードポリシーを設定すればよいのか

 コンピュータの性能は日進月歩で向上しているため、パスワードの暗号解読(例えば総当り攻撃)のスピードは年々上がっていっています。それに伴いパスワードの複雑化が求められています。
 2020年3月31日発行の内閣サイバーセキュリティーセンター「インターネットの安全・安心ハンドブック Ver.4.10」のP30によると、パスワードの安全性を高めるためのポリシーとして「英大文字+英小文字+数字+記号混じりで10桁以上」を推奨しています。
 ここではハンドブックに則ったパスワードポリシーを正規表現にて作成していきます。

5.パスワードポリシー設定のための正規表現

 ここでは段階的にそれぞれの正規表現を考えていきたいと思います。

 (1) 10桁以上40桁以下の半角英数記号
 (2) 英大文字1桁以上+英小文字1桁以上の条件追加
 (3) 数字1桁以上の条件追加
 (4) 記号1桁以上の条件追加

すべてのパターンを網羅できているわけではありませんが、上から

 ・9桁の文字
 ・10桁の英小文字
 ・10桁の英大文字+英小文字
 ・10桁の英大文字+英小文字+数字
 ・10桁の英大文字+英小文字+数字+記号
 ・41桁の文字

で下記のテキストで検証をしていきます。

Aaaaaaa1?
aaaaaaaaaa
Aaaaaaaaaa
Aaaaaaaaa1
Aaaaaaaa1?
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1?

 (1) 10桁以上40桁以下の半角英数記号

  正規表現で使用する文字についてはASCIIコードを使用して指定ができます。ここでの半角英数字記号はASCIIコード表の!から~までですので、下記のようになります。

Ruby
^[!-~]{10,40}$

予想通りの結果となりました。
スクリーンショット 2020-09-23 23.43.47.png

 (2) 英大文字1桁以上+英小文字1桁以上の条件追加

  理解するのに時間がかかりましたが、段階を踏んで説明します

   a.肯定的先読み(?= )を使用して、条件の順序の依存をなくす
   b.条件の前の文字は、任意の1文字.を最短一致で0回以上連続*?する
   c.条件を設定する[A-Z][a-z]

  参考にしました→パスワード向け正規表現 /^(?=.?[a-z])(?=.?\d)[a-z\d]{8,100}$/i を解読する

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])[!-~]{10,40}$

成功しました。

スクリーンショット 2020-09-24 0.26.35.png

 (3) 数字1桁以上の条件追加

  (2)に数字0-9の条件\dを追加しました。

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?\d)[!-~]{10,40}$

成功しました。

スクリーンショット 2020-09-24 0.31.15.png

 (4) 記号1桁以上の条件追加

  ASCIIコードを確認して記号を指定しました。
  記号とメタ文字がかぶる箇所については、エスケープ\をしました。

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?\d)(?=.*?[!-\/:-@\[-`{-~])[!-~]{10,40}$

成功しました。
記号確認用にテキストを追加しました。

スクリーンショット 2020-09-24 0.49.20.png

6.まとめ

 正規表現は理解するのに骨が折れる部分がありました。パスワードポリシー設定以外にも色々使わせていただきます。

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