20200924のRailsに関する記事は17件です。

【Rails】ページの上部に戻るボタン

 はじめに

・Railsを使用してアプリケーションを開発
・viewsの下部地点から上部に戻るボタンを設置する

Ruby 2.5.7
Rails 5.2.4.4

流れ

gem 'jquery-rails' を導入
・アクションを起こすボタンを用意(画像でなく文字やfont-awesome等でも代用可能)
・viewにボタンを記載しcss及びjavascriptへ追記

ボタン画像

top_btn.png

適当に準備します・・・

画像ファイルをapp/assets/images配下に格納

viewsへ追加

<span id="top-btn">
  <a href="#">
    <%= image_tag asset_path('top_btn.png'), class: 'top-btn' %>
  </a>
</span>

・asset_path('~~') は、app/assets/images配下の画像を読み込む

css 及び jsファイルへ追記

.top-btn {
  position:   fixed;
  right:      30px;
  bottom:     40px;
}

.top-btn a {
  width:      50px;
  height:     50px;
}
$(function() {
  $('#top-btn a').on('click',function(event){
    $('body, html').animate({
      scrollTop:0
    }, 1000);
    event.preventDefault();
  });
});

初期状態(ページの上部)では表示したくない場合?

$(function() {
    var topBtn = $('#top-btn a');
    topBtn.hide();
    $(window).scroll(function(){
         if ($(this).scrollTop() > 100) {
             topBtn.fadeIn(1000);
         } else {
             topBtn.fadeOut();
         }
    });
    topBtn.click(function () {
        $('body, html').animate({
      scrollTop:0
    }, 1000);
    event.preventDefault();
  });
});

fadeIn 及びfadeOut のif文にてボタンの表示を設定しています・・

終わり

上記記載内容で、実装できるはず・・・
不明点及び説明がずれている(誤っている等)ございましたら、コメントにてにてお伝えいただければありがたいです。

以上、ご参考になれば幸いです。

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

Alpine Linuxでrails-erd実行時の文字化け対応

はじめに

個人開発のサービスにて、Alpine Linux上でrails-erdを使ったところ、生成されたPDFが文字化けしてしまいました。
この解決に時間がかかってしまったので、備忘録もかねて現象と解決方法を載せておきます。

環境

macOS Catalina Version 10.15.5
Ruby on Rails : 5.2.3
Ruby on Railsのコンテナベースイメージ:ruby:2.6.5-alpine3.9

課題

個人開発にて開発中、新しくテーブルの追加が必要になりました。その際、既存のテーブルや各テーブルの関係が大事なので、現状どうなっているのかを調べ始めました。幸いにも、少ないテーブルのみで構成されていたのでコードだけで把握することができました。
ただ、今後テーブルが増えていった時を考えると、ER図を用意し、それだけを見れば構成が分かるようにしておくと、把握する時間が短縮できると考えました。

さっそくER図を作る方法を考えましたが、テーブル構成をコードから書き起こしていっては本末転倒です。なるべく手間を掛けずにER図を自動生成してくれるツールを探したところ、rails-erdというgemを見つけました。このrails-erdはActiveRecordのモデルからER図を自動生成してPDFに出力してくれるツールです。

これだ!と思ってgemを入れて、コンテナ上でER図を生成するようbundle exec erdを実行したのですが、出来上がったPDFは全部文字化け(すべて白色の四角文字)となっていました。
なお、ローカルで同じコマンドを実行したところ、文字化けされていないPDFが生成されました。
ただ私としては、ローカルを極力汚さずコンテナ上ですべて行いたかったので、この解決策を探りました。

対応

まず、文字化けする場合は出力する文字コードがおかしいのではないかと考えました。そのためrails-erdで出力する文字コードには何を使っているのか調べました。
rails-erdのインストール手順を見たところ、出力用のツールとしてGraphvizを使用しているようです。

Rails ERD generates diagrams using Graphviz, a visualisation library.

このGraphvizで扱っている文字コードはUTF-8、iso-8859-1の2つであり、デフォルトはUTF-8となっています。

現在、Graphvizがサポートするエンコーディングは、
- UTF-8
- iso-8859-1 (Latin1) 

の2つです。

Graphvizで日本語を使う (Graphviz version 2.26.3)

Specifies the character encoding used when interpreting string input as a text label. The default value is "UTF-8". The other legal value is "iso-8859-1" or, equivalently, "Latin1".

Node, Edge and Graph Attributes

UTF-8がデフォルトのため、Graphvizが使う文字コードがおかしい線は消えました。

では次に、そもそものベースイメージの文字コードに問題があるのではと考えました。
rails-erdを使っているコンテナのベースイメージにはAlpine Linuxを使っています。
alpineについて調べたところ、alpineには文字コードがセットされていないようでした。
How I use locales in Alpine
試しにalpineを起動してlocaleにen_US.UTF-8をセットし、rails-erdを実行してみましたが、現象は変わりませんでした。

ここまでやってみた結果をTwitterでつぶやいたところ、maitubameさんから記事を紹介して頂きました。

この記事には、私が抱えている課題と解決策がそのまま書いてありました。せっかくなのでこの記事に書かれているフォントや実行コマンドが何なのか軽く調べてみました。

上記のフォントや実行コマンドを入れたコンテナを作り、bundle exec rails-erdを行ったところ、文字化けせずに正常に出力されました。

まとめ

コマンド1つでER図を出せるのは嬉しいですね。また、CIのパイプラインに載せて、テーブル更新ごとにドキュメントを出力できるようにしておくのも良さそうです。
ただ、rails-erdの開発があまり活発ではなく、2019年のリリースが最後なので、今後別のライブラリにて出力できるようになるかもしれませんね。

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

[Rails][jQuery] remote:trueとjs.erbを使って非同期のいいね機能実装

概要

Railsプロジェクトで掲示板サイトを作成中、remote:truejs.erbファイルを用いた非同期のいいね機能を実装する機会がありました。
備忘録としてここに手順を残しておきます。

前提

Rails 5.0

favorites_tableの作成モデルのアソシエーションを定義は既にされているものとします。

アソシエーション:
users - has_many :posts, has_many :favorites
posts - belongs_to :user, has_many :favorites
favorites - belongs_to :user, belongs_to :post

jQueryの準備

Rails内でjQueryを使えるように準備します。
まずはgemの導入します。

Gemfile
gem 'jquery-rails'

ターミナルでbundle installします。
そしてapplication.jsに記述を追加します。

application.js
# Rails5.1より前の場合
//= require jquery
//= require jquery-ujs

