Im trying to render a dynamic nav menu using a application helpers but all get is a hash
when I load the page this is all that it displays
[#<Project id: 15, title: "downer", created_at: "2012-07-03 08:36:16", updated_at: "2012-07-03 08:36:16", company_id: 2>]
here is the code that is used in the application helper
def project_list(user)
company ||= user.profile.company
projects ||= company.projects
projects.each do |project|
link_to project.title, company_project_path(company, project)
project.scopes.each do |scope|
link_to scope.name, company_project_scope_path(scope.company, scope.project, scope)
end
end
end
and
_nav.erb.html
<%= project_list(current_user) %>
In ruby a method returns the last evaluated expression by default. Also, each returns the array/hash which was iterated upon. So effectively your project_list is returning projects back to the view. You should change your method to return the html you want to insert:
def project_list(user)
html = ''
company ||= user.profile.company
projects ||= company.projects
projects.each do |project|
html += link_to project.title, company_project_path(company, project)
project.scopes.each do |scope|
html+= link_to(scope.name, company_project_scope_path(scope.company, scope.project, scope))
end
end
return html.html_safe
end
Your enumerable #each is returning the last object in the collection which becomes the return value of the project list method.
You need to build up a list of tags then return that object.
If you are using 1.9.2 you could use each_with_object either with a string as on object or an array that you could join before returning it.
Related
I am not able to show data in the view. I don't know why?
When I do this in view
<%= #p.inspect %>
It shows me this in view
#<ActiveRecord::Relation [#<Post id: 1, Title: "My First Post", Text: "The world with out worries", CommentsCounter: 1, LikesCounter: 1, created_at: "2022-09-17 21:41:08.544136000 +0000", updated_at: "2022-09-17 21:41:08.544136000 +0000", author_id: 1>]>
This is the controller function
def detail
#p = Post.where(id: params[:id]).where(author_id: params[:idp])
end
But when I try to show only the title like
#p.Title
it shows this error
In ruby you can set queries to iterate through collections such as you code does. that means that it is always returning an array. Even if there is just one object inside this array, you need to select the object manually:
#p = Post.where(id: params[:id]).where(author_id: params[:idp]).first
notice that first?
However, an id is always unique, so your query does not make sense. Instead you should use
#p = Post.find(params[:id])
since you are already having the param for the id from the route itself. The where statement for the author does not work in this case. What you can do to access the author, assuming that your models are set up correctly is:
#p = Post.find(params[:id])
#p.author.name # "name" is just an example here.
If you need to return a collection use the code you wrote and iterate through it:
#p = Post.where(id: params[:id]).where(author_id: params[:idp])
<% #p.each do |posts| %>
<p><%= posts.title %></p>
<% end %>
but again, the where(id: params[:id]) does not make sense in this context, since where searches for all records of that model that have the given attributes, but there will always be just one.
I have this code in User#index view:
<%= User.find_each do |user| %>
<%= user.name %>
<% end %>
which returns the user names:
user1 user2 user3 ...
Then I move this code to the UserHelper:
module UserHelper
def get_users
User.find_each do |user|
user.name
end
end
end
And call it from the User#index view with:
<%= get_users %>
The problem is that this is not returning any user. If I use <%= get_users.class %> it outputs NilClass. Why is the helper method not returning the user names?
Your helper method implicitly returns the result of calling find_each, which is different than returning a collection of user names.
Think of it like running 5.times { |n| puts n }: what's the value of that? Not "0 1 2 3 4", but "5", because times returns what it was called on, not what's run in its block.
Your original code, by the way, returns the exact same thing--you are relying on a side effect inside the find_each block, i.e., appending user.name to the response.
If you want to return a collection of the users' names you'd want to map/etc. and grab each user's name. Or, IIRC, you can do a find and a pluck so you only get back the users' names instead of all user fields.
You can use as below also,
<%= User.all.map(&:name).join(' ') %>
I'm a newbie trying to implement the datagrid gem (https://github.com/bogdan/datagrid). Unfortunately, I'm getting three error messages:
Error 1: undefined method 'filter' for #<UsersGrid:0x000000044e9400>
Referring to: line `#grid.filter` in def index
Error 2: undefined local variable or method 'user' for #<UsersController:0x007f821f434808>
Referring to: line `link_to view_context.image_tag("delete.gif",...` in the controller.
Error 3: undefined method `image_tag' for UsersGrid:Class
Referring to: line `link_to image_tag(user.avatar.url,` in users_grid.rb
Placing `view_context` in front of link_to in users_grid.rb doesn't work either: undefined local variable or method 'view_context' for UsersGrid:Class.
Also tried `ActionController::Base.helpers.image_tag`. Although that seems to solve the image_tag issue, I then get the error message: `undefined method 'user_path' for #<ActionView::Base:0x007f821d3115b8>` referring to that same line.
Removing all the lines above, and the form works :-)
Any suggestions how to change the code below to adjust for these errors?
My code: After installing the gem I have created /app/grids/users_grid.rb:
class UsersGrid
include Datagrid
scope do
User.order("users.created_at desc")
end
filter(:id, :integer)
filter(:email, :string) { |value| where('email like ?', "%#{value}%") }
filter(:organization, :string) { |value| where('organization like ?', "%#{value}%") }
filter(:verified, :xboolean)
filter(:created_at, :date, :range => true, :default => proc { [1.month.ago.to_date, Date.today]})
column(:id)
column(:avatar) do |user|
if user.avatar?
link_to image_tag(user.avatar.url, alt: "Profile"), user_path(user) #Error3
else
link_to image_tag("profile.gif", alt: "Profile"), user_path(user)
end
end
column(:organization)
column(:verified) do |user|
image_tag("verifiedaccount.gif", title: "verified") if user.verified
end
column(:created_at) do |model|
model.created_at.to_date
end
end
The users controller:
def index
#grid = UsersGrid.new(params[:users_grid]) do |scope|
scope.where(admin: false).page(params[:page]).per_page(30)
end
#grid.assets
if (current_user && current_user.admin?) # This is a Sessions helper method.
#grid.filter(:activated, :xboolean, :default => true) #Error1
#grid.column(:myadmin, :header => "My admin?") do |user|
view_context.image_tag("adminamina.png", title: "admin") if user.myadmin
end
#grid.column(:activated) do |user|
user.activated_at.strftime('%v') if user.activated
end
#grid.column(:remove) do |user|
link_to view_context.image_tag("delete.gif", title: "remove"), user, method: :delete,
data: { confirm: "Please confirm" } #Error2
end
end
end
The view:
<%= datagrid_form_for #grid, :method => :get, :url => users_path %>
<%= will_paginate(#grid.assets) %>
<%= datagrid_table(#grid) %>
<%= will_paginate(#grid.assets) %>
This DataGrid seems to be set up more or less like a Model class, and I think that's an important hint that they're serious about object orientation and encapsulation: the innards of the class won't automatically have access to the outside world, you need to manually specify the info you need.
One technique called dependency injection might be useful here, and fortunately DataGrid appears to have decent support for it. Quoted from this wiki page:
To pass an object to the Grid instance, simply merge it to the params hash on initialization:
grid = TheGrid.new(params[:the_grid].merge(current_user: current_user))
You can then access it in the Grid object by declaring the corresponding getter
def TheGrid
...
attr_accessor :current_user
...
end
So, when you new up the UserGrid object, specify current_user: current_user as part of the hash, then the grid should have access to it.
Then you'll have one more problem: the UserGrid object will have access to current_user, but the UserGrid class won't, and you've written your conditional in at the class level. Again the wiki has an answer for this, it's called dynamic columns and you can click that link to see a couple examples.
A brief dynamic columns example: in the controller (or wherever you create your UserGrid), try something like the following (again, more examples at the above link):
user_grid = UserGrid.new(same_params_as_before)
# The Grid object has been created, but we can still add columns to it after-the-fact
if current_user && current_user.admin?
# This block will only execute if the above condition is met. We can
# define as many extra columns as we feel like here (or change the
# user_grid object in any other way we want)
user_grid.column(:myadmin, header: "My admin?") do |user|
image_tag("adminamina.png", title: "admin") if user.myadmin
end
...
end
Some notes about this example:
The UserGrid object doesn't even need to know about current_user. It doesn't care that you're an admin; the controller simply decides whether you're an admin, and if so, makes some changes to the UserGrid object.
Consequently, you can get rid of the current_user method definition inside the UserGrid class, plus all code that references it.
This is an example of bad object-oriented programming because the controller is tasked with managing the internal state of another object (ie. manually defining more columns on the UsersGrid in certain conditions). I like this approach because it's direct and easier to understand, but be aware that there are better ways to organize this code so that all of the UserGrid "column definition stuff" is kept in the same place, not scattered around different files.
I am trying to create helper method that displays the navigation. How ever my helper displays an array even though i am using content_tag. Was wondering if i was doing something wrong.
module SubscriberNavigation
def navigation
get_menu_items.find_each.map do |menu|
content_tag(:li, link_to("#{ menu.title.try(:capitalize) }", "#{ menu.url.downcase }"))
end
end
def get_menu_items
#get_menu_items ||= Subscriber::Menu.all
end
end
And when i display
<%= navigation %>
An array of records in being displayed. Instead of content_tag list items.
["<li>Contacts</li>", "<li>Terms and conditions</li>", "<li>About us</li>"]
I tried .html_safe etc but looks like i'm missing something.
It is returning an Array. You can try this:
<%= navigation.join(' ').html_safe %>
I am reading the book Agile web developpment with rails 4.
there is a part where the products' cart is showing only if it is not empty, my question is the function in the view send to the helper only 2 attributes while in the implementation there are 3 parameters.
in the view I have the bellow code, which render to _cart where I have the cart show
<%= hidden_div_if(#cart.line_items.empty?, id: 'cart') do %>
<%= render #cart %>
<% end %>
the helper has:
module ApplicationHelper
def hidden_div_if(condition, attributes = {}, &block)
if condition
attributes["style"] = "display: none"
end
content_tag("div", attributes, &block) end
end
My question is the &block in this case receives id: 'cart' but is it a optional attibute? that why it comes with &. but what about attributes = {}?
I am really not sure how that is happening, could someone explain me a bit?
Thanks!!
The code between and including do and end is the block, and this is the third argument for hidden_div_if, which is simply passed on to content_tag. The & in the definition of hidden_div_if captures the block in your view, whereas the & in the call to content_tag expands it again to pass it along.
The answer here explains this idea nicely with a few examples. I recommend testing everything out yourself in irb to get a feel for it.