"colon" notation in Ruby - ruby-on-rails

I'm new to Ruby, and trying to read/understand some rb files. I've come across this line. Could anyone explain what it is doing (and the gramatical meaning behind it, too)?
#account = current_user.accounts.find(params[:id])
1: what is current_user? I grepped the entire file, but didn't see where it was declared. (Well, I know variables don't need to be declared in Ruby, but it is not referred to with an # sign, so this might not be a variable?)
Where should I expect to find current_user? (ie., in app/model, app/view, etc?)
2: What is the meaning of :id?
params looks like an array, so I guess :id somehow means the index, but why is there the colon before id?
Thanks

what is current_user?
It's often hard to tell the difference between a local variable and a method being called. This is because the () is optional, and often omitted. In this case, current_user is a method, declared in a mixin or superclass somewhere, which is why it's not in this file. Think of current_user.accounts as current_user().accounts, which should illustrate what's going on there.
Where should I expect to find current_user?
Most likely, it provided in a plugin or gem that handles authentication for your project. It's hard to advise more than that without knowing more about your project.
What is the meaning of :id? params
It's pulling a value from a hash, by it's key. In irb, try this:
params = { :id => 123 }
puts params[:id] # => prints 123
So params is a hash (some languages call this datatype an associative array, or a dictionary). It's a way to store a set of key/value pairs. The somehash[somekey] notation returns the value for somekey in somehash.
:id itself is a symbol. It's sort of like a string that never changes. Do some research on ruby symbols to learn more on that.
And in rails, params is a special hash. Values passed form the request show up in this hash. So the route /books/:id would active when you load /books/123, which would set params[:id] to 123 in the controller.

Current user is likely provided by a gem. Example of a gem would be Devise.
"params" is a hash of the parameters passed as the query string on the URI. So, ":id" would reference the value of the id parameter.
Example:
www.domain.com?id=abc would yield params[:id] as "abc".

Related

What is map(&:id) in Rails?

My question is not an error, it is for understanding. As I'm new to Rails, I can't read all the code yet.
what does (&:id) do after .map
#user_cnae_classifications = user.cnae_classifications.map(&:id)
what is the difference of .map with it and without it?
in this method call:
UserCnaeClassification.create(
user: #user,
cnae_classification_id: id
)
How do I read that part of the code...
user: #user,
cnae_classification_id: id
are they keys and values?
1 )
You should read some tutorials on map to get acquainted.
https://www.rubyguides.com/2018/10/ruby-map-method
But the short answer is that running user.cnae_classifications.map(&:id) will loop over all cnae_classifications and extract the id from them and put them all into an array. The & character allows for map shorthand to avoid passing an entire block.
From the link above:
2 )
The #create method can accept a key-value hash of known attributes (known to the class in question, in this case that is UserCnaeClassification) to assign upon creation. So you're basically right, they are key-value pairs but they are specific to this class/object. Those same keys might not work on another class/object.
Additional reading: https://guides.rubyonrails.org/active_record_basics.html#create
what does (&:id) do after .map
The syntax map(&:method) is equivalent to:
object.map do |i|
i.method
end
The complete explanation is that the & operator is used to convert any Ruby object that responds to to_proc into a Proc, which encapsulates a block of code. In this case, the Symbol object (:id) is converted into the block of code above.
If you're interested in learning more about it, notice this is pure Ruby, not Rails-specific. Check the documentation for Proc.
In this method call:
How do I read that part of the code...
are they keys and values?
These are keyword arguments. It's a way to name the parameters of a method to explicitly tell the reader what each value should be. Just be aware that the behavior of methods accepting hashes as keyword arguments is deprecated, as seen in this official post.
The .map(&:id) is a shorthand for the longer form of .map { |x| x.id }.
Some interesting things to say: if you're using database (ORM - ActiveRecord), you will see that writing map(&:id) could be helpful. There also exists method called pluck, which does similiar things, but it's a little faster.
Usage:
Also pluck doesn't work with regular Arrays.

In Rails 5, is there a way to modify the underlying params in a controller? Or give it a default?