# バージョンがRails5.1以降の場合
//= require jquery
//= require rails-ujs

今回は、Rails 5.0 を使っているのでjquery_ujsrequireしてあげてください。
この場合、必ずjqueryを先にrequireするようにしてください。

ちなみにrails-ujsRails5.1よりデフォルトで搭載されているので、application.jsにてrequireしてあげるだけで大丈夫です。

ルーティングの設定

createアクションをpostメソッド、destroyアクションをdeleteメソッドを使ってルーティングを記述します。
オプションでas:を記述することでPrefixの名前を指定できます。

routes.rb
  post '/favorite/:post_id' => 'favorites#create', as: 'like'
  delete '/favorite/:post_id' => 'favorites#destroy', as: 'unlike'

favorites_controllerの作成

いいねのcreateとdestroyを定義します。

app/controllers/favorites_controller.rb
class FavoritesController < ApplicationController
   before_action :set_post

   def create
      @favorite = Favorite.create(user_id: current_user.id, post_id: @post.id)
   end

   def destroy
      @favorite = Favorite.find_by(user_id: current_user.id, post_id: @post.id)
      @favorite.destroy
   end

   private

   def set_post
      @post = Post.find(params[:post_id])
   end
end

部分テンプレートでviewを作成

使い回ししやすいように、部分テンプレート化しています。

app/views/users/show.html.erb
<%= render 'posts/posts', posts: @posts %>
app/views/users/_post.html.erb
<% posts.each do |post| %>


  <div id="favorite-<%= post.id %>">
    <%= render partial: "favorites/favorite", locals: { post: post } %>
  </div>
<% end %>

jsでイベントを発火させるためには、idまたはclassでセレクタを指定しますが、今回はどのpostに対するいいねなのかを判別するために、id内にそのpostのidを含める必要があります。
上のように書くことによって、id="post-1"のようなidを指定することができます。

app/views/favorites/_favorite.html.erb
<% if Favorite.find_by(user_id: current_user.id, post_id: post.id) %>
  <%= link_to "いいね", unlike_path(post.id), method: :delete, remote: true,  class: "btn btn-default" %>
<% else %>
  <%= link_to "いいね", like_path(post.id), method: :post, remote: true, class: "btn btn-success" %>
<% end %>
<%= post.favorites.length %>

いいねボタンのlink_toにremote: trueを追加します。
この記述により、通常であれば、link_toで呼ばれるアクションに対応するhtml.erbファイルを呼び出すところ、js.erbファイルを呼び出せるようになります。
なのでページ遷移を行わず非同期で通信が行われるようになります。
最後にいいね数を<%= post.favorites.length %>で表示しています。

js.erbファイル作成

まずはcreateとdestroyそれぞれのアクションに対応するjs.erbファイルを作成します。
app/views/favorites/create.js.erb
app/views/favorites/destroy.js.erb

app/views/favorites/create.js.erb
$("#favorite-<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");
app/views/favorites/destroy.js.erb
$("#favorite-<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");

まずはセレクタの指定ですが、id="favorite-<%= post.id %>に対応するように指定します。
そして、jQueryのhtml()メソッドで、指定したセレクタのhtmlを置き換えます。

置き換える内容が
"<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>"の部分で、部分テンプレートの_favorite.html.erbを呼び出しています。
(renderの前にあるjは、escape_javascriptのエイリアスで、改行と括弧をエスケープしてくれるメソッドです。)

これで非同期のいいね機能実装完了です!

スクリーンショット 2020-09-24 21.26.48.png

スクリーンショット 2020-09-24 21.31.17.png

最後に

ここまで読んでいただきありがとうございます。
間違っている点などがありましたらご指摘いただきますよう、よろしくお願いいたします。

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

【Docker】function not implemented @ io_fread ~ というエラーの解決方法【超簡単】

まずは解決策からいいます

Dockerデスクトップアプリを再起動するだけ!!

これだけでした、、今までの時間はなんだったんだろう、、
Docker for Windowなら右下のクジラのアイコンを右クリックしてRestartを押せばOKです!(画像参照↓)

スクリーンショット 2020-09-24 202325.png
スクリーンショット 2020-09-24 202344.png

エラーの経緯

githubからcloneしたRailsアプリを動かすために新たしくDocker環境を構築して色々いじっていたら、なぜか他のプロジェクトで正常に動いていたDockerコンテナがいきなり起動しなくなったという感じです。

【環境】
- windows10 Pro
- Rails: 6.0.3.2
- ruby: 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
- Docker for windows
- MySQL 5.7
- nginx:1.15.8

エラー内容

・・・(省略) Function not implemented @ io_fread (ディレクトリやファイル名)

見たことないエラーで焦りました。仮想環境であるDockerコンテナが他のコンテナに影響を与えることなんてあるわけない、、なぜだ、、と思い、Dockerfileをあれこれいじってしまったのが地獄の始まりでした。
原因の切り分けがまったくできていませんでしたね。

最後まで見ていただきありがとうございました。

エラー解決に少しでも役に立てば幸いです!!

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

【Rails】ドット(.)とか、コロン(:)とは一体

::とか、.とかよくお見かけします。
これはメソッドの呼び出しの違いで、表記が変わるという種明かしなんです。

ドット記法

class User
  def method
    puts "Hello"
  end
end
> user = User.new

> user.method
Hello
=> nil

コロン記法

class User
  def method
    puts "Welcome"
  end
end
> user = User.new
> user::method
Welcome
=> nil

ドット記法とコロン記法の違い

ドット記法とコロン記法の違いは、定数を呼び出せるかどうか。

記法 メソッドの呼び出し 定数の呼び出し
ドット記法 ×
コロン記法

Rubyでは、クラスやモジュールも定数として扱われる。

require 'uri'
require 'net/http'

url = URI.parse("http://yahoo.co.jp")
http = Net::HTTP.start(url.host, url.port)
> document = http.get(url.path)

> puts document.body
# ドキュメントを表示

まとめ

このようにコロン記法だと、指定した別ファイルのクラス内やモジュール内のアイテムにアクセスすることができる。

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

Rails6のマルチDBの自動切換(ActiveRecord::Middleware::DatabaseSelector)の実装を読んでみる

概要


Rails6.0より実装された複数DB対応に関して、自身のプロジェクトに適用できるか?という判断のために実装を追ってみます

Railsガイドでの記載

https://railsguides.jp/active_record_multiple_databases.html#コネクションの自動切り替えを有効にする

