In a controller method I have:
#user = current_user
#rel = Relationship.where('user_id = ? and organization_id = ? and fish = ?', #user.id, params[:user][:default_relationship_id], true).first
#user.update_attributes(default_relationship_id: #rel.id)
I understand the last line is not secure and requires strong parameters to prevent mass assignment (meaning a user could set any other db variable for that user as well). But how to refactor to make this secure (in this case and more general)?
If I am correct there are two ways: 1) by replacing it with strong params, or 2) by using a model method.
Ad 1) Strong params:
#user.update_attributes(update_params)
private
def update_params
params.require(:user).permit(:default_relationship_id)
end
But how would this know to set default_relationship_id to #rel.id?
Ad 2) Add it to a model method:
#user.update_default(#rel.id) # In controller
def update_default(value) # In model file
self.update_attributes(default_relationship_id: value)
end
But would this indeed be secure since it's not a private model method?
Could someone explain the question I have for each of the two approaches, and perhaps explain which approach is preferred?
Strong params is for mass assignment, i.e. it is for cases like this
some_user.update_attributes(params[:user])
where the user might have manipulated the form to include extra values. What you're doing isn't mass assignment, so strong params is not relevant. In addition strong params doesn't check the content of the parameters (except for checking that values are scalars).
You may still want to check that is ok for the user to set their default_relationship_id to that value, but you'll need to implement those checks yourself. Neither of your 2 suggestions add any security (which may be fine if the query populating #rel will only return objects the user is allowed to associated with)
Related
ruby_on_rails rails 4 assignment non-screen data to insert record
Rather than using screen values (e.g. simple_form_for #user_evaluation_result) to populate the columns to insert a row I need to calculate some of the values in controller.
For example if I have these statements in the controller
….
# which if I had simple_form_for user_evaluation_result would get populated by the screen
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
….
# standard stuff I use for screen derived updates
def user_evaluation_result_params
params.require(:user_evaluation_result).
permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars )
end
How do I assign values to :user_assumption_id etc so that insert works. I have tried all sorts of statements. Alternatively do I use another format instead of calling "def user_evaluation_result_params".
Thanks in advance - Pierre
I'm hoping I've interpreted the question properly...
First, to make sure we're on the same page... The code inside of your user_evaluation_result_params method is using Strong Parameters to create an ActionController::Parameters object for the purpose of protecting your model from unpermitted mass-assignments. So, in general, when you're creating or updating an ActiveRecord object from a form in a view template, you want to use Strong Parameters so that users can't manipulate your form to set attributes that you're not expecting.
That said, if you want to set attributes on an object you don't have to use mass assignment. Here is an example of using one-at-a-time assignment (the opposite of mass-assignment):
obj = MyObject.new
obj.attr_one = "One"
obj.attr_two = "Two"
obj.save
There is nothing wrong with this approach other than that it's kind of a lot of work for the general case. So mass-assignment just saves us from having to do this all the time. But it sounds like this one-at-a-time assignment is what you're wanting in this case. So try something like this:
def create
#user_evaluation_result = UserEvaluationResult.new
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = "some value"
#user_evaluation_result.save
end
Note, instead of setting #user_evaluation_result.user_assumption_id directly, as you asked about, it is preferred to set the actual object association as I did above. Try to keep associations outside of mass-assignment and use object relationships to build up your object graphs.
Or, if you have some attributes coming from a form you can mix and match the two approaches:
def create
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = params[:user_evaluation_result][:some_other_attr]
#user_evaluation_result.save
end
private
def user_evaluation_result_params
params.require(:user_evaluation_result)
.permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars)
end
Hey I wasn't quite sure what to call this but here's the deal.
I'm trying to only assign things to my database value if
There isn't a value in the database already, and
The value I'm assigning isn't blank.
The rudimentary version of this code is:
venue.address = venue_json['address'] if venue.address.blank? && !venue_json['address'].blank?
where venue is my ActiveRecord result.
This is what I have now (a little better). With the init_value in the Venue.rb class.
Venue.init_value(venue.address, venue_json['address'])
def self.init_value(record, value)
if record.blank? && !value.blank?
record = value
end
end
I'd like to get to this point, but really have no idea how.
venue.address.init_value(venue_json['address'])
especially since I'd like it it work with any attribute of the ActiveRecord class not just the address value.
Separating it into a method sounds like a good idea, but in this case it makes more sense to use an instance method rather than a class method.
def init_attribute(attribute, value)
self.update(attribute => value) if self.send(attribute).blank? && value.present?
end
venue.init_attribute(:address, venue_json['address'])
Some quick comments on the snippet above:
Using direct assignment won't persist the database value. You could go with something else like update or update_column. Or you can use assignment and then call #save on the object.
Whenever you need something not to be blank, you can use the more readable Object#present? which is part of ActiveSupport.
You'll need to call the method with the same name as the attribute on the database object. For this you'll want to use Object#send from Ruby.
I want to create a simple method for initializing different counter fields for users. However, I'm not sure how to set the value of a field referred to as a variable.
def self.initialize(user, field)
counter = "#{field}".to_sym
user.send(counter, nil)
user.save
end
I tried:
user.counter instead of user.send(counter), but it comes back with an undefined method error
user.send(counter) = nil, but that's not the correct syntax
Ruby's accessors work using the name= method for an attribute called name.
You can probably access it this way through the model attributes interface:
user[counter] = nil
user.save
Alternatively, a more generic way that should work on any Ruby object that exposes an attr_writer, attr_accessor, or equivalent:
user.send("#{counter}=", nil)
user.save
You'd only use the send version when dealing with arbitrary method names, like you have here. Converting to_sym is not strictly necessary.
Always be careful to white-list the kinds of method calls you're accepting. You shouldn't let counter be an arbitrary user parameter without some validation.
I'm trying to DRY up a method in which I need to perform the same task on three different attributes. Like this:
if !#item.picture.blank?
picture_copy = Picture.new
picture_copy.save!
item_copy.picture = picture_copy
end
if !#item.picture_for_x.blank?
picture_for_x_copy = PictureForX.new
picture_for_x_copy.save!
item_copy.picture_for_x = picture_for_x_copy
end
if !#item.picture_for_y.blank?
picture_for_y_copy = PictureForY.new
picture_for_y_copy.save!
item_copy.picture_for_y = picture_for_y_copy
end
So basically I'm running the same code, but instantiating different objects and then assigning them to different attributes. It feels like there should be a way to DRY up this view using reflection. Is there a way that I can refer to these attributes and objects as strings passed into a helper method?
For various reasons, I can't just use .clone or .dup: mainly because there's binary file pointers involved and I also need deep copies.
{
picture: Picture,
picture_for_x: PictureForX,
picture_for_y: PictureForY
}.each do |name,klass|
if !#item.send(name).blank?
copy = klass.new
copy.save!
item_copy.send("#{name}=",copy)
end
end
Remember that in Ruby there are no properties or attributes available externally, just methods (which you may choose to call without parentheses so that it looks like you're accessing a property, and which sometimes might be just returning the value of an instance variable).
Object#send is the magic method that lets you invoke a method based on a name stored in a variable.
def picture_for_x_blank?(s = "")
s = "_for_#{s}" unless s.empty?
m = "picture#{s}"
unless #item.send(m).blank?
copy = Kernel::const_get(m.camelize).new
copy.save!
item_copy.send("#{m}=", copy)
end
end
picture_for_x_blank?
picture_for_x_blank?("x")
picture_for_x_blank?("y")
I have array of objects. I can't store it in DB for performance reason. I tried store array in Global ($var) and Class (##var) variables (in controller), but it was the same array for all users. It should be unique for each user session and in each session it should be able to be modified quickly.
I understand that the session[] - is not the best solution. What is the best way?
I'm doing something like this $lines_rules << Processing::rule_creator(...) in every time, when run action in controller.
$lines_rules - it is my array of objects.
Why DB is not right for store $lines_rules? In $lines_rules I store objects with lambda function. When user press button I need call every lambda function with user input and keep result. Then I load new objects into $lines_rules. Thus every request change $lines_rules. I think work with memory is the better way for perfomance.
UPDATE
I use $global_hash [ session[:session_id] ] and this technique for resource saving:
def dead_sessions_killer
ActiveRecord::SessionStore::Session.where(["updated_at < ?", 30.minutes.ago]).each do |session|
$global_hash.delete_if {|key, value| key == session.session_id }
session.delete
end
end
Use a global, but a global hash, keyed by the user's session id.
Store whatever you want in there, although with proper caching, hard to say if this is a great idea.
You could declare the array as a class variable (only 1 # sign), and provide an accessor method.
That is:
class Foo
#some_array
def some_array
#some_array
end
end
From other classes, this lets you call foo_instance.some_array, which keeps the array specific to the user, but allows public access from any class.
You could store the user session in memory with memcache. That would be convenient and fast.
http://awesomerails.wordpress.com/2011/08/23/rails-3-memcached-session-store/