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

今日の学習

やったこと

・atom のエディターをオートセーブに設定。

・メソッドについて

学んだこと

・atom のエディターをオートセーブに設定やり方

→atom →Preference→Packagesで「autosave」を検索、[Settings]でEnabledにチェックを入れる。

・メソッドについて

・メソッド

メソッドはユーザーが独自に定義するいことができる関数のこと。メソッドを定義することにより、共通した処理をひとまとめにし、メソッドを呼び戻すことで何度でも使うことができる。
Rubyのメソッドは始まりにdefを指定して、終わりをendを記述し、その中に実行する処理を記述する。

def メソッド名
実行する処理
end

メソッドの使い方

ex say と言うメソッドを定義する場合

def say
puts "YOLO" #You Only Live Once(人生は一度きり)
end

say

実行結果→YOLO

またメソッドにあらかじめ引数を設定することができる。

ex goodbyeという引数付きのメソッドを定義する場合。

def goodbye(name)
puts "Goodbye #{name}"
end

goodbye("World!")

実行結果→Goodbye World!

文字列の中で、メソッドで渡した値を使用したい時、#{}で変数を囲む必要がある。
今回はnameが引数のため#{name}となる。

メソッドの呼び出し

通常メソッドというのは全てクラスに定義されている。
→(あるクラスを継承した)オブジェクト.メソッド名(第1引数、第2引数、…)){ブロック}
例えば、rubyのなかの文字列オブジェクトのクラスはstringであるため、stringクラスのメソッドを使うことができる。

1 name = "Samurai" #文字列オブジェクトnameを定義
2 
3 "Samurai".class #クラスを確認→Stringクラスだった

5  p name #=> "Samurai" #中身が侍であることを確認

7  name.replace("Bushi") #Stringクラスからreplaceメソッドを呼び出す(文字列SamuraiをBushiに入れ替える)

9  p name #=> "Bushi" #ちゃんと入れ替わった
10

この場合、継承したオブジェクトはnameでメソッドはreplaceである。
そしてreplaceの第一引数はBushiである。

トップレベルにメソッドが定義されてた場合はオブジェクトの省略をしてメソッドを呼び出すことができる。
先ほどの
→def goodbye(name)
puts "Goodbye #{name}"
end
goodbye("World!")

また、{ブロック}を渡す場合は例えば配列の場合だと
→a = [1,2,3]
a.each{|content| p content}

実行結果
→[実行結果]
1 1
2 2 
3 3

{|content| p content}の部分がブロックを渡している部分。

補足、追加

変数の使い方https://www.sejuku.net/blog/12879
defを使ってメソッドを定義するhttps://www.sejuku.net/blog/19275
クラスメソッドの使いかたhttps://www.sejuku.net/blog/19208

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

MaterializecssのCarouselを使用して、不特定多数の@変数sをeachで表現する

概要

TECH::EXPERTのカリキュラムでオリジナルのミニアプリを作成する機会があり、
その一部のページでMaterializecssのCarouselを使用し、users_controller.rb
で定義した@usersをそれぞれ"carousel-item"で表現したので紹介します。

MaterializecssのCarouselとは

画像をくるくると回せる機能です。
Image from Gyazo
https://materializecss.com/carousel.html

自分が作成したページ紹介

Image from Gyazo
画像が自動で切り替わる方法は
こちらを参照していただけば幸甚です(ついでにイイね:yum:)

作成する前提

MaterializecssがCDNで読み込めている

編集するファイル

・コントローラーファイル
・ビューファイル

コントローラーファイル

user_contoroller.rb
    def show
     user=User.find(params[:id])
       @users=User.where(training: user.training)
    end

上の例では、表示しているuserページのuserのtrainingと同じtrainingを持っている人を
@usersで定義してます。

ビューファイル

show.html.erb
 <div class="card">
    <h6>この人と同じトレーニングが好きな人</h6>
      <div class="carousel #fafafa grey lighten-5" id="recommend_user" >
        <% users.each do |user| %>
          <a class="carousel-item" href= "/users/<%= user.id %>" data-id ="<%=user.id %>" >
            <%=user.nickname%>
            <img src="">
            <%= image_tag  image(user),class:'circle' %>
          </a>
        <%end%>
      </div>
 </div>

eachメソッドで"carousel-item"ごと表現しています。
名前とプロフ画像を描画してます。

最後に

この記事を書いた目的

・自分なりに工夫した点をアウトプットして、理解を深める。
・あわよくば有識者にフィードバックをもらいたい。
・私と同じ初学者からも奇譚のない意見をもらいたい。(自分だったらどうこうする的な)

筆者について

TECH::EXPERTにて4月27日より52期夜間・休日コースでruby/railsを学習している未経験エンジニアです。
ご不備等ありましたら、ご指摘ください。ちなみに本記事が初投稿になります。
言わずもがなかもしれませんが、趣味はボディメイク・筋トレでございます。
余談ですが、120kg⇨66kgまで減量して大会出場した経験があり
ダイエットについての質問はなんでも答えられるかと思います:muscle:

ひとこと

最後までご覧いただきまして、ありがとうございました。
もし気に入っていただけたら、イイね・ストック・フォローご自由に!

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

[Ruby] DelegateClass を使って僕だけの Logger を実装する

標準出力に出力するための Logger オブジェクトを作りたい :raised_hands_tone1:

require 'logger'

logger = Logger.new(STDOUT)
logger.formatter = Logger::Formatter.new
logger.datetime_format = '%Y/%m/%d %H:%M:%S '

