20200402のRailsに関する記事は20件です。

【Rails】hoge.herokuapp.comへのアクセスをリダイレクトさせる

環境設定

Rails 6.0.0
ruby 2.6.5

リダイレクトさせるために必要なコードとは?

お名前.comでせっかく独自ドメインを買ったのに、
hogehoge.heroku.comでアクセスできてしまう。。。

これを解消するのがリダイレクト処理。

gemで、rack-rewriteというのもあるようですが、
コードを足すだけでも実現可能とのことでgemは使わず実装してみました。

application_controller.rb
class ApplicationController < ActionController::Base

  #herokuapp.comから独自ドメインへリダイレクト
  before_action :ensure_domain
  FQDN = 'www.hogehoge.net'

  # redirect correct server from herokuapp domain for SEO
  def ensure_domain
  return unless /\.herokuapp.com/ =~ request.host

  # 主にlocalテスト用の対策80と443以外でアクセスされた場合ポート番号をURLに含める 
  port = ":#{request.port}" unless [80, 443].include?(request.port)
  redirect_to "#{request.protocol}#{FQDN}#{port}#{request.path}", status: :moved_permanently
  end
end

参考記事ではbefore_filterとなっていましたが、Rails4以降は使えなくなっているので、before_actionで書きます。

FQDNは取得した独自ドメインを定義してあげます。

他はコピペでも大丈夫です(僕はこれでうまくリダイレクトしてくれています)

参考記事

【Rails】herokuapp.comから独自ドメインに301リダイレクトを行う方法

今回はこれで以上です。

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

railsでffiをインストールできない

環境

Mac
Ruby 2.6.0
Rails 6.0.0

エラー

Fetching ffi 1.12.2
Installing ffi 1.12.2 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

解決策

以下をターミナル上で実行

> brew install libffi
> export LDFLAGS="-L/usr/local/opt/libffi/lib"
> export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 自作アプリへの道 Part2

Rails 自作アプリを作った時の経過をまとめていきます。

環境

OS Ruby Rails
Mac Mojave 10.14.16 2.6.3p62 5.0.7.2

参考
https://qiita.com/cigalecigales/items/f4274088f20832252374

前提
環境構築済

【conformableの設定】

1.送信メールアドレスの登録

①設定ファイルの追記/編集

config/environments/depelopment.rb
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    :address => "smtp.gmail.com",
    :port => 587,
    :user_name => "Gメールアドレス", # メアドを書く
    :password => "Gメールパスワード",
    :authentication => :plain,
    :enable_starttls_auto => true
  }
config/initialiezers/devise.rb
  config.mailer_sender = 'Gメールアドレス' # メアドを書く

②gmailアプリパスワードの取得、追記

config/environments/development.rb
   (省略)
    :password => "Gメールパスワード", # Gmailのアプリパスワードを書く
   (省略)

③サインアップ、ログインの検証

サーバーを起動し、サインアップ。
確認メールをMacで開き、『Confirm my accout』をクリック
その後、ログイン可能か確認する

【lockableの設定】

1.設定値の登録

①設定ファイルの編集 ※ 必要に応じて、設定値やコメントの有無を変更

config/initializers/devise.rb
   (省略)

  config.lock_strategy = :failed_attempts

  config.unlock_keys = [:email]

  config.unlock_strategy = :both

  config.maximum_attempts = 20

  config.unlock_in = 1.hour

  config.last_attempt_warning = true

   (省略)

【timetableの設定】

1.設定値の登録

①設定ファイルの編集 ※ 必要に応じて、設定値やコメントの有無を変更

config/initializers/devise.rb
   (省略)

  # config.timeout_in = 30.minutes


   (省略)

【その他の設定】

1.サインアップ画面で入力させたい項目の追加

①設定ファイルの編集 ※ 必要に応じて、入力させたい項目を追加

app/views/devise/registrations/new.html.erb
<h2>サインアップ</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>
  <!-- 省略 -->

  <div class="field">
    <%= f.label :sellername, '取扱者' %><br />
    <%= f.text_field :sellername, autofocus: true, autocomplete: "sellername" %>
  </div>
  <!-- 省略 -->
<% end %>

<%= render "devise/shared/links" %>

2.変更できるようにしたい項目の追加

①設定ファイルの編集 ※ 必要に応じて、入力させたい項目を追加

app/views/devise/registrations/edit.html.erb
<h2>取扱者情報編集</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>
  <!-- 省略 -->

  <div class="field">
   <div class="field">
    <%= f.label :sellername, '取扱者' %><br />
    <%= f.text_field :sellername, autofocus: true, autocomplete: "sellername" %>
  </div>
  <!-- 省略 -->
<% end %>

<%= render "devise/shared/links" %>

3.ストロングパラメータの設定

①データベースに登録したい項目をストロングパラメータで指定する

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
    def configure_permitted_parameters
      devise_parameter_sanitizer.permit(:sign_up, keys: [:sellername])
      devise_parameter_sanitizer.permit(:account_update, keys: [:sellername])
    end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gem ancestry + seedファイルを利用した、データベースへの複数カラム挿入

本記事について

Ruby on Railsのseedファイルを使って、ancestryのデータを挿入するとき、カラムを複数同時に挿入したいと思い、その実装ができたので解説させていただきます。
(とあるプログラミングスクールのカリキュラムによって、某フリマアプリのカテゴリーに関する記事がたくさんありますが、そちらはカラムが1つしかなかったため投稿しようと考えました。)

目的

  1. seedファイルを使ってデータベースを作成すること
  2. データベース作成時に複数のカラムを同時に生成すること

参考資料

Qiita参考記事
【Rails】ancestryで簡単に多階層型データの作成し呼び出す
railsのseedの書き方いろいろ
Rails・seedファイルを分割して管理する

事前準備

ancestryの基本的な導入方法はたくさん記事があるので、割愛させていただきます。
gemのインストール、モデルの定義、テーブルの作成まで終わったものとします。

実践

今回はfood(食材)のテーブルを作成し、seedファイルで複数のカラムを同時に挿入してみます。
完成後のテーブルはこのような感じです。
データベースのサンプル.png

ancestryのgemを使っているので、「ancestry」のカラムはこのように自動的に入ります。
今回実践するカラムの同時挿入は、「name」と「description(説明)」です。
ではいきましょう。

seedファイルにデータの書き込み

テーブルに登録するデータを書き込み、データを作成するようにcreateメソッドで定義してあげます。
今回はこのようになります。

db/seeds.rb
parent_array = [
  {name: "野菜", description: "健康を保つための栄養が取れる食材"},
  {name: "果物", description: "食を彩る栄養価も高い食材"}
]

vegetable_child_array = [
  {name: "根菜類", description: "根っこの部分を食べる野菜"},
  {name: "葉菜類", description: "葉っぱの部分を食べる野菜"}
]

vegetable_grandchild_array = [
  [ # 野菜 >> 根菜類
  {name: "大根", description: "火を通すと甘味が増す野菜"},
  {name: "にんじん", description: "鮮やかな橙色の野菜"}
  ],[ # 野菜 >> 葉菜類
  {name: "ほうれん草", description: "鉄分が豊富な野菜"},
  {name: "レタス", description: "シャキシャキとした食感が特徴の野菜"}
  ]
]

