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

タクシー数をRubyで調べてみた

不思議な数をRubyで検証シリーズ第2弾!(あと何回続くんだ?)

前回の第1回はこちら↓
ある範囲内の整数にカプレカ数があるかRubyで調べてみた

今回はタクシー数です。
前回同様Youtubeから題材をいただきました。

予備校のノリで学ぶ「大学の数学・物理」
ラマヌジャンの天才エピソードでおなじみ【タクシー数】

タクシー数とは

n 番目のタクシー数(タクシーすう、taxicab number、Ta(n)もしくはTaxicab(n)と表記される)とは、2つの立方数の和として n 通りに表される最小の正の整数と定義される。
「タクシー数」と言う名前はハーディが乗ったタクシーの番号1729についてそれがTa(2)であることをシュリニヴァーサ・ラマヌジャンが指摘したエピソードから来ている

引用元:Wikipedia

立法数とはある数を3乗した数のことです。
平方数が2乗なのでその進化系ですね。

スクリーンショット 2020-07-04 18.31.02.png

そしてタクシー数は立法数の和(足し算)で何通りかに表されるものらしい

調べてみた

内容: x^3 + y^3でn通りに表される最小の正の整数(x, yの組み合わせの出力を含む)
条件: (1 ≦ x ≦ y ≦ 500)

taxi.rb
def safe_invert(orig_hash)
  orig_hash.each_key.group_by { |key| orig_hash[key] }.sort.to_h
end

hash = {}

(1..500).each do |y|
  (1..500).each do |x|
    hash.store([x, y], x ** 3 + y ** 3)
    if x == y
      break
    end
  end
end

taxi = []
n = 1

safe_invert(hash).each do |k, v|
  if v.size == n
    taxi.push("Ta(#{n}) => #{k}: #{v}")
    n += 1
  end
end

puts taxi
Ta(1) => 2: [[1, 1]]
Ta(2) => 1729: [[9, 10], [1, 12]]
Ta(3) => 87539319: [[255, 414], [228, 423], [167, 436]]

立法数の和として3通りで表せる最小の数が既に8753万とは、かなり大きな数ですね。

コード説明

順を追って説明します
簡略化するために範囲を小さくしてみていきましょう

条件: (1 ≦ x ≦ y ≦ 5)

taxu.rb
hash = {}

(1..5).each do |y|
  (1..5).each do |x|
    hash.store([x, y], x ** 3 + y ** 3)
  end
end

p hash
{[1, 1]=>2, [2, 1]=>9, [3, 1]=>28, [4, 1]=>65, [5, 1]=>126,
[1, 2]=>9, [2, 2]=>16, [3, 2]=>35, [4, 2]=>72, [5, 2]=>133,
[1, 3]=>28, [2, 3]=>35, [3, 3]=>54, [4, 3]=>91, [5, 3]=>152,
[1, 4]=>65, [2, 4]=>72, [3, 4]=>91, [4, 4]=>128, [5, 4]=>189,
[1, 5]=>126, [2, 5]=>133, [3, 5]=>152, [4, 5]=>189, [5, 5]=>250}

まずxyに1〜5までの整数を与え.

それぞれ3乗した数を足した答え(和)を総当たりに調べてhashに追加していきましょう。
Hashクラスのstoreメソッドは、第一引数にkey、第二引数にvalueとする要素を追加するメソッドです。

hashの長さは 5 × 5 = 25通りになります。

スクリーンショット 2020-07-04 18.43.14.png

表を見ていただいてお分かりの通り、xとyを入れ替えて求められる答えは同じです。
(x, y) = (2, 5)のときも、(x, y) = (5, 2)のときも答えは133
これは2通りとは数えません。1通りです。
なので表の中で求める必要があるのはこの範囲

スクリーンショット 2020-07-04 18.57.40.png

ということで、xyが同じ場合はhashに要素を追加したらループを抜けるようにbreakします

taxu.rb
hash = {}

(1..5).each do |y|
  (1..5).each do |x|
    hash.store([x, y], x ** 3 + y ** 3)
    if x == y
      break
    end
  end
end

p hash
{[1, 1]=>2,
[1, 2]=>9, [2, 2]=>16,
[1, 3]=>28, [2, 3]=>35, [3, 3]=>54,
[1, 4]=>65, [2, 4]=>72, [3, 4]=>91, [4, 4]=>128,
[1, 5]=>126, [2, 5]=>133, [3, 5]=>152, [4, 5]=>189, [5, 5]=>250}

 
 
 
続いて後半
特にメソッドの中が複雑です。

hashの中はkeyに[x, y]という配列、valueに立法数の和という要素が入っていましたが、
safe_invertメソッドを通すと、重複を考慮した上でkeyとvalueを逆にしたハッシュを作ります。

taxi.rb
def safe_invert(orig_hash)
  orig_hash.each_key.group_by { |key| orig_hash[key] }.sort.to_h
end

p safe_invert(hash)
{2=>[[1, 1]], 9=>[[1, 2]], 16=>[[2, 2]], 28=>[[1, 3]], 35=>[[2, 3]],
54=>[[3, 3]], 65=>[[1, 4]], 72=>[[2, 4]], 91=>[[3, 4]], 126=>[[1, 5]],
128=>[[4, 4]], 133=>[[2, 5]], 152=>[[3, 5]], 189=>[[4, 5]], 250=>[[5, 5]]}

each_key.group_by { |key| orig_hash[key] }で各keyをvalueに、
orig_hash[key](つまりvalue)をkeyとするハッシュを作ります。
key valueの反転です。

難しい発想ですがこちらを参考にしました
Hash#invert (Ruby 2.7.0 リファレンスマニュアル)

そしてsortでkeyの昇順に並べ替えると同時に配列に変換されてしまうので、
.to_hでハッシュに戻します。

x, yが1〜5までではこの処理のメリットがいまいちピンとこないと思いますが、
Ta(2)である1729が出現するよう範囲を1〜12まで広げると、出力結果でこのように…

{ ......, 1729=>[[9, 10], [1, 12]], ......}

valueの配列の長さが2つになり、1729の立法数の和としての表し方が2通りあることがわかりますね。

taxi.rb
taxi = []
n = 1

safe_invert(hash).each do |k, v|
  if v.size == n
    taxi.push("Ta(#{n}) => #{k}: #{v}")
    n += 1
  end
end

puts taxi

最後に出来上がったハッシュに対してeach
valueの配列の長さが1から順に最初にマッチしたものを空の配列taxiに追加し、
結果を出力して終了です。

余談

Wikipediaを見て知ってしまったのですが
Ta(4)は範囲を1〜19000まで広げないと出てきません。

試しに(1..19000)で実行してみましたが、
数分待っても処理が終わらず断念。

やはりRubyは網羅的な検証には向かないかも:sweat:

 

もっといい書き方がありましたらコメントで教えてください!

(2020/07/05 追記)
コメントにて改良版をご提案いただきました!

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

【Ruby】条件分岐について[第一回]

本日より学習したことのアウトプットや備忘録を兼ねて、記事を書いていきます。
最初のうちは、基礎的な内容を中心に考えています。

記事内容に誤りなどございましたら、指摘いただければ幸いです。

本日はRubyの条件分岐一回目について、書いていきたいと思います。

if式

ある条件式が真の場合、if式の処理が実行されます。
ポイントとして「false」と「nil」は偽として判定され、それ以外は真と判定されるという点です。

if 条件式 then
  # 条件式が真の場合の処理
end

※thenは省略可能で、省略するのが一般的なようです。以後の項目は省略して記述します。

else式

if式の条件式が偽の場合の処理を実行します。

if 条件式
  # 条件式が真の場合の処理
else
  # 条件式が偽の場合の処理
end

elsif式

elsif式を使うことでより多くの分岐を記述できます。

if 条件式1
  # 条件式1が真の場合の処理
elsif 条件式2
  # 条件式2が真の場合の処理
else
  # いずれの条件式も偽の場合の処理
end

if式やelse式は値を返す

Rubyの多くの式は値を返すことができ、if式やelse式も同様に値を返すことができます。
ifやelseの最後の値が戻り値となります。

result = if 条件式
           '条件式が真です'
         else
           '条件式が偽です'
         end

条件式が真の場合、resultには'条件式が真です'という文字列が格納されます。
条件式が偽の場合、resultには'条件式が偽です'という文字列が格納されます。

unless式とelse式

if式と異なり、偽の場合に処理を実行します。
unless式の場合はelse式は使えますが、elsif式は使えません。
また、条件が複雑になると分かりにくくなる印象で、if式を使うかは条件次第で検討した方が良いと感じます。

unless 条件式
  # 条件式が偽の場合の処理
else
  # 条件式が真の場合の処理
end

今回は以上となります。

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

rails s 実行時のCould not find a JavaScript runtime.のエラー対応方法

macOSのバージョンを上げてしまい、開発中のRailsアプリのローカル環境でローカルサーバーが起動出来なくなり、以下のようなエラーが発生したため、忘備録のために投稿します。

macOS Catalina バージョン 10.15.5
環境:Ruby,Ruby on Rails,JavaScript

エラー内容

Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)

解決方法

Node.jsの最新版をインストールする。
以上です。これだけでした。
スクリーンショット 2020-06-22 3.55.41.png

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

Rails5.2から導入されたcredentials.yml.encの使い方

Rails5.2では新規アプリを作成した時にconfig/secrets.ymlが生成されず
代わりにconfig/credentials.yml.encが生成されるようになったみたいです。

既に本番環境などでRailsの古いバージョンで運用していてRails5.2にアップグレードする場合は
config/secrets.ymlではなく、config/credentials.yml.encの仕組みが使われるみたいです。

Rails5.2までは従来のconfig/secrets.ymlを使用するやり方などもあるみたいですが
Rails6アップグレードのことも考えconfig/credentials.yml.encを導入してみましたので
忘備録のために投稿します。

1. credentials.yml.encとmaster.keyの生成と設定

credentials.yml.encは直接エディタから編集する事はできないので、ターミナルでエディタを指定して作成、編集する。

$ EDITOR=vim bundle exec credentials:edit

ちなみに、.bash_profileなどに以下のように設定しておけば、EDITOR="vim"の指定は不要になります。

~/.bash_profile
$ echo 'export EDITOR="vim"' >> ~/.bash_profile
$ source ~/.bash_profile

$ bundle exec credentials:edit

上記のコマンドを実行すると、config/credentials.yml.encとconfig/master.keyが生成される。
config/master.keyはgitで管理しないため必ず.gitignoreに追加する。

.gitignore

# Ignore master key for decrypting credentials and more.
/config/master.key

生成されたconfig/credentials.yml.encを以下のように修正する。

config/credentials.yml.enc

# aws:
#   access_key_id: 123
#   secret_access_key: 345

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base:
  • 今回はsecret_keyだけ設定する。AWSの設定は既存のままいくのでコメントアウトのまま
  • 上記の画面にawsのaccess_key_idやsecret_access_key、その他APIキーなどを入力すれば暗号化されて保存される
  • 環境変数など使用せず、直接入力して問題ない
  • シングルクオーテーションやダブルクオーテーションは不要

本番環境ではcredentialを使うので config/environments/production.rb で次の変数にtrueをセットする

config/environments/production.rb

config.require_master_key = true

2.Capistranoによる自動デプロイ

本番環境のshared/configにmaster.keyを配置しないとデプロイできない。
本番環境のサーバーにscpコマンドなどで、config/master.keyを転送する。

$ scp -i ~/.ssh/example.pem config/master.key ec2-user@x.x.x.x:/var/www/AppName/shared/config/master.key

ex. /var/www/AppName/shared/config/master.key

