在Rails中使用delayed_job for Mechanize脚本(NoMethodError:nil的未定义方法:NilClass)

时间:2013-05-02 16:47:23

标签: ruby-on-rails ruby mechanize delayed-job

我要说这是一个相对初学者的序言,虽然我可能还不太了解,但我更愿意学习。我希望你可以帮助解决这个问题,因为我一直在试图解决这个问题并且仍然陷入困境。我知道这是一个很长的问题 - 我不会把它扔出去,除非我已经花了很多天试图弄清楚自己。

我正在使用Mechanize在我的Rails应用程序中运行脚本来填写几个webforms。它在当地完美运作。但是,我需要使用delayed_job才能在后台运行时间密集型脚本。现在,我正在本地开发这个(之前是在Heroku上),直到问题得到解决。

所以我安装了delayed_job_active_record gem,并且一直试图将其设置为工作状态。我知道虽然我的表单成功地成功接受了所有参数,但没有任何内容插入到SQL表中。它没有拿起对象的ID。它不识别对象的类(它将其视为NilClass),因此它甚至将常规Ruby方法(例如.strip)视为未知。 (控制台的输出显示所有这些都在底部)。

我显然做错了什么。它很可能是一件非常明显的事情 - 但我还没有相关的技巧。如果你能帮助我弄清楚我在这里做错了什么,我会永远感激你。

首先,这是我的代码:


MODEL:

require 'digest'
class Jumper < ActiveRecord::Base

after_save do |runscript|
  runscript.delay.scrape
end

validates :myuserid, :presence => true
validates :mypass, :presence => true
validates :mydate, :presence => true, :numericality => true
validates :mymonth, :presence => true, :numericality => true
validates :myyear, :presence => true, :numericality => true
validates :mylist, :presence => true

attr_accessor :myuserid, :mypass, :mydate, :mymonth, :myyear, :mylist

def scrape

 agent = Mechanize.new 

 page = agent.get('http://mywebsite.org/')

 myform = page.form_with(:name => 'signinForm')

 myuserid_field = myform.field_with(:name => "email")
 myuserid_field.value = myuserid  
 mypass_field = myform.field_with(:name => "password")
 mypass_field.value = mypass 

 page = agent.submit(myform, myform.buttons.first)

 mylistarray = mylist.strip.split(/[\s]+/)

 mylistfinal = mylistarray.map{|l| l[0..-5].sub(/(.*)\./,'\1').gsub('.','/')}.uniq

 mylistfinal.each do |doi|
  url ='http://mywebsite=' + doi + '&role=some_info#some_dates' 
  page = agent.get("http://mywebsite.org/submit")
  page = agent.get("#{url}") 

  entryform = page.form_with(:name => 'submit') 

  entryform.field_with(:name => 'month').options[("#{mymonth}").to_i].select
  entryform.field_with(:name => 'day').options[("#{mydate}").to_i].select
  entryform.field_with(:name => 'year').options[("#{myyear}").to_i].select

  page = agent.submit(entryform, entryform.button_with(:name => 'continue'))

    end
  end
end`  

控制器:

def index
  @doilists = Doilist.all

respond_to do |format|
  format.html # index.html.erb
  format.json { render json: @doilists }
 end
end

def show
 @doilist = Doilist.find(params[:id])

 respond_to do |format|
  format.html # show.html.erb
  format.json { render json: @doilist }
 end
end

def new
  @doilist = Doilist.new

 respond_to do |format|
   format.html # new.html.erb
   format.json { render json: @doilist }
 end
end

def create
@jumper = Jumper.new(params[:jumper])

  if @jumper.save
    flash[:notice] = "Sucessfully submitted your information."
    redirect_to @jumper
  else
    render :action => 'new'
  end
 end`

的Gemfile:

source 'https://rubygems.org'

gem 'rails'

group :development do
  gem 'sqlite3', '1.3.5'
end

group :assets do
  gem 'sass-rails',   '~> 3.2.5'
  gem 'coffee-rails', '~> 3.2.2'
  gem 'uglifier', '>= 1.2.3'
end

gem 'jquery-rails', '2.0.2'
gem 'mechanize'

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

gem 'delayed_job_active_record'
gem 'daemons'
gem 'thin'`

PROCFILE:

web: bundle exec rails server thin -p $PORT -e $RACK_ENV

worker:  bundle exec rake jobs:work`

脚本/ delayed_job的:

require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
require 'delayed/command'
Delayed::Command.new(ARGV).daemonize

