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

Ruby技術者認定試験対策 | 破壊的メソッド一覧(!がつくメソッドのみ)

Class.instance_methods(false).grep(/.*!/)

これを各クラスに行いビックリマークがつく破壊的メソッドを全て取得して、以下に一覧としてまとめておきます。
それぞれのメソッドにリファレンスのリンクを付与しています。
(非公式ですがAmiWikiのほうが自分はわかりやすいと思ったのでAmiWikiをチョイスしています。AmiWikiにないものは、るりまサーチにしています。)

Array

=> [:reverse!, :rotate!, :sort!, :sort_by!, :collect!, :map!, :select!, :reject!, :slice!, :uniq!, :compact!, :flatten!, :shuffle!]

Hash

=> [:select!, :reject!, :merge!]

String

=> [:succ!, :next!, :scrub!, :upcase!, :downcase!, :capitalize!, :swapcase!, :reverse!, :sub!, :gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr!, :tr_s!, :delete!, :squeeze!, :slice!, :encode!]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの数値について少し詳しく備忘録

10進数以外の整数リテラル

2進数の場合は0b、8進数の場合は0xを先頭につけると、それぞれ10進数以外の整数値を作成することができる!

10進数以外の整数リテラル
# 2進数
0b11111111 #-> 255

# 8進数
0377 #->255

# 16進数
0xff #->255

数値クラス

Ruby2.3までの整数は、Bignum、Fixnumクラスに別れていましたが、Ruby2.4以降は全てIntegerクラスにまとめられた!

この他にも有理数を表すRationalクラスや、複素数を表すComplexクラスがある。

有理数と複素数
# 有理数
r = 2 / 3r
r        #->(2/3)
r.class  #->Rational

# 複素数
c = 0.3 - 0.5i
c       #->(0.3-0.5i)
c.class #->Complex

小数クラスのFloatクラスも含め、数値クラスは全てNumericクラスのサブクラス!

スクリーンショット 2019-11-28 22.38.34.png

あたいの種類によってクラスが異なるので、メソッドの使い方をAPIドキュメントで調べたりする場合は、適切なクラスのAPIドキュメントを参照するように注意!

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

テスト

iPhoneで記事を作成するテスト

‘’’rb
p ‘hello’
‘’’

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

コードを和訳する②(chomp,function,def)

はじめに

前回の記事が思いのほか書くのが楽しかったので、続けざま書いてみます。

実行

まずはRubyの次のコードです。

num = gets.chomp.to_i

入力した数字をnumに代入するコードです。
このうちgets理解する、入手するといった意味があります。
そしてchompむしゃむしゃ食べるという意味があります。
このコードは、言ってみれば人間の言葉を機会の言葉に翻訳する処理とも言えるので、
入力された人間の言葉を咀嚼して、機械にも理解できるようにするという意味合いだと予想できます。

続いては、JavaScriptの関数に使われているfunctionです。

function Say(Hello){
  console.log(Hello);
}

といった具合に使われます。

functionはそのまま関数、機能という意味があるので、特に解釈の必要はなさそうです。

ではRubyの方で関数定義するときに使うdefはどうでしょうか?

