When #User is activated create an Object that belongs to that User - ruby-on-rails

I have a User model that has many galleries and categories(both separate models that belong_to :user). I would like to create a default category named "Everything" and a default gallery entitled "Everything".
I am adapting Mike Hartl's tutorial for my app, specifically the User creation and activation part. So, after a user is created using this code:
User.rb
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = 'Please check your email to activate your account.'
redirect_to root_url
else
render 'new'
end
end
An activation email is sent to the User and if they click on the link it will activate their account. At the point of activation, so the activated column switches from false to true, I would like the "Everything" gallery and category to be created. In my mind that is the most logical place for this to happen because it is likely to only be triggered once. That way I dont' have to worry about an "Everything" category and gallery trying to be created everytime someone logs on to their account.
The way I am trying to create these is by inserting the following code immediately after the account is activated:
user.categories = user.categories.build(name: 'Everything')
So it looks like, this:
account_activations_controller.rb
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
user.categories = user.categories.build(name: 'Everything')
log_in user
flash[:success] = 'Account activated!'
redirect_to user
else
flash[:danger] = 'Invalid activation link'
redirect_to root_url
end
end
end
My problem is that while the User is created, the "Everything" category is not and I get the following error, which I can't reconcile:
NoMethodError at /account_activations/0hbg0T33tjQlleiwKqvUDg/edit
undefined method `each' for #<Category:0x00000004f95b60>
I don't know where 'each' would be called in this process.
Though I mentioned creating a gallery and category and user activation, the above code only address category. It seems to me that if I can get a category to work I should be able to get the gallery to work using a similar solution. This is what my create action looks like for category:
def new
#category = current_user.categories.build
end
def create
#category = current_user.categories.create(category_params)
if #category.save!
respond_to do |format|
format.html
format.js
end
end
end
I have a couple questions related to my issue:
The first is the the most obvious, why am I getting the nomethod error, there doesn't appear to be an 'each' method associated with categories?
Am I approaching this issue correctly? Should I trigger the creation of my default category and gallery from somewhere else in the code? I've read a bit about a .change? that I tried to use on the activated column on User, but I couln't figure out how to use it.
Any help you can offer is greatly appreciated.

You are trying to assign the result of user.categories.build(name: 'Everything') to user.categories. user.categories.build(name: 'Everything') creates the new record on it's own. You do not need to further assign it to user.categories. Trying to do so is causing Rails to iterate on the result of user.categories.build(name: 'Everything'), which is a single Category, not a collection.
So the solution to your question 1 is to replace:
user.categories = user.categories.build(name: 'Everything')
with:
user.categories.build(name: 'Everything')
The answer to question 2 depends on how many different places and/or ways a user can be activated. If the controller action for activating is the only place, your solution is probably fine. But if you ever want to activate a user another way (from the console, for example), you would probably be better off using a an ActiveRecord callback on User, such as :after_save and checking for user.activated_changed? && user.activated?, then generating the generic category and gallery there.
In either case, you may want to consider checking that the generic category and gallery don't already exist for the user (in case they were once activated and then deactivated), before creating them.

I figured out what I was doing wrong. I was trying to create categories/an array instead of a category/a single object. I turned this:
user.categories = user.categories.build(name: 'Everything')
to this:
user.categories << user.categories.create(name: 'Everything')

Related

Ruby, before_action confusion

In ruby, I'm trying to prevent users from adding someone as a friend if either one of them has the other on their ignore list. In the friendships controller, I'm trying to define
class FriendshipsController < ApplicationController
before_action :ignored_user, only: :create
And
def ignored_user
if current_user.foes.include?(#user)
flash[:notice] = "Unable to send friend request."
redirect_to :back
end
end
In this context, current_user is the user who is logged in, #user is the user whose profile they're viewing, and foes means "either someone they're choosing to ignore, or someone who's ignoring them." All of that seems to work except the "if current_user blah blah" line. If I replace it with "if current_user.admin?" then it works exactly as you'd expect. If that user is an admin, they can't send anyone friend requests. If I replace it with "if current_user.id == 2", it also works as you'd expect. I've tried replacing the faulty line with many, many variations of the above code, including things like:
if Disagreement.where(foe_id: [#user, params[:id]], user_id: [current_user, params[:id]]).first
which works elsewhere, just not in the friendships controller.
As for error messages: there are none. My attempts always either end with the friend being added when they shouldn't be, or all friend requests being blocked, including friend requests to users who aren't ignored. Any help with this would be greatly appreciated.
A much smarter person than myself gave me the solution. I completely removed the "before_action :ignored_user, only: :create" part, as well as the "def ignored_user" section. All of that has been replaced with this:
class FriendshipsController < ApplicationController
def create
#user = User.find(params[:friend_id])
if current_user.foes.include?(#user)
flash[:notice] = "Unable to send friend request. You are ignoring this user or vice versa."
redirect_to :back
puts "#user is: #{#user}" #This line only existed for the sake of figuring out the problem
puts "#user.id is: #{#user.id}" #Same
puts "current_user.foes are: #{current_user.foes}" #Same
else
#friendship = current_user.friendships.build(friend_id: params[:friend_id])
if #friendship.save
flash[:notice] = "Friend request sent!"
redirect_to :back
end
end
end
The problem was that the friendships controller couldn't recognize #user in any of the ways it's normally defined, due to the params[:id] part. Because that's rewritten for use in the friendships controller, the controller assumes that by "id," you mean the id of some friendship, not a user's id. The person who gave me the solution realized that you could just replace ":id" with ":friend_id" since, in the context of this action, those two IDs will always be synonymous, since you'll always be adding people from their own profile page. So I never figured out how to directly reference a user's ID from within a different controller, but I've learned the next best thing.

Disable autoincrementing id inside of ActiveRecord::Base.transaction

I have a FormObject for registration which creates a user and a lot of models for him inside create method.
def create
ActiveRecord::Base.transaction do
#user = User.create(#params[:user])
process_bonuses_for_user
process_actions_for_user
# et.c., giant amount of methods
end
#user.persisted? # to return the true of false to controller
end
I met the strange behaviour of my FormObject. Even it ran successfull (create a lot of models) or unsuccessful (not saving them), the id of User model is autoincrementing. So, every trying to save something using my FormObject increment the value of next id for User. This is normal situation when User created successfully, but not normal when user makes a mistake on registration form.
How can I disable this unpredictable behaviour?
P.S. I know that everything is work when I write #user = User.new(#params[:user]) at the start of create method and #user.save at the end, but there are a lot of associations, and I don't want to write a lot of autosave or inverse_of in my models.
P.P.S. I'm postgresql-9.4 user
Your transaction is not working because you're using create. You need to use the bang version (create!) to raise an exception on failure which triggers the rollback. Do note that you'll need to rescue the InvalidRecord exception yourself.
In my opinion, it could be something like this:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
process_bonuses_for_user
process_actions_for_user
# et.c., giant amount of methods
...
format.html { redirect_to ... }
end
end

Get any id to any user exists in the database

I am new to rails and have a task that asks me to send an invitation for any user to be admin in my magazine here is my piece of code
def invite
inviteUser = { 'user_id' => current_user.id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
I need to replace current_user.id with something that refers to any user's id which exists in my database to send him an invitation to be admin with me I tried to add #User=Users.All and then pass it as a variable but it got me an error I tried a lot of things but every time I get an error except for adding current_user.id
ps: I am using devise for authentication
You asked a couple things, and it is kind of confusing what you want to do.
Here is how you get all ids of records in a model.
Rails4: User.ids
Rails3: User.all.map(&:id)
Or (not sure if #pluck is in Rails 3 or not)
User.pluck(:id)
If you want to get a random user (you mentioned "any user") you could do.
User.find(User.pluck(:id).sample)
Though I think what you really want to do is to pass the id or some other attribute of a user as a param to the action and send that user an invitation.
Presumably you either have a post or get route for "users#invite" (the action you wrote in your question). You can add a named parameter there or you can pass a url param or if you are using a post route, you could add the param to the post body.
Then in your contoller you can do something like this (I'll use email as an attribute):
def invite
#user = User.find_by(email: params[:user_email])
#Rails 3 like this
# #user = User.find_by_email(params[:user_email])
# now do stuff with user
end
User.all will return you the collection of users. So,
Find the user object to get an id...
Try this code....
def invite
inviteUser = { 'user_id' => User.find_by_email('user#example.com').id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
You can try
User.last.id
or
User.find_by_email("xyz#test.com").id
or
User.where(email: "xyz#test.com").first.id
Replace xyz#test.com with desired user email address. To get more details on rails active record query interface, please read rails guides http://guides.rubyonrails.org/active_record_querying.html

How to redirect output of user defined class to view in rails

I am facing the following problem.
I am making a naive Bayes gender prediction algorithm in rails. For that I have coded it and put my code file in roots directory. I am able to access the class in my Model controller and run the code by appropriate calling of class.
I have a form with three fields "weight, height and gender", when I fill all these three, they should get saved in the db. I have written its logic and it works well. In case I leave out the gender field as it is, it should run my code and render an appropriate view.
In the create action of controller I am doing the following:
def create
#user = User.new(params[:user])
if #user.gender.blank?
#b_obj= Bayes.new
#b_obj.predict(#user[height], #user[weight]).
else
if #user.save
redirect_to #user
else
#title = "Train"
render 'new'
end
end
end
This code works well. Now I am faced with the problem, how to redirect the result of my code (first if statement) to a view and where to make it. My code returns "male" or "female" as an output. Should I store it in some variable? how should I go about it?
This is the last thing left in my project. After trying many approaches I have come to this, but now I am in sort of a deadlock.
Regards,
Arun
You could add an extra boolean attribute to your user object called 'gender_predicted' which would indicate whether the gender set for the user is predicted or was specified:
#user.gender = #b_obj.predict(#user[height], #user[weight])
#user.gender_predicted = true
That would allow you to store your prediction and only have to run it once. Then you can treat all users the same way but still allow you to track where predictions have been used.
def create
#user = User.new(params[:user])
#user.gender ||= Bayes.new.predict(#user[height], #user[weight])
if #user.save
flash[:notice] = 'User saved'
redirect_to #user
else
#title = "Train"
render 'new'
end
end

Rails - Add Record to join table from controller

I'm trying to create a record within a join table from the action of a button. I would have an events model and would like to track selected events from each user.
I used the HABTM relationship since I don't really need any extra fields.
User.rb:
has_to_and_belongs_to_many :events
Event.rb:
has_to_and_belongs_to_many :users
Events_Users Migration:
[user_id, event_id, id=>false]
I'm getting stuck on the actual creation of the record. Someone helped me earlier with adding the record in within the console:
u = User.find(1)
u.events << Event.find(1)
Now I would like to perform the action as a result of clicking a link... Is this in the right direction?
def add
#user = User.find(session[:user_id])
#event = Event.find(params[:id])
if #user.events.save(params[:user][:event])
flash[:notice] = 'Event was saved.'
end
end
Should I add a #user.events.new somewhere and if so where do I put the params of which user and which event?
The following code should work (assuming that you pass in an parameter with the name id that corresponds to the id of an event object):
def add
#user = User.find(session[:user_id])
#event = Event.find(params[:id])
#user.events << #event
flash[:notice] = 'Event was saved.'
end
The problems I see in your code are:
You are passing a hash to .save. Save should only take a boolean value corresponding whether validations should be run and is true by default. However .create and .new can accept a hash of values. (.save would be used after .new).
You load an event through params[:id] but then you attempt to create an event through params[:user][:event]. Which do you want to do? Create or load? (my example assumes load)
Actions that have an effect such as this one should happen when a user clicks a button and submits a form rather than 'clicking a link'. This code may be vulnerable to cross site request forgery (Someone could trick someone into clicking a link on another site that ran this action). Rails forms, if correctly implemented, are protected against this because they use a request forgery protection token.
Most likely you want to redirect the user after this action. Rendering pages after executing actions like this (rather than redirecting) is considered bad practice.
What you did in the console you need to do in the controller.
def add
#user = User.find(session[:user_id])
#event = Event.find(params[:id])
#user.events << #event
flash[:notice] = 'Event was saved.'
end
The thing to note here is that the << operator for existing records will cause the association to be persisted immediately.
Take a look at the ActiveRecord documentation for more info.
If the event_id is passed as params[:id] and you are adding only one event in this call then, you can do the following in your controller code:
User.find(session[:user_id]).events << Event.find(params[:id])
flash[:notice] = 'Event was saved.'
You don't need explicit save to save the has_many association of an existing model instance.
Scenario 1
u = User.new(..)
u.events << Event.first
# Now you need to call `save` in order to save the user object
# and the events association
u.save
Scenario 2
u = User.first
u.events << Event.first
# Don't need to call `save` on `u` OR `u.events`

Resources