How to define a method for a model attribute - ruby-on-rails

How do I define a method the attribute of a model.
I have a Picture model that has a title.
Picture.title = "Some title #with a few #hashtags"
I want to make those hashtags links to the tag#show
Picture.title.with_links = "Some title <%= link_to "#with", tag_path(tag) %> a few <%= link_to "#hashtags", tag_path(tag) %>"
Whats the best way to do this. Where do I define the method(with_links)? in Picture.rb? or Pictures_helper.rb?

In short, Rails model does not have access to routes. In very rare cases it is possible to use routes from within model, however in this case it is not the right place to do so.
The right place for with_links is PicturesHelper, so that it would be accessible in view via with_links(picture). The declaration would be:
def with_links(picture)
out= "Some title "
out += picture.tags.collect do |tag|
link_to(tag.name, tag_path(tag))
end.join(", ")
raw out
end

Related

How to define actionmailer subject

Is there a way to define ActionMailer subjects in the email view erb?
Something like:
<%= set_subject("abc") %>
I would like to use class variable values in the subject through string interpolation and was wondering if there was a way to have all the email parameters in one place so I can edit them cleanly.
Add this line at the beginning of the view.
<% message.subject = "abc" %>

Rails Display Link in Database Using link_to

In Rails, I have a "notifications" class, one field of which is "link". The links contained within this class are formatted like: exchange_path(6), where that is the path to the show action in the exchange controller.
I'm now trying to output this link as such:
<%= link_to "View Exchange", notification.link %>
This line is in a loop which begins as such:
<% #notifications.each do |notification| %>
When I click this link, it takes me to localhost:3000/users/exchange_path(6) instead of localhost:3000/exchanges/6 like I would expect. (The loop generating the faulty link is on localhost:3000/users/2)
this could be scary...
<%= link_to "View Exchange", eval(notification.link) %>
should evaluate and use the path helpers. but you need to be 100% sure that nothing bad gets put in the link field..
You could do this:
<%= link_to("View Exchange", "/#{notification.link.gsub('(', '/').gsub(')', '').gsub('_path', 's')}") %>
or set up a method in your model that formats it for you:
def format_link
link.gsub('(', '/').gsub(')', '').gsub('_path', 's')
end
and just call that in your link_to:
link_to("View Exchanges", notification.format_link)
This will only work if all the links are formatted exactly as the example in the question

how to pass a static string into a link_to as an argument in ruby on rails view

I have a modified string. Here is the code where I do the changes:
<% device=#devices.find(1) %>
<% #string ="" %>
<% device.attributes.keys.each do |attribute| %>
<% next if attribute == 'id' || attribute== 'token' || attribute =='carrier' || attribute =='segment' || attribute =='created_at' || attribute =='updated_at' %>
<% x=attribute.to_s %>
<%#string = #string + x +":device."+x +"," %>
<% end %>
<% #string %>
<% #arguments= #string.gsub(/\,$/, '') %>
<%= #arguments %>
It works and it is in the right format to put it in the link_to helper.
This is how I first wrote the link_to helper, and it worked.
<td><%= link_to 'Send notification', controller: "home", action: "send_notification", token: device.token, first_name: device.first_name, last_name: device.last_name %></td>
I tried to change it like this:
<td><%= link_to 'Send notification', controller: "home", action: "send_notification", token: device.token, #arguments %></td>
or #{arguments}
But it doesn't work. I even created another variable without # but it didn't work either.
How can I paste my arguments?
This is my arguments string btw:
"first_name:device.first_name,last_name:device.last_name,nickname:device.nickname"
What should I change?
Another simple newbie question; I feel like I am doing most of the coding in the wrong place. Is it right thatI write so many things in view?
What is the best approach in Ruby on Rails programming?
Thanks in advance
I'll answer your code question first and leave the string question for last.
First, some of your code is in the wrong places. Rails expects you to retrieve the database record in the controller and then pass it into the view. Something like:
devices_controller.rb
class DevicesController < InheritedResources::Base
def send_notification
#device = Device.find(id)
end
....
Then in your show view (app/views/devices/send_notification.html.erb) you can use the #device object and access its attributes like #device.first_name and #device.last_name and print them out or whatever.
Second, the link_to method needs a Hash of arguments, not a string. But either way, there is no use case in Rails that I can think of for passing the entire set of object attributes into the link_to method. It's just generating a link. You probably don't really want it to be littering your html elements with every one one of your record attributes.
All you need to do if you want access to that data when the user clicks the link is to pass the id in as a url element and then have the controller at the other end of the link (in your case Home?) catch the id and create an object out of it there.
I'd suggest taking a good look at: http://guides.rubyonrails.org/