def Say(Hello){
 puts Hello
end

という風に使われます。

def自体の意味はすばらしいという意味で、このままイマイチしっくりこないので、defで始まる単語の略であるという説をもって検索してみます。
するとdefinite :確定するという単語が見つかりました。
ここから解釈すると、前述の関数はSayは~という処理と決めるという意味合いだと想像できます。

結果

defなどは若干無理矢理感がありましたが、実際に単語の意味を調べてみると自分なりに理解ができそうです。
まだ短いコードでしか和訳を試していたないので、いずれはもっと長いコードの和訳に挑戦してみたいです。

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

コードを和訳する①(Hello World)

はじめに

プログラミング言語というのは大体英語です。
こういう記事(Qiita外記事)があるくらい、とにかく英語で書かれています。
割と簡単な単語もあれば、辞書を引かないとわからないような単語もあります。
そして、実際に意味を調べると、コードの意味がよくわかることもあります。

本記事では、過去の記事で書いたコードにある英単語を和訳してみて、解りやすくなるのか…わけがわからなくなるのか…それとも笑える感じになるのか、そういったことを試してみます。

実行

どんなプログラムでも最初に勉強することはHello Worldと出力することですが、RubyとJavaScriptではそれぞれ次のようにコードを書きます。

puts "Hello World"
console.log(Hello World)

まずputsの方ですが、goo辞書で調べてみると暴動、反乱といった単語がでます。
明らかに違います
そこで、putの複数形でputsという説を立て再調査してみると、置く、移動する、言う、表現する etcという意味が出てきます。
この中では言う、表現するあたりがしっくりきます。

同じようにconsole.logの方を調べてみます。
consoleの方は、動詞だと慰める、名刺だと端末といった意味が出てきました。
このままではピンとこないので、logも調べてみます。
logの意味は色々ありますが記録に関する意味があるようです。

これらをふまえて、前述のコードを翻訳してみると、

言う "Hello World"
端末の記録(Hello World)

rubyの方はHello Worldと言うという風に解釈できそうです。
JavaScriptの方は端末(=()の中身)の記録を出すかなり強引にに解釈できそうです。

結果

JavaScriptの方はかなり強引になってしまいましたが、Rubyの方は割としっくりくる感じです。
Rubyは日本人が作った言語というのも、こういう結果に影響しているのかもしれません。
JavaScriptの方は英語ではないのでは?という疑念が沸々と湧いています。

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

[ruby2.5~] 文字列中の先頭, 末尾から特定文字を一つだけ消す

特定の文字を先頭から見つかった最初だけ、末尾から見つかった一つだけ消したいニーズがあった
gsubやdeleteだと下記の様に見つかった全部の文字を消してしまうので使えない

'中野区東中野'.gsub('中野', '')
=> "区東"
'中野区東中野'.delete('中野')
=> "区東"

地味に面倒な記憶があったけれど、ruby2.5からdelete_suffix, delete_prefixというメソッドが追加されていた

'中野区東中野'.delete_prefix('中野')
=> "区東中野"
'中野区東中野'.delete_suffix('中野')
=> "中野区東"

破壊的メソッドも用意されているらしい。便利!

参考
https://docs.ruby-lang.org/ja/latest/method/String/i/delete_suffix.html
https://docs.ruby-lang.org/ja/latest/method/String/i/delete_prefix.html

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

サクッと高機能なWebスクレイピングを実現できるRubyGem「Kimurai」

はじめに

Ateam cyma Advent Calendar 2019 の 6日目です。
本日の担当はエイチームのEC事業本部でWebアプリケーションエンジニアをしている@hibiheionです。
業務では主に自転車ECサイトcymaのバックエンドの機能をRailsで書いています。
今年のアドベントカレンダーでは2日目と3日目に続いての登場です。

本題

WebスクレイピングはWebページの情報を自動的に取得する手法です。
RubyはCapybara(※1)やNokogiri(※2)といったRubyGemのおかげでわりと簡単にWebスクレイピングを実現できます。
ですが、夜間に自動でスクレイピングするという場合などには「Webページから情報を取得する」という本来やりたいこと以外にエラーハンドリングやログの出力といったことに手間をかける必要がでてきます。
そういった手間をかけずに高機能なWebスクレイピングを実現できるすばらしいRubyGemがあります。
それが今回ご紹介するKimuraiです。

※1:CapybaraはWebページを操作するためのRubyGem
※2:NokogiriはWebページの要素を解析するためのRubyGem

Kimuraiとは

Kimuraiの公式ドキュメントはこちら
https://github.com/vifreefly/kimuraframework

概要

  • Webスクレイピングを目的としたRubyGem
  • Webスクレイピングフレームワークという立ち位置で、Kimuraiを入れることでWebスクレイピングに必要な機能がまとめて手に入る
    • PythonにScrapyという有名なWebスクレイピングフレームワークがあるのですが、GithubのタグにScrapyが入っているなどRuby版Scrapyを目指しているように感じます
  • CapybaraやNokogiriといったRubyでのWebスクレイピングでは定番のRubyGemを使用している
  • この記事を公開した2019年12月の時点ではマイナーな存在
    • RubyGemsのダウンロード数は9,500くらい
    • Githubのスター数は470くらい
  • Kimuraiの由来は公式ドキュメントで言及されていないのですが、クモの一種の学名「Heptathela kimurai」ではないかと推測しています

特徴

以下のリストは公式ドキュメントのFeaturesの日本語訳(意訳)です。
https://github.com/vifreefly/kimuraframework#features

  • JavaScriptで表示するWebサイトもスクレイピングできる
  • ヘッドレスChrome、ヘッドレスFirefox、PhantomJS、HTTPリクエスト(mechanize gem)をドライバとして使用できる
  • スクレイピングのコードを書いてから使用するドライバを変更できる
  • Capybaraのすべての機能を使用できる
  • 次のような項目を設定できる
    • デフォルトのヘッダー
    • クッキー
    • リクエスト間の待ち時間
    • プロキシやユーザーエージェントのローテーション
  • 以下のようなスクレイピングを簡単にするための機能が備わっている。
    • save_to : 結果をJSONやCSVで保存できる
    • unique? : 同じデータをスキップする
  • リクエストエラーを自動的にハンドリングする
  • メモリやリクエストの上限に達したときに自動的にリスタートしてスクレイピングを継続する
  • wheneverを使って定期的に実行できる
    • (訳注)wheneverは定期的にジョブを実行するRubyGem
  • 「in_parallel」というシンプルなメソッドでスクレイピングを並列実行できる
  • 2つのモードがある
    • 単一ファイルのシンプルなスパイダー
    • Scrapyのようなプロジェクト形式
  • コンソールでの開発モード・色付けされたロガー・デバッカーといった便利な機能が備わっている。これらはPryやByeBugに対応している。
  • ubunt18.04での実行環境の自動セットアップ、Ansibleでのコマンドを使用したデプロイが可能
  • コマンドラインからすべてのプロジェクトのスパイダーを動かすことができる。スパイダーはひとつずつ動かすことも並列して動かすこともできる。

Webスクレイピングにあたってあるとうれしい機能が盛りだくさんです。
Kimuraiを導入すると、自分ではほとんど手を動かさずにこれらの機能が手に入るのです。
実に素晴らしいと思いませんか?

使い方

ここからはKimuraiの使い方を説明します。
特徴のところで書いたように2つのモード(単一ファイルとプロジェクト形式)があるのですが、ここでの説明は単一ファイルのモードについてのみです。

準備

Kimuraiを使用するためには次のような準備が必要です。

  1. ruby2.50以降のインストール
  2. Kimuraiのインストール(gemをインストールするだけ)
  3. ブラウザと対応したWebドライバーのインストール

公式ドキュメントにインストール手順がコマンドと一緒に載っているのでそちらを参考にしてください。
https://github.com/vifreefly/kimuraframework#installation

スクレイピングの実行

作成したプログラムをrubyコマンドから動かすだけです。

ruby spider.rb

サンプルコード

Kimuraiを使用しない場合と使用した場合の2パターンでサンプルコードを作成しました。
サンプルコードでは弊社の自転車ECサイトcymaをスクレイピングして自転車の情報を取得しています。
具体的には次のようなことをしています。

  1. トップページに訪れる
  2. ナビゲーションからカテゴリページのリンクを取得する
  3. カテゴリページに移動する
  4. カテゴリページで商品名、最低価格、最高価格を取得する
  5. 4で取得した情報をCSVファイルに出力する

サンプルコードの途中でCSSセレクタが出てきますが、それぞれ下記の位置を示しています。

■トップページの「#nav-global ul li a」
トップメニュー.png

■カテゴリページの「#cy-products .cy-product-list .product」
カテゴリページキャプチャ.png

なお、記事の公開時点ではサンプルコードの動作を確認していますが、サイトの変更により今後動かなく可能性があります。

Kimurai未使用

まずはKimuraiを使用しないプログラムです。
Webスクレイピングを実行するCapybaraSpiderクラスでスクレイピングの入り口になるメソッドはcrawl!です。

# 動作確認する際は以下のRubyGemのインストールが必要
require "capybara"
require "nokogiri"
require "selenium-webdriver"
require "csv"

# Webスクレイピング実行クラス
class CapybaraSpider
  # トップページのURL
  CYMA_HOME = "https://cyclemarket.jp".freeze

  # ドライバの接続情報
  @session = nil

  # ドライバを初期化する
  def initialize
    Capybara.register_driver :selenium do |app|
      Capybara::Selenium::Driver.new(app,
        browser: :chrome,
        desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
          chrome_options: {
            args: %w(headless disable-gpu window-size=1280,800),
          }
        )
      )
    end
    Capybara.javascript_driver = :selenium

    @session = Capybara::Session.new(:selenium)
  end

  # スクレイピングを実行する
  def crawl!
    # トップページからカテゴリページへのリンクを取得する
    categories = parse_top_page
    sleep 2

    # カテゴリページから商品情報を取得する
    products = []
    categories.each do |category|
      products += parse_category_page(category)
      sleep 2
    end

    # CSV出力
    CSV.open("results.csv", "wb") do |csv|
      csv << %w[name category_name min_price max_price]
      products.each { |product| csv << [product[:name], product[:category_name], product[:min_price], product[:max_price]] }
    end
  end

  private

  # トップページを解析する
  # @return [Array] カテゴリページへのリンク
  def parse_top_page
    # トップページを訪問し、HTMLを解析する
    @session.visit CYMA_HOME
    response = Nokogiri::HTML.parse(@session.html)

    categories = []
    # CSSセレクタを使ってヘッダのナビゲーションを取得する
    response.css("#nav-global ul li a").each do |menu|
      # HTMLのクラスを見てカテゴリページ以外は除外する
      next if menu[:class].include?("outlet") || menu[:class].include?("parts")

      # ナビゲーションからカテゴリページへのリンクを取得する
      category_name = menu.css(".caption").text
      category_url = "#{CYMA_HOME}#{menu[:href]}"
      categories << { category_name: category_name, category_url: category_url }
    end
    categories
  end

  # カテゴリページを解析する
  # @param [Array] category カテゴリページへのリンク
  # @return [Array] 商品情報
  def parse_category_page(category)
    # カテゴリページを訪問し、HTMLを解析する
    @session.visit category[:category_url]
    response = Nokogiri::HTML.parse(@session.html)

    products = []
    response.css("#cy-products .cy-product-list .product a").each do |product|
      # CSV出力のために1件ごとにハッシュに入れる
      row = {}
      row[:name] = product_name(product.css(".body .title").text.strip)
      row[:category_name] = category[:category_name]
      row[:min_price] = product.css(".min-price").text.strip.delete("^0-9")
      row[:max_price] = product.css(".max-price").text.strip.delete("^0-9")

      products << row
    end
    products
  end

  # 商品名にメーカー名がついている場合はメーカー名を取り除く
  # @param [String] base_name 元の商品名
  # @return [String] メーカー名を取り除いた商品名
  def product_name(base_name)
    base_name.include?("\n") ? base_name.split("\n")[1].strip : base_name
  end
end

# スクレイピングを開始する
spider = CapybaraSpider.new
spider.crawl!

Kimurai使用

次にKimuraiを使用したプログラムです。
説明のためにプログラム中のコメントを多めにしています。
基本的な使い方はこのサンプルコードで書いた内容で十分だと思います。
Webスクレイピングを実行するKimuraiSpiderクラスは以下の手順でスクレイピングを開始します。

  1. 継承しているKimurai::Baseクラスのcrawl!メソッドを呼び出す
  2. crawl!メソッドからKimuraiSpiderクラスのparseメソッドを呼び出す