logger.info('BONFIRE LIT')
logger.error('YOU DIED')
I, [2019-07-09T21:40:12.663123 #18929]  INFO -- : BONFIRE LIT
E, [2019-07-09T21:40:13.036144 #18929] ERROR -- : YOU DIED

このような形で毎回 logger オブジェクトを作るのは大変だから、クラス化しよう :smirk_cat:

require 'forwardable'
require 'logger'

class StdoutLogger
  extend Forwardable

  def_delegators :logger, :debug, :info, :warn, :error, :fatal

  private

  def logger
    @logger ||=
      Logger.new(STDOUT).tap do |logger|
        logger.formatter = Logger::Formatter.new
        logger.datetime_format = '%Y/%m/%d %H:%M:%S '
      end
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

以前はこのように「Logger オブジェクトである logger を所有し、infoerror などのメソッドを logger に委譲する」というクラスを実装していました。しかし、この方法では委譲したいメソッドが増えた場合、例えば Logger#unknown も委譲したい場合などに def_delegators に追記しなくてはいけません :weary:

そこで便利なのが Kernel#DelegateClass です。

require 'delegate'
require 'logger'

class StdoutLogger < DelegateClass(Logger)
  def initialize
    super(logger)
  end

  private

  def logger
    @logger ||=
      Logger.new(STDOUT).tap do |logger|
        logger.formatter = Logger::Formatter.new
        logger.datetime_format = '%Y/%m/%d %H:%M:%S '
      end
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

DelegateClass(Logger) を継承することで、Logger のインスタンスメソッドはすべて logger に委譲されます。

最後に

DelegateClass は非常に便利で、例えば Array のラッパークラスを定義したい場合にも使えます。これについては以前書いた

という記事を参照してください。

Logger 関連でよろしければこちらの記事もご覧ください。

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

[Ruby] DelegateClass を使って僕だけの Logger を実装する (追記あり)

標準出力に出力するための Logger オブジェクトを作りたい :raised_hands_tone1:

require 'logger'

logger = Logger.new(STDOUT)
logger.formatter = Logger::Formatter.new
logger.datetime_format = '%Y/%m/%d %H:%M:%S '

logger.info('BONFIRE LIT')
logger.error('YOU DIED')
I, [2019-07-09T21:40:12.663123 #18929]  INFO -- : BONFIRE LIT
E, [2019-07-09T21:40:13.036144 #18929] ERROR -- : YOU DIED

このような形で毎回 logger オブジェクトを作るのは大変だから、クラス化しよう :smirk_cat:

require 'forwardable'
require 'logger'

class StdoutLogger
  extend Forwardable

  def_delegators :logger, :debug, :info, :warn, :error, :fatal

  private

  def logger
    @logger ||=
      Logger.new(STDOUT).tap do |logger|
        logger.formatter = Logger::Formatter.new
        logger.datetime_format = '%Y/%m/%d %H:%M:%S '
      end
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

以前はこのように「Logger オブジェクトである logger を所有し、infoerror などのメソッドを logger に委譲する」というクラスを実装していました。しかし、この方法では委譲したいメソッドが増えた場合、例えば Logger#unknown も委譲したい場合などに def_delegators に追記しなくてはいけません :weary:

そこで便利なのが Kernel#DelegateClass です。

require 'delegate'
require 'logger'

class StdoutLogger < DelegateClass(Logger)
  def initialize
    super(logger)
  end

  private

  def logger
    @logger ||=
      Logger.new(STDOUT).tap do |logger|
        logger.formatter = Logger::Formatter.new
        logger.datetime_format = '%Y/%m/%d %H:%M:%S '
      end
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

DelegateClass(Logger) を継承することで、Logger のインスタンスメソッドはすべて logger に委譲されます。

ちなみに Logger クラスを継承する方法では

最後に

DelegateClass は非常に便利で、例えば Array のラッパークラスを定義したい場合にも使えます。これについては以前書いた

という記事を参照してください。

Logger 関連でよろしければこちらの記事もご覧ください。

追記: 通常の継承の方がいいような……

この場合 Logger クラスを継承する方法でもいいような……。

require 'logger'

class StdoutLogger < Logger
  def initialize
    super(STDOUT, formatter: Formatter.new, datetime_format: '%Y/%m/%d %H:%M:%S ')
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

違いのひとつとして、Logger クラスのサブクラスかどうかが異なる。

# DelegateClass(Logger) を継承する場合
logger.is_a?(Logger)
#=> false

# Logger を継承する場合
logger.is_a?(Logger)
#=> true

この場合、むしろ Logger オブジェクトであると見なしたほうが適切かもしれない。

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

あなたはRailsチュートリアルで一体なにを学んだというのか【3章】

  • 注意:プログラミング歴29日の初心者が書いています

  • 注意:間違っていたら優しく教えてください(喜びます)

「Ruby on Rails チュートリアル実例を使ってRailsを学ぼう」
https://railstutorial.jp/

素晴らしいチュートリアルに感謝します。

3.2 静的ページ

トピックブランチを切る

$ git checkout -b static-pages: 変更を行う時には、masterではなく、ブランチを作成して作業します。(ブランチを作成することを「切る」と言います)

  • 作成したブランチ上で、こまめにコミットしながら作業を進めることができます。でも、本流のmasterにはなんの影響も及ぼしませんので思う存分コミットできます。

  • また、複数人がそれぞれブランチを用意して同時進行で作業することも可能です。

3.2.1 静的なページの生成

コントローラの生成(ルーティングの追加とviewも同時生成)

$ generate controller StaticPages home help: StaticPagesコントローラを生成する(そのなかには、homehelpというページへのアクションも含む)

同時に「routes.rb」ファイルには、StaticPagesコントローラ内のhomeアクションとhelpアクションで使うルーティングが自動で書き込まれます。

これによって、今後rootURLの末尾に、/static_pages/home/static_pages/helpと付け加えることで、StaticPagesコントローラ内のhomeアクションとhelpアクションにアクセスが可能です。

class StaticPagesController < ApplicationController
  def home
  end

  def help
  end
end

StaticPagesコントローラ内では、homeアクションとhelpアクションが自動で定義されています。そして、それらによって対応するviewが呼び出されます。

では、viewディレクトリを見てみると、こちらにも自動でhome.html.erbhelp.html.erbが生成されています。これがブラウザに表示されるHTMLの一部になっています。

コントローラの中のクラスとメソッドについて

class StaticPagesController < ApplicationController
  def home
  end

  def help
  end
end

StaticPagesコントローラ内は以下の構成になっています。

  • StaticPagesControllerクラスの定義

  • クラスメソッド(アクション)の定義: def~end部分

Rubyにおいて、メソッドとは関数のことです。
ここでは、「home」「help」という名前のついた関数が定義されていますが、中身は空っぽです。

通常、このように中身の空っぽのメソッドは何もしません。

では、なぜこれらのメソッドは対応するURLリクエストに反応して適切なviewを返してくれるのでしょうか?

それは、StaticPagesControllerクラスがApplicationコントローラクラスを継承しているからです。
そして、ApplocationコントローラクラスはActionController::Baseクラスを継承しています。
ActionController::Baseクラスが、rails特有のコントローラとしての動きを定義しています。

今回あたらしく作成したStaticPagesControllerクラスと、その中の空っぽのメソッドが機能しているのは、ActionController::Baseクラスのコントローラ機能を継承しているからです。

3.3 テストから始める

テスト駆動開発(TDD)については、以下の動画をみると一通りやるべきこととその動機がわかります。
「50分でわかるテスト駆動開発」
https://channel9.msdn.com/Events/de-code/2017/DO03

test "should get home" do
  get static_pages_home_url
  assert_response :success
end

言葉で表すと
「Homeページのテスト。
GETリクエストをhomeアクションに対して発行 (=送信) せよ。
そうすれば、リクエストに対するレスポンスは[成功]になるはず」
となります。

3.4 少しだけ動的なページ

テストから先に書いて、テストが通る実装を書いていく

assert_select "title", "Home | Ruby on Rails Tutorial Sample App"

assert_selectメソッドは、特定のHTMLタグが存在するかどうかをテストします。

assert_select "title", "タイトル" # titleタグの文字列が、「タイトル」と一致すれば成功。

テストに合わせて、それぞれのviewファイルを修正していきます。
1ページ修正するごとにテストを行うと、ちゃんと「failures」が減っていくことがわかります。

テストコードをリファクタリングする

"Ruby on Rails Tutorial Sample App" というタイトルの一部がなんども繰り返されています。
同じことをなんども繰り返すのは美しくありませんので、変数に格納してから使う形にリファクタリングします。

以下はtest/controllers/static_pages_controller_test.rbの一部です。

  def setup
    @base_title = "Ruby on Rails Tutorial Sample App"
  end

ここではまず、"Ruby on Rails Tutorial Sample App"を@base_titleというインスタンス変数に格納しています。
「インスタンス変数」とは、同じクラス内で使える変数のことで、頭に@がついています。
ここではStaticPagesControllerTestクラスで使用可能なインスタンス変数として、@base_titleを定義しました。

assert_select "title", "Home | Ruby on Rails Tutorial Sample App"

つづいて上記の"Ruby on Rails Tutorial Sample App"を@base_titleという変数に置き換えていきます。

assert_select "title", "Home | #{@base_title}"

${@base_title}という形で、変数が${}の中に入っています。
これは、"文字列"の中で変数を使う時のルールです。
これを「文字列の式展開」といいます。

もしも変数@base_titleがもし${}の中に入っていなかったら、"@base_title"という、ただの文字列として処理されてしまいます。
"Ruby on Rails Tutorial Sample App"として@base_titleを使う場合は、${}の中に入れてあげる必要があるということです。

変更が終わったら、コンソールからテストを走らせて、エラーがないことをチェックします。

3.4.3 レイアウトと埋め込みRuby (Refactor)

Rubyの文法をHTML内で使うときには、<% %>でRubyコードを囲みます。

provide(:title, "Home")

ここでは、:titleというシンボルに、文字列"Home"を紐づけて、擬似的なハッシュのようなものを作っています。
ただしprovideメソッドで宣言したこのシンボルと文字列の擬似ハッシュは、このHomeページ内でしか使うことはできません。

<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>

その後、yield(:title)文字列"Home"を呼び出しています。
Rubyの文法をHTML内で使い、かつ結果を出力するときには、<%= %>でRubyコードを囲みます。

今回は呼び出した文字列をHTML内に出力したいので、<%= %>でRubyコードを囲んでいます。

レイアウトファイルのリファクタリング

レイアウトファイルは、全てのページで共通する

や部分を担うviewです。

共通部分をレイアウトファイルに統一することによって、個別のviewファイルの記述を減らし、シンプルに保つことができます。

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

f.selectで生成されるoptionタグを改変する

やりたいこと

"f.select"で生成されるセレクトボックスの中身(optionタグ)をいじりたい
初期値を設定したい

概要

= f.select :name, options_for_select(セレクトボックスで表示したい要素配列.map{|c|[セレクトボックスで表示される値, value属性値にセットしたい値, {追加属性記述}]}, 初期値のvalueを選択), {}, {class: 'hoge', id: 'hogehoge'}

value属性値にセットしたい値の記述をなくすと
"value = セレクトボックスで表示される値"
になります

具体例

#セレクトボックスの選択肢にしたい要素配列
@sample_array_1 = ['aaa', 'bbb', 'ccc']
@sample_array_2 = [{id: 1, name: "aaa"}, {id: 2, name: "bbb"}, {id: 3, name: "ccc"}]
#初期値要素(データベースから読み取ったと仮定)
@selected_sample_1 = 'bbb'
@selected_sample_2 = {id: 2, name: "bbb"}

#基本形1
= f.select :sample, @sample_array_1, {}, {class: 'hoge', id: 'hogehoge'}
#基本形2
= f.select :sample, options_for_select(@sample_array_2.map{|c|[c[:name], c[:id]]}), {}, {class: 'hoge', id: 'hogehoge'}
#属性追加1
= f.select :sample, options_for_select(@sample_array_1.map{|c|[c, {'data-sample'=>c}]}), {}, {class: 'hoge', id: 'hogehoge'}
#属性追加2
= f.select :sample, options_for_select(@sample_array.map{|c|[c[:name], c[:id], {'data-sample'=>c[:id]}]}), {}, {class: 'hoge', id: 'hogehoge'}
#初期値設定1
= f.select :sample, options_for_select(@sample_array_1.map{|c|[c, {'data-sample'=>c}]}, @selected_sample_1), {}, {class: 'hoge', id: 'hogehoge'}
#初期値設定2
= f.select :sample, options_for_select(@sample_array.map{|c|[c[:name], c[:id], {'data-sample'=>c[:id]}]}, @selected_sample_2.id), {}, {class: 'hoge', id: 'hogehoge'}

基本形1

= f.select :sample, @sample_array_1, {}, {class: 'hoge', id: 'hogehoge'}
#基本形1
<select class="hoge" id="hogehoge" name="sample">
  <option value="aaa">aaa</option>
  <option value="bbb">bbb</option>
  <option value="ccc">ccc</option>
</select>

基本形2

= f.select :sample, options_for_select(@sample_array_2.map{|c|[c[:name], c[:id]]}), {}, {class: 'hoge', id: 'hogehoge'}
#基本形2
<select class="hoge" id="hogehoge" name="sample">
  <option value="1">aaa</option>
  <option value="2">bbb</option>
  <option value="3">ccc</option>
</select>

属性追加1

= f.select :sample, options_for_select(@sample_array_1.map{|c|[c, {'data-sample'=>c}]}), {}, {class: 'hoge', id: 'hogehoge'}
#属性追加1
<select class="hoge" id="hogehoge" name="sample">
  <option data-sample="aaa" value="aaa">aaa</option>
  <option data-sample="bbb" value="bbb">bbb</option>
  <option data-sample="ccc" value="ccc">ccc</option>
</select>

属性追加2

= f.select :sample, options_for_select(@sample_array.map{|c|[c[:name], c[:id], {'data-sample'=>c[:id]}]}), {}, {class: 'hoge', id: 'hogehoge'}
#属性追加2
<select class="hoge" id="hogehoge" name="sample">
  <option data-sample="1" value="1">aaa</option>
  <option data-sample="1" value="1">bbb</option>
  <option data-sample="1" value="1">ccc</option>
</select>

初期値設定1

= f.select :sample, options_for_select(@sample_array_1.map{|c|[c, {'data-sample'=>c}]}, @selected_sample_1), {}, {class: 'hoge', id: 'hogehoge'}
#初期値設定1
<select class="hoge" id="hogehoge" name="sample">
  <option data-sample="aaa" value="aaa">aaa</option>
  <option selected="selected" data-sample="bbb" value="bbb">bbb</option>
  <option data-sample="ccc" value="ccc">ccc</option>
</select>

初期値設定2

= f.select :sample, options_for_select(@sample_array.map{|c|[c[:name], c[:id], {'data-sample'=>c[:id]}]}, @selected_sample_2.id), {}, {class: 'hoge', id: 'hogehoge'}
#初期値設定2
<select class="hoge" id="hogehoge" name="sample">
  <option data-sample="1" value="1">aaa</option>
  <option selected="selected" data-sample="1" value="1">bbb</option>
  <option data-sample="1" value="1">ccc</option>
</select>

参考

https://stackoverflow.com/questions/18100834/how-to-make-the-f-select-rails-selected
https://stackoverflow.com/questions/5052889/ruby-on-rails-f-select-options-with-custom-attributes

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

Railsにおける新規アプリ作成手順まとめ

はじめに

この記事のRailsのバージョンは5.2.2.1です。
自分の備忘録としてメモします。

今回は「memo-app」というアプリを作成する想定で書きます。

具体的な手順

新規アプリ作成コマンド

terminal
$ rails new アプリケーション名
  # アプリケーションを新規作成

  $ rails new アプリケーション名 -オプション名
  # オプションを付けてアプリケーションを作成

この新規ファイル全体の事を
Railsアプリという。

新規アプリ作成コマンドの具体例

terminal
$ cd ~/projects
  # projectsディレクトリに移動
  $ rails _5.2.2.1_ new memo-app -d mysql
  # 「memoapp」を「mysql」オプションで作成。バージョンを5.2.2.1で作成。

  $ cd memo-app
  # 「memo-app」ディレクトリに移動

  $ pwd
  # 現在のディレクトリのパスを表示

続いてデータベースの作成を行う。

データベース作成コマンド

terminal
$ rake db:create
#database.ymlというファイルに基づいて作成される

続いてルーティングの設定を行います(ルーティングに関するファイルはrails newコマンドですでに作成されている)

ルーティング設定の具体例

routes.rb
Rails.application.routes.draw do
    get 'memo' => 'memos#index'
  end

続いてコントローラーの作成を行います。

コントローラー作成コマンド

terminal
$ rails g controller コントローラ名
# コントローラを作成

コントローラー作成コマンドの具体例

terminal
$ rails g controller memos
# memosコントローラを作成

コントローラーファイル設定

memos_controller.rb
class MemosController < ApplicationController

    def index
    end

  end

#indexアクションを定義

後はビューファイルの作成を行います。
今回はindex.html.erbというファイル名で作成します。
この際拡張子がerbになっている事に注意。(rubyを埋め込めるHTMLだと思っておけばOK)

モデルの作成

terminal
$ rails g model memo
  #  Memoモデルを作成→モデルクラス名(全て小文字)

この後にマイグレーションファイルで作成するカラムの設定などを行う。

マイグレーションファイルの作成

terminal
$ rake db:migrate
# マイグレーションファイルの実行

DB上にmemosテーブルが作成される。

あとはrails sコマンドでサーバーを立ち上げればアプリとしての最低限のモノが。
各人カスタマイズして良いアプリを創っていきましょう。

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

RubyMine / Docker for Windows: docker-compose上で作ったrbenvのRuby環境をうまくロードできないトラブル 2019-07版

問題

  • Windows 10 Pro
  • RubyMine 2019.1
  • docker-compose (Docker for Windows)

にて、Dockerコンテナ上でrbenvを使って作ったRuby環境を、RubyMineがうまくロードしてくれない。

参考: Can't configure Remote Ruby Interpreter for docker-compose: "Unable to read RBConfig from specified interpreter" : RUBY-24360

解決方法を簡潔に

RubyMine 2019.2以降をインストールする。

ただし現状(2019-07-09)では Early Access Program (EAP) の段階なので、EAPのRubyMineを入れる。

※ 正式に2019.2以降がリリースされたときは、そちらをインストールしてください!(要ライセンス購入 or 試用)

前提

  • ホスト
    • Windows 10 Pro 10.0.18362
    • Docker for Windows Community 2.0.0.3
      • docker-composeを使用
    • RubyMine 2019.1 2019.2
      • これをバージョンアップする
  • コンテナ
    • Dockerイメージ: centos:7
      • docker-compose経由でDockerイメージを作成

Dockerfile

FROM centos:7

ENV APP_PATH /app
WORKDIR ${APP_PATH}

ENV ruby_ver="2.6.3"

ENV RBENV_ROOT="/usr/local/rbenv" 
ENV RBENV_SH="/etc/profile.d/rbenv.sh"
ENV CONFIGURE_OPTS="--disable-install-doc"
ENV PATH="${RBENV_ROOT}/bin:${PATH}"

# 日本語表示を正しくする (lessとRuby向け)
ENV LESSCHARSET="utf-8"
ENV LANG="en_US.UTF-8"

# yum (EPEL): ビルドに必要なライブラリをインストール
RUN set -x && \
    yum -y update && \
    yum -y install epel-release && \
    yum -y install \
        autoconf \
        bzip2 \
        curl \
        gcc-c++ \
        git \
        glibc-headers \
        libyaml-devel \
        make \
        openssl-devel \
        readline \
        readline-devel \
        sqlite-devel \
        zlib \
        zlib-devel \
    && \
    yum -y install less which && \
    yum clean all

# Ruby (rbenv) のインストール
RUN set -x && \
    git clone https://github.com/sstephenson/rbenv.git /usr/local/rbenv && \
    git clone https://github.com/sstephenson/ruby-build.git /usr/local/rbenv/plugins/ruby-build && \
    echo 'eval "$(rbenv init --no-rehash -)"'  > ${RBENV_SH} && \
    source ${RBENV_SH} && \
    rbenv install ${ruby_ver} && \
    rbenv global ${ruby_ver}

# Ruby: Bundlerで必要なGemをインストール
COPY Gemfile ${APP_PATH}
COPY Gemfile.lock ${APP_PATH}
RUN set -x && \
    source /etc/profile.d/rbenv.sh && \
    rbenv exec gem install bundler && \
    bundle install

# その他、Rubyと関係ないインストールをここに書いている

VOLUME [ ${APP_PATH} ]

やったこと

  1. Early Access Program (EAP) からRubyMine 2019.2をダウンロードしインストールする
  2. Dockerfile中で which コマンドをインストールする:
    • yum -y install which
    • RubyMineがRubyのパスを検索するときにwhichを使う?
  3. RubyMineでDockerを設定する
    • ポップアップが出るので、指示に従えばOK
  4. RubyMineのSettings
    • Languages & Frameworks -> Ruby SDK and Gems
    • 「+」→「New Remote...」をクリックする→ダイアログが表示される
  5. Configure Remote Ruby Interpreter ダイアログ
    • 「Docker Compose」を選択
    • Server: 既に設定したDockerの項目を選ぶ
    • Service: docker-compose.ymlに書いたサービス名
    • Ruby or version manager path (次に説明)

Ruby or version manager path

この項目は重要です。

動いた例

/usr/local/rbenv/bin/rbenv
/usr/local/rbenv/versions/2.6.3/bin/ruby
  • フルパス
  • 2.6.3実際にインストールしたRubyのバージョン番号
  • ユーザのホームディレクトリにインストールした場合は /home/foo/.rbenv/versions/2.6.3/bin/ruby になるはずです。(試してませんが)

うまく動かなかった例

ruby
rbenv
/usr/local/rbenv/bin/rbenv
/usr/local/rbenv/shims/ruby

参考


これでOKをクリックして、Gemの一覧が出てくれば成功です!
以上です。

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

【備忘録】DockerでRuby 2.6.3, Rails 5.2.3, MySQL 8.0.16の環境を構築し,Herokuにデプロイするまで

Herokuにデプロイするまでにかなり時間がかかってしまったので,備忘録として。初心者なので,ミスや無駄があればコメントいただけるとありがたいです。

開発環境

  • macOS Mojave 10.14.5

公式ドキュメント

  1. Docker Documentation

  2. Docker ドキュメント日本語化プロジェクト

  3. Heroku Dev Center

参考

  1. 丁寧すぎるDocker-composeによるrails + MySQL on Dockerの環境構築

  2. DockerでRails5.2.1とMySQL8を動かすgitプロジェクトを作る方法

  3. Railsアプリでherokuを使うときのDBをMySQLに変更する

手順

1. インストール・新規登録

  • 以下は,Homebrew, git, Docker desktopなどのインストール, GitHubの新規登録, Herokuの新規登録・クレジットカード登録などの初期作業を行っている前提とします

2. 作業ディレクトリ・Dockerファイルの作成

  • 適当に作業ディレクトリを作成
    ターミナルでの作業は必ずこのディレクトリ内で行うこと

  • ファイル「Dockerfile」「docker-compose.yml」「Gemfile」「Gemfile.lock」を作成し,テキストエディタなどで以下を入力
    (作業が面倒なら下記の【補足】を参照)

Dockerfile
FROM ruby:2.6.3
ENV LANG C.UTF-8
RUN apt-get update -qq && \
    apt-get install -y build-essential libpq-dev nodejs && \
    rm -rf /var/lib/apt/lists/*
RUN gem install bundler
WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install
ENV APP_HOME /my_app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
COPY . $APP_HOME
docker-compose.yml
version: '3.2'
services:
  db:
    image: mysql:8.0.16
    command: mysqld --default-authentication-plugin=mysql_native_password
    volumes:
      - ./db/mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: root
    ports:
      - "3306:3306"
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/my_app
    ports:
      - "3000:3000"
    links:
      - db
    tty: true
    stdin_open: true
Gemfile
source 'https://rubygems.org'
gem 'rails'
Gemfile.lock
※空ファイルでOK

【補足】
上記ファイル作成作業が面倒なら,次のシェルスクリプトを作成して,ターミナルで実行してもよい。(コマンドはbash docker1.sh

docker1.sh
#!/bin/bash

# make Dockerfile
cat <<'EOF' > Dockerfile
FROM ruby:2.6.3
ENV LANG C.UTF-8
RUN apt-get update -qq && \
    apt-get install -y build-essential libpq-dev nodejs && \
    rm -rf /var/lib/apt/lists/*
RUN gem install bundler
WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install
ENV APP_HOME /my_app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
COPY . $APP_HOME
EOF

# make docker-compose.yml
cat <<'EOF' > docker-compose.yml
version: '3.2'
services:
  db:
    image: mysql:8.0.16
    command: mysqld --default-authentication-plugin=mysql_native_password
    volumes:
      - ./db/mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: root
    ports:
      - "3306:3306"
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/my_app
    ports:
      - "3000:3000"
    links:
      - db
    tty: true
    stdin_open: true
EOF

# make Gemfile
cat <<'EOF' > Gemfile
source 'https://rubygems.org'
gem 'rails'
EOF

# make Gemfile.lock
touch Gemfile.lock

3. Dockerイメージの作成

ターミナルで実行
docker pull ruby:2.6.3
docker pull mysql:8.0.16
docker-compose run web rails new . --force --database=mysql --skip-bundle
docker-compose build

※「$」を付けるとコピペの邪魔になるので,以下も省きます

4. 「database.yml」を書き換える

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch("MYSQL_USERNAME", "root") %>
  password: <%= ENV.fetch("MYSQL_PASSWORD", "root") %>
  host: <%= ENV.fetch("MYSQL_HOST", "db") %>

development:
  <<: *default
  database: my_app_development

test:
  <<: *default
  database: my_app_test

production:
  <<: *default
  database: my_app_production

5. Dockerコンテナの作成・開始

ターミナルで実行
docker-compose up

6. データベースの作成

  • 「Command + N」などで新しいターミナルを開き,作業ディレクトリまで移動
ターミナルで実行
docker-compose exec web rails db:create
  • この作業で「NoDatabaseError」が解消され,ブラウザからhttp://localhost:3000にアクセスすると,「Yay! You’re on Rails!」が見られる。

7. gitの準備

ターミナルで実行
git init
git add .
git commit -m ”first commit”

8. Herokuの準備

  • ブラウザからHerokuにログインしておく
ターミナルで実行
heroku login
  • 適当なキーを押して,ログインボタンを押す
ターミナルで実行
heroku create <アプリ名>
  • 補足

    • この作業で「Heroku」にアプリが作成されると同時に,「heroku」というgitのリモートリポジトリが作成される。
    • <アプリ名>は自由。<>をターミナルに入力しないこと。
    • すでに使用されているアプリ名は使用できない。
    • アプリ名に「アルファベット大文字」は使えない。
    • アプリ名を入力しなくてもよい(その場合はランダムに決定される)
  • データベースの設定をMySQLに変更する

ターミナルで実行
heroku addons:add cleardb
heroku config | grep CLEARDB_DATABASE_URL
  • 表示されたmysql://〜〜〜の部分を取り出し,「mysql」を「mysql2」に変更
  • 次のDATABASE_URL=の後にmysql2://〜〜〜を貼り付けたものをターミナルで実行
ターミナルで実行
heroku config:set DATABASE_URL=

【補足】
ブラウザからアプリを選択し,「Setting」の「Reveal Config Vars」のページから編集してもよい。
「CLEARDB_DATABASE_URL」の
「CLEARDB_DATABASE_URL」を「DATABASE_URL」
「mysql」を「mysql2」に変更したものを追加すればよい。

9. Herokuにデプロイ

ターミナルで実行
git push heroku master
heroku run rake db:migrate
heroku open

ここまでで最低限度の作業は終了です。お疲れ様でした。

10. おまけ

  • 終了するときは,ブラウザを閉じるだけでなく,HerokuのログアウトとDockerコンテナの停止をしておくこと
ターミナルで実行
heroku logout
docker-compose down
  • コマンドの変更点
dockerを使用しないときのコマンドの例
rails console
rails g controller home top
rails g model Post content:text
rake db:migrate 
docker-composeコマンドの例
docker-compose exec web rails console
docker-compose exec web rails g controller home top
docker-compose exec web rails g model Post content:text
docker-compose exec web rake db:migrate 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyでmysqlのデータベースに接続する方法

Rubyだけでmysqlに接続する

今までRailsの力に頼ってきたのでrubyだけでmysqlに実行する方法を知らなかった

まずシステムにgemを入れる

gem install mysql2
require 'mysql2' //requireする

client = Mysql2::Client.new(host: "******", username: "******", password: '******', database: '******')

result = client.query("select call_duration from talk_histories") //SQLここに書く

result.each do |r|
 puts r["call_duration"]
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】配列に入ったテーブルのデータを、特定のカラムを指定して並び替える

やりたいこと

通常テーブル内のデータの並び替えをする際は
モデル名.order('カラム名 desc')等で簡単に実行できます。
しかし今回はテーブル内全体のデータを対象とせずに、あらかじめ特定の条件に合ったレコードのみ配列に入れてあることを前提とします。
例えば以下のような感じ。

users = [
  #<User:0x000056357bd5c9b8>
  id: 2
  name: "たろう",
  age: 16,
  copuntry: "Japan",
  #<User:0x000056357bd5c710>
  id: 6
  name: "はなこ",
  age: 17,
  copuntry: "US",

  ・
  ・
  ・
]

この配列に対して並び替えを行いたいが、orderメソッドはそもそもモデルを対象にしか使用できません。
今回は配列にデータを入れてしまっているので、配列のデータの並び替え、しかも特定のカラムの値を対象に並び替えたいです。

試行錯誤の履歴(必要ない方は飛ばしてください)

配列の並び替えだからsort_byメソッドだろうと予測はつきました。
https://ref.xaio.jp/ruby/classes/array/sort_by_bang

●sort_byメソッドの使い方

こちらのRubyリファレンスをそのまま抜粋すると、使用方法は以下のような感じ。

test.rb
animals = ["mouse", "cat", "hippopotamus", "giraffe"]
p animals.sort_by {|anim| anim.size }

#出力結果 ==> ["cat", "mouse", "giraffe", "hippopotamus"]

配列animalsの要素を、字数順に並べています。
この配列の場合は、配列の要素が単純なので問題はないんですが、
今回は配列の要素(1つのレコード)のさらに中の、カラムの値を指定して並び替える必要があるので、少し躓いてしまいました。

●そもそも配列に保存されたデータ形式はどうなっているのか

そもそも配列にしたデータは、カラムを指定して特定の要素を呼び出せるのかが不安でした。
配列にしちゃってる時点で、nameやage等のカラム名をカラムとして認識してくれるのかわかりませんでした。

しかし、改めてよく配列を見てみると#<User:0x000056357bd5c9b8>とある。。
これは、"ちゃんとテーブルのデータとして一つ一つ保存している"に違いない!ということで
試しにコンソールで以下を実施してみました。

users[0][:name] 

# 配列usersの1番目のデータを取得し、さらにそのnameカラムを指定してみる
# 出力結果==> "たろう"

ちゃんと拾えました!
データを配列に入れた際、ただの文字列として保存されるのではなく、テーブルのデータとして認識して保存されてるんですね。
多分超基本なんでしょうが、私は今回でやっと認識できました。。
基本すぎてすみません。。配列に関しての理解がまだまだでした。

結果

以上のように色々と試行錯誤した結果、とてもシンプルに実行することができました!
例えばこの記事の一番上の配列usersに対して、ageカラムの値で並び替えしたい場合は、

test.rb
users.sort_by(&:age)

これだけです!
こうすると、ageの値が小さい順にデータを並び替えることができます。
sort_byメソッドの引数にageを指定するだけでした。
簡単すぎます。

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

あなたはRailsチュートリアルで一体なにを学んだというのか【2章】

  • 注意:プログラミング歴29日の初心者が書いています

  • 注意:間違っていたら優しく教えてください(喜びます)

「Ruby on Rails チュートリアル実例を使ってRailsを学ぼう」
https://railstutorial.jp/

素晴らしいチュートリアルに感謝します。

2.1 アプリケーションの計画

アプリケーションを作成してbitbucketにpush

rails new : 新しいアプリケーションの作成
bitbucketで新しいリポジトリを作成(1章とは別のアプリを一からつくるから別のリポジトリ)
git remote add origin git@bitbucket.org:<username>/toy_app.git:新たに作成したファイル群をbitbucketのgit対象に指定
git push -u origin --all: bitbucketにプッシュ(アップロード)

herokuにログインできないとき

ターミナルでheroku CLIにログイン
$ heroku login --interactiveパスワード入力

それでも
heroku: command not foundとエラーがでるときは、

source <(curl -sL https://cdn.learnenough.com/heroku_install)でIDEにherokuをインストールしなおし。

2.1.1 ユーザーのモデル設計

今、ツイッターみたいなアプリを作ろうとしています。
そのためのデータベースを作っていきます。

ユーザーのモデル設計とは、その名の通り投稿を行うユーザーのデータベースです。
一人一人のユーザーは、

  • ID

  • name

  • email

の情報を持っています。

2.1.2 マイクロポストのモデル設計

マイクロポストは、ツイッターでいうところのツイートです。
1つ1つのマイクロポストは、

  • ID

  • Content (投稿内容)

  • user_id (誰が投稿したポストかわかるように)

の情報を持っています。

2.2 Usersリソース

データの集まりで、さらにWebで扱うことができる状態になっているのが、リソースです。

例えば、さっきのUsersモデル(ユーザーのデータベース)はそのままではただのデータの集まりですが、webサービスにおいては、新規追加したり、情報を編集したりするわけです。

「webから情報を扱う」というのは、HTTPで「新規登録しろ」とか「このユーザーのデータは削除だ!」とかリクエストできるということです。

そうゆうことができる状態のデータの集まりが、リソースです。

Railsのscaffold

Railsのscaffoldが、リソースを作る作業を全部やってくれます。
今回はそれに頼りましょう。

2.2.1 ユーザーページを探検する

URLの末尾に/usesをつけることで、user一覧ページにアクセスすることができます。
そのほかにも/new/editなど、色々なことができるようになっています。
これは、「scaffold」コマンドが全て自動で設定してくれたからです。

2.2.2 MVCの挙動

ルーティングってなんですか

ルーティングはURLとアクション(実行する内容)を紐づけている部分です。

Rails.application.routes.draw do
  resources :users
  root 'application#hello'
end

root 'application#hello'は、『ルート(トップページ)は、appricationコントローラのhelloメソッドを使って表示してね』と言っています。

さらに注意深くroutes.rbファイルの中を見ると、resources :usersというのが新たに追加されています。
これは「resourcesメソッド」です。

まず、『「users」というURLに対しては、「usersコントローラを参照してね」』ということを言っています。

また、railsの特徴として、リソースに対するアクションはすでに7つ定義されています。

アクション名 役割
index リソースの一覧を表示させる
show リソースの詳細を表示させる
new 投稿フォームを表示させる
create リソースを追加させる
edit 更新フォームを表示させる
update リソースを更新させる
destroy リソースを削除する

resourcesメソッドは、これら7つのアクションを自動的にUsersリソースに割り当てます。

データベースの操作内容まで、全てRailsが作ってくれているのです。

このように、最初から7つのアクションが定義してあって、それぞれのアクションの名前や、URLの末尾に付け足す内容までがルールとして決めてあります。
あなただけでなく、みんなが同じルールに則って開発すると、誰が見てもわかりやすくて管理しやすくなります。
ちなみに、今回使っているルールは「REST」と呼ばれています。詳しくはググるといっぱい出てきます。

2.3.2 マイクロポストをマイクロにする

バリデーションとは

データベースにデータを保存する前に、そのデータが本当に保存していいのかどうか検証する仕組みをバリデーションといいます。

class Micropost < ApplicationRecord
  validates :content, length: { maximum: 140 }
end

app/models/micropost.rb内のコードです。
ここでは、

  • マイクロポストモデルの「content」の「length」がマックス140文字まで

というバリデーションを設けています。

2.3.3 ユーザーはたくさんマイクロポストを持っている

has_meny / belongs_toとは

has_menyは、モデルとモデルを関連づけるために使います。
今回のチュートリアルの場合、「Usersモデル」と「Micropostsモデル」を関連づけるために使います。

class User < ApplicationRecord
  has_many :microposts
end

Userモデル(app/models/user.rb)内に「has_many :microposts」と記述することで、モデル同士が関連します。
ただし、マイクロポストがユーザーを持っているわけではないことに注意してください。
あくまでも、個々のユーザーは1人です。
1人のユーザーがたくさん投稿することがあっても、1つの投稿がたくさんのユーザーに紐づいているわけではないからです。

そのため、マイクロポストモデル内に、has_many :usersと記述するのは不適です。

そのかわり、マイクロポストモデル内には

class Micropost < ApplicationRecord
  belongs_to :user
  validates :content, length: { maximum: 140 }
end

belongs_to :userと記述します。
これで、マイクロポストはユーザーに所有されているという関係になりました。

マイクロポストモデルにはuser_idという属性を設定してあったので、自動的にuserモデルのIDと対応します。

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

[React.js]Rails on Rails上でモダンなフロント環境構築

はじめに

React公式チュートリアルに挑戦するべく、Railsサーバ上でReactを動作させる環境を構築していこうと思います!

Rails5.1からwebpackerやnpm,yarnなどに対応したため、それらを利用してReactを使えるようにします。

目次

  • 1. yarnをインストールする
  • 2. Railsプロジェクトを作成する
  • 3. Gemfileの編集
  • 4. webpack,reactをインストールする
  • 5. 動作確認

前提環境

  • cloud9 (amazon linux)
  • Ruby 2.6
  • Rails 5.1
  • node 10.16

実践

1. yarnをインストールする

初めにyarnをインストールしておきます。

$ npm install -g yarn

2. Railsプロジェクトを作成する

おなじみrails newしていきます。
ついでにディレクトリも作成したプロジェクトに変更しておきます。

$ rails new webpack_test --skip-turbolinks && cd webpack_test

turbolinksはJavascriptにおいて不具合の原因になりかねないので使用しません。

3. Gemfileの編集

webpacker gemをインストールするため、作成したプロジェクトフォルダ内のGemfileを編集します

~省略~

gem "webpacker"

Gemfileを保存してからbundle installしてください。

4. webpack,reactをインストールする

webpackとreactをそれぞれインストールしていきます。

$ rails webpacker:install
$ rails webpacker:install:react

そして最後に
HTMLヘッダーを以下のように編集します
javascript_include_tagjavascript_pack_tag

javascriptを呼び出すときwebpackerのヘルパータグで<%=javascript_pack_tag'FILE_NAME'%>と記述します。

app/views/layouts/application.html.erb
<%= javascript_pack_tag 'application' %>

5. 実際に動作させてみよう!

正常にインストールできていれば
app/javascript/hello_react.jsxが作成されていると思います。
reactはjsx内で動作しているので、jsxファイルを呼び出すことでreactを動かします。

試しに、hello_react.jsxを表示させていこうと思います。

まずcontrollerとviewを作成しrouteを割り当てます。

rails g controller react_app index
config/routes.rb
Rails.application.routes.draw do
  get 'react_app/index'
end

作成したviewでjsxファイルを呼び出します。

view/react_app/index
<%=javascript_pack_tag 'hello_react'%>

rails サーバを起動して、正常に表示されるか確認します。

rails s

react_app/indexへ接続しHello React!と表示されていればReactの導入は完了です。

キャプチャ.PNG

6. Reactチュートリアルへの準備

公式Reactチュートリアル

まず、チュートリアル用に新しくjsxファイルを作っていきましょう。
app/javascript/pack/tutorial.jsxを作成しておきます。

tutorial.jsx内にreactをimportします。

tutorial.jsx
import React from "react"
import ReactDOM from "react-dom"
//ここから下の行にチュートリアルの内容を記述していく

実際にチュートリアルのスターターコードをtutorial.jsxに記述して動作を確かめてみます。

tutorial.jsx
import React from "react"
import ReactDOM from "react-dom"

class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {/* TODO */}
      </button>
    );
  }
}

