为什么我的表单中的实例变量返回nil,而该语句的赋值部分没有?

时间:2014-11-21 01:50:00

标签: ruby-on-rails ruby-on-rails-4

这很奇怪。

我的app/views/videos/upload.html.erb看起来像这样:

<div class="bootstrap-styles">
 <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
    <h3 id="myModalLabel">Upload your Video</h3>
    <p><i>Step 2 of 2 - TEST</i></p>
  </div>
  <div class="modal-body">
    <div class="form">
      <% binding.pry %>
      <%= form_tag @upload_info[:url], :multipart => true do %>
        <div>Step 2 of 2</div>
        <%= hidden_field_tag :token, @upload_info[:token] %>
        <%= file_field_tag :file, title: 'Choose video to upload' %>
        <p class="uploader">
          <button class="btn btn-success ladda-button" data-color="green" data-style="expand-left"><span class="ladda-label">Upload Video</span><span class="ladda-spinner"></span></button>
        </p>
      <% end %>

    </div>
  </div>
  <div class="modal-footer">
  </div>
</div>

相应的Videos#Upload控制器如下所示:

  def upload
    authorize! :read, @family_tree
    @video = Video.find(params[:video_id])
    @upload_info = Video.token_form(@video, video_save_video_path(@family_tree, @video))

    respond_to do |format|
        format.html
        format.js
    end
  end

每当执行此upload操作时,我都会收到此错误:

NoMethodError - undefined method `[]' for nil:NilClass:
  app/views/videos/upload.html.erb:9:in `_app_views_videos_upload_html_erb___1805626926918975871_70299844626480'

所以我打破了我可靠的binding.pry,这就是它变得有趣/奇怪的地方:

[1] pry(#<#<Class:0x007fdfea1e34e0>>)> @upload_info
=> nil
[2] pry(#<#<Class:0x007fdfea1e34e0>>)> @video
=> #<Video id: 39, title: "Drones", description: "Down in drones", yt_video_id: nil, is_complete: nil, created_at: "2014-11-21 00:41:03", updated_at: "2014-11-21 00:41:03", reply_id: nil, circa: nil>
[3] pry(#<#<Class:0x007fdfea1e34e0>>)> @family_tree
=> #<FamilyTree id: 1, name: "'s Family Tree", user_id: 1, created_at: "2014-10-04 15:37:18", updated_at: "2014-10-04 15:37:18">
[4] pry(#<#<Class:0x007fdfea1e34e0>>)> Video.token_form(@video, video_save_video_path(@family_tree, @video))
=> {:url=>
  "http://uploads.gdata.youtube.com/action/FormDataUpload/AIwbFASzqNJ1rg?nexturl=/videos/1/save_video.39",
 :token=>
  "AI-SOME-TOKEN-b9ftw"}
[5] pry(#<#<Class:0x007fdfea1e34e0>>)> video_save_video_path
ActionController::UrlGenerationError: No route matches {:action=>"save_video", :controller=>"videos", :family_tree_id=>"1"} missing required keys: [:video_id]
from /.rvm/gems/ruby-2.0.0-p353@myapp/gems/actionpack-4.1.6/lib/action_dispatch/journey/formatter.rb:39:in `generate'
[6] pry(#<#<Class:0x007fdfea1e34e0>>)> @upload_info
=> nil
[7] pry(#<#<Class:0x007fdfea1e34e0>>)> @upload_info[:url]
NoMethodError: undefined method `[]' for nil:NilClass
from (pry):8:in `_app_views_videos_upload_html_erb___1805626926918975871_70299841189460'

所以我的控制器中设置的@upload_info返回nil,但是赋值部分,即:

Video.token_form(@video, video_save_video_path(@family_tree, @video))

返回我在pry中期望的值。

修改1

正在我的Upload控制器中调用Video#Create操作,如下所示:

  def create
    authorize! :read, @family_tree
    @video = Video.new(video_params)

    respond_to do |format|
      if @video.save
        format.html { render action: 'upload' }
      else
        format.html { render action: 'new' }
        format.json { render json: @video.errors, status: :unprocessable_entity }
      end
    end
  end

我修改了upload操作以包含调试器信息,我看到没有任何内容写入日志文件 - 这表明Video#Upload操作没有被执行。

这是有道理的,因为我的Video#Create进程的日志如下所示:

Started POST "/family_trees/1/videos" for 127.0.0.1 at 2014-11-21 00:01:11 -0500
Processing by VideosController#create as JS
  Parameters: {"utf8"=>"✓", "video"=>{"title"=>"Hello There", "description"=>"Hello World", "circa"=>"", "user_ids"=>[""]}, "commit"=>"Add Video", "family_tree_id"=>"1"}
  User Load (1.9ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
  FamilyTree Load (0.9ms)  SELECT  "family_trees".* FROM "family_trees"  WHERE "family_trees"."user_id" = $1 LIMIT 1  [["user_id", 1]]
  FamilyTree Load (2.8ms)  SELECT  "family_trees".* FROM "family_trees"  WHERE "family_trees"."id" = $1 LIMIT 1  [["id", 1]]
   (1.0ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))  [["user_id", 1]]
  Membership Load (1.6ms)  SELECT "memberships".* FROM "memberships"  WHERE "memberships"."user_id" = 1 AND "memberships"."family_tree_id" = 1
   (2.2ms)  BEGIN
  SQL (3.0ms)  INSERT INTO "videos" ("created_at", "description", "title", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["created_at", "2014-11-21 05:01:11.486342"], ["description", "Hello World"], ["title", "Hello There"], ["updated_at", "2014-11-21 05:01:11.486342"]]
   (2.4ms)  COMMIT
  Rendered videos/upload.html.erb within layouts/application (3.3ms)
Completed 500 Internal Server Error in 42ms

NoMethodError - undefined method `[]' for nil:NilClass:
  app/views/videos/upload.html.erb:10:in `_app_views_videos_upload_html_erb___1805626926918975871_70299789284400'

所以它似乎试图在不执行控制器动作的情况下渲染videos/upload的视图。

如何让它执行操作,但只有在@video.save工作后才能执行?

1 个答案:

答案 0 :(得分:1)

啊,这就是问题所在! render action: 'upload'不会调用upload控制器方法。它只会渲染上传视图。因此@upload_info没有更新。使用redirect_to而不是render来首先执行控制器代码:

具体而言,将format.html { render action: 'upload' }替换为redirect_to upload_video_path(@video, ...)