【问题标题】:When my object is called via HAML it tries to trigger Active Record instead of just calling a method当通过 HAML 调用我的对象时,它会尝试触发 Active Record,而不仅仅是调用方法
【发布时间】:2011-08-30 12:45:04
【问题描述】:

在构造要发送给用户的电子邮件时,我调用了以下 HAML sn-p。

%p
  Please <a href="#{base_url}/verify/#{user.primary_email.email}/#{user.registration_token}">click this link to confirm your email address</a>. You will then be logged in and can choose a password and continue the process of confirming your identity.

用户对象定义方法primary_email

def primary_email()
  result = self.emails.first(:conditions => {:primary => true})
  puts "DEBUG: No Primary Email address found." unless result
  puts "DEBUG: Primary Email address found was #{result.email}" if result
  return result
end

但我收到错误消息

NoMethodError - undefined method `primary_email' for #<ActiveRecord::Relation:0x10f0d3a58>:
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/activerecord-3.0.7/lib/active_record/relation.rb:374:in `method_missing'
 ./views/user_notifications/confirm_registration.haml:30:in `__tilt_2272398620'
 ./views/user_notifications/confirm_registration.haml:-7:in `instance_eval'
 ./views/user_notifications/confirm_registration.haml:-7:in `__tilt_2272398620'
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/tilt-1.3.2/lib/tilt/template.rb:140:in `call'
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/tilt-1.3.2/lib/tilt/template.rb:140:in `cached_evaluate'
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/tilt-1.3.2/lib/tilt/template.rb:127:in `evaluate'
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/tilt-1.3.2/lib/tilt/haml.rb:24:in `evaluate'
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/tilt-1.3.2/lib/tilt/template.rb:76:in `render'
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/sinatra-1.2.6/lib/sinatra/base.rb:563:in `render'
 /Users/davesag/src/git/showpony3/rack/ruby/1.8/gems/sinatra-1.2.6/lib/sinatra/base.rb:463:in `haml'
 ./showpony.rb:256:in `build_messge_for'

在我的简单单元测试中,我测试了 primary_email 方法

george = setup_dummy_user("George")
assert george.primary_email.email == GEORGE_TEST_EMAIL

一切顺利。

当我触发此模板的发送时,它会工作一次,然后在同一请求中的第二次发送失败。 (当我通过浏览器进行测试时,管理员用户和测试用户都会收到相同的通知电子邮件)

同样奇怪的是,在我的日志中,我看到了对 primary_email 方法的调用。 (实际电子邮件已编辑)

org = {"number"=>"34567890", "name"=>"Test Limited", "country"=>"au", "website"=>"http://www.testing.com"}
people = [{"name"=>"Test 1", "username"=>nil, "roles"=>[{"name"=>"admin"}, {"name"=>"billing"}], "email"=>"##############"}, {"name"=>"Test 2", "username"=>nil, "roles"=>[], "email"=>"@@@@@@@@@@@@@@@@@"}]
DEBUG: Primary Email address found was ##############
Sending confirmation email to ##############
DEBUG: Primary Email address found was ##############
Sending confirmation email to @@@@@@@@@@@@@@@@@
DEBUG: Primary Email address found was @@@@@@@@@@@@@@@@@
NoMethodError - undefined method `primary_email' for#<ActiveRecord::Relation:0x10ea44e58>:

有什么线索吗?

在 Ruby 1.8.7 上使用最新的 Sinatra 以及最新的 ActiveRecord 和 HAML。

触发发送电子邮件请求的实际sinatra 路由如下:注意底部的循环遍历每个提供的用户(通过JSON)并向他们发送电子邮件。该过程触发了将用户作为本地模板变量的 HAML 模板的构建。

