I have a model called sections, which has many feedbacks. This is working ok, and on the 'section' show page I can show the associated 'feedbacks' like so:
<% #section.feedbacks.each do |feedback| %>
<%= feedback.name %>
<%= link_to 'Show', feedback %>
<%= link_to 'Edit', edit_feedback_path(feedback) %> |
<% end %>
Works fine. But now I need a button that takes me to the create page for a new feedback item, and it needs to be associated to this 'section'.
At first I did it with a nested form, but the feedback items have quite a lot of fields and so it's messy to do it all on one page.
I'm new to ruby, so hopefully it's a really simple thing!
Thanks in advance for any help,
James
You should use nested form for it. If your form contains many fields then use bootstrap wizard for it.
or
<%= link_to 'New', new_feedback_path(section_id: #section.id) %>
& in your new method of feedback_controller , write the below:
#feedback = Feedback.new
#feedback.section_id = params[:section_id]
What you are trying to do is pretty much standard Rails stuff and you need to read a bit more of the official guides. You will need:
A controller with different actions for feedbacks (edit and new for example)
Some views for the above actions. The edit and new views can share the same form.
Routing to make it possible to work with a Feedback in the context of a Section.
Like:
resources :sections do
resources :feedbacks
end
This will allow you to use the following instead of your edit link:
<%= link_to 'Edit', edit_feedback_path([#section, feedback]) %>
And will go to this edit route:
/sections/:section_id/feedbacks/:feedback_id
You will also have the following new route available:
/sections/:section_id/feedbacks/new
Which will allow you to get the right section from the url params in order to create a feedback for it.
Related
I am new to rails.I have model Review, with "text" column.I need to make a form where i fill a word and then on another page write all reviews which contain this word.How to filter it and write view if it can be 1 or can be a lot of reviews?Where i need pass filtering method(model or controller)?
Make a form for search review eg: -
<%=form_tag filter_review_path do %>
<%=text_field_tag :terms%>
<%=submit_tag :submit%>
<%end%>
In reviews controller
def filter
#filter_reviews = Review.where("LOWER(text) LIKE ?", "%#{params[:terms].downcase}%")
end
In routes.rb
get '/filter_review', to: "reviews#filter", as: :filter_review
In filter.html.erb show all filter_reviews
<%unless #filter_reviews.blank?%>
<%#filter_reviews.each do |review|%>
<%=review.text%>
<%#=review.another_attribute%>
<%end%>
<%else%>
No review found
<%end%>
<%= form_for [#blog,#blog.comments.build] do |f| %>
<p><%= f.text_area :text, :size => '40x10' %> </p>
<p><%= f.submit "Post Comment" %> </p>
<% end %>
This is handler by comments_controller, but I would like to know the reason, especially for form_for
The form_for creates a form for creation or update of passed object. If the object is not persisted, the associated url will target the creation action. Otherwise the targeted action will the update. form_for can receive many different kinds of parameter to generate the form.
If you check out the Rails url_helpers documentation, you will see that you can do something like:
<%= link_to 'First comment', blog_comment_path([#blog, #blog.comments.first]) %>
This will generate a link to the first comment of the blog with a path like /posts/#post.id/comments/#post.comments.first.id. This also assumes that you have the correct setup on your routes.rb:
resources :blogs do
resources :comments
end
With this, you generate a bunch of paths that you can use to build, for instance, links and forms. Thus, the form_for in your code works similarly. Think of it as a url_helper. You have a #blog and a comment associated to the post(#blog.comments.build). As the comment is not persisted yet, the will generate a form for the creation of comments targetting CommentsController#create. The associated path will be something like /blogs/#blog.id/comments and the HTTP method will be POST.
Also, check these links to get more info:
Rails Routing
Rails Form Helpers
It adds a form with a text box, submit button and some hidden authentication related hidden fields for entering comment.
The comment is added to #blog object with relationship:
has_many :comments
Comment is build by the code if not present by:
#blog.comments.build
So overall you get a form for entering comments in a # blog object. The blog object is necessary in this case and the comment will be automatically combined to the blog entry in proper column in comment record column "blog_id" by default.
This is called Nested Form Relationship, where instead of editing only one record of comment you can combine the parent object also and edit it.
build is basically used to create a structure for object, something like new ( e.g. Model.new). Form action is decided on the basis of given objects. In your case the objects are #blog and #blog.comments.build so the action called will be of either update of Blog controller or Create of Comments Controller..
Hope this helps.
I'm trying to hide parts of my views depending on the User role.
So let's say I want only admins to be able to destroy Products. Besides the code in the controller for preventing regular users from destroying records, I would do the following in the view:
<% if current_user.admin? %>
<%= link_to 'Delete', product, method: :delete %>
<% end %>
The previous code works, but it's prone to errors of omission, which may cause regular users to see links to actions they are not allowed to execute.
Also, if I decide later on that a new role (e.g. "moderator") can delete Products, I would have to find the views that display a delete link and add the logic allowing moderators to see it.
And if there are many models that can be deleted only by admin users (e.g. Promotion, User) maitenance of all the ifs would be pretty challenging.
Is there a better way of doing it? Maybe using helpers, or something similar? I'm looking for something maybe like this:
<%= destroy_link 'Delete', product %> # Only admins can see it
<%= edit_link 'Edit', promotion %> # Again, only admins see this link
<%= show_link 'Show', comment %> # Everyone sees this one
I found these two questions that are similar to mine, but none of them answered my question:
Show and hide based on user role in rails
Ruby on Rails (3) hiding parts of the view
I strongly recommend pundit.
It allows you to create "policies" for each model. For your Product model you might have a ProductPolicy that looks something like this
class ProductPolicy < ApplicationPolicy
def delete?
user.admin?
end
end
In your view you can do something like this
<% if policy(#post).delete? %>
<%= link_to 'Delete', product, method: :delete %>
<% end %>
If later on you want to add a moderator role, just modify the policy method
class ProductPolicy < ApplicationPolicy
def delete?
user.admin? || user.moderator?
end
end
So I kind of figured a way to move the IFs out of the view. First, I override the link_to helper in my application_helper.rb:
def link_to(text, path, options={})
super(text, path, options) unless options[:admin] and !current_user.admin?
end
Then on my views I use it as:
<%= link_to 'Edit Product', product, admin: true, ... %>
This prevents regular users from seeing admin links, but for other html tags with content inside, such as divs, tables etc., an if would still be needed.
CanCan is another gem that lets you define "Abilities" per user role.
In views you can use something like if can? :delete, #post to check if the
user may delete that specific post.
Using the CanCan and Role gems, what is still needed is a way to Check The Route and see if "current_user" has permissions to access that Route based on their role(s) - then show/hide based on that.
This saves the user clicking on things and getting told they cannot see it - or us having to write per-item "if" logic specifying what roles can see what list-items (which the customer will change periodically, as roles are changed/refined) around every single link in one's menu (consider a bootstrap menu with 50+ items nested in groups with html formatting, etc), which is insane.
If we must put if-logic around each menu-item, let's use the exact same logic for every item by checking the role/permissions we already defined in the Ability file.
But in our menu-list, we have route-helpers - not "controller/method" info, so how to test the user's ability to hit the controller-action specified for the "path" in each link?
To get the controller and method (action) of a path (my examples use the 'users_path' route-helper) ...
Rails.application.routes.recognize_path(app.users_path)
=> {:controller=>"users", :action=>"index"}
Get just the controller-name
Rails.application.routes.recognize_path(app.users_path)[:controller]
=> "users"
Ability uses the Model for its breakdown, so convert from controller name to it's model (assuming default naming used) ...
Rails.application.routes.recognize_path(app.users_path)[:controller].classify
=> "User"
Get just the action-name
Rails.application.routes.recognize_path(app.users_path)[:action]
=> "index"
And since the "can?" method needs a Symbol for the action, and Constant for the model, for each menu-item we get this:
path_hash = Rails.application.routes.recognize_path(app.users_path)
model = path_hash[:controller].classify.constantize
action = path_hash[:action].to_sym
Then use our existing Abilty system to check if the current_user can access it, we have to pass the action as a symbol and the Model as a constant, so ...
<% if can? action model %>
<%= link_to "Users List", users_path %>
<% end %>
Now we can change who can see this resource and link from the Ability file, without ever messing with the menu, again. But to make this a bit cleaner, I extracted out the lookup for each menu-item with this in the app-controller:
def get_path_parts(path)
path_hash = Rails.application.routes.recognize_path(path)
model_name = path_hash[:controller].classify.constantize
action_name = path_hash[:action].to_sym
return [model_name, action_name]
end
helper_method :get_path_parts
... so I could do this in the view (I took out all the html-formatting from the links for simplicity, here):
<% path_parts = get_path_parts(users_path); if can?(path_parts[1], path_parts[0]) %>
<%= link_to "Users Listing", users_path %>
<% end %>
... and to make this not take all day typing these per-menu-item if-wraps, I used regex find/replace with capture and wildcards to wrap this around every list-item in the menu-item listing in one pass.
It's far from ideal, and I could do a lot more to make it much better, but I don't have spare-time to write the rest of this missing-piece of the Role/CanCan system. I hope this part helps someone out.
I'm trying to pass the value of a element to a rails controller!
Currently, I have something like this:
<td id="dbname"><%= link_to db, :action => :show %></td>
This represents a row in an html table, which contains a string value, e.g. "development".
When the user clicks on the "development" link, the <%= link_to ... %> grabs the value from the current clicked and passes that to a rails controller action, in this case the show action.
How can this be achieved!?
UPDATE - generating links:
<% #dbs.each do |db| %>
<tr>
<td id="dbname"><%= link_to db, :action => :show %> </td>
</tr>
UPDATE 2:
this is my index controller:
conn = Mongo::Connection.new
#dbs = conn.database_names #this returns an array of strings (which are the names of the databases)
Now I want to be able to click on of these databases and then to pass the clicked text to the rails controller show action. I'm not sure how I would generate a custom resources path for these links... but I was contemplating of doing it using Ajax or something javascript related. Maybe get the text of clicked link using jQuery and then send an Ajax request to the rails controller with the text as a parameter!
I think that it's a strange thing what you're trying to do, but a solution could be to use javascript to append the id to the href of each link as a query string.
If you could explain a little bit what you're trying to achieve maybe we could find a better solution.
Hope it helps!
Edit:
If you have a table of links I think that you should consider them as a resource and managing them the REST way.
Your controller should have an index and show action and you should declare the links as a resource in the routes file (maybe link it's a reserved word and you will have to use a different name, I'm not sure), the index action will fetch all the links and when you render them, you could specify the link for each one with something similar to "link_path(link.id)" (remember, you should have a show action defined) in the controller you will receive the link id so you could load it with a simple "find" and pass it to the view.
I recommend you to always look for the REST way to solve a problem in ROR.
Edit 2:
Ok let's see if this works for you:
I suppose that you have a model that represent those links that you're talkin about, for example:
class DataBaseLinks < ActiveRecord:Base
end
This model with be backed up by a table in your database, if you have generated it the rails way, you will also have an id column that identify each database link.
in your controller, let's say DataBaseLinksController, you'll have:
class DataBaseLinksController < ApplicationController
def index
#dabatase_links = DataBaseLink.all
end
def show
#database_link = DataBaseLink.find(params[:id])
end
end
(I've avoided all the validations and checks).
All you have to do in your index.html.erb is:
<% #database_links.each do |database_link| %>
<%= link_to database_link.name, database_link_path(database_link.id) %>
<% end %>
This will generate all the links with the correct path to the show action (maybe the route helper is a little bit different, but not so much).
Notice also that you'll have to add into your routes.rb the following line:
resources :database_links, :only => [:index, :show]
How do you see it?
Edit 3:
(I'll delete all my edited answers when we find a correct one)
Ok I'm going to suppose that you are not using something like mongoid so you don't have active record similar objects.
Have you tried this in your view:
<% dbs.each do |dbs_name| %>
<%= link_to dbs_name, :controller => "your_controller", :action => :show, :dbs_name => dbs_name %>
<% end %>
Let's say you have a resource called Article. On the index page, you have many articles listed. In one scenario, I have this Cucumber step:
When /^I activate the edit article switch for the article "(.+)"$/ do |name|
I'm trying to identify that specific article - i.e. clicking the "edit" button for that specific article. I've been thinking about using the "within" helper of capybara, but I don't feel like that's the solution. I somehow need to find the name of the article on the page and then click that article's particular edit button... see what I mean?
If anyone has a clean solution, I'd appreciate knowing it. Thanks in advance.
What I do for this kind of thing is write a step like this:
When I edit article "Some article name or id"
My step definition then looks like this:
When %|I edit article "$article_name"| do |article_name|
article = Article.find_by_name(article_name)
within "#article_#{article.id}" do
#do whatever you need to do for that article
click "Edit"
end
end
In my views I'm clearly defining the id on some parent element that will sufficiently scope calls like this. This pattern keeps you from having to get too dirty in the cukes themselves, while still giving you a certain degree of flexibility. For example:
<% #articles.each do |article| %>
<div id="article_<%= article.id %>">
Stuff in here <%= link_to "Edit", edit_article_path(article) %>
</div>
<% end %>