【问题标题】:Directly adding API data into a hash直接将 API 数据添加到哈希中
【发布时间】:2021-03-04 10:50:01
【问题描述】:

我正在使用这个 GiantBomb API(https://github.com/games-directory/api-giantbomb) 来获取游戏列表。用户搜索游戏,然后将其添加到他们的库中。用户还可以查看其他人的图书馆。我让它工作,除了我添加到库中的项目无法调用所有 API 数据。我想将 GiantBomb::Game 数据放入一个 has 中,这样我就可以在视图中调用数据,例如 @game.description 等等。

这是我的游戏控制器。有一个搜索功能、单个游戏的显示页面,以及将单个游戏添加到用户库页面的我的库功能。

class GamesController < ApplicationController

#Users search for games
  def index
  @games = GiantBomb::Search.new().query(params[:query]).resources('game').limit(100).fetch
  end

#individual game profile page
  def show
  @game = GiantBomb::Game.detail(params[:id])
  end

#Adding games to user libraries
  def library
    type = params[:type]
    @game = Game.new(game_params)

    if type == "add"
      current_user.library_additions << @game
      redirect_to user_library_path(current_user), notice: "Game was added to your library"

    elsif type == "remove"
      current_user.library_additions.delete(@game)
      redirect_to root_path, notice: "Game was removed from your library"
    else
      # Type missing, nothing happens
      redirect_to game_path(@game), notice: "Looks like nothing happened. Try once more!"
    end
  end


private

  def game_params
    params.require(:game).permit(:name, :id)
  end
end

将游戏添加到库中查看此代码在视图中。

 <% if user_added_to_library?(current_user, game) %>

            <%= link_to 'Remove from library', add_game_path(game['id'], type: "remove", game: game), method: :put %>

          <% else %>
            <%= link_to 'Add to library', add_game_path(game['id'], type: "add", game: game), method: :put %>
          <% end %>

在我的展示页面上,我通过 GiantBomb::Game.detail(params[:id]) 直接从 API 中提取,所以我可以调用如下参数:

<div class="cards">

  <div class="card">
  <%= image_tag @game.image['medium_url'], class: "cover"%>
  <div class="container">
    <h2><%= @game.name %></h2>
    <p><%= @game.deck %></p>
    <p><%= @game.id %></p>
    <% if @game.platforms === nil %>
    <p>Platform Unknown</p>
    <% else %>
    <p><%= @game.platforms[0]['name'] %></p>
    <% end %>
    <p><%= @game.original_release_date.to_s[0..3] %></p>
  </div>
</div>
  </div>

但是,我的库函数不能使用@game = GiantBomb::Game.detail(params[:id]),我必须使用@game = Game.new(game_params)。我的游戏模型只有 name 和 id 与之关联,所以我似乎只能在库页面上显示该信息。

这是我的图书馆控制器。

class LibraryController < ApplicationController
  #find user id through params
  def index
    @library_games = User.find(params[:id]).library_additions
  end
end

这是索引页:

<h1>Library</h1>

<% if @library_games.exists? %>
<% @library_games.each do |game| %>
    <div class="container">
      <p><%= game.name%></p>
      <p><%= game.id %></p>

    </div>
  <% end %>
  <% else %>
  <div class="container">
    <div class="message-body">You haven't added any games to your library yet. <%= link_to 'Add some', root_path %>.</div>
  </div>
  <% end %>

如果我使用 game.deck,我会得到一个未定义的方法,但如果我使用 game['deck'],我不会收到错误消息,但在我的显示页面中没有显示任何内容。

所以我是否需要为我的游戏模型中的每个单独的数据(甲板、图像、平台等)添加迁移,或者有没有办法编辑我的库方法以便我可以传递所有 GiantBomb: :游戏数据到我的 library_additions 中,这样我就可以通过@game.insertdatahere 调用任何数据?

【问题讨论】:

    标签: ruby-on-rails api hash


    【解决方案1】:

    正如您所指出的,您的 Game 模型仅存储 nameid,因为这就是 game_params 包含的全部内容,并且可能也是您在基础表中定义的唯一列。如果您查看您的数据库 (rails db) 并运行 SELECT * FROM games;,您可以看到实际保存到数据库中的数据。

    因此,如果您想存储完整的数据集,最快的答案是将其存储为任意散列。 GiantBomb::Game 记录并没有一种简单的方法来获取其中数据的哈希值,但是您可以使用一些元编程的 sn-p 来提取其中的所有内容:

    data = Hash[game.instance_variables.map { |var| [var.to_s[1..-1], game.instance_variable_get(var)] } ]
    

    然后,您可以将哈希存储在一个新的 TEXT 列中并告诉 Rails 到 serialize 它,或者如果您使用支持它的数据库,直接存储到 JSON/@987654331 @ 柱子。由于我不知道你的数据库,这里是序列化方法的一个例子:

    # in a new migration
    change_table :games do |t|
      t.text :data
    end
    
    # game.rb
    class Game < ApplicationRecord
      serialize :data
    
      # this method will overwrite the `data` column with the latest game data from the
      # API. I've extracted it so refreshing the data if it becomes stale is easier.
      def fetch_data
        game = GiantBomb::Game.detail(id)
        self.data = Hash[game.instance_variables.map { |var| [var.to_s[1..-1], game.instance_variable_get(var)] } ]
      end
    
      # ...
    end
    
    # in your controller
    game = Game.new(game_params)
    game.fetch_data
    
    # in wherever you want to access the data
    platforms = game.data["platforms"]
    

    现在,我看了一下 GiantBomb 代码,它仅从普通哈希(我假设是从 JSON API 调用解析)实例化自己的记录,因此您也可以从存储的数据中重新实例化它们:

    giant_bomb_game = GiantBomb::Game.new(game.data)
    

    这可能会帮助您解决倒数第二段中的问题:您的视图都可以使用 GiantBomb::Game 类型的实例,而不必处理有时获取 Game 有时获取 GiantBomb::游戏。

    例如,如果您将上述内容作为新方法添加到游戏中:

    class Game < ApplicationRecord
      # ...
     
      def to_giant_bomb_game
        GiantBomb::Game.new(data)
      end
    end
    

    然后您的视图可以使用该方法简洁地将对象重新水化为您想要的:

    <h1>Library</h1>
    
    <% if @library_games.exists? %>
    <% @library_games.map(&:to_giant_bomb_game).each do |game| %>
        <div class="container">
          <p><%= game.name%></p>
          <p><%= game.id %></p>
          <p><%= game.deck %></p>
    //etc
    

    我可能应该在这里停下来,但是因为我们在 Ruby 中,可能(并且很容易)更进一步,让 Game 表现得好像它是 GiantBomb::Game。如果这是您付费开发的生产应用程序,请立即返回,否则:

    class Game < ApplicationRecord
      def method_missing(*args)
        if (gbgame = to_giant_bomb_game) && gbgame.respond_to?(args.first)
          return gbgame.public_send(*args)    
        end
    
        super(*args)
      end
    end
    

    这将使Game 实例接收它不知道的方法,例如deck,在它知道如何构造的GiantBomb::Game 实例上调用该方法。因此,无论 @game 是 Game 还是 GiantBomb::Game,您的 @game.deck 调用都将正常工作。这很简洁,但也可能导致一些超级混乱的错误......这就是元编程的诅咒。

    【讨论】:

    • 感谢您的详细解答。我继续运行迁移并将您的建议添加到我的代码中,但是对于您描述的 fetch_data 方法,我得到了一个未定义的方法 to_h。如果我删除 to_h 并尝试添加游戏,to_giant_bomb_game 方法会给我一个未定义的方法 `each' 用于 nil:NilClass。
    • @SJK 抱歉,to_h 是一个通用约定,但 GiantBomb gem 没有实现它。你必须自己实现它——像data = Hash[game.instance_variables.map { |var| [var.to_s[1..-1], game.instance_variable_get(var)] } ] 这样的东西可以工作,虽然它很丑。我会用它来更新我的答案文本。
    • 感谢您的更新,我现在设法让它工作。非常感谢您抽出宝贵时间给出如此详细而彻底的答案!
    猜你喜欢
    • 1970-01-01
    • 2012-09-03
    • 2016-08-17
    • 1970-01-01
    • 2015-08-16
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 2020-12-08
    相关资源
    最近更新 更多