第一个用户可以正常工作,但第二个用户不行。

    post '/register' do
        content_type :json
      req = JSON.parse request.body.read
      if req['organisation'] == nil
        puts req.inspect
        return {:success => false, :error => "Request did not contain a proper Registration_Request object." }.to_json
      end
      org = req['organisation']
      people = req['people']

      message = "You (#{people[0]['name']} #{people[0]['email']}) have applied for membership on behalf of #{org['name']} (Company Number #{org['number']})."

      @@log.debug "org = #{org.inspect}"
      # do we have this org already?
      oo = Organisation.find_by_name(org['name'])
      if oo
        @@log.debug "Found exisiting Org #{oo.inspect}"
        return {:success => false, :error => "An organisation with that name is already a Member" }.to_json
      end

      these_people = []
      begin
        ActiveRecord::Base.transaction do

          o = Organisation.create(:name => org['name'],
                                  :number => org['number'],
                                  :website => org['website'],
                                  :country => org['country'])

          @@log.debug "people = #{people.inspect}"

          people.each {|person|
            p = User.create(:name => person['name'], :username => person['email'], :password => random_password)
            p.set_email({:email => person['email'], :primary => true})
            p.organisation = o

            person['roles'].each {|role|
              p.add_role(role['name'])
            }
            p.set_preference("HTML_EMAIL", "true")
            p.save!
            these_people << p
          }
        end
      end

      # send a secure notification to settings.notification_email
      send_system_email 'Membership Application received', :'system_notifications/registration', {:incoming_org => org, :incoming_people => people }

      # send an email_validation_request email to each user email.
      these_people.each {|p|
        @@log.debug "Sending confirmation email to #{p.primary_email.email}"
        send_user_email p, 'Please confirm your account details', :'user_notifications/confirm_registration', {:user => p }
      }

        return {:success => true, :message => message }.to_json
    end

【问题讨论】:

    标签: ruby activerecord methods sinatra haml


    【解决方案1】:

    仔细检查您的错误表明您不是在 ActiveRecord 对象上而是在 Relation 对象上调用“primary_email”方法

    例如,如果您通过以下方式获取“用户”变量,则可能会发生这种情况

    user = User.where("some conditions")
    

    因为“where”返回的不是 ActiveRecords 数组,而是 Relation 对象

    要获得实际的 AR 对象,您可以尝试

    user = User.where("some conditions").first
    

    user = User.find("some conditions")
    

    【讨论】:

    • 这很有趣。我可以确认用户没有通过.where 加载,但是在这种情况下,它正在被创建并保存到一个数组中。但奇怪的是,只有当我的列表中有多个 User 对象时才会发生错误。数组中的第一个 User 工作正常。通知电子邮件完美发送,没有任何问题。但是第二个User 失败了,因为它似乎是一个ActiveRecord::Relation 对象,而不是User 对象。
    • 好吧,只是为了确保我更改了我的代码,以便它只将用户名存储在数组中,然后在生成注册电子邮件时,它循环通过用户名数组并通过 p = User.find_by_username(pname) 加载用户对象只是要确定。但是,我得到了相同的结果。第一个p 很好,第二个是Relation 不是User 但只有通过HAML 加载时才可以。
    • 你确定只有在HAML模板中调用primary_email方法时才会出现这个问题吗?如果您在these_people.each 循环中提取此电子邮件并将其与{:user =&gt; p} 一起传递给模板,它会正常工作吗?
    • 嗨@cug,你的问题让我走上了一条有趣的道路。似乎正在发生的事情实际上与我如何找到Organisationadmin 用户有关。在我的HAML 中,我可以使用o = user.organisation 从用户那里得到组织,然后问题就会出现,我调用a = o.get_adminget_admin 方法通过return self.users.with_role('admin') 获取管理员用户。这就是我返回 Relation 的地方。
    • 第一个用户没有调用它的原因是第一个用户是管理员,所以haml 的那个位没有被执行。所以我尝试在查询末尾添加.first,但随后出现SQL 错误。
    【解决方案2】:

    我的第一个想法是重构这个:

    result = self.emails.first(:conditions => {:primary => true})
    

    到:

    result = self.emails.where(:primary => true).first
    

    但是,不确定是不是您的问题的原因。

    另外,尝试在您的方法中输出 user.attributes:

    def primary_email()
      puts self.attributes.sort
      result = self.emails.first(:conditions => {:primary => true})
    

    如果这不能说明问题,我认为您也必须向我们提供您的用户、组织和电子邮件模型的代码。

    您还可以检查您的数据库,以确认旧迁移中没有遗留的杂散列,也许?

    【讨论】:

    • 嘿@suranyami,primary_email 调用正常,大部分时间都可以正常工作。只是在从HAML 渲染操作(即从Tilt)调用它的情况下,它突然变成Relation 而不是User。无论如何,我做出了您建议的更改,并吐出self.attributes.sort 表明它在调用该方法时肯定有效。问题比las级别低。 primary_email 方法没有从 HAML 模板中调用,尽管它在紧接在它之前的代码行中被愉快地调用了。
    【解决方案3】:

    这听起来很疯狂,但是尝试在 .haml 文件中重写您的 haml 代码,这样您就可以利用多行缩进来编写它:

    %p
      - email = user.primary_email.email
        Please 
        %a{:href=>"#{base_url}/verify/#{email}/#{user.registration_token}"}
          click this link to confirm your email address...
        You will then be...
    

    别忘了@instance 你的变量

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多