Generate method dynamically using strong params in Rails - ruby-on-rails

Is it possible to generate a dynamic method using the strong parameter I get from my view?
In my view I will generate a different path according to the card the user clicks on. Then, in the controller I want to generate a dynamic method using the parameter obtained from the view, but I'm not sure how to write that. Thanks!
show.html.erb
<div class="branch-names">
<% #branches.each do |branch| %>
<div>
<%= image_tag "cat.jpeg" %>
<%= link_to "#{branch.name} Posts", send("#{branch.branch}_posts_path") %>
</div>
<% end %>
</div>
posts_controller.rb
def self.define_category(name)
define_method(name) do |params[:id]|
#posts = Post.where(category_id = params[:id])
end
end
define_category("#{params[:id]}")

You shouldn't define method based on user input. It may cause security issue, and for sure it causes performance penalty related to method cache invalidation.
Instead you can create one method that have an alternarive on params[:id] and then decide what to show to the user:
class MyController
def branches
case params[:id]
when('cat')
do_cat_stuff
when('dog')
do_dog_stuff
end
end
end

For having routes like /posts/cats you do not have to add dynamic methods. Think of branch like of an id of category:
routes:
resources :post_categories, only:[:index, :show]
view:
...
<%= link_to "#{branch.name} Posts", post_category_path(branch.branch) %>
PostCategories controller:
def show
#posts = Post.where(category_id: params[:id])
end
Also you can make posts a nested resource under categories and use a more RESTful structure with /post_categories/some_branch/posts mapping to posts#index

Related

Rails: How to check which form in a view was submitted

There are 2 forms on one page. I want an if else statement in the controller to use different params and variable values depending on which form was submitted. From doing a google search the best I came across was to have a value on the submit button.
<%= f.submit "Save" :value => "x" %>
If this is a plausible way I cant find how to make an if else statement for checking if the submit value is 'x'.
Something like
if submit.value == 'x'
do something
else
do something else
end
Really not sure. If there is another way of having an if else statement in the controller to catch witch form was submitted by using an id or name or whatever I'm happy to hear it.
The value of the submit button can be accessed with the help of params[:commit], so you can use it to check which form is submitted.
if params[:commit] == 'x'
do something
else
do something else
end
#Pavan has the direct answer, however if you're evaluating form submissions by their respective submit values, you've got a major issue with your pattern.
Form
Forms should be a way to pass values to your controller, which will then populate the model. You should not have to determine actions based on those values, unless you have different functionality...
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= form_for post do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
<% end %>
The above will create multiple forms, all submitting to the posts#update method:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
#post = Post.find params[:id]
#post.update post_params
end
private
def post_params
params.require(:post).permit(:x, :y, :z)
end
end
The values inside those forms don't matter, nor to which post object they were sent; they will all be evaluated in exactly the same way.
--
Actions
The other way around this is to make separate actions for the different forms:
#config/routes.rb
resources :posts do
patch :update_2, on: :member
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update_2
#post = Post.find params[:id]
#post.param = "value"
#post.update post_params
end
end
#app/views/posts/show.html.erb
<%= form_for #post, url: posts_update_2_path(#post) do |f| %>
<%= f.submit %>
<% end %>
You could use something like
<%=f.submit "Basic update", name: "basic-update" %>
<%=f.submit "Security update", name: "security-update" %>
and then check in your controller:
if params.has_key? "security-update"
#do something
elsif params.has_key? "basic-update"
#do another thing
end

Rendering view from different controller NilClass Error

