質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

なぜJSON.parseが必要なのか、変数に入る値とは?

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 613

kku65614

score 2

前提・実現したいこと

Ruby on rails 5で書籍を読みつつ簡易掲示板を写経しています。
モデルは使わず、コントローラーとビューのみをつかっていて、データはdata.txtに保管しています。
そのときにどうしてもわからない部分があったため質問しました。

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

dengonban_controller.rb内の@dengon_dataというインスタンス変数があると思います。
この中には、JSON形式の投稿内容が入りますが、DengonbanControllerのindexメソッド内での@dengon_dataの挙動が理解できません。
念のためですが、挙動を確認しつつ、理解できないところをあげていきます。
※今回聞きたいところは太字にしてあります。

  1. request.post?でリクエストがPOSTかどうか判別する(今回はPOSTだったとして進めます)
  2. obj変数に、MyDataクラスでオブジェクトにした(←なぜこうするのか、理解できていない)投稿内容を入れる
  3. @dengon_dataに投稿があった時間をキーとして手順2で作ったオブジェクトを入れる
  4. data.txtに新しく投稿された内容を含めたデータを入れるために@dengon_datato_jsonでJSON形式の文字列にする
  5. File.writeを使い、data.txtの内容を更新する
  6. JSON.parse(data)でJSONを@dengon_dataに代入(なぜ@dengon_dataのままではいけないのか、理解できない)

該当のソースコード

dengonban_controller.rb

class DengonbanController < ApplicationController
  layout "dengonban"

  def initialize
    super

    begin
      @dengon_data = JSON.parse(File.read("data.txt"))
    rescue
      @dengon_data = Hash.new
    end

    @dengon_data.each do |key, obj|
      if Time.now.to_i - key.to_i > 24*60*60
        @dengon_data.delete(key)
      end
    end

    File.write("data.txt", @dengon_data.to_json)

  end

  def index
    if request.post?
      obj = MyData.new(message: params["msg"], namae: params["name"], mailaddress: params["mail"])
      @dengon_data[Time.now.to_i] = obj
      data = @dengon_data.to_json
      File.write("data.txt", data)
      @dengon_data = JSON.parse(data)
    end
  end
end

class MyData
  attr_writer(:name, :mail, :msg)

  def initialize(message: msgs, namae: names, mailaddress: mails)
    self.name = namae
    self.mail = mailaddress
    self.msg = message
  end
end

view/dengonban/index.html.erb

<table class="form">
  <%= form_tag controller: "dengonban", action: "index" do %>
      <tr>
        <td>name:</td>
        <td>
          <%= text_field_tag("name") %>
        </td>
      </tr>
      <tr>
        <td>mail:</td>
        <td>
          <%= text_field_tag("mail") %>
        </td>
      </tr>
      <tr>
        <td>message:</td>
        <td>
          <%= text_area_tag("msg", "", {:size => "50x3"}) %>
        </td>
      </tr>
      <tr>
        <td></td>
        <td>
          <%= submit_tag("click") %>
        </td>
      </tr>
  <% end %>
</table>

<table class="dengon">
  <tr>
    <th style="width: 50%;">message:</th>
    <th>name:</th>
    <th>mail:</th>
    <th>message:</th>
  </tr>
  <% @dengon_data.each do |key, obj| %>
      <tr>
        <td class="msg"><%= obj["msg"] %></td>
        <td class="name"><%= obj["name"] %></td>
        <td class="mail"><%= obj["mail"] %></td>
        <td class="time"><%= Time.at(key.to_i) %></td>
      </tr>
  <% end %>
</table>


view/layouts/dengonban.html.erb

<!DOCTYPE html>
<html>
<head>
  <title>伝言板</title>
  <%= csrf_meta_tags %>
  <%= stylesheet_link_tag("application", media: "all", "data-turbolinks-track": "reload") %>
  <%= javascript_include_tag("application", "data-turbolinks-track": "reload") %>
</head>
<body>
<h1 class="title">簡単伝言板</h1>
<%= yield %>
<div class="footer">copyright hogehoge</div>
</body>
</html>


routes.rb

Rails.application.routes.draw do
  get 'dengonban/index', to: "dengonban#index"
  post 'dengonban/index', to: "dengonban#index"

  get "helo/index", to: "helo#index"
  post "helo/index", to: "helo#index"
end

dengonban.scss

h1.title {
  font-size: 20pt;
  color: #999999;
}

.form {
  margin: 50px 0;
}

.form tr td {
  color: #999999;
  padding: 0 10px;
  font-size: 14pt;
}

th {
  background: #999999;
  color: #eeeeee;
  font-size: 14pt;
  padding: 2px;
}

.dengon tr td {
  background-color: #eeeeee;
  color: #666;
  padding: 10px 5px;
}

td.msg, td.name, td.mail {
  font-size: 12pt;
}

td.time {
  font-size: 9pt;
}