自動切り替え機能によって、アプリケーションはHTTP verbや直近の書き込みの有無に応じてprimaryからreplica、またはreplicaからprimaryへと切り替えます。
アプリケーションがPOST、PUT、DELETE、PATCHのいずれかのリクエストを受け取ると、自動的にprimaryに書き込みます。書き込み後に指定の時間が経過するまでは、アプリケーションはprimaryから読み出します。アプリケーションがGETリクエストやHEADリクエストを受け取ると、直近の書き込みがなければreplicaから読み出します。

HTTPメソッドによる判定 + 書き込みからの時間による判定 の2種類にて行われているという記載になっている
設定方法としては下記の記載

config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session

コードリーディング

まずはミドルウェアの適用部分から追ってみる

Railtieでのミドルウェアの適用部分

activerecord-6.0.3.2/lib/active_record/railtie.rb

  class Railtie < Rails::Railtie # :nodoc:
    config.active_record = ActiveSupport::OrderedOptions.new

    initializer "active_record.database_selector" do
      if options = config.active_record.delete(:database_selector)
        resolver = config.active_record.delete(:database_resolver)
        operations = config.active_record.delete(:database_resolver_context)
        config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations, options
      end
    end
  • database_selectorが設定されていれば実行される
  • database_resolverdatabase_resolver_contextはただ渡されるようになっている

続いてミドルウェア部分の実装を追っていく

[補足] OrderedOptionsとは?

https://api.rubyonrails.org/classes/ActiveSupport/OrderedOptions.html

Hashをドットでアクセスできるようにしている機構
deleteで設定されていない場合はnilが返る

Ruby2.6.5 pry(main)> x = ActiveSupport::OrderedOptions.new
=> {}
Ruby2.6.5 pry(main)> x.delete(:hoge)
=> nil

DatabaseSelectorの実装内容

activerecord-6.0.3.2/lib/active_record/middleware/database_selector.rb

module ActiveRecord
  module Middleware
    class DatabaseSelector
      def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
        @app = app
        @resolver_klass = resolver_klass || Resolver
        @context_klass = context_klass || Resolver::Session
        @options = options
      end

      attr_reader :resolver_klass, :context_klass, :options

      def call(env)
        request = ActionDispatch::Request.new(env)

        select_database(request) do
          @app.call(env)
        end
      end
  • database_resolverが設定されていない場合はDatabaseSelector::Resolverが設定される
  • database_resolver_contextが設定されていない場合はDatabaseSelector::Resolver::Sessionが設定される

続いて重要そうな#select_databaseを見てみる

DatabaseSelector#select_database

module ActiveRecord
  module Middleware
    class DatabaseSelector
      private
        def select_database(request, &blk)
          context = context_klass.call(request)
          resolver = resolver_klass.call(context, options)

          if reading_request?(request)
            resolver.read(&blk)
          else
            resolver.write(&blk)
          end
        end

        def reading_request?(request)
          request.get? || request.head?
        end
    end
  end
end

contextが先に実行され、それをもとにresolverが作られているため、まずはcontextのほうを確認してみる

Resolver::Session.call

class Resolver # :nodoc:
  class Session # :nodoc:
    def self.call(request)
      new(request.session)
    end

    def initialize(session)
      @session = session
    end

    attr_reader :session

.call#newしているだけで、ここではActionDispatch::Requestのsessionをセットしているのみ
なので続いてはResolverの.callを確認

Resolver.call

class Resolver # :nodoc:
  SEND_TO_REPLICA_DELAY = 2.seconds

  def self.call(context, options = {})
    new(context, options)
  end

  def initialize(context, options = {})
    @context = context
    @options = options
    @delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
    @instrumenter = ActiveSupport::Notifications.instrumenter
  end

  attr_reader :context, :delay, :instrumenter

ここでもnewしているだけなので、元のDatabaseSelector#select_databaseをもう少し読み進めていく
※ちなみにinstrumenterは通知の仕組み(購読すれば通知を受け取れる)

HTTPメソッドの判断部分

def select_database(request, &blk)
  context = context_klass.call(request)
  resolver = resolver_klass.call(context, options)

  if reading_request?(request)
    resolver.read(&blk)
  else
    resolver.write(&blk)
  end
end

def reading_request?(request)
  request.get? || request.head?
end

ここではGETかHEADの場合 => read
それ以外の場合 => write
としてresolverの処理を実行している(Resolverがさらに判定を行っているっぽい

まずはreadから見てみる

Resolver#read

class Resolver # :nodoc:
  def read(&blk)
    if read_from_primary?
      read_from_primary(&blk)
    else
      read_from_replica(&blk)
    end
  end

  private
    def read_from_primary(&blk)
      ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
        instrumenter.instrument("database_selector.active_record.read_from_primary") do
          yield
        end
      end
    end

    def read_from_replica(&blk)
      ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role, prevent_writes: true) do
        instrumenter.instrument("database_selector.active_record.read_from_replica") do
          yield
        end
      end
    end

roleをどちらにするかとinstrumentにより通知を行ったうえで処理を進めているよう。
では続いて#read_from_primary?にて何をどう判定しているのか?

  private
    def read_from_primary?
      !time_since_last_write_ok?
    end

    def send_to_replica_delay
      delay
    end

    def time_since_last_write_ok?
      Time.now - context.last_write_timestamp >= send_to_replica_delay
    end

contextいわゆるsessionの#last_write_timestampというものが、
最初のconfig.active_record.database_selector = { delay: 2.seconds }のdelay分だけ経過していなかった場合はprimaryにして、
delay分経過している場合はreplicaを見るようになっている

ということでsessionの#last_write_timestampを確認

class Session # :nodoc:
  # Converts milliseconds since epoch timestamp into a time object.
  def self.convert_timestamp_to_time(timestamp)
    timestamp ? Time.at(timestamp / 1000, (timestamp % 1000) * 1000) : Time.at(0)
  end

  def last_write_timestamp
    self.class.convert_timestamp_to_time(session[:last_write])
  end

ただsesion[:last_write]をtimeオブジェクトに直しているのみ
readはこれで終わりなので続いてwrite

Resolver#write

class Resolver # :nodoc:
  def write(&blk)
    write_to_primary(&blk)
  end

  private
    def write_to_primary(&blk)
      ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
        instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
          yield
        ensure
          context.update_last_write_timestamp
        end
      end
    end

これはただただ書き込みのroleにて書き込んでいるのみ

class Session # :nodoc:
  def self.convert_time_to_timestamp(time)
    time.to_i * 1000 + time.usec / 1000
  end

  def update_last_write_timestamp
    session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
  end

そして、session[:last_write]に現在のタイムスタンプを付与

まとめ

さすがRailsですね。とても分かりやすく作られていました。

