What do these weird characters mean? - ruby-on-rails

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.

Related

Syntax differences between method calls and variable calls in embedded ruby scripts?

This might be a noob question but here it goes:I'm trying to parse embbeded ruby code and I need some help in understanding the synthax for methodcalls and variable accesing in embedded ruby scripts.
For example in this call #user.followed_users.count (as I understand it) #user is an instance of a the user model followed_users is a (automatically generated) method and count is also a method right?
But what are these calls micropost.content micropost.created_at micropost.user? They are from this erb file:
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data:{ confirm: "You sure?" },
title: micropost.content %>
<% end %>
</li>
(these are code examples from this michael hartl rails tutorial)
What kinds of syntaxes for method calls and variable accessing from erb files are there in Rails? Is it possible to access variables from ruby files that are not instance or class variables?
Thanks in advance for reading and helping :)
The syntax is the same.
There are no major syntactical differences between ERB and Ruby except that ERB is an embedded language where only the code in "erb" tags are executed <% %>.
Whats the difference between a local variable and a method?
A method is a method object that is defined on an object.
def foo
"hello world"
end
# we can call it by
self.foo
# we can access the method object by
self.method(:foo)
This defines the method foo on main.
bar = "value"
Defines the locally scoped variable bar. Not that you can't do self.bar since it just points to a memory register, not a method! You don't call variables - you reference them*. Thus the term "variable call" is just wrong.
As #SergioTulentsev points out you can check if a micropost is a local variable or method by using defined?. Note the caveat when using rails locals below.
See:
What is the difference between local, instance, and class variables?
Using ERB outside of rails
In Ruby the top level object is called main which is the "global" object.
When you assign a instance variable <% #foo = "bar" %> you are assigning it to main which is the implicit self.
<%# raw_template.erb %>
<%= self.inspect %>
If we run erb raw_template.erb it will output main.
Assigning a regular lexical variable (a local) <% foo = "bar" %> works just like in any other ruby code.
As you can see is no difference in how variables work in an ERB template and any other Ruby code.
What does rails do differently?
A core piece of Rails is what is called the view context. This is an instance of ActionView::Base which is the implicit self - the "global" object.
You don't have to take my word for it. Try including this in a view in Rails:
<pre>
<%= self.inspect %>
</pre>
Rails takes all the instance variables of the controller and assigns them to the view context. Which why the #something instance variable you assign in your controller is also available to your views.
Rails locals are not really local variables
The view context also contains a hash called local_assigns. These are the "local variables" you can pass when rendering a template:
render template: 'test', locals: { foo: 'bar' }
When you call <%= foo %> in the test.html.erb template the call goes to method_missing which checks if local_assigns has a :foo key.
Thats why using defined? on local_assigns variables does not work as expected.
But the syntax is the same.
Rails uses a bit of metaprogramming magic to pass variables around - but it does not alter the syntax of the language.
Is it possible to access variables from ruby files that are not
instance or class variables?
Yes, global variables - in Ruby you create a global variable by using the sigil $.
$foo = "bar"
But this is rarely used because globals are evil. And there are better ways to it in Ruby.
micropost in <%= micropost.content %> can be a local variable OR a method. There's no way to tell which is it, just by looking at the line. All that matters is micropost evaluates to something you can call .content on.
If you want to know which is it (for education), you can output its type like this:
<%= defined?(micropost) %>
It'll return either "local-variable" or "method" (or nil, if it's not defined).

How to interpolate ruby inside of an interpolated bit of ruby in ERB