RAILS_ENV=production script/delayed_job start --exit-on-complete`

这是我在终端中运行Foreman时的输出:

11:33:30 web.1    | Started POST "/MyJumper" for 127.0.0.1 at 2013-05-02 11:33:29 -0400
11:33:30 web.1    |   Delayed::Backend::ActiveRecord::Job Load (1.2ms)  SELECT "delayed_jobs".* FROM "delayed_jobs" WHERE ((run_at <= '2013-05-02 15:33:30.437882' AND (locked_at IS NULL OR locked_at < '2013-05-02 11:33:30.438065') OR locked_by = 'delayed_job host:myroot pid:7081') AND failed_at IS NULL) ORDER BY priority ASC, run_at ASC LIMIT 5
1:33:30 web.1    | Processing by JumpersController#create as HTML
11:33:30 web.1    |   Parameters: {"utf8"=>"✓", "jumper"=>{"myuserid"=>"email@gmail.com", "mypass"=>"mypassword123”, "mylist"=>"listitem", "mymonth"=>"4", "mydate"=>"30", "myyear"=>"1"}, "commit"=>"Submit Your Entry"}
11:33:30 web.1    |    (0.1ms)  begin transaction
11:33:30 web.1    |   SQL (21.4ms)  INSERT INTO "jumpers" ("created_at", "mydate", "mylist", "mymonth", "mypass", "myuserid", "myyear", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?)  [["created_at", Thu, 02 May 2013 15:33:30 UTC +00:00], ["mydate", nil], ["mylist", nil], ["mymonth", nil], ["mypass", nil], ["myuserid", nil], ["myyear", nil], ["updated_at", Thu, 02 May 2013 15:33:30 UTC +00:00]]
11:33:30 web.1    |   SQL (0.5ms)  INSERT INTO "delayed_jobs" ("attempts", "created_at", "failed_at", "handler", "last_error", "locked_at", "locked_by", "priority", "queue", "run_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["attempts", 0], ["created_at", Thu, 02 May 2013 15:33:30 UTC +00:00], ["failed_at", nil], ["handler", "--- !ruby/object:Delayed::PerformableMethod\nobject: !ruby/ActiveRecord:Jumper\n  attributes:\n    id: 68\n    mylist: \n    created_at: 2013-05-02 15:33:30.920857000 Z\n    updated_at: 2013-05-02 
15:33:30.920857000 Z\n    myuserid: \n    mypass: \n    mymonth: \n    mydate: \n    myyear: \nmethod_name: :scrape\nargs:\n- :jumper\n"], ["last_error", nil], ["locked_at", nil], ["locked_by", nil], ["priority", 0], ["queue", nil], ["run_at", Thu, 02 May 2013 15:33:30 UTC +00:00], ["updated_at", Thu, 02 May 2013 15:33:30 UTC +00:00]]
11:33:30 web.1    |    (10.7ms)  commit transaction
11:33:30 web.1    | Redirected to `http://mylocalhost:5000/MyJumper/68`
11:33:30 web.1    | Completed 302 Found in 74ms (ActiveRecord: 33.1ms)
11:33:31 web.1    | Started GET "/show/68" for 127.0.0.1 at 2013-05-02 11:33:30 -0400
11:33:31 web.1    | Processing by JumpersController#show as HTML
11:33:31 web.1    |   Parameters: {"id"=>"68"}
11:33:31 web.1    |   Jumper Load (0.3ms)  SELECT "jumpers".* FROM "jumpers" WHERE "jumpers"."id" = ? LIMIT 1  [["id", "68"]]
11:33:34 worker.1 | [Worker(host:myroot pid:10470)] Jumper#scrape failed with NoMethodError: undefined method `strip' for nil:NilClass - 0 failed attempts
11:33:34 worker.1 | [Worker(host:myroot pid:10470)] 1 jobs processed at 0.8520 j/s, 1 failed ...

2 个答案:

答案 0 :(得分:0)

看起来你有一些未定义的。例如,哪里是mymonth

我想给你一个简化代码的提示,我在这里:

而不是:

myuserid_field = myform.field_with(:name => "MSTRServlet.email")
myuserid_field.value = myuserid  

尝试:

myform['MSTRServlet.email'] = myuserid  

这不太可能解决你的问题,但也许它会让你更接近简单到可以解决的问题。

最后,我知道Heroku是当前媒体的宠儿,但您应该考虑它可能不适合您的特定项目。

答案 1 :(得分:0)

感谢您的建议。我最终弄清楚如何自己解决这个问题。问题显然是Delayed_Job在Rails 3中attr_accessor存在严重问题。

此处有更多相关信息: delayed_job: attribute accessor values are not stored in delayed_jobs table in rails 3 after upgrade from rails 2

在这里: YAML, delayed_job : Psych vs Syck. How to make pysch read attr_accessors for a ruby object

此处解释了此问题的解决方法: https://gist.github.com/jrafanie/3011499

简单地删除模型的attr_accessor部分后,一切都很完美。真是一种解脱!

另外:我从模型中删除了after_save代码,只需在控制器保存对象后立即在控制器中添加@jumper.delay.scrape。这可能没有必要,但它与文档更加一致:

if @jumper.save
    @jumper.delay.scrape 
    flash[:notice] = "Currently sending your info"
    redirect_to start_index_path 

当我转向生产时,我将进一步检查解决方法并查看项目的最佳方法,以便在模型中使用attr_accessor。但是现在,很高兴让这个工作!