Confused when using symbols in Rails 4 - ruby-on-rails

As I transition from Ruby to Ruby on Rails there's one thing that I don't understand when using symbols. For instance I have a form and in the first line there's a form helper with two arguments inside.
<%= form_for :article, url: articles_path do |f| %>
The way I'm interpreting this is that they represent a key and key/value pairs. But why are there two variations of a symbol used (:article, url:) instead of just one? Or is the second argument not a symbol and represents something else within the Rails framework? The semi-colons are throwing me off.

This isn't a Rails thing. You're calling a method with two arguments; the first argument is a symbol and the second argument is a hash. The syntax you see is just a shorthand way to write a hash with symbol keys in newer versions of Ruby. You could also write it like this, perhaps it will make it clearer:
<%= form_for(:article, {:url => articles_path}) do |f| %>

Related

Why does the form_for method take a symbol as its argument?

I am working on the Rails blog tutorial and the syntax goes:
<%= form_for :article do |f| %>
I don't really understand when a helper method or a method takes a symbol as an argument and when it doesn't...
form_for accepts multiple types of arguments.
(a) symbol: form_for :article
form elements can accessed in controller by params[:article]
(b) string: form_for "article"
form elements can be accessed in controller by params[:article]
(c) object: form_for #article
(1) Here #article is expected to contain an instance of Article. In the controller this form elements can be accessed through params[:article].
(2) If a form is expected to be accessed through a custom name, it can be done by using :as operator. form_for #post, as: :my_post
(d) array: form_for [:my, #article]
:my is used to namespace #article. With params[:my_article] form elements can be accessed in controller
Some additional details are needed to understand the answer: This function actually takes a symbol, string or path as the possible argument.
To answer the question of "why": Symbol is used by default because the comparison is faster than a direct string comparison.

Is there a way of just having one pair of <% %> in ruby on rails [duplicate]

Sometimes it's more convenient to print in <%%>. How to do it in Rails?
http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-concat
Should be what you are looking for.
E.g. the following statement using concat:
<% concat "Output" %>
is equivalent to:
<%= "Output" %>
In ERB:
The <% %> signify that there is Ruby code here to be interpreted.
The <%= %> says output the ruby code, ie display/print the result.
So it seems you need to use the extra = sign if you want to output in a standard ERB file.
Otherwise, you could look at alternatives to ERB which require less syntax,.. maybe try something like HAML. http://haml-lang.com/tutorial.html
Example:
# ERB
<strong><%= item.title %></strong>
# HAML
%strong= item.title
Is that more convenient?
erb has two method to evaluate inline ruby expressions. The <% which evaluates the expression and the <%= which evaluates and prints. There is no global object to print to within the binding context.
As mentioned by Omar, there is a concat method, which is part of ActionView. This will do what you want.
Unlike a scripting language escape, there is no default output for erb. Since erb is simply a function, and given a template and binding will return a variable, it returns the values of text and functions recursively.
There is hot debate as to how much logic should be allowed in a view, but as little as possible is what most people aim for. If you are putting more code than text in the view, you may want to consider refactoring your code.

Am I doing symbols right?

I understand that symbols and strings are not same, but I don't understand the concept of symbols.
As I understand, a symbol refers to something else. I use the word refer here for my better understanding, not in the in sense of C/C++ pointers or references.
When I create a new controller in Rails, using something like rails g controller Posts, does Ruby automatically create a new symbol :posts, which refers to the Posts controller? Is this :posts symbol globally accessed from anywhere in the application? Only in that case, I can understand a construct like:
resources :posts
which Ruby can translate for itself: "Ok, I know that :posts refers to posts_controller, so I need to create CRUD routes for that controller". Am I right?
What roles do symbols :title and :content have in this example? I assume that form.label is equivalent to form.label(:title).
<%= form_for #post, :html => {:class => "new_post"} do |form| %>
<%= form.label :title %><br>
<%= form.text_field :title %><br>
<%= form.label :content %><br>
<%= form.text_area :content %><br>
<%= form.submit("Add new post") %>
Are there built-in symbols like :all and symbols that are created on-the-way? When I say on-the-way, as I can see when I create some method like:
def something
...
end
Does Ruby create a new symbol :something, which will refer to method something? If that is correct, why do I need that symbol, where can I can use it?
#posts = Post.all #Or Post.all() => Standard OOP approach, i know what happens here
#posts1 = Post.find(:all) # Dont know what happens here
What about #posts1, what am I doing here? What do :all mean here? Is it some constant value (not string) for Ruby to know that it must pull every Post from DB?
A Symbol in Ruby is just an immutable string with a special syntax. It's used mostly so you can use less memory when doing string matching like operations as in:
options = { :me => 'joe' }
puts options[:me]
This creates a single symbol for :me, if you used strings:
options = { 'me' => 'joe' }
puts options['me']
Two strings would have been created, since strings in Ruby are mutable. So, no, there's no special meaning and it's not something that refer on something other, it's mostly a hack to the fact that strings in Ruby are mutable by default.
As for your last question, :all is just a value that the method implements in something like "a == :all ? return everything : so something else". It's just a method parameter.
Symbol is just an immutable interned string meaning that all symbol variables with the same value point to exactly the same memory location. They also never garbage collected so avoid creating them with to_sym. They are frequently used as keys in hash tables but here's the catch - some hash tables you'll receive from external libraries (e.g. API clients) have strings as keys so when you try to get the value as you're used with with obj[:some_attribute] you'll get nil.
:all is instance of Symbol class, it's built-in Ruby class. All
#posts1 = Post.find(:all)
line is deprecated way to load all posts to #posts1 variable. :all symbol is what indicates that you want to fetch all posts. If you type:
#post = Post.find(5)
you'd get only one post, the one with id = 5.
symbol is the same as the string, but one with the same value have the same memory address. And this is a key difference.
puts 'string'.object_id
puts 'string'.object_id
puts 'string'.object_id
#=> 3099310
#=> 3099310
#=> 3099310
puts :symbol.object_id
puts :symbol.object_id
puts :symbol.object_id
#=> 3098410
#=> 3021341
#=> 3012440
next
Post.find :all
Same as
Post.find 'all'
:posts isn't reference to posts_controller
resources :posts
What does it mean? resources is a method who generate standart CRUD routes. For search controller, it use the naming convention. If resource named as :posts, then routes will be set to PostsController.
I will recommend you to have a look to this blog Ruby Symbol and String.
You will get a lot more apart from symbol and string definition with details example.

What do these weird characters mean?

I'm reading a Ruby book but it doesn't explain the following:
What is this: validates :name, :presence => true
I mean I know what it does but what's validates? Is it a method of the validator class? If so, how come it's called without mentioning the class name first?
What's the meaning of : in the previous code and in Rails on general?
In the following code: <%= form_for([#post, #post.comments.build]) do |f| %>
Is form_for an object or a procedural function?
What's the meaning of the | character in |f|
In <%= link_to 'Edit Post', edit_post_path(#post) %>
Who, where and when was edit_post_path method defined?
To which class it belongs?
validates is a method, part of the validators in Rails. It is declared in (actually, included to) a superclass, that is why it does not have to be declared in the model. The : in front of anything signifies a symbol, not a variable. Symbols are part of Ruby, somewhat similar to strings.
form_for is a method, which takes a number of parameters and a block (that is why there is a do afterwards). The | is part of Ruby syntax, the way you enclose code block parameters.
edit_post_path is defined by the Rails magic and the routes. It is a helper method.
I encourage you to read this book about Ruby to get more familiar with symbols, code blocks, modules and other things that make Ruby a great programming language.

Rails multi-model form with data having a Hash containing a Hash

I'm faced with the following problem in rails. I have a form to edit/create a new project that may contain 1 to n sub-projects that again can contain 1 to n tasks.
So when I create a new project the controller executes:
#project = Project.new
sub_project = SubProject.new
work = Work.new
sub_project.works << work
#project.sub_projects << sub_project
Having the basic structure I generate the input fields in the view, the form I build up like this:
Project
<% form_for (:project, :url => action_parameter, :id => project) do |form| %>
Subproject
<% fields_for "project[sub_projects][]", sub_project do |subproject_form| %>
Up to here all went well but how do I now define the fields_for the tasks? The following attempt..
<% fields_for "project[sub_projects][works][]", work do |work_form| %>
.. is not the solution as I get the following error from Mongrel:
Conflicting types for parameter
containers. Expected an instance of
Hash, but found an instance of Array.
This can be caused by passing Array
and Hash based paramters
qs[]=value&qs[key]=value.
Why doesn't this work? And how should I tackle my problem?
You need to use a nested model form. Check out this web cast from Ryan Bates.
The reason why my code didn't work was that I forgot to add some extra braces in the work line..
<% fields_for "project[sub_projects][][works][]", work do |work_form| %>
However what I attempted to do does not seem possible with pre Rails 2.3.X. So I updated my App to that version and then used the nested forms which John Drummond suggested.

Resources