もしくは、本番環境サーバーのbashファイルにRAILS_MASTER_KEY環境変数を設定する。
Capistranoは.bash_profileに書いても読み込まれないので、~/.bashrcに記載する必要がある。
config/master.keyをRAILS_MASTER_KEYとしてexportします。つまり、.bashrcに以下のように設定する。

~/.bashrc
export RAILS_MASTER_KEY='XXXXXXXXXXXXXXXXXXX'

次に、config/deploy.rbでデプロイ時の設定ファイルにシンボリックリンクを指定する。

set :linked_files, fetch(:linked_files, []).push('config/master.key')

3.Capistranoを使ってデプロイできれば完了。

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

Rails5でECサイトを作る② ~Bootstrap4設定、Controller・アクション定義~

はじめに

架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る①の続きです。
必要なControllerとViewを揃え、アクションも定義だけ書いて、最低限のページ遷移ができるようにします。

ソースコード

https://github.com/Sn16799/bakeryFUMIZUKI

Controller

コントローラを一気に作ります。Viewも伴うアクションは、ここでまとめて作ってしまいます。

Customerサイト

$ rails g controller addresses edit index
$ rails g controller cart_items index
$ rails g controller customers edit show withdraw
$ rails g controller genres index show
$ rails g controller homes about top
$ rails g controller order_items index
$ rails g controller orders confirm index new show thanks
$ rails g controller products index show

Adminサイト

$ rails g controller admin::customers edit index show
$ rails g controller admin::genres edit index
$ rails g controller admin::homes top
$ rails g controller admin::order_items
$ rails g controller admin::orders index show
$ rails g controller admin::products edit index new show
$ rails g controller admin::searches search

アクション

ViewのないアクションをControllerファイルに記述していきます。
詳しい内容は後で書くので、今は一通りアクションを定義して、ページ遷移できることを優先します。

Customerサイト

app/controllers/addresses_controller.rb
  def create
  end

  def update
  end

  def destroy
  end
app/controllers/cart_items_controller.rb
  def create
  end

  def update
  end

  def destroy
  end

  def destroy_all
  end
app/controllers/customers_controller.rb
  def udpate
  end

  def withdraw_done
  end
app/controllers/order_items_controller.rb
  def new
  end
app/controllers/orders_controller.rb
  def create
  end

Adminサイト

app/controllers/admin/customers_controller.rb
  def update
  end
app/controllers/admin/genres_controller.rb
  def create
  end

  def update
  end
app/controllers/admin/order_items_controller.rb
  def update
  end
app/controllers/admin/orders_controller.rb
  def update
  end
app/controllers/admin/products_controller.rb
  def create
  end

  def update
  end

Bootstrap4 読み込み

gem(bootstrap, jquery-rails)のインストールについては前回行ったので割愛します。

app/assets/stylesheets/application.cssapplication.scssにリネームしておきます。
そのファイル内にBootstrapの読み込みを記述します。

appseets/stylesheets/application.scss
@import 'bootstrap';

javascriptファイルにも同様に記述を行います。

app/assets/javascripts/application.js
//= require rails-ujs
//= require activestorage
--------------------------------
//= require jquery3
//= require popper                #ここを追記
//= require bootstrap-sprockets
--------------------------------
//= require_tree .

これでBootstrapを利用できるようになりました。試しに適当なところで使ってみましょう。

app/views/homes/top.html.erb
<h1>Homes#top</h1>
<p>Find me in app/views/homes/top.html.erb</p>

<%= link_to 'ABOUT', homes_about_path, class: 'btn btn-info' %>

localhost:3000/homes/top
homes_top.jpg

正しくページが表示され、"ABOUT"と書かれたリンクがボタンになりました! きちんとBootstrapが適用されていますね。
containerなどの枠組みをまだ何も設定していないので、全体的におそろしく端に寄ってしまいました。後ほど直しますが、今はとりあえず気にしないことにします。

後記

ひとまずRoutingとControllerを作ったので、必要なページは全て表示できるようになりました。次の記事から、具体的な機能の実装に入れそうです。
ところで、ページのデザインをどんな感じにするか、まだ迷っています。基本的にはBootstrapを下地としたレスポンシブ対応の設計にする予定ですが、おしゃれなCSSに挑戦するのも悪くないなぁと思ったり……。
今更ながら「パン屋の温かみあるイメージ」と「スタイリッシュな今風のサイト」が全然結び付かず、テーマ選びを間違えた感じがしているのですが、写真素材がすでに手元にあるのでこのまま進めます。
果たして、webサイトを違和感なく仕上げられるのか? 次回へ続く!

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

Crystal というプログラミング言語について

はじめに

まず Crystalとはなんぞや!ですが、簡単に言えばrubyの構文を使ってC言語みたいな実行速度でプログラムが動くように設計されたプログラミング言語です。今回は、このcrystalの魅力を簡単にご紹介できればと思い記事にいたしました。

実行速度の確認

まず、crystalを入れていない方は公式サイトから環境に合わせてインストールしてください。
その後で、適当なところにcrystalフォルダを作成します。
続けて、crystalフォルダの中にtest.crを作成し、中身を以下のように編集してください。

puts "Hello World!"

見ていただくと分かるように、文字を表示するプログラムです。
これをrubyのように実行すると、実行速度は次の通りになります。

time crystal test.cr
→ Hello world!
→ crystal test.cr  0.85s user 0.27s system 134% cpu 0.833 total

今度は、test.crをコンパイル(一度にファイルの中身を全てパソコンが読み込める状態に変換すること)を行ってから実行すると、次の通りとなります。

crystal build test.cr --release
time ./test 
→ Hello world!
→ ./test  0.00s user 0.00s system 2% cpu 0.263 total

比較すると、前者が0.85秒だったのに対して、後者は0.00秒と最低でも10倍以上速くなっていることが分かります。

ここまでのことはこちらのサイトにも記載されていますので、さらに知りたい方はこちらもご覧いただけたら良いかと思います。

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

【Rails】FullCalendarを用いたスケジュール管理昨日の実装

目標

ezgif.com-video-to-gif (1).gif

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

下記実装済み。

Slim導入
Bootstrap3導入
Font Awesome導入
ログイン機能実装
投稿機能実装

実装

1.Gemを導入

Gemfile
# 追記
gem 'jquery-rails'
gem 'fullcalendar-rails'
gem 'momentjs-rails'
ターミナル
$ bundle

2.application.scssを編集

application.scss
 *= require_tree .
 *= require_self
 *= require fullcalendar /*追記*/

3.application.jsを編集

application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery
//= require moment // 追記 
//= require fullcalendar // 追記
//= require_tree .

4.JavaScriptファイル作成・編集

ターミナル
$ touch app/assets/javascripts/calendar.js
calendar.js
$(function() {
  function eventCalendar() {
    return $('#calendar').fullCalendar({});
  }

  function clearCalendar() {
    $('#calendar').html('');
  }

  $('#calendar').fullCalendar({
    events: '/events.json',

    titleFormat: 'YYYY年 M月',
    dayNamesShort: ['', '', '', '', '', '', ''],

    header: {
      left: '',
      center: 'title',
      right: 'today prev,next',
    },

    defaultTimedEventDuration: '03:00:00',
    buttonText: {
      prev: '',
      next: '',
      prevYear: '前年',
      nextYear: '翌年',
      today: '今日',
      month: '',
      week: '',
      day: '',
    },
    timeFormat: 'HH:mm',
    eventColor: '#63ceef',
    eventTextColor: '#000000',
  });
});

【解説】

① turbolinks対策の為、カレンダーを読み込む関数と削除する関数をそれぞれ用意する。

function eventCalendar() {
  return $('#calendar').fullCalendar({});
}

function clearCalendar() {
  $('#calendar').html('');
}

② 日本語で表示する。

// カレンダー上部を年月で表示させる
titleFormat: 'YYYY年 M月',

// 曜日を日本語表示
dayNamesShort: ['', '', '', '', '', '', ''],

③ ボタンのレイアウトを整える。

header: {
  left: '',
  center: 'title',
  right: 'today prev,next',
},

④ イベントの設定をする。

// 終了時刻がないイベントの表示間隔を設定
defaultTimedEventDuration: '03:00:00',
buttonText: {
  prev: '',
  next: '',
  prevYear: '前年',
  nextYear: '翌年',
  today: '今日',
  month: '',
  week: '',
  day: '',
},

// イベントの時間表示を24時間にする
timeFormat: 'HH:mm',

// イベントの色を変える
eventColor: '#63ceef',

// イベントの文字色を変える
eventTextColor: '#000000',

4.モデルを作成

ターミナル
$ rails g model Event user_id:integer title:string body:text start_date:datetime end_date:datetime
~_create_events.rb
class CreateEvents < ActiveRecord::Migration[5.2]
  def change
    create_table :events do |t|
      t.integer :user_id
      t.string :title
      t.text :body
      t.datetime :start_date
      t.datetime :end_date

      t.timestamps
    end
  end
end
ターミナル
$ rails db:migrate

5.コントローラーを作成

ターミナル
$ rails g controller events new show edit my_calendar
events_controller.rb
class EventsController < ApplicationController
  before_action :set_event, only: [:show, :edit, :update, :destroy]

  def new
    @event = Event.new
  end

  def create
    @event = Event.new(event_params)
    @event.user_id = current_user.id
    @event.save ? (redirect_to event_path(@event)) : (render 'new')
  end

  def index
    @events = Event.where(user_id: current_user.id)
  end

  def show
  end

  def edit
    unless @event.user == current_user
      redirect_to root_path
    end
  end

  def update
    @event.update(event_params) ? (redirect_to event_path(@event)) : (render 'edit')
  end

  def destroy
    @event.destroy
    redirect_to my_calendar_path
  end

  def my_calendar
  end

  private

    def set_event
      @event = Event.find(params[:id])
    end

    def event_params
      params.require(:event).permit(:user_id, :title, :body, :start_date, :end_date)
    end
end

6.ルーティングを追加

routes.rb
# 追記
resources :events
get 'my_calendar', to: 'events#my_calendar'

7.json.jbuilderファイルを作成・編集

index.json.jbuilder
json.array!(@events) do |event|
  json.extract! event, :id, :title, :body
  json.start event.start_date
  json.end event.end_date
  json.url event_url(event)
end

【解説】

indexアクションで抽出したレコードを繰り返し処理し、配列を作成する。

json.array! @category_children do |children|

② 各データを で作成した配列に格納する。

json.extract! event, :id, :title, :body
json.start event.start_date
json.end event.end_date
json.url event_url(event)

8.ビューを編集

new.html.slimを編集

① HTMLを作成する。

new.html.slim
.row
  .col-xs-3

  .col-xs-6
    = form_with model: @event, url: events_path, local: true do |f|

      = f.label :title, 'スケジュール名'
      br
      = f.text_field :title, class: 'form-control'
      br

      = f.label :body, 'スケジュール内容'
      br
      = f.text_area :body, class: 'form-control'
      br

      = f.label :start_date, '開始日時'
      br
      = f.datetime_select :start_date, {}, class: 'form-control datetime-form'
      br
      br


      = f.label :end_date, '終了日時'
      br
      = f.datetime_select :end_date, {}, class: 'form-control datetime-form'
      br
      br

      = f.submit '新規登録', class: 'btn btn-success btn-block'

  .col-xs-3

② CSSでデザインを整える。

application.scss
// 追記
.datetime-form {
  display: inline-block;
  width: auto;
}

show.html.slimを編集

show.html.slim
.row
  .page-header
    h3
      | スケジュール管理

.row
  #calendar
  br

