20200730のRubyに関する記事は18件です。

Jekyllはカスタムプラグインを使うと色々できるよという話

この記事では、静的サイトジェネレーター「Jekyll」のカスタムプラグインで柔軟にサイトをいじる方法を紹介します。

もうちょっとピンポイントな記事もあります: JekyllでパーマリンクにFront Matterのカスタム変数を使う方法

そもそもJekyllとは?

Jekyllは、Ruby製のシンプルな静的サイトジェネレーターです。ブログなどを書けるような機能が備わっています(便利!)。.mdファイルや.htmlファイルなどを、Webサイトの表示に必要なファイル一式にして書き出してくれます。共通部分のレイアウトをまとめてかけたり、記事をマークダウン形式で書けたりと、そのままでもとても便利に使えます。

しかし、Rubyで書かれたプラグインを活用することで、さらに様々な実装ができます。

プラグインについて

プラグインは、gemやローカルの.rbファイルを読み込むことで、機能拡張ができるものです。
プラグインのインストール方法
プラグインのインストール方法(日本語)

"jekyll-multiple-languages-plugin"(多言語対応に便利なプラグイン) や 公式の"jekyll-sitemap"(サイトマップ生成に便利なプラグイン)など、公開されているプラグインを使うことができます。

今回は、自分で実装する「カスタムプラグイン」について紹介します。

カスタムプラグインの動かし方

ルートディレクトリに、_pluginsディレクトリを作り、その下に.rb(カスタムプラグインの中身)を置くことで使用できます。
※ちなみに、GitHub Pagesではカスタムプラグインが動きません orz

カスタムプラグインの種類

Generators、Converters、Commands、Tags、Filters、Hooks の6つがあります。

種類 カスタムするところ
Generators Webサイトのファイル一式を書き出す前の部分
Converters mdファイルなどを変換する部分
Commands Jekyllコマンドのサブコマンド
Tags Liquidタグ
Filters Liquidフィルタ
Hooks Jekyllでサイトを生成する際の各プロセス

Jekyllでは{% if statement %}のようにして生成処理時にLiquidタグを実行できます。また、{{ content | filter }} のようにしてフィルターが使えます。

各プラグインの詳しい書き方は公式サイトに載っているので参考にしてもらえればと思います。

実際に書いてみる

実際に、カスタムプラグイン(Hooks)を書いてみました。きれいなコードが書けなくてすみません(プルリクはこちらから)。https://mekurun.com/ という、子ども向けのプログラミング学習サイトを作る際に使用しているコードです。

ここでは、レンダリング後のimgタグのsrcを設定しています。今回は単純に正規表現でマッチした部分を置き換えているだけの単純な処理ですが、普通にRubyが書けるので様々な応用が効きそうです。

ちなみに、サイト高速化を目指しCloudinaryというCDNを利用するために、画像パスを場合に応じて置き換えているというコード内容になっています。

サイト高速化についての記事は、友人が書いたこちらの記事をぜひ: 【打倒!阿部寛のサイト】高校生が本気でサイト読み込み速度の最適化に取り組んでみた

要するに、各ページのレンダリング後のHTML(doc.output)を読んで、文字列の置き換え処理(素直にgsub)をしています。

