Rails 3 and update_attributes - ruby-on-rails-3

I am moving an application from Rails 2.2.2 to Rails 3. I have a form that is used to update a user's info that was fully functional in Rails 2 but breaks in Rails 3
When the form is submitted, a method is called that creates the user object and then tries to do the update like this:
if #user.update_attributes params[:user] ## line 126
Then the controller throws this exception:
undefined method `update_attributes' for #<ActiveRecord::Relation:0xacfc178>
in: app/controllers/admin_controller.rb:126:in `save_user'
So it looks like ActiveRecord in Rails 3 is returning a different type of object? One that doesn't inherit update_attributes? How do I fix this?
Here is the full controller method in question:
def save_user
#needs_password_gen = "YES"
#user = B2bUser.where("id = ?",params[:id])
#needsAPICredentials = false
##### Make sure thay gave us an email address
if !params[:user][:email] || !validEmailAddress(params[:user][:email].to_s)
flash[:warning] = "Valid email address is required."
redirect_to :controller => "admin/edit_user/#{#user.id}" and return
end
#user.first.update_attributes params[:user]
end
THANKS

Looks like your #user object may be an array. You are probably using :where to query and are forgetting to pop it off the array. Try this:
#user.first.update_attributes params[:user]

Related

How to correctly scope public method in Rails using Devise

I am trying to make sure this method only allows editing of data that is appropriate for the current account. I am running Rails 3.12, using acts_as_tenant for managing accounts, and devise for user authentication.
When I try to access /dataload_mailchimp/edit I get the following error:
NoMethodError (undefined method `api_key' for #<ActiveRecord::Relation:0x00000006d02590>):
2013-09-27T17:12:23.947590+00:00 app[web.1]: app/controllers/dataload_mailchimps_controller.rb:32:in `edit'
As I understand the error an api_key is not found. However, I am logged in as a user for account_id = 3 and there is a valid api_key for that account_id in the dataload_mailchimps table.
I am trying to better understand how I scope this method correctly so the correct record from dataload_mailchimps is returned?
Thanks for any help or advice.
my controller:
class DataloadMailchimpsController < ApplicationController
before_filter :authenticate_user!
def edit
#dataload = DataloadMailchimp.where( account_id: current_user.account )
unless #dataload
redirect_to new_dataload_mailchimp_path
else
gibbon = Gibbon.new(#dataload.api_key)
#lists = []
gibbon.lists['data'].each do |list|
#lists << MailchimpList.find_or_create_by_list_id(:list_id => list['id'], :name => list['name'])
end
end
end
end
from the dataload_mailchimps table:
id api_key account_id
4 6a3 3
Your current #dataload instance variable holds an ActiveRecord::Relation type, where you actually want it to hold a DataloadMailchimp object. Try the following lookup instead:
#dataload = DataloadMailchimp.find_by_account_id(current_user.account.id)
As an FYI, the where() method returns an ActiveRecord::Relation object holding a collection of all class objects that match the conditions. In contrast, find_by_ returns the first instance of a class object that matches the conditions.

Delayed_job ActionMailer caching?

I currently call a delayed_job (gem delayed_job_mongoid) to send a confirmation email. However, it doesn't seem to use the latest data I'm passing, and instead uses a cached version. See below:
My controller:
...
_user.calculate_orders
_user.save
_user.reload
Mailer.delay.order_reported(_user)
...
The Mailer
class Mailer < Devise::Mailer
def order_reported(to_user)
#to_user = to_user
email_with_name = "#{#to_user.name} <#{#to_user.email}>"
mail(:to => email_with_name, :subject => "Test email")
end
end
For example, if an attribute _user.total_orders = 3 gets updated to 5 and saved. It's correctly reflected in the database, the delayed job DB record contains the updated info of 5, but when email is sent, it uses cached info of 3.
I've also tried calling the method via rails console:
This works and uses the parameter that's passed and updated info
Mailer.order_reported(u).deliver
This doesn't work and uses cached data
Mailer.delay.order_reported(u)
I had a very similar problem with Sidekiq recently, related to passing in a serialized version of my User object instead of just an ID.
I would refactor slightly to only pass simple objects as parameters in the delayed method. So, you could do this instead:
Mailer.delay.order_reported(_user.id)
and then update the method to read as follows:
class Mailer < Devise::Mailer
def order_reported(to_user_id)
#to_user = User.find(to_user_id)
email_with_name = "#{#to_user.name} <#{#to_user.email}>"
mail(:to => email_with_name, :subject => "Test email")
end
end
That would ensure you always get a fresh user object when your mailer is actually delivered.

Rails if referrer then do issue

I am trying to figure out the best way to build an if else if statement in the controller around a rails specific referrer. Is there a way to pull the last rails path and use it in the below statement? I think I am close but totally stumped...
This is an update action that will be hit from one form at multiple locations on the site.
I am looking to replace "form_path"
def update
#object = Milestone.find(params[:id])
if #milestone.update_attributes(params[:milestone])
if request.referer == form_path
redirect_to root_path
else
redirect_to object2_path
end
else
....
end
end
Is form_path a variable you're defining somewhere else in the controller? Outside of understanding that, it looks like it should work.
Instead of messing with referer, you could place a hidden_field in the form based on where it's coming from, and pull that out of the params hash.
Something like:
hidden_field_tag :location, controller_name
Then in the controller:
if params[:location] == yadda yadda

Rails 3: Manipulating devise "resource" in controller?

I'm using devise & devise_invitable in a rails 3 project, and I'm trying to manipulate some of the 'User' object fields in the devise controller.
The action is question is this:
def update
self.resource = resource_class.accept_invitation!(params[resource_name])
resource.first_name = 'Lemmechangethis'
if resource.errors.empty?
set_flash_message :notice, :updated
sign_in(resource_name, resource)
respond_with resource, :location => after_accept_path_for(resource)
else
respond_with_navigational(resource){ render_with_scope :edit }
end
end
I'd have thought that the (commented out) resource.first_name call would influence resource in much the same way as a model - but it doesn't seem to. I'm still getting a 'blank' validation error on this form.
So, the question is, how do I specify values to the User model in devise (and/or devise_invitable) that will actually be subject to verification?
Any suggestions appreciated,
John
resource does return a User models instance. So the resource.first_name = 'Lemmechangethis' statement does change your User models instance but it doesnot trigger your User models validations, which is probably why resource.errors always returns an empty array. One way to trigger the User models validation is to call resource.valid? for example. You can then check the resource.errors array for specific error messages.

How to slugify a parent model with fields from a child model?

I'd like to slugify the urls for a model in Rails 3, using Mongoid. The problem is the fields I want to use in the slug are located in a child model. I am using mongoid-slug gem to find a solution to this and my attempt so far is this:
class Building
references_one :address
def to_param
address.to_param
end
end
class Address
referenced_in :building
field :houseno
field :street
slug :houseno, :street
end
While this allows me to form the correct url by calling building_path(building), the page does not contain the correct values. Error message complains that object id is incorrect, and I'm not sure how to get Rails to listen and find the record by to_param.
For the curious, here is how I solved my own problem. I realized that I needed to use change the show action from
#building = Building.find(params[:id])
to
#building = Address.find_by_slug(params[:id]).building
And voila! It works.