.row
  table.table.table-bordered
    tbody
      tr
        th.active
          | スケジュール名
        td
          = @event.title
      tr
        th.active
          | スケジュール内容
        td
          = @event.body
      tr
        th.active
          | 開始日時
        td
          = @event.start_date.to_s(:datetime_jp)
      tr
        th.active
          | 終了日時
        td
          = @event.end_date.to_s(:datetime_jp)

.row
  - if @event.user === current_user
    .pull-right
      = link_to 'スケジュール登録', new_event_path, class: 'btn btn-success'
      = link_to '編集', edit_event_path(@event), class: 'btn btn-primary'
      = link_to '削除', event_path(@event), method: :delete, data: { confirm: '本当に削除しますか?' }, class: 'btn btn-danger'

edit.html.slimを編集

edit.html.slim
.row
  .col-xs-3

  .col-xs-6
    = form_with model: @event, url: event_path(@event), local: true do |f|

      = f.label :title, 'スケジュール名'
      br
      = f.text_field :title, class: 'form-control'
      br

      = f.label :body, 'スケジュール内容'
      br
      = f.text_area :body, class: 'form-control'
      br

      = f.label :start_date, '開始日時'
      br
      = f.datetime_select :start_date, {}, class: 'form-control datetime-form'
      br
      br


      = f.label :end_date, '終了日時'
      br
      = f.datetime_select :end_date, {}, class: 'form-control datetime-form'
      br
      br

      = f.submit '保存', class: 'btn btn-primary btn-block'

  .col-xs-3

my_calendar.html.slimを編集

my_calendar.html.slim
.row
  .page-header
    h3
      | マイカレンダー

.row
  = link_to 'スケジュール登録', new_event_path, class: 'btn btn-success'

  #calendar
  br

注意

turbolinksを無効化しないとカレンダーが表示されない事があるので、必ず無効化しておきましょう。

turbolinksを無効化する方法

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

Rails migration

Railsのmigrationを触るのは初めてだったので、勉強備忘録のために簡単に書きとどめてみる。

migrationについて

マイグレーションは、データベーススキーマの継続的な変更 を、統一的かつ簡単に行なうための便利な手法。

migrationファイルを作成する

サンプルとしてCompanyテーブルを準備

カラム名 データ型
id integer
name string
description text
created_at datetime
updated_at datetime
$ rails g migration CrateCompany

Active Recordにはマイグレーションの実行順序をファイル名のタイムスタンプでの表示を自動でやってくれる。
作成されたファイルはdb/migrateディレクトリに保存される。

作成されたファイル

class CrateCompany < ActiveRecord::Migration[5.2]
    def change
    end
 end

ActiveRecord::Migration[]にはバージョンが入る。

chengeの代わりにupとdownを使うことができる。
upメソッドにはmigrationする時の内容、downメソッドにはrollbackする時の内容を書く。

class CrateCompany < ActiveRecord::Migration[5.2]
    def up
    end

    def down
    end
end

変更を加える

マイグレートファイルを作成しrails db:migrateを打てば実行してくる。
また直前の変更を取り消すためにはrails db:rollback

テーブルを作成する

class CrateCompany < ActiveRecord::Migration[5.2]
    def change 
     create_table :companies do |t|
    t.string :name
      t.text :description
      t.timestamps
    end
  end
end

このコマンドを打てば自動で上記のようなもの作成してくれる

$ rails g migration CrateCompany name:string description:text

テーブルを変更する

class ChangeTableCompany < ActiveRecord::Migration[5.2]
    def change 
     change_table :companies do |t|
     t.remove :name
     t.string :root_number
     t.index :root_number
     t.rename :description, :description_note
  end
  end
end

上のマイグレーションではnameカラムが削除され、stringカラムであるroot_numberが作成されてインデックスがそこに追加されます。そして最後にdescriptionカラムをリネームしている。

カラムを変更する

change_column :companies, :root_number, :text

モデル名のroot_numberのカラムをtextに変更。

ここでの注意点だが、change_columnchangeメソッドではロールバックすることができない。
changeメソッドではサポートさていないとのこと。
changeでサポートされているマイグレーション定義は

add_column
add_foreign_key
add_index
add_reference
add_timestamps
change_column_default (:fromと:toの指定は省略できない)
change_column_null
create_join_table
create_table
disable_extension
drop_join_table
drop_table (ブロックを渡さなければならない)
enable_extension
remove_column(型を指定しなければならない)
remove_foreign_key(2番目のテーブルを指定しなければならない)
remove_index
remove_reference
remove_timestamps
rename_column
rename_index
rename_table

もしロールバックする場合はup,downメソッドであればchange_columnを使用可能。

class ChangeColumnCompany < ActiveRecord::Migration[5.2]
    def up
     change_column :companies, :root_number, :text
    end

    def down
     change_column :companies, :root_number, :string
    end
end

まだまだmigrationについては知らないことばかりだが、身につけていきたい。

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

[Ruby] whileにおける条件式の評価の挙動について

はじめに

 whileを使っていたときに初めての挙動に遭遇したのでメモしておきます。

環境

ruby: 2.7.1

現象

現象_A

a = 0

while
  if a == 5
    break
  end
  a += 1
end

puts a
# 実行結果: 0

現象_B

b = 0

while
  b += 1
  if b == 5
    break
  end
end

puts b
# 実行結果: 5

現象_C

c = 0

while c < 5 do
  c += 1
end

puts c
# 実行結果: 5

現象_D

d = 0

loop do
  if d == 5
    break
  end
  d += 1
end

puts d
# 実行結果: 5

現象_E

e = 0

loop do
  e += 1
  if e == 5
    break
  end
end

puts e
# 実行結果: 5

 想定していた結果としては、現象_Aでも5が出力するものだと思っていました。

なぜこうなるのか

 (@scivolaさんのコメントにより解決することができました。)

 どうやら、whileの直後の式が条件式として評価されるようです。つまり、今回の現象_Aでは、

if a == 5
  break
end

この箇所がwhileの条件式として初回に評価され、当然a == 5falseとなりif式の値はnilとなるのでループから抜けてしまっていたようです。

ちなみにRubyではnilfalse以外の全ての値がtrueとなります。

試しに以下のように条件式を与えてやるとループ内の処理が実行されました。

a = 0

while true do # doは省略可
  if a == 5
    break
  end
  a += 1
end

puts a
# 実行結果: 5

ループ内にある式が、実は条件式になっていたことが今回の落とし穴でした。

編集履歴

2020/07/04 17:11 コメントにて@scivolaさんから問題の原因を解説いただきましたので「なぜこうなるのか」を編集しました。
2020/07/04 17:31 タイトルを内容に即したものに変更し、文章を少し校正しました。

備考

 普段はwhileloopは使わず、別の繰り返し用のメソッドを使っています。

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

[Ruby] whileの挙動について

はじめに

 whileを使っていたときに初めての挙動に遭遇したのでメモしておきます。

環境

ruby: 2.7.1

現象

現象_A

a = 0

while
  if a == 5
    break
  end
  a += 1
end

puts a
# 実行結果: 0

現象_B

b = 0

while
  b += 1
  if b == 5
    break
  end
end

puts b
# 実行結果: 5

現象_C

c = 0

while c < 5 do
  c += 1
end

puts c
# 実行結果: 5

現象_D

d = 0

loop do
  if d == 5
    break
  end
  d += 1
end

puts d
# 実行結果: 5

現象_E

e = 0

loop do
  e += 1
  if e == 5
    break
  end
end

puts e
# 実行結果: 5

 想定していた結果としては、現象_Aでも5が出力するものだと思っていました。

なぜこうなるのか

 (@scivolaさんのコメントにより解決することができました。)

 どうやら、whileの直後の式が条件式として評価されるようです。つまり、今回の現象_Aでは、

if a == 5
  break
end

この箇所がwhileの条件式として初回に評価され、当然a == 5falseとなりif式の値はnilとなるのでループから抜けてしまっていたようです。

(ちなみにRubyではnilfalse以外の全ての値がtrueとなります)。

試しに以下のように条件式を与えてやるとループ内の処理が実行されました。

a = 0

while true do # doは省略可
  if a == 5
    break
  end
  a += 1
end

puts a
# 実行結果: 5

ループ内にある式が、実は条件式になっていたことが今回の落とし穴でした。

編集履歴

2020/07/04 17:11 コメントにて@scivolaさんから問題の原因を解説いただきましたので「なぜこうなるのか」を編集しました。

備考

 普段はwhileloopは使わず、別の繰り返し用のメソッドを使っています。

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

Your Ruby version is 2.4.6, but your Gemfile specified 2.6.4

マイグレーションファイルを作成しようとしたら、RubyのバージョンがGemfileで指定しているものと違うと言われ、実行できなかった。

$ bin/rails g migration hoge
Your Ruby version is 2.4.6, but your Gemfile specified 2.6.4

rbenvがインストールされていることを確認。

$ which rbenv
/usr/local/bin/rbenv

指定されたRubyのバージョンをインストール。

$ rbenv install 2.6.4
Downloading openssl-1.1.1g.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46
Installing openssl-1.1.1g...
Installed openssl-1.1.1g to /Users/tamu/.rbenv/versions/2.6.4

Downloading ruby-2.6.4.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.4.tar.bz2
Installing ruby-2.6.4...
ruby-build: using readline from homebrew
Installed ruby-2.6.4 to /Users/tamu/.rbenv/versions/2.6.4

インストールされたことを確認。

$ rbenv versions
  system
* 2.4.6 (set by /Users/tamu/.rbenv/version)
  2.6.4

今回は、特定のディレクトリ配下にのみ適用したかったので、以下のように指定。

$ rbenv local 2.6.4

切り替わったことを確認。

$ ruby -v
ruby 2.6.4p104 (2019-08-28 revision 67798) [x86_64-darwin18]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

migrateファイルの変更(rails db:rollback)

こんにちは、tt_tsutsumiです。
今回はmigrateファイルの変更方法についてです。
こちらの記事が何方かのお役に立てると嬉しいです。

アプリケーション作成中にカラムの変更や追加を行った時に
普通のコードの書き方と違いが生まれたので困惑しました。
その際に自分が行った工程と流れの記載を致します。

1. rails db:migrate:status

まずは現在作成しているアプリケーションのmigrateの稼働状況の確認を行いましょう。
コンソールにて下記コードを入力します。

$ rails db:migrate:status

Status   Migration ID    Migration Name
--------------------------------------------------
   up     year/month/day  Devise create users
   up     year/month/day  Create spots

※ year/month/dayの部分はご自身で作成された日付等が表示されます

上記でstatusの部分がupになっている場合、migrateファイルは稼働しております。
この際にmigrateファイルの変更や追加を行っても反映されません。

2. rails db:rollback

migrateファイルの稼働状況をdownします。

$ rails db:rollback
$ rails db:migrate:status

Status   Migration ID    Migration Name
--------------------------------------------------
   up     year/month/day  Devise create users
   down   year/month/day  Create spots

ここで重要なのは rails db:rollback です!!
このコードを打つことにより稼働しているmigateファイルをdownさせる事ができます。

※ 注意点としてこちらのコードはファイルを1つずつしかdownに出来ません。

3. rails db:migrate

downになっているのを確認してからmigrateファイルの変更や消去を行って下さい。
そして変更が終わったら保存をし、rails db:migrate を行います。

$ rails db:migrate
$ rails db:migrate:status

Status   Migration ID    Migration Name
--------------------------------------------------
   up     year/month/day  Devise create users
   up     year/month/day  Create spots

これでmigrateファイルの変更と保存は終了です。
ご覧いただきありがとうございました !!

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

