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).
Related
Rails 7 / Ruby 3
I'm currently working on a site that requires code examples to be displayed on a page - I can get these to display utilising the extra % character trick, however, for some of the examples I need to have a variable within them that is resolved (e.g. like the users' own API key etc...).
Consider I have #variable = "Resolved Variable"
<%%= link_to #variable, variable_path %>
Outputs on the page explicitly as
<%= link_to #variable, variable_path %>
But I really need the #variable to resolve and show on the page as:
<%= link_to "Resolved Variable", variable_path %>
I've tried all kinds of escaping the variable, but it seems that <%%= ensures that nothing following it can be resolved.
Any ideas?
Any text you haven't html_encodeed will be displayed as plain text.
My suggestion to you is to create a interpolated string that you could use to generate your intended result. For example:
output_text = "<%= link_to '#{#variable}', variable_path %>"
And, not sure if this is what you're looking for, but you can get a good UI by adding some Javascript library to format you code in the language in intend (in this case Ruby, it seems).
In case that's interesting to you, check the Prism lib, or check how to add it to your project here
I hope this helps.
With kind regards,
Rogerio
<%% in ERB will simply output <%, no more, no less. In particular, it won't attempt to parse the code after <%% as Ruby. However, this doesn't mean that you can't have another <%= ... %> after <%%:
require 'erb'
template = <<-EOD
<%%= link_to <%= #variable.inspect %>, variable_path %>
EOD
#variable = "Resolved Variable"
puts ERB.new(template).result
The inspect method will add quotes around your string and also escape certain characters as needed.
Output:
<%= link_to "Resolved Variable", variable_path %>
According to the Rails API,
If you need to find out whether a certain local variable has been assigned a value in a
particular render call, you need to use the following pattern:
<% if local_assigns.has_key? :headline %>
Headline: <%= headline %>
<% end %>
Testing using defined? headline will not work. This is an implementation restriction.
But when I tested it in my Rails projects, the defined? test worked just fine.
Could anyone explain, what exactly the restriction is and when it gets triggered?
Is there any standard or emerging standard to document the parameters that can be passed into a Rails partial ?
When _my_partial.html.erb expects a title and an elements local var passed with render 'my_partial', title: t, elements: e, there must be a common way to document their names, expected types and roles, without reading the whole partial code. Something like RDoc or Tomdoc for methods and classes. Isn't there ?
Edit: I've found a post whose author advocates initializing parameters with <% var ||= 'default_val' %> in the first lines of the partial, which is indeed a safe practice and a kind of in-code doc. Is there really no comment/parameter-declaration solution for this ?
At the beginning of your partial, simply call all the variables that are referenced.
# _my_partial.html.erb
<% title %> <--- first line of file
<% elements[0] %>
<h3><%= title %></h3>
<% elements.each do |element| %>
<p> etc ... </p>
Reasons why this is good for your project:
it does not rely on comments or non-code files
any developer on the project can quickly find out which variables are needed by looking at the top of the file in question
by calling the variables, you ensure that a missing variable will result in an exception.
elements is called with square brackets because we also want it to blow up if it's not an enumerable, right?
The practice of using <% var ||= 'default_val' %> is actually unsafe because it allows bugs to hide. You want your code to immediately blow up the moment something isn't done right. And if these variables should be passed, then you want the code to blow up when they're not there.
I am having difficulty getting my helper to display a list item. The markup looks like the following:
- #bars.each do |bar|
<% display_bar(bar) %>
The actual helper looks like the following:
module MyHelper
def display_bar(bar)
type = bar.type
concat(%li.type)
concat(%b some text)
concat(%i some more text)
end
end
What am I doing wrong here?
Such things has to be implemented via partials. Or see 5.
<% won't show you anyting. You're in Haml. It's ERb stuff (but even there it wouldn't have shown anything: you'd forgotten the = sign, it should have been <%=).
About concat(%li.type): you cant put your markup inside your Ruby code. Ruby knows nothing about your %li "code".
Amokrane Chentir already mentioned.
You're trying to reinvent the wheel. Rails already provides magnificent helper for such simple cases.
Take a look:
= content_tag_for(:li, #bars) do |bar|
%b= bar.title
%i= bar.id
UPD: content_tag_for sets styles/ids for each li tag based on the current model instance that makes it easy to implement styling/scripting in the future.
The name of your helper is display_bar not display_event.
You should use = instead of <% %>
- #bars.each do |bar|
= display_event(bar)
EDIT
Oops didn't read carefully the content of display_bar method, as #jdoe mentioned you can't use Haml markup syntax in your Ruby code.
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.