require "kimurai"

# Webスクレイピング実行クラス
# Kimurai::Baseを継承する
class KimuraiSpider < Kimurai::Base
  # 名前
  @name = "kimurai_spider"
  # スクレイピングに使用するドライバ
  @engine = :selenium_chrome
  # 最初に訪れるURL。配列で複数設定することも可能。
  @start_urls = ["https://cyclemarket.jp"]
  @config = {
    # ユーザーエージェント
    user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36",
    # ページを開く前に2秒待つ
    before_request: { delay: 2 }
  }

  # starts_urlsのページを解析する
  # 継承しているKimurai::Baseのcrawl!メソッドから呼ばれる
  # @param [Nokogiri::HTML::Document] response 対象ページを対象としたNokogiri::HTML.parseの結果
  # @param [String] url 対象ページのURL
  # @param [Hash] data 前ページからの引数
  def parse(response, url:, data: {})
    # CSSセレクタを使ってヘッダのナビゲーションを取得する
    # 項目の取得には「css」や「xpath」といったNokogiriの検索系メソッドが使用できる
    response.css("#nav-global ul li a").each do |menu|
      # HTMLのクラスを見てカテゴリページ以外は除外する
      # ノードのアクセスでもNokogiriと同じメソッドを使用できる
      next if menu[:class].include?("outlet") || menu[:class].include?("parts")

      # ナビゲーションからカテゴリページの情報を取得する
      category_name = menu.css(".caption").text
      category_url = absolute_url(menu[:href], base: url)

      # 「request_to」メソッドでカテゴリページに移動し、カテゴリページを解析する。引数は下記の通り。
      # ・第1引数は移動先のページの解析に使うメソッド
      # ・キーワード引数の「url」は移動先のページのURL
      # ・キーワード引数の「data」は移動先のページの解析に使うメソッドへの引数
      request_to :parse_category_page, url: category_url, data: { category_name: category_name }
    end
  end

  # カテゴリページを解析する
  # @param [Nokogiri::HTML::Document] response 対象ページを対象としたNokogiri::HTML.parseの結果
  # @param [String] url 対象ページのURL
  # @param [Hash] data 前ページからの引数
  def parse_category_page(response, url:, data: {})
    # 商品情報を取得する
    response.css("#cy-products .cy-product-list .product a").each do |product|
      # CSV出力のために1件ごとにハッシュに入れる
      row = {}
      row[:name] = product_name(product.css(".body .title").text.strip)
      row[:category_name] = data[:category_name]
      row[:min_price] = product.css(".min-price").text.strip.delete("^0-9")
      row[:max_price] = product.css(".max-price").text.strip.delete("^0-9")

      # CSVファイルに出力する
      save_to "results.csv", row, format: :csv
    end
  end

  private

  # 商品名にメーカー名がついている場合はメーカー名を取り除く
  # @param [String] base_name 元の商品名
  # @return [String] メーカー名を取り除いた商品名
  def product_name(base_name)
    base_name.include?("\n") ? base_name.split("\n")[1].strip : base_name
  end
end

# スクレイピングを開始する
KimuraiSpider.crawl!

こちらはドライバの初期化などが不要なのでスッキリして見えると思います。
ソースコードが短いほど良いというものでもないですが、コメントと空行を除いた行数を比較するとKimurai未使用が67行、Kimurai使用が33行とほぼ半減しています。

所感

実際の運用ではここに書いたサンプルコードよりいくらか複雑なことをしています。
その中で感じた良かった点と気になった点は下記の通りです。

良かった点

  • 「Webページから情報を取得する」という本来やりたいことに集中できる
    • エラーハンドリングやCSVファイルの制御などを書かなくても良い
  • CapybaraやNokogiriのメソッドをそのまま使うことができる
    • すでにRubyでWebスクレイピングを行っている場合、慣れ親しんだ仕組みを活用できるので移行しやすい
  • 別ページに移動する際にrequest_toメソッドを呼ぶ必要があるため、HTMLを解析するメソッドが自然とページごとに分かれる
    • ページの構成が変わったときの影響が限定されるので良い設計だと思う
  • 公式ドキュメントが充実している
    • まだ有名ではないRubyGemだとソースコードを見ないと使い方がわからないことも珍しくないが、Kimuraiは公式ドキュメントだけで使い方を理解できた

気になった点

  • Kimuraiの想定から外れた使い方はしにくい
    • スタート地点にあたるstart_urlsからWebページをたどっていくような使い方を想定している
    • start_urlsを変更する仕組みが公式にはないため、商品コードのリストをもとにURLのリストを組み立てて特定の商品ページだけから情報を取得するいうことはやりにくい
    • できないわけでなくて、今の運用では下記のようなメソッドをクラス内に書いてstart_urlsを動的に書き換えている
  def self.build_start_urls
    start_urls = []
    %w[1 2 4 8 16 32 64].each { |n| start_urls << "https://cyclemarket.jp/product/category_#{n}/" }
    @start_urls = start_urls
  end
  • 今の段階だと日本語の情報がないに等しい
    • とはいえ、公式ドキュメントの英語はわかりやすい

まとめ

Kimuraiの基本的な使い方を身に付けるのはそれほど難しくなく、使い方を身に付ければ効率良くWebスクレイピング機能を実装できます。
この記事で少しでもKimuraiに興味を持っていただければうれしいです。

最後になりましたが、開発者のVictor Afanasevさん、素晴らしいRubyGemを作っていただきありがとうございます!

次回予告

Ateam cyma Advent Calendar 2019 の 6日目の記事は以上です。

7日目はエンジニアの@namedpythonさんが担当します。
内容はPythonに関するもの、ではなくデータの可視化に関するものです。

さいごに

株式会社エイチームでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。

エンジニアで興味を持った方はcymaのQiita Jobsをご覧ください。

そのほかの職種は、エイチームグループ採用サイトをご覧ください。

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

【Rails】CSVファイルからデータをインポート

簡単なTODOアプリに、CSVファイルからタスクを追加する機能を実装します。
手順は以下の記事で説明されているものとほとんど同じです。めちゃくちゃ参考になりました。
【Ruby on Rails】CSVインポート

実装

タスク追加、編集、削除機能、ログイン機能などを持つTODOアプリにCSVアップロード機能を実装して行きます。
Ruby on Railsで簡単なアプリを作成
【Rails】ログイン機能を実装する

rubyの標準ライブラリcsvを追加

/config/application.rb
require 'csv'

rooというgemを追加

csvファイルを読み込むためのgemrooを追加します。

Gemfile
gem 'roo'
terminal
$ bundle install

タスク一覧画面にcsvアップロード用のフィールドを追加

/app/views/tasks/_logged_in.html.erb
<%= form_tag import_tasks_path, multipart: true do %>
  <%= file_field_tag :file %>
  <%= submit_tag "インポート" %>
<% end %>

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

Task.import(params[:file])で使われているimportメソッドは後ほど定義します。

/app/controllers/tasks.controller.rb
def import
  Task.import(params[:file])
  redirect_to root_url
end

ルーティングを設定

collection {post :import}と書き込むことで、resources :tasksで作成されるルーティング以外の、tasksコントローラーのアクションへのルーティングを追加することができます。

/config/route.rb
resources :tasks do
  collection {post :import}
end
terminal
$ rails routes
.
.
import_tasks POST   /tasks/import(.:format)   tasks#import
.
.

import_tasksという名前付きルートが追加されました。

モデルにCSV読み込み、登録処理を実装

/app/models/task.rb
#importメソッド
def self.import(file)
  CSV.foreach(file.path, headers: true) do |row|
    # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成
    task = find_by(id: row["id"]) || new
    # CSVからデータを取得し、設定する
    task.attributes = row.to_hash.slice(*updatable_attributes)
    task.save
  end