結局はActionDispatch::Requestをもとに処理されているだけで、かつResolverの#read#writeを両方書き換えれば
GETとHEADに限らず好きに書くことができそうでした。

ヘッダーやIPアドレス、localhostなどによる判定も、Requestが持っている情報なので使えますし
できることはかなり多いと思いました。

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

ECサイトにおけるジャンル検索(customer側)

1. 前提条件

app/models/genre
has_many :items
app/models/item
belongs_to :genre

genreとitemは1対多の関係となっています。

2.genreコントローラ,view, route.rbの作成

以下のコードでジャンルのコントローラとshowページを作ります。
$ rails g controller Genres show

以下のコードをroute.rbに記載します。

config/route.rb
resources :genres, only: [:show]

以下のコードをgenre_controllerに記載します

app/controllers/genre_controller.rb
    def show
          @genres = Genre.all
          @genre = Genre.find(params[:id])
    end

3.showページにgenreに基づいたitem一覧を表示させる

showページに以下のコードを記載

app/views/genre/show
<div class="item-index-box">

    <!-- ジャンル選択のサイドバー -->
    <%= render "items/genres_sidebar", genres: @genres %>
    <!-- ジャンル選択のサイドバー -->
    <div class="item-index">
    <h3>商品一覧</h3>
        <table>
          <tbody>
            <tr class="item-box">
              <% @genre.items.each do |item| %>
                <td class="item">
                    <%= link_to item_path(item) do %>
                      <%= attachment_image_tag(item, :image, :fill, 300, 300, fallback:"top.image.jpg") %><br>
                      <%= item.name %><br>
                      ¥<%= item.price %>
                    <% end %>
                </td>
              <% end %>
            </tr>
          </tbody>
        </table>
    </div>
</div>

@genre.itemsとすることでgenreに基づいたitem一覧を表示させることができる。

5. 今回の実装で学んだこと

当初itemsコントローラ内にgenreアクションを追加し、ルートもresources:items内にしていたのだがそうするとルートがitemsのidを取ってしまう為、新たにコントローラとルートを付け加えた。

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

未経験者が自社開発企業に就職するためのREADME書き方

はじめに

こんにちは!

先月、無事にエンジニアデビューした1年目の者です。

私は某プログラミングスクールに3ヶ月間お世話になり、卒業後、3週間で自社開発企業に転職することができました。
この経験を生かして、私が意識してきたことを伝えていければと思います。

まず、前提として転職では見せ方が最も重要だと考えています。
例えば、職務経歴書、面接対策、wantedlyのプロフィールなどなど、、
この記事では、その中でも重要なことの一つである、ポートフォリオの見せ方について共有します。

記事の対象者

・未経験からエンジニア転職を考えていて、ポートフォリオを作成している方
・ポートフォリオのREADMEにて書く内容で迷っている方

ポートフォリオの見せ方とは

エンジニアの方々は、忙しいので、書類選考の段階で、ソースを細かく見たりサイトを触って見たりしてくれるとは限りません。
ここで活躍するのが、READMEです。
私は、多くの転職成功された方々のREADMEを見て、自分なりの最適解を見つけました。
共通点は、端的でわかりやすく、視覚的に訴えていることでした。
この共通点からREADMEを使ってわかりやすく、視覚的に伝えられるようにすることが大事だと思います。
この記事では内容についてフォーカスしているので、書き方については他の方の記事を参考にしてください。

READMEの構成

⬇️私の転職活動時に使用していたREADMEを元に説明していきます。
https://github.com/sora-uzu/The-View/blob/master/README.md

書くことは、主に4つです。
1, タイトルと概要、URL
2, 使用技術
3, AWS(インフラ)構成図
4, 機能、非機能一覧

1, タイトルと概要、URL

まずは、タイトル、URLはそのままの意味なので、概要について説明します。
概要は、以下の3点を一言で書いてください。
 ・どういうサイトか
 ・何ができるのか
 ・アピールポイント

これがかけたら、あとはサイトの分かりやすい画像を載せます。
載せ方は、癖があるので、
github readme 画像
などでググってください。

image.png

2, 使用技術

ここには、使用言語や、インフラ周りで使用しているサービスを書きます。
バージョンも書いていると良いです。

3, AWS(インフラ)構成図

インフラ周りで力を入れている方や、AWSを使っている方は、draw.ioを使ってインフラ構成図を作成してください。
各サービスの画像は、ググると公式サイトなどから転載可能な画像が取得できます。
インフラ周りでアピールポイントがあればここに記載すると良いと思います。
私の場合は、CircleCIでやっていることを書いています。
image.png

4, 機能、非機能一覧

機能と使用したgemを記載します。細かく書くというよりは、主な機能とそれに付随した機能を簡単に書いてください。
次に、非機能一覧では、なにでテストをしたか、何のテストをどのファイルで行っているかを書いてください。

最後に

以上が私が転職活動時にReadmeにおいて意識していたポイントです。私自身も多くの方のポートフォリオを参考にこういった構成になったので、皆さんも一つの参考にしていただけると嬉しいです。
ご覧いただきありがとうございました!

会社の紹介

私は現在、株式会社ダイアログという物流×ITの会社に勤務しております。
2020年9月現在、エンジニアの募集はしていませんが、他にも様々な職種を募集しているので、Wantedlyのページをご覧ください。

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

railsのいいね機能の変数エラーについて

前提・実現したいこと

railsを使ってwebアプリケーションを作成中です。
自分がいいねをつけた投稿を全投稿の中からソートして画面に表示したいです。
しかし、postの部分にエラーが出てしまいました。
その実

発生している問題・エラーメッセージ

Showing C:/Users/user/dome/app/views/users/likesline.html.erb where line #26 raised:

undefined method `id' for nil:NilClass
   <% post = Post.find_by(id: like.post_id) %>
      <div class="users-index-item">
<%# ここから=========%>
        <%= link_to("/posts/#{post.id}") do %>
<%# ここまで=========%>
        <div class="user-left">
          <img src="<%= "/post_images/#{post.image_name}" %>">
        </div>

該当コード

likesline.html.erb

<div class="users-contents">
      <div class="users-index-item-sorce">
      <% @like.each do |like| %>
      <% post = Post.find_by(id: like.post_id) %>
      <div class="users-index-item">
        <%= link_to("/posts/#{post.id}") do %>
        <div class="user-left">
          <img src="<%= "/post_images/#{post.image_name}" %>">
        </div>
        <div class="post-right">
            <div class="post-right-link">
          <%= post.title %>
        </div>
      </div>
      <%end%>
  </div>
    <% end %>
</div>
</div>

users_controller.erb

def likesline
    @user = User.find_by(id: params[:id])
    @like = Like.where(user_id: @user.id)
  end

DB(SQLite)

users

id name email created_at updated_at image_name password
10 たんぽぽ tanpopo@gmail.com 2020-09-03 2020-09-14 10.jpg tanpopo
12 kosumosu kosumosu@gmail.com 2020-09-19 2020-09-19 12.jpg kosumosu

posts

id content created_at updated_at user_id image_name title
46 contents1 2020-09-19 2020-09-19 10 46.jpg title1
47 dfsdfsdf 2020-09-19 2020-09-19 10 47.jpg df

likes

id user_id post_id created_at updated_at
1 10 26 2020-09-04 03:57:42.503803 2020-09-04 03:57:42.503803
3 10 31 2020-09-18 12:28:35.206818 2020-09-18 12:28:35.206818
5 10 32 2020-09-19 02:27:20.465790 2020-09-19 02:27:20.465790
7 10 36 2020-09-19 06:27:53.983748 2020-09-19 06:27:53.983748
9 10 47 2020-09-19 10:50:58.208238 2020-09-19 10:50:58.208238
10 10 46 2020-09-20 07:30:15.554143 2020-09-20 07:30:15.554143

問題点

posts.idとlikes.post_idの間に共通して存在しないカラムが存在していること。
よって、likesテーブルの中からid: 1,3,5,7をコンソールを使ってdestroyを行いました。

likes

**id**  **user_id** **post_id** **created_at**  **updated_at**

9   10  47  2020-09-19 10:50:58.208238  2020-09-19 10:50:58.208238
10  10  46  2020-09-20 07:30:15.554143  2020-09-20 07:30:15.554143

すると、エラーが解消され無事にお気にいり登録されている投稿が表示できるようになりました。

回答してくれた方:tky_7201さん(teratailのurl:https://teratail.com/users/tky_7201#reply)

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

Catalinaにアップグレード後のbundle installでエラー

エラー内容

$ bundle install
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

(省略)

An error occurred while installing ffi (1.13.0), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.13.0'` succeeds before bundling.

In Gemfile:
  guard-rspec was resolved to 4.7.3, which depends on
    guard was resolved to 2.16.2, which depends on
      listen was resolved to 3.2.1, which depends on
        rb-inotify was resolved to 0.10.1, which depends on
          ffi

試したこと

$ gem install ffi -v '1.13.0'  
Building native extensions. This could take a while...
ERROR:  Error installing ffi:
        ERROR: Failed to build gem native extension.
...

エラーの通りにコマンドを実行するもエラー

$ xcode-select --install                             
xcode-select: error: command line tools are already installed, use "Software Update" to install updates

既にinstallされていた。

解決策

$ sudo xcodebuild -license accept   

を実行したところ解決。

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

(Ruby on Rails6) 投稿内容を編集する機能を作る

まえがき

こちらでは、投稿ページ制作後を前提に"投稿内容を編集"の作業を忘却録として残します。

投稿内容を編集する機能を作る

編集ページの作成

routes・controllers・view の3点に、編集ページを新規作成してください
また、ページ作成には

・コントローラー名→ form
・アクション名→ Edit

で設定してます。

(例)

config/routes
Rails.application.routes.draw do
  get '/' => 'form#index'
  get '/form' => 'form#form'
  post "form/create" => "form#create"
  get "form/:id/edit" => "form#edit" ⇦これ
  get "form/:id" => "form#post"
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
app/views/任意.html.erb
<h1>編集ページ</h1>
app/controllers/任意.controller.rb
class FormController < ApplicationController
  def index
    @forms = Form.all
    @forms = Form.all.order(created_at: :desc)
  end
  def post
    @forms = Form.find_by(id: params[:id])
  end
  def form
    @forms = Form.find_by(id: params[:id])
  end
  def create
    @forms = Form.new(content: params[:content])
    @forms.save 
    @forms = Form.new(title: params[:title])
    @forms.save 
    redirect_to("/")
  end

  def edit ←ここから

  end      ←ここまで
end

routesのpostは、フォームデータの取得を行う時に使います。
新規ページ作成ではgetで設定してください。エラーの原因になってしまいます。

編集ページでの初期値設定(textarea・input)

controllers

app/views/任意.html.erb



def edit
    @forms = Form.find_by(id: params[:id]) ←ここ
end

editアクションに @forms = Form.find_by(id: params[:id]) を設定して、URLと同じidのデータを取得させます。

views

app/views/任意.html.erb
<h1>編集ページ</h1>
<div class="form">
  <div>
    <textarea><%= @forms.content %></textarea>  ←ここ
    <input type="submit" value="保存"> </div>
</div>

<%= @forms.content %> により、入力の初期値を設定しています。

編集内容の保存

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

フォームから値を受け取るので、routesでは get ではなく post で実行してください。

config/routes.rb
post "form/:id/new" => "form#new"

リダイレクトの設定

アクションにフォームから値を受け取る準備をしたら、submitを押した後にリダイレクトさせる必要があります。

app/views/任意.controllers.rb
def new
    redirect_to("/")
end

redirect_to("/") を利用してリダイレクト設定を行います。 / は私が任意で設定しているので個人でアレンジしてください。

Viewsで送信先の指定する

先により フォームからの値の取得・リダイレクト を行いました。
ここでは、Viewsで送信先を指定します。

app/views/任意.controllers.erb
<h1>編集ページ</h1>
<%= form_tag("/posts/#{@post.id}/new") do %> ←ここ
<div class="form">
  <div>
    <textarea><%= @forms.title %></textarea> 
    <textarea><%= @forms.content %></textarea> 
    <input type="submit" value="保存"> </div>
</div>
<% end %> ←ここ

↓ 詳細

app/views/任意.html.erb
<%= form_tag("/posts/#{@post.id}/new") do %>
フォームタグ
<% end %>

↑により、送信先が指定されます。

投稿内容の更新を行う

データーベースからデータを取得する必要があります。
取得アクションは、アクション・name属性の追加で行えるので、実行しましょう。

Viewでname属性を指定する

app/views/任意.html.erb
<h1>編集ページ</h1>
<%= form_tag("/posts/#{@post.id}/new") do %>
<div class="form">
  <div>
    <textarea name="content"><%= @forms.content %></textarea>  ←ここ
    <input type="submit" value="保存"> </div>
</div>
<% end %>

コントローラーで params と save で指定

params では、送られてきたデータを保持するオブジェクトを作成できます。