Jekyll::Hooks.register [:pages, :documents], :post_render do |doc|
  # レンダリング後に実行

  out = doc.output
  if ENV["JEKYLL_ENV"] == "production" #本番環境の場合
    # 幅設定がないcloudinaryパス
    src = '\& src="https://res.cloudinary.com/nztm/image/fetch/c_fit,q_auto,f_auto/\1"'
    # 幅設定があるcloudinaryパス
    srcw = 'src="https://res.cloudinary.com/nztm/image/fetch/w_\2,c_fit,q_auto,f_auto,dpr_2/\1" \&'

    # data-srcをもとにパス設定
    out.gsub!(/data-src=[\"|\'](.*?)[\"|\']/, src)
    out.gsub!(/data-src=[\"|\'](.*?)[\"|\'].*data-width=[\"|\'](.*?)[\"|\']/, srcw)
  else #本番環境ではない場合
    src = '\& src="\1"'
    out.gsub!(/data-src=[\"|\'](.*?)[\"|\']/, src)
  end
  puts "#{doc.data["title"]}の画像パスを設定しました"
  doc.output = out
end

難しいところ

データ構造がわからないので、Jekyllのコードや他の方のプラグインを見ながら試行錯誤していました。今もあまり完全理解できていないため、ここでの説明は省かせていただきます。

まとめ

静的サイトジェネレーター「Jekyll」はRubyで様々な拡張ができて楽しいです。とはいえ、サイトビルド時のみいじれるので、インタラクティブなぬるぬるコンテンツは作れないです。そういう実装は素直にJavaScriptを触りましょう。

参考:
Jekyll
Jekyllで作るシンプルWebサイト 第1回 Jekyllとはなにか
Plugins | Jekyll

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

RSpecの単体テストにて"translation missing: ja.activerecord.○○"というエラーが出たときの対処法

Deviseを日本語化したときのエラー。エラー画面はこんな感じ。
image.png

◆原因

どうやらdeviseのエラーメッセージを日本語化したことによって本来Deviseで用意されているエラー文(英語)テスト実行時のエラー文(devise.ja.yml)に相違が出てしまい、エラーになってしまってるぽい。

◆解決策

巷に落ちているdevise.ja.ymlの内容もそれぞれ引用元によって異なるため、このエラーが出た際にはdevise.ja.ymlに記述を追加する必要がある。

実際に記述を追加していく。
今回の場合エラー分が
expected [translation missing: ja.activerecord.errors.models.user.attributes.name.blank] to include "can't be blank"
なので

config/locals/devise.ja.yml
ja:
  activerecord:
    errors:
      models:
        user:
          attributes:
            name:
              blank: "が入力されていません"

と追記してあげる。

これで無事にテストが通るように!

参考にした記事

Devise日本語化後の「translation missing」に対処する

translationmissing:ja.activerecord.errors.models.hoge.attributes.base.restrict_dependent_destroy.many の対処方法

Createの際のエラーUser translation missing: ja.activerecord.errors.models.company.attributes.user.required

deviseを日本語化する

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

Fabricateの使い方をまとめる

なんとなく使えてしまうFabricateですが、ふとしたきっかけでちゃんと理解したいなと思ったのでまとめました。

Fabricateとは

オブジェクト生成ライブラリです。
テストで使用するためのオブジェクトを簡単に作成できます。

使い方

非常にシンプルで、モデルの属性に無難なデフォルト値を設定します。
サンプルでpersonというオブジェクトを定義しました。

Fabricator(:person) do
  name "Niki"
  age 23
end

生成メソッド

生成メソッドは3種類あります。よく使うのはFabricate.buildFabricate

Fabriate(:person)
# newしてsaveしたものを返す
Fabricate.build(:person)
# newだけしてsaveはしない
Fabricate.attributes_for(:person)
# 属性のHashを返す

一見Fabricate.buildは必要なくない?となりますが、これは間違いです。
.buildを使うとインスタンスを保存しないので、specの処理時間の軽減につながります。

なので、.buildを使えない場合のみインスタンスを生成し、基本的には.buildを使うようにしています。

属性の値を明示する

定義したオブジェクトの属性の値を変えたい場合は以下の様にします。

person = Fabricate(:person, name: "Peru")
person.name = "Peru"

また、関連しているデータを紐づけることも簡単にできます。
例えば、bookモデルがbelong_topersonモデルに紐づいている場合は以下の様にしてFabricateで表現できます。

Fabricator(:book) do
  name "キングダム"
  price 1000
end

book = Fabricate.build(:book, person: Fabricate.build(:person))

これらは基本的な内容ですが、ちゃんと理解するとテストのコーディングスピードも上がると思います。他にも色々便利なメソッドがありますので暇があれば調べてみて下さい。
何かいいメソッドや書き方があれば、是非コメントください!

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

[Rspec]Fabricateの使い方をまとめる

なんとなく使えてしまうFabricateですが、ふとしたきっかけでちゃんと理解したいなと思ったのでまとめました。

Fabricateとは

オブジェクト生成ライブラリです。
テストで使用するためのオブジェクトを簡単に作成できます。

使い方

非常にシンプルで、モデルの属性に無難なデフォルト値を設定します。
サンプルでpersonというオブジェクトを定義しました。

Fabricator(:person) do
  name "Niki"
  age 23
end

生成メソッド

生成メソッドは3種類あります。よく使うのはFabricate.buildFabricate

Fabriate(:person)
# newしてsaveしたものを返す
Fabricate.build(:person)
# newだけしてsaveはしない
Fabricate.attributes_for(:person)
# 属性のHashを返す

一見Fabricate.buildは必要なくない?となりますが、これは間違いです。
.buildを使うとインスタンスを保存しないので、specの処理時間の軽減につながります。

なので、.buildを使えない場合のみインスタンスを生成し、基本的には.buildを使うようにしています。

属性の値を明示する

定義したオブジェクトの属性の値を変えたい場合は以下の様にします。

person = Fabricate(:person, name: "Peru")
person.name = "Peru"

また、関連しているデータを紐づけることも簡単にできます。
例えば、bookモデルがbelong_topersonモデルに紐づいている場合は以下の様にしてFabricateで表現できます。

Fabricator(:book) do
  name "キングダム"
  price 1000
end

book = Fabricate.build(:book, person: Fabricate.build(:person))

これらは基本的な内容ですが、ちゃんと理解するとテストのコーディングスピードも上がると思います。他にも色々便利なメソッドがありますので暇があれば調べてみて下さい。
何かいいメソッドや書き方があれば、是非コメントください!

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

[Rspec]Fabricateの基本的な使い方まとめ

なんとなく使えてしまうFabricateですが、ふとしたきっかけでちゃんと理解したいなと思ったのでまとめました。

Fabricateとは

オブジェクト生成ライブラリです。
テストで使用するためのオブジェクトを簡単に作成できます。

使い方

非常にシンプルで、モデルの属性に無難なデフォルト値を設定します。
サンプルでpersonというオブジェクトを定義しました。

Fabricator(:person) do
  name "Niki"
  age 23
end

生成メソッド

生成メソッドは3種類あります。よく使うのはFabricate.buildFabricate

Fabriate(:person)
# newしてsaveしたものを返す
Fabricate.build(:person)
# newだけしてsaveはしない
Fabricate.attributes_for(:person)
# 属性のHashを返す

一見Fabricate.buildは必要なくない?となりますが、これは間違いです。
.buildを使うとインスタンスを保存しないので、specの処理時間の軽減につながります。

なので、.buildを使えない場合のみインスタンスを生成し、基本的には.buildを使うようにしています。

属性の値を明示する

定義したオブジェクトの属性の値を変えたい場合は以下の様にします。

person = Fabricate(:person, name: "Peru")
person.name = "Peru"

また、関連しているデータを紐づけることも簡単にできます。
例えば、bookモデルがbelong_topersonモデルに紐づいている場合は以下の様にしてFabricateで表現できます。

Fabricator(:book) do
  name "キングダム"
  price 1000
end

book = Fabricate.build(:book, person: Fabricate.build(:person))

これらは基本的な内容ですが、ちゃんと理解するとテストのコーディングスピードも上がると思います。他にも色々便利なメソッドがありますので暇があれば調べてみて下さい。
何かいいメソッドや書き方があれば、是非コメントください!

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

【Sinatra】エラー解決 Rack::Flash::SessionUnavailable at /

はじめに

この記事の内容はSintaraでの開発中、rack-flash3を使ってフラッシュを表示させようとした時に発生した下記エラー、

Rack::Flash::SessionUnavailable at /
Rack::Flash depends on session middleware.

の解決方法について学んだ事を記しています。

解決への道のり

エラーを出したソースコードはこちら↓

require "rack-flash"

use Rack::Flash

エラー文はRack::Flashのセッションが利用できません、Rack::Flashはセッションミドルウェアに依存していますよ!ってな感じに訳せるかと思います。

という事でメンターさんにも聞いたりもしつつ、いろいろ調べて、READMEを見ていたら、ヒントがありました!

参考、引用)https://github.com/nakajima/rack-flash

If you're using Sinatra, you can use the flash hash just like in Rails:
(訳)Sinatraで使うとRailsと同じようにフラッシュハッシュを使えますよー、だと思う

#省略

require 'rack-flash'
#省略
enable :sessions
use Rack::Flash

#省略

enable :sessionsのところがセッションというワードが出てきてます、もしかしてこれ?

enable :sessionsとは、、、

実はSinatraではデフォルトでRackのセッション機能は無効化されているらしいです、
そして、enable :sessionsはセッションを有効にするための指定らしいです!

なるほどおお、絶対これだ!と思い、問題のコードに書き加えてページ更新してみました、、、がまたもエラー!

どうやら、、、

サーバーを再起動しないとなおりません!

この事に気づかず、僕はここからまた迷走してました笑

サーバーを再起動して無事解決〜♪

考察、学び

  • おそらく、Rack-flashはセッションを使ってフラッシュを保存している
  • Sinatraはセッション機能が無効化されていて、enable :sessionsを書いてサーバーを再起動しないと有効化されない
  • 再起動をしてみる、という事の大事さ

勉強になりました!

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

コントローラーでのデータ検索等

データの絞り込み

データの絞り込みは下記の構成

例)Task.all.first
Task = 起点
all = 絞り込み条件
first = 実行部分

起点

処理対象のモデルのクラス

絞り込み条件

メソッド 効果
where 特定のレコードの検索
order 検索結果の並び順を指定
joins 他のテーブルとのJOINを指定
group 指定したカラムの値を基準にデータをグループ化
select 指定したからむだけを属性として取得する
limit 取得個数を制限する
distinct 取得するカラムの値が一致しているデータは除外してデータを取得 
all 全件取得
none 何もヒットさせない

実行部分

メソッド 効果
find idを指定して取得 ない場合は例外 Task.find(1)
find_by 条件指定し取得 ない場合はnil Task.find_by(name:"mayu")
なし 他のテーブルとのJOINを指定
first 検索条件に合う最初のレコードに対応するオブジェクトを取得
last 検索条件に合う最後のレコードに対応するオブジェクトを取得
exisit? 検索条件に合うレコードの有無を取得
count 取得するカラムの値が一致しているデータは除外してデータを取得 
average COUNT関数を使って平均を取得
maximun AVG関数を使って最大値を取得
minimum AVG関数を使って最小値を取得
update_all 検索条件にあうレコードを全てインスタンス化せずに更新
delete_all 検索条件にあうレコードを全てインスタンス化せずに削除
destroy_all 検索条件にあうレコードを全てインスタンス化した上で削除

並び替え

tasks_controller
class TasksController < ApplicationControlller
 def index
  @tasks = current_user.tasks #新しい順で検索するには① 古い順で検索するには②
  @tasks = current_user.tasks.order(created_at: :desc) #①
  @tasks = current_user.tasks.order(created_at: :asc) #②

 end

scopeの活用

scopeを使い、クエリ用のメソッドの連続した呼び出し部分に名前をつけて、メソッドとして使用することができる

model
class Task < ApplicationRecord
 scope :recent, -> {order(created_at: :desc)} #recentという名で定義
end

#recentの使い方
tasks = Task.recent
task = Task.recent.first
task = Task.recent.last
tasks = current_user.tasks.recent
tasks = Task.where(user_id: [1,2,5]).recent

フィルタ

同じコードを複数記述していると変更を行う必要があるときに重複箇所全てに対して変更を行わなければいけない。フィルタを使って重複を避けることを心がける

tasks_controller
class TasksController < ApplicationController
 before_action :set_task, only: [:show, :edit, :update, :destroy]

 def show
 end
 def edit
 end
 def update
 end
 def destroy
 end

 private 
 def set_task
  @task = current_user.tasks.find(params[:id])
 end
end

URL表示

rails_autolinkと言うgemを使いURLを表示させる

gemfile
gem 'rails_autolink'
auto_link(simple_format(h(@task.description),{},sanitize: false, wrapper_tag: "div:))

参考|現場で使える Ruby on Rails 5速習実践ガイド

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

Controllerを作成しよう

Controllerを作成する

コントローラでは、ユーザーのリクエストを受けてModelと連携したり、
どの画面(View)を表示するかを制御しています。
ユーザーがURLにアクセスしたときに、リクエストを最初に受け取るのがコントローラです。
受け取ったリクエストを元にして、Modelからのデータを、対応するViewに渡して画面を表示させます。

具体的には、以下のような制御を行っています。

Modelとやり取りする
Viewに渡すインスタンス変数を定義する
表示するViewファイルを指定する

controllerを作成するのは以下を実行するだけです。
gはgenerateの略称です。

$ rails g controller コントローラ名

ただ、一つだけ注意があります。
コントローラー名は複数形になります。こんな感じです。

$ rails g controller homes

成功するとこんな感じになります。

[vagrant@localhost sample_app]$ rails g controller homes
Running via Spring preloader in process 29768
      create  app/controllers/homes_controller.rb
      invoke  erb
      create    app/views/homes
      invoke  test_unit
      create    test/controllers/homes_controller_test.rb
      invoke  helper
      create    app/helpers/homes_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/homes.coffee
      invoke    scss
      create      app/assets/stylesheets/homes.scss

間違えてControllerを作成してしまったら?

コントローラー名などを間違えて作成した場合は、以下のコマンドで消しましょう。

勘の良い人はわかるかもしれませんが、
dはdestroyを意味します。

[vagrant@localhost sample_app]$ rails d controller homes
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MVC(Model View Controller)を理解しよう

MVC(Model View Controller)とは?

railsはMVCという考え方で構成されています。
全てのコードを同じ場所に書いてしまうと、コードが複雑になるため、
railsでは、保守性を維持するために、Model、View、Controllerの3つの処理パターンに分けて管理しています。

model

データを扱う部分です。
データベースに対して、データの登録や取得、更新、削除などの処理を行います。

view

PCの画面に関わる部分です。
HTMLを読み込み、ブラウザに表示させるためのものです。

controller

modelとviewの中間に位置します。
ユーザーからのリクエストを受けて、Modelと連携したり、どの画面(View)を表示するのかといったことを制御します。

image.png

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

Railsでのアプリケーション作成

rails newで作成

railsで新規アプリケーションを作成するのは簡単です。
rails newコマンドだけで作成できてしまいます。

$ rails new アプリケーション名

もしアプリケーション名を間違えたら?

アプリケーション名を間違えて作成してしまっても安心です。
このコマンドでアプリケーションを消してしまいましょう。

$ rm -rf アプリケーション名

rmはremoveの略称です。
-rf remove recursively forceの略)はコマンドオプションの1つで、「フォルダ内のすべてのファイルとフォルダを削除する」を意味します。

作成したアプリケーションを確認

rails newコマンドでアプリケーションの作成が完了すると以下のような画面が出ます。
この画面が出たら、成功です。

[vagrant@localhost sample_app]$ rails new sample_app
      create  
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  Gemfile
        run  git init from "."
Initialized empty Git repository in /home/ec2-user/environment/sample_app/.git/
      create  package.json
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/javascripts/application.js
      create  app/assets/javascripts/cable.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]Sheets API v4のupdate_spreadsheet_valueでNameErrorが出たときの対処方法(uninitialized constant ActiveSupport::JSON (NameError))

Rubyからスプレッドシートのセル情報を読み書きしたくて、まず公式が提供しているQuickstartを試してみました。
Ruby Quickstart  |  Sheets API  |  Google Developers

Quickstartではシートのセル値を取得まであり、セルを更新(値を書き込む)は用意されておりませんでした。

なのでドキュメントやQiitaを参考に試したのですが、エラーが出たためその解決方法を書いていきたいと思います。

SheetsAPIの操作はQuickstartと同じgoogle-api-ruby-clientを使用しています。

uninitialized constant ActiveSupport::JSON (NameError)の対処方法

update_spreadsheet_valueにてセルの更新を行おうとしたときに、uninitialized constant ActiveSupport::JSON (NameError)が発生しました。

google-api-clientの中でto_jsonメソッドが使用されているのですが、to_jsonメソッドはActiveSupportのメソッドであり、ActiveSupportが入っていないことが原因でした。

Gemfileにgem 'activesupport'を追記し、update_spreadsheet_valueを使うファイルにrequire 'active_support'を追記することでエラーを解消できました。

Gemfile
...

gem 'google-api-client'
gem 'activesupport'

sample.rb
require 'google/apis/sheets_v4'
require 'active_support' # active_supportを読み込む

def updated_cells(values)
  spreadsheet_id = 'xxxxxxxxx'
  value_range_object = Google::Apis::SheetsV4::ValueRange.new(values: values)
  range = 'sheet!D2:D'
  result = service.update_spreadsheet_value(
    spreadsheet_id,
    range,
    value_range_object,
    value_input_option: 'RAW'
  )
  puts "#{result.updated_cells} cells updated."
end

実際のエラー内容

実際のエラーはこのような内容でした。

Traceback (most recent call last):
    11: from main.rb:25:in `<main>'
    10: from main.rb:12:in `main'
     9: from /myapp/src/sheets.rb:32:in `write_price'
     8: from /usr/local/bundle/gems/google-api-client-0.42.2/generated/google/apis/sheets_v4/service.rb:787:in `update_spreadsheet_value'
     7: from /usr/local/bundle/gems/google-api-client-0.42.2/lib/google/apis/core/base_service.rb:360:in `execute_or_queue_command'
     6: from /usr/local/bundle/gems/google-api-client-0.42.2/lib/google/apis/core/http_command.rb:99:in `execute'
     5: from /usr/local/bundle/gems/google-api-client-0.42.2/lib/google/apis/core/api_command.rb:66:in `prepare!'
     4: from /usr/local/bundle/gems/representable-3.0.4/lib/representable/json.rb:44:in `to_json'
     3: from /usr/local/bundle/gems/multi_json-1.15.0/lib/multi_json.rb:139:in `dump'
     2: from /usr/local/bundle/gems/multi_json-1.15.0/lib/multi_json/adapter.rb:25:in `dump'
     1: from /usr/local/bundle/gems/multi_json-1.15.0/lib/multi_json/adapters/json_common.rb:19:in `dump'
/usr/local/bundle/gems/activesupport-6.0.3.2/lib/active_support/core_ext/object/json.rb:42:in `to_json': uninitialized constant ActiveSupport::JSON (NameError)

実際に使用したファイル

実際に使用したファイルを参考に載せておきます。

sheets.rb
require "google/apis/sheets_v4"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"
require 'active_support'

class Sheets
  OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'.freeze
  APPLICATION_NAME = 'Sample App'.freeze
  CREDENTIALS_PATH = 'credentials.json'.freeze
  TOKEN_PATH = 'token.yaml'.freeze
  SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
  SPREADSHEET_ID = 'xxxxxxxxxxxxxxxxxx'.freeze

  attr_reader :service

  def initialize
    @service = setup_service
  end

  def symbols
    range = 'sheet!C2:C'
    response = service.get_spreadsheet_values(SPREADSHEET_ID, range)
    puts 'No data found.' if response.values.empty?

    response.values.flatten
  end

  def update_price_cells(values)
    value_range_object = Google::Apis::SheetsV4::ValueRange.new(values: values)
    range = 'sheet!D2:D'
    result = service.update_spreadsheet_value(
      SPREADSHEET_ID,
      range,
      value_range_object,
      value_input_option: 'RAW'
    )
    puts "#{result.updated_cells} cells updated."
  end

  private

    def setup_service
      service = Google::Apis::SheetsV4::SheetsService.new
      service.client_options.application_name = APPLICATION_NAME
      service.authorization = authorize
      service
    end

    def authorize
      client_id = Google::Auth::ClientId.from_file(CREDENTIALS_PATH)
      token_store = Google::Auth::Stores::FileTokenStore.new(file: TOKEN_PATH)
      authorizer = Google::Auth::UserAuthorizer.new(client_id, SCOPE, token_store)
      user_id = 'default'
      credentials = authorizer.get_credentials(user_id)
      if credentials.nil?
        url = authorizer.get_authorization_url(base_url: OOB_URI)
        puts 'Open the following URL in the browser and enter the ' \
             "resulting code after authorization:\n" + url
        code = gets
        credentials = authorizer.get_and_store_credentials_from_code(
          user_id: user_id, code: code, base_url: OOB_URI
        )
      end
      credentials
    end
end

まとめ

Railsを書いているときに何気なく書いていたto_jsonはActiveSupportのメソッドだということを知り勉強になりました。
ActiveSupportは様々なgemで使用されているようです。

なにかわからない部分などありましたら、質問やコメント、Twitterにてお願いします。

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

Rubyで「ある期間の毎月1日」をDateオブジェクトの配列として取得する方法

Rubyの小ネタです。

お題

2020年4月1日から2021年3月1日まで、1年分の「xxxx年xx月1日」のDateオブジェクトを配列として取得するコードを書いてください。

イメージとしてはこんな感じです。

# 見やすいように文字列の配列にしていますが、本当はDateオブジェクトの配列を取得するのがゴールです
[
  "2020-04-01",
  "2020-05-01",
  "2020-06-01",
  "2020-07-01",
  "2020-08-01",
  "2020-09-01",
  "2020-10-01",
  "2020-11-01",
  "2020-12-01",
  "2021-01-01",
  "2021-02-01",
  "2021-03-01"
]

解答例

こんな感じで書けます。

require 'date'
start_date = Date.parse '2020/04/01'
end_date   = Date.parse '2021/03/01'
(start_date..end_date).select{|d| d.day == 1}
#=> [#<Date: 2020-04-01 ((2458941j,0s,0n),+0s,2299161j)>, #<Date: 2020-05-01 ((2458971j,0s,0n),+0s,2299161j)>, (中略), #<Date: 2021-03-01 ((2459275j,0s,0n),+0s,2299161j)>]

RailsであればDate.parseの代わりにto_dateが使えます。

# Railsの場合
start_date = '2020/04/01'.to_date
end_date   = '2021/03/01'.to_date
(start_date..end_date).select{|d| d.day == 1}
#=> [Wed, 01 Apr 2020, Fri, 01 May 2020, (中略), Mon, 01 Mar 2021]

むりやり1行で書くとこんな感じ。(可読性がイマイチなのであまりお勧めしませんが)

# Railsの場合
Range.new(*%w[2020/04/01 2021/03/01].map(&:to_date)).select{|d| d.day == 1}
#=> [Wed, 01 Apr 2020, Fri, 01 May 2020, (中略), Mon, 01 Mar 2021]

いったん335日分のDateオブジェクトの配列を作ってから、毎月1日のDateオブジェクトだけをselectするので若干処理効率が悪いところはありますが、何十年、何百年という期間を対象にしないのであれば、たぶん大きな問題にはならないんじゃないかなー、と思っています。

以上、Rubyの小ネタでした!

おまけ

Railsのnext_monthメソッドとRuby 2.7で導入されたEnumerator.produceを使って、無駄なDateオブジェクトを作らない処理を考えてみました。これもなかなか良さそうです。

# Rails + Ruby 2.7
start_date = '2020/04/01'.to_date
end_date   = '2021/03/01'.to_date
Enumerator.produce(start_date, &:next_month).take_while{|d| d <= end_date}
#=> [Wed, 01 Apr 2020, Fri, 01 May 2020, (中略), Mon, 01 Mar 2021]

素のRubyでも>> 1を使えば1ヶ月後のDateオブジェクトが取得できるので、Ruby 2.7単体でこういう書き方もできます。

# Ruby 2.7
require 'date'
start_date = Date.parse '2020/04/01'
end_date   = Date.parse '2021/03/01'
Enumerator.produce(start_date){|d| d >> 1}.take_while{|d| d <= end_date}
#=> [#<Date: 2020-04-01 ((2458941j,0s,0n),+0s,2299161j)>, #<Date: 2020-05-01 ((2458971j,0s,0n),+0s,2299161j)>, (中略), #<Date: 2021-03-01 ((2459275j,0s,0n),+0s,2299161j)>]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで開発中にLocalのMySQLのバージョンを8系から5.7に変更したらいろいろハマった

現在Railsでポートフォリオ作成中のたか(@ktkr7195)です。

Sequel Proを使ってDBの情報を参照しようとしたら、MySQL8系だと繋がらない問題に打ち当たり、バージョンを5.7にすれば治るらしいという記事を拝見し、変更したら事件が起きました。

環境

  • Rails 5.2.4
  • macOS 10.15.5
  • MySQL 5.7
  • ruby 2.5.1

状況

まずHomebrewでbrew install mysqlでMySQLをインストールしてローカルで開発をしていました。おそらく、バージョン指定しないと最新の8系がインストールされます。rails db:createrails db:migrate等無事に成功し、アプリケーションが表示されるようになりました。開発を進めていく中でDBを参照することが多くなったのでSequel Proを使おとしたら弾かれてしまいこちらの記事を参考に修正したらrails db:migrateができなくなったのでMySQLのバージョンを落とすことに。

手順

①Mac MySQLをきれいにする
まずはこちらの記事を参考にMySQLをきれいにして、インストールし直す。この際自分はこちらを見て5.7をインストールしました。

②bundle doctorで原因を調べる
こちらの記事を参考に。

感想

自分の場合は上の手順で解決しましたが、ここまで来るのに結構時間を使ってしまったので、同じような境遇の人がいたら参考になれば嬉しいです。

参考

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

Ruby on rails とは?

Ruby on railsとは

Ruby on railsはWebアプリケーションを開発するためのフレームワークです。

「フレームワーク」は「ひな形」のことで、アプリケーション開発に必要な機能を簡単な手順で
設定できるようになっています。
たとえば、Webサイトを制作する際、「ブログ」機能を使うと、HTMLやCSSを一つひとつ書かなくてもサイトが完成します。
ブログには、あらかじめ必要な機能が用意されているので、それに従ってテキストや画像を組み込むだけで完成できます。

同様に、フレームワークでも必要な機能があらかじめ用意されているので、
ルールに従って記述していくだけで、プログラムを一つひとつ書くよりも効率的にWebアプリケーションを開発できます。
フレームワークは他にもありますが、Rubyでは、Ruby on Railsを利用するのが一般的です。

Ruby on railsの特徴

1.生産性が高い
あらかじめ基本的な構造が組まれているので、必要最低限のプログラミングだけで済ませられます。
記述するコード量は、フレームワークを使わなかった場合とではまったく違います。よりコンパクトに記述できるので、機能開発の生産性も向上します。

2.メンテナンスが容易
フレームワークには、記述ルールがありますので、
ルールに従ってコードを書くことで、一貫性を保ちやすくなります。

Ruby on railsの設計理念

Ruby on railsには2つの設計理念があります。

1.DRY(don't repeat yourself)
DRYは「同じ記述を繰り返さない」を意味します。
同じコードや似たようなコードがあちこち散らばると、メンテナンスや修正がしにくくなります。

2.CoC(Convention over Configuration)
Cocは「設定より規約」を意味します。
開発者が考えたり決定すべきことを極力少なくして、ロジックに集中させる思想のことです。
アプリケーションの設定を行うファイルや項目は、増えれば増えるほど仕組みが複雑化していきます。
CoCの思想を徹底することで、アプリケーションの実装に集中できるようになります。

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

プロを目指す人のためのRuby入門のためにRuby2.4.1をインストールする

はじめに

プロを目指す人のためのRuby入門を学習するためにRuby2.4.1を入れます。

環境

  • macOS Catalina Version 10.15.6
  • rbenv 1.1.2
  • Ruby 2.4.1

条件

  • rbenvがインストール済みあること
  • 特定のフォルダのみRuby 2.4.1を有効にしたい場合

手順

  1. Ruby 2.4.1をインストールする rbenv install 2.4.1 ※結構時間がかかります。
  2. Ruby 2.4.1を有効にしたいフォルダを作る (例)touch ./ruby-book-codes
  3. フォルダに移動する (例)cd ./ruby-book-codes
  4. Ruby 2.4.1を有効にする rbenv local 2.4.1

Enjoy Ruby life!!

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

Kinx アルゴリズム - ハノイの塔

Kinx アルゴリズム - ハノイの塔

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。「プログラム=アルゴリズム+データ構造」。アルゴリズムの実装例をご紹介。

元ネタは「C言語による(30年経っても)最新アルゴリズム事典」。今回はライフゲームです。

最新アルゴリズム事典にはこういうのも結構載ってる。パズル的な。

Kinx では最初のころにこのサンプルコードを書いて、再帰関数の確認に使った。良いテストになった。

ハノイの塔

Wikipedia より

以下のルールに従ってすべての円盤を右端の杭に移動させられれば完成。

  • 3本の杭と、中央に穴の開いた大きさの異なる複数の円盤から構成される。
  • 最初はすべての円盤が左端の杭に小さいものが上になるように順に積み重ねられている。
  • 円盤を一回に一枚ずつどれかの杭に移動させることができるが、小さな円盤の上に大きな円盤を乗せることはできない。

ソースコード

function movedisk(n, a, b) {
    if (n > 1) movedisk(n - 1, a, 6 - a - b);
    System.println("円盤 %d を %d から %d に移す" % n % a % b);
    if (n > 1) movedisk(n - 1, 6 - a - b, b);
}

const N = 4;
System.println("円盤 %d 枚を柱 1 から柱 2 に移す方法は"
        "次の %u 手です." % N % ((1 << N) - 1));
movedisk(N, 1, 2);

結果

円盤 4 枚を柱 1 から柱 2 に移す方法は次の 15 手です.
円盤 1 を 1 から 3 に移す
円盤 2 を 1 から 2 に移す
円盤 1 を 3 から 2 に移す
円盤 3 を 1 から 3 に移す
円盤 1 を 2 から 1 に移す
円盤 2 を 2 から 3 に移す
円盤 1 を 1 から 3 に移す
円盤 4 を 1 から 2 に移す
円盤 1 を 3 から 2 に移す
円盤 2 を 3 から 1 に移す
円盤 1 を 2 から 1 に移す
円盤 3 を 3 から 2 に移す
円盤 1 を 1 から 3 に移す
円盤 2 を 1 から 2 に移す
円盤 1 を 3 から 2 に移す

おわりに

こちらも C 言語版ほぼそのままです。GitHub は英語基準なのでこれまで文言を英語に直していましたが、一応日本語でも動くので、今回の上記のソースコードは日本語のままにしました。ただし GitHub に例としてコミットしているものは英語にしてあります。

ではまた、次回。

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

Rubyで外部コマンドを実行する

概要

Rubyで外部コマンドを実行するときの関数について、それぞれ実行して試しました。
自分用メモです。

動機

安全なWebアプリケーションの作り方 のOSコマンド・インジェクションの項で、
Rubyの場合どうなるか興味を持った

実行環境

Ruby2.5.1

試してみた

  • Kernel.#system

systemはsystem(コマンド)のように直接コマンドを打つことと、
system(コマンド, パラメータ)のように指定することができる

子プロセスが終了ステータス 0 で終了すると成功とみなし true を返します。それ以外の終了ステータスの場合は false を返します。コマンドを実行できなかった場合は nil を返します。

引数のコマンドをサブプロセスで実行するのが特徴。

$system("echo test")
test
=> true

$system("echo test ; echo test2")
test
test2
=> true
$system("/bin/echo","test")
test
=> true

$system("/bin/echo","test; echo test2")
test; echo test2
=> true

後者の場合、パラメータ内のセミコロンが文字列として処理されている。
OSコマンドインジェクションの対策に有効。

  • Kernel.#exec

systemと似ている。

プロセスの実行コードはそのコマンド(あるいは shell)になるので、起動に成功した場合、このメソッドからは戻りません。

こちらは実行中のプロセスが置き換わるので、返り値を持たない。

$exec("echo test ; echo test2")
test
test2
$exec("/bin/echo","test; echo test2")
test; echo test2

外部コマンドが実行できる(できてしまう)例

他の関数で外部コマンドを実行することはあるのだろうか?

  • Kernel.#open
$open('|/bin/echo test;').read
=> "test\n"

このように、引数の中にパイプを組み込むことで外部コマンドを実行できてしまう。

File.openを使えばエラーが出る。

$File.open('|/bin/echo test;').read
Errno::ENOENT: No such file or directory @ rb_sysopen - |/bin/echo test;

まとめ

systemやexecといった関数を使うときはOSコマンドインジェクションに配慮して実装をする
→外部から入力された文字列をコマンドラインのパラメータに渡さない
→system関数に第二引数を追加する

open関数は外部コマンドを実行される危険があるので、File.openに置き換えるのが望ましい

参考ページ

Ruby リファレンスマニュアル
https://docs.ruby-lang.org/ja/latest/doc/index.html

Rails セキュリティガイド
https://railsguides.jp/security.html

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

Rubyとは?

Rubyとは?

Rubyは「Webアプリケーション」を作るためのプログラミング言語です。

ここでWebアプリケーションとWebサイトの違いが分からない人向けに説明しておくと、
Webサイトとは、訪問者が見ることしかできないものです。
俗に言う、ホームページと言うやつですね。
WebサイトはHTML,CSSを用いて作られます。

一方で、Webアプリケーションは訪問者が見る+操作(検索や登録など)できます。
例えば、Amazonでユーザー情報を登録したり、商品を検索したりしますよね。
それをできるものがWebアプリケーションです。

Rubyの特徴とは?

1.スクリプト言語である
スクリプト言語とは、簡単に言うと、記述したプログラムをすぐに実行して、
動きを確認できる言語です。

Ruby以外のプログラミング言語(例えば、JavaやCなど)は、
プログラミングを実行するには、コンパイルする必要があります
(コンパイルとは、人間が書いたソースコードを、機械が読み取れるように変換する作業)。
Rubyは、記述したコードをすぐに実行・確認できるので、
円滑にプログラミングを進められます。

2.記述がシンプルである
Rubyは、可読性(読みやすさ)を重視しています。そのため、Javaなどに比べて、
シンプルにプログラミングを行えます。
実際のコードで、JavaとRubyのコードを比較してみましょう。

◆JAVA

public class Main {
  public static void main(String[] args){
    System.out.println("Hello World");
  }
}

◆Ruby

puts "Hello World"

Rubyの方が圧倒的にコードの量が少ないことがわかると思います。
このようにRubyはシンプルで書きやすい言語なのです。

3.日本語の文献が多い

Rubyは日本人によって作られたプログラミング言語なので、国内でも非常に人気のある言語となっています。
そのため、エラーなどが出ても、google先生に聞くと、大体は出てきます。
しかし、本当にたまに何も出てこない時があります。
そのときは、誰かに聞くしかありません(笑)

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