end

# 更新を許可するカラムを定義
def self.updatable_attributes
  ["title", "user_id"]
end

動作確認

以下のようなファイルを用意します。

taskForTodoApp.csv
title,user_id
パンを買う,2
筋トレ,2
メルカリの発送,8
ティッシュを交換する,2

実行結果
 2019-11-28 18.43.08.png

ハマったポイント

・CSVファイル内にuser_idを記載しておらず、データベースへの登録時にエラーが発生していたところで少しハマりました。

TODO

・TSVファイルも取り込めるようにする

参考

【Ruby on Rails】CSVインポート

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

さくらVPSでCentOS7 10.Ruby On Railsインストール

はじめに

自由にテスト出来るLinuxのサーバーがほしくて、さくらVPSで構築してみました。
順次手順をアップしていく予定です。

今回は、Ruby On Railsをインストールします。
どういった構成が正解かは判断がつきかねているのですが、今のところこんな感じです・・(^^;

目次

  1. 申し込み
  2. CentOS7インストール
  3. SSH接続
  4. Apache・PHPインストール
  5. MariaDBインストール
  6. FTP接続
  7. sftp接続
  8. phpMyAdminインストール
  9. 環境のバックアップ
  10. Ruby On Railsインストール

10.Ruby On Railsインストール

必要パッケージインストール

Node.jsインストール

Node.jsのバージョンを管理する「n」パッケージをインストールします。
鶏と卵のような話ですが、「n」パッケージインストールにはnpmが必要で、npmにはNode.jsが必要ということで、Node.jsをインストールする為にまずnpmとNode.jsをインストールします。

npmインストール

$ sudo yum install epel-release

node.jsインストール

$ sudo yum install nodejs npm

「n」パッケージインストール

$ sudo npm install -g n

「n」パッケージを使ってNode.jsをインストール

最新版は「latest」、安定版は「stable」でインストールできます。

$ sudo n latest
$ node -v
v11.13.0
$ sudo n stable
$ node -v
v10.15.3

「n」パッケージの使い方

コマンド 内容
n インストールしているバージョンの表示やバージョンを切り替える
n ls すべてのバージョンを表示する
n --latest 最新バージョンを表示する
n --lts 安定バージョンを表示する
n latest 最新バージョンをインストールする
n stable 安定バージョンをインストールする
n [version] 指定したバージョンをインストールする
n rm [version] 指定したバージョンを削除する
n prune すべてのバージョンを削除する

その他もろもろインストール

$ sudo yum -y install gcc make openssl openssl-devel gcc-c++ mysql-devel readline-devel libxml2-devel libxslt-devel git bzip2 zlib-devel sqlite-devel
$ sudo npm install yarn -g

Rubyインストール

rbenvインストール

rbenvは、複数のRubyのバージョンを管理し、プロジェクトごとにRubyのバージョンを指定して使うことを可能としてくれるツールです。
まずこれをインストールします。

$ cd /usr/local
$ sudo git clone https://github.com/rbenv/rbenv.git
$ sudo mkdir rbenv/plugins && cd rbenv/plugins
$ sudo git clone git://github.com/sstephenson/ruby-build.git

ログイン時にrbenvを使えるようにする初期化スクリプトを記述します。

$ sudo vi /etc/profile.d/rbenv.sh

以下を追加

export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init -)"

パーミッションの変更

$ sudo chmod -R 777 /usr/local/rbenv

設定を反映させるために、ここで一度ログアウトして再ログインします。
再ログインしたら、確認。

$ rbenv install -l
2.6.5

Rubyインストール

rbenvを使ってRubyをインストールします。
インストール可能なRubyバージョン一覧を表示します。

$ rbenv install -l

バージョン2.6.5のRubyをインストールします。

$ rbenv install 2.6.5

全体で使用するRubyのバージョンを指定

$ rbenv global 2.6.5

確認

$ ruby --version
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]

Rails環境構築

GemとBundler

GemはRubyGems(Ruby用のパッケージ管理ツール)で管理されるrubyのアプリケーションやライブラリです。
Bundlerは、gem動詞の互換性を保ちながらパッケージの種類やバージョンを管理するツールです。

gem

確認

$ which gem
/usr/local/rbenv/shims/gem

最新版にアップデート

$ gem update --system

バージョン確認

$ gem -v
3.0.6

bundlerインストール

システムのgemにはbundlreのみインストールし、bundler以外のものはプロジェクトのvendor/bundleに格納します。
ので、ここではbundlerのみインストール。

$ gem install bundler

プロジェクトのフォルダ

以前、4. Apache・PHPインストールでApacheをインストールした時に作成した、/var/www/の中にappというフォルダを作成し、その中にプロジェクトを作っていきます。

フォルダ作成

$ mkdir /var/www/app

Webコンテンツに複数のユーザが操作出来るように作成したグループに権限を与えます。

$ sudo chown root:webadmin /var/www/app/
$ sudo chmod 2775 /var/www/app/ -R

「bundle exec」の省略設定

railsコマンドをプロジェクト内のvendor/bundleに格納すると、railsコマンド呼出時に、「bundle exec rails server」のように「bundle exec」を付けて呼び出す必要があります。
このbundle execを省略できるように設定します。
この設定は、各ユーザーごとに必要です。

スクリプトをダウンロード

$ cd
$ curl -L https://github.com/gma/bundler-exec/raw/master/bundler-exec.sh > ~/.bundler-exec.sh

bundler-exec.shを編集

$ vi ~/.bundler-exec.sh

リストにrailsを追記(下の方にあります)

  ・
  ・
 (省略)
  ・
  ・
unicorn
unicorn_rails
wagon
rails
}"

define-bundler-aliases

unset -f define-bundler-aliases

反映させます。

$ source ~/.bashrc

利用可能なRailsのバージョンの確認

$ gem query -ra -n "^rails$"

現時点で最新は6.0.1でしたが、以下のプロジェクトでは5.2.4を使います。

プロジェクト作成

プロジェクト用フォルダ

bundlerを利用する為、「rails new 」する前に、フォルダを作成します。

$ mkdir /var/www/app/HelloWorld
$ cd /var/www/app/HelloWorld

ローカルのrubyのバージョンを指定。

$ rbenv local 2.6.5

確認

$ rbenv version
2.6.5 (set by /var/www/app/HelloWorld/.ruby-version)

Gemfile

Gemfileの雛形作成

$ bundle init
riting new Gemfile to /var/www/app/HelloWorld/Gemfile

Gemfileの編集

$ vi Gemfile

railsのバージョン5.2.4を使用できるように下記のように修正します。

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# コメントを外し、バージョンを記述
gem "rails", "5.2.4"

gemのインストール

$ bundle install --path vendor/bundle

インストールのメッセージが流れ、無事に終わるかと思ったのですが、こんなメッセージが・・・

HEADS UP! i18n 1.1 changed fallbacks to exclude default locale.
But that may break your application.

Please check your Rails app for 'config.i18n.fallbacks = true'.
If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be
'config.i18n.fallbacks = [I18n.default_locale]'.
If not, fallbacks will be broken in your app by I18n 1.1.x.

For more info see:
https://github.com/svenfuchs/i18n/releases/tag/v1.1.0

「i18nの設定方法が変わったので気を付けなさい」ということみたいです。
だだ、今回はRailsのバージョン5.2.4にしていますので、I18n (>= 1.1.0) and Rails (< 5.2.2)の条件には引っ掛からないので大丈夫だと思います。

新規プロジェクト作成

これでやっとrailsコマンドが使えるようになりましたので、プロジェクトを作成します。
フォルダは作成済みですので、プロジェクト名の指定は不要です。

$ rails new .

.ruby-versionを上書きしますか?と聞いてきますのでYを入力。