class Board extends React.Component {
  renderSquare(i) {
    return <Square />;
  }

  render() {
    const status = 'Next player: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

class Game extends React.Component {
  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

rails sを実行し、react_app/indexにアクセスしてみます。

この画面になっていればreactは正常に動作しています。
キャプチャ.PNG

このままでは、デザインがおかしいのでスターターコードのcssを適応していきます。

app/assets/stylesheets/react_app.scss
body {
  font: 14px "Century Gothic", Futura, sans-serif;
  margin: 20px;
}

ol, ul {
  padding-left: 30px;
}

.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}

もう一度rails sを実行しreact_app/indexに接続すると
このような画面になっていればチュートリアルの準備は完了です。
キャプチャ.PNG

あとがき

記事を書く経験が初めてでしたが、思ったより大変でした...。
分かりにくい箇所、至らない箇所があれば修正します。

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

Rails6 のちょい足しな新機能を試す49(consumes? 編)

はじめに

Rails 6 に追加されそうな新機能を試す第48段。 今回は、 consumes? 編です。
Rails 6 では、 ActiveSupport::Multibyte::Chars.consumes? を使うと DEPRECATION WARNING が表示されるようになります。(私は自分で使ったこともないし、使われているところを見たことも無いのですが...)

Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は gem install rails --prerelease でインストールできます。

$ rails --version
Rails 6.0.0.rc1

簡単なスクリプトを作る

今回は、動作確認用の簡単なスクリプトを書いて確認します。

bin/consumes.rb
#!/usr/bin/env ruby
require 'active_support/core_ext'

puts '--- DEPRECATED WARNING ---'
p ActiveSupport::Multibyte::Chars.consumes?('あ')

puts '--- NO DEPRECATED WARNING ---'
p 'あ'.is_utf8?

Rails 6 では

実行すると DEPRECATION WARNING が表示されます。

$ bin/duration.rb
--- DEPRECATED WARNING ---
DEPRECATION WARNING: ActiveSupport::Multibyte::Chars.consumes? is deprecated and will be removed from Rails 6.1. Use string.is_utf8? instead. (called from <main> at bin/consumes:5)
true
--- NO DEPRECATED WARNING ---
true

試したソース

試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try049_deprecated_consumes

参考情報

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

method_sourceってgemを読んだら知恵が詰まってた。

method_source is 何?

minimum sample

require 'method_source'

def my_important_method
  # do nothing, just sleep
  sleep 4 
  'successfuly done with my hard work!!'
end

method = method(:my_important_method)
method.source.display

すると。。。

def my_important_method
  # do nothing, just sleep
  sleep 4 
  'successfuly done with my hard work!!'
end

メソッドの定義がプリントされる。

どんな実装か

Method objectからは定義もとのfile, lineが取れる。
そのファイルを全部読み込み、line以下を全て取ってくる

lines = File.readlines(file)
lines = lines[(line - 1)..-1]

linesをイテレートして、methodの定義が終わるところを判定する。

lines.each do |v|
  code << v
  return code if complete_expression?(block ? block.call(code) : code)
end

これでmethod定義が抜き出せたので、あとは表示するだけ

知恵

rescueされるerrorを判別するだけのmodule

def hoge
  rescue MyModule
end

module MyModule
  def self.===(error)
    some_condition(error)
  end
end

evalを使ってstrの構文をチェックだけして、BEGIN throwで実行させずに戻る

catch(:valid) do
  eval("BEGIN{throw :valid}\n#{str}")
end

どうゆう仕組みか?
strが構文としておかしいとSyntaxErrorが出て throwされない。
strが構文としてokだったらそのstr実行前に BEGIN で登録された throwが走る。

所感

まず BEGIN とか知らなかった。
この3行でこれだけのこと詰め込めるrubyはスマートで好き。

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

Ruby で [-1, -2, -3].map(&:succ >> :to_s) という感じで書きたい

[-1, -2, -3].map(&:succ >> :to_s) という関数型を意識した記述

& を使いブロック渡しで任意のオブジェクトを渡す仕組みがあります。
カラクリについては https://qiita.com/hamajyotan/items/f2a96bbb1ccc60a06053 に記述。

[-1, -2, -3].map(&:succ)  #=> [0, -1, -2]

上記記述に対して、
Proc#>> と同じ要領で succ を実施した結果に更に to_s を実施できたら嬉しいですね。
けど、できません。。 :sweat_drops:
この記事はこれをできるようにするための実装の話です。

[-1, -2, -3].map(&:succ >> :to_s)  #=> NoMethodError: undefined method `>>' for :succ:Symbol

Proc#>> および Proc#<<

Ruby 2.6 から、 Proc#>> および Proc#<< が追加されました。いわゆる関数合成と言われる仕組みです。

f1 = -> x { x.succ }
f2 = -> x { x.to_s }

f3 = f1 >> f2
f3.call(-3) # f1 の実行結果を f2 の引数として渡す。 f2.call(f1.call(-3))
#=> "-2"

f4 = f1 << f2
f4.call(-3) # >> とは逆順に合成される。 f1.call(f2.call(-3))
#=> "-4"

Symbol#>> と Symbol#<< があったら嬉しい

自分自身を to_proc により Proc オブジェクトに変換、そこに更に >> で合成すればよさそうです。

# >> および << メソッドを生やす
module SymbolComposeToLeftAndRight
  def >>(g)
    to_proc >> g
  end

  def <<(g)
    to_proc << g
  end
end
Symbol.prepend(SymbolComposeToLeftAndRight)

# どうだ!?。。。残念
[-1, -2, -3].map(&:succ >> :to_s)  #=> NoMethodError: undefined method `call' for :to_s:Symbol

どうやら Proc#>> の引数に対して call を要求しているようです。

Proc の >> および << の引数側も to_proc するようになれば嬉しい

Proc#>> および Proc#<< の引数は Proc オブジェクトであることが期待されています。
もう少し正確に言うと、 call に反応するオブジェクトであることが期待されています。

class Callable
  def call(x)
    x + 5
  end
end

proc1 = proc { |x| x + 1 }
proc2 = proc1 << Callable.new

# 引数に 1加算され、更にその結果に 5加算。
proc2.call(3)  #=> 9

ここが拡張されて、引数自体に to_proc がかかってから既存の処理となるようにならないでしょうか?
言い換えると、 .call を期待するオブジェクト改め、 .to_proc.call を期待するオブジェクトと言っても良いです。

module ProcComposeToLeftAndRightWithToProc
  def >>(g)
    super(g.to_proc)  #=> 引数オブジェクトを to_proc してから既存の処理
  end

  def <<(g)
    super(g.to_proc)
  end
end
Proc.prepend(ProcComposeToLeftAndRightWithToProc)

これで上記エラーも回避できるはず。

実装した (まとめ)

module ProcComposeToLeftAndRightWithToProc
  def >>(g)
    super(g.to_proc)
  end

  def <<(g)
    super(g.to_proc)
  end
end
Proc.prepend(ProcComposeToLeftAndRightWithToProc)

module SymbolComposeToLeftAndRight
  def >>(g)
    to_proc >> g
  end

  def <<(g)
    to_proc << g
  end
end
Symbol.prepend(SymbolComposeToLeftAndRight)

# succ してから to_s
[-1, -2, -3].map(&:succ >> :to_s)  #=> ["0", "-1", "-2"]

# succ してから to_s, さらに length
[-1, -2, -3].map(&:succ >> :to_s >> :length)  #=> [1, 2, 2]

# 逆側の合成。 to_s してから succ
[-1, -2, -3].map(&:succ << :to_s)  #=> ["-2", "-3", "-4"]

関数型っぽい!
既存 2.6 とも互換はあるし、 Ruby の自体がこうなってたら嬉しい人は少なくないんじゃないかと思うけどどうでしょう?

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

ruby snmpでbulkwalkの習作

Rubyでbulkwalkするスクリプトを組んでみた。

ネットワーク内に存在する大量装置にsnmpbulkwalkするスクリプトを従来Perlで作ってたんだけど、rubyで作り直してみた。
取得回数が限られているbulkwalkであればruby-snmpのExamplesにあったんだけど、同一ツリー内を再帰取得するコードが見当たらなかったので書いておきます。

  • v1の装置があることも考慮し、v1であればwalk、v2cであればget_bulkします。
  • 応答結果は「oid値」「応答値」「タイプ」を配列で回答します
  • ホスト単位にMIB値を連続取得します。ホスト単位にパラレルにしてもいいかも。
snmpbulkwalk.rb
#! /usr/local/bin/ruby
require 'snmp'

class SNMPWALK
    @@array = Array.new
    def self.bulk(hostname:,community:'public',oid:,max_rep:10)
        SNMP::Manager.open(:Host => hostname , :Community => community) do |manager|
            next_oid = oid
            while next_oid.subtree_of?(oid) do
                response = manager.get_bulk(0,max_rep.to_i,next_oid)
                response.each_varbind do |r|
                    next_oid = r.name
                    break if !next_oid.subtree_of?(oid)
                    @@array.push([r.name.to_a.join('.'),r.value.to_s,r.value.asn1_type])
                end
            end
        end
        return @@array
    end

    def self.walk(hostname:,community:'public',oid:)
        SNMP::Manager.open(:Host => hostname , :Community => community) do |manager|
            next_oid = oid
            manager.walk(next_oid) do |response|
                response.each do |r|
                    @@array.push([r.name.to_a.join('.'),r.value.to_s,r.value.asn1_type])
                end
            end
        end
        return @@array
    end
end

#####################################################################################
##  get_mibdata Main
#####################################################################################
['host_a','host_b','host_c'].each do |host|
    ['1.3.6.1.2.1.2.2.1.1','1.3.6.1.2.1.2.2.1.7'].each do |mib|
        oid = SNMP::ObjectId.new(oid) if !oid.kind_of?(SNMP::ObjectId)
        result = ""
        if mib['snmp_version'] == 'v1'
            result = SNMPWALK.walk(hostname:host,community:'public',oid:mib)
        else
            result = SNMPWALK.bulk(hostname:host,community:'public',oid:mib)
        end
        p result
    end
end

自作コードをQiitaに書いたの初めてなので突っ込み歓迎です。

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

ruby snmpでbulkwalk

Rubyでbulkwalkするスクリプトを組んでみた。

ネットワーク内に存在する大量装置にsnmpbulkwalkするスクリプトを従来Perlで作ってたんだけど、rubyで作り直してみた。
取得回数が限られているbulkwalkであればruby-snmpのExamplesにあったんだけど、同一ツリー内を再帰取得するコードが見当たらなかったので書いておきます。

  • v1の装置があることも考慮し、v1であればwalk、v2cであればget_bulkします。
  • 応答結果は「oid値」「応答値」「タイプ」を配列で回答します
  • ホスト単位にMIB値を連続取得します。ホスト単位にパラレルにしてもいいかも。
snmpbulkwalk.rb
#! /usr/local/bin/ruby
require 'snmp'

class SNMPWALK
    @@array = Array.new
    def self.bulk(hostname:,community:'public',oid:,max_rep:10)
        SNMP::Manager.open(:Host => hostname , :Community => community) do |manager|
            next_oid = oid
            while next_oid.subtree_of?(oid) do
                response = manager.get_bulk(0,max_rep.to_i,next_oid)
                response.each_varbind do |r|
                    next_oid = r.name
                    break if !next_oid.subtree_of?(oid)
                    @@array.push([r.name.to_a.join('.'),r.value.to_s,r.value.asn1_type])
                end
            end
        end
        return @@array
    end

    def self.walk(hostname:,community:'public',oid:)
        SNMP::Manager.open(:Host => hostname , :Community => community) do |manager|
            next_oid = oid
            manager.walk(next_oid) do |response|
                response.each do |r|
                    @@array.push([r.name.to_a.join('.'),r.value.to_s,r.value.asn1_type])
                end
            end
        end
        return @@array
    end
end

#####################################################################################
##  get_mibdata Main
#####################################################################################
['host_a','host_b','host_c'].each do |host|
    snmp_version = "v2c"
    ['1.3.6.1.2.1.2.2.1.1','1.3.6.1.2.1.2.2.1.7'].each do |mib|
        oid = SNMP::ObjectId.new(oid) if !oid.kind_of?(SNMP::ObjectId)
        result = ""
        if snmp_version == 'v1'
            result = SNMPWALK.walk(hostname:host,community:'public',oid:mib)
        else
            result = SNMPWALK.bulk(hostname:host,community:'public',oid:mib)
        end
        p result
    end
end

自作コードをQiitaに書いたの初めてなので突っ込み歓迎です。

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

kaminariのgemを用いたページネーションの実装方法

ページネーションとは、長い文章を複数のページに分割して、各ページへのリンクを並べアクセスしやすくすること。
https://gyazo.com/7acc560088bf6fb2c04b90dda3b3d38e

実装手順

1.kaminariのGemをインストールしてサーバーを立ち上げ直す

Gemfileの最後の行にgemを記述する。

gem 'kaminari'

Gemをインストールする。

$ bundle install

rails s(サーバー)を立ち上げ直す。

rails s

2.コントローラファイルを編集する

kaminariのgemをインストールすることで使えるメソッド

1.pageメソッド

params[:page]

2.perメソッド
1ページあたりに表示する件数を指定するメソッドである。

per(1ページあたりに表示する件数)

3.「page」と「per」メソッドを利用して、コントローラに記述すると以下のようになる。

def index
    @tweets = Tweet.includes(:user).page(params[:page]).per(5).order("created_at DESC")
end

3.ビューファイルを編集する

paginateメソッド
ページネーションのリンクを表示したいときに使用するメソッドである。

paginate(インスタンス変数)

ビューファイルにはルビータグで囲って使用する。

<div class="contents row" >
    <% @tweets.each do |tweet| %>
      <div class="content_post" style="background-image: url(<%= tweet.image %>);">
        <%= simple_format(tweet.text) %>
        <span class="name"><%= tweet.name %></span>
      </div>
    <% end %>
  <%= paginate(@tweets) %>
</div>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpecのfeatureテストでsessionを扱う方法

環境

  • macOS 10.14.5
  • ruby 2.5.5
  • rails 5.2.3

はじめに

RailsチュートリアルのテストをRSpecで書いていた時にあることに気づきました。

featureテストではsessionが使えない!?

Railsチュートリアル8章のテストでis_logged_in?というユーザーがログイン中かどうかを論理値で返すテスト用ヘルパーを紹介していました。

test/test_helper.rb
# テストユーザーがログイン中の場合にtrueを返す
  def is_logged_in?
    !session[:user_id].nil?
  end

なので同様にspec/support/test_helper.rbに記述してfeatureテストでヘルパーを使用した。
(ヘルパーを定義する方法はこちらを見るとよいと思います
マクロ(ヘルパーメソッド)を定義してフィーチャースペックのユーザー切替えを楽に行う
)

spec/support/test_helper.rb
module TestHelper
  def is_logged_in?
    !session[:user_id].nil?
  end
end
spec/features/users_signup_spec.rb
require 'rails_helper'

RSpec.feature "UsersSignups", type: :feature do

  feature "valid signup information" do
    before do
      visit signup_path
      fill_in "Name", with: "Example User"
      fill_in "Email", with: "user@example.com"
      fill_in "Password", with: "password"
      fill_in "Confirmation", with: "password"
    end

    scenario "add users count" do
      expect {
        click_button "Create my account"
        expect(page).to have_current_path user_path(User.last)
      }.to change(User, :count).by(1)
    end

    scenario "show flash message" do
      click_button "Create my account"
      expect(page).to have_content "Welcome to the Sample App!"
      visit current_path
      expect(page).to_not have_content "Welcome to the Sample App!"
    end

    scenario "login created user" do
      click_button "Create my account"
      expect(is_logged_in?).to be_truthy  #### ここで使用 ####
    end
  end

  feature "invalid signup information" do
    before do
      visit signup_path
      fill_in "Name", with: ""
      fill_in "Email", with: "user@invalid"
      fill_in "Password", with: "foo"
      fill_in "Confirmation", with: "bar"
    end

    scenario "no difference users count" do
      expect {
        click_button "Create my account"
        expect(page).to have_current_path signup_path
      }.to_not change(User, :count)
    end

    scenario "is show error messages" do
      click_button "Create my account"
      expect(page).to have_content "Name can't be blank"
      expect(page).to have_content "Email is invalid"
      expect(page).to have_content "Password is too short"
      expect(page).to have_content "Password confirmation doesn't match Password"
    end
  end
end

するとこんなエラーが、、、

1) UsersSignups valid signup information login created user
     Failure/Error: !session[:user_id].nil?

