Polymorphic model user association - ruby-on-rails

Rails 3.2. I am using the following code to associate user_id to the record:
# review.rb
class Review < ActiveRecord::Base
belongs_to :reviewable, :polymorphic => true, :counter_cache => true
end
# reviews_controller.rb
def create
#review = #reviewable.reviews.new(params[:review])
#review.user_id = current_user.id
if #review.save
flash[:success] = 'Thanks for adding your review.'
redirect_to #reviewable
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #reviewable
end
end
I want to find a way to use this, but it keeps saying that the current_user is not defined, but I could find the current_user object:
def create
#review = #reviewable.current_user.reviews.create(params[:review]
if #review.save
flash[:success] = 'Thanks for adding your review.'
redirect_to #reviewable
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #reviewable
end
end

If you can post your code to what the #reviewable object is, it might help to give a more specific answer. But if you want a one liner, you can do something like this:
#review = #reviewable.reviews.build(params[:review].merge({:user_id => current_user.id})
But personally, i think your original looks better as it's easier to read.
As an additional note, your second example also calls create and save. You don't need to call both, as create saves the object when accepting a hash of parameters. Save is nice to use if you want to initialize an object, modify it in some way, then save it later.

I think that the reason this does not work is because current_user is a method that is not defined on a reviewable.
The reviewable may belong to a user, in which case #reviewable.user.reviews.create... may be valid.

Related

Save both or neither model in controller

