ruby on rails changing variable - ruby-on-rails

i have a problem in ruby on rails. I want to make current user's store id to be 0 when user accesses to /homepage/, and i want to make user's store id to be the input id in the url when user accesses to /homepage/:id/.
My code:
routes.rb:
match "/homepage" => "users#access", :as => "store"
match "/homepage/:id" => "users#homepage", :as => "store"
def access
#user = current_user
#user.update_attributes(:store => "0")
#user.save
end
def homepagestore
#user = current_user
#user.update_attribute(:id, user.store = :id)
#user.save
end

update_attribute updates the record in the database. But it skips the validation checks. update_attributes also updates (saves) the record in the database. It does not skip validation.
So:
You should use params[:id] as Sergio says
You may want to use update_attributes instead since it does not skip validation checks
You do NOT need the save method if you use update_attribute or update_attributes
My suggestions:
def access
#user = current_user
#user.update_attributes(:store => "0")
end
def homepagestore
#user = current_user
#user.update_attributes(:store => params[:id])
end
Added update_attributes uses the mass-assignment protection system. So you need the :store field in your User model's attr_accessible call to allow it to be changed. Or override the protection, see the update_attributes docs. Ask if you have more questions.

Related

Access current_user in model (or some other workaround)

I've read several SO links on this topic. Even if you can hack it to get current_user in model, you shouldn't do it. So, what are my options in my case?
I'm using the devise_invitable gem, and one of the commands is User.invite!({:email => email}, current_user), which stores who the user is invited by (current_user). I'd like to have this information.
Currently, users are invited to join a private group, and this process is handled in my group.rb model:
# group.rb
def user_emails
end
def user_emails=(emails_string)
emails_string = emails_string.split(%r{,\s*})
emails_string.each do |email|
user = User.find_for_authentication(email: email)
if user
self.add user
GroupMailer.welcome_email(user)
else
User.invite!(email: email) # But I want this: User.invite!({:email => email}, current_user)
user = User.order('created_at ASC').last
self.add user
end
end
end
If relevant, it's just a text_area that receives these emails to process:
# groups/_form.html.erb
<%= f.text_area :user_emails, rows: 4, placeholder: 'Enter email addresses here, separated by comma', class: 'form-control' %>
Without having to re-arrange too much, how can I run User.invite!({:email => email}, current_user) in this process, so that this useful information (who is invited by whom) is stored in my database? Much thanks!
Update:
With #Mohamad's help below, I got it working.
# group.rb
def emails
end
def invite_many(emails, inviter)
emails.split(%r{,\s*}).each do |email|
if user = User.find_for_authentication(email: email)
add user
GroupMailer.group_invite user
else
add User.invite!({:email => email}, inviter)
end
end
end
# groups_controller.rb
def update
#group = Group.friendly.find(params[:id])
if #group.update_attributes(group_params)
emails = params[:group][:emails]
#group.invite_many(emails, current_user) # also put this in #create
redirect_to #group
else
flash[:error] = "Error saving group. Please try again."
render :edit
end
end
And then nothing in my User model because User.invite is defined already by devise_invitable and I didn't need to do anything else. This process is working great now!
There are some subtle issues with your code. There's a potential race condition on the else branch of your code where you try to add the last created user. I'm also unsure that you need a setter method here unless you are access emails from elsewhere in the instance of Group.
As suggested by others, pass the current user as an argument form the controller. I'm not sure how invite! is implemented, but assuming it returns a user, you can refactor your code considerably.
I would do somethng like this:
def invite_many(emails, inviter)
emails.split(%r{,\s*}).each do |email|
if user = User.find_for_authentication(email: email)
add user
GroupMailer.welcome_email user
else
add User.invite!(email, inviter)
end
end
end
# controller
#group.invite_many(emails, current_user)
# User.invite
def invite(email, inviter)
# create and return the user here, and what else is necessary
end
If you are calling user_emails() from the controller (and I'm guessing you are as that must be where you are receiving the form to pass in emails_string), you can pass in the current_user:
user_emails(emails_string, current_user)
and change user_emails to receive it:
def user_emails=(emails_string, current_user)
You can store the current_user with global scope ,like #current_user,which can be assigned in sessions controller,so in model you will just #current_user as the current user of the app.

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

two forms one model in Ruby, separate validations for each

I have three fields in one form and two fields in another (same as the earlier form, but just missing one field). I want to validate only two fields in the smaller form, but the issue is that it is validating all the three.
I have written the following logic:
**
class User < ActiveRecord::Base
validate :validate_form #for form with 2 fields
private
def validate_form
if :classify_create
self.errors.add(:weight, "need weight") if weight.blank?
self.errors.add(:height, "need height") if height.blank?
end
end
# Validations of attributes (for form with three fields)
validates :weight, :presence => true
validates :height, :presence => true
validates :gender, :presence => true
end
**
and this is my controller action: basically I have written two separate creates:
**# for form with two fields
def classify
#user = User.new
#title = "Classify"
end
def classify_create
#user = User.where("weight = ? and height = ?", params[:weight] ,params[:height])
end
# for form with three fields
def new
#user = User.new
#title = "Train"
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
#title = "Train"
render 'new'
end
end**
When I submit to the two field form, it gives me an error for gender too and redirects to the page with form having three fields. How should I go about it?
Any help will be appreciated.
Regards,
Arun
First, I would not use classify as a method name. You may conflict with a core inflector provided by ActiveSupport. Call it classification or something.
Second, your validation is running on if #user.save in the create method.
In classify_create you use User.where(...) which is a finder method. You're pulling a matching record and setting it to #user. This does not run validation, yet you receive validation errors. You are posting to create, not classify_create. Bad routes will cause this.
Let's address conditional validation first. In your User model, create a variable to act as a bypass switch for your gender validation. Then tell your validation to check if this bypass switch is false before running:
User < ActiveRecord::Base
attr_accessor :skip_gender # defaults to nil (false)
# ...
validates :gender, :presence => true, :if => :validate_gender? # validate if...
# ...
private
def validate_gender?
!self.skip_gender # true = run validation, false = skip validation
end
# ...
end
Next, clean up your controller. Write two create methods, one setting the switch, one not. (This isn't DRY):
def new_classification
# for form with two fields
#user = User.new
#title = "Classify"
end
def new
# for form with three fields
#user = User.new
#title = "Train"
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :action => 'new' # render three-field form
end
end
def create_classification
#user = User.where(:weight => params[:weight], :height => params[:height])
# ... do something with #user ...
#user.skip_gender = true # tell #user to skip gender validation
if #user.save
redirect_to #user
else
render :action => 'new_classification' # render two-field form
end
end
Next, adjust config/routes.rb to specify routes to your custom methods.
resources :users do
member do
get 'new_classification', :to => 'users#new_classification', \
:as => :new_classification_for
post 'create_classification', :to => 'users#create_classification', \
:as => :create_classification_for
end
end
Now change your two-field form view. Specify where your form is submitted to.
<%= form_for #user, :url => create_classification_for_user_url(#user) do |f| %>
That should get you by with what you have...
Your problem is two-fold:
You're trying to use one controller for two distinct actions.
The Rails validation model is somewhat limited and inflexible, there should be separate validation passes for controller methods and models.
The easy solution is to kludge around the limitations with a separate controller:
def create_genderless
# Force the issue to an explicit "unknown" state so that
# "unknown" and "missing" can be differentiated.
params[:user][:gender] = 'U'
# And then punt to the existing `create` method.
create
end
Then a bit more validation in your model for good measure
class User < ActiveRecord::Base
validates :gender, :inclusion => { :in => %w[M F U] }
#...
end
Then update your forms to use UserController#create or UserController#create_genderless as appropriate.

How do I prevent access to records that belong to a different user

How do I prevent accessing a specific set of records based on a session variable?
i.e. I have a table of items with a user_id key, how do I filter access to the items based on user_id. I don't want someone to be able to access /items/3/edit unless that item has their user id against it (based on session var)
update:
I am using the answer suggested by #fl00r with one change, using find_by_id() rather than find() as it returns a nil and can be handled quite nice:
#item = current_user.items.find_by_id([params[:id]]) || item_not_found
where item_not_found is handled in the application controller and just raises a routing error.
Restrict access by fetching items through your current_user object (User should :has_many => :items)
# ItemsController
def edit
#item = current_user.items.find(params[:id])
...
end
where current_user is kind of User.find(session[:user_id])
UPD
Useful Railscast: http://railscasts.com/episodes/178-seven-security-tips, TIP #5
You can check access in show/edit/update method:
def edit
#item = Item.find(params[:id])
restrict_access if #item.user_id != current_user.id
....
end
and add restrict_access method, for example in application_controller
def restrict_access
redirect_to root_path, :alert => "Access denied"
end

NoMethodError when I push a object into a Doc with mongoid

I'm having this problem, I tried a lot of differents aproachs but
everytime it falls in that error.
Enviroment:
Rails 3.0.5
Mongoid 2.0.1
class User
include Mongoid::Document
field :name
has_and_belongs_to_many :companies
end
class Company
include Mongoid::Document
field :name
has_and_belongs_to_many :users
end
In my UserController method Create a I do something like this:
#user = User.where(:email => params[:user][:email])
if #user.count > 0
#user.companies.push(#company)
#user.save
#company.users.push(#user)
#company.save
else
#user = User.create(:name => params[:user][:name],
:email => params[:user][:email],
:password => "123456")
#user.companies.push(#company)
#user.save
#company.users.push(#user)
#company.save
end
When the user dont exist works great.
But if the user is already in the DB, fall a error.
NoMethodError in UserController#create
undefined method `companies' for #<Array:0x10679f638>
But after all it pushes the object into the document.
I don't know if I'm missing something.
If someone know how to solve this ... will be great.
Thanks in advance.
Try this:
#user = User.where(:email => params[:user][:email]).first
On a side note, you may also want to push some of this code into one of your models, either the User or Company model, so that in your controller you would only have one call such as:
#company.add_user(#user)
The implementation details of adding a user would then be encapsulated in your model.
You may also want to consider embedding the two calls to ActiveRecord::Base#save into a single transaction to avoid ending up with inconsistent data in your database.

Resources