     NameError:
       undefined local variable or method `session' for #<RSpec::ExampleGroups::UsersSignups::ValidSignupInformation:0x00007fe38a4a3e20>
     # ./spec/support/login_macros.rb:3:in `is_logged_in?'
     # ./spec/features/users_signup_spec.rb:30:in `block (3 levels) in <top (required)>'
     # -e:1:in `<main>'

解決策

どうやら調べてみるとfeatureテスト内ではsesssionが扱えないようで、コントローラーテスト内では使えるみたい。
でもどうにかfeatureテスト内でこのヘルパーを使えるようにする方法はないかと調べていたところある記事を見つけました。
Rails feature testing with logged in user (undefined method 'session')

'rack_session_access'というgemを使うとfeatureテスト内でもsessionを参照したり、変更したりできるみたいなのでgemのREADMEを参考にセットアップしました。

Gemfile
gem 'rack_session_access' # 追加
$ bundle install   # gemをインストール
config/environments/test.rb
Rails.application.configure do
  :
  :
  config.middleware.use RackSessionAccess::Middleware  # 追加
end
spec/spec_helper.rb
require "rack_session_access/capybara"   # 追加

このgemを使うとsessionに値を挿入することもできるらしいが今回は値が入っているかどうかを調べたいだけなので以下のメソッドを記述してRailsチュートリアルに習って論理値を反転。

spec/support/test_helper.rb
module TestHelper
  def is_logged_in?
    !page.get_rack_session_key('user_id').nil?   # 変更
  end
end

はじめはget_rack_session_key(:user_id)と修正したがダメだったのでREADEMEを見ると、文字列('user_id')を渡さなきゃいけないらしい

まとめ

'rack_session_access'gemを使うとfeatureスペックでもsessionを扱える!

しかし頻繁にsessionの操作をfeatureスペック内で行わないようなら、ログインしているかどうかをテストする方法は他にもいくらでもあるのでgemをわざわざ入れる必要はないかも。。

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

[考えてみた]セッション変数から素直?に値を取り出したい

nilか?明示的に調べる方法

こんな感じの実装を見かけることがあります。
session[:hoge] == nil だったときに備えているわけです。

if session[:hoge]
  fuga = session[:hoge][:fuga]
  piyo = session[:hoge][:piyo]
end

しかし、ifを読むと、ビジネスロジック的になにか意味があるのかな?と思えてしまうので、プログラムの都合のifはなるべく減らして、なるべくビジネスロジックだけに集中した書き方をしたいです。

nilか?を暗黙のうちに済ます方法

rubyにはぼっち演算子(&.)というレシーバがnilのときはnilを返してくれる演算子があります。素直か?というと少し微妙かもしれませんが、

Hash#digを使うことで、ifを減らしてロジックな素直に書くことができそうです。

fuga = session.dig(:hoge,:fuga)
piyo = session.dig(:hoge,:piyo)

(追記)
..と思いましたが、with_indifferent_accessを途中に入れないと、nilが帰ってきてしまう。

fuga = session.with_indifferent_access.dig(:hoge,:fuga)
piyo = session.with_indifferent_access.dig(:hoge,:piyo)

with_indifferent_access はHashのサブクラスなので、to_hでもいいというか、そもそもセッションはHashのサブクラスなのだが、、、ちょっと要調査です。

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

ある社内SEのリストーラ?とRuby on Railsを学ぶための本ご紹介

はじめに

こんばんは。
大手SIer → ブラック → ブラック → ヤクザブラック → グレーだけど潰れそう → リストーラ?
という経歴を渡り歩いているとある社内SEのお話です。

それは唐突に

定時後、社長から呼び出しがかかりました。
まあ会社が今大変うんぬん。。。
赤字うんぬん。。。

で一通り話したあと、うーんと考えて

社長曰く、「今は転職活動やってる?」

いや、え?

あれ、これリストラ的なやつ?
とりあえず素直にそういうことは今はやっていないけど、常に危機意識はあります。
という模範解答をとまどいつつも出す。

からなぜかスルーされて
いつも通り、経理のおじいちゃんの愚痴が始まって、
お金の動き分からないうんぬん。。。
給料払い過ぎかなうんぬん。。。
リストラとかも考えなうんぬん。。。

あ、いまリストーラ言ったよね

レベル23くらいで覚えそうなやつ。
とりあえず、適当に相槌打って素直に聞いてみた。
「僕は早めに出て行ったほうがいいってことですかね?」

以下、社長のお言葉
いや、そんなこと言ってるわけじゃないよ~!!(てへぺろ)
残ってほしいし、頑張ってほしいって思ってるよ~!!
でもほら転職しそうだったら、引継ぎとか考えないかんし~!!(もじもじ)
給料に見合った結果をね~。ボーナスは契約上仕方なく出すけど。(いやいや)
僕のサラリーマン時代はね~。ボーナスなんてなかったし。苦しいサラリーマン時代だったし。(いらいら)
会社赤字だけど、僕(社長)の給料は下げないよー。でも社員は実績に応じて!きりっ!

あ、引継ぎ考えてるのね。今目の前に僕いますけど。

入社時約束した通り、ボーナスは出るみたいです。ただ、コスト的に割高と判断されているようで、
まあ、野球選手の高年棒?ベテラン選手みたいな扱いをまさに受けています。
色々周りの話を聞いてみると、過去から今の私のポジションを3人ほど潰しているようです。
詳細書くとあれなんで、省略しますが。とりあえず僕、4人目。
ちなみにこのポジションにつくのも過去から数えて4人目。
あ、打率10割のやつやん。。。・・・

ということで、かなり長くなったので続きはまた後日書くとして、本題の

Rubyを学ぶ人、Ruby On Railsを学ぶ人へ本のご紹介

プロを目指す人のためのRuby入門
Railsやるなら、上の本は必ず読んだほうがいい。仕様が簡潔にまとまっているし、読みやすい。
すぐにRailsでアプリを作り始めるより、まずは上記の本でRubyの仕様を理解すると、
詰まった時に大いに役立つ。いわゆるコピペで動かない問題への対応。
仕様を理解しておかないと、コード読めなくてなんで詰まったかすら分からないから。
難しいところやめんどくさいところは飛ばしてOK。私はテスト周りや正規表現、最後らへんの章は飛ばしました。
他の言語から入ってきたので、ほんとによくできてる言語だなと感心しました。
とくに型の排除、式の結果自体が返り値になる(if文書くとよく分かります)、
メソッドチェーンって言うんだっけ?コロンでつなげるやつ、ループ周り
強力なORマッパー的なやつSQL書かずにコードだけでDBのトランザクション発行できる。(これはRailsか。)

で、次に
改訂4版 基礎 Ruby on Rails (IMPRESS KISO SERIES)
立ち読みして、サンプルが分かりやすく書かれてたので買い。これが良かった。
動くアプリを真似ながら習得するのが一番早いので、私は必要な部分を真似して習得しました。
Google先生とこの本が5体5って感じ。
あ、ちなみにRailsアプリはscaffoldが推奨されているのをよく見ますが、お勧めしません。
だって意味が分からないまま、動いてしまうからです。
ルートも1つ1つ書いたほうがよいと思います。汚くてもいいから自分の書き方で書く。動けばいいの精神で。
ここでまっとうなやり方にこだわっていたら、私は挫折していたと思います。
あとでまっとうなやり方を学んでソースコードをリファクタリングすればよいのです。自分の書き方で書いていれば、
リファクタリングもほんとにスムーズにいきます。びっくりするくらい。
また、ルートをしっかり書こうとすると、RESTFULな考えを自然に覚えさせられます。それが分かれば、scaffold
使ってもいいかなと。だってscafflodってRESTFULな感じでCRUD操作を簡単に実装してくれるやつだし。

あとはRails 〇〇 でぐぐって、ひたすらソースコードを真似る。

です。これでWebサービスを1つ作ることができました。
(検証・本番環境の構築も激しく詰まったけど、また後日書きます)

蛇足ですが、本のお勧め読み方

ある程度分からなくてもいい、適当でもいいから、まず斜め読みで全部読む。
それから手を動かしながら本のソースコードを書くといい感じ。
この前東大生が言ってた。本を読んで最初から100%理解できるわけないと。
まず斜め読みして全部の20%把握してから(たぶん東大生は70%くらいだと思うけど)、
もっかいしっかりやったほうがはるかに効率よいと。
実践してみたら、ほんとでした。高校生の頃にこの真理を学んでおけば。。。と悔やむくらい。
20%把握しておけば、これは難しいからあとでいいやとか、これは大事だからしっかりとかもある程度
事前に分かっているから、とても効率よいのです。本を読むぞって構えなくてもいいのも〇。隙間時間でざっとね。
最後にしっかり時間とってやる。章ごとくらいが集中力続く限界かな。
だまされたと思ってお試しあれ。

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

ある社内SEと初心者向けRuby on Rails関連の本ご紹介

Rubyを学ぶ人、Ruby On Railsを学ぶ人へ本のご紹介

プロを目指す人のためのRuby入門
Railsやるなら、上の本は必ず読んだほうがいい。仕様が簡潔にまとまっているし、読みやすい。
すぐにRailsでアプリを作り始めるより、まずは上記の本でRubyの仕様を理解すると、
詰まった時に大いに役立つ。いわゆるコピペで動かない問題への対応。
仕様を理解しておかないと、コード読めなくてなんで詰まったかすら分からないから。
難しいところやめんどくさいところは飛ばしてOK。私はテスト周りや正規表現、最後らへんの章は飛ばしました。
他の言語から入ってきたので、ほんとによくできてる言語だなと感心しました。
とくに型の排除、式の結果自体が返り値になる(if文書くとよく分かります)、
メソッドチェーンって言うんだっけ?コロンでつなげるやつ、ループ周り
強力なORマッパー的なやつSQL書かずにコードだけでDBのトランザクション発行できる。(これはRailsか。)

で、次に
改訂4版 基礎 Ruby on Rails (IMPRESS KISO SERIES)
立ち読みして、サンプルが分かりやすく書かれてたので買い。これが良かった。
動くアプリを真似ながら習得するのが一番早いので、私は必要な部分を真似して習得しました。
Google先生とこの本が5体5って感じ。
あ、ちなみにRailsアプリはscaffoldが推奨されているのをよく見ますが、お勧めしません。
だって意味が分からないまま、動いてしまうからです。
ルートも1つ1つ書いたほうがよいと思います。汚くてもいいから自分の書き方で書く。動けばいいの精神で。
ここでまっとうなやり方にこだわっていたら、私は挫折していたと思います。
あとでまっとうなやり方を学んでソースコードをリファクタリングすればよいのです。自分の書き方で書いていれば、
リファクタリングもほんとにスムーズにいきます。びっくりするくらい。
また、ルートをしっかり書こうとすると、RESTFULな考えを自然に覚えさせられます。それが分かれば、scaffold
使ってもいいかなと。だってscafflodってRESTFULな感じでCRUD操作を簡単に実装してくれるやつだし。

あとはRails 〇〇 でぐぐって、ひたすらソースコードを真似る。

です。これでWebサービスを1つ作ることができました。
(検証・本番環境の構築も激しく詰まったけど、また後日書きます)

蛇足ですが、本のお勧め読み方

ある程度分からなくてもいい、適当でもいいから、まず斜め読みで全部読む。
それから手を動かしながら本のソースコードを書くといい感じ。
この前東大生が言ってた。本を読んで最初から100%理解できるわけないと。
まず斜め読みして全部の20%把握してから(たぶん東大生は70%くらいだと思うけど)、
もっかいしっかりやったほうがはるかに効率よいと。
実践してみたら、ほんとでした。高校生の頃にこの真理を学んでおけば。。。と悔やむくらい。
20%把握しておけば、これは難しいからあとでいいやとか、これは大事だからしっかりとかもある程度
事前に分かっているから、とても効率よいのです。本を読むぞって構えなくてもいいのも〇。隙間時間でざっとね。
最後にしっかり時間とってやる。章ごとくらいが集中力続く限界かな。
だまされたと思ってお試しあれ。

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

WindowsでRuby/GTK3を実行する

MacやLinuxの場合,
gem install gtk3
でインストールできるらしいのですが,Windowsだと少し面倒なのでメモ.

RubyInstaller for Windows をダウンロード