I want to save two models in one controller action, or save neither, and return with the validation errors.
Is there a better way than this?
def update
#job = Job.find(params[:id])
#location = #job.location
#job.assign_attributes(job_params)
#location.assign_attributes(location_params)
#job.save unless #job.valid? # gets validation errors
#location.save unless #location.valid? # gets validation errors
if #job.valid? && #location.valid?
#job.save
#location.save
flash[:success] = "Changes saved."
redirect_to edit_job_path(#job)
else
render 'edit'
end
end
New version:
def update
#job = Job.find(params[:id])
#location = #job.location
begin
Job.transaction do
#job.assign_attributes(job_params)
#job.save!(job_params)
#location.assign_attributes(location_params)
#location.save!(location_params)
end
flash[:success] = "Changes saved."
redirect_to edit_job_path(#job)
rescue ActiveRecord::RecordInvalid => invalid
render 'edit'
end
end
Have a look at Active Record Nested Attributes.
Using Nested attributes, you can save associated record attributes through parent.If parent record fails, associated records won't be saved.!
the first thing you'd want to do is delete these two lines
#job.save unless #job.valid? # gets validation errors
#location.save unless #location.valid? # gets validation errors
and only keep the #save in the if statement. because if one of them is valid, but the other isn't, you'll still save the valid one to the db.
To answer your second question, is there a better way to do this? At first blush, it looks like a job for #accepts_nested_attributes_for. However, accepts_nested_attributes_for is somewhat notorious for being difficult to get working (really it just takes a fare amount of tinkering) and what you're currently doing should get you where you're trying to go, so it's up to you.
You can use validates_associated rails helper:
class Model < ActiveRecord::Base
has_one :location
validates_associated :location
end
Then:
if #job.save
#blah
else
#blah
end
Is enough without having to mess with ActiveRecord#Nested_attributes. It's fastest, but less cleaner. Your choice.
Reference:
http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_associated

converting the methods for a has_many association to a has_one association

I have 2 models, users, and common_apps.
users has_one :common_app.
Before this, I wrote the code as the users has_many common_apps, however I'm not sure how to rewrite that for a has_one association. The main confusion is how to structure 'new' in common_app controller.
When I try, I get an undefined method error.
undefined method `new' for #<CommonApp:>
This is my code -->
def new
if current_user.common_app.any?
redirect_to current_user
else
#common_app = current_user.common_app.new
end
end
def create
#common_app = current_user.common_app.build(common_app_params)
if #common_app.save
flash[:success] = "Common App Created!"
redirect_to root_url
else
redirect_to 'common_apps/new'
end
end
def show
#common_apps = current_user.common_app
end
how would you restructure this, if this were to be a has_one association?
I think I know how the 'create' one should be -->
def create
#common_app = current_user.build_common_app(common_app_params)
if #common_app.save
flash[:success] = "Common App Created!"
redirect_to root_url
else
redirect_to 'common_apps/new'
end
end
Your new action should look like this:
def new
if current_user.common_app.present?
redirect_to current_user
else
#common_app = current_user.build_common_app
end
end
You can also call build_common_app without any parameters passed to it, which will initialize an empty CommonApp for current_user.

Build an object after user registers in rails

So i'm having this issue trying to figure out how to use the build method in rails to create an object once a user completely registers and still have that object connected to the users id. I'm using devise for authentication and the model that needs to be created is called "app".
This is the create method for "app".
def create
#app = App.new(app_params)
#app.id = current_user.id
respond_to do |format|
if #app.save
format.html { redirect_to #app, notice: 'Application successfully created.'}
else
format.html { render action: 'new' }
end
end
end
Im getting this error:
Couldn't find App with id=1
from my multi step form controller:
def show
#user = User.find(current_user)
case step
when :school, :grades, :extra_activity, :paragraph, :submit
#app = App.find(current_user)
end
render_wizard
end
You need an after_create callback in the User model. It makes no sense to mess with the AppController because no forms have been filled up for the app and you have no app_params.
class User < ActiveRecord::Base
after_create :build_initial_app
protected
def build_initial_app
self.create_app
end
end
You can read more about this at the Rails Guides page for ActiveRecord Callbacks.
The problem line in your code is here:
#app.id = current_user.id
Setting an ActiveRecord object's id is a no-no. Think of the id attribute like you would a pointer in C. The system creates it for you, and you can use it to refer to a unique model object.
What you probably want is something along the lines of:
#app.user_id = current_user.id
Or, even better:
#app.user = current_user
To do that, you need to set up an association between your App model and your User model. There's a good tutorial on that here.

getting current_user in any model in rails

I am trying to get the current_user value into a model. I know that this is probably not appropriate since models should be kept secluded from this type of interaction but I'm running into a problem. I need to include a current_user within a method in a model and do not know how to do it.
I need to make this happen on an update in my stage controller and pass the current_user to the stage model and have that current_user value available. Any help is appreciated.
def update
if #stage.update_attributes(params[:stage])
redirect_to [#project, #stage], :notice => 'Stage was successfully updated.'
else
render :action => "edit"
end
end
You could also store the current user into Thread.current's hash.
See http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
class User < ActiveRecord::Base
def self.current
Thread.current[:user]
end
def self.current=(user)
Thread.current[:user] = user
end
end

How to permit creator to destroy his own record with Devise on Rails3

When the user is logged in, only the user who create the record can destroy his own record.
What should I add to the code below??
def destroy
#topic = Topic.find(params[:id])
#topic.destroy
flash[:notice] = "topic deleted!"
end
What you are looking for is not really devise but a authorization solution like CanCan.
Devise can only authenticate users and verify that they are logged in and active. What you need is a way to determine if the user has the right to delete this topic or not.
You can of course roll your own like this:
def destroy
#topic = Topic.find(params[:id])
if #topic.user_id == current_user.id
#topic.destroy
flash[:notice] = "topic deleted!"
else
flash[:error] = "not allowed"
end
end
(The code assumes you have a belongs_to :creator, :class_name => :user association set up in your Topic.. But you get the idea).
But using something like CanCan will make your life a whole lot easier and would reduce the code to something like this:
def destroy
#topic = Topic.find(params[:id])
authorize! :destroy, #topic
#topic.destroy
flash[:notice] = "topic deleted!"
end
With your ability file (See defining abilities) set up like this:
can :manage, Topic, :owner_id => user.id

Resources