Overwrite /var/www/app/HelloWorld/.ruby-version? (enter "h" for help) [Ynaqdhm]Y

Gemfileを上書しますか?と聞いてきますのでYを入力。

Overwrite /var/www/app/HelloWorld/Gemfile? (enter "h" for help) [Ynaqdhm]Y

順調に行くと思いきや、以下のメッセージが出ました。

The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Bundler could not find compatible versions for gem "sprockets":
  In snapshot (Gemfile.lock):
    sprockets (= 4.0.0)

  In Gemfile:
    sass-rails (~> 5.0) was resolved to 5.1.0, which depends on
      sprockets (>= 2.8, < 4.0)

    rails (~> 5.2.4) was resolved to 5.2.4, which depends on
      sprockets-rails (>= 2.0.0) was resolved to 3.2.1, which depends on
        sprockets (>= 3.0.0)

Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.
         run  bundle exec spring binstub --all
bundler: command not found: spring
Install missing gem executables with `bundle install`

依存関係に問題があるから、「bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java」を実行しろということのようです。
あと、「bundle update」を実行すると、スナップショットを最初から再構築します、と。
なので、やってみます。

$ bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies......
Bundler could not find compatible versions for gem "sprockets":
  In snapshot (Gemfile.lock):
    sprockets (= 4.0.0)

  In Gemfile:
    sass-rails (~> 5.0) was resolved to 5.1.0, which depends on
      sprockets (>= 2.8, < 4.0)

    rails (~> 5.2.4) was resolved to 5.2.4, which depends on
      sprockets-rails (>= 2.0.0) was resolved to 3.2.1, which depends on
        sprockets (>= 3.0.0)

Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.
$ bundle update
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
 ・
 ・
(以下略)
 ・
 ・

と、同じようなメッセージが・・・
とりあえず、再度「bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java」して、「bundle update」すると、「Bundle updated!」と出たので、とりあえずOKかなと。

サーバー起動

3000のポートを開ける

ファイアウォールを使っている場合

ポートを開ける