  • 以下からRubyInstallerをダウンロードします.
  • 以後 rubyinstaller-2.6.3-1-x64.exe をダウンロードしたものとして説明します.

ダウンロードしたrubyinstaller-2.6.3-1-x64.exeを実行

  • ウィザードに従って進めます.
    ri1.png

  • ダブルクリックでrbを起動したい場合は,「Associate .rb and .rbw files ~」にチェックを入れます.
    ri2.png

  • 最後の画面で「Run 'ridk install' to setup MSYS2 ~」にチェックを入れます.
    ri3.png

  • 以下のコンソールが起動します.
    ri4.png

MSYS2をインストール

  • 「1」を入力し,「MSYS2 base installation」を実行します.
    ri5.png

  • 以下のウィザードが起動するので「次へ」をクリックします.
    ri6.png

  • インストール先フォルダを選択します.(以後「C:\msys64」にインストールしたものとします)
    ri7.png

  • 「次へ」をクリックします.
    ri8.png

  • 「完了」をクリックします.
    ri9.png

  • 続いて「2」を入力し,「MSYS2 system update (optional)」を実行します.
    ri10.png

  • 続いて「3」を入力し,「MSYS2 and MINGW development toolchain」を実行します.
    ri11.png

  • 完了したらEnterを押します.
    ri12.png

GTK3のインストール