How can do an ActiveRecord call on two values?

I'm writing a rails app, and i have an Article Model. This model has two attributes of issue_id and article_position
In the show view i'm attempting to make a button that goes to the "next" article in a series. the articles go in order from 1 to whatever.
How can i properly do the following call (Psuedo-code):
<%= link_to 'Next', #article_url.where(#article.article_posistion = current_article.article_position + 1, #article.issue_id = current_article.issue_id) %>
I'm pretty stuck as of right now, if i need to be using find, let me know.
thanks!
You can define a method on the Article model that would do what you want:
def next_article
Article.where(issue_id: issue_id, article_position: article_position + 1)
end
Then in your controller you could do:
#next_article = #article.next_article
And in your view:
<%= link_to 'Next', article_path(#next_article) if #next_article %>
This should work how you want, assuming you have article_path defined as a named route.

Getting text to show up if a condition is true

I'm trying to get the text "Tags:" to show up only if tags are present, so I did the following in my view:
<% if #tags.present? %>
<%= puts "Tags:"%>
<% end %>
Which doesn't work... I'm a beginner, and have no idea what I'm doing wrong.
Thanks
EDIT:
A tag belongs to an Article.
Tags is defined in my Article model as:
def tag_tokens
self.tags.collect{|t| t.name}.join(", ")
end
def tag_tokens=(tags_delimited)
tags_delimited.split(",").each do |string|
self.article_tags.build(:tag => Tag.find_or_create_by_name(string.strip.downcase))
end
end
I'm trying to make it so that when an article has tags the word "Tags:" shows up before the list of tags, and when an article doesn't have any tags, the word "Tags:" doesn't show up.
Right now <% if #tags.nil %> just causes "Tags:" to show up on every post.
You don't use puts in views -- puts causes the text to go to your console. This will fix it:
<% if #tags.present? %>
<%= "Tags:"%>
<% end %>
You also don't need to use .present? by the sound of it. If you only want to see if it's been set, you should use .nil? instead. You can also condense this down to a single line.
<%= "Tags:" unless #tags.nil? %>
UPDATE: It looks like the tag_tokens method is broken for you in both the getter and setter. Your setter isn't actually saving anything by the looks of it (.build returns a new object, you need to save it). Your getter is also referencing tags, instead of article_tags which is what you're trying to save by the looks of it. Changing it to this should work for saving:
self.article_tags.build(:tag => Tag.find_or_create_by_name(string.strip.downcase)).save
This is assuming that you have a line that is something like:
has_many :article_tags
has_many :tags, through: :article_tags
Which I'm assuming you do based on your setter.
I assume this is a many-to-many relationship, but it looks like you're using has_many :through, rather than has_and_belongs_to_many. Is there a reason for this? If you're using has_and_belongs_to_many you should be able to do this:
has_and_belongs_to_many :tags
def tag_tokens=(tags_delimited)
self.tags = []
tags_delimited.split(",").each do |string|
self.tags << Tag.find_or_create_by_name(name: string)
end
end
If you do that, you should not have an ArticleTags model at all, and you should have a table called articles_tags with no primary column, and an article_id and tag_id column.
Update 2:
You're not setting #tags to anything, which is why it's always nil. #tags is a variable, which needs to be set to have a value just like #articles is being set in your controller's index method. Regardless, since this is for an index method, you wouldn't want it to be a single instance variable regardless. You should be accessing your tag_tokens method for that particular instance. app/views/articles/index.html.erb lines 53-55 should be changed to this:
<%= "Tags:" if article.tags.any? %>
Check the answer by sgrif, it contains a lot of good points. To just answer your main question:
In erb (the "language" used for view templates in Rails) you can use <%= ... %> to interpolate the result of some Ruby code into your view template.
When you are doing:
<%= puts "Tags:" %>
the following happens:
Ruby evaluates/executes your code: "Tags: " is printed to STDOUT and nil is returned since a call to puts alsways returns nil
erb interpolates the result into your template, the result is nil, which shows up as "nothing"
To fix it, just use:
<% if #tags.present? %>
<%= "Tags:"%>
<% end %>
or, since you are not doing anything in Ruby, you can just use:
<% if #tags.present? %>
Tags:
<% end %>
What has #tags been defined as? Where do you want to check if it is present?
Do you want if #tags.nil?

Resources