わたろぐ

仕事、読書、ガジェット、グルメ、写真、旅行など雑多な備忘

Ruby on Railsを触ってみる ⑨月別アーカイブ

なんとなくWebアプリっぽくなってきたTwiceだが、Twitterが公式で提供しているアーカイブのダウンロードでは、JSで動作する過去のツイートを参照できる機能が付いている。月別アーカイブと検索機能が付いているため、せめてそれぐらいは実装しないと、ここまで作った甲斐がないというものではないか。

というわけで、月別アーカイブ機能的なものを作りたい。 サイドバーあたりに、投稿があった年月+件数を表示し、選択するとその年月のツイートを参照できる、といった具合にしたい。 コントローラにアーカイブを集計するメソッドを作る。 年月でGroup by するために、timestampをyyyymmに変換し、groupで指定する。

class TweetsController < ApplicationController
  before_action :set_tweet, only: [:show, :edit, :update, :destroy]
  before_action :check_user
  before_action :make_archive

    # ユーザがログインしていなければ、ホームにリダイレクト
    def make_archive
      @user = current_user
      @archives = @user.tweet.group("strftime('%Y%m', tweets.timestamp)")
                             .order("strftime('%Y%m', tweets.timestamp) desc")
                             .count
    end

Tweetページの時のみサイドバーに表示したいので、view/layouts/tweets.html.erbを作成する。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= content_for?(:title) ? yield(:title) : "Twice" %></title>
    <%= csrf_meta_tags %>

    <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
    <!--[if lt IE 9]>
      <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.6.1/html5shiv.js" type="text/javascript"></script>
    <![endif]-->

    <%= stylesheet_link_tag "application", :media => "all" %>

    <!-- For third-generation iPad with high-resolution Retina display: -->
    <!-- Size should be 144 x 144 pixels -->
    <%= favicon_link_tag 'apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144' %>

    <!-- For iPhone with high-resolution Retina display: -->
    <!-- Size should be 114 x 114 pixels -->
    <%= favicon_link_tag 'apple-touch-icon-114x114-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '114x114' %>

    <!-- For first- and second-generation iPad: -->
    <!-- Size should be 72 x 72 pixels -->
    <%= favicon_link_tag 'apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72' %>

    <!-- For non-Retina iPhone, iPod Touch, and Android 2.1+ devices: -->
    <!-- Size should be 57 x 57 pixels -->
    <%= favicon_link_tag 'apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png' %>

    <!-- For all other devices -->
    <!-- Size should be 32 x 32 pixels -->
    <%= favicon_link_tag 'favicon.ico', :rel => 'shortcut icon' %>

    <%= javascript_include_tag "application" %>
  </head>
  <body>

    <div class="navbar navbar-fluid-top">
      <div class="navbar-inner">
        <div class="container-fluid">
          <a class="btn btn-navbar" data-target=".nav-collapse" data-toggle="collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </a>
          <a class="brand" href="#">Twice</a>
          <div class="container-fluid nav-collapse">
            <ul class="nav">
              <li><%= link_to "Link1", "/path1"  %></li>
              <li><%= link_to "Link2", "/path2"  %></li>
              <li><%= link_to "Link3", "/path3"  %></li>
              <% if user_signed_in? %>
                <li><%= link_to "ログアウト", (destroy_user_session_path)  %></li>
              <% else %>
                <li><%= link_to "ログイン", (user_omniauth_authorize_path :twitter)  %></li>
              <% end %>
            </ul>
          </div><!--/.nav-collapse -->
        </div>
      </div>
    </div>

    <div class="container-fluid">
      <div class="row-fluid">
        <div class="span3">
          <div class="well sidebar-nav">
            <ul class="nav nav-list">
              <li class="nav-header">Archives</li>
              <% @archives.each do |yyyymm, count| %>
              <li><%= ymconv(yyyymm, count.to_s) %><li>
              <% end %>
            </ul>
          </div><!--/.well -->
        </div><!--/span-->
        <div class="span9">
          <%= bootstrap_flash %>
          <%= yield %>
        </div>
      </div><!--/row-->

      <footer>
        <p>&copy; Company 2014</p>
      </footer>

    </div> <!-- /container -->

  </body>
</html>

年月件数表示にはヘルパーメソッドを作成した。 コントローラではyyyyMMと件数という形式で集計結果が出される。'yyyy年MM月'とも出せたのだが、アーカイブ表示画面の遷移に使えなくなりそうなので、単純な形式にした。そこでtweet_helper.rbにymconvというヘルパーメソッドを作り'yyyy年MM月(件数)'という形式の文字列で出力させることでViewをスッキリさせる。

# coding: utf-8
module TweetsHelper
  def ymconv(yyyymm,cnt)
    yyyy = yyyymm[0,4]
    mm = yyyymm[4,2]
    
    return yyyy + "" + mm + "月 (" + cnt + ")"
  end
end

こんな感じで表示される。

ここからリンクをクリックしたら、その年月のツイートが表示されるようにしたい。 コントローラにメソッドを追加する。 年月のパラメータを受け取って、ツイートを絞り込む。

  # GET /tweets/archives?yyyymm=201402
  def archives
    @user = current_user
    @yyyymm_now = params[:yyyymm]
    @tweets = @user.tweet
             .where("strftime('%Y%m', tweets.timestamp) = '"+@yyyymm_now+"'")
             .order(:id)
             .page params[:page]
  end  

ルーティングも修正する。archivesへのルートを追加。

  resources :tweets do
    collection do
      get 'import_csv_new'
      post 'import_csv'
      get 'archives'
    end
  end

「yyyy年MM月のツイート」と表示したいので、もう一つhelperメソッドを作る。

 def ymconvn(yyyymm)
    yyyy = yyyymm[0,4]
    mm = yyyymm[4,2]
    
    return yyyy + "" + mm + ""
  end

これを使ってViewファイル、archives.html.erbを作成する。

<h1><%= ymconvn(@yyyymm_now) %>のツイート一覧</h1>

<%= render(@tweets) %>
<% if @tweets.count == 0 %>
  <p><%= ymconvn(@yyyymm_now) %> のツイートはありません</p>
<% end %>
<%= paginate (@tweets) %>

ツイートがあればこんな感じで表示される。 なければこんな感じ。

add archive in sidebar twata701/twice GitHub