app/controllers/任意.controller.rb
def update
    @forms = Form.find_by(id: params[:id])
    @forms.content = params[:content]
    @forms.save

    redirect_to("/")
  end

あとがき

ここまで読んでいただき、ありがとうございました。
初期値の設定などの処理が理解できるまで、少し時間がかかりそうですが地道に理解を深めたいと思います。
また、ここの紹介だけでは"投稿ページへのリンク"など詳細を省いてしまってるので、自作されるかたは設定することをおすすめします。

参考リンク

Ruby on Rails6 実践ガイド

Myリンク

また、Twitter・Portfolio のリンクがありますので、気になった方は
ぜひ繋がってください。プログラミング学習を共有できるフレンドが出来るととても嬉しいです。

Twitter
Portfolio
Github

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

コントローラーのアクションについて

Railsで使用するアクションとは何か?

コントローラーで定義されるメソッドを『アクション』と言います。(コメントからご指摘を受け認識誤りでしたので文章を改善いたします。↓の意味合いで間違いないと思います。)
コントローラーで定義されるメソッドを指してアクションと呼びます。
これを理解するになるには、MVCについて理解しないといけないです。それを理解した上で、情報を持ったルーティングからコントロラーに情報が送られ、ビューで様相を整え表示させる。この流れの、ルーティングから情報を持った内容にアクションを組み合わせることで、その情報で何を行いたいかの指針を決める役割になるのです。(そう言う認識でいます)
主として使用するアクションとして7つありますのでそれらを調べていきます。

7つのアクションについて

それぞれの役割を要約したものは以下のとおりです。
index(HTTPメソッド = get )
リソースの一覧表をするものです。要は、あるホームページを検索して、検索結果のurlをクリックして、そのホームページの一覧が表示される状態(処理)を指します。
new(HTTPメソッド = get )
リソースを新規作成するものです。要は、ユーザー等の情報をデータベースに登録する際に行う処理を指します。
create(HTTPメソッド = post )
リソースを新規作成して追加(保存)するものです。要は、ユーザー等の情報をデータベースに保存する際に行う処理を指します。
edit(HTTPメソッド = get )
リソースを更新するためのフォームを作成するものです。要は、データベースに保存している情報に編集を加える処理を指します。
show(HTTPメソッド = get)
レコードの内容を表示させるものです。要は、一覧表示されている情報から、ある特定の箇所に遷移する際の処理を指します。例)詳細ページに遷移する
update(HTTPメソッド = patch/put)
リソースを更新するものです。要は、編集した内容を、データベースに反映させる処理を指します。
destroy(HTTPメソッド = delete)
リソースを削除するものです。要は、データベースに保存されている情報を消去する処理を指します。
※リソースについて、プログラムを利用するデータ全般のことを指します。要は、パソコンで使用する情報的なことだと言う認識です。
※HTTPメソッドに関しては、こちらで記述してます。

ややこしい違いを調べました

上記でのアクションに、「あれ、同じことを言ってね」と思った箇所がいくつかあったかと思います。なので、それを砕いていきます。

newとcreateって何が違う?

簡単にいきますよ、newはオブジェクトを生成するだけでデータベースに保存されないものです。createは、オブジェクトを生成して、データベースに保存するものです。アプリケーションを作成する際は、臨機応変に使用するといったものだと考えてます。

saveについて

調べていくと、saveメソッドというものがありました。このメソッドは、newの中で使うもので、保存すると言った意味を持ったメソッドです。で、ここで疑問に思うものが、createと何が違うのか?ということです。違いとしては、newでオブジェクトを生成してからsaveを行えるので、その中で行いたい処理等があるのであれば、それをやってから保存すると言った流れができます。(多分こんな感じ、違ったらご指摘ください?)

editとupdateの違いについて

newとcreateとの違いについてと同じで、editはオブジェクトを生成するだけでデータベースに保存されません。updateはオブジェクトを生成して、保存を行うものです。

まとめ

Railsを使用する際には、必要な知識なので覚えるようにしたいと思います。

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

Ruby on Rails チュートリアル 第一章

はじめに

自分がRailsを勉強するときに、簡単にまとめたやつがあったらいいのにと思ったので学習しながら編集しました。
自分の勉強用です。


AWSでのCloud9の設定はとばします。

1.2.2 Railsをインストールする

ターミナル
$ echo "gem: --no-document" >> ~/.gemrc

Rubyドキュメントをインストールしないよう.gemrcファイルを設定する
.gemrcって言うのはgemの設定ファイル
設定は「gemのインストールやアップデートの際にドキュメントファイル生成しない」的な感じ
ドキュメントとは、説明書のこと。
Gemを使うに当たって絶対必要ではなく、サイズも大きくなるし時間もかかるからスルーしますというやつ。

ターミナル
$ gem install rails -v 6.0.3

$ rails -v 

$ ruby -v 

Railsのインストールとバージョン確認をする。

ターミナル
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)

JavaScriptの依存関係を管理するYarnというプログラムをインストールする。

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

ターミナル
$ cd ~/environment
$ rails _6.0.3_ new hello_app 

rais newを実行する(バージョン番号を指定)
並び順は rails バージョン指定 new アプリ名

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

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

group :development do
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'capybara',           '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  gem 'webdrivers',         '4.1.2'
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Gemfileを書き換える。

ターミナル
$ cd hello_app/
$ bundle install

gemをインストールする。
ここでエラーが出てしまったのでbundle uddateする。

ターミナル
$ bundle uddate

再度bundle installする。

ターミナル
$ bundle install

1.3.2 rails server

config/environments/development.rb
Rails.application.configure do
  .
  .
  .
  # Cloud9 への接続を許可する
  config.hosts.clear
end

Webサーバーへの接続を許可が必要らしいので、config/environments/development.rbを開いて2行足す。

ターミナル
$ cd ~/environment/hello_app/
$ rails server

実行したままにしたいので、もう1つターミナルを開いてRailsサーバーを実行する。
ブラウザを開いて、ヾ(´∀`)ノYay! You’re on Rails!ヾ(´∀`)ノわーい みたいのが出たらOK。

1.3.4 Hello, world!

ここからMVCモデルに入る。

