acts_as_state_machine helper method rails 6 - ruby-on-rails

i have a verified and unverified states in my booking model, how do i implement helper methods for my views? i would like something like this in my index views.
<% #bookings.each do |booking| %>
<%= link_to booking_path(booking) do %>
<%= booking.name %>
<% if verified_booking %>/* here is where i want implemented*/
<div class="pt-4 font-semibold"><i class="fa fa-user-check"></i></div>
<% end %
<% end %>
</div>
<% end %>
helper method
def verified_booking
!!Booking.verified
end
booking model
include AASM
aasm :column => :state, :whiny_transitions => false do
state :unverified, initial: true
state :verified
event :verify do
transitions from: [:unverified], to: :verified
end
end

EDIT
Removed the sample AASM definition since you added yours to your question.
AASM will define public instance methods for each state you define, which you can use to check your state. So, in your case there will be .verified? and .unverified? methods on your instances. You can use these methods directly in the view, so you don't really need a helper method:
<% #bookings.each do |booking| %>
<%= link_to booking_path(booking) do %>
<%= booking.name %>
<% if booking.verified? %>
<div class="pt-4 font-semibold"><i class="fa fa-user-check"></i></div>
<% end %>
<% end %>
</div>
<% end %>
However, if you want to understand why the helper method in your question doesn't work, the helper method you give in your question won't work for two reasons. You call Booking.verified but Booking is the class and the AASM methods are instance methods (that is, they only work when invoked on an instance of Booking. You need to pass the individual booking instance to it from the view as a parameter (as #gordon has in their answer). The second issue is the method is .verified? (the question mark is part of the method name). So:
def verified_booking(booking)
booking.verified?
end

ApplicationHelper.rb
def verified_booking(state)
# check state in the database if it is verified
# return true if state is verified
# return false if not verified
end
html.erb
<% #bookings.each do |booking| %>
<%= link_to booking_path(booking) do %>
<%= booking.name %>
<% if verified_booking(booking.state) %>/* here is where i want implemented*/
<div class="pt-4 font-semibold"><i class="fa fa-user-check"></i></div>
<% end %
<% end %>
</div>
<% end %>

Related

Acts_As_Votable Gem Automatically Liking Everything

I'm using the acts_as_votable gem to like and unlike "Deals" in my Ruby on Rails project. My user is set to act_as_voter and my deal is set to acts_as_votable, but for some reason everything is set to like as soon as a new user is created, and they can't unlike the deal. For some reason my list of deals all have an unlike button and it doesn't actually do anything but refresh the page. Here's some of my code.
app/views/catalog/index.html.erb
<ul class="deals_list">
<% #deals.each do |deal| %>
<li>
<div>
...
<div class="favorite">
<% if account_signed_in? and current_account.accountable_type == "Personnel" %>
<%= image_tag("dark-favorite.png") %>
<% if deal.liked_by current_account %>
<%= link_to unlike_deal_path(deal), method: :put do %>
Unlike
<% end %>
<% else %>
<%= link_to like_deal_path(deal), method: :put do %>
Like
<% end %>
<% end %>
<% end %>
</div>
</li>
<% end %>
</ul>
app/controllers/deals_controller.rb
def like
#deal = Deal.find(params[:id])
#deal.liked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
def unlike
#deal = Deal.find(params[:id])
#deal.unliked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
config/routes.rb
resources :deals do
member do
put 'like', to: "deals#like"
put 'unlike', to: "deals#unlike"
end
end
Be sure and read the entire Readme because you're using the library wrong.
To check if a voter has voted on a model, you can use voted_for?. You can check how the voter voted by using voted_as_when_voted_for.
I zeroed in on your problem because I was expecting to see a "?" after the deal.liked_by call, which would indicate a boolean result (by convention, not always the case).
So use this instead:
<% if current_account.voted_for? deal %>

Helper method within each do loop not working

I have a loop that looks like this
<% #user.collections.each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= collection.stories.count %>
<% end %>
It works perfectly to show the Collections that belongs to a User, and then show how many Stories are in each Collection.
However, I want to use a helper that does this.
in the view
<% #user.collections.each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= number_of_stories_in_collection %>
<% end %>
in the helper
module CollectionsHelper
def number_of_stories_in_collection
collection.stories.count
end
def render_stories_count
if number_of_stories_in_collection.zero?
'No stories in this collection yet'
else
"#{number_of_stories_in_collection} #{'story'.pluralize(number_of_stories_in_collection)}"
end
end
end
I get an error that says
undefined method `stories' for #<Collection::ActiveRecord_Relation:0x007f510f504af8>
Any help is appreciated, thanks!
The 'collection' variable isn't an instance variable, so the helper can't see it.
Change your view to this:
<% #user.collections.each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= number_of_stories_in(collection) %>
<% end %>
And your helper method to:
def number_of_stories_in(collection)
collection.stories.count
end
This way you are passing the variable to the helper correctly.
extending #Richard's answer and little bit of optimisation to avoid n+1 queries..
<% #user.collections.includes(:stories).each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= render_stories_count(collection) %>
<% end %>
helper:
module CollectionsHelper
def number_of_stories_in(collection)
collection.stories.length
end
def render_stories_count(collection)
if (count = number_of_stories_in(collection)).zero?
'No stories in this collection yet'
else
"#{count} #{'story'.pluralize(count)}"
end
end
end

Scope of each on rails template

I'm new to rails and I'm trying to build a view that will list the parents and related children
Ex:
Passport has many Visas
I want to list information about the passport and the visas that the passport has.
So I have
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<% end %>
<% end %>
I'm getting the error
undefined method `visa_type' for #Passport:0x000000091b8b28
It looks like rails is trying to find the property visa_type for passport, instead of in visa. How does the scope work within each? Can I force it to access visa_type from visa?
I think you're looking for the fields_for form helper. This will allow you to create fields for the relevant visa attributes. Replace your code sample with the following, and you should be all set.
<% #passport_list.each do |passport| %>
# passportFields
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
You can also iterate over the list as follows:
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
<% end %>
For more information on fields_for, check out the link I added above, and to customize further for your use case, check out the "One-to-many" section.
IMO you should always handle the null case of an object.
Something like this if you use rails (present? is a Rails function)...
<% if #passport_list.present? %>
<% #passport_list.each do |passport| %>
passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<%end%>
<%end%>
<% else %>
<p>Nothing to see here</p>
<% end %>
However if your #passport_list is backed by an ActiveRecord Query, you can handle this in the model/helper/controller by returning the .none query on the model. Note that this differs from an empty array because it is an ActiveRecord Scope, so you can chain AR queries onto it
# scope on AR model
def self.awesomeville
where(country_of_origin: "awesomeville")
end
# method queried in controller
#passport_list = Passport.all
if #passport_list.present?
#passport_list
else
Passport.none
end
# additional filtering in view is now possible without fear of NoMethodError
#passport_list.awesomeville
Whereas a ruby Array would raise an error as it would respond to the Array methods.

Am I supposed to do this in helper? or does this make slower?

In this case which pattern will be faster?
Obviously Pattern1 with helper looks much more sophisticated and looks clean.
But it send SQL every time when user_link method is called.
Here it calls up to 100times at one page loading.
Which way would be better for benchmark performance?
Pattern1. With helper
application_helper
def user_link(username)
link_to User.find_by_username(username).user_profile.nickname, show_user_path(username)
end
view
<% #topics.order("updated_at DESC").limit(100).each do |topic| %>
<%= user_link(topic.comment_threads.order("id").last.user.username) if topic.comment_threads.present? %>
<% end %>
Pattern2. Without helper. Just only view
<% #topics.order("updated_at DESC").limit(100).each do |topic| %>
<%= link_to(topic.comment_threads.order("id").last.user.nickname, show_user_path(topic.comment_threads.order("id").last.user.username) ) if topic.comment_threads.present? %>
<% end %>
try
# Topics model
#via scope
scope :get_topic_list, lambda do
order("updated_at DESC").joins(:comment_threads => :user).limit(100)
end
#via method
def self.get_topic_list
Topic.order("updated_at DESC").joins(:comment_threads => :user).limit(100)
end
# in your controller or move to model itself (recommened)
#topics = Topic.get_topic_list
# in you view
<% #topics.each do |topic| %>
<%= link_to(topic.comment_threads.order("id").last.user.nickname, show_user_path(topic.comment_threads.order("id").last.user.username) ) if topic.comment_threads.present? %>
<% end %>

rails 3: How can I concatenate a number to a symbol?

I would like the #comment1 to change to #comment2 by using the i in the 1..5 loop. I have the following code that is pretty repetitive. I am hoping to dry it up.
Hi,I am using acts_as_commentable_with_threading. I am basically looping through all comments and checking to see if that comment has children. If so, print out the children while checking to see if those children have children. So I plan on going a few levels deep, hence the #comment1,2,3, etc...How can I DRY this? Recursion some how? If not, I could maybe go a few levels deep and end the comment indentation at #comment5 for example.
EDIT!
Thank you Samiron!
Here is the updated helper function...
def show_comments_with_children(comments)
comments.each do |comment|
yield comment
if comment.children.any?
concat <<-EOF.html_safe
<div class="span7 offset1-1 pcomment">
EOF
show_comments_with_children(comment.children) { |x| yield x } #Dont worry, this will not run another query :)
concat <<-EOF.html_safe
</div>
EOF
end
end
end
<div class="span7 offset1-1 pcomment">
<% #comment1 = comment.children%>
<% for comment in #comment1 %>
<%= render "comment_replies", :comment => comment %>
<div class="span7 offset1-1 pcomment">
<% #comment2 = comment.children%>
<% for comment in #comment2 %>
<%= render "comment_replies", :comment => comment %>
<div class="span7 offset1-1 pcomment">
<% #comment3 = comment.children%>
<% for comment in #comment3 %>
<%= render "comment_replies", :comment => comment %>
<% end %>
</div>
....
<%(1..5).each do |i| %>
<% #comment1 = comment.children%>
<% for comment in #comment1 %>
<%= render "comment_replies", :comment => comment %>
<% end %>
<% end %>
Probably you are looking for instance_variable_set.
# Following snippet is not TESTED. It is here to just demonstrate "instance_variable_set"
<%(1..5).each do |i| %>
<% instance_variable_set("#comment#{i}", comment.children) %>
<% for comment in instance_variable_get("#comment#{i}") %>
<%= render "comment_replies", :comment => comment %>
<% end %>
<% end %>
But definitely this is not a recommendable approach. You can share your controller code and what you want to achieve in your view. There must be some way to make it properly DRY. In your post you are always getting comment.children. is it really?
Actual Solution:
Your view code will be like this
#0th level is the top level
<% show_comments_with_children(#comments, 0) do |comment, level|%>
<!-- #Use level to differ the design for different depth-->
<%= render "comment_replies", :comment => comment %>
<%end%>
and add this helper function show_comments_with_children in your helper function. Which will be.
def show_comments_with_children(comments, level)
comments.each do |comment|
yield comment, level
if comment.children.any?
show_comments_with_children(comment.children, level+1) {|x, l| yield x, l} #Dont worry, this will not run another query :)
end
end
end
The manor in which you are defining this code is rather poor, and you should consider defining #comment as an array rather than as independent variables for each #comment1, #comment2, etc..
That said, what you are looking for is the instance_variable_get() method
<(1..5).each do |i| %>
<% instance_variable_set("#comment#{i}", comment.children) %>
<% for comment in instance_variable_get("#comment#{i}") %>
<%= render "comment_replies", :comment => comment %>
<% end %>
<% end %>
This is definitely something good to know but in this case I highly recommend converting you comment instance variables to an array!

Resources