Ruby シンボルについて

rubyのシンボルについて学習したので、メモ。
シンボルは一見、文字列に見えるがコンピューターは整数として扱うので、
処理速度が向上するというメリットがある。

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

JWTを用いたTokenAPIの実装(Rails)

JWTとは

JSON Web Tokenの略称です。
今回はJWTを用いたTokenAPIをRailsに実装するのが目的なのでJWTについては簡単な説明だけにします。
TokenAPIは単純にemail,passwordでログインチェックを行って該当ユーザーが存在する時にそのユーザーのTokenを返すAPIです。
詳細内容については以下のリンクを参考にしてください。
https://openid-foundation-japan.github.io/draft-ietf-oauth-json-web-token-11.ja.html

JWT構成

ヘッダ、ペイロード、署名の3つのパートになっててそれぞれBase64でエンコードされている。

ヘッダ
{
  "typ":"JWT",
  "alg":"HS256"
}
ペイロード
{
  "sub": "1234567890",
  "iss": "John Doe",
  "aud": "audience",
  "exp": 1353604926
}
署名
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

3つのパートは . (ドット) で結合されている。

ヘッダ・ペイロード・証明を簡単に作ってみたいなら以下のリンクでできます。
https://jwt.io/#debugger

JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyIiwic3ViIjoidGVzdCIsImF1ZCI6ImF1ZGllbmNlIiwicGFyYW1zIjoie1wiZW1haWxcIjogXCJ0ZXN0QGdtYWlsLmNvbVwiLCBcInBhc3N3b3JkXCI6IFwicGFzc3dvcmRcIn0iLCJleHAiOiIxNTkzODM2ODA2In0.4fC4yLEmYTjiwaXk3R_AUUPEQSuI_ARmkoMqosWEJ-c

JWTクレーム

JWT クレームセットは JSON オブジェクトであり, それぞれのメンバは JWT として送られるクレームである. JWT クレームセット内のクレーム名は一意でなければならない
今回は以下の4つのクレームを使います。

"iss" (Issuer) クレーム:JWTの発行者の識別子
"sub" (Subject) クレーム:JWTの主語となる主体の識別子
"aud" (Audience) クレーム:JWTを利用することが想定された主体の識別子一覧
"exp" (Expiration Time):JWTの有効期限

APIプロジェクト生成

console
$ rails new jwt --api

Gemfileにjwt追加

gem 'jwt'

Jwt認証チェックモジュール作成

app/controllers/concerns/signature.rb
module Signature
  extend ActiveSupport::Concern

  def verify_signature
    render status: 401, json: { message: '認証エラー'} if request.headers['jwt-signature'].blank?

    request_params = JSON.parse(request.body.read)

    @signature ||= JwtSignature.new(jwt: request.headers['jwt-signature'])
    @signature.verify!(params: request_params)
  rescue JwtSignature::InvalidSignature
    render status: 401, json: { message: '認証エラー'}
  end
end

Jwt認証処理モデル作成

app/models/jwt_signature.rb
class JwtSignature
  class InvalidSignature < StandardError; end
  ALGORITHM = 'HS256'
  ISSUER = 'user'
  AUDIENCE = 'audience'
  SUB = "test"
  TOKEN_TYPE = 'JWT'
  # SECRET_KEYは重要なので環境ごとに定義して安全に管理してください〜
  SECRET_KEY = '1gCi6S9oaleH22KWaXyXZAQccBx4lUQi'

  def initialize(jwt:)
    @jwt = jwt
  end

  def verify!(params:)
    raise InvalidSignature unless valid_payload? && valid_params?(params: params) && valid_header?
  end

  private

  def valid_payload?
    return false unless jwt_payload['iss'] == ISSUER

    return false unless jwt_payload['sub'] == SUB

    return false unless jwt_payload['aud'] == AUDIENCE

    true
  end

  def valid_header?
    return false unless jwt_header['alg'] == ALGORITHM

    return false unless jwt_header['typ'] == TOKEN_TYPE

    true
  end

  def valid_params?(params:)
    JSON.parse(jwt_payload['params']) == params
  end

  def jwt_header
    @jwt_header ||= decoded_jwt.second
  end

  def jwt_payload
    @jwt_payload ||= decoded_jwt.first
  end

  def decoded_jwt
    @decoded_jwt ||= JWT.decode(@jwt, SECRET_KEY, true, algorithm: ALGORITHM)
  rescue JWT::DecodeError
    raise InvalidSignature
  end
end

JWT.decodeはHashのArrayを返すので
decoded_jwt.firstでPayload、decoded_jwt.secondでHeaderを取得
スクリーンショット 2020-07-04 13.55.00.png

User Table作成

app/models/user.rbが生成される

$ rails g model User name:string email:string token:string expired_at:datetime
$ rails db:migrate

routes.rbにAPI追加

routes.rb
Rails.application.routes.draw do
  post 'tokens/create'
end

Token Controller作成

tokens_controller.rb
class TokensController < ActionController::API
  include Signature

  # createの時だけ認証を確認する
  before_action :verify_signature, only: %i(create)

  def create
    # 今回は認証確認処理だけ実装
    user = User.find_by(email: params[:email], password: params[:password])

    return render status: 400, json: { message: 'ユーザーが存在しません。' } unless user

    # Tokenが存在しない場合は更新
    if user.token.blank?
      user.token = SecureRandom.uuid
      user.save
    end
    render status: 200, json: { name: user.name, email: user.email, token: user.token }
  end
end

テストユーザー登録

$rails c

irb(main):001:0> User.new(name: 'test', email: 'test@gmail.com', password: 'password')
   (0.5ms)  SELECT sqlite_version(*)
=> #<User id: nil, name: "test", email: "test@gmail.com", password: [FILTERED], token: nil, expired_at: nil, created_at: nil, updated_at: nil>
irb(main):002:0> User.new(name: 'test', email: 'test@gmail.com', password: 'password').save
   (0.1ms)  begin transaction
  User Create (0.9ms)  INSERT INTO "users" ("name", "email", "password", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["name", "test"], ["email", "test@gmail.com"], ["password", "password"], ["created_at", "2020-07-04 02:30:17.701626"], ["updated_at", "2020-07-04 02:30:17.701626"]]
   (0.8ms)  commit transaction
=> true

JwtEncodedデータ生成

https://jwt.io/#debugger
上記のリンクからJwtEncodedデータを生成
Payload部分は自分が必要なJwtクレームとパラメータを追加
paramsにemail, passwordを追加してAPIのパラメーターのJsonとPayloadのJsonを比較している
VERIFY SIGNATUREのSecurityキーも自分のSecurityに変更(JwtSignatureモデルのSECRET_KEY使用)
満期時間を設定したい場合はexpにUnixtimeを設定

unixtime(現在時間から5分後)
irb(main):040:0> (Time.now + 300).to_i
=> 1593838401

スクリーンショット 2020-07-04 13.49.22.png

Postmanで実行してみる

Headerに「jwt-signature」を追加してJwtEncodedデータを追加

JwtEncodedデータ
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyIiwic3ViIjoidGVzdCIsImF1ZCI6ImF1ZGllbmNlIiwicGFyYW1zIjoie1wiZW1haWxcIjogXCJ0ZXN0QGdtYWlsLmNvbVwiLCBcInBhc3N3b3JkXCI6IFwicGFzc3dvcmRcIn0iLCJleHAiOiIxNTkzODM4NDAxIn0.dSNqdhHBJKUJHnJa_2sS_3Qr4oNNdr5MKFx5ufwqLv4

スクリーンショット 2020-07-04 13.48.47.png

Bodyにjson形式でemail, password追加

json
{ "email": "test@gmail.com", "password": "password"}

スクリーンショット 2020-07-04 11.47.42.png

実行

認証確認後無事にtokenが取得できました〜
スクリーンショット 2020-07-04 11.50.29.png

同じ状態で5分後実行

expに5分を設定したので5分後には認証失敗
スクリーンショット 2020-07-04 13.29.26.png

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

acts-as-taggable-onを使って絞り込み機能を作る

環境

  • ubuntu(WSL)
  • rails 6.0.3

事前準備

以下の機能は作成済みとします。

  • 投稿機能
  • 投稿詳細
  • gemのインストール
  • テーブルの作成

手順

  • タグの保存
  • ビューで表示
  • 絞り込みの実装

タグの保存

タグをつけたいモデルに以下を追加。

micropost.rb
acts_as_taggable

保存するためにストロングパラメータに以下を追加。
念のためcreateも載せておきます。

microposts_controller.rb
 def create
    @micropost = Micropost.new(micropost_params.merge(user_id: current_user.id))
    if @micropost.save
      redirect_to @micropost, flash:{success: "投稿しました"}
    else
      render :new
    end
 end

 def show
    @micropost = Micropost.find(params[:id])
 end

 private

 def micropost_params
   params.require(:micropost).permit(:content, :image, :tag_list)
 end

ビューの作成と表示

ビューに以下を追加してください。
入力ビューと表示ビューを作ります。

new.html.slim
= form_with model:  micropost, local: true do |f|
  .form-group
    = f.label :tag
    //,で区切る
    = f.text_field :tag_list, value: @micropost.tag_list.join(","), class: "form-control"
  = f.submit "送信", class: "btn btn-primary"
show.html.slim
table.table.table-hover
    today
      tr
        th = Micropost.human_attribute_name(:tag)
        td
          - @micropost.tag_list.each do |tag|
            = link_to tag, microposts_path(tag_name: tag), class: "micropost_tags__link"

絞り込み機能の実装

microposts_controller.rb
  def inde
    if params[:tag_name]
      #選択した同じ投稿の情報を返す
      @microposts = Micropost.tag_search(params[:tag_name]).page(params[:page])
    elsif params[:q]
      #投稿を一覧を返す
      @microposts = @q.result(distinct: true).page(params[:page])
    else
      @microposts = Micropost.page(params[:page])
    end
  end

終わりに

間違いがありましたら編集リクエストまたはコメントお願いします。

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

オフラインどう書く第15回参考問題解答

問題はこちら->http://nabetani.sakura.ne.jp/hena/ord15subpalin/

最初と最後の文字の組み合わせから一文字づつ近づけながら2文字を比較し、
n1番目とn2番目が同じ文字なら長さ(ans)を2増やし
n1+1番目とn2-1番目の間で同じ文字を調べます。(loop関数のforループ中のloop)
最後n番目とn+1番目が同じにならなければ長さに1をたします。
最初Juliaで枝刈せずに遅かったので、NimやRubyでも試しましたが、
結局、今の長さ(ans)と残りの文字数の和が今までの最大の長さ(fans)より大きい場合
(fans<j-i+ans+1)のみ探索を進める簡単な枝狩りをし、
forループ中のloopの後にbreakを入れたら、だいぶ速くなりました。
Julia

#Julia v 0.6-1.4