ターミナル
#app/controllers/の中にある〇〇controller.rbと言うファイルは?
$ ls app/controllers/*_controller.rb
#結果
app/controllers/application_controller.rb

ファイルを調べる。まずは第一章ではまずコントローラから。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  #helloアクション
  def hello   
    render html: "hello, world!" #HTMLで表示したい:"表示したい内容"
  end
end

3行追加する。これでこの後のルーティングを設定すると"hello, world!"が表示できるようになるらしい

config/routes.rb
Rails.application.routes.draw do
  root 'application#hello'
end

#root(一番上、/)では'application_controller'のhelloアクション'を実行するという意味。
'application_controller'のhelloアクション'というのは1個前に設定したやつ。

ここでエラーが出てしまった。エラーの内容は読み込み中がすごい長いというもの。
Rails sを再起動しようと思ってRails sを実行しているターミナルを見ると、終了できなくなってしまっていた。

ターミナル
$ ps aux | grep [p]uma

$ kill -9 <出てきたプロセス番号>

これでもう一度Rails sを再起動できた。
ブラウザに、Hello,world!と表示されればOK。

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

ターミナル
$ git config user.name #登録されていればここに表示される

$ git config user.email #登録されていればここに表示される

#登録されていなければ下記コマンドで登録
$ git config --global user.name "Your Name"
$ git config --global user.email your.email@example.com

git configで設定。

ターミナル
$ sudo ln -sf `which nano` /usr/bin
$ git config --global alias.co checkout

Gitで使うデフォルトのエディタを設定。
git coをcheckoutのエイリアスに設定。これから'git checkout' を'git co'と省略できる。

ターミナル
$ cd hello_app #アプリのルートディレクトリに移動する
$ git init #リポジトリの初期化
$git add -A #すべての更新ファイルをステージに移動
$git status #ステージングの状況を確認 #現状ではすべての更新ファイルがステージングされて緑で表示

$git commit -m "Initialize repository" #メッセージを追加するcommitのコマンド メッセージ→"リポジトリを初期化"
$ git log #ログ表示 「q」で終了

git initでセットアップする。

1.5.1 Herokuのセットアップ

gemfileを書き換える。

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

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

group :development do
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'capybara',           '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  gem 'webdrivers',         '4.1.2'
end

group :production do
  gem 'pg', '1.1.4'
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Herokuではsqlite3は使えない。PortgreSQLを使う。
本番(production)環境にpg gemをインストールしてRailsがPostgreSQLと通信できるようにする。
sqlite3 gemが本番環境に導入されないようにしておく。

ターミナル
bundle install --without production

本番用以外のgemをインストールする。

ターミナル
$ heroku --version

Herokuがインストールされているか確認する。されていなかった。

ターミナル
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)

Herokuをインストールする。

ターミナル
$ heroku --version

確認。OK。

ターミナル
$ heroku login --interactive

herokuにログインする。 --interactiveはブラウザを開かないようにするやつ。

ターミナル
$ heroku create

Herokuに新しいアプリケーションを作成する。

ターミナル
$ git push heroku master

HerokuにリポジトリをPUSHする。
heroku createの時に出てきたURLを開くとデプロイ完了が確認できる。

ターミナル
$ rails webpacker:install 

webpackerがないと怒られたとき。

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

小数点以下を切り捨てたい

【概要】

1.結論

2.どのように使うのか

3.ここから学んだこと

1.結論

floorメソッドを使う!


2.どのように使うのか

def cal_points(point)
  if point < 6000
    cal_point = points * 0.05
  else
    cal_point = points * 0.1
  end
  puts "ポイントは#{cal_point.floor}点です"
end

上記ではif条件式の中で小数点が入った計算を#{cal_point.floor}を小数点以下を切り捨てています。
式展開にfloorメソッドを適用しています。

参考URL:
Rubyで数値の切り捨て・切り上げ・四捨五入する

3.ここから学んだこと

他にもroundメソッド(四捨五入),ceilメソッド(切り上げ)があります。また、.round(1)とすることで"小数点第1位まで四捨五入"や.round(-1)とすることで1の位を四捨五入できます。

ex)1.23.round(1) #1.2
ex)123.round(-1) #120


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

テーブルにテーブルの一番古いデータを削除していくメソッドを作成して実行するようにしたい

環境

・Ruby 2.6.6, Rails 6.0.3.2
・Docker,Docker-compose(開発環境)

困ったこと

Newsテーブルにある一番古いデータを削除していくメソッドを作成して実行したいのだが、添付画像のエラーが表示される。

スクリーンショット 2020-09-24 8.04.39.png

  def destroy_oldest_news
    news = News.all
    n = news.last(1)
    n.destroy
  end

調べたこと・確認したこと

①binding.pryで変数の値を確認。配列でデータが返ってきた。

pry(#<Guide::ToursController>)> n
=> [#<News:0x00007f8084d6e848 id: 5, created_at: Tue, 25 Aug 2020 16:18:18 JST +09:00, updated_at: Tue, 25 Aug 2020 16:18:18 JST +09:00, tourist_id: 1, guide_id: nil, tour_id: 1348, action: "review">]

②配列のデータに対してはdestroyメソッドを使う事ができない事が分かり、配列のデータを消してくれるpopメソッド・delete_at(0)をやってみたが、そもそもデータベースからデータは消してくれない事が分かった。

  def destroy_oldest_news
    news = News.all
    n = news.last(1)
    n.delete_at(0)
  end
  def destroy_oldest_news
    news = News.all
    n = news.last(1)
    n.pop
  end

解決方法

  def destroy_oldest_news
    news = News.all
    n = news.last
    n.destroy
  end

解説

lastメソッドを個数指定で実行すると数に応じた配列を返ってしまう。
一つのデータを削除する場合はlastのみで使う。

反省

これまで個数指定でしか使っていたので、個数指定をしなくても使えることを知らなかった。
メソッドを使うときはそのメソッドについての知識を付けないといけない。

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

Rails6 テーブルの一番古いデータを削除していくメソッドを作成して実行するようにしたい

環境

・Ruby 2.6.6, Rails 6.0.3.2
・Docker,Docker-compose(開発環境)

困ったこと

Newsテーブルにある一番古いデータを削除していくメソッドを作成して実行したいのだが、添付画像のエラーが表示される。

スクリーンショット 2020-09-24 8.04.39.png

  def destroy_oldest_news
    news = News.all
    n = news.last(1)
    n.destroy
  end

調べたこと・確認したこと

①binding.pryで変数の値を確認。配列でデータが返ってきた。

pry(#<Guide::ToursController>)> n
=> [#<News:0x00007f8084d6e848 id: 5, created_at: Tue, 25 Aug 2020 16:18:18 JST +09:00, updated_at: Tue, 25 Aug 2020 16:18:18 JST +09:00, tourist_id: 1, guide_id: nil, tour_id: 1348, action: "review">]

②配列のデータに対してはdestroyメソッドを使う事ができない事が分かり、配列のデータを消してくれるpopメソッド・delete_at(0)をやってみたが、そもそもデータベースからデータは消してくれない事が分かった。

  def destroy_oldest_news
    news = News.all
    n = news.last(1)
    n.delete_at(0)
  end
  def destroy_oldest_news
    news = News.all
    n = news.last(1)
    n.pop
  end

解決方法

【参考URL】
https://docs.ruby-lang.org/ja/latest/method/Array/i/last.html

  def destroy_oldest_news
    news = News.all
    n = news.last
    n.destroy
  end

解説

lastメソッドを個数指定で実行すると数に応じた配列を返ってしまう。
一つのデータを削除する場合はlastのみで使う。

反省

これまで個数指定でしか使っていたので、個数指定をしなくても使えることを知らなかった。
メソッドを使うときはそのメソッドについての知識を付けないといけない。

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

【Ruby】セキュアなパスワードポリシー設定のための正規表現

1.はじめに

 Webアプリケーション作成の際に特に意識をしないとならない箇所はセキュリティ面です。パスワードに関しては慎重に取り扱わなければいけません。
 この記事ではパスワードポリシー設定の際に正規表現を使うことが有用という話を聞きましたので、パスワードと正規表現について考察してみました。

2.正規表現とはなにか

 初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」によると

「パターンを指定して、文字列を効率よく検索・置換するためのミニ言語」

ということみたいです。
 実際にRuby用の正規表現エディタのRubularというWebサイトを使用して、正規表現がどういったものか見ていきます。
例えば下記のようなテキストがあります。

8月30日学習時間:1時間
8月31日学習時間:3時間
9月1日学習時間:2時間
9月2日学習時間:4時間
9月3日学習時間:6時間
9月4日学習時間:10時間

この中で9月の学習時間の数字だけ正規表現を使って抽出します。
正規表現を使うと下記のような文字列になります。

  ^9.*:(\d*)時間$

^.*などの正規表現で使用する特別な文字を「メタ文字」と呼びます。
ここでのメタ文字の意味は後述するとしてRubularで検証していきます。
Rubularの「Your test string」に検証したいテキストを、「Your regular expression」に正規表現を入力していきます。
そうすると下記のような結果になります。
スクリーンショット 2020-09-23 10.41.33.png
「Match result」の水色の部分が検索して見つかった部分です。
「Match groups」がキャプチャされた部分です。ここで学習時間を抽出します。
こうやって正規表現を使用して文字列を検索していきます。

3.この記事で使用する正規表現のメタ文字

参考にしました→基本的な正規表現一覧

メタ文字 意味
^ 文頭を表す
$ 文末を表す
[] いずれか1文字を表す文字クラスを作る
- []内で使われる文字の範囲を示す
. 任意の1文字を表す
? 直前の文字やパターンが1回、もしくは0回現れる
* 直前の文字やパターンが0回以上連続する。条件に合う最長の部分に一致
*? 直前の文字やパターンが0回以上連続する。条件に合う最短の部分に一致
+ 直前の文字やパターンが1回以上連続する。条件に合う最長の部分に一致
() 内部でマッチした文字列をキャプチャもしくはグループ化する
{n,m} 直前の文字やパターンがn回以上、m回以下連続する
\ メタ文字をエスケープしたり、\dや\wといった他のメタ文字の一部になったりする
\d 1個の半角数字(0123456789)
\w 半角英数字とアンダースコア'_'
(?=x) xの「直前の位置」を表す(肯定先読み)

 

4.どういったパスワードポリシーを設定すればよいのか

 コンピュータの性能は日進月歩で向上しているため、パスワードの暗号解読(例えば総当り攻撃)のスピードは年々上がっていっています。それに伴いパスワードの複雑化が求められています。
 2020年3月31日発行の内閣サイバーセキュリティーセンター「インターネットの安全・安心ハンドブック Ver.4.10」のP30によると、パスワードの安全性を高めるためのポリシーとして「英大文字+英小文字+数字+記号混じりで10桁以上」を推奨しています。
 ここではハンドブックに則ったパスワードポリシーを正規表現にて作成していきます。

5.パスワードポリシー設定のための正規表現

 ここでは段階的にそれぞれの正規表現を考えていきたいと思います。

 (1) 10桁以上40桁以下の半角英数記号
 (2) 英大文字1桁以上+英小文字1桁以上の条件追加
 (3) 数字1桁以上の条件追加
 (4) 記号1桁以上の条件追加

すべてのパターンを網羅できているわけではありませんが、上から

 ・9桁の文字
 ・10桁の英小文字
 ・10桁の英大文字+英小文字
 ・10桁の英大文字+英小文字+数字
 ・10桁の英大文字+英小文字+数字+記号
 ・41桁の文字

で下記のテキストで検証をしていきます。

Aaaaaaa1?
aaaaaaaaaa
Aaaaaaaaaa
Aaaaaaaaa1
Aaaaaaaa1?
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1?

 (1) 10桁以上40桁以下の半角英数記号

  正規表現で使用する文字についてはASCIIコードを使用して指定ができます。ここでの半角英数字記号はASCIIコード表の!から~までですので、下記のようになります。

Ruby
^[!-~]{10,40}$

予想通りの結果となりました。
スクリーンショット 2020-09-23 23.43.47.png

 (2) 英大文字1桁以上+英小文字1桁以上の条件追加

  理解するのに時間がかかりましたが、段階を踏んで説明します

   a.肯定的先読み(?= )を使用して、条件の順序の依存をなくす
   b.条件の前の文字は、任意の1文字.を最短一致で0回以上連続*?する
   c.条件を設定する[A-Z][a-z]

  参考にしました→パスワード向け正規表現 /^(?=.?[a-z])(?=.?\d)[a-z\d]{8,100}$/i を解読する

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])[!-~]{10,40}$

成功しました。

スクリーンショット 2020-09-24 0.26.35.png

 (3) 数字1桁以上の条件追加

  (2)に数字0-9の条件\dを追加しました。

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?\d)[!-~]{10,40}$

成功しました。

スクリーンショット 2020-09-24 0.31.15.png

 (4) 記号1桁以上の条件追加

  ASCIIコードを確認して記号を指定しました。
  記号とメタ文字がかぶる箇所については、エスケープ\をしました。

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?\d)(?=.*?[!-\/:-@\[-`{-~])[!-~]{10,40}$

成功しました。
記号確認用にテキストを追加しました。

スクリーンショット 2020-09-24 0.49.20.png

6.まとめ

 正規表現は理解するのに骨が折れる部分がありました。パスワードポリシー設定以外にも色々使わせていただきます。

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