change params value before patch - ruby-on-rails

I have a texfield in a form where a foreign key's value is displayed. now I want to update the value and save it to the DB. Here is the code:
for the form I use:
f.text_field :port, :value =>#entry.port.number, class:"form-
control", placeholder:"Port"
in the controller I am using a param method:
def entry_params
params.require(:entry).permit(:description,:rule_id, :protocol_id,
:url, :port)
end
the update method looks like this:
def update
#entry.url.name = params[:entry][:url]
#entry.port.number = params[:entry][:port]
if #entry.update(entry_params)
flash[:success] = "Entry was successfully updated!"
redirect_to entry_path(#entry)
else
render 'edit'
end
end
but if I want try to save it, it shows this error:
Url(#70247237379440) expected, got "www.drive.google.com" which is an
instance of String(#70247218839280)
Now my question is, (I'm relativ new to rails) how can I fix this? I know that it expect an object as a parameter but if I change the param like this:
params[:url] = #entry.url
it doesn't work.

There are two approaches I can think of here, depending on exactly what you're looking to achieve.
If you're looking to assign an entry a new url based on a string params, you can use something like the following:
#entry.url = Url.find_by_name(params[:entry][:url])
Depending on your models' setup, if there's a url_id column on your entries, you might be better off using a select field on this, passing the URL's name and id to the options. If you can add this info to your question, I can update / rule this out as needed.
If you're just looking to update the URL via the entry's form, you'd be best looking at using accepts_nested_attributes_for.
Doing this, you can directly update the associated objects through the parent's form. If this sounds like the right approach for you, let me know and I can provide more detail :)
Edit: as per your comment, is sounds like this is the option you're after. So, you'd need something like the following:
entry.rb
accepts_nested_attributes_for :url
In the form:
...
f.nested_fields_for :url do |url_fields|
url_fields.text_field :name
end
...
And you'll need to update the params in your controller to accept these nested fields. I can't remember the exact for they take, but it's something like:
def new / edit # whichever you're in
...
#entry.build_url unless #entry.url.present?
end
def entry_params
params.require(:entry).permit(:description,:rule_id, :protocol_id,
:port, url_attributes: [:name])
end
(It might be you need an empty array or a hash for url_attributes.)
That'll then directly update the associated url. FYI if you've not got an associated URL, you'll need to build one using #entry.build_url in the controller.
Hope this helps - if you've any questions / details to help clarify, let me know and I'll update as needed.

Related

Ruby - How to define params for require()?

questions_controller.rb
def index
#questions = Question.all(app_params)
end
private
def app_params
params.require(:questions).permit(:question, :answer)
end
end
question.rb
class Question < ActiveRecord::Base
end
I am completely new to ruby-on-rails. I was following a guide and it said I should take care of some "loopholes" or "security issues" and it used attr_accessible, but on Rails 4, they suggest strong parameters, so now I'm trying to use them. I'm confused on how to define the :questions params, because I'm currently getting an error saying that :questions param is not found.
:questions is pretty much something that I will define myself as the web developer.
So for example, I will define questions = "How are you?", "What is your name?". I'm basically starting very simply. I want questions that I have created to be displayed on my webpage. Ultimately, I plan to make a website what is basically a list of questions and, with answer options. After the user clicks "submit" I want to store the information into my database.
Am I supposed to even be requiring this as a param? I'm completely lost..
Do you have a dump of the params we could look at? They are shown when your app encounters an error, and typically shows you the params array which rails will pass through
Strong Params In Rails 4
Strong Params allow you to allow certain parameters for use in the controller, protecting against any malicious assignment client-side. They replaced attr_accessible in Rails 4.0
Strong Params is only for user-submitted content, as it's designed to protect the params hash. To that end, it's mostly used with the create and find functions:
class PeopleController < ActionController::Base
# Using "Person.create(params[:person])" would raise an
# ActiveModel::ForbiddenAttributes exception because it'd
# be using mass assignment without an explicit permit step.
# This is the recommended form:
def create
Person.create(person_params)
end
# This will pass with flying colors as long as there's a person key in the
# parameters, otherwise it'll raise an ActionController::MissingParameter
# exception, which will get caught by ActionController::Base and turned
# into a 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap { |person|
person.update!(person_params)
}
end
private
# Using a private method to encapsulate the permissible parameters is
# just a good pattern since you'll be able to reuse the same permit
# list between create and update. Also, you can specialize this method
# with per-user checking of permissible attributes.
def person_params
params.require(:person).permit(:name, :age)
end
end
params.require
The params.require function works by taking this params hash:
params{:question => {:question => "1", :answer => "5"}}
That's why people asked what your params hash looks like, because the require function can only work if the :question hash is present.
Possible Solutions For You
Question.all(app_params)
Regardless of what you're trying to achieve, don't use all. The where function is better for receiving an array of data based on certain values. I believe all is depreciated anyway.
def index
#questions = Question.where("value = ?", variable)
end
What data is being passed?
I will define questions = "How are you?", "What is your name?"
This is okay, but typically in rails, you'd call data by using an ID in the database. If you're defining these questions in a form, you'd use the strong params system; but you'd need a form to submit the data to
Further Additions
The rails way is to keep all your data in a database, and use the application to manipulate that data, either by showing it, or allowing people to input more.
The "params" variables are basically there to help the rails controllers & models accept & process data from end users, and consequently allow you to keep the system growing. Instead of having to write custom code to accommodate all sorts of different data, the params give you a rigid structure to work with. Here is a good explaination of how MVC (and params) works for you: How does an MVC system work?
I think you're getting confused with how your app should work
Your "questions" should be stored in a questions table / model, and can be accessed by calling their ID's with the find function. This code would be like this:
#app/controllers/questions_controller.rb
def show
#question = Question.find(params[:id])
end
If you want to add new questions, you'll be best to add them to the questions table, like this:
#app/controllers/questions_controller.rb
def new
#question = Question.new
end
def create
#question = Question.new(question_params)
#question.save
end
private
def question_params
params.require(:question).permit(:question)
end
#app/views/questions/new.html.erb
<%= form_for #question do |f| %>
<%= f.text_field :question %>
<% end %>
This will give you a central store of your questions, which you'll then be able to access when you need them, either with a helper or with your ".all" call :)
Give it a shot with question (singular):
params.require(:question).permit(:text, :answer)
Assuming question is your model and text (which I made up) is the wording of the question.

Adding a parameter to an instance variable in Rails

I am creating a instance variable that gets passed to my view. This variable 'post' has a user_id associated with it and I wanted to add an extra attribute called 'username' so I can also pass that and use it in the view.
Here is an example of what I would like to do.
#post = Post.find(params[:id])
#post.username = User.find(#post.user_id).username
A username column does exist on my Users model but not my Songs model. So it won't let me use
#post.username
I know I can just make an entirely new instance variable and put that information in there but I would like to keep everything nice and neat, in one variable. Which will also make my json rendered code look cleaner.
Any ideas on how I can accomplish this?
Thanks!
Based on the presence of a user_id in your Post model, you probably already have an association set up that can retrieve the username. It will probably save a lot of trouble to simply use the existing association:
#post = Post.find(params[:id])
username = #post.user.username
If you're likely to be querying more than one post at a time (e.g., on an index page, calling .includes to tell Rails to eager-load an association will help you avoid the N+1 problem:
#posts = Post.includes(:user).all
Finally, to include the associated record in your JSON output, pass the :include parameter as you serialize:
# in controller
render :json => #post.to_json(:include => :user)
This question includes a much more comprehensive discussion of serialization options. Well worth a read.
No need to pass a separate instance variable.
1. You can use #post.user.username in view itself.
2. Or you can create a helper and pass #post.user
def username user
user.username
end

How to stop a user form adding forms to a field?

I have a form that allows a user to update their profile information, but I would like to prevent some information from being changed. I also would like to keep my controller code very simple. In the update action of my Users Controller, I have the following code:
def update
#user = Users.find params[:id]
if #user.update_attributes(params[:user])
flash[:notice] = 'Update successful.'
redirect_to user_path(#user)
else
render :action => :edit
end
end
This is very clean and simple, and I like that. What I don't like, however, is that a user can add a field to the form, with the same name as an attribute, and use it to modify forbidden attributes. Is there a simple way to do this, or do I need to devise a way to do this myself?
One method I was considering was to generate a hash value, using a hash-based message authentication code, of all the form's element names. This message access code would be a hidden value in the form. Then, once the form is submitted, I would calculate the message access code (MAC) again using the names of the parameter Hash's keys. If the two MACs are different, or if the first MAC is missing from the parameter Hash, I would throw an error. I would rather not spend the time implementing this if there was already and easy solution out there.
Thanks.
On your model you can use attr_protected or attr_accessible to blacklist or whitelist attributes when being set via mass assignment (like when a form is submitted).
Rails will prevent mass assignment if you use attr_protected :protectedcolumn (blacklist) or attr_accessible :safecolumn (whitelist) within your model. More information on this topic can be found in the Ruby on Rails Security Guide (Section 6.1)

Rails gem rails3-jquery-autocomplete how to scope by user

I'm using the Rails gem rails3-jquery-autocomplete to add categories to posts.
I would like to restrict the search to include only categories that belong to the current user or post's author in the results.
The documentation says that I can specify a scope:
:scopes
Added option to use scopes. Pass scopes in an array. e.g :scopes =>
[:scope1, :scope2]
But I'm not sure how I would pass the user id here?
It seems like a comon scenario, am I missing something obvious?
I found an answer that suggests modifying the get_item method, but that seems to break the auto-complete
Scoping the results for rails3 jquery autocomplete plugin
In posts_controller:
def get_autocomplete_items(parameters)
items = super(parameters)
items = items.where(:user_id => current_user.id)
end
I'm first calling the original get_autocomplete_items method, and then filtering out the results by current_user.id.
This question helped:
Rails 3: alias_method_chain still used?
I had a similar problem I solved thanks to the answers above.
My autocomplete also worked against a User model, but I needed to restrict the results to the user's institution (Institution has many :users). My controller creates an #institution instance variable that is accessed in the view.
Although the get_autocomplete_items method cannot directly access the instance variable, I found that the data CAN be passed to autocomplete as a parameter (note: I use the simple_forms gem, so the input call looks a little different than the standard rails syntax).
In my view:
<%= f.input :email, :url => autocomplete_user_email_institutions_path(:institution_id=>#institution.id.to_s), :as => :autocomplete %>
In my controller:
autocomplete :user, :email, :extra_data => [:first_name, :last_name]
def get_autocomplete_items(parameters)
super(parameters).where(:institution_id => params[:institution_id])
end
My autocomplete list is now scoped to just the users who work for a particular institution.
deb's answer works for me.
The code can be cleaned up a bit:
def get_autocomplete_items(parameters)
super(parameters).where(:user_id => current_user.id)
end
There is small update to code for those who have having trouble with super method.because of dynamic dispatch it above code need to replaced as below:
def get_autocomplete_items(parameters)
items = super(parameters)
items = items.where(searchable: true)
end
to this:
def get_autocomplete_items(parameters)
items = active_record_get_autocomplete_items(parameters)
items = items.where(searchable: true)
end
Reference: https://github.com/crowdint/rails3-jquery-autocomplete/issues/278
To answer the question posed by #ctilley79, multiple autocompletes is not a problem because, in addition to the possibility of passing more values in the params hash, you also have access to the autocomplete parameters. On my form (as an example), I have both a City and a Zip autocomplete. I need to restrict the City to those in a certain state. So my controller action looks like this:
def get_autocomplete_items(parameters)
if (parameters[:model] == City)
super(parameters).where("state_id" => params[:state_id])
else
super(parameters)
end
end
You also have access to the method in case you need it. Do logger.debug on the parameters to see all that is available.
I know the gem and the question are old but I found myself using this gem and needing this answer recently... None of the old answers will work anymore because in the source code, the method get_autocomplete_items is generated dynamically and has the ORM prepended on the method name. This is what got it working for me. I assume most folks are using ActiveRecord too but check the autocomplete.rb method 'get_prefix' to figure out what you should prepend to the method name to get it working.
Hope this saves someone a bunch of time. Be the change you want to see and all that ;)
def active_record_get_autocomplete_items(parameters)
super(parameters).where(id: current_user.id)
end
I faced a similar problem. Our site is multi-tenant, so everything needs to be scoped to the tenant.
To make this easier, I modified rails3-jquery-autocomplete to accept another option called :base_scope. It takes a string, that gets eval'd instead of using the model. All the other functionality works, so you can append additional scopes and where clauses if you need to.
My fork is here: https://github.com/GiveCorps/rails3-jquery-autocomplete
I am not sure that the tests i wrote prove it will always work. I just checked that it was using the scope instead of the model in the items method.
i would appreciate any thoughts on it. Not sure whether it merits a pull request.

update_attributes field tweaks

So I've got an edit page that has butt-load of editable fields on it...simple update...
#patient.update_attributes(params[:patient])...everything's great, except....
I've got one field out of these 20 that I need to tweak a little before it's ready for the db and it would seem I either need to do
two trips
#patient.update_attributes(params[:patient])
#patient.update_attribute( :field=>'blah')
or set them all individually
patient.update_attributes(:field1=>'asdf', :field2=>'sdfg',:field3=>'dfgh', etc...)
Am I missing a way to do this is one swoop?
What's the attribute you need to tweak? There's two ways to do this:
Either massage the params before you send them to the update_attribute method:
I'm just giving an example here if you wanted to underscore one of the values:
params[:patient][:my_tweak_attribute].gsub!(" ", "_")
#patient.update_attributes(params[:patient])
Then there's the preferred way of doing your tweaking in a before_save or before_update callback in your model:
class Patient < ActiveRecord::Base
before_update :fix_my_tweak_attribute, :if => :my_tweak_attribute_changed?
protected
def fix_my_tweak_attribute
self.my_tweak_attribute.gsub!(" ", "_")
end
end
This keeps your controller clean of code that it probably doesn't really need.
If you just need to add a new param that didn't get sent by the form you can do it in the controller like this:
params[:patient][:updated_by_id] = current_user.id
#patient.update_attributes(params[:patient])
Assuming current_user is defined for you somewhere (again, just an example)
You can create a virtual attribute for that field. Say the field is :name. You create a function in your Patient model like :
def name
self[:name] = self[:name] * 2
end
And of course, you do your things inside that function :) Instaed of self[:name], you can also use read_attribute(:name).

Resources