In a Rails 5 controller, you can call params and it returns a hash of the parameters from the request.
But you can't modify the params that way. Because what you're modifying is a copy of the params hash values, not a reference to the underlying params.
params[:starting_value] ||= "abc" # doesn't work for my purposes
What you're supposed to do is store the values elsewhere.
#starting_value = params[:starting_value] || "abc"
But if a bunch of other places in the code expect params[:starting_value], then this solution might require some messy changes.
Is there a way to set the default value of a param in the controller? Or am I going to have to do it the slightly messier way.
I could also accomplish what I want with a redirect, but that isn't ideal either.
I think you're looking for the merge! method. Docs Here
params = params.merge!(:starting_value, 'abc)
It returns the original params with the new one merged in or overwritten. Be aware that merge without an exclamation mark does not modify in place. You need it to keep the changes.

Ruby: adding hash of params to params of a function: looking for a cleaner solution

I need to call a function which its params is a hash. For its params I have a hash, and an attribute that I need to merge. To do so, I use the following, which works correctly:
paramsHash={:att1=> "1", :att2=>"2"} #this is obtained from a function
result=MyClass.where({:att0=> "0"}.merge(paramsHash))
As said, this works, no problem there. My question is, is there a nice ruby fancy way to to this? Something like
paramsHash={:att1=> "1", :att2=>"2"} #this is obtained from a function
result=MyClass.where(:att0=> "0", paramsHash.as_params)
Thanks
There is no fancier way to do this than merge, I just would write it the other way around so you can relieve the curly braces:
result = MyClass.where(params_hash.merge(att0: "0"))
This is the fanciest way I can think of writing your code. It does however change the way the hashes get merged, which makes no difference in the code you presented in your question, but may make a difference if the same key is present in both hashes.
Other things to make your ruby fancier:
It is common to use underlined notation and not camelCase for local variables in ruby, so params_hash and not paramsHash.
Spaces for between the assignment operator, the variable getting assigned and the assignment are common: result = 'this' and not result='this'
Same with the key-value pairs in Hashes: {:this => 'is a hash'} and not {:this=>'is a hash'} in ruby 1.9., you can even do {this: 'is a hash'}, which is ruby 1.9 notation for symbols as hash keys.
Some rubyists like to relieve the optional braces, you can do that too if you like:
result = MyClass.where params_hash.merge(att0: "0")
or
result = MyClass.where(params_hash.merge att0: "0")

Understanding will_paginate arguments

Total Rails noob, working through the Rails Tutorial videos. I'm all the way to the last lesson, and there's something I don't understand:
#users = #user.followed_users.paginate(page: params[:page])
Specifically, the bit I'm not tracking on is paginate(page: params[:page]). I looked at the paginate docs and I understand the paginate method can take three params, :page being one of them. I think this parameter means "current page," but the will_paginate docs say it defaults to 1.
I also know (think) that params[:page] refers to the built-in Rails params hash, meaning, current session params. Right?
So... I don't get it. Why do I need it? How does the :page symbol get into the params hash? What does this really do?
For additional context, see listing 11.30 on the Ruby Tutorial book. Any help would be much appreciated.
I think what you might be misunderstanding is how Ruby arguments work in this case. paginate does not actually take 3 arguments, but instead takes a single hash argument with three options (key/value pairs).
In Ruby, when you pass key/value pairs as the last set of arguments, they are automatically converted to a hash. For example, the following are equivalent:
paginate({page: 1})
is the same as:
paginate(page: 1)
So really what you are doing is passing a single argument, which is a hash that has multiple key/value pairs.
Now to specifically answer your questions:
Why do I need it?
You need to pass this value in so that will_paginate knows which page you are currently on. It defaults to page one because on the initial page load, you will not have ?page=x in your URL. After you change to a different page, it takes the page value from the URL and passes that to the paginate method.
How does the :page symbol get into the params hash?
Any argument that is part of the query params in the URL will get automatically passed to the params hash by Rails (more likely Rack which Rails is built upon)
What does this really do?
I'm hoping the above answered this, but if not, maybe it provided you with enough info to come up with a more specific question.

how to parse multivalued field from URL query in Rails

I have a URL of form http://www.example.com?foo=one&foo=two
I want to get an array of values ['one', 'two'] for foo, but params[:foo] only returns the first value.
I know that if I used foo[] instead of foo in the URL, then params[:foo] would give me the desired array.
However, I want to avoid changing the structure of the URL if possible, since its form is provided as a spec to a client application. is there a good way to get all the values without changing the parameter name?
You can use the default Ruby CGI module to parse the query string in a Rails controller like so:
params = CGI.parse(request.query_string)
This will give you what you want, but note that you won't get any of Rails other extensions to query string parsing, such as using HashWithIndifferentAccess, so you will have to us String rather than Symbol keys.
Also, I don't believe you can set params like that with a single line and overwrite the default rails params contents. Depending on how widespread you want this change, you may need to monkey patch or hack the internals a little bit. However the expeditious thing if you wanted a global change would be to put this in a before filter in application.rb and use a new instance var like #raw_params
I like the CGI.parse(request.query_string) solution mentioned in another answer. You could do this to merge the custom parsed query string into params:
params.merge!(CGI.parse(request.query_string).symbolize_keys)

Resources