div.footer {
  border-style: solid;
  border-width: 1px 0 0 0;
  border-color: #999999;
  margin: 50px 0 0 0;
  padding: 3px 0 0 0;
  color: #999999;
  font-size: 12pt;
  text-align: right;
}

試したこと

発生している問題・エラーメッセージの6番で言った「なぜ@dengon_dataのままではいけないのか、理解できない」について試したことがあります。
それは@dengon_data = JSON.parse(data)を削除してdata.txtに入る内容を見るというものです。その結果得られた文字列が下になります。

{1513499068=>#<MyData:0x000000000ca58c58 @name="s", @mail="s", @msg="s">}


謎の数列(恐らくタイムスタンプ)とMyDataとその後の記号、そのあとにはMyDataクラスに渡した変数の内容が入っています。
このことから、@dengon_dataに新しく入った値はJSONの形式ではないからJSON形式の文字列にしたものをJSONに戻して代入しなければならないのかなあ、と考えていますが、あまり自信がありません。

これを試した結果、JSON形式にしたり、それを文字列にしたり戻したりしていてややこしいため、何をしているのか理解しきることができないのかなと考えています。

補足情報(言語/FW/ツール等のバージョンなど)

Ruby on rails 5

 追記

 書籍のソースコード

上記のソースコードは私が書籍のコードを理解するために色々な編集を加えたものでした。質問と回答の食い違いを防ぐために、追記として書籍のコードを記述させていただきます。ここに記述されていないファイルのものは、上記と同じ内容のものです。

MyDataクラスinitializeメソッドについて、後述の私が参考にしている書籍で使用されるRubyのバージョンでは警告が発生しますが、これが書籍に載っている内容であることをあらかじめご理解お願いします。
dengonban_controller.rb

class DengonbanController < ApplicationController
  layout "dengonban"

  def initialize
    super

    begin
      @dengon_data = JSON.parse(File.read("data.txt"))
    rescue
      @dengon_data = Hash.new
    end

    @dengon_data.each do |key, obj|
      if Time.now.to_i - key.to_i > 24*60*60 then
        @dengon_data.delete(key)
      end
    end

    File.write("data.txt", @dengon_data.to_json)

  end

  def index
    if request.post?
      obj = MyData.new(msg: params["msg"], name: params["name"], mail: params["mail"])
      @dengon_data[Time.now.to_i] = obj
      data = @dengon_data.to_json
      File.write("data.txt", data)
      @dengon_data = JSON.parse(data)
    end
  end
end

class MyData
  attr_accessor(:name)
  attr_accessor(:mail)
  attr_accessor(:msg)

  def initialize msg: msg, name: name, mail: mail
    self.name = name
    self.mail = mail
    self.msg = msg
  end
end

 書籍のタイトルについて

「質問への追記・修正、ベストアンサー選択の依頼」にて質問がありました、参考にしている書籍のタイトルについてですが、私が参考にしている書籍は「Ruby on Rails 5 超入門」という書籍で、発行日が2016年12月20日のものです。

どうか、よろしくお願いします

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ockeghem

    2017/12/18 15:37

    書籍のタイトルを教えていただけないでしょうか?

    キャンセル

  • kku65614

    2017/12/18 21:59

    書籍のタイトルについて、質問文に追記させていただきました。ご確認よろしくお願いします。

    キャンセル

回答 1

0

全体に奇妙な印象を受けるコードです。

例えば,MyData の initialize メソッドが

def initialize(message: msgs, namae: names, mailaddress: mails)

のようになっていますね。
仮引数の message: msgs の msgs は,キーワード引数 message のデフォルト値を与えているはずですが,msg が未定義なので,たとえば

MyData.new

とやると NameError が発生します。

また,index アクションでデータを追加するなど,Rails の流儀に反しているようです。

もし書籍にこのとおりに載っているのだとすると,あまりお手本にしないほうがよいコードではないかなあ,という印象です。

それはそれとして,index メソッドでなぜこんな回りくどいことをしているか,ですが,MyData をハッシュにしたいからかもしれません。

MyData を Rails 上で to_json して,JSON.parse すると,ハッシュになります。(一般に,オブジェクトを to_json して,それを JSON.parse しても,元には戻るとは限りません)

MyData のままだと,ビューで obj["msg"] の形で使えませんので。

それならなぜ,①最初からハッシュにしないで MyData なんか定義するのか,とか,②MyData に attr_reader を付けて obj.msg の形で使えるようにすればいいのではないか,という疑問が湧きますが,著者に聞いてみたいですね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/18 22:04

    ご回答ありがとうございます。まだ回答の内容を完全に理解しているわけではないですが(ここに少し時間がかかるかもしれません)、質問文の内容を修正したことと、回答を一度読んだことを伝えるためにコメントしました。

    「該当のソースコード」のコードは私が書籍のコードを理解するために色々な編集を加えたものでしたので、質問と回答の食い違いをなくすために、追記として書籍のコードをそのまま記述させていただきました。また、追記依頼のあった書籍のタイトルについても追記しました。

    大変申し訳ありませんが、以上の点について一度ご確認よろしくお願いします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る