inp="0  I_believe_you_can_solve     9   evo_n_ove
1   a   1   a
2   aa  2   aa
3   aaa     3   aaa
4   ab  1   b
5   aabb    2   bb
6   ABBA    4   ABBA
7   Abba    2   bb
8   1234567890  1   0
9   1234567890987654321     19  1234567890987654321
10  abcdcba     7   abcdcba
11  0a1b2c3d4c5b6a7     7   abc4cba
12  abcdcba0123210  7   0123210
13  abcdcba_123210  7   abcdcba
14  _bcdcba0123210  7   0123210
15  abcddcba0123210     8   abcddcba
16  abcdcba01233210     8   01233210
17  a0bc1dc2ba3210a     9   a0123210a
18  a0bc1ddc2ba3210     8   0bcddcb0
19  3a0bc1ddc2ba3210    10  3abcddcba3
20  11oooo1111o1oo1o111ooooooooooo  20  oooo111o1oo1o111oooo
21  11o1111o1111oo11ooo11111ooo1oo  20  o1o1111oo11oo1111o1o
22  111111oo11o111ooo1o1ooo11ooo1o  21  1ooo11ooo1o1ooo11ooo1
23  11o1o1o11oo11o11oo111o1o1o11oo  27  11o1o1o11oo11o11oo11o1o1o11
24  oo111o1o11o1oo1ooo11o1o11o1o1o  27  oo111o1o11ooo1ooo11o1o111oo
25  1o1oo11111o1o1oo1o1o1111oo1o1o  28  1o1oo1111o1o1oo1o1o1111oo1o1
26  QQooooQooooQQyQoyQQQyyyyQQoyoy  15  ooQQyyQQQyyQQoo
27  oQoooQooooQyoyQoyoyyyQQyQQQQoQ  16  QoQQyQyyyyQyQQoQ
28  yyyyyooyQyyyoyyQyyooyQoQoQQoQy  17  yooQyoyyQyyoyQooy
29  yyQoyQoyyQyQQoyooooyyQQyQyooQy  24  yQooyQyQQyooooyQQyQyooQy
30  oQQooQoQyQQoyoQQoQyQyQyQoQoooo  24  oooQoQyQQyoQQoyQQyQoQooo
31  oQQyQQyyQyQQoooyQQyyyQQQyyQQoy  25  oQQyQQyyyQQoooQQyyyQQyQQo
32  WAk9iHI4jVDlStyudwTNqE138kwan2  3   wkw
33  c43fIu1Mlz0K9hEVOgGcUdbeB5ksa7  3   cGc
34  CAbYcW5VqHjzFdIkH_61PM0TsyRuie  3   HkH
35  jInQnUvSayrJTsQWujovbbqW0STvoj  10  jvTWbbWTvj
36  iZDrvpUKgtj3FrZsZ4CLjrEgUdZzQG  11  ZUgrZsZrgUZ
37  ROgYUOu6er_DA7nB6UGquO1GUHC62R  11  RUOu6B6uOUR
38  Oh_be_a_fine_girl_kiss_me   9   e_i_l_i_e
39  8086_6502_6809_Z80  11  8086_2_6808
40  xcode_visualstudio_eclipse  11  ce_iutui_ec
41  word_excel_powerpoint_outlook   9   ol_opo_lo
42  abcdefghijklmnopqrstuvwxyz0123  1   3"

function main()
local fans
    function loop(str,ans)
        if str !=""
            for i in 1:length(str)-1
                for j in length(str):-1:i+1
                    if str[j]==str[i] && fans<j-i+ans+1
                            loop(str[i+1:j-1],ans+2)
                            break
                     end
                end
            end
            ans +=1
        end
        if  fans<ans 
            fans=ans
        end
    end
str0=split(inp,"\n")
for s0 in str0
  s1=split(s0," ")
  fans=0
  ans=0
  loop(strip(s1[2]),ans)
  if fans==parse(Int,s1[3])
    sel="pass"
  else
    sel="fail"
  end
  print(sel," ",fans,"\n")
end
end

@time main()

Nim
Juliaと大差ない速さの時もありますが、この問題(コード)では、桁違いに速いです。

#Nim v 0.194

import os,strutils,sequtils,times
import math

const inp="""0  I_believe_you_can_solve     9   evo_n_ove
1   a   1   a
2   aa  2   aa
3   aaa     3   aaa
4   ab  1   b
5   aabb    2   bb
6   ABBA    4   ABBA
7   Abba    2   bb
8   1234567890  1   0
9   1234567890987654321     19  1234567890987654321
10  abcdcba     7   abcdcba
11  0a1b2c3d4c5b6a7     7   abc4cba
12  abcdcba0123210  7   0123210
13  abcdcba_123210  7   abcdcba
14  _bcdcba0123210  7   0123210
15  abcddcba0123210     8   abcddcba
16  abcdcba01233210     8   01233210
17  a0bc1dc2ba3210a     9   a0123210a
18  a0bc1ddc2ba3210     8   0bcddcb0
19  3a0bc1ddc2ba3210    10  3abcddcba3
20  11oooo1111o1oo1o111ooooooooooo  20  oooo111o1oo1o111oooo
21  11o1111o1111oo11ooo11111ooo1oo  20  o1o1111oo11oo1111o1o
22  111111oo11o111ooo1o1ooo11ooo1o  21  1ooo11ooo1o1ooo11ooo1
23  11o1o1o11oo11o11oo111o1o1o11oo  27  11o1o1o11oo11o11oo11o1o1o11
24  oo111o1o11o1oo1ooo11o1o11o1o1o  27  oo111o1o11ooo1ooo11o1o111oo
25  1o1oo11111o1o1oo1o1o1111oo1o1o  28  1o1oo1111o1o1oo1o1o1111oo1o1
26  QQooooQooooQQyQoyQQQyyyyQQoyoy  15  ooQQyyQQQyyQQoo
27  oQoooQooooQyoyQoyoyyyQQyQQQQoQ  16  QoQQyQyyyyQyQQoQ
28  yyyyyooyQyyyoyyQyyooyQoQoQQoQy  17  yooQyoyyQyyoyQooy
29  yyQoyQoyyQyQQoyooooyyQQyQyooQy  24  yQooyQyQQyooooyQQyQyooQy
30  oQQooQoQyQQoyoQQoQyQyQyQoQoooo  24  oooQoQyQQyoQQoyQQyQoQooo
31  oQQyQQyyQyQQoooyQQyyyQQQyyQQoy  25  oQQyQQyyyQQoooQQyyyQQyQQo
32  WAk9iHI4jVDlStyudwTNqE138kwan2  3   wkw
33  c43fIu1Mlz0K9hEVOgGcUdbeB5ksa7  3   cGc
34  CAbYcW5VqHjzFdIkH_61PM0TsyRuie  3   HkH
35  jInQnUvSayrJTsQWujovbbqW0STvoj  10  jvTWbbWTvj
36  iZDrvpUKgtj3FrZsZ4CLjrEgUdZzQG  11  ZUgrZsZrgUZ
37  ROgYUOu6er_DA7nB6UGquO1GUHC62R  11  RUOu6B6uOUR
38  Oh_be_a_fine_girl_kiss_me   9   e_i_l_i_e
39  8086_6502_6809_Z80  11  8086_2_6808
40  xcode_visualstudio_eclipse  11  ce_iutui_ec
41  word_excel_powerpoint_outlook   9   ol_opo_lo
42  abcdefghijklmnopqrstuvwxyz0123  1   3"""

proc main()=
  var fans=0
  proc loop(str:string,ans:int)=
    var ans1=ans
    if str != "":
      for i in 0..len(str)-2:
        for j in  countdown(len(str)-1,i+1):
          if str[j]==str[i] and fans<j-i+ans1+1:
             loop(str[i+1..j-1],ans1+2)
             break
      ans1=ans1+1
    if  fans<ans1:
      fans=ans1

  for s0 in inp.splitLines :
    var s1=s0.split(" ")
    fans=0
    loop(s1[1].strip,0)
    var sel=""
    if fans==s1[2].strip.parseInt:
      sel="pass"
    else:
      sel="fail"
    echo(sel," ",fans)

var t = cpuTime()
main()
echo cpuTime()-t

Ruby
「どう書く」の参加者にはRuby使いの方が多かったような気がします。
私は「オブジェクト指向スクリプト言語Ruby」の初版を読んでいるのですが、
GUIプログラムはDelphiのほうが便利だし、パズルを解くには遅いという印象がありましたので、
たまにawkの代わりに使うくらいでした。
しかし別の問題で、他の方のコードを参考に書いてJuliaと比べたら結構早かったので、
今回も試してみました。
この問題では枝刈前はあまり早くありませんでしたが枝刈後はJuliaといい勝負です。
ruby使いの方から見れば古風で冗長な書き方かもしれませんが、枝刈した方を載せます。

#ruby v 2.5.5
require 'benchmark'

$inp="0     I_believe_you_can_solve     9   evo_n_ove
1   a   1   a
2   aa  2   aa
3   aaa     3   aaa
4   ab  1   b
5   aabb    2   bb
6   ABBA    4   ABBA
7   Abba    2   bb
8   1234567890  1   0
9   1234567890987654321     19  1234567890987654321
10  abcdcba     7   abcdcba
11  0a1b2c3d4c5b6a7     7   abc4cba
12  abcdcba0123210  7   0123210
13  abcdcba_123210  7   abcdcba
14  _bcdcba0123210  7   0123210
15  abcddcba0123210     8   abcddcba
16  abcdcba01233210     8   01233210
17  a0bc1dc2ba3210a     9   a0123210a
18  a0bc1ddc2ba3210     8   0bcddcb0
19  3a0bc1ddc2ba3210    10  3abcddcba3
20  11oooo1111o1oo1o111ooooooooooo  20  oooo111o1oo1o111oooo
21  11o1111o1111oo11ooo11111ooo1oo  20  o1o1111oo11oo1111o1o
22  111111oo11o111ooo1o1ooo11ooo1o  21  1ooo11ooo1o1ooo11ooo1
23  11o1o1o11oo11o11oo111o1o1o11oo  27  11o1o1o11oo11o11oo11o1o1o11
24  oo111o1o11o1oo1ooo11o1o11o1o1o  27  oo111o1o11ooo1ooo11o1o111oo
25  1o1oo11111o1o1oo1o1o1111oo1o1o  28  1o1oo1111o1o1oo1o1o1111oo1o1
26  QQooooQooooQQyQoyQQQyyyyQQoyoy  15  ooQQyyQQQyyQQoo
27  oQoooQooooQyoyQoyoyyyQQyQQQQoQ  16  QoQQyQyyyyQyQQoQ
28  yyyyyooyQyyyoyyQyyooyQoQoQQoQy  17  yooQyoyyQyyoyQooy
29  yyQoyQoyyQyQQoyooooyyQQyQyooQy  24  yQooyQyQQyooooyQQyQyooQy
30  oQQooQoQyQQoyoQQoQyQyQyQoQoooo  24  oooQoQyQQyoQQoyQQyQoQooo
31  oQQyQQyyQyQQoooyQQyyyQQQyyQQoy  25  oQQyQQyyyQQoooQQyyyQQyQQo
32  WAk9iHI4jVDlStyudwTNqE138kwan2  3   wkw
33  c43fIu1Mlz0K9hEVOgGcUdbeB5ksa7  3   cGc
34  CAbYcW5VqHjzFdIkH_61PM0TsyRuie  3   HkH
35  jInQnUvSayrJTsQWujovbbqW0STvoj  10  jvTWbbWTvj
36  iZDrvpUKgtj3FrZsZ4CLjrEgUdZzQG  11  ZUgrZsZrgUZ
37  ROgYUOu6er_DA7nB6UGquO1GUHC62R  11  RUOu6B6uOUR
38  Oh_be_a_fine_girl_kiss_me   9   e_i_l_i_e
39  8086_6502_6809_Z80  11  8086_2_6808
40  xcode_visualstudio_eclipse  11  ce_iutui_ec
41  word_excel_powerpoint_outlook   9   ol_opo_lo
42  abcdefghijklmnopqrstuvwxyz0123  1   3"

