I wan to do a generic grid, that could be used in my app to show data from different Models. As I am a newbie in RoR - please let me know if my "vision" is ok, or i should change the way i think about this problem:
Controller:
class Admin::UsersController < ApplicationController
before_action :authenticate_user!
authorize_resource
def index
#current_scope = params[:role]
#users = User.select(:id, :email, :role, :last_sign_in_at).role(#current_scope)
end
end
User Model:
scope :role, -> (role) {where('role = ?', role) if role.present?}
Index view:
<%= render '/layouts/shared/grid', object: #users%>
Grid partial:
<% object.column_names.each do |column_name| %>
<th><%= column_name %></th>
<% end %>
My problem is that i can show always ALL columns (that is what ActiveRecord::Relation returns), not only columns for parameters selected in my query. I see few potential solutions - which one would be the best?
usage of ActiveRecord::Base.connection.exec_query - if i do so, i could use then .columns method that returns what i need. But i am not sure if i like it...
usage of search_params instead of query result to iterate
Any other suggestion?
You can use values method. It's not very documented but it returns parsed sql query.
For this query:
User.select(:id, :email, :role,:last_sign_in_at).role(#current_scope).values
It returns something like this:
{:select=>[:id, :email, :role, :last_sign_in_at], :where=>#<ActiveRecord::Relation::WhereClause:0x007fe0d3b29bf0 #predicates=["role = admin"], #binds=[]>}
If a query will not use select when values will not return select result too. In this case, you need to use column_names.
Too I can recommend you wrap table code to decorator. This is raw example:
class TableDecorator
def initialize(scope)
#scope = scope
end
def header_columns
scope.values[:select] || #scope.column_names
end
def columns
#scope
end
end
# controller
#table = TableDecorator.new(#users)
# index view
<%= render '/layouts/shared/grid', locals: {table: #table} %>
# grid partial
<table>
<thead>
<tr>
<% table.header_columns.each do |column_name| %>
<th><%= column_name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% table.columns.each do |item| %>
<tr>
<% table.header_columns.each do |column_name| %>
<td><%= item[column_name] %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
Related
Here the show.html displays a dropdown, which contain Roles.
When we select the role we should able to get permission result in the same page
For that I used partial, but getting error as given in the image,
Without using partial when i tried to display in a separate display.html.erb file , I am getting proper result.
but i want to get result in same show.html.erb file.
Kindly give my some suggestions to attain the proper results
permission_controller
def display
param = params[:role]
id=param[:id]
#roles = Role.includes(:permissions).all
#uniq_controller = Role.joins(:permissions).where('roles.id=?',id).select('permissions.*').group_by { |p| p.description }
redirect_to permissions_show_path
end
def show
#permission = Permission.new
end
show.html.erb
<%= form_tag(:controller => "permissions", :action => "display") do %>
<%= collection_select(:role, :id, Role.all, :id, :name) %>
<button type="submit">search</button>
<% end %>
<th width="25px"> <%= "Controller" %></th>
<th width="25px"> <%= "Permissions" %></th>
<% #uniq_controller.each do |permission| %>
<%= render partial:"display", locals:{permission:permission} %>
<% end %>
_display.html.erb
<thead>
<th width="25px"> <%= permission.first.gsub("_"," ") %></th>
<% permission.second.each do |cont| %>
<tr>
<th width="25px"><%= check_box_tag :permission_ids, {multiple: true},
cont.id %><%= cont.name %></th>
</tr>
<% end %>
</thead>
You haven't defined #uniq_controller in the show action which triggered that error. Just define it in the show action
def show
#permission = Permission.new
#uniq_controller = Role.joins(:permissions).where('roles.id=?',id).select('permissions.*').group_by { |p| p.description }
end
You have not defined #uniq_controller in the show action in permission_controller controller which triggers this error.
I'd recommend that you define a method called uniq_controller in permission_controller as follows:
def uniq_controller(id)
Role.joins(:permissions).where('roles.id=?',id).select('permissions.*').group_by { |p| p.description }
end
and then make it available in your view as a helper method by adding this code to your permission_controller:
helper_method :uniq_controller
So the code in permission_controller should be like:
helper_method :uniq_controller
def display
#roles = Role.includes(:permissions).all
redirect_to permissions_show_path
end
def show
#permission = Permission.new
end
def uniq_controller(id)
Role.joins(:permissions).where('roles.id=?',id).select('permissions.*').group_by { |p| p.description }
end
Then in your view show.html.erb replace:
#uniq_controller.each
with:
uniq_controller(params[:role][:id]).each
This should fix the error that you are getting and follows Rails practices, for more details about helper_method please refer to:
https://apidock.com/rails/ActionController/Helpers/ClassMethods/helper_method
One more recommendation is to rename permission_controller to permissions_controller to follow Rails resources/controller naming convention.
I have a search form and I need to be able to filter based on whether or not pets are allowed, but I'm not sure how to accomplish this. I have setup a route, a controller method, and a button but none of that seems to be working.
listings_controller:
def pets_allowed
#listings = #listings.where(pets: true)
end
routes.rb:
get "pets_allowed" => "listings#pets_allowed"
html.erb file:
<div>
<%= link_to 'Pets Allowed', pets_allowed_path, :class => 'button btn-transparent' %>
</div>
Maybe you meant
def pets_allowed
#listings = Listing.where(pets: true)
end
This is a basic example of another way to do what I think you're aiming for (as per comments).
This adds a new action in your Listings controller that returns a filtered list of results based on the users input from the search form on your listings index page. The results are rendered using the same index template. The logic for checking/retrieving results can be modified based on what you want. If you just want a check box, only have a checkbox or a button that calls the action.
You could do similar logic but use ajax to return the results and render them on the index template using a partial.
This should give you enough information to google for examples/tutorials and try different ways of getting what you want.
Add a route:
# routes.rb
get 'pets_allowed', to: 'things#pets_allowed'
Add a new action:
# listings_controller.rb
# GET /things
# GET /things.json
def index
#listings = Listing.all
end
# Get /pets_allowed
def pets_allowed
#listings = Listing.where("name LIKE ? and pets = ?", "%#{params[:name]}%", params[:pets] )
render template: "listings/index", variable: #listings
end
Add a search form to your view:
# listings/index.html.erb
<h1>Listings</h1>
<%= form_tag('pets_allowed', method: 'GET' ) do %>
<%= label_tag :name %><br>
<%= text_field_tag :name %>
<br>
<%= label_tag :pets %><br>
<%= check_box_tag :pets, 't' %>
<br>
<%= submit_tag("Search") %>
<% end %>
<table>
<thead>
<tr>
<th>Listing name</th>
</tr>
</thead>
<tbody>
<% #listings.each do |listing| %>
<tr>
<td><%= listing.name %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Listing', new_listing_path %>
I want to print in tabular form the entries given by user. The table will contain 3 columns for name,registration number and classes attended, however the output is not as expected. Its first printing all the names and then all the registration numbers and so on. I want to print a name and the registration number according to name and classes attended.
</head><table width="100%">
<tr>
<th>NAME</th>
<th>REGISTRATION NUMBER</th>
<th>CLASSES ATTENDED</th>
</tr>
<tr>
<% #name_students.each do |t| %>
<th><%= t.name_student %></th><br>
<% end %>
<% #reg_nos.each do |t| %>
<th><%= t.reg_no %></th><br>
<% end %>
<% #classes_ats.each do |t| %>
<th><%= t.classes_at %></th><br>
<% end %>
</tr>
</table>
Here is my controller action.
class PagesController < ApplicationController
def home
#name_students = Page.all
#reg_nos = Page.all
#classes_ats = Page.all
end
def list
#name_students = Page.all
#reg_nos = Page.all
#classes_ats = Page.all
end
def add
Page.create(:name_student => params[:nam])
Page.create(:reg_no => params[:reg])
Page.create(:classes_at => params[:cls])
redirect_to :action => 'home'
end
end
If I understand what you're doing, you should probably have PagesController#home return something like #pages = Page.all object and display the data kind of like this:
<thead>
<tr>
<th>Name</th>
<th>Registration Number</th>
<th>Classes Attended</th>
</tr>
</thead>
<tbody>
<% #pages.each do |p| %>
<tr>
<td><%= p.name %></td>
<td><%= p.registration_number %></td>
<td><%= classes_attended(p.classes_attended) %></td>
</tr>
<% end %>
</tbody>
Above, classes_attended(p) is a call to a helper method that you would use to map the class names of the classes that student attended into an display-able array. Really, that kind of display logic might be better in a decorator, but a helper method should be fine for now.
Let me know if I've totally misunderstood what you're doing, and I'll delete my answer.
Edit to add:
Example index method:
def home
#pages = Page.all
end
Also, looking at your question again, is there a reason you're creating three Page objects with one attribute each instead of one Page object with all three attributes? It should probably look something like Page.create(:name_student => params[:nam], :reg_no => params[:reg], :classes_at => params[:cls]). That's pretty much the only way the solution I posted will work. Again, though, I might be totally misunderstanding what you're going for.
Lets say that I have a model with name of User. How can I add a virtual attributes to the final result of generated query ?
class User < ActiveRecord::Base
ATTRIBUTES = %w[name email balance]
scope :main_selection, -> { select('name,email,total_bought, total_deposit') }
def balance
(total_deposit - total_bought).round
end
end
and inside my controller I have
#user = User.main_selection
#attributes = User::ATTRIBUTES
Inside the View I would like to show it in a table
<table>
<thead>
<tr>
<% #attributes.each do |a| %>
<th><%= a %><th>
<% end %>
</tr>
</thead>
<tbody>
<% #user.each do |u| %>
<tr>
<% #attributes.each do |a| %>
<td><%= u[a] %><td>
<% end %>
</tr>
<% end %>
</tbody>
<table>
The only problem is that I need to add the balance attribute into the generated result, so that the loop with u[a] could work.
I need to mention that the balance can be called by #user.first.balance, but inside the loop does not work and it shows a nil value instead.
Try u.send(a) instead of u[a]
u[a] will only work on attributes. In your example, balance is not an attribute, it's a method.
I have Search controller that searches 2 models - Posts and Categories. The search works, however I am unable to display results in the View correctly - I can't get category names to show up.
I am very confused and frustrated at this point and hope to find some help!
I'm pretty sure (99% sure) the problem is in the View somewhere, because I can get results to display through render inspect thingy.
SearchController.rb
class SearchController < ApplicationController
def index
#posts = Post.search(params[:search])
#categories = Category.search(params[:search])
# combine the results
#results = #posts + #categories
#results.uniq # may be necessary to remove duplicates
end
end
index.html.erb (views/search)
<%= render 'posts/posts', :posts => #posts %>
_posts.html.erb (view/posts)
<h1>Listing posts</h1>
<table>
<tr>
<th>Name</th>
<th>Category</th>
<th>Description</th>
<th>Flag</th>
</tr>
<% if posts %>
<% posts.each do |post| %>
<tr>
<td><%= post.name %></td>
<td><%= post.category.name %></td>
<td><%= post.description %></td>
<td><%= post.flag %></td>
</tr>
<% end %>
<% else %>
<tr><td>No posts</td></tr>
<% end %>
</table>
I can get posts that match the search to display, but I can't display categories. How can I do this? Any help highly appreciated!! Thank you.
If you are using a search backend like sunspot solr then you would be able to combine the searches like
#search = Sunspot.search [User, Company] do
fulltext params[:search]
end
#results = #search.results
And then return the necessary values. In this example, it's showing where you can retrieve the class of the action (controller_name) may not work depending on which controller the results are returned in.
<% #results.each do |result| %>
<% case result.class.to_s %>
<% when "Company" %>
<li><%= "Company: #{result.name}" %></li>
<% when "User" %>
<li><%= "User: #{result.username}" %></li>
<% end %>
<% end %>
The answer (I include all the things I changed to make it work + few files that I didn't change but that have to be there), or How to make simple search for multiple models:
SearchController.rb
class SearchController < ApplicationController
def index
#posts = Post.search(params[:search])
end
end
post.rb
class Post < ActiveRecord::Base
attr_accessible :category_id :name :description :flag
belongs_to :category
def self.search(search)
if search
find(:all, :joins => :category, :conditions => ['posts.equipment LIKE ? OR posts.description LIKE ? or categories.name like ?', "%#{search}%", "%#{search}%", "%#{search}%"])
else
find(:all)
end
end
search/index.html.erb
<%= render 'posts/posts', :posts => #posts %>
I added 2 files _post.html.erb and _category.html.erb. They are similar, this is _post.html.erb:
post: <%= post.name %>
(This might not be necessary in some cases or for some models. I can search a third model without this file in its' views. However the third model doesn't have file like _posts.html.erb either).
Finally, _posts.html.erb remains the same:
...
<% if posts %>
<% posts.each do |post| %>
<tr>
<td><%= post.name %></td>
<td><%= post.category.name %></td>
<td><%= post.description %></td>
<td><%= post.flag %></td>
</tr>
<% end %>
<% else %>
<tr><td>No posts</td></tr>
<% end %>
...
This works now. Can add new models to the search easily. The only other thing needed for the search is input field.