I have a hard time understanding the form :attribute => parameter
Can anyone give me some explanations for it? Is :attribute a field (variable) belonging to the class or something else? Why we can pass this pair as one parameter to methods?
If you're referring to something like this:
some_method(:foo => "bar", :baz => "abc")
then it's just shorthand which causes ruby to convert those things into a Hash. Please note that when using this form, that the hash must be the final argument to the method in order for this to work.
Based on the explanation above, this
some_method(:foo => "bar", :baz => "abc")
is ok, but this
some_method(:foo => "bar", :baz => "abc", moo)
is not.
Though you will see this commonly in Rails, it is not a Rails specific question. It is Ruby.
The answer to your question is that it is key/value pairs in a Hash, generally passed as an argument to a method.
You will see this as well when it is being assigned to a variable directly. But let me show you a sample method, and a sample usage, so that you can put them together:
def some_method(*args, name: 'Joe', amount: 42, **other_params )
puts "#{name}, #{amount}, glob of arguments = #{args.inspect}",
"other params #{other_params}"
end
some_method(:occupation => 'programmer', :phone => '123-456-7890', name: 'Jane')
This is Ruby 2.0.0 specific in the fact that you can provide for that last argument, which provides for unnamed parameters, in practice. Using the 1.9+ syntax for a Hash in the argument list, you are allowed to provide for other unnamed "parameters" which can appear after the hash argument.
Notice that if I had used the older syntax for Hash, namely the :key => 'value' syntax, I would not be allowed (at least as of this writing) to have the **other_params argument at the end of the argument list.
You could also provide the hash using the newer syntax in the calling code, though I left it as the Hash syntax when calling some_method.
The Hash still needs to be the last provided in the calling argument list, the same as indicated in the argument list for the method definition.
Related
Good aftern, SO folks
I am strong parameterizing a Rails 3 application that we plan to upgrade to Rails 4. Some of the controllers use the params object to hold not just nested hashes, but hashes within arrays within hashes within arrays etc. Changing the nature of the data structure would be too intense, we want to ideally have it return the same data structure, but strong parameterized
Here's an example as JSON:
"my_example" => {
"options" =>
[{"id" => "1"
"name" => "claire"
"keywords" =>
["foo", "bar"]
},
{"id" => "2",
"name" => "marie",
"keywords =>
["baz"]
}],
"wut" => "I know, right?"
}
But for added fun, the keywords array can contain any string. Which I've read about and which is tricky and supported in other versions of rails but whatever.
Are there any general rules of thumb about making complex data structures with the strong_parameters gem? I know that Rails 4 and 5 handle this better, but I'm curious.
Nested parameters are not really that challenging.
params.require(:my_example)
.permit(:wutz, options: [:id, :name, keywords: []])
This expects that options is an array of resources where the keys :id, :name, and :keywords are to be whitelisted.
:wutz, :id, :name can be any permitted scalar type. keywords: [] permits an array of any scalar type (any string, integer, date, etc). I don't really get why you're fretting here.
The issue is mainly with nested hashes with extremely dynamic contents. In that case which is not quite covered the Rails strong parameters you can use .permit! and unleash the full tools of Ruby hash slicing and dicing which are quite formidable.
The gem pretty much backports the api of ActionController::Parameters in later versions of Rails pretty closely so I would not expect any major hickups when upgrading.
https://github.com/rails/strong_parameters#nested-parameters
I used to be more of a hobby Java guy and try to switch to Ruby on Rails right now.
But I'm having some difficulties, believe it or not, I like braces and semicolons..gives some orientation.
But here's the question:
Right now I'm taking an online course in RoR and it occured to me, that I'm always wrong on how to work with symbols, hashes etc.
Example 1:
Let's take a look at this single line of code for example:
form_for(:session, :html => {class: "form-horizontal", role: "form"}, url: login_path)
And this is how I read it:
Method / Function name is
form_for
Parameters parsed to this method are:
:session, :html => {class: "form-horizontal", role: "form"}, url: login_path
Let's break up those into
:session
:html => {class: "form-horizontal", role: "form"}
url: login_path
How the heck should I know how to declare those parameters?
Why are :session and :html passend in as keys and url not?
Is the :html symbol a Hashmap-symbol?
Example:
In an model File you declare an n:m relationship like this (for example users <-> stocks)
has_many :users, through: :user_stocks
Ok, I get that the first argument is :users and the second is the same as
:through => :user_stocks
correct?
But in the same way, let's look at an routes.rb config from the same project:
resources :user_stocks, except: [:show, :edit, :update]
Now we're using an array of keys on the except hash, correct?
It does get clearer when writing an question but still, is there a rule of thumb / convention on when to use
:name
name: value
:name => {values}?
name: [values]
Or is it just an personal preference? In that case I should hope that my online teacher stays consistent..
Generally speaking, I'm very confused on how the parameter syntax convention is and when to use what (what type of argument).
Is it just because I am starting with Ruby or did I miss some piece of convention.
I hope my problem is kind of understandable and excuse my english - non native speaker.
I really like to get along with RoR but right now watching the online course sometimes leaves me more confused than before because if I would've done it by myself, I would've used a completely different way.
How the heck should I know how to declare those parameters?
You look up the method in the docs and you read about it.
Parameters parsed to this method are:
:session,
:html => {class: "form-horizontal", role: "form"},
url: login_path
How the heck should I know how to declare those parameters? Why are
:session and :html passend in as keys and url not? Is the :html symbol
a Hashmap-symbol?
In ruby, if you pass in a series of key-value pairs at the end of the argument list, ruby gathers them all into a hash and passes them as one argument to the method. Here is an example:
def go(x, y)
p x
p y
end
go(:hello, a: 10, b: 20)
--output:--
:hello
{:a=>10, :b=>20}
Another example:
def go(x, y)
p x
p y
end
go(
:session,
:html => {class: "form-horizontal", role: "form"},
url: 'xyz'
)
--output:--
:session
{:html=>{:class=>"form-horizontal", :role=>"form"}, :url=>"xyz"}
has_many :users, through: :user_stocks
Ok, I get that the first argument is :users and the second is the same
as
:through => :user_stocks
correct?
Correct. In old ruby, key-value pairs in hashes were written like this:
'a' => 'hello'
If the value was a symbol, then it looked like this:
'a' => :hello
If the key was also a symbol, then you wrote:
:a => :hello
In modern ruby, if the key is a symbol you can write:
a: 'hello'
which is a shortcut for:
:a => 'hello'
and if the value is a symbol as well, in modern ruby it looks like this:
a: :hello
which is a shortcut for:
:a => :hello
resources :user_stocks, except: [:show, :edit, :update]
Now we're using an array of keys on the except hash, correct?
The hash isn't named except, but otherwise you are correct.
a rule of thumb / convention on when to use
:name # Single symbol argument
name: value # A key-value pair in a hash. The key is a symbol.
:name => {values}? #A key-value pair in a hash. The value looks like a hash, but the syntax is incorrect.
name: [values] #A key-value pair in a hash. The value is your notation for an array.
Or is it just an personal preference? In that case I should hope that my online teacher stays consistent..
Once again, a method can be defined to take any type of argument. Because ruby variables don't have types, you have to check the docs. If a method expects you to pass in a hash where the key :name has a value that is a hash, then you need to do that. On the other hand, if the method expects you to pass in a hash where the key :name has a value that is an array, then you need to do that.
Generally speaking, I'm very confused on how the parameter syntax
convention is and when to use what (what type of argument). Is it just
because I am starting with Ruby or did I miss some piece of
convention.
Ruby has a lot of shortcuts, which can be confusing to a beginner. Then there is the whole String v. Symbol concept. If you can understand the practical difference between a Symbol and a String, then you will be ahead of the game. A Symbol is like an integer. So when ruby has to compare whether Symbols are equal, ruby compares two integers, which is fast. If ruby has to compare Strings, then ruby has to compare the ascii code of each letter in one string to the ascii code of each letter in the other string until ruby finds a difference. For instance, in order for ruby to compare the following two Strings:
"helloX" v. "helloY"
ruby won't find a difference until after it has made six integer comparisons:
'h' v 'h' => equal
'e' v 'e' => equal
...
...
'X' v 'Y' => not equal
On the other hand, if ruby were comparing:
:helloX v. :helloY
the Symbols are essentially stored as single integers, something like:
341343 v. 134142 => not equal
To compare them takes only a single integer comparison, so it's faster. As someone is sure to point out, that isn't quite how Symbols are implemented, but the details don't matter. It's sufficient to know that Symbol comparisons are faster than String comparisons, and as to why that is true the example above is sufficient to demonstrate there is at least one implementation where it can be true.
Hashes: hashrockets vs literal
In Ruby hashes can use either "hashrockets" or the newer literal notation (since Ruby 1.9):
# hashrockets
{ :foo => "bar" }
# literal
{ foo: "bar" }
They both do the exact same thing. They create a hash with the symbol :foo as a key. The literal syntax is now generally preferered.
Hashrockets should only be used today if you have something other than symbols as keys (you can have numbers, strings or any object as keys):
{ 1 => 'a', 2 => 'b' }
# using literals won't work here since it will cast the keys to symbols:
{ 1: 'a', 2: 'b' } # => { :1 => 'a', :2 => 'b' }
As Ruby is loosly typed hashes can contain any kinds of values:
{ foo: "bar" }
{ foo: [:bar, :baz] }
{ foo: { bar: :baz } }
Hash options
In Ruby methods can recieve both ordinal arguments and a hash with options:
def foo(bar, hash = {})
#test= hash[:]
end
The hash options must come after the positional arguments in the arguments list.
# syntax error
foo(a: 2, "test")
# good
foo("test", a: 2)
When passing a hash options you can forgo the surrounding brackets as they are implied:
foo("test", { a: 1, b: 2 })
# same result but nicer to read
foo("test", a: 1, b: 2 )
Ruby 2.0 introduced keyword arguments which reduces the amount of boilerplate code needed to parse the options:
def foo(bar, test: nil)
#test= test
end
def foo(bar, test: nil, **kwargs)
#test= test
# all the options except test
puts kwargs.inspect
end
# will raise an error if the test key is not passed
def foo(bar, test:)
#test= test
end
Postitional arguments vs hash options
Postitional arguments are shorter and ideal where the order of the parameters is self explainatory:
class Number
def add(x)
#val += x
end
end
When you have more complex methods with a large number of arguments it can be tricky to keep track of the order:
def book_flight(destination, seats, airline_preference= nil, special_meals= nil)
end
thats where hash options come in and shine as you can have an arbirtrary number of options and the program does not blow up because you forgot a nil in the call.
What is the difference between these two?
render plain: params[:article].inspect
render :plain params[:article].inspect
For the second statement, a syntax error occurred. What's wrong with this?
Besides, I experimented
render plain: params[article:].inspect`.
Using params[article:] here will lead to a syntax error. Why do we have to use params[:article] instead of params[article:] here?
In Ruby hash syntax is key => value, where key can be any (almost) object, ex:
'my_key' => 1
:my_key => 2
'my_key' is a String. :my_key is a Symbol. The Symbol syntax is to put the colon before.
And the 'colon after symbol' syntax - symbol: object - is a syntactic sugar for :symbol => object. It means that this line:
:my_key => 2
is exactly the same as:
my_key: 2
You can read about it in my article Ruby for Admins: Hashes
The plain: usage is valid shortcut for symbols when you're declaring a literal hash starting on Ruby 1.9, as in:
some_method(class: MyClass, name: 'register-me')
It's also valid when declaring named parameters which is new to Ruby 2.1. You would do it like this:
def some_method( key: 'some key' )
end
And then call it like this:
some_method # assumes default value
some_method(key: "another key")
For all other cases, symbols are still declared as :some_name and you will have to declare them like this.
The reason your second sentence isn't working is because you are missing a =>:
render :plain => params[:article].inspect
So these two are equivalent:
foo: "bar"
:foo => "bar"
I am consuming an API that expects me to do requests in the following format:
?filter=value1&filter=value2
However, I am using Active Resource and when I specify the :params hash, I can't make the same parameter to appear twice in the URL, which I believe is correct. So I can't do this:
:params => {:consumer_id => self.id, :filter => "value1", :filter => "value2" }, because the second filter index of the hash will be ignored.
I know I can pass an array (which I believe is the correct way of doing it) like this:
:params => {:consumer_id => self.id, :filter => ["value1","value2"] }
Which will produce a URL like:
?filter[]=value1&filter[]=value2
Which to me seems ok, but the API is not accepting it. So my question are:
What is the correct way of passing parameters with multiple values? Is it language specific? Who decides this?
http://guides.rubyonrails.org/action_controller_overview.html#hash-and-array-parameters
Try :filter[] => value, :filter[] => value2
to create a valid query string, you can use
params = {a: 1, b: [1,2]}.to_query
http://apidock.com/rails/Hash/to_query
http://apidock.com/rails/Hash/to_param
You can generate query strings containing repeated parameters by making a hash that allows duplicate keys.
Because this relies on using object IDs as the hash key, you need to use strings instead of symbols, at least for the repeated keys.
(reference: Ruby Hash with duplicate keys?)
params = { consumer_id: 1 } # replace with self.id
params.compare_by_identity
params["filter"] = "value1"
params["filter"] = "value2"
params.to_query #=> "consumer_id=1&filter=value1&filter=value2"
Could anyone please explain what this test code does? :
assert_difference('Post.count') do
post :create, :post => { :title => 'Hi', :body => 'This is my first post.'}
end
and:
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post :invite_friend, :email => 'friend#example.com'
end
I can't understand it even though I read the documentation.
Thanks!
assert_difference verifies that the result of evaluating its first argument (a String which can be passed to eval) changes by a certain amount after calling the block it was passed. The first example above could be "unrolled" to:
before = Post.count # technically, eval("Post.count")
post :create, :post => { :title => 'Hi', :body => 'This is my first post.'}
after = Post.count
assert_equal after, before + 1
This is just checking to make sure that the number of objects for whatever type was specified has increased by 1. (It is an easy way to check to see that an object was added to the DB)
This assertion is to verify the certain/specified difference in the first argument. 1st argument should be a string i.e "Post.count". Second argument has a default value 1, you can specify other numbers also, even negetive numbers. for more details visit:
http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html
This method is to verify that there is a numerical difference between first argument and the second argument. In Most cases first argument is a string which is like “Post.count” and second argument is a block. In rails this is mostly used in functional testing to check if an object can be saved in the database. Logic is that before a new is object being saved, number of records in that particular table must be different from the number of records after the object is saved ( from 1 to be precise).