def main()
  def loop(str,ans)
      if str !=""
          for i in 0..str.length-2
              (str.length-1).downto(i+1) {
                |j|
                  if str[j]==str[i] && $fans<j-i+ans+1
                      loop(str[i+1..j-1],ans+2)
                      break
                  end
              }
          end
          ans +=1#*string(str[1])
      end
      if  $fans<ans 
          $fans=ans 
      end
  end

  $inp.lines do |s0|
    s1=s0.split(" ")
    $fans=0
    ans=0
    loop(s1[1].strip,ans)
    if $fans==s1[2].to_i
      sel="pass"
    else
      sel="fail"
    end
    print(sel," ",$fans,"\n")
  end
end

time = Benchmark.realtime do
 main()
end
p time

別解(Prolog)
元の文字列と逆向きの文字列で同じ文字の組み合わせの最長を求める方法です。メモ化再帰です。
二つの文字列のn1番目とn2番目が同じ文字なら、長さRを1増やしてn1+1番目とn2+1番目を比べます。
違う場合はn1+1番目とn2番目を比較した場合と、n1番目とn2+1番目を比較した場合の
長いほうとRを加えます。
コード風に書くと
f(n1,n2) = if n1==n2: f(n1+1,n2+1)+1     #solve/4の4節目
      else:max(f(n1+1,n2),f(n1,n2+1))   #solve/4の5節目
メモ化の部分はfunctorを利用して二次元配列(F)のメモを作り、
solve/4の3節目で、メモに書き込まれている場合(nonvar(R1))、その値を利用します。
data/4はメモの書き込みにも読み込みにも用います。

%SWI-Prolog v 7.4.2,7.6.4
%start.
%:-initialization(start).  %ideone

twoar(_,0,_).      % two dimensional array
twoar(F,X,Y):-arg(X,F,V),functor(V,y,Y),X1 is X-1,twoar(F,X1,Y).
data(F,X,Y,V):-arg(X,F,V1),arg(Y,V1,V).

solve([],_,_,0).
solve(_,[],_,0).
solve(L1,L2,F,R):-
     length(L1,N1),length(L2,N2),data(F,N1,N2,R1),nonvar(R1),!,R=R1.
solve([H1|T1],[H2|T2],F,R):-
     H1==H2,!,length([H1|T1],N1),length([H2|T2],N2),
     solve(T1,T2,F,R1),R is R1+1,data(F,N1,N2,R).
solve([H1|T1],[H2|T2],F,R):-
     length([H1|T1],N1),length([H2|T2],N2),
     solve([H1|T1],T2,F,R1),solve(T1,[H2|T2],F,R2),R is max(R1,R2),data(F,N1,N2,R).

start:-str(S),split_string(S,"\s,\n","\s",L),pre(L),!.

disp(R,Q):-
     number_string(R,R1),(R1==Q->Str="pass ";Str=" fail "),
     write(" "),write(Str),writeln(R).

pre([]).
pre([_,B,C,_|T]):-
     atom_chars(B,L),reverse(L,L1),length(L,M),M1 is M+5,functor(F,x,M1),twoar(F,M1,M1),
     solve(L,L1,F,R),disp(R,C),pre(T).

