Why are these default parameters defined as they are? - ruby-on-rails

I'm currently learning Ruby and RoR and I stumbled across this declaration:
link_to_remote(name, options = {}, html_options = nil)
I discovered that this pattern is used on several other Rails functions.
Why are the default values defined that way? Why not one of these two?
... options = {}, html_options = {})
... options = nil, html_options = nil)
Is this some kind of convention I should follow in my own functions, too?

The method is defined like this:
link_to_function(name, remote_function(options), html_options || options.delete(:html))
This allows you to specify the html_options as part of the options hash instead of as a separate parameter. On the other hand, the options hash is always passed to remote_function, so we need it.
It's also a bit more efficient to use the singleton nil rather than construct an array that will never be used every time the method is called. I wouldn't say this reason is so compelling that I wouldn't use {} if it made the resulting code cleaner, but in the absence of any other consideration, it seems like the logical thing to do.

Not an expert on it, but for options it might be because the calling code uses the merge method to combine whatever you pass in with the assumed defaults; setting it to nil would just remove all of the options. I'm not sure about html_options, but it might be something similar to that.
html_options might call a block and have something defined to check if it's given a hash or not. Like I said I'm not an expert but that might be why. I just checked on an Ubuntu VM and according to irb, an empty hash evaluates to true so that might be why. The calling code probably uses a block with something line:
if html_options
# do stuff
end
so it's nil by default because the code would execute and probably give you a nil error with an empty Hash

Related

Check if hash is nil before checking specific attribute

What I'm currently doing is
"Hello" if options && options[:greet]
What I would like to do is cut that line down. If options is nil, options[:greet] obviously will be too. Does Ruby/Rails provide a method that offers this "hash checking" ability? Or perhaps there's a way of writing this more succinctly?
There's also one more shortcut, I tend to use it more often, when I don't have control over options variable (i.e. it may be either nil or hash):
options.to_h[:greet] # just convert it to a hash before key access
Note, that it works only starting from Ruby 2.0.
I would argue that that line is perfectly fine.
Perhaps it might make sense to ensure that options are always set. If the options are passed in as a parameter to a method, you might want to set a default:
def method_name(options = {})
Or you might want to initialize options with an empty hash if nil before you start using them:
options ||= {}
Ruby on Rails also offers the try method that does not fail when you call methods on nil:
options.try([], :greet)

"colon" notation in Ruby

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".

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")

Making dynamic scope helpers in Rails

Several of my partials can be rendered in two "modes". If full_display is false, I don't render several fields. To make things easy I wanted to make one of the "modes" default - if full_display is not defined, treat it as false. I came up with this code:
(((not defined?(full_display).nil?) && full_display) || false)
Quite a lot to move around. It would be nice to put it inside a helper or something, but since Ruby has only lexical scope I can't think of any good way to do it.
Bad ideas that I've already tried:
on the top of the partial do <% display = long code that is above %> and use display through the code, but creating local variables in a view looks bad and has to be copied into every partial using full_display.
wrap it inside a string, put it into a helper and use eval(display_helper) in view, but obviously this creates security concerns.
That's quite a convoluted way of saying something as simple as:
defined?(full_display) && full_display
In Ruby there are two values that are non-true, nil and false, all others evaluate as true, which includes 0, empty string, among others, that would otherwise evaluate as false in Perl, PHP, and C. Testing with .nil? is usually reserved for those rare cases where you want to differentiate between false and undefined, and this is sometimes the case with boolean fields where a missing value is different from a false value.
In any case, in the view space it is a lot easier to assign defaults using the or-equals operator ||= like this:
<% full_display ||= false %>
That is equivalent to full_display = full_display || false. This does two things. First, it defines the full_display variable even if it was not previously created, and secondly it ensures that the it will contain a value that is at least false, never undefined.
You will see the ||= default pattern a lot in Ruby code as it's an easy way to assign something in the case where it will be nil otherwise.

defined? method in Ruby and Rails

I have a quite old templating system written on top of ERB. It relies on ERB templates stored in database. Those are read and rendered. When I want to pass data from one template to another I use the :locals parameter to Rails render method. For setting default variables of those variables in some templates I use the defined? method which simply tells me if local variable has been defined and if not I initialize it with default value like this:
unless defined?(perex)
perex = true
end
I am upgrading the app to latest Rails and I see some weird behavior. Basically this sometimes works (sometimes perex is undefined) and sometimes it does not (perex is defined and set to nil). This happens without anything else changing.
I have two questions:
Is there any better way other than using defined? which is proving unreliable (was reliable for several years on top Rails 1.6)? Such a way should not result in me rewriting all the templates.
I have been going through Ruby docs and was not able to find anything about defined? method. Was it deprecated or am I just plain blind?
Edit: The actual issue was caused by what seems to be a Ruby/eRB bug. Sometimes the unless statement would work, but sometimes not. The weird thing is that even if the second line got executed perex stil stayed nil to the rest of the world. Removing defined? resolved that.
First: actually, defined? is an operator.
Second: if I understand your question correctly, the way to do it is with this Ruby idiom:
perex ||= true
That'll assign true to perex if it's undefined or nil. It's not exactly what your example does, since yours doesn't evaluate the assignment when the value is nil, but if you are relying on that then, in my opinion, without seeing it, you're not writing clear code.
Edit: As Honza noted, the statement above will replace the value of perex when it's false. Then I propose the following to rewrite the minimum number of lines:
perex ||= perex.nil? # Assign true only when perex is undefined or nil
The safest way of testing if a local is defined in a Rails template is:
local_assigns[:perex]
This is documented in the Rails API together with the explanation that defined? cannot be used because of a implementation restriction.
Per mislav's answer, I went looking for that documentation in the Rails API, and found it in Class ActionView::Base (under the heading "Passing local variables to sub templates"). It was hardly worth the search, though, since it barely said anything more than mislav did. Except that it recommends this pattern:
if local_assigns.has_key? :perex
Taking into considerationg mislav's original answer and KenB's elaboration, I think the following is the absolute best approach (though I'm open to opinion). It utilizes Ruby's Hash#fetch method to fallback on an alternate value if the key does not exist in the original hash.
perex = local_assigns.fetch(:perex, true)
This is even better than the ||= method that most users will suggest since sometimes you will want to allow false values. For example, the following code will never allow a false value to be passed in:
perex = local_assigns[:perex] || true

Resources