$ sudo firewall-cmd --permanent --add-port=3000/tcp
$ sudo firewall-cmd --reload![2019-11-28.png](https://qiita-image-store.s3.ap-northeast-

さくらVPSのパケットフィルタを使っている場合

  1. さくらVPSコントロールパネルにログインし、サーバーを選択
  2. [パケットフィルタ]-[パケットフィルタ設定へ>]を選択
  3. [+任意の解放ポート設定を追加する]ボタンをクリック
  4. [TCP][3000]を設定
  5. [設定]ボタンをクリック


以上でポートが開きます。

サーバー起動

$ rails server -b 0.0.0.0
=> Booting Puma
=> Rails 5.2.4 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.1 (ruby 2.6.5-p114), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

無事起動しました。

ブラウザで確認

次回

次回は、Pythonのインストールの予定です。

前回:Ruby On Railsインストール

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

RailsのDM一覧に最後のメッセージを表示させる方法

はじめに

記事①記事②を参考にDM機能を作らせていただきました。投稿してくださった方々に深く感謝いたします。
以下は記事①の通りにDM機能を作成した前提で進めます。

やりたいこと

TwitterやLINEのようにメッセージの一覧に最後のメッセージを表記させる。

実装

記事①通りに進めるとメッセージ一覧はrooms#indexに表示させることになると思うので、以下のようにすればユーザ間の最後のメッセージを引っ張ってくることができます。

まず、記事②を参考にroomsコントローラ側に@anotherEntriesを定義します。

roomsコントローラー
def index
  @currentEntries = current_user.entries
  myRoomIds = []

  @currentEntries.each do | entry |
    myRoomIds << entry.room.id
  end

  @anotherEntries = Entry.where(room_id: myRoomIds).where('user_id != ?', @user.id)
end

そして、@anotherEntriesにはuser_idが自分のidではない相手の情報が配列として入っているので、以下のようにすればやりとりした最後のメッセージを引っ張ってこられます。

rooms#index
<% @anotherEntries.each do |e| %>
  <%= Message.find_by(id: e.room.message_ids.last).content %>
<% end %>

上はただ表記させただけなので、あとは自分好みにリンクにしたり字数に制限をかけたりしてみてください。
また、ここでは最後のメッセージを表示させましたが、上記のcontentの部分をuser.nameにすれば最後にメッセージを送ったユーザ(自分か相手)の名前を引っ張ってくることもできます。

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

has_many_attachedなActiveStorageに属性を付与する

とあるModelに複数のファイルをAttachさせるときに、
ActiveStoragehas_many_attached を使うことはよくあると思いますが、
このとき、

  • ファイルに属性を付与したい
  • 同じ属性のファイルが既にあるなら削除したい

的な感じの気持ちが溢れ出ると思います。

ので、やります。

class Foo < ApplicationRecord
  has_many_attached :bars
end

このクラスで何かをattachします

class Foo < ApplicationRecord
  has_many_attached :bars
  def attach_bars
    bar = 'something' #何らか適当なファイル的なやつ
    bars.attach(
      io: StringIO.new(bar),
      filename: 'なんらかてきとうなふぁいるめい.txt',
      content_type: 'text/plain',
      metadata: { bar_type: 'something' }
    )
  end
end

metadata: { bar_type: 'something' } の部分が何かいい感じのやつで、これで active_storage_blobs.metadata"bar_type": "something" な感じでデータが保持されるようになります。やったね。

これで属性付与できたので、 bars の中で bar_type == 'something' なattachmentを削除したい場合は、

foo = Foo.find(xxx)
foo.bars.select {|bar| bar.metadata[:bar_type] == 'something' }
  .each {|bar| bar.purge }

みたいな感じでpurgeすることはできるんですが、仮に bars が100件とか200件とかなってきたらウザウザなので、事前にクエリで絞り込む的なことがしたくなりました。

というわけで、しました。

foo.bars.joins(:blob).where(
  "`active_storage_blobs`.`metadata`->>'$.bar_type' = ?", 'something'
).each { |bar| bar.purge }

やったね!

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

コメントの削除も非同期で対応する

コメント投稿の非同期は成功しました。
そこで、コメント削除も非同期にて対応しようと思い。
クロームの検証画面でエレメントコピーして修正して下記コードを追加しました。
削除

コメントして即削除でActiveRecord::RecordNotFound in TweetsController#destroyが出ます。

一度リロードをすれば問題なく削除できているので
非同期部分で引っかかっているのは確認済みです。

どうすればいいのでしょうか?
コメント投稿と削除で別々の
コードがいるのでしょうか?
記載をするべきでしょうか?
何かファイルを増やしてそこに記載がいるのでしょうか?

$(function(){
  function buildHTML(comment){
    var html = `<p>
                  <strong>
                    <a href=/users/${comment.user_id}>${comment.user_name}</a>
                    :
                  </strong>
                  ${comment.text}
                  <a class="comment-delete" rel="nofollow" data-method="delete" href="/tweets/${comment.tweet_id}">削除</a>
                </p>`
    return html;            
  }


  $('#new_comment').on('submit',function(e){
    e.preventDefault();
    console.log(this)
    var formData = new FormData(this);
    var url = $(this).attr('action')
    $.ajax({
      url: url,
      type: "POST",
      data: formData,
      dataType: "json",
      processData: false,
      contentType: false
    })
    .done(function(data){
      var html = buildHTML(data);
      $('.comments').append(html);
      $('.form_message').val('');
      $('.form__submit').prop('disabled', false);
    })
    .fail(function(){
      alert('error');
    })
  })
})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsとLaravelの比較

はじめに

この記事はRailsとLaravelを比較分析してみた記事です.
Railsは以前から勉強していて,新たにLaravelを使ってみました.
Railsの勉強にはRuby on Rails 5 超入門改訂4版 基礎 Ruby on Rails (IMPRESS KISO SERIES)を使いました.
Ruby on Rails 5 超入門についてはチュートリアル形式で一見わかりやすいのですが,誤植などが多いのとレベルが優しすぎるのであまりオススメしません.

Laravelの勉強はPHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応を使いました.買う前に中身を確認しなかった僕が悪いのですが,本の厚さの割に内容が薄いです.また辞書的に使う分には良いのですが,チュートリアル形式で作っていく感じではないので,フレームワークを使ったことのない人などにはオススメ出来ません.

RailsとLaravelの基本情報

*以下,2019/11/28現在の情報

Rails Laravel
言語 Ruby PHP
初リリース 2004年 2011年
github https://github.com/rails/rails https://github.com/laravel/framework
github start数 44.6K 19.5K

githubのstart数ではrailsの方がLarvelの倍くらいの数になっています.Railsはいま流行りと個人的に思っていたりするのですが,リリースからはもう10年以上経過していました.

Googleトレンドで調べてみた

Googleトレンドでは検索のトレンドを調べて,検索キーワードで比較したりできるサービスです.Googleは面白いサービスを作っていますね.
これを使ってRailsとLaravelについて調べてみました.

すべての国

https://trends.google.co.jp/trends/explore?date=all&q=Rails,Laravel

スクリーンショット 2019-11-28 16.02.49.png

日本

https://trends.google.co.jp/trends/explore?date=all&geo=JP&q=Rails,Laravel

スクリーンショット 2019-11-28 16.03.22.png

Laravelはリリースが2011年なので,2012年頃まではほぼ0です.
全ての国の場合ではLaravelがRailsをやや追い越したくらいになっています.日本の場合ではまだRailsを追いかけていますが,このままの状態が続くと追い越しそうです.

個人的分析

ここからはRailsとLaravelを使ってみた感想になります.

本,参考資料などの多さは? 勝者:Rails

Railsの方がネットでググったときなどの参考になる記事などが多いです.それはRailsで使われている言語のRubyの開発者が日本人(まつもとゆきひろ氏)なのでRubyの日本語資料がそもそも多いという点と,Laravelより7年早くリリースしているからかなと思っています.

*IT分野の中で日本発祥の技術はRubyと深層学習のChainerくらいしか知らない(他にあったらコメントください)のでRubyは本当すごいです.

勉強のしやすさは? 勝者:Rails

難易度的にもRailsとLaravelは遜色ないですが,やはり本や参考資料などが多い方が勉強しやすいです.以前Go言語のフレームワークのGinを使う機会があったのですが,公式以外のドキュメントが少なくて開発しにくかったことがあります.

使いやすさは? 同じ

  • railsにはscaffoldという色々必要なものを一発で作ってくれるコマンドがあるのですが,Laravelは標準でサポートしていません(あるけど,githubの更新も止まっているのでバージョンに寄っては今後使えなくなるかも).
  • Laravelは比較的命名規則などが緩いので,ネットで調べても色々な書き方をしているのでわかりにくいです.
  • 逆にRailsは規則が厳しいのですが,厳しいが故に,変数名などを修正するのが億劫になります.
  • Laravelはデフォルトでログイン認証などが入っているので,インストールなどの手間が省けます.

RailsとLaravelのどっちがいいの? 若干Laravelかな

ここまで読んだ人にはRailsの方が良い感じに見えますが,他のエンジニアの人の意見を聞くと

「Railsは比較的開発が速くできるからスタートアップに多いが,保守・運用や難しい機能にはRailsは向いておらず,JavaやPHPのフレームワークの方が開発が容易」

という意見が多かったです(社会人になって1ヶ月くらいなのでこの辺は自分の中で不確定要素).確かに求人とか見てるとPHPとかJavaの募集の方が多いなという印象を持ったのでLaravelとしました.

まとめ

Railsが最強(僕の大学の教授)という人もいれば,Railsはオワコンと言っている人もいますのでどうなんですかね...
僕の周囲ではRailsの方がよく耳にします.
Rails,LarvelはRubyとPHPという言語の違いは多少ありますが,考え方や使い方はほとんど同じです.個人的には勉強のしやすいRailsを最初にやってMVCの基礎を勉強して,Laravelとかに移ればいいかなと思います.

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

【Rails】もっと早く知りたかったデバッグ用gem 'better_errors','binding_of_caller'

はじめに

先日伊藤さんのこちらの動画(↓)を見ました。
プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意 - YouTube

「いや、なんでもっと早く見なかった!」と思うくらい具体的なデバッグ手法が諸々解説されています。

そこで出てきたgem better_errors&binding_of_callerの導入方法と見方について簡単にまとめます。

今まで

  • puts デバッグ
  • binding.pry
  • rails serverのログ

を使ってエラーと闘っていましたが、この2つのgemはもっと早く知りたかったです:sweat_smile:

この記事が役に立つ方

  • エラーに苦しんでいるRails初心者

この記事のメリット

  • デバッグの効率が上がる

環境

  • macOS Catalina 10.15.1
  • zsh: 5.7.1
  • Ruby: 2.6.5
  • Rails: 5.2.3
  • Docker: 19.03.5

better_errorsとは?

デフォルトのエラー画面をわかりやすく整形してくれるgem。

binding_of_callerとは?

上記better_errorsと一緒に使うことで、ブラウザ上でirbを使えるようになるgem。
※本記事では使用方法について触れていません。

導入方法

Gemfile
group :development do
  gem 'better_errors'
  gem 'binding_of_caller'
end

Gemfileに上記追記し、

bundle install

で完了。簡単!

※Dockerを使用している場合はもうひと手間必要

app/config/environments/development.rb
BetterErrors::Middleware.allow_ip! "0.0.0.0/0"

仮想環境を使っている方は、うまく動作しないようです。
私はDockerを使っていますが、上記コードを追記してサーバー再起動で動作しました。
Gem 『Better errors』が動かないとき | HippoBlog

使用例

ArgumentErrorが出ていた場合

form_withを使おうとしたらエラーが出た場合を例にします。

いつもの赤いエラー画面から表示が変わっています。
スクリーンショット 2019-11-28 14.54.16.png

エラーメッセージが最上部にあるのは変わりませんが、
その下に2つタブがあります。これが超便利。

  • Application Frames
  • All Frames

1. Application Frames

最初はApplication Framesが表示されています。
ここでは、自分の書いたコードを対象にエラーに関係する箇所を明示してくれています。
スクリーンショット 2019-11-28 14.54.16.png

2. All Frames

次に、All Framesをクリックすると、自分が書いた箇所以外(gemやActiveSupportなど)まで踏み込んでエラーに関係する箇所が表示されます。

それぞれクリックしていくとコードが表示されます。

例えば今回は、form_withのソースコードまで表示してくれます。
スクリーンショット 2019-11-28 14.55.22.png

form_withの最初の1行を抜き出すとこんな感じです。

def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)

これで指定すべき引数がわかり、今回は

「いけね!そういえばmodel:って書き忘れた!」

と気づくことが出来ます。便利!


その他、NoMethodError~ for nil:NilClassとか出ていたら、ブラウザでそのまま変数の中身を確認したり出来て非常に便利です。
スクリーンショット 2019-11-28 15.57.41.png
※画像最下部がコンソールになっています。

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

デバッグ手法は学び始めの段階でなるべく多くリストアップしておいたほうが効率がいいと思いますが、better_errorsはもっと早く導入していたかったです:sweat_smile:

伊藤さんのYoutube、非常に勉強になるのでまた他の動画も見させて頂きます。

参考にさせて頂いたサイト(いつもありがとうございます)

プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意 - YouTube
プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意(解説動画付き) - Qiita
Gem 『Better errors』が動かないとき | HippoBlog
【Rails】better_errorsとbinding_of_callerで自分でエラーを解決できるようになろう【初心者向け】 - Qiita

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

Webエンジニア業界に感じた違和感