str("0  I_believe_you_can_solve         9       evo_n_ove
1       a       1       a
2       aa      2       aa
3       aaa     3       aaa
4       ab      1       b
5       aabb    2       bb
6       ABBA    4       ABBA
7       Abba    2       bb
8       1234567890      1       0
9       1234567890987654321     19      1234567890987654321
10      abcdcba         7       abcdcba
11      0a1b2c3d4c5b6a7         7       abc4cba
12      abcdcba0123210  7       0123210
13      abcdcba_123210  7       abcdcba
14      _bcdcba0123210  7       0123210
15      abcddcba0123210         8       abcddcba
16      abcdcba01233210         8       01233210
17      a0bc1dc2ba3210a         9       a0123210a
18      a0bc1ddc2ba3210         8       0bcddcb0
19      3a0bc1ddc2ba3210        10      3abcddcba3
20      11oooo1111o1oo1o111ooooooooooo  20      oooo111o1oo1o111oooo
21      11o1111o1111oo11ooo11111ooo1oo  20      o1o1111oo11oo1111o1o
22      111111oo11o111ooo1o1ooo11ooo1o  21      1ooo11ooo1o1ooo11ooo1
23      11o1o1o11oo11o11oo111o1o1o11oo  27      11o1o1o11oo11o11oo11o1o1o11
24      oo111o1o11o1oo1ooo11o1o11o1o1o  27      oo111o1o11ooo1ooo11o1o111oo
25      1o1oo11111o1o1oo1o1o1111oo1o1o  28      1o1oo1111o1o1oo1o1o1111oo1o1
26      QQooooQooooQQyQoyQQQyyyyQQoyoy  15      ooQQyyQQQyyQQoo
27      oQoooQooooQyoyQoyoyyyQQyQQQQoQ  16      QoQQyQyyyyQyQQoQ
28      yyyyyooyQyyyoyyQyyooyQoQoQQoQy  17      yooQyoyyQyyoyQooy
29      yyQoyQoyyQyQQoyooooyyQQyQyooQy  24      yQooyQyQQyooooyQQyQyooQy
30      oQQooQoQyQQoyoQQoQyQyQyQoQoooo  24      oooQoQyQQyoQQoyQQyQoQooo
31      oQQyQQyyQyQQoooyQQyyyQQQyyQQoy  25      oQQyQQyyyQQoooQQyyyQQyQQo
32      WAk9iHI4jVDlStyudwTNqE138kwan2  3       wkw
33      c43fIu1Mlz0K9hEVOgGcUdbeB5ksa7  3       cGc
34      CAbYcW5VqHjzFdIkH_61PM0TsyRuie  3       HkH
35      jInQnUvSayrJTsQWujovbbqW0STvoj  10      jvTWbbWTvj
36      iZDrvpUKgtj3FrZsZ4CLjrEgUdZzQG  11      ZUgrZsZrgUZ
37      ROgYUOu6er_DA7nB6UGquO1GUHC62R  11      RUOu6B6uOUR
38      Oh_be_a_fine_girl_kiss_me       9       e_i_l_i_e
39      8086_6502_6809_Z80      11      8086_2_6808
40      xcode_visualstudio_eclipse      11      ce_iutui_ec
41      word_excel_powerpoint_outlook   9       ol_opo_lo
42      abcdefghijklmnopqrstuvwxyz0123  1       3").
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】jquery を使わずに javascript で flashメッセージをフェードアウトさせる

はじめに

Railsアプリを開発していてflashメッセージをフェードアウトさせたく、javascriptを用いて実装したので書く。

こういったやつ↓↓(Bootstrapのalertを適用させています)
スクリーンショット 2020-07-04 8.59.20.png

これをフェードアウトさせる

環境

Ruby: 2.5.1
Rails: 5.2.4.2

実装

実装イメージとしては徐々に透明度を高くしていき、最終的に非表示にします。

要素のstyle属性
①opacityの値を減少させていく
②opacityが0になったらdisplay: none; にする

viewファイル

erbはこのようになっている

_flash_messages.html.erb
<% flash.each do |message_type, message| %>
  <div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>

jsファイル

flash_message.js
// turbolinks:loadでページ読み込み時に実行
document.addEventListener('turbolinks:load', () => {

  // flashメッセージ要素を取得
  const flashMessage = document.querySelector('.alert');

  // フェードアウトさせる(徐々に透過し,非表示にする)関数を定義
  const fadeOutFlashMessage = () => {
    // setIntervalを特定するために返り値を変数timer_idに格納
    const timer_id = setInterval(() => {
      // flashメッセージのstyle属性 opacityを取得
      const opacity = flashMessage.style.opacity;

      if (opacity > 0) {
      // opacityの値が0より大きければ0.02ずつ値を減少させる
        flashMessage.style.opacity = opacity - 0.02;
      } else {
        // opacityの値が0になったら非表示に
        flashMessage.style.display = 'none';
        // setIntervalをストップさせる
        clearInterval(timer_id);
      };
    }, 50); // 今回は0.05秒ごとにsetIntervalを実行
  }

  // flashメッセージがある場合のみ実行
  if (flashMessage !== null) {
    // style属性opacityをセット
    flashMessage.style.opacity = 1;
    // 今回は表示から3秒後に上記で定義したフェードアウトさせる関数を実行
    setTimeout(fadeOutFlashMessage, 3000);
  };
});

参考文献

下記記事を参考にさせていただきました。
ありがとうございました。

turbolinksチートシート
【JavaScript】Railsのflashをフェードアウトして消す方法

最後に

読んでいただきありがとうございます。
間違いや、より効率的な記述方法等あればご指摘いただけると非常に嬉しいです。

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

[Rspec]更新前のデータに対するmethod呼び出し確認テストでハマった

ある特定のモデルインスタンスがupdateされた時にafter_updateで呼び出したmethodの中で、更新前のrelation関係にあるmodelに対してmodel_methodが呼ばれているか確認するテストで結構ハマったので備忘録

model.rb
after_update :hoge_method

def hoge_method
  old_fuga = Fuga.find_by(id: fuga_id_before_last_save)
  fuga&.model_method
  old_fuga&.model_method
end

こういうのがあったとして、fugaに対する呼び出しのチェックは、これでいけた

let!(:hoge) { create :hoge }
let(:fuga) { create :fuga } 
it 'invokes model_method of the related fuga.' do
  allow(fuga).to receive(:model_method)
  hoge.update(fuga: fuga)
  expect(fuga).to have_received(:model_method)
end

更新後のモデルインスタンスに対してmodel_methodの呼び出しできたー
じゃ、これ最初からそいつ紐づけてallowで監視しとけばいけるんじゃね?
と思った

だから、最初から紐づけて監視してupdateで全く違うインスタンスに更新することにした。

let!(:hoge) { create :hoge, fuga: fuga }
let(:fuga) { create :fuga } 
let(:other_fuga) { create :fuga }
it 'invokes model_method of the related fuga.' do
  allow(fuga).to receive(:model_method)
  hoge.update(fuga: other_fuga)
  expect(fuga).to have_received(:model_method)
end

がこれだと、failした。

どうやらfind_byで参照しているモデルインスタンスと、exampleで使っているモデルインスタンスは全く別のものとして捉えられるらしい。
元のコードを、

def hoge_method
  new_fuga = Fuga.find_by(id: fuga_id)
  old_fuga = Fuga.find_by(id: fuga_id_before_last_save)
  new_fuga&.model_method
  old_fuga&.model_method
end

とすると更新後のfugaへのテストもfailすることが確認できた。

じゃ、どうするか
モデルに対してfind_byが呼ばれた時に戻り値としてdoubleを返すmockを用意することで解決した。

let!(:hoge) { create :hoge, fuga: fuga }
let(:fuga) { create :fuga } 
let(:other_fuga) { create :fuga }
let(:mock_fuga) { double('mock') }
it 'invokes model_method of the related fuga.' do
  allow(Fuga).to receive(find_by).with(id: fuga.id).and_return(mock_fuga)
  allow(mock_fuga).to receive(:model_method)
  hoge.update(fuga: other_fuga)
  expect(mock_fuga).to have_received(:model_method)
end

with引数はなくてもいける。

これに到達するまでにだいぶ時間を使ってしまった。
調べるといっぱい出てくるが応用することができなかった。
でも、こうやってみてみるとめっちゃ簡単だな〜

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

名古屋で開催されている技術イベント・勉強会

はじめに

この度、名古屋を離れ、関東へ引っ越すことになりました。名古屋(東海圏)エンジニアを増やして地元を盛り上げていくという夢が叶わなくなってしまいました。

名古屋でもエンジニアコミュニティを盛り上げようと頑張っている企業さんや、エンジニアさんがたくさんいることを多くの人に共有したくこの記事を書いています。

名古屋へのUターンを考えていたり、リモートができるようになり都心を離れようかなぁと考えている人は、ぜひ次の拠点として名古屋を選択に入れる参考にしてみてください。

*以下イベント情報の概要はリンクページから一部引用させていただいています。

定期開催編

以下、定期開催されている勉強会です。

Yahoo! JAPAN Nagoya

主催:Yahoo! JAPAN
概要:ヤフー株式会社の名古屋オフィスで開催するクリエイター向け勉強会コミュニティ。 多種多様なクリエイター同士が交流できる場所として、開発スキルやノウハウについて学びながら、社内外のクリエイターがアウトプット/インプットできる機会になるべく、2019年3月にスタート。

フロントエンドもくもく会

主催:Nagoya Frontend User Group
概要:フロントエンドを中心にしたもくもく会。名古屋の企業・団体からの会場提供を受けている。

名古屋アジャイル勉強会

概要:アジャイルの考え方や方法を体験的に学び、職場で活かすことを応援する、誰でも参加できるグループ。

Mobile Act

主催:フェンリル
概要:モバイルアプリ開発関連の情報共有や参加者同士の交流を目的とした勉強会。

Nagoya.Swift+

概要:技術者としての好奇心や向上心を満たすために技術的な挑戦をするふりをして遊びます。

nagoya.go

概要:名古屋のGoコミュニティ。もくもく会やGo勉強会が開催されている。

OthloTech

主催:OthloTech
概要:OthloTech<オスロテック>は東海圏の学生エンジニア・デザイナーによる学校を超えたコミュニティ。 月に1〜2回、学生限定の勉強会やハッカソンを開催。

JAWS-UG名古屋

主催:JAWS-UG名古屋支部
概要:Amazon Web Services(AWS)のユーザグループ、JAWS-UGの名古屋支部のイベント。
AWSに興味があるんだけど周りに知ってる人がいない、がっつり勉強したい!という人たちが集い、一緒に勉強する場を企画。

MISO MOKU

主催:Misoca Developer Meetup
概要:毎週ベースで行われていた、エンジニア・デザイナー向けのもくもく会。弥生株式会社と合併したため、2020年7月以降の開催は未定。

Nagoya.unity

概要:名古屋を中心に活動を行うUnityユーザのコミュニティで、主に勉強会などの開催を行っています。

OWASP Nagoya

主催:OWASP Nagoya Chapter
概要:Webをはじめとするソフトウェアのセキュリティ環境の現状、またセキュアなソフトウェア開発を促進する技術・プロセスに関する情報共有と普及啓発を目的としたプロフェッショナルの集まる、オープンソース・ソフトウェアコミュニティ。

名古屋ギークバー

概要:技術的なものから、アナログゲーム、LT大会、生ハムを食べるなど、幅広くイベントを開催され、名古屋のギークな人達に会える。

Nagoya.php

概要:名古屋のPHPユーザーのコミュニティで、隔月で勉強会をやっています。(現在コロナのため休止中)

番外編

開催頻度が低いけど大規模だったり、面白い試みをしているイベントを書いていきます。

NGK

概要:NGKは、東海地方IT系コミュニティ合同の大新年会。普段から東海地方の勉強会に参加している人、今まで参加したことがない人、他地域の人など、 誰にとっても、さまざまなコミュニティとつながりを広げ、深める良い機会になることを目指すイベント。

東海のエンジニアはみんな参加しているのではないか?と思うぐらい大規模なイベント。

みそかつウェブ

概要:名古屋という土地を生かし、エンジニア&デザイナーコミュニティを盛り上げるため、 IT業界に関わる人同士の交流の場として似た境遇の仲間を探したり、相談できる友人を作ったりできるコミュニティです。

最後に

思いついたイベントをばっと書いているので、どんどん追記していきます。他もあるよ~みたいなご意見ありましたら是非いただけますと嬉しいです。

コミュニティも盛り上がっているので、ぜひ色々参加してみてください。
割とアットホームな土地(外からはそう思われていないかもしれないが)なので、いいですよ~。

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

名古屋で開催されいている技術イベント・勉強会

はじめに

この度、名古屋を離れ、関東へ引っ越すことになりました。名古屋(東海圏)エンジニアを増やして地元を盛り上げていくという夢が叶わなくなってしまいました。

名古屋でもエンジニアコミュニティを盛り上げようと頑張っている企業さんや、エンジニアさんがたくさんいることを多くの人に共有したくこの記事を書いています。

名古屋へのUターンを考えていたり、リモートができるようになり都心を離れようかなぁと考えている人は、ぜひ次の拠点として名古屋を選択に入れる参考にしてみてください。

*以下イベント情報の概要はリンクページから一部引用させていただいています。

定期開催編

以下、定期開催されている勉強会です。

Yahoo! JAPAN Nagoya

主催:Yahoo! JAPAN
概要:ヤフー株式会社の名古屋オフィスで開催するクリエイター向け勉強会コミュニティ。 多種多様なクリエイター同士が交流できる場所として、開発スキルやノウハウについて学びながら、社内外のクリエイターがアウトプット/インプットできる機会になるべく、2019年3月にスタート。

フロントエンドもくもく会

主催:Nagoya Frontend User Group
概要:フロントエンドを中心にしたもくもく会。名古屋の企業・団体からの会場提供を受けている。

名古屋アジャイル勉強会

概要:アジャイルの考え方や方法を体験的に学び、職場で活かすことを応援する、誰でも参加できるグループ。

Mobile Act

主催:フェンリル
概要:モバイルアプリ開発関連の情報共有や参加者同士の交流を目的とした勉強会。

Nagoya.Swift+

概要:技術者としての好奇心や向上心を満たすために技術的な挑戦をするふりをして遊びます。

nagoya.go

概要:名古屋のGoコミュニティ。もくもく会やGo勉強会が開催されている。

OthloTech

主催:OthloTech
概要:OthloTech<オスロテック>は東海圏の学生エンジニア・デザイナーによる学校を超えたコミュニティ。 月に1〜2回、学生限定の勉強会やハッカソンを開催。

JAWS-UG名古屋

主催:JAWS-UG名古屋支部
概要:Amazon Web Services(AWS)のユーザグループ、JAWS-UGの名古屋支部のイベント。
AWSに興味があるんだけど周りに知ってる人がいない、がっつり勉強したい!という人たちが集い、一緒に勉強する場を企画。

MISO MOKU

主催:Misoca Developer Meetup
概要:毎週ベースで行われていた、エンジニア・デザイナー向けのもくもく会。弥生株式会社と合併したため、2020年7月以降の開催は未定。

Nagoya.unity

概要:名古屋を中心に活動を行うUnityユーザのコミュニティで、主に勉強会などの開催を行っています。

OWASP Nagoya

主催:OWASP Nagoya Chapter
概要:Webをはじめとするソフトウェアのセキュリティ環境の現状、またセキュアなソフトウェア開発を促進する技術・プロセスに関する情報共有と普及啓発を目的としたプロフェッショナルの集まる、オープンソース・ソフトウェアコミュニティ。

名古屋ギークバー

概要:技術的なものから、アナログゲーム、LT大会、生ハムを食べるなど、幅広くイベントを開催され、名古屋のギークな人達に会える。

Nagoya.php

概要:名古屋のPHPユーザーのコミュニティで、隔月で勉強会をやっています。(現在コロナのため休止中)

番外編

開催頻度が低いけど大規模だったり、面白い試みをしているイベントを書いていきます。

NGK

概要:NGKは、東海地方IT系コミュニティ合同の大新年会。普段から東海地方の勉強会に参加している人、今まで参加したことがない人、他地域の人など、 誰にとっても、さまざまなコミュニティとつながりを広げ、深める良い機会になることを目指すイベント。

東海のエンジニアはみんな参加しているのではないか?と思うぐらい大規模なイベント。

みそかつウェブ

概要:名古屋という土地を生かし、エンジニア&デザイナーコミュニティを盛り上げるため、 IT業界に関わる人同士の交流の場として似た境遇の仲間を探したり、相談できる友人を作ったりできるコミュニティです。

最後に

思いついたイベントをばっと書いているので、どんどん追記していきます。他もあるよ~みたいなご意見ありましたら是非いただけますと嬉しいです。

コミュニティも盛り上がっているので、ぜひ色々参加してみてください。
割とアットホームな土地(外からはそう思われていないかもしれないが)なので、いいですよ~。

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

Ruby on Rails ポートフォリオ実装 - 要件定義 - Port 1

ポートフォリオ実装にあたり各工程ごとに、学習のアウトプットとして手順や詳細について、記し共有させていただきますので、一つの例として参考にしていただければ幸いです。

<アウトプットする理由について>
・"わかった気になっている"を無くすため
・フィードバックを受ける機会を得るため
・得た情報を自分の中にしっかり定着させるため
・数多くの情報から本当に必要な情報を判断できるようになるため

<各工程>
1. 要件定義(仕様決め、ガントチャート作成)
2. 設計(画面設計、DB設計、URL設計、タスクばらし)
3. 実装(たくさん...)

上記のように、各工程で中身がいくつも分岐してくるため 3工程でも何記事かに分ける必要があるかと思います。
ポートフォリオ作り終わる頃には合計で 10記事くらいで濃い内容をアウトプットしていく予定です。
また、実装部分については全てアウトプットする必要はないので、自身が難しいまたは面白い、メモに残す必要があるといった機能を記していきます。

要件定義とは

要件定義は、本格的な開発工程の前段階で、要求をまとめ具体的な進め方を決めることです。システム開発では、ユーザー側の要求を実現することがゴールです。
そのため、開発をスタートする前に、実装する機能や性能などを決めておかなければなりません。要件定義は本格的な開発工程の前段階で、具体的にどのように進めていくかを決めるために必要です。
※引用:https://it-trend.jp/development_tools/article/32-0060

とのこと。
今回要件定義として私が実施したのは、サービスにどのような機能や技術を実装、使用するかを決める「仕様決め」とすスケジュール管理をする「ガントチャートの作成」です。
考えてみた後にだいぶ規模が大きくなってしまうことに気づきますが、こちらを目標として進めます。

仕様決め

下記のようにコンセプト〜インフラをどうするかまで詳細を決めていきます。
ついては、自身でもなかなか規模の作品になり相当苦労するだろうと認識しています。
差別化するという箇所としては 私自身、以前にセキュリティエンジニアで脆弱性診断の経験があり、脆弱性診断の自動化の組み込みは何とか実装したいと思っています。

【コンセプト】
舞台観劇を通して繋がる口コミサービス

【言語・フレームワーク】
・Ruby / RubyonRails 5
・bootstrap or vue.js

【開発環境】
ローカル(Rubymine -ver 2019-3)

【実装予定の機能】
・ログイン/ログアウト機能(devise)
・画像付き投稿機能(観た舞台(口コミ)、観たい舞台(期待度とコメント))
・投稿編集機能
・投稿一覧表示機能(ページネーション)
・同じ舞台に関する口コミをまとめて表示機能
・同じ舞台への評価の平均点表示機能
・ユーザーの口コミに対する評価の平均点表示機能
・投稿詳細表示機能(自分の投稿であれば編集可)
・投稿コメント機能
・星評価機能
・プロフィール編集機能(アイコン画像あり)
・いいね機能(非同期処理)
・フォロー機能(非同期処理)
・ダイレクトメッセージ機能
・キーワード検索機能
・都道府県絞り込み機能
・地図検索機能(google map API)
[管理機能]
・管理ユーザーログイン機能
・DBテーブルのリレーション管理
・DBトランザクション制御機能
[Rspec]
・単体テスト機能
・統合テスト機能

【インフラ】
第一段階:Heroku
第二段階:AWS
第三段階:開発環境にDockerを導入
第四段階:CircleCIを使用してCI/CDパイプラインを構築しテスト自動化
余裕があれば、脆弱性診断の自動化の組み込み
https://circleci.com/ja/integrations/devsecops/

また詳細を決め終わったら、どなたか実際のエンジニア等にポートフォリオ開発の事前レビューをしていただくことをオススメします。
理由としては、もし仕様決めの時点で何か問題があってそのまま開発を進めていくと後から修正できない事態になりかねないからです。
また仕様決め段階で一点不安がありました、それはサービスのコンセプトに悩みオリジナリティのあるコンセプトを決めることができなかった点についても相談しました。

私はオンラインサロンの方で事前レビューをしていただき以下の仕様決めで特に問題ないことを確認していただきました!

下記、レビュー内容抜粋

「過去のご経歴の中で非常に強く感じた問題点の解決」をテーマにした方が企業さん側に響きやすいとは思いますが、そこら辺の判断は人それぞれなので、こちらのテーマでも大きな問題はないのではないかと思います。
「ポートフォリオにおいてほぼ誰でも最低限実装してきそうな一般的な機能だけ」になってしまっているので、もうちょっと印象度が強かったり実用性の高い機能があった方が企業さん側からの評価は高くなりやすいと思います。

雑食系エンジニアサロン

ガントチャート作成

私は Google スプレッドシートのテンプレートを使って直近の予定をざっくり立てました。
ポートフォリオの作成期間としては約2ヶ月半を想定してますが、転職活動をする必要があるので巻きで作業を進めていきたいところですね。
ガントチャートやタスク管理については個々のやり方があるかと思うので、参考までに。
https://www.google.com/intl/ja_jp/docs/about/
スクリーンショット 2020-07-04 2.41.10.png
以上、要件定義についてのアウトプットといたします。

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

Rails ポートフォリオ実装 - 要件定義 -

ポートフォリオ実装にあたり各工程ごとに、学習のアウトプットとして手順や詳細について記し共有させていただきますので、一つの例として参考にしていただければ幸いです。

<アウトプットする理由について>
・"わかった気になっている"を無くすため
・フィードバックを受ける機会を得るため
・得た情報を自分の中にしっかり定着させるため
・数多くの情報から本当に必要な情報を判断できるようになるため

<各工程>
1. 要件定義(仕様決め、ガントチャート作成)
2. 設計(画面設計、DB設計、URL設計、タスクばらし)
3. 実装(たくさん...)

上記のように、各工程で中身がいくつも分岐してくるため 3工程でも何記事かに分ける必要があるかと思います。ポートフォリオ作り終わる頃には合計で 10記事くらいで濃い内容をアウトプットしていく予定です。
また、実装部分については全てアウトプットする必要はないので、自身が難しい、または面白い、メモに残す必要があると判断した機能やエラーなどを記していきます。

要件定義とは

要件定義は、本格的な開発工程の前段階で、要求をまとめ具体的な進め方を決めることです。システム開発では、ユーザー側の要求を実現することがゴールです。
そのため、開発をスタートする前に、実装する機能や性能などを決めておかなければなりません。要件定義は本格的な開発工程の前段階で、具体的にどのように進めていくかを決めるために必要です。
引用:https://it-trend.jp/development_tools/article/32-0060

とのこと。
難しく書いていますが、個人ポートフォリオで要件定義として私が実施したのは、サービスにどのような機能や技術を実装、使用するかを決める「仕様決め」とすスケジュール管理をする「ガントチャートの作成」の2つだけとなります。

仕様決め

下記のようにコンセプト〜インフラをどうするかまで詳細を決めていきます。
ついては、自身でもなかなか規模の作品になり相当苦労するだろうと認識しています。
差別化するという箇所としては 私自身、以前にセキュリティエンジニアで脆弱性診断の経験があり、脆弱性診断の自動化の組み込みは何とか実装したいと思っています。

【コンセプト】
舞台観劇を通して繋がる口コミサービス

【言語・フレームワーク】
・Ruby / RubyonRails 5
・bootstrap or vue.js

【開発環境】
ローカル(Rubymine -ver 2019-3)

【実装予定の機能】
・ログイン/ログアウト機能(devise)
・画像付き投稿機能(観た舞台(口コミ)、観たい舞台(期待度とコメント))
・投稿編集機能
・投稿一覧表示機能(ページネーション)
・同じ舞台に関する口コミをまとめて表示機能
・同じ舞台への評価の平均点表示機能
・ユーザーの口コミに対する評価の平均点表示機能
・投稿詳細表示機能(自分の投稿であれば編集可)
・投稿コメント機能
・星評価機能
・プロフィール編集機能(アイコン画像あり)
・いいね機能(非同期処理)
・フォロー機能(非同期処理)
・ダイレクトメッセージ機能
・キーワード検索機能
・都道府県絞り込み機能
・地図検索機能(google map API)
[管理機能]
・管理ユーザーログイン機能
・DBテーブルのリレーション管理
・DBトランザクション制御機能
[Rspec]
・単体テスト機能
・統合テスト機能

【インフラ】
第一段階:Heroku
第二段階:AWS
第三段階:開発環境にDockerを導入
第四段階:CircleCIを使用してCI/CDパイプラインを構築しテスト自動化
余裕があれば、脆弱性診断の自動化の組み込み
https://circleci.com/ja/integrations/devsecops/

また詳細を決め終わったら、スクールやオンラインサロンで実際のエンジニア等にポートフォリオ開発の事前レビューをしていただくことをオススメします。
理由としては、もし仕様決めの時点で何か問題があってそのまま開発を進めていくと後から修正できない事態になりかねないからです。
私は、仕様決め段階でサービスのコンセプト考案に悩み、オリジナリティのあるコンセプトを決めることができなかった点について不安があったのでその点を相談したりして、事前レビューをしていただき、これからの実装について不安点を取り除くことができました。

--------------------------下記、レビュー内容抜粋--------------------------
「過去のご経歴の中で非常に強く感じた問題点の解決」をテーマにした方が企業さん側に響きやすいとは思いますが、そこら辺の判断は人それぞれなので、こちらのテーマでも大きな問題はないのではないかと思います。
「ポートフォリオにおいてほぼ誰でも最低限実装してきそうな一般的な機能だけ」になってしまっているので、もうちょっと印象度が強かったり実用性の高い機能があった方が企業さん側からの評価は高くなりやすいと思います。

雑食系エンジニアサロン

ガントチャート作成

私は Google スプレッドシートのテンプレートを使って直近の予定をざっくり立てました。
ポートフォリオの作成期間としては約2ヶ月半を想定してますが、転職活動をする必要があるので巻きで作業を進めていきたいところですね。
ガントチャートを作成する時点で自分が毎日どれだけ開発に時間をあてることができ、いついつまでに完成させるというを各工程の目安時間と照らし合わせながら、考えていく必要があるため意外とガントチャート作成には時間がかかりまそうです。
しっかりとしたタスク管理ツールを使った方が良いかもしれませんが、ポートフォリオのため自分が一番見やすいガントチャートにいたしました。タスク管理については個々のやり方があるかと思うので、参考までに。
https://www.google.com/intl/ja_jp/docs/about/
スクリーンショット 2020-07-04 2.41.10.png

以上、要件定義についてのアウトプットといたします。
※ちなみに、要件定義は約2~3日で終わるかと思います!

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

【Nuxt/Rails】axiosとdevise_token_authを使ってPOSTした実装

Nuxt.jsとRuby on Railsでaxiosとdevise_token_authを利用してPOSTする時に、地味に詰まってしまったので備忘録がてらまとめます。

Ruby on Rails側の実装

devise_token_authの設定諸々は省きます。

以下のURLあたりが参考になりましたので、そちらをご覧いただけると良いかもしれません。

https://github.com/lynndylanhurley/devise_token_auth
https://qiita.com/Masahiro_T/items/6bc49a625b437a7c2f45
https://sainu.hatenablog.jp/entry/2018/08/11/194319

FormObjectを導入してるので、そちらも込みで載せます。

articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = ArticleForm.new(article_params)
    if @article.save
      # 省略
    end
  end

  private

  def article_params
    json_request = ActionController::Parameters.new(JSON.parse(request.body.read))
    json_request.permit(
      :title,
      :description,
    ).merge(user_id: current_user.id)
  end
end
articles_form.rb
class ArticleForm
  include ActiveModel::Model
  attr_reader :title, :description

  validates :title, presence: true, length: { maximum: 50 }
  validates :description, length: { maximum: 300 }

  def initialize(article_params)
    @article = article_params
  end

  def save
    return false if valid?
    Article.create(@article)

    true
  end
end

これで、POSTするまでの準備が整いました。

補足

POSTされる時にJsonをごにょごにょすることになったので、JsonをParseする処理を書いてあげる必要がありました。(ここに凄くハマりました。)

def article_params
  json_request = ActionController::Parameters.new(JSON.parse(request.body.read))
  json_request.permit(
    :title,
    :description,
  ).merge(user_id: current_user.id)
end

以下の記事に救われた(というかJsonのParse部分はコピペ)ので載せておきます。

http://tabunmuri.hatenablog.com/entry/2015/07/02/rails%E3%81%A7json%E3%81%AB%E3%80%81permit%E3%81%A7%E8%A6%81%E7%B4%A0%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%97%E3%81%9F%E3%81%84%E6%99%82%E3%81%AE%E6%96%B9%E6%B3%95

Nuxt.js側の実装

Componentに書いた実装は省いて、Vuexの中でやってるaxiosでPOSTした部分の実装だけ切り取ります。

article.js
export const actions = {
  async postArticle({ dispatch }, article) {
    await this.$axios
      .post('/articles', article, {
        headers: {
          'Content-Type': 'multipart/form-data', // サムネ画像を送信する想定のため
          'access-token': this.state.user.userToken.accessToken,
          client: this.state.user.userToken.client,
          uid: this.state.user.userToken.uid,
        },
      })
      .then(() => dispatch('getArticleList'))
      .catch((error) => console.log(error))
  },
}

devise_token_authのtokenをCookieに詰めて、nuxtServerInit時にVuexに書き出す実装をしていて、リクエスト時にヘッダーにTokenを突っ込めるようにしました。

ちなみに、Ruby on Railsではありませんが、GOのechoを利用した時に似たような実装をしていて、その備忘録を以下にまとめています。

https://qiita.com/arthur_foreign/items/fadd784610d764419786

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

ruby メソッドの定義

アウトプット用です。
rubyのメソッド定義を簡単にアウトプットしていきます。

使用法としてはシンプル def メソッド名 
             end

例えば下記使用していないパターン

puts "1"
puts "2"
puts "3"
puts "4"
puts "5"
puts "6"
puts "7"
puts "8"
puts "9"
puts "10"

puts "1"
puts "2"
puts "3"
puts "4"
puts "5"
puts "6"
puts "7"
puts "8"
puts "9"
puts "10"

puts "1"
puts "2"
puts "3"
puts "4"
puts "5"
puts "6"
puts "7"
puts "8"
puts "9"
puts "10"

使用したパターン

def number_count
  puts "1"
  puts "2"
  puts "3"
  puts "4"
  puts "5"
  puts "6"
  puts "7"
  puts "8"
  puts "9"
  puts "10"
end

number_count
number_count
number_count

出力結果は同じです。

基本的にdef endはそのメソッドが読み込まれるまでは
飛ばされる処理になるので、そこだけ注意しておきたいところ

railsに手こずってるので抑えておきたい基本です。

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