I'm trying to render a view in a different controllers view but I'm getting:
undefined method `each' for nil:NilClass
I'm rendering the view in 'views/users/show' as:
<%= render :template => 'groups/index' %>
The view itself is under 'views/groups/index':
<% #groups.each do |group| %>
<li>
<%= group.name %>
<%= group.description %>
</li>
<% end %>
And my groups controller for index looks like this:
def index
#groups = Group.all
end
I think it's a problem with how i'm rendering the view since if I make an instance variable in my index controller and call it in the view it won't appear. There are entries in the Group table in my database.
Any help would be appreciated.
Thanks in advance.
I think it should be enough to replace the template: with a partial: parameter.
Try this:
<%= render partial: 'groups/index' %>
You will have to rename/copy groups/index.html.erb with groups/_index.html.erb
This only works for rendering a view, but will not implement the functionality of your GroupsController.
Edit
You will have to redefine the groups inside your UsersController
# UsersController
def index
#groups = Group.all
end
Depending on how many times you will need to present all these groups to your user, this can become hard to maintain. If you use it frequently, consider adding
# i.e. ApplicationController
def groups
Group.all
end
inside your ApplicationController (or some module you want to include in different controllers). Then you could call
# UsersController
def index
#groups = groups
end
and still <%= render partial: 'groups/index' %>
Change:
<%= render :template => 'groups/index' %>
To:
<%= render 'groups/index' %>
and make sure the file name of your index action is _index.html.erb and not index.html.erb.
EDIT
When you render a view, you are only rendering the template, this does invoke a request on your index action. You must define #groups in your initial view's action.

Having problems with putting database to use

So I have this books database and a burrows database. In burrows, there is a field for book_id and also a field for user_id, so that I can see who burrowed which book.
Now, I am trying to create a controller and view for it but it is not going well really. Right now the view is looking like this:
<% provide(:title, "Burrow") %>
<b align="center">Choose the name of the book you want t burrow'</b>
<%= form_for(#book) do |f| %>
<div class="forms">
<%= f.name %>
<%= f.check_box(:book_id) %>
<%= f.submit 'Submit!' %>
</div>
<% end %>
But this puts me to the problem where it creates an error because I want to put all books into #books in burrows controller. But I dont really see any other way? \
The final idea would be so that I have all the books displayed and after them a checkbox, so I can select which books I want to burrow. And after that I also want a dropdown menu where all users are listed, I can choose to burrow the book for another user, but the default value would be the logged in user but theres time till that. Right now I am struggline to understand, why my solution for listing books does not work?
Listing my controller here also:
class BurrowsController < ApplicationController
before_action :signed_in_user, only: [:index,:edit,:update, :destroy]
before_action :admin_user, only: :destroy
def index
#burrows = Burrow.all
end
def show
#burrow = Burrow.find(params[:id])
end
def new
#burrow = Burrow.new
end
def create
#burrow = Burrow.new(burrow_params)
if #burrow.save
flash[:success] = "Burrowing a book was successful!"
redirect_to #burrow
else
render 'new'
end
end
def listing
#book_list = Book.all
end
# Private section, makes the page unable to be seen for non logged in users
private
def burrow_params
params.require(:burrow).permit(:user_id, :book_id)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
# Redirecting not logged in user etc.
def signed_in_user
unless signed_in?
store_location
redirect_to '/sessions/new', notice: "Please sign in!"
end
end
end
Right now I am struggline to understand, why my solution for listing books does not work?
I'm not sure what listing you mean. The view you pasted apparently corresponds to the controller action Burrows#new?
So I'm going to tell you what's wrong with your view:
<%= form_for(#book) do |f| %>
This prints a form for a Book, not a Burrow. You could create a new book with this form, but that's certainly not what you're trying to do here.
You'll want to have all these variables in your controller:
def new
#users = User.all
#books = Book.all
#burrow = Burrow.new
end
so you can use collection_select with #users, have a #burrow object to use with form_for, and do a each loop on #books, for instance, like this:
<%= form_for(#burrow) do |f| %>
<% #books.each do |book| %>
<%= f.check_box :book_ids, multiple: true, book.id, nil %>
<% end %>
<%= f.collection_select :user_id, #users, :id, :name, {}, selected: current_user.id %>
<%= f.submit %>
<% end %>
Click the links for documentation on these commands: collection_select, check_box
This is not ideal code, but it's as close to your example as I can get.
I understand if you don't get this right away. Your code is a bit of a mess right now and there's too much wrong with it to be explained in one post and fixed by just one line. You might want to start over, and make a single controller action work before you try to make everything at once.
Sometimes it's better to sit back and really think about what you're trying to achieve, and first lay out how to achieve it inside your head; and only then start programming!
It seems to me that you're using the RailsTutorial by Michael Hartl, so all I can recommend to you right now is, read what you've read again more thoroughly and try to stick to his examples first until you feel confident, before really starting to make your very own application.

How do I setup the views for nested resources that are displayed as tabs within their parent resource in Rails?

I have a resource Company that has many Projects and People. The index action for the companies displays each company in a table. The show action shows some additional information about it, and then what I'd like to show is another table, with tabs "Projects" and "People".
When I click on the "People" tab, I should go to URL "companies/:id/people", and likewise for the "Projects" tab. I'm not worried about AJAX or pre-loading all of this information into the #company variable during the show action. A simple nested resource is fine.
Now when I'm at "companies/:id/people", it will use PeopleController#index, but I want to show that view (which is JUST it's table, I suppose?) nested within the company's show view. So that when I switch between "companies/:id/people" and "companies/:id/projects", the only thing changing is the table, not the company information around the outside.
Is this sort of thing easily do-able? If Rails isn't build to handle this sort of thing easily, I don't mind using something else. I just don't have much experience with the view layer, so I don't know much about it since I primarily work with JSON.
Basic Example:
ProjectsController && PeopleController:
layout :current_layout
def current_layout
if #company && #company.persisted? && request.path_parameters[:action] == "index" # i prefer helper 'current_action'
"company"
else
"application"
end
end
Helper:
def parent_layout(layout)
#view_flow.set(:layout, self.output_buffer)
self.output_buffer = render(:file => "layouts/#{layout}")
end
Company layout:
#views/layouts/company.html.erb
<h1><%= #company %></h1>
<ul class="tabs">
<li>Info</li>
<li>Info</li>
<li>Info</li>
</ul>
<%= yield %>
<%= parent_layout(:application) %>
People template:
# views/people/index.html.erb
<% if current_layout == "company" %> # just table
<%= render "people_table" %>
<% else %>
<h1>People controller</h3>
<%= render #people %>
<% end %>
Projects template:
# views/projects/index.html.erb
<% if current_layout == "company" %> # just table
<%= render "projects_table" %>
<% else %>
<h1>Projects controller</h3>
<%= render #projects %>
<% end %>
I suggest you take a look at the rails guides, specifically routing from the outside in.
However rails can handle this, your route would be setup in the following way:
resources :company do
resource :projects
resource :people
end
I assume you already have all your CRUD actions setup, then this would work. However do note, it will change your named routes.
i.e
if you were calling
projects_path
in your views, they will now become:
company_projects_path
You can see a full list of routes with the command
rake routes

best practice for using a date selector in an overview/index

I am new to rails and try to realise a overview of a list of meetings created using the index method in the meeting controller. The page has also a select with all years, so that you can select a specific year to see only the meetings of the selected year. I realized it adding a form on the page:
index.html.erb:
<h1>Protokolle</h1>
<%= form_tag 'meetings', :method => :get do %>
<%= select_tag :selected_year, options_for_select(available_years, #year), {onchange: 'this.form.submit();'} %>
<% end %>
<%= link_to image_tag('new'), new_meeting_path %>
...
Using put as the action method of the form, does not work, but get seems to be ugly.
How can this be done better?
meetings_controller:
class MeetingsController < ApplicationController
def index
#year = selected_year(params[:selected_year])
#meetings = Meeting.where(:held_on => ("01.01.#{#year}".to_date)..("31.12.#{#year}".to_date)).order('held_on desc').all
respond_to do |format|
format.html # index.html.erb
end
end
...
Thanks for your tips ...
There's no problem using GET in a form for a get-only action. PUT doesn't work, because of RESTful routes - the POST version would activate the 'meetings' route will call 'create' for a new meeting, PUT is simply denied by the router as not existing.

Resources