私は18年間ほど企業向け製品開発の世界(SIer含む)にいました。
メインで使っていた言語はC++とC#です。

2014年にウェブスタートアップを数カ月手伝う経験があり、
フロントエンドの技術やWebフレームワークに興味を持ち、ウェブ系のカンファレンスに行くようになりました。

ウェブの技術は大変面白かったのですが、そこである大きな違和感を感じもしました。

カンファレンスで発表する人の中にはその道の有名人みたいな人がいて、ブログやTwitter、Githubなどで沢山フォロワーがついています。

常に数字や営業的な雰囲気に包まれている企業向け製品開発にはない、純粋に技術を楽しむ雰囲気がとても楽しかったです。

ですが、よくよく観察しているとWeb業界には「何が凄いのかよくわからないけど有名な人」もいました。

はっきり言ってしまえば、「ただツールやライブラリの使い方を紹介するだけで有名人になっている人達がいる」ように見えてきたのです。

Webカンファレンスのほとんどの発表内容は、ツールやライブラリの構造上の理解や仕組み、またはそれによる営利的なメリットではなく、「このツールを使えばこんなことができる」という使い方の説明であり、多少の業務経験がある人が英語リファレンスを読み解けば、比較的容易に発表できてしまう内容が多かったです。

さらに業界を観察していると「新しい技術を紹介したもの勝ち」的な雰囲気が蔓延しており
目的を見失ってそのスピードの速さ、紹介するレトリックの巧みさが競われているような文化さえ感じたのです。

そして周囲から有名になると、まるで芸能人のように祭り上げられます。(それが業界の面白さや楽しさでもあるのは理解してます)

世の中に何かをアウトプットできる
インフルエンサーとしての能力を持っている

これは素晴らしいことですし、その能力を持っていない人より持っている人は優れていると思います。
しかしそれがイコールその人の技術者としての技術力ではないはずです。

ツールやライブラリをスクラッチで自作した本人じゃなければ評価されちゃいけないと言ってるわけではないです。

ですが、Web業界では「ライブラリを作った人」より「上手く紹介した人」の方が有名になっている、といった違和感を感じました。

技術とその価値は「具体的な成果物を出して世の中にどれだけの影響を与えたか?」ではないでしょうか?

その成果物は技術の紹介ブログ記事やTwitterやカンファレンスの発表やQiitaのGood数やGitHubのスターの数ではなく、「直接、あるいは間接にでも売上に貢献した目に見えるプロダクト」であるべきです。

極端かもしれませんが、これが基準にならなければ評価の基準も曖昧になります。

最後に、私はWeb業界の雰囲気が嫌いではありません。
Qiitaを見ていると新しいことを学ぶモチベーションが高まります。

ですが、Qiitaやブログの記事を一つ読むにしても、その人は「本当に価値のあるアウトプット」をしている人なのかを考えながら接する必要があると思っています。

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

openssl1.0系がbrewから消えたことでrailsでエラーが発生する時の対処法

openssl1.0系がbrewから消えたことでrailsでエラーが発生する時の対処法

次のようなエラーが発生する場合の対処法です。
Library not loaded: /opt/local/lib/libssl.1.0.0.dylib (LoadError)

openssl1.1系にアップデートされた状態で、postgresqlのバージョンが古いとエラーが発生します。
対処法としては、
brew upgrade postgresqlでpostgresqlをアップデートしたら直りました。

参考までに、openssl1.0系がbrewから消えた原因のHomebrewのコミットはこれです。
https://github.com/Homebrew/homebrew-core/commit/0349a7ca76f483483c6d5c1d4cfe6b458dee2665

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

Ruby on Rails のselectメソッドで生成されるoptionタグにdisabled属性を追加する

はじめに

Ruby on Rails のselectメソッドでされるoptionタグにdisabledを設定したい。

まとめ

第3引数の要素情報を入れている箇所に、ハッシュ形式で追加すれば、設定されました。
他の属性追加についても同様にできました。

<%= f.select :users, User.all.map { |user| [user.name, user.id, {disabled: true}] } %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ABC085B - Kagami Mochi

問題

https://atcoder.jp/contests/abs/tasks/abc085_b
スクリーンショット 2019-11-28 12.01.02.png

1回目

回答

重複削除のメソッドあるかな!って調べたらあったので一瞬で終わった。

N = gets.to_i
D = N.times.map{gets.to_i}
puts D.uniq.size

結果

スクリーンショット 2019-11-28 12.02.30.png

2回目

回答

せっかくなので自力で重複削除してみた

N = gets.to_i
D = N.times.map{gets.to_i}

for i in 0..(D.size-2)
  (D.size-1).step(i+1, -1) do |j|
    if D[j] == D[i]
      D.delete_at(j)
    end
  end
end

puts D.size

結果

スクリーンショット 2019-11-28 12.04.02.png

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

投稿できるかテスト

Ruby on Rails 勉強するぞ

これから4ヶ月プログラミングスクールに通いながら勉強するので、そこで学んだことなんかを投稿していきます。

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

Rubyでブラックジャックのアプリを作った

プログラミング初学者がたどり着く定番のブラックジャックを作ってみました。

ちょっと雑感もあるし、いかにも初学者らしい感じのコーディングになっている気がするので、もう少しクラスを意識したり、可読性を良くしたり、効率の良い感じを検討して改修しようかな。

仕様要件

・ジョーカーなしの1〜13×4種の構成。
・相手が先に山札から引き、17以上になるまで引く。但し、1枚目しか数字がプレイヤーには見えない。
・プレイヤーは初期に2枚引き、ヒットがスタンドを選択する。ヒットならば1枚引き、スタンドならば現在の手札で勝負する。
・JQKは10として扱い、Aは1もしくは11として扱う。
・手札の合計値が21で勝利した場合はブラックジャックという結果が出る。

実際に書いてみた

blackjack.rb
# Card judgement
def card_judge(i)
    # mark judgement
    mark_no = (i % 13 == 0) ? (i / 13) - 1 : i / 13

    # Mark no: 0 SPADE / 1 HEART / 2 DIAMOND / 3 CRAB
    card_mark = case mark_no
    when 0 then "♠️"        #SPADE
    when 1 then "♥️"        #HEART
    when 2 then "♦️"        #DIAMOND
    when 3 then "♣️"        #CRAB
    end

    # number judgement
    card_no = i - ( 13 * mark_no )

    # display judgement
    disp_list = %w(A 2 3 4 5 6 7 8 9 10 J Q K)
    disp_num = disp_list[(card_no - 1)]

    # return: card number / mark / display number
    return card_no, card_mark, disp_num
end

# card deisplay
def disp_card(n)
    puts "#{card_judge(n)[1]} #{card_judge(n)[2]}"
end

# hand deisplay dealer
def disp_hand_dealer(p)
    puts "\nhand list(#{p.length}):"
    p.each{|i|
        case i
        when p[0]
            disp_card(p[0])
        else
            puts "Reverse"
        end
    }

end

# hand deisplay
def disp_hand(p)
    puts "\nhand list(#{p.length}):"
    p.each{|i|
        disp_card(i)
    }
end

# Draw action
def card_draw
    # Deck to hand
    draw_card = DECK.shift(1)

    # Card number
    return draw_card[0]
end

# Deck shuffle
def deck_shuffle
    DECK.shuffle!
end

# Number sum
def card_sum(m,o)
    if card_judge(m)[0] > 10
        return 10
    elsif card_judge(m)[0] == 1
        if o + 11 < 21
            return 11
        else
            return 1
        end
    else
        return card_judge(m)[0]
    end
end

def duel_judge(d_score, p_score)
    puts "Dealer's score:#{d_score}"
    puts "Player's score:#{p_score}"
    if p_score == d_score
        return "\nIt' draw."
    elsif p_score == 21
        return "\nYou win. It's blackjack."
    elsif d_score == 21
        return "\nYou lose. It's blackjack."
    elsif p_score > 21
        return "\nYou lose. Player's burst."
    elsif d_score > 21
        return "\nYou win. Dealer's burst."
    elsif p_score > d_score
        return "\nYou win."
    elsif p_score < d_score
        return "\nYou lose."
    end