  • コマンドプロンプトを開き,インストールしたRuby環境を用いて,GTK3をインストールします.
> cd C:\Ruby26-x64\bin  
> gem install gtk3
  • 結構時間がかかりますが,インストールが完了すると以下のようになります.
    ri14.png

動作確認

  • 以下のコードを任意の場所へ保存します.
test.rb
#!ruby -w

require "gtk3"
cWindow = Gtk::Window.new()
cWindow.signal_connect(:destroy){Gtk.main_quit()}
cWindow.show()
Gtk.main()
  • インストールしたRuby環境でtest.rbを実行します.
> C:\Ruby26-x64\bin\ruby.exe test.rb
  • 以下のウィンドウが起動すればOKです.
    ri15.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CRUD機能のD(Delete)におけるデータベース間とのやりとり

CRUD機能とは、ほぼ全てのソフトウェアが有する4つの永続的な基本機能の頭文字をそれぞれ並べた用語のことをいう。 その4つの機能とは、Create(生成)、Read(読み取り)、Update(更新)、Delete(削除)を指す。

今回は、DのDeleteについての仕組みを解説する。そのため、主にコントローラのdestroyアクションについて解説する。Deleteは、既にデータベースに存在するデータを削除することである。Deleteの流れは、destroyアクションのみを実装すれば良い。

Deleteの処理の流れ

削除したいデータをデータベース上の特定テーブルから抽出して削除する処理

ルーティングを設定する。

delete  'tweets/:id'  => 'tweets#destroy'

destroyアクションを作動させるためのhttpメソッドは、deleteを用いる。

コントローラを設定する

def destroy
    tweet = Tweet.find(params[:id])
    if tweet.user_id == current_user.id
      tweet.destroy
    end
end

コントローラの記述は、editアクションやupdateアクションのときと同様削除したいデータのidと現在ログイン中のユーザーのidが一致していれば、抽出したデータを削除するという処理を行う。

ちなみに、if文を1行に省略して記述する方法として、後置ifを用いて記述する方法がある。

def destroy
    tweet = Tweet.find(params[:id])
    tweet.destroy if tweet.user_id == current_user.id
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CRUD機能のU(Update)におけるデータベース間とのやりとり

CRUD機能とは、ほぼ全てのソフトウェアが有する4つの永続的な基本機能の頭文字をそれぞれ並べた用語のことをいう。 その4つの機能とは、Create(生成)、Read(読み取り)、Update(更新)、Delete(削除)を指す。

今回は、UのUpdateについての仕組みを解説する。そのため、主にコントローラのeditアクションとupdateアクションについて解説する。Updateは、既にデータベースに存在するデータを新たに更新することである。Update処理の流れとしては、editアクション→updateアクションの順番となる。

1.Updateの処理の流れ

データベース上既に存在する特定のデータを抽出する処理(editアクション)
     ↓
抽出したデータを更新し、再びデータベースに保存する処理(updateアクション)

2.データベース上既に存在する特定のデータを抽出する処理(editアクション)

データを更新するためには、編集画面に移動する必要がある。

ルーティングを設定する。

get   'tweets/:id/edit'  => 'tweets#edit'

コントローラを設定する

def edit
    @tweet = Tweet.find(params[:id])
end

editアクションは、データベースの特定のテーブルから,編集したいレコード(ここでは、ツイートのid)を抽出する。そして、そのデータを編集画面のビューに送る。

3.抽出したデータを更新し、再びデータベースに保存する処理(updateアクション)

editアクションによって呼び出された編集画面に新規投稿する。その際、ビューの方であらかじめ、ルーティングが呼び出される記述をする。(今回は、form_tagを使う。)

<%= form_tag("/tweets/#{@tweet.id}", method: :patch ) do %>

これにより、編集をして新規投稿した時にルーティングが呼びだされる。ちなみに、編集をする際に使用するhttpメソッドはpatchである。

ルーティングの設定

patch   'tweets/:id'  => 'tweets#update'

これにより、新規投稿した時にコントローラのupdateアクションが実行するようになる。

コントローラの設定