I'm trying to create a situation where one user makes message templates and another one can plug in values. I'm using the best_in_place gem, which will allow a user to edit the message on the show page.
The problem is this. When I call the message, with the required erb to make the gem work, it treats all of this as a regular string, not as ruby.
This is unclear, I'm sorry.
Here's the code.
#announcement.content = "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
/show.html.erb
<%= #announcement.content %>
I want it to put "The click to set train is arriving in five minutes." and if the user clicks where it says "click to set train," a text field will open for them to edit (this is something the best-in-place gem does).
Instead, it puts "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
I understand why it is doing this, but I don't know how to make it instead interpret the ruby I'm trying to pass in.
Ideas?
Use regular old string interpolation:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
You can use ERB to render any ERB template string. In this case something like:
<%= ERB.new(#announcement.content).result %>
Although you likely won't have access to all your Rails helpers, etc.
The Rails way to do this:
#announcement.content_type = :arriving
Later:
<%= render(partial: #announcement.content_type)
In _arriving.erb:
The <%= best_in_place #announcement, :train %> is arriving in five minutes.
TL;DR: ERB is not Ruby, and Rails uses both at different times.
You want simple Ruby string interpolation here:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
This is unclear, I'm sorry.
Not to worry, the Rails framework throws so many different new concepts at you it can be frustrating for newcomers.
Start from this: the Ruby framework builds the answer to the user's browser from a collection of resources Each file is evaluated by an interpreter for its own language. The trick is: look at the extension.
Files ending in .coffee will be compiled into javascript, files ending in .scss will become CSS, and in the same way files ending in .erb will yield HTML.
ERB is a language composed of mostly HTML already, plus a tag that allows you to interpolate Ruby. ERB stands for Embedded Ruby.
What about files ending in .rb, like the file in which you (surely) are evaluating #announcement.content = "The <%= best_in_place[...]" (a controller, I guess)?
Well, that's just pure Ruby :) that's why the ERB interpolation syntax <%= ... > is not recognized.
What you want to do in the controller, is (as you're trying to do) preparing the data for the view. The ruby in the <%= ... > tag in ERB will have access to the controller's instance variables, i.e. the variables with an # in front defined in the controller. But to define those, inside the controller, you should rely on Ruby alone.
Take-home message:
Be aware of which language you are writing in at each moment. For example:
# show.html.erb
<p>Here is ERB, which will be interpreted straight into HTML</p>
<% "Inside the '<% ...' tag is Ruby, but results won't show up in the HTML because there's no '<%='."%>
<% which_language = "Ruby" # Even variable assignments, and comments, do work %>
<%= "Inside the '<%=' tag, you're writing and interpolating #{which_language} :)" %>
I think the fact that I wasn't clear made it hard to answer this question.
What I'm doing is transforming user-inputted text (using a method in the model, called by the controller) to replace certain keywords with erb tags that call the best_in_place plugin. In my view, when presenting this content to another user, I wanted to call this content, which is saved as an attribute in the database, in such a way that it would render correctly for the other user to have the best_in_place functionality active.
Here's what I ended up doing. It is working, but if you have better ideas, please let me know.
In the announcements#create view, the user creates an announcement with certain pre-defined blocks of bracketed text as well as free-input text. For example, they might write "[train] is leaving from [platform] in [time] minutes."
When they hit save, the controller's create action calls the construct_message method from the model. It looks like this:
def construct_message(msg)
msg.gsub! '[train]', '<%= best_in_place #announcement, :train_id, :as => :select, collection: Train::list_trains, place_holder: "Click here to set train." %>' #note: list_trains and list_platforms are methods on the model, not really important...
msg.gsub! '[platform]', '<%= best_in_place #announcement, :platform_id, :as => select, collection: Platform::list_platforms, placeholder: "Click here to set platform." %>'
msg.gsub! '[time]', '<%= best_in_place #announcement, :number_of_minutes, placeholder: "Click here to set." %>'
end
Then, when I want to show that attribute in my view, I'm using render :inline, like this.
on announcements/:id
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<% announcement = #announcement %>
<%= render :inline => announcement.content, locals: { :announcement => announcement } %>
</p>
This allows the erb call that I wrote into the attribute to be functional.
Also note that I'm choosing to use a local rather than instance variable here; this is because in announcements#index, I also render this text and the table there uses local variables.

Confused when using symbols in Rails 4

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| %>

Send an extra parameter through a form in rails 3

Is there a way to send an extra parameter through a form in rails 3?
For example:
<%= form_for #post do |f| %>
<%= f.hidden_field :extraparam, :value => "22" %>
<% end %>
but lets say :extraparam isn't part of the post model..
I have an unknown attribute error in the create method of the controller when I try this, any ideas?
(I want to use the param value itself in the controller for some extra logic)
Call hidden_field_tag directly. See: http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-hidden_field_tag
These helpers exist for all the major form field types, and are handy when you need to go beyond your model's methods.
The following worked for me in passing extra parameters from the view back to the controller that were a part of my model and not part of my model.
<%= hidden_field_tag :extraparam, value %>
Example usage
<%= hidden_field_tag :name, "John Smith" %>
Ya Paul is right. Hidden_field is associated with your model whereas the extra _tag fields are not. I'm not sure of your needs but It's generally recommended in the RoR community to avoid passing a ton of hidden_fields like you might do in a php application.
Ive seen some code where ids were getting passed around in hidden fields which rails takes care on its own if you know the best practices and take full advantage of the framework. Of course I'm just saying this as general info as there are sometimes better ways at accomplishing the same functionality. Good luck on your apps.

Rails: Fields_for without a scope?

Is there a way to use fields_for with in a form without having a scope?
For example:
<% fields_for "user[]" do |x|
<%= x.text_field :name %>
<% end %>
Without the user model being loaded in memory?
I got it working using territory[user][][name], but I would like to keep it in ERB.
I think the answer would be 'no', since those form_for and fields_for would try to determine default value from that given instance variable.
However, I think if you want to lower memory usage from loading that model, you might try to create a mock-up model to return nil values, and create a instance object from that one instead.
is there any specific reason you need to use form_for specifically? Its really designed to be used with an instantiated model object.
Alternatively, why don't you just use the regular form helper tags. You can define it as follows:
<%form_tag :my_form do %>
<%= text_field_tag :foo, :bar %>
<%end%>
You may want to check the documentation for action view to see how it all works.

Resources