end

# First setup
# Dealer
DEALER_HAND = []
dealer_score = 0
#Player
PLAYER_HAND = []
player_score = 0
# Deck
TRASH = []
card_max = 52

# Deck setup
DECK = [*(1..(card_max))]

# Deck shuffle
puts "Shuffle the desk.\n"
deck_shuffle

# Dealer's turn
puts "The dealer draws the card."
while dealer_score <= 17 do
    DEALER_HAND.push(card_draw)
    dealer_score += card_sum(DEALER_HAND[-1], dealer_score)
end

disp_hand_dealer(DEALER_HAND)

# Player's turn
puts "\nThe player draws the card."
2.times{
    PLAYER_HAND.push(card_draw)
    player_score += card_sum(PLAYER_HAND[-1], player_score)
}

disp_hand(PLAYER_HAND)

puts "\nPlayer's score:#{player_score}"

# Hit or Stand
loop {
    puts "\nhit or stand [H/S]"

    case gets
    when /^[hH]/
        puts "Hit"
        PLAYER_HAND.push(card_draw)
        player_score += card_sum(PLAYER_HAND[-1], player_score)
        disp_hand(PLAYER_HAND)
        puts "\nPlayer's score:#{player_score}"
        if player_score >= 21
            break
        end
    when /^[sS]/
        puts "Stand"
        break
    end
}

# Duel
puts "\nDuel!"
puts duel_judge(dealer_score, player_score)

exit

ソースコードで使用したもの

後でまとめます

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

Railsで開発環境のURLをlocalhost:3000ではなくa.example.comとかにする方法

/etc/hosts ファイルをsudoで編集します。Linuxでも同様に動きます。

127.0.1.1      a.example.com
127.0.1.1      b.example.com

これで、アプリを起動させて、ブラウザーをそれらのいずれかに向けると、動作するはずです。次に、Railsアプリのサーバーを起動します。

rails s 
rails s -p 3001

最後に、ブラウザで次を指定します。

foo.example.com:3000
bar.example.com:3001

3000番とかを消したい場合

80番ポートで起動させてください

sudo rails server --port=80

参考

local Rails with domain name? [duplicate]

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

【Rails】モデルの関連付けを行う

関連付けとは

Railsでは、モデルやテーブル同士の関係を設定することができます。
例えばUserモデルとTaskモデルの関係などを定義します。
(この場合、1人のユーザーが複数のタスクを持っている)

何がうれしいのか

必要な記述が少なく、シンプルになります。
TODOアプリを例に説明すると、以下のように記述が簡素化します。

タスク作成時

Task.create(title: "パンを買う", user_id: @user.id)

@user.tasks.create(title: "パンを買う")

ユーザー削除時

@tasks = Task.where(user_id: @user.id)
@tasks.each do |task|
  task.destroy
end
@user.destroy

@user.destroy

その他、以下のようなメソッドが使えるようになります。

メソッド 用途
task.user Micropostに紐付いたUserオブジェクトを返す
user.tasks Userのタスクの集合を返す
user.tasks.build(arg) userに紐付いた新しいTaskオブジェクトを返す
user.tasks.find_by(id: 1) userに紐付いていて、idが1であるタスクを検索する

実装してみる

簡単なTODOアプリを例に、UserモデルとTaskモデルの関連付けをします。
Ruby on Railsで簡単なアプリを作成
【Rails】ログイン機能を実装する

tasksテーブルにuser_idを追加

$ rails g migration Addカラム名Toテーブル名 カラム名:データ型でカラムを追加するマイグレーションファイルを作成できます。

$ rails g migration AddUserToTasks user:references

作成されるファイル

[timestamp]add_user_to_tasks.rb
class AddUserToTasks < ActiveRecord::Migration[5.1]
  def change
    add_reference :tasks, :user, foreign_key: true
  end
end

ここでは外部キー制約を持ったreference型user_idというカラムを追加し、user_idにはusersテーブルのidカラムに入っているデータだけを許可します。
(データベース上には、usersテーブルのidカラムの型であるinteger型として登録されます。)
外部キー (foreign key)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

$ rails db:migrate

has_manybelongs_toで関連付け

Taskモデルにbelongs_to、Userモデルにhas_manyを記述し、関連付けを行います。
その他default_scopeで取り出す時の順番を指定したり、dependent: :destroyでユーザーが削除された時にそのユーザーの持つタスクを全て削除するように設定できます。

/app/model/task.rb
class Task < ApplicationRecord
  belongs_to :user
  default_scope -> { order(created_at: :desc) }
.
.
end
/app/model/user.rb
class User < ApplicationRecord
  has_many :tasks, dependent: :destroy
.
.
end

タスク登録処理を修正

これまでTask.newで取得していたタスクオブジェクトをcurrent_user.tasks.buildで書き換えます。
(このアプリではcurrent_userという関数で現在ログイン中のユーザーオブジェクトを返すことができます。)

.
.
def create
    # @task = Task.new(task_params)
    @task = current_user.tasks.build(task_params)
    if @task.save
      flash[:success] = "タスクを追加しました。"
.
.

まだこのアプリにはユーザー削除の機能がないので、削除時の動作の実装は割愛しますが、@user.destroyでユーザーに紐づいた全てのタスクを削除できます。

参考

Active Record の関連付け - Rails ガイド

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

カレンダーを表示する (たのしいRuby 練習問題)

はじめに

以下の問題を解いている記事を読んで、自分もやってみようと思いました。

たのしいRuby 第5版から引用
[第20章 TimeクラスとDateクラス 練習問題]
Date クラスを使って、今月の1日と月末の日付と曜日を求め、次のような形式でカレンダーを表示させてください

      April 2015
 Su Mo Tu We Th Fr Sa
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29 30

私の場合は以下のように回答しました。

require "date"

today = Date.today

# header
puts "#{Date::MONTHNAMES[today.month]} #{today.year}".center 21
puts ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map{|d| "%3s"%d}.join

# body
current_day = today - today.day + 1
row = [nil] * current_day.wday
loop do
    row << current_day.day
    current_day = current_day + 1
    if current_day.wday == 0
        puts row.map{|n| "%3s"%n }.join
        row.clear
    end

    if current_day.month != today.month
        break
    end
end
    November 2019    
 Su Mo Tu We Th Fr Sa
                 1  2
  3  4  5  6  7  8  9
 10 11 12 13 14 15 16
 17 18 19 20 21 22 23
 24 25 26 27 28 29 30

参考

たのしいRuby 第5版
たのしい Ruby 第2版 第17章 Time クラスと Date クラス 練習問題(3)
カレンダー整形問題を色々なパターンで解いてみた

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

Ruby で平仮名を打つとエラーが発生した!!

rubyで平仮名を打つとエラーが発生していろんなことをしてみたが解決できなくて、

which ruby

をしてみると、

/usr/bin/ruby

とのことだった。

echo $PATH

をした後に

open ~/.bash_profile

をしまして、

下記2つのがあれば良いらしいが、よくないことに、全然違うことが書いてあった。
①export PATH="$HOME/.rbenv/bin:$PATH"
②eval "$(rbenv init -)"

.bash_profileの中身を削除して上記のものを書いて保存をした後に、

$ source ~/.bash_profile

をして反映させる。

which ruby

をしてみると、

/Users/自分の名前/.rbenv/shims/ruby

と言う感じでいけた。

ruby --version

をするとしっかりと反映されていた。

参考にした記事
https://qiita.com/SAYJOY/items/be06302ae8a2fac7f0c4

めちゃくちゃわかりやすかったです、ありがとうございましたww

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