  def update
    tweet = Tweet.find(params[:id])
    if tweet.user_id == current_user.id
      tweet.update(tweet_params)
    end
  end

updateアクションは、編集するデータがログイン中のユーザーであれば、編集されたデータを新規投稿を行い、テーブルに新たなレコードを保存するようになる(データベースとのやりとりにおいては、createアクションと同じ動きをする。)

ちなみに、if文を1行に省略して記述する方法として、後置ifを用いて記述する方法がある。

def update
    tweet = Tweet.find(params[:id])
    tweet.update(tweet_params) if tweet.user_id == current_user.id
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

あなたはRailsチュートリアルで一体なにを学んだというのか【1章】

  • 注意:プログラミング歴29日の初心者が書いています

  • 注意:間違っていたら優しく教えてください(喜びます)

1.2.1 開発環境

開発環境ってなんですか

「開発」っていうのはつまりコードを書いて何か作るってことです。
「環境」っていうのはつまりコードを書くための準備環境のことです。(トートロジー)

『自分のパソコンで黒い画面とか、エディタ(コード書くアプリのこと)とか使って「開発」してもいいけど、あなたがどんなパソコンでどんなエディタ使ってるのかわからないから、「IDE」で統一してチュートリアルすすめようね』ということです。

「IDE」ってなんですか

ネット上であなたのパソコンの代わりをやってくれます。
『チュートリアルを、みーんなおんなじパソコン使ってやろうね』ってことです。
「AWS Cloud9」っていう名前がついてます。アマゾンが、インターネット上であなたにパソコンを貸してくれています。無料なのでとりあえず借りときましょう。

「Cloud9」の初期登録はググるしかないです。わからなかったらとにかくググるしかないです。検索力も大事らしいです。

登録がおわったら、早速cloud9を開く。
「ファイルとかフォルダが表示された画面」と、「ファイルの中身を表示する画面」と「ターミナル」にわかれています。