parent = Food.create(parent_array[0])
vegetable_child_array.each_with_index do |child,i|
  child = parent.children.create(name: child[:name], description: child[:description])
  vegetable_grandchild_array[i].each do |grandchild|
    child.children.create(name: grandchild[:name], description: grandchild[:description])
  end
end


fruit_child_array = [
  {name: "仁果類", description: "花の先端部分が膨らんで果実になる果物"},
  {name: "柑橘類", description: "ビタミンCやクエン酸を多く含む果物"}
]

fruit_grandchild_array = [
  [ # 果物 >> 仁果類
  {name: "りんご", description: "甘い蜜を含んだ果実"},
  {name: "なし", description: "水分を多く含んだ甘い果物"}
  ],[ # 果物 >> 柑橘類
  {name: "みかん", description: "橙色の甘い果物"},
  {name: "レモン", description: "酸味が特徴的な果物"}
  ]
]

parent = Food.create(parent_array[1])
fruit_child_array.each_with_index do |child,i|
  child = parent.children.create(name: child[:name], description: child[:description])
  fruit_grandchild_array[i].each do |grandchild|
    child.children.create(name: grandchild[:name], description: grandchild[:description])
  end
end

少し長いですが、このようになります。
記述の方法はいろいろあるので、一例と思って見てください。

ここで大事なのが、createする時に、

~ xx.create(name: child[:name], description: child[:description])

と記述することで、カラムを指定して登録できます。実は結構簡単でした。

データの挿入

続いて、データを挿入していきます。
ターミナルで、以下のコマンドを実行します。

$ rails db:seed

これで、エラーメッセージが出なければ、データが挿入されているのができるはずです。
データの読み込みが成功したときは、何もメッセージが出ないはずです。

注意点

seedファイルを読み込むときは、テーブルに作成済みのレコードがないか確認してから行いましょう。
重複するデータがあるとエラーで弾かれてしまいます。

最後に

seedファイルの読み込みは、慣れるまで結構大変に感じました。
エラーの原因がわかりにくいので、根気よくがんばってください!

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

【Docker】コンテナ内のデータベース閲覧(ローカル,EC2)

はじめに

ローカル環境/本番環境(EC2)の其々でコンテナを起動させた際のデータベース閲覧方法と
環境による閲覧方法の違いがあるのか気になったので、調べてみました。

環境

  • Rails:5.0.7
  • MySQL:5.6
  • Docker:19.03.8
  • EC2(AMI):Amazon Linux AMI

ソースコード

Dockerfile
FROM ruby:2.5.1

RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && apt-get update && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/*

RUN mkdir /app

WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN gem install bundler
RUN bundle install

COPY . /app

RUN mkdir -p tmp/sockets
docker-compose.yml
version: '2'
services:
  db:
    image: mysql:5.6
    environment:
      MYSQL_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: password
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    volumes:
      - mysql-data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"

  app:
    build:
      context: .  
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/app
      - public-data:/app/public
      - tmp-data:/app/tmp
      - log-data:/app/log
    depends_on:
      - db
    depends_on:
      - web

  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/app/public
      - tmp-data:/app/tmp
    ports:
      - 80:80

データベース接続

ローカル、EC2共通
#起動中のコンテナ名確認
docker-compose ps
#DBコンテナに入る
docker exec -it DBコンテナNAME  bash
#mysqlへ接続(パスワードはdocker-compose.ymlに記載したもの)
mysql -u root -p
Enter password: 
EC2
#DBコンテナのPORT確認
docker ps
#確認結果(例)
0.0.0.0:3306->3306/tcp
#mysqlへ接続(パスワードはdocker-compose.ymlに記載したもの)
mysql -h 0.0.0.0 -P 3306 -u root -p
Enter password: 

データベースの中身閲覧

ターミナル
#データベース接続
mysql -u root -p
#どんなデータベースがあるか
show databases;
#使用したいデータベースに切り替え
use データベ-ス名;
#テーブル一覧
show tables;
#テーブルの構造確認
describe テーブル名複数系;
#テーブルの中身確認 
select * from テーブル名複数系;

おわりに

今回ローカルとEC2でそれぞれ起動したコンテナにどのような違いがあるのか、データベースの観点から調べてみました。間違ってる点があれば指摘していただけると幸いです。
同じポート3306で起動しているはずが、ローカルではmysql -h 0.0.0.0 -P 3306 -u root -pのコマンドが効かなかったので引き続き調査します。。

参考URL

https://qiita.com/hayabusa3703/items/9893a53c21ddc3c2403a
https://qiita.com/hot_study_man/items/4e129dacb7c3cab4b568

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

rails whereとfind、find_byの違い

こんにちわ。
未経験から独学でwebベンチャーに入社して3ヶ月のバスケンです。

ActiveRecord の where句でオブジェクトを出力した後の挙動がおかしい、、

ruby on railsにて
オブジェクト選択でfindではなくwhereを使う必要があったため
whereを利用して出力したオブジェクトでインスタンス変数を作成したら、
オブジェクトの値が今までどうり取り出せなくなりました。。

何故(Why??)

findwhere の違い

find:オブジェクトを返す
where:ActiveRecord_Relationを返す

※ActiveRecord_Relationに関してはまとめたものを後日投稿します?

対処

オブジェクトが単数の場合は、最後に「.first」を記述すると
findで出力した時と同じように扱うことができます。

(例)
object = Object.where(id: 1).first

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

マイクロサービスとモノレポ

コロナの影響で在宅勤務(テレワーク)も一ヶ月以上も続き環境の変化に慣れてきましたが、BBCパパのように急な家族の割り込みによる対応をどうかわすかを考えている streampack の Tana です。

IMAGE ALT TEXT HERE
(在宅疲れした際に見るとリラックスできますw)

マイクロサービスとモノレポ(Monorepo)の取り組みについて

streampack は動画配信プラットフォームをお客様の要望にカスタマイズしながら、AWSなどのクラウド上に提供しているサービスです。既存のシステムを最大限に活かしつつ、効率的に適用するためのマイクロサービスとモノレポについての経験談のお話になります。

以前は案件やレイヤー毎にリポジトリを切って開発してました。いわゆる Polyrepo(Multi-Repo) と呼ばれる方法です。

例えば、
- project1_frontend
- project1_cms
- project2_api
- project3_dev
などなど。

当時はまだ案件が少なかったり、固有な機能やシステムがあり、案件の影響を避けるという理由もあり、別のリポジトリで管理した方がいいだろうと、当時は判断してました。

管理・保守の問題が発生・・・

しかし、案件が増えるたびにリポジトリの管理が複雑になり、セキュリティアップデート、機能廃止、バグの修正が入る場合、全てのリポジトリで下記の作業が必要した。

  • Project-1レポジトリのチェックアウト、アプリ起動、修正、テスト、レビュー、デプロイ
  • Project-2レポジトリのチェックアウト、アプリ起動、修正、テスト、レビュー、デプロイ
  • Project-3レポジトリのチェックアウト、アプリ起動、修正、テスト、レビュー、デプロイ

単純作業かもしれませんが、上記の作業やタスクの切り替えはどうしても時間がかかります。

なぜモノレポの採用に至ったのか?

そこで、調査していく上で、影響を受けたのが、なぜ Google は全てのコードをシングルレポジトリを管理しているのかのYouTubeになります。Facebook, Twitter などの大規模システムでも採用されている手法です。

この手法で重要なのが、Trunk-based Development であり、常に Trunk に開発されたコードがマージされるということになります。そうすれば、常に最新なコードで動作確認できます。リリースはリリースブランチがあり、リリースしたいコードのみを Cherry Pick して、リリースブランチに反映してリリースを行います。特別な機能やベータ段階のものは、フィーチャーフラグで制御します。必要に応じて ON/OFF にすることにより、コードを修正することなく制御できます。また、シングルリポジトリなので、コード、ライブラリの共有ができるのもメリットの一つになります。

streampackとしても今後の保守・運用を考慮すると、RailsGolangNEXT.js(NodeJS)などのアプリケーション毎のバージョン・セキュリティアップデートがあることを想定すると、それぞれの一つのモノレポで運用した方がいいと判断し進めることにしました。また、再利用することで開発の効率や無駄な開発を減らすのも目的の一つです。

調査とコード移植

まずは、現状調査ですが、案件ごとに特別な機能やシステムを洗い出しをしました。その特別な機能の移植を モノレポ に反映し、フィーチャーフラグとして提供するようにしました。また、設定情報やサービスの固有な情報は環境変数で管理し、ローカル開発の際は、.envの参照、また本番環境では、ECS(タスク定義)上で汎用的に管理できるようにしてます。例え、ホスト名、AWS環境、API情報が変わったとしても簡単に変更できます。Docker上で開発する上では重要な方法です。

Config - The TWELVE-FACTOR APP

DBマイグレーション

モノレポによる移行を考慮する際に重要だったのが、Backwards Compatibilityです。移行前と移行後でも同様に正常にダウンタイムなく動く必要がありました。ある案件ではある機能不要だからといって、テーブルやカラムを削除したりすると、DBマイグレーション時に移行前の状態だと動かなくなるので、基本は削除しないようにしております。将来的に不要と判断したタイミングで精査して削除するという流れです。また、一部データ移行・更新が必要だったものに関しては、DBマイグレーションで実行のタイミングで、タスク(Script)により、制御し手動対応させず自動化させてます。

マイクロサービス

ライブ配信制御機能やライブチャットなど一部のサービスや機能に応じてマイクロサービスとして管理されております。シングルなモノレポなので、ディレクトリを変え環境変数を変えることで、開発環境を立ち上げたり、異なる環境にデプロイし再現したり再利用することができるようにしてます。一部のお客様の要望により、固有な機能なものを幅広く使えるように共通仕様に持っていき再利用できるように心がけてはいますが、共有ライブラリ化できていないのもあるので、今後の課題です。

ビルド&デプロイ

マイクロサービスでのアプリケーションごとに、インストール方法や実行コマンドが違って混乱することがあります。どんな人でも同じコマンドで動かせることで、ハードルを下げ、さらにドキュメント(README.md) などもシンプルになります。以前こちらのブログでも紹介させていただきましたが、make コマンドを使ってアプリケーションの立ち上げ、ビルド、デプロイまでできるようにしてます。

アプリを立ち上げたい場合:

$ make run

ビルドしリリースしたい場合:

$ make release

というイメージです。Code Deploy, Circle CIなどのCI/CDを導入するのも可能ですが、開発・リリース頻度の兼ね合いや社内用のgitではCI/CDに対応していないため見送り状態です。またBazelなどのビルドツールだと並列処理などメリットはありそうですが要検討が必要そうです。

新規・追加開発でのメリット

モノレポ対応できたことで、マイクロサービス間の修正が一貫性の変更がコードレビューで確認できます。それぞれのレポジトリの切り替えが必要なく、コードレビューをそれぞれ出す必要もありません。一つのリポジトリさえクローンして修正すればいいのです。マイクロサービス化では重要な手法かと思っております。

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

モノレポとマイクロサービス

コロナの影響で在宅勤務(テレワーク)も一ヶ月以上も続き環境の変化に慣れてきましたが、BBCパパのように急な家族の割り込みによる対応で頭を悩まされている streampack の Tana です。

IMAGE ALT TEXT HERE
(在宅疲れした際に見るとリラックスできますw)

モノレポ(Monorepo)とマイクロサービスへの取り組みについて

streampack は動画配信プラットフォームをお客様の要望にカスタマイズしながら、AWSなどのクラウド上に提供しているサービスです。既存のシステムとマイクロサービスを最大限に活かすために、案件ごとに管理していた複数のレポジトリ管理からモノレポへ移管したかについての経験談のお話になります。

以前は案件やレイヤー毎にリポジトリを切って開発してました。いわゆる Polyrepo(Multi-Repo) と呼ばれる方法です。

例えば、
- project1_frontend
- project1_cms
- project2_api
- project3_dev
などなど。

当時はまだ管理している案件が少なくまた固有な機能やシステムがあり、
別のリポジトリで管理した方がいいだろうと、当時は短期的な目線で判断してました。

がしかし・・・

管理・保守の問題が発生

案件が増えるたびにリポジトリの管理が複雑になり、セキュリティアップデート、機能廃止、バグの修正によって、全てのリポジトリで下記の作業が必要した。

  • Project-1レポジトリのチェックアウト、アプリ起動、コード修正、テスト、レビュー、デプロイ
  • Project-2レポジトリのチェックアウト、アプリ起動、コード修正、テスト、レビュー、デプロイ
  • Project-3レポジトリのチェックアウト、アプリ起動、コード修正、テスト、レビュー、デプロイ

単純作業のかもしれませんが、数が多いとどうしても時間がかかり非効率です。
また案件によっては、開発環境がないものがあったりなど、それをどう考慮するか、本番直反映でいけるかなど考慮すべき点が多々ありました。

なぜモノレポの採用に至ったのか?

そこで、調査していく上で、影響を受けたのが、なぜ Google は全てのコードをシングルレポジトリを管理しているのかのYouTubeになります。Facebook, Twitter などの大規模システムでも採用されている手法です。

この手法で重要なのが、Trunk-based Development であり、常に Trunk に開発されたコードがマージされるということになります。そうすれば、常に最新なコードで動作確認できます。リリースはリリースブランチがあり、リリースしたいコードのみを Cherry Pick して、リリースブランチに反映してリリースを行います。特別な機能やベータ段階のものは、フィーチャーフラグで制御します。必要に応じて ON/OFF にすることにより、コードを修正することなく制御できます。また、シングルリポジトリなので、コード、ライブラリの共有ができるのもメリットの一つになります。

streampackとしても今後の保守・運用を考慮すると、RailsGolangNEXT.js(NodeJS)などのアプリケーション毎のバージョン・セキュリティアップデートがあることを想定すると、それぞれの一つのモノレポで運用した方がいいと判断し進めることにしました。また、再利用することで開発の効率や無駄な開発を減らすのも目的の一つです。

調査とコード移植

まずは、現状調査ですが、案件ごとに特別な機能やシステムを洗い出しをしました。その特別な機能の移植を モノレポ に反映し、フィーチャーフラグとして提供するようにしました。また、設定情報やサービスの固有な情報は環境変数で管理し、ローカル開発の際は、.envの参照、また本番環境では、ECS(タスク定義)上で汎用的に管理できるようにしてます。例え、ホスト名、AWS環境、API情報が変わったとしても簡単に変更できます。Docker上で開発する上では12factorの概念は重要です。

Config - The TWELVE-FACTOR APP

DBマイグレーション

モノレポによる移行を考慮する際に重要だったのが、Backwards Compatibilityです。移行前と移行後でも同様に正常にダウンタイムなく動く必要がありました。ある案件ではある機能不要だからといって、テーブルやカラムを削除したりすると、DBマイグレーション時に移行前の状態だと動かなくなるので、基本は削除しないようにしております。将来的に不要と判断したタイミングで精査して削除するという流れです。また、一部データ移行・更新が必要だったものに関しては、DBマイグレーションで実行のタイミングで、タスク(Script)により、制御し手動対応させず自動化させてます。

マイクロサービス

ライブ配信制御機能やライブチャットなど一部のサービスや機能に応じてマイクロサービスとして管理されております。シングルなモノレポなので、ディレクトリを変え環境変数を変えることで、開発環境を立ち上げたり、異なる環境にデプロイし再現したり再利用することができるようにしてます。一部のお客様の要望により、固有な機能なものを幅広く使えるように共通仕様に持っていき再利用できるように心がけてはいますが、共有できない、固有な仕様になっているのが現状で、改善の余地があります。

ビルド&デプロイ

マイクロサービスでのアプリケーションごとに、インストール方法や実行コマンドが違って混乱することがあります。どんな人でも同じコマンドで動かせることで、ハードルを下げ、さらにドキュメント(README.md) などもシンプルになります。以前こちらのブログでも紹介させていただきましたが、make コマンドを使ってアプリケーションの立ち上げ、ビルド、デプロイまでできるようにしてます。

アプリを立ち上げたい場合:

$ make run

ビルドしリリースしたい場合:

$ make release

というイメージです。Code Deploy, Circle CIなどのCI/CDを導入するのも可能ですが、開発・リリース頻度の兼ね合いや社内用のgitではCI/CDに対応していないため見送り状態です。またBazelなどのビルドツールだと並列処理などメリットはありそうですが要検討が必要そうです。

新規・追加開発でのメリット

モノレポ対応できたことで、マイクロサービス間の修正が一貫性の変更がコードレビューで確認できます。それぞれのレポジトリの切り替えが必要なく、コードレビューをそれぞれ出す必要はなくなりました。一つのリポジトリさえクローンして修正すればいいのです。マイクロサービス化では重要な手法かと思っております。

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

オブジェクト指向とは

オブジェクト指向とはなにか

複数のオブジェクトを組み合わせてプログラムを構築する考え方です。
人をオブジェクトとして考えてみると、人には「名前」、「生年月日」、「年齢」、「住所」などのデータがあります。
オブジェクトはこれらのデータ(状態)の他に振る舞いも持ち、オブジェクトに対して「名前を教えて」と伝えたときにオブジェクト自身が保持している名前を返す動作を指します。
たとえば「鈴木さん」と「佐藤さん」がいるとします。この場合、二人は別々の異なるオブジェクトです。
鈴木さんは「名前が鈴木である」という状態を保持していて、「名前を教えて」と問えば「鈴木」と答える振る舞いを持っています。

オブジェクト指向とクラス

class Person
  def initialize(name, birthday, age, address)
    @name = name
    @birthday = birthday
    @age = age
    @address = address
  end

  def answer_name
    puts @name
  end
end


//実行結果
suzuki = Person.new("suzuki", "1988/5/5", "31", "東京都〇〇区〇〇")
=> #<Person:0x007fc55c1604f8 @name="suzuki", @birthday="1988/5/5", @age="31", @address="東京都〇〇区〇〇">
irb(main):016:0> suzuki.answer_name
suzuki

人(Person)クラスを定義して、名前や生年月日などのデータを持つようにしています。answer_nameで自分の名前を答えます。クラスの中にそのクラスとして必要なデータや振る舞いを持たせることで、そのオブジェクトの仕事はそのオブジェクトに任せることができます。
ちなみに、オブジェクト指向を全く取り入れずにアプリケーションを作ることもできます。ですが、オブジェクト指向はユーザーのためのではなく、開発者同士の円滑なコミュニケーションのためにあると考えています。

手続き型とオブジェクト指向

「手続き型とオブジェクト指向は何が違うのか」というと、手続き型(手続き型言語)とは、「プログラムを順序どおりに実行させるような書き方」のことを言います。
例えば、「Rubyを使ってあるファイルから品物の合計金額を表示する」というタスクを考えた時に以下のように書きます。
出力結果

りんご,100,3
ばなな,180,2
ぶどう,240,1
みかん,300,5
いちご,500,2

手続き型の場合

手続き型で記述をすると以下のようなコードになります。
手続き型

# ファイルを読み込む
filename = 'sample.txt'
file = File.open(filename)

while text = file.gets
  # 1行ずつ解析する
  name, price, count = text.chomp.split(',')
  price = price.to_i
  count = count.to_i

  puts "#{name}: 合計#{price * count}円"
end

file.close

オブジェクト指向の場合

オブジェクト指向で記述すると以下のようなコードになります。Itemクラスを作成し、読み込んだファイルの値をもとにインスタンスを作成し、インスタンスメソッドで合計金額を計算するようにします。
オブジェクト指向

# 品物クラス
class Item
  def initialize(name, price, count)
    @name = name
    @price = price
    @count = count
  end

  def total
    @price * @count
  end
end

# ファイルを読み込む
filename = 'sample.txt'
file = File.open(filename)

while text = file.gets
  # 1行ずつ解析する
  name, price, count = text.chomp.split(',')
  price = price.to_i
  count = count.to_i

  item = Item.new(name, price, count)

  puts "#{name}: 合計#{item.total}円"
end

file.close

手続き型とオブジェクト指向の比較
オブジェクト指向は手続き型に比べコード行数が増えていて、メリットが少ないように感じます。しかし、このコードに機能追加が増えてくると、「ファイルを読み込む部分」と「品物に関して処理する部分」が分離されているので見通しが良くなることが期待できます。
簡単に記述するなら手続き型風の記述は便利ですが、ロジックが増えることが予想される部分はオブジェクト指向型の記述方法を取り込みます。

オブジェクト指向の例

Railsでオブジェクト指向活かしたアプリケーションを作成する例です。
前提として
名刺管理アプリケーションを開発していると想定します。
以下のような機能があるとします。
個人の名前とメールアドレスを登録できる
企業の名前を登録できる
個人は企業と紐づく(所属する)
Railsでは、以下のようなモデルになります。

class Person < ActiveRecord::Base
  belongs_to :company

  # DBにname, emailカラムを持つ
end

class Company < ActiveRecord::Base
  has_many :people

  # DBにnameカラムを持つ
end

人物情報の表示
例えばあるページで次のような表示とします。

山田 太郎 (株式会社◯◯ 所属)

このためにViewに次のような記述にしてます。

<%= @person.name %> (<%= @person.company.name %>)

このような表示に変えたい箇所がアプリケーション内に複数あります。
もし、すべての箇所でこの記述に変更すると、同じコードがたくさんの場所にコピーされることになります。
今回は、個人が企業に紐付いてる場合です。
しかし、例えばサービスの仕様変更により、企業に紐付かない個人も登録することができる場合になったとします。
個人が企業に紐づくかどうかによって以下のように場合分けが必要です。

山田 太郎 (株式会社◯◯ 所属)  <- 所属がある場合
山田 太郎 (フリーランス)     <- 所属が無い場合

Viewは以下のように変更します。

<% if @person.company %>
  <%= @person.name %> (<%= @person.company.name %>)
<% else %>
  <%= @person.name %>
<% end %>

変更箇所が1つなら良いですが、既にアプリケーション内の多くの場所にコピーしてたら変更を加えなければなりません。
これでは、変更すべき箇所が抜け漏れたり、多くの箇所が変更されることによりバグが混入可能性があります。
「このサービスには今後このような仕様変更の可能性がある」と予測を立て、変更に強いアプリケーションの設計を考えましょう。
以下のように変更すると、今回のような大規模なViewの変更という事態は防げます。

class Person < ActiveRecord::Base
  belongs_to :company

  def display_info
    if company
      "#{name} (#{company.name} 所属)"
    else
      "#{name} (フリーランス)"
    end
  end
end


# view
<%= @person.display_info %>

共通部分を Personクラスのメソッドとして実装すれば、所属情報を変更するときもView側は変更しなくて済みます。

オブジェクト指向のメリット

「人物情報を出力する」という処理をまるごと「人物 (Person)」の中に入れてしまうことで、将来発生するだろう面倒を避けることができます。
これが、オブジェクト指向を活用して得られる多くのメリットの内の1つです。
例えば、開発を行うとしてオブジェクト指向に則ったアプリケーションを開発することで、開発時に現れる課題が自動的に解決されます。

クラスとインスタンス

上記の名刺管理アプリケーションの例を使って用語の説明。
Person
クラス。具体的なデータに紐付かない「データの抽象的な振る舞い」を設定するもの。
@person
Personクラスの インスタンス。クラスから生成される「具体的なデータとそれに対する処理」をまとめたもの。
@person.name
プロパティ。インスタンスが持つデータのこと。Ruby on Railsの場合には、データベースのカラムと同一のものを指している事が多い。
@person.display_info
インスタンスメソッド。インスタンスがもつプロパティなどを使ってデータを加工したり処理するもの。
※厳密には、Rubyという言語の性質上、プロパティとメソッドは実は同じ仕組みで成り立っており違いはないのですが、「オブジェクト指向」として考えた時には違いがあるために区別しました。

役に立つ原則

DRY

繰り返しを避けるという原則です。アプリケーションのコードはどのコードも将来バグになる可能性があります。重複があると、アプリケーションのコードは不必要に大きくなり、それにつれて、バグが生じる危険性も高まります。また、システムの構造は、そう意図していないにもかかわらず複雑になってしまいます。重複によりコードベースが大きくなれば、開発に携わる人間がシステム全体を完全に理解することも難しくなります。特に困るのはコードに変更を加える時です。どこかに変更を加えた場合、それとロジック等が重複している箇所にも同様の変更が必要かどうか確認しなければなりません。DRY原則を守ればこのような事態を防ぐことができます。

YAGNI

実際に必要になった機能だけを追加すべきという原則です。漠然とした予想や見込みで追加された機能が使われないまま放置されたり、プログラムの規模が必要以上に大きくなることで設計や構造が複雑になり保守や修正が難しくなり、バグや不具合が起きやすくなります。つまり、今現在具体的な必要性がないのに、将来必要になるかもしれない、あれば便利かもしれないなどといった見込みや思い込みで機能や要素を追加するべきではないとうことになります。

単一責任の原則

クラスが果たす役割を一つだけにするという原則です。例えば、Rectangleクラス(四角形クラス)があるとします。このクラスには四角形を描くメソッドと四角形の面積を計算するメソッドがあるとします。Rectangleクラスには描画と計算の2つの役割があることになります。計算方法を変える(単位の変更など)時と、描画方法を変える(描画する正方形に色をつけるなど)時でクラスの記述を変更する理由が2つになります。役割が増えるほど変更理由が増え、変更が起きやすくなります。このような時は役割ごとにクラスを分けるべきで、役割ごとに分けることで変更する際に該当するクラスを変更するだけで済みます。

インターフェイス分離の原則

あるクラスを利用するクライアントが複数存在するような場合に、各クライアントに共通の大きなインターフェイスを使ってしまうと、各クライアント内では使用しないメソッドがあった場合、クラスが使わないメソッドにも依存してしまいます。つまり、各クライアントが利用していないメソッドに対する変更の影響まで受けてしまう可能性があるということになります。そこで各クライアントが呼び出す必要のあるサービスの単位でクライアントをグループ分けし、そのグループに特化したインターフェイスを準備することで、異なるサービス間での関連性を分断することができます。

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

本番環境で画像が表示されない

ローカルで表示された画像が本番環境だと表示できない

localhostの時は表示されていた画像がデプロイした本番環境だと表示できず、altの文字だけが表示されてしまう状態になりました。
画像はapp/assets/imagesの中に保存されてあります。
画像のpathがおかしいのかな?と狙いをつけて調べたところ解決できたので、記事にします!

元のコード

<img src="assets/MHW.jpg" alt="ゲーム画像1">

修正後のコード

<%= image_tag asset_path("MHW.jpg", alt: "ゲーム画像1") %>

自動デプロイの際に画像がコンパイルされ、パスが変わるのが原因のようです。
背景画像が表示できない場合は、下記を参照してみてください。

元のコード

img {
  background-image: url("MHW.jpg");
}

修正後のコード

img {
  background-image: image-url("MHW.jpg");
}

画像が表示されないエラーはよくあるので、今後も何か発見があれば記事にしていきたいと思います。
以上、ここまで閲覧していただきありがとうございました!!

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

Rails_MySQL_Docker_Vue.jsの環境をシェル一発で始められるようしてみた

■ 何これ?

$ rails newする機会が久々にあって、諸々込みでもっと簡単に環境構築出来たら良いなと思ったので、@azul915さんの記事のshellを参考にちょっと改変して使えるようにしてみました。

Githubにもありますので好きなだけいじってください。

ご指摘などありましたらお気軽に。

■ 最終的なディレクトリ構成

【app_name】
  ∟ Dockerfile
  ∟ docker-compose.yml
  ∟ src
    ∟ [rails new で生成されたファイル群]

■ Special Thanks

■ 使い方

1. docker / docker-composeが入っている前提

2. 任意のディレクトリを作成

ex.
$ mkdir sample_app
$ cd sample_app

3. shellを配置して実行

   $ bash docker-rails-vue.sh

4. 起動確認して接続

   $ docker-compose ps
         Name                   Command             State                 Ports              
   ------------------------------------------------------------------------------------------
   sample_app_db_1    docker-entrypoint.sh mysqld   Up      0.0.0.0:3306->3306/tcp, 33060/tcp
   sample_app_web_1   rails s -p 3000 -b 0.0.0.0    Up      0.0.0.0:3000->3000/tcp

5. http://localhost:3000/に接続

6. 生成されたファイル群をGitにあげて開発スタートすれば良かろうもん

■docker-rails-vue.sh

#!/bin/bash

echo "----- 1. docker pull ruby:2.7.0 -----"
docker pull ruby:2.7.0

echo "-----2. docker pull mysql:5.7 -----"
docker pull mysql:5.7
docker images

echo "----- 3. create Dockerfile -----"
APP_ROOT="/`pwd | xargs basename`"
cat <<EOF > Dockerfile
FROM ruby:2.7.0
ENV LANG C.UTF-8

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && \
    apt-get install -y build-essential \
    libpq-dev \
    nodejs \
    yarn \
    && rm -rf /var/lib/apt/lists/* \
    && yarn install --check-files

RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT
ADD ./src/Gemfile $APP_ROOT/Gemfile
ADD ./src/Gemfile.lock $APP_ROOT/Gemfile.lock
RUN bundle install
ADD ./src/ $APP_ROOT
EOF

echo "----- 4. create Gemfile -----"
mkdir src && cd src
cat <<'EOF' > Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.0'

gem 'rails', '~> 6.0.2', '>= 6.0.2.1'
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
gem 'puma', '~> 4.1'
gem 'sass-rails', '>= 6'
gem 'webpacker', '~> 4.0'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.7'
gem 'bootsnap', '>= 1.4.2', require: false
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

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

group :development do
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  gem 'webdrivers'
end
EOF
touch Gemfile.lock
cd ../

echo "----- 5. create docker-compose.yml -----"
cat <<EOF > docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    volumes:
      - ./src/db/mysql_data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=password
    ports:
      - "3306:3306"
  web:
    build: .
    command: rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./src:$APP_ROOT
    environment:
      RAILS_ENV: development
      MYSQL_DATABASE: db_dev
      MYSQL_USERNAME: root
      MYSQL_PASSWORD: password
      MYSQL_HOST: db
    ports:
      - "3000:3000"
    links:
      - db
EOF

echo "----- 6. create Rails new app -----"
docker-compose build
docker-compose run web rails new . --force --database=mysql --webpack=vue --skip-bundle --skip-turbolinks

echo "----- 7. fix config/database.yml -----"
cd src
echo "fix config/database.yml"
cd config
rm database.yml
cat <<'EOF' > database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  socket: /var/run/mysqld/mysqld.sock
  database: <%= ENV.fetch("MYSQL_DATABASE") %>
  username: <%= ENV.fetch("MYSQL_USERNAME") %>
  password: <%= ENV.fetch("MYSQL_PASSWORD") %>
  host: <%= ENV.fetch("MYSQL_HOST") %>

development:
  <<: *default

production:
  <<: *default
EOF
cd ../

echo "----- 8. create database -----"
docker-compose run web rake db:create

echo "----- 9. docker-compose up -d -----"
docker-compose up -d

スクリーンショット 2020-04-02 14.38.01.png

■ エラーとか

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

https://wp.tekapo.com/2019/07/15/difference-between-apt-and-apt-get/

aptじゃなくてapt-get使いましょう。

■ 他参考サイト様

■ この後やること

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

【Rails】一対一対多のアソシエーション

結局は基本的なことなのですが、少し変わると途端にハマってしまう。
アソシエーションがうまくいかずに時間を溶かしてしまったので、そんな過去の自分といるかもしれない未来の誰かのために書き残しておきます。

テーブル構造

UserモデルとTagモデルの中間テーブルから派生させたTagSettingモデルを作りました。
erd.jpg
ただ、今回の話にはUserモデルはあまり関係ありません。
また、この後「tag_usersとtag_settingsを別々にしなくていいんじゃないか?」という指摘を受けてテーブル構造を変えました。なので、例としては少々不適切かもしれませんがご容赦を。

モデルのアソシエーション

user.rb
class User < ApplicationRecord
  has_many :tag_users
  has_many :tags, through: :tag_users
  has_many :tag_settings
end
tag_user.rb
class TagUser < ApplicationRecord
  belongs_to :user
  belongs_to :tag
  belongs_to :tag_setting
end
tag_setting.rb
class TagSetting < ApplicationRecord
  has_one :tag_user
  has_one :tag, through: :tag_user
  belongs_to :user
end
tag.rb
class Tag < ApplicationRecord
  has_many :tag_users
  has_many :tag_settings, through: :tag_users
end

注意点

中間テーブルへのアソシエーションを追加する

has_many :tag_usersなしでhas_many :tag_settings, through: :tag_usersを定義することはできない。
また、中間テーブルへのアソシエーションの方を上に記述する必要がある。

単数形・複数形に注意する

has_many :tag_settings, through: :tag_users
has_manyのときは:throughも複数形

has_one :tag, through: :tag_user
has_oneのときは:throughも単数形

リンク

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

「brew --prefix」とは何か?

mysql2のインストール中にエラーが起きた際の対処法で見かけるopensslのインストール先を指定する以下の方法

brew --prefix openssl

brew --prefixとは何ぞや?と思ったので調べてみました。ターミナルで。

そもそも「--prefix」とは?

言わずとしれたインストール先の指定方法

--prefix='インストール先のアドレス'
ex.
--prefix=/usr/local

brew --prefix

じゃあ「brew --prefix」は何だと思って試しにターミナルさんに聞いてみた。

% brew --prefix
/usr/local

brewのインストール先のアドレスだった。単純な話だった。

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

【MySQL】Mysql2::Error::ConnectionError 自分用メモ

rails sしてサーバーを切り忘れてしまった後にmysqlのエラーが出る。
よくやってしまうので、自分用にまとめました。実際にやっていて手順が増えたら随時追加する。

エラー

$ rails s
=> Booting Puma
=> Rails 5.2.4.2 application starting in development 
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.4 (ruby 2.5.3-p105), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2020-04-02 11:39:55 +0900

Mysql2::Error::ConnectionError - Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2):

pumaでrailsサーバーを起動することはできるが、実際にアクセスするとmysqlのエラーが出る。

解決法

$ sudo mysql.server start  # mysqlサーバーの起動
Password:  # アカウントのパスワードを入力
Starting MySQL
.. SUCCESS!

$ mysql_config --socket  # socketの有無を確認
/tmp/mysql.sock

$ chmod 777 /tmp/mysql.sock  # 権限を付与

今回はこれで解決しました。

リンク

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

PostgreSQLを利用すると、rails db:createでエラーが発生するトラブルシューティング

環境

Vagrant + Ubuntu 16.04.5 LTS
Rails 5.2.4.2
PostgreSQL9.5.19

はじめに

データベースにPostgreSQLを指定して、データベースを作成しようとすると、PostgreSQLに、vagrantというロールがありませんというエラーが表示されて、Railsからデータベースが作成できません。

$rails new test_migrate -d postgresql
$cd test_migrate
$rails db:create

FATAL:  role "vagrant" does not exist
Couldn't create 'test_migrate_test' database. Please check your configuration.
rails aborted!
ActiveRecord::NoDatabaseError: FATAL:  role "vagrant" does not exist
bin/rails:4:in `<main>'

Caused by:
PG::ConnectionBad: FATAL:  role "vagrant" does not exist
bin/rails:4:in `<main>'
Tasks: TOP => db:create
(See full trace by running task with --trace)

解決方法

PostgreSQLに接続する。

$sudo su - postgres

vagrantという名前のロールを作成します。また、予め、パスワードを設定しておかないと、pgAdminから接続ができない事るので注意。オプションの-sは、スーパーユーザーの事。

~$createuser vagrant -s

\duで、ロールの一覧を表示して、vagrantが追加されている事を確認する。既に存在しているpostgresは、デフォルトで用意されているものです。

~$psql
postgres=#\du

image.png

終了します。

postgres=#\q
~$exit
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【爆速フロント実装】夢と魔法のeach文【Rails-haml】

はじめに

TECH CAMPというプログラミング教室に45日ほど通った添野です。

昨日、教室内で「Rails-hamlを使ったフロントの爆速コーディング法」を同期に紹介したところ、大変ご好評を頂けたので、これについて記事を作成します。

爆速コーディング

今回は「メルカリのマイページ」を模写することについて考えます。
皆さんは下記画像の要素を実装するにあたり、どのようなコードを書くでしょうか?

スクリーンショット 2020-04-02 8.22.07.png

見た瞬間「面倒くさそう!」と感じる方も多いと思います。
今回はこれを爆速で実装する方法を紹介いたします。

結論

爆速実装の手順は下記になります。

1.コントローラに配列@contents = ["マイページ","お知らせ","やることリスト",...]を定義
2.hamlにeach文を書く
3.cssを整える

これだけです。実際にコードを見ながら説明していきます。

1.コントローラに配列を定義

ビューを呼び出しているコントローラのアクションに以下の記述をしてください。コピペでOKです。
今回はusersコントローラのindexアクションだと仮定します。

users_controller.rb
class UsersController < ApplicationController
  def index
    @contents = ["マイページ","お知らせ","やることリスト","いいね!一覧","出品する","下書き一覧","出品した商品-出品中","出品した商品-取引中","出品した商品-売却済み","購入した商品-取引中","購入した商品-過去の取引","ニュース一覧","評価一覧","ガイド","お問い合わせ"]
  end
end

ここで何をしているかというと、ビューに表示するための文字列を先にコントローラで配列の中に入れたのです。まだメリットは分からないかもですが、あとで説明していきます。これをやると、よく言われる「変更に強いWEBサービス」になります。

2.hamlにeach文を書く

ビューファイルに以下の記述をしてください。これもコピペでOKです。

index.html.haml
.wrapper
  - @contents.each do|c|
    .content-title
      = c

3.cssを整える

はい。コピペ推奨!

users.scss
.wrapper{
  display: flex;
  flex-direction: column;
  &__content-title{
    &:hover{
      color:#000;
      background-color: #aaa;
      cursor: pointer;
    }
    color: #555;
    display: inline-block;
    width: 400px;
    height: 40px;
    line-height: 40px;
    padding-left: 20px;
    margin-bottom: 2px;
    background-color: #ddd;
    box-shadow: 0 2px 2px 0 rgba(0,0,0,0.2);
  }
}

たった数分で完成

d08e3cf398fffd1a88efa9d9a5245556.gif

え?これ、ヤバくね・・・?
この記述を使用するメリットは2つあります。

メリット1 HTMLが短くなる
ビューがたった4行で書けてしまいます。時短効果ハンパない。
HTML要素をコピペして増やしていく作業は、もうやめましょう。

メリット2 変更に強くなる
実際にサービスを開発する上で、後から「あの項目も必要だった!」となることがあります。
今回の実装だと、変更が簡単です。さっきコントローラで定義した配列に何か文字を追加してください。

@contents = ["マイページ",..(省略)..,"追加項目1","追加項目2","追加項目3"]

そして、もう一度ビューを見にいきます。

スクリーンショット 2020-04-02 8.59.19.png

なんということでしょう。勝手に項目が増えました。HTML書いてないのに。これが夢と魔法のeach文の力です。科学の力ではどうすることもできない奇怪な輩に立ち向かうことができました。

おわりに

爆速フロントコーディングの手順は下記でした。

1.コントローラに配列を定義
2.hamlにeach文を書く
3.cssを整える

メリットは下記2つです。

1.HTMLが短くなる
2.変更に強くなる

以上になります。ここまで読んでいただきありがとうございました!


添野文哉(@enjoy_omame)
https://twitter.com/enjoy_omame

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

【爆速実装】夢と魔法のeach文【Rails-haml】

はじめに

TECH CAMPというプログラミング教室に45日ほど通った添野です。

昨日、教室内で「Rails-hamlを使ったフロントの爆速コーディング法」を同期に紹介したところ、大変ご好評を頂けたので、これについて記事を作成します。

爆速コーディング

今回は「メルカリのマイページ」を模写することについて考えます。
皆さんは下記画像の要素を実装するにあたり、どのようなコードを書くでしょうか?

スクリーンショット 2020-04-02 8.22.07.png

見た瞬間「面倒くさそう!」と感じる方も多いと思います。
今回はこれを爆速で実装する方法を紹介いたします。

結論

爆速実装の手順は下記になります。

1.コントローラに配列@contents = ["マイページ","お知らせ","やることリスト",...]を定義
2.hamlにeach文を書く
3.cssを整える

これだけです。実際にコードを見ながら説明していきます。

1.コントローラに配列を定義

ビューを呼び出しているコントローラのアクションに以下の記述をしてください。コピペでOKです。
今回はusersコントローラのindexアクションだと仮定します。

users_controller.rb
class UsersController < ApplicationController
  def index
    @contents = ["マイページ","お知らせ","やることリスト","いいね!一覧","出品する","下書き一覧","出品した商品-出品中","出品した商品-取引中","出品した商品-売却済み","購入した商品-取引中","購入した商品-過去の取引","ニュース一覧","評価一覧","ガイド","お問い合わせ"]
  end
end

ここで何をしているかというと、ビューに表示するための文字列を、先にコントローラで配列の中に入れたのです。まだメリットは分からないかもですが、あとで説明していきます。これをやると、よく言われる「変更に強いWEBサービス」になります。

2.hamlにeach文を書く

ビューファイルに以下の記述をしてください。これもコピペでOKです。

index.html.haml
.wrapper
  - @contents.each do|c|
    .wrapper__content-title
      = c

3.cssを整える

はい。コピペ推奨!

users.scss
.wrapper{
  display: flex;
  flex-direction: column;
  &__content-title{
    &:hover{
      color:#000;
      background-color: #aaa;
      cursor: pointer;
    }
    color: #555;
    display: inline-block;
    width: 400px;
    height: 40px;
    line-height: 40px;
    padding-left: 20px;
    margin-bottom: 2px;
    background-color: #ddd;
    box-shadow: 0 2px 2px 0 rgba(0,0,0,0.2);
  }
}

たった数分で完成

d08e3cf398fffd1a88efa9d9a5245556.gif

え?これ、ヤバくね・・・?
この記述を使用するメリットは2つあります。

メリット1 HTMLが短くなる
ビューがたった4行で書けてしまいます。時短効果ハンパない。
HTML要素をコピペして増やしていく作業は、もうやめましょう。

メリット2 変更に強くなる
実際にサービスを開発する上で、後から「あの項目も必要だった!」となることがあります。
今回の実装だと、変更が簡単です。さっきコントローラで定義した配列に何か文字を追加してください。

@contents = ["マイページ",..(省略)..,"追加項目1","追加項目2","追加項目3"]

そして、もう一度ビューを見にいきます。

スクリーンショット 2020-04-02 8.59.19.png

なんということでしょう。勝手に項目が増えました。HTML書いてないのに。これが夢と魔法のeach文の力です。科学の力ではどうしようもできない、その奇っ怪なる輩に立ち向かうことができました。

おわりに

爆速フロントコーディングの手順は下記でした。

1.コントローラに配列を定義
2.hamlにeach文を書く
3.cssを整える

メリットは下記2つです。

1.HTMLが短くなる
2.変更に強くなる

以上になります。ここまで読んでいただきありがとうございました!


添野文哉(@enjoy_omame)
https://twitter.com/enjoy_omame

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

attr_accessorについて

attr_accessorとは?

attr_accessorについて理解が不十分であったので紐解きます。
attr_accessorはデータを代入するメソッド(setter)とデータを取り出せるメソッド(getter)を同時に定義できるメソッドである。これは省略形であり、いきなり覚えようとするとかえって混乱するので一個一個分解していく。

attr_accessor・setter/getterを使わずに定義する場合

class okashi
  def name = (name)     #どんな処理を行っているのか
  @name = name    →   ・いわゆるsetterの部分
  end             お菓子クラスに名前という情報をセット
                しますという処理。@を使用しているた
                め、メソッドの中だけでなく外でも使用できる。

 def name           →    #どんな処理を行っているのか
  @user           ・いわゆるgetterの部分
 end              お菓子クラスにセットされている
end               @nameという情報を取り出す(ゲット) 
                           しますの処理
oks = Okashi.new

#setterにnameの値をいれる
oks.name = "クッキー"

#getterを取り出す
puts oks.name

console上ではクッキーと表示される。これだと中の処理を一個一個細かく書いていかなければならない。これをsetter/getterを使って省略する。

setter/getterを使用して定義する場合

class okashi
  attr_writer :name   →   setterの部分。この処理だけでクラスに
                 @nameの値をwrite(書き込む)こと
                 ができるようにする処理。
 
  attr_reader :name  →   getterの部分。@nameという情報を取り出す
                 処理
 end
oks = Okashi.new

#setterにnameの値をいれる
oks.name = "クッキー"

#getterを取り出す
puts oks.name

上記の処理を省略することでかなりわかりやすくなる。
このsetter getterの処理をさらに省略したものがattr_successorである。

class okashi
  attr_successor :name 
end

oks = Okashi.new

oks.name = "クッキー"

puts oks.name  

ここまで省略することができる。

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

[Rails]refileの使い方を初心者がまとめてみた。[refile]

はじめに

私のオリジナルサービスの画像投稿機能にrefileを使用したので、簡単にまとめておきたいと思います。
つまづいたところとかも記載しておくので、参考になれば幸いです。

refileの導入

gem

いくつかQiitaやブログなどで参考記事はありますが、gemをinstallできないもののいくつか見受けられました。
githubに記載されている github/refile

Gemfile
gem "refile", require: "refile/rails"
gem "refile-mini_magick"

↑この記述方法ではbundle install できませんでした。(環境によっては変わるのか?)

なので、私はこの記事(refile使い方)を参考にして、

Gemfile
gem "refile", require: "refile/rails", github: 'manfe/refile'
gem "refile-mini_magick"

としました。そして 

$ bundle install

で追加したgemをインストールします。
次にgithubを参考にしていくと

We're requiring both Refile's Rails integration and image processing via the MiniMagick gem, which requires ImageMagick (or GraphicsMagick) to be installed. To install it simply run:

と書かれており、ImageMagickをインストールする必要があると言われているので、

$ sudo apt-get install imagemagick

とします。
(僕の場合、 apt-get を yum に直して、 imagemagick を ImageMagick に変更するとインストールできました。ちなみにAWSのcloud9で開発しています。)

model

インストールが終わったら、次は画像投稿機能を追加したいmodelに

画像投稿機能をつけるmodel
class User < ActiveRecord::Base
  attachment :image_id
end

attachment :画像を入れたいカラム名 + _id を追記します。
_idの説明についてはまた後でしますので、ひとまずつけておいてください。

カラムの追加

カラムの追加は rails g migration add_(追加したいカラム名+_id)_to_追加するモデル名 (追加したいカラム名+_id):型 とします。

$ rails generate migration add_image_id_to_users image_id:string

ポイントは (追加したいカラム名+_id) ←これです。
refileの決まりで、先ほどの attachment : くっつけるカラムには _idが必要になります。
なので、さきほどのmodelでも出てきましたが、カラム名には _idをつけてあげてください。

View

次は画像投稿用のフォームを作成します。 <%= form.attachment_field :カラム名 %> で投稿用のフォームが完成します!
ややこしいですが、ここでのカラム名には _idはいりません。

<%= form_for @user do |form| %>
  <%= form.attachment_field :profile_image %>
<% end %>

Controller

次はストロングパラメータに先ほど追加したカラムをpermit(許可)してあげないといけません。
ここでも _idは必要なし。

users.controller
def user_params
  params.require(:user).permit(:name, :email, image ←これを追加)
end

View

これで最後です。画像を表示したいところに

<%= image_tag attachment_url(@user, :image, :fill, 300, 300, format: "jpg") %>

のように追記してあげるだけです!ここでも _idは必要なし。
この :fill, 300, 300 の部分で縦横幅を決めれます。(が、レスポンシブ対応にするならCSSとかで指定した方がいいかも?)
これで投稿した画像が表示されるはずです!

まとめ

実際、なにもわからない時に画像投稿機能をつけたいな〜と思いながらQiitaの記事を漁ってましたが、ほとんど分からずにかなり苦戦しました…。
なので、初心者の方にもわかるようにできるだけ詳しく解説してみました。
ポイントはなんども出てきた _id かな〜と個人的には思います。

参考記事

正直どんな記事よりもうるぞーさんのyoutubeを見るのが手っ取り早いです。ほんと神。
ほとんどこの動画を参考にしてました。ありがとうございます。

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

【Rails】孫モデルの数が最大の子モデルの要素を取得する方法(maximum,maxを使わず、sortで実装する)

やりたいこと

食べログ的なサービスを作っているとして、
いいねが最も多い投稿の画像を、お店の画像として表示したい

まえがき

今回やりたかったことが、RailsのmaximumメソッドやRubyのmaxメソッドを使ってもどうしてもできなかった。(やり方あったら教えてください。)
ので、自分でメソッドを作りました。

アイデアとしては、最大のものを取ってくるというよりも、「並べ替えて端っこを持ってくる」ってだけです。

sortメソッドを使いました。
自分的に少し詰まったのでまとめておきます。
誰かの役に立てることを願って。

状況

機能

食べログ的なサービスを作っている。機能としては、

  • お店に対して
  • ユーザーが
  • 画像を投稿する
  • ユーザーは投稿に対していいねできる

テーブル

テーブル名 カラム1 カラム2 カラム3 カラム4
Shop id name
User id name
Post id user_id shop_id image
Like id user_id post_id

アソシエーション

  • Shop has many posts
  • User has many posts
  • User has many likes
  • Post has many likes

(belongs toは省略)

概要

  • ControllerでShopモデルのインスタンス取得
  • Viewでmost_liked_imageメソッドを使って画像を表示
  • Modelでmost_liked_imageメソッドを定義

この順に説明します。

ControllerでShopモデルのインスタンス取得

@shop = Shop.find(params[:id])

とか。
変数@shopにShopモデルのインスタンスが入れる。

Viewでmost_liked_imageメソッドを使って画像を表示

<%= image_tag @shop.most_liked_image.to_s %>

most_liked_imageメソッドについては次項で説明します。

Modelでmost_liked_imageメソッドを定義

most_liked_imageメソッドで、最もいいねが多い投稿の画像を取得することを考える

models/shop.rb
class Shop < ApplicationRecord
  has_many posts, dependent: :destroy

  def most_liked_imege
    sorted_posts = self.posts.sort { |a,b| a.likes.count <=> b.likes.count }
    sorted_posts.last.image
  end
end

以上.
間違ってたらごめんなさい。

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