Rails: Inspect an array in template engine - ruby-on-rails

I am maintaining a 5.2 rails app and I came across the two lines below in the template:
<%
clazzes = Clazz.joins(:staffs).includes(clazz_students: :student).where(staffs: { id: #staff.id })
all_students = clazzes.flat_map { |clazz| clazz.clazz_students.map(&:student) }
%>
I have like to somehow inspect all_students to see what is in the array. Something like console log. Any suggestions on how I can do that?

You could use the debug view helper. Note that you have to change to output mode by changing <% to <%=.
<%=
clazzes = Clazz.joins(:staffs).includes(clazz_students: :student).where(staffs: { id: #staff.id })
all_students = clazzes.flat_map { |clazz| clazz.clazz_students.map(&:student) }
debug(all_students)
%>
Sidenote: Loading and transforming data is usually done in the controller or even in the model when possible. And I would argue that those lines in a view are a code smell and make the view much harder to maintain and potentially to reuse. I suggest moving those lines into the controller method and just calling the instance variables in the view like this:
# in the controller method:
#clazzes = Clazz.joins(:staffs).includes(clazz_students: :student).where(staffs: { id: #staff.id })
#all_students = clazzes.flat_map { |clazz| clazz.clazz_students.map(&:student) }
# in the view
<%= debug(#all_students) %>

Related

Adding a drop down in rails and do particular action

I am new to rails and this might me be a basic question. I checked on the internet but not able to find a simple example(might be my search is bad).
I need to create a form in my view and based the value selected and button clicking action i need to execute a particular action in my controller. I am able to create a drop down in my view using following lines.
= form_tag("/rtes", method: "get") do
= label_tag(:q, "Get Trip Type:")
= select_tag(:q, options_for_select({ "a" => "r4d_001", "b" => "r4d_002" })
<br>
= button_to( "Get Trip", :action => "rtes", :controller =>:q)
where rtes is my controller and i have mapped the value of the drop down values to the corresponding action names that needs to be executed when the button is clicked. This is my controller.
class RtesController < ApplicationController
##client=OptimusRtesServiceModel.new
def index
end
def r4d_001
result = ##client.r4t_001()
#driver_username = result.trip.loads[0].driver_details[0].driver_user_name
#driver_password = result.trip.loads[0].driver_details[0].driver_password
#trip_id = result.trip.trip_id
#carrier_username = result.carrier_details.carrier_user_name
#carrier_password = result.carrier_details.carrier_password
end
def r4d_002
result = ##client.r4t_002()
#driver_username = result.trip.loads[0].driver_details[0].driver_user_name
#driver_password = result.trip.loads[0].driver_details[0].driver_password
#trip_id = result.trip.trip_id
#carrier_username = result.carrier_details.carrier_user_name
#carrier_password = result.carrier_details.carrier_password
end
end
Now, if the first option in the drop down is selected and the button is clicked the "r4d_001" action in the controller needs to be executed. Your help is highly appreciated.
Rails is not like angular that you are trying to use :q variable to pass the value of selection to the button. You can create a drop-down menu instead of using select field to send them to different action for different option. Still if you want to use select field, then you need javascript to handle the front-end task here.
I have slightly modified my code to made it work. The idea is to get use the rails params and execute the corresponding action in my controller using case/when.
My view code:
= form_tag({:controller=>"r4d", :action=>"result"}, method: :get) do
= label_tag(:q, "Trip Type: ")
= select_tag(:q, options_for_select({"a" => "1", "b" => "2"})
= submit_tag("GetDetails")
My Contoller code:
class R4dController < ApplicationController
##client=ServiceModel.new
def r4d_result
result = case params[:q]
when "1"
##client.1
when "2"
##client.2
end
#value = result.value
end
end
By this way, i am able to pass the selected value of the drop down and execute the corresponding thing.

Rails display current controller errors

The problem is really simple. I want to display error notifications outside simple_form_for tag for every controller in my app.
I use the following code:
- if #user.errors.present?
.alert-box.alert{ data: { alert: true } }
%div= t('simple_form.error_notification.default_message')
Which is OK, but only for one controller with #user variable. I want to know if there is some clever way to get this class instance variable (#user) without hardcoding the name. In each controller its different but it corresponds with current controller name #user for UsersController etc.
Thanks for the help. Unfortunately I can accept only one answer :)
Possible solution
I end up with helper method:
def errors_present?
# returns string like "UsersController" with support for name-spaced controllers
controller_name = controller.class.to_s.split('::').last
# extract out the "user" portion
prefix_name = controller_name.gsub(/controller/i, '').singularize.downcase
# is there a defined instance variable with this name?
i_var = controller.instance_variable_get(:"##{prefix_name}")
return i_var.errors.present? if i_var != nil
false
end
and view code:
- if errors_present?
.alert-box.alert{ data: { alert: true } }
%div= t('simple_form.error_notification.default_message')
If all of your controllers follow the same naming convention, then the instance variable name is the singular name of the controller. You can replace #user with the following:
instance_variable_get("##{controller.controller_name.singularize}")
If you move that into a helper method in application_helper.rb:
def controller_record
instance_variable_get("##{controller.controller_name.singularize}")
end
You can then reference controller_record in your view:
- if controller_record.errors.present?
.alert-box.alert{ data: { alert: true } }
%div= t('simple_form.error_notification.default_message')
If you're going to do this in a dynamic route than you'll need to ensure that the instance variable you use matches what can be inferred from the controller name. You can then use this in a helper method.
def controller_error_present
# returns string like "UsersController" with support for name-spaced controllers
controller_name = controller.class.to_s.split('::').last
# extract out the "user" portion
prefix_name = controller_name.gsub(/controller/i, '').singularize.downcase
# is there a defined instance variable with this name?
controller.instance_variable_get(:"##{prefix_name}") != nil
end
To be used like:
- if controller_error_present
.alert-box.alert{ data: { alert: true } }
%div= t('simple_form.error_notification.default_message')
As long as you initialize a #user variable in your controller than it will be picked up by the instance variable check.

Ruby simple map object to has not working

I have a simple problem but I cannot find a solution.
I have a Forum model (active record) with several fields.
I'm creating a class method that return to me an has with one value as key (not the id) and the other as value.
This is my method:
Forum.all.map { |f| [f.old_id => f.icon.url(:micro) ]}
It returns
[[{10=>"/images/fallback/icon_fallback.png"}],
[{6=>"/images/fallback/icon_fallback.png"}],
[{18=>"/images/fallback/icon_fallback.png"}]]
instead of
{10=>"/images/fallback/icon_fallback.png", 6=>"/images/fallback/icon_fallback.png", 18=>"/images/fallback/icon_fallback.png"}
What is the error?
In your code, map returns an array and the square brackets produce arrays containing hashes.
res = {}
Forum.all{|f| res[f.old_id] = f.icon.url(:micro) }
in short you can just modify like this, change square brackets to curly brackets:
Forum.all.inject({}) { |r,f| r.merge!(f.old_id => f.icon.url(:micro)) }
You can make a minimal change to your code and receive your needed result by using to_h:
Forum.all.map { |f| [f.old_id, f.icon.url(:micro)] }.to_h
Yes, you can use reduce or inject method, or just construct the Hash from Arrays:
Hash[Forum.all.map { |f| [f.old_id, f.icon.url(:micro) ]}]
for ruby-2.0 you can use #to_h method:
Forum.all.map { |f| [f.old_id, f.icon.url(:micro) ]}.to_h
use active supports each_with_object:
Forum.all.each_with_object({}) { |f, h| h[f.old_id] = f.icon.url(:micro) }

Returning multilpe values from model method in view

I have the following method in my model:
def self.set_bad_recommedation_size(rating_set)
bad = Rating.where(rating_set: rating_set).where(label: 'Bad').count
total = Rating.where(rating_set: rating_set).count
percentage_bad = (bad.to_f/total.to_f * 100)
return bad, total, percentage_bad
end
How do I call the variable bad, total, percentage_bad in my view.
What I want:
<%= "#{Model.set_bad_recommedation_size(rating_set).bad}/#{Model.set_bad_recommedation_size(rating_set).total"%>
You're better off doing:
<% bad, total, percentage_bad = Model.set_bad_recommedation_size(rating_set) %>
<%= "#{bad}/#{total}" %>
That way you're not calling the method multiple times.
I would add an intermediate helper so that your view reads better
<%= bad_recommendation_ratio(result_set) %>
Application.helper
def bad_recommendation_ratio(result_set)
bad, total = Model.set_bad_recommedation_size(rating_set)
"#{bad}/#{total}"
end

Params contain an array that wants to be a hash

I have an array (coming from a file_field, :multiple => true) in my params that I want to turn into a hash so I can build associated models for each element and process in my create action.
Currently receiving:
{"gallery"=>{"name"=>"A Gallery", "photos_attributes"=>{"0"=>{"image"=>[#<1st Image data removed for brevity>, #<2nd Image data removed for brevity>]}}}, "commit"=>"Save"}
I'd like to turn it into something like:
{"gallery"=>{"name"=>"A Gallery", "photos_attributes"=>{"0"=>{"image"=>#<1st Image data removed for brevity>}, "1"=>{"image"=>#<1st Image data removed for brevity>}}}, "commit"=>"Save"}
considered something like this but it's clearly wrong:
i = 0
params[:gallery][:photos_attributes]["0"][:image].reduce({}) do |result, element|
result[i++.to_s] = element
end
What's the "Rail's Way"?
You need to return the result hash at the end of each iteration.
i = 0
params[:gallery][:photos_attributes]["0"][:image].reduce({}) do |result, element|
result[(i += 1).to_s] = element
result
end
I've done something similar when receiving data from an iOS device. But, if I understand what you want and what your model(s) look like, to get nested attributes to work you don't want it to look like:
{ "photos_attributes" => { "0" => <image1>, "1" => <image2>, ... }
You want it to look like:
{ "photos_attributes" => [ <image1>, <image2>, ... ] }
And to do that all you need to do is:
params["gallery"]["photos_attributes"] = params["gallery"]["photos_attributes"]["0"]["image"]
Now, if I've misunderstood what you need, to get what you've asked for what you have might work (I don't use much reduce aka inject) or you could use tap:
i = 0
params["gallery"]["photos_attributes"] = {}.tap do |hash|
params["gallery"]["photos_attributes"]["0"]["image"].each do |image|
hash[i.to_s] = image
i = i + 1
end
end
Not a whole lot better IMO.

Resources