  • 「ファイルとかフォルダが表示された画面」: あなたのパシコンでいうところの「エクスプローラ」とか「ファインダー」と同じです。

  • 「ファイルの中身を表示する画面」: ファイルの中身を表示する画面です。

  • 「ターミナル」: あなたのパソコンでいうところの「黒い画面」と同じです。

「ターミナル」ってなんですか

映画で悪いハッカーとかがいじってる「黒い画面」のことです。必ずしも黒である必要はないです。
昔のパソコンはマウスとかカーソル(矢印)がなかったから、文字を直接打ち込んでファイルを作ったりコピーしたりしていたらしい。(知らんけど)
その名残。
現代では、発展的な内容をするときは、黒い画面に文字を打ち込んでパソコンを操作します。
「Cloud9」では、色が黒くなくなって怖さが減っています。

1.2.2 Railsをインストールする

インストールしないと使えません。
「Rails」は元々あなたが持っているものではありませんので、インターネットからダウンロードして使います。
ただし、ダウンロードしかたらといってどこかにわかりやすく表示されるわけではありません。
でも、チュートリアルの通りにすれば使えるようになっているらしいので、ひとまずよしとしてください。

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

cd environment に

$rails new でプロジェクト作成

「アプリケーション」ってなんですか

全部アプリです。パズドラもアプリ、ウェブサイトもアプリ、Webサービスもアプリ、ホームページもアプリ、これからは全部アプリと呼びます。

「ディレクトリ」ってなんですか

「フォルダ」のことです。これからは「ディレクトリ」と呼びます。
「新しいフォルダを作る」したり、コピーしたり削除したり、今後はターミナルに「コマンド(命令)」を打ち込んで操作することを覚えます。(最初から全部暗記するのは無理なので徐々に覚えればいいと思います)

「rails new」するってなんですか

「rails new」 するとディレクトリとファイルが自動でいっぱい出てきます。「Rails君」が勝手に全部作りました。「Rails君」がいなかったら、あなたが全部作るハメになっていました。

それと、「Rails君」がいつも決まった名前を各ディレクトリに付けてくれるおかげで、みんながわかりやすい名前になってます。わからないのは勉強を始めたばかりのあなただけです。

1.3.1 「Bundler」「gem」ってなんですか

  • 「gem」: 追加機能のことです

  • 「bundler」: 追加機能をまとめて管理するやつのことです。

「rails new」 したときに「Rails君」が勝手にいろんな追加機能(gem)を入れてます。チュートリアルでは、いるものといらないものを整理するように言っています。

1.3.2 rails serverってなんですか

これするとアプリケーションが動きます。(ちがうけどそうだと思います)

1.3.3 Model-View-Controller (MVC)ってなんですか

アプリケーションを構成する3銃士です。

  • 「Model」: データベース担当です

  • 「View」: 画面表示担当です

  • 「Controller」: 指示担当です(例えば、ログイン画面にアクセスしたらちゃんとログイン画面が表示されるのはコントローラが指示してるからです。コントローラが間違っていたら違う画面が表示されます)

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

「ヤベー!間違えたー!」 とか
「あ、これ3日前の状態まで戻さないと無理だわコレ」

っていうときに戻せるように、こまめにセーブデータを作っておくことです。
これを自分でやるとめちゃ大変なのでgitという仕組みで管理します。

gitの管理下に置く

$git initリポジトリの初期化
$git add -A新しいファイルをgit対象に
$git commit -m "initialize repository"コミットとコミットメッセージ

「リポジトリ」「コミット」ってなんですか

  • 「リポジトリ」: セーブデータの保存場所です

  • 「コミット」: セーブです

  • 「git add -A」 :変更のあったファイルを「セーブ対象」として登録します

1.4.3 Bitbucket

dropboxとかgoogleドライブとかと同じです。自分のパソコンだけじゃなくて、ネット上に保存しておくと、他の人も見れるので便利です。チームで開発するときにも便利です。
それと、あなたのパソコンが壊れたときでもデータは無事です。

bitbusketに新しいリモートリポジトリ作成

bitbusketのサイトから新しいリポジトリの作成を選択
$git remote add origin 自分のアドレス@bitbucket.org:username/アプリ名.git:gitにadd
$push -u origin --all:bitbucketリポジトリにプッシュ

「ssh」とか「公開鍵」ってなんですか

  • 「ssh」: 遠隔操作のことです。あなたのcloud9をbitbucketで遠隔操作でデータ保存したりします。

  • 「公開鍵」: 遠隔操作するための鍵です。知らない人に遠隔操作されたら怖いので、普段は鍵がかかってます。

「ブランチ」「マージ」ってなんですか

(master)ってなっているのが、一番大事な元セーブデータです。
元セーブデータをいじると、間違ったときに大変なので、別のセーブデータにコピーして、そっちで変更するなり作業します。

それで上手くいったら、元セーブデータと合体(マージ)させます。

「プッシュ」ってなんですか

Bitbucketにデータを保存することです。アップロードみたいなものです。

1.5 デプロイする

ネットに公開するっていうことです。
「Heroku」というサービスを使うとが簡単で便利なのでぜひ使いましょう。

ターミナルでheroku CLIにログイン

$ heroku login --interactiveパスワード入力

ヘロクに登録して、Cloud9からログインして、プッシュすると、あら不思議。
もうあなたのアプリケーションは世界中から見ることができちゃいます。

herokuにアプリケーションを作成してプッシュ

$ heroku create:自動でherokuに新規アプリケーション作成
$ git push heroku master:herokuにプッシュ

まとめ

cd environment に

$rails new でプロジェクト作成

gitの管理下に置く

$git initリポジトリの初期化
$git add -A新しいファイルをgit対象に
$git commit -m "initialize repository"コミットとコミットメッセージ

bitbusketに新しいリモートリポジトリ作成

bitbusketのサイトから新しいリポジトリの作成を選択
$git remote add origin 自分のアドレス@bitbucket.org:username/アプリ名.git:gitにadd
$push -u origin --all:bitbucketリポジトリにプッシュ

ターミナルでheroku CLIにログイン

$ heroku login --interactiveパスワード入力

herokuにアプリケーションを作成してプッシュ

$ heroku create:自動でherokuに新規アプリケーション作成
$ git push heroku master:herokuにプッシュ

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