I have some user types such as Student, Teacher, Admin. For each user type I have a controller. For instance student controllers placed in the app/controllers/students directory. But mostly controllers are the same. Because of this I created core controllers placed in the app/controllers/core.
app/controllers/core/settings_controller.rb
class Core::SettingsController < ApplicationController
def show
# instance variables, redirections etc. placed here...
#
end
# other actions...
#
end
app/controllers/student/settings_controller.rb
class Student::SettingsController < Core::SettingsController
end
routes.rb
namespace :student do
# ...
resource :schedule, :only => [:show]
resource :settings, :only => [:show, :update]
end
namespace :admin do
# ...
resource :settings, :only => [:show, :update]
end
Problems started when I added <%= link_to "<", schedule_path(:date => #date.prev_month)" %> to my views/core/settings/_month_nav.html.erb file.
Is there any solution except writing this ugly peaces of code:
<% if admin? %>
<%= link_to "<", admin_schedule_path ... %>
<% elsif student? %>
<%= link_to "<", student_schedule_path ... %>
...
You could do something like
<%= link_to "<", self.send("#{current_user.class.to_s.downcase}_schedule_path",:date => #date.prev_month) %>
I used current_user because I am not sure how you are setting this as your view code was not very clear.
Related
I'm trying to create a customer action in my campaigns controller called building.
http://localhost:3000/campaigns/building
Shows error:
Showing .../app/views/campaigns/show.html.erb where line #1 raised:
undefined method `name' for nil:NilClass
<h2><%= #campaign.name %></h2>
<p>
Created by: <%= #campaign.user.email %> <%= time_ago_in_words(#campaign.created_at) %> ago.
</p>
<h5>Website: <%= #campaign.website %></h5>
My routes file:
resources :campaigns do
resources :targets
end
get "campaigns/building" => "campaigns#building", :as => :campaigns_building
Controller:
class CampaignsController < ApplicationController
before_action :find_campaign, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def show
end
def building
end
...
My show.html.erb:
<h2><%= #campaign.name %></h2>
<p>
Created by: <%= #campaign.user.email %> <%= time_ago_in_words(#campaign.created_at) %> ago.
</p>
<h5>Website: <%= #campaign.website %></h5>
How do I make it not give this error?
It seems you are trying to make a custom route and unable to make it work. If so, to make it work, you should move your custom route above resources to avoid conflicts with the default resourceful route and correctly route to building action
get "campaigns/building" => "campaigns#building", :as => :campaigns_building
resources :campaigns do
resources :targets
end
This is because Rails tries to match routes starting from the top down. You can also define it as a collection route on the resources
resources :campaigns do
get "building", on: :collection
resources :targets
end
I have set up a polymorphic association between Event, Meeting, and TaskList such that:
An Event can have many TaskLists, A TaskList can belong to one Event.
A Meeting can have many TaskLists, A TaskList can belong to one Meeting.
This works and I can create task_lists from the view of these models. However my problem is that I want to be able to add TaskItem to each TaskList such that:
A TaskList can have many TaskItems, A TaskItem can belong to one TaskList.
I'm having trouble routing the form for the creation of a task item. I've created a "_form.html.erb" for this and am rendering it from the view of the task_item. I'm using the form below for this, at the moment from the events view, which shows the form fine but throws the routing error "No route matches [POST] "/events/3/task_lists/new.3" when clicked on submit.
_form.html.erb
<%= form_for TaskItem.new, url: new_polymorphic_path([#listable, #task_list, #task_item]) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I have also tried setting it up like below which doen't even show the form throwing the error "First argument in form cannot contain nil or be empty"
<%= form_for #task_item do %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Models
class TaskList
belongs_to :listable, polymorphic: true
has_many :task_items
end
class TaskItem
belongs_to :task_list
end
class Event
has_many :task_lists, as: :listable
end
class Meeting
has_many :task_lists, as: :listable
end
Routes (added :show to task_lists, only: , as my link wouldnt work otherwise.)
concern :has_task_lists do
resources :task_lists, only: [:new, :index, :create, :show]
end
resources :events, :meetings, concerns: [:has_task_lists]
resources :task_lists, except: [:new, :index, :create] do
resources :task_items
end
task_items_controller (want it to redirect to the page item was created from, which is the show view for task_list)
def create
#task_item = #task_list.task_items.new(task_item_params)
if #task_item.save
redirect_to #task_list, notice: "Task Item Created"
else
render :new
end
end
task_lists_controller
before_action :load_listable
def show
#task_list = #listable.task_lists.find(params[:id])
end
def load_listable
klass = [Event, Meeting].detect { |c| params["#{c.name.underscore}_id"]}
#listable = klass.find(params["#{klass.name.underscore}_id"])
end
One of the key issues here is that task_items are nested a level too deep.
Rule of thumb: resources should never be nested more than 1 level
deep. A collection may need to be scoped by its parent, but a specific
member can always be accessed directly by an id, and shouldn’t need
scoping.
- Jamis Buck
So to untangle this we would declare the routes like so:
concern :has_task_lists do
resources :task_lists, only: [:new, :index, :create]
end
resources :events, :meetings, concerns: [:has_task_lists]
resources :task_lists, except: [:new, :index, :create] do
resources :task_items
end
The routing concern lets us re-use the nested routes.
Since we have gotten rid of the extra "parent" we can simplify the form:
form_for([ #task_list, #task_item || #task_list.task_items.new ])
#task_item || #task_list.task_items.new will prevent errors if you embed the form in another controller / action.
Added
You actually have to create the resource scoped to the parent. Just doing form_for([#a, #b]) does not automatically associate the records - it just generates the url and sets the form method (POST or PATCH).
class TaskItemsController < ApplicationController
before_action :set_task_list
def new
#task_item = #task_list.task_items.new
end
def create
#task_item = #task_list.task_items.new(task_item_params)
if #task_item.save
redirect_to task_list_path(#task_list), notice: "Task Item Created"
else
render :new
end
end
private
def set_task_list
#task_list = TaskList.find(params[:task_list_id])
end
# ...
end
When you call #task_list.task_items.new you are calling the .new method on the collection - so the new TaskItem will have the task_list_id column set.
Note that you can also write redirect_to #task_list and rails will figure the rest out. If you need to redirect to a nested resource such as the newly created item you would do:
redirect_to [#task_list, #task_item]
Any help and advise would be much appreciated.
i have a company model that has many recruiters (users) &
a recruiter (user) belonging to a company
also recruiters belong to a gender & gender has many recruiters
using the GEM ransack, i am trying to display all male recruiters(users) in a
company but i am unsure of the code needed for the 'recruiters' action in the companies controller - anyway help & advise would be much appreciated. Below are my files:
Models:
user.rb
class User < ActiveRecord::Base
belongs_to :company
belongs_to :category_gender
end
company.rb
class Company < ActiveRecord::Base
has_many :users
end
category_gender
class CategoryGender < ActiveRecord::Base
has_many :users
end
Companies_controller.rb
class CompaniesController < ApplicationController
respond_to :html, :xml, :json
before_action :set_company, only: [:show, :edit, :update, :destroy]
# load_and_authorize_resource
........
def recruiters
#search = Company.search(params[:q])
#companies = #search.result.includes(:users)
#user = current_user
#company = #user.company
end
end
routes.rb
Rails.application.routes.draw do
resources :companies do
resources :comments, only: [:create, :destroy]
member do
get 'recruiters'
end
end
devise_for :users
resources :users do
resources :comments, only: [:create, :destroy]
resources :adverts
resources :blogs
member do
get 'applications'
get 'applicants'
get 'settings'
get 'dashboard'
get 'management'
end
end
recruiters.html.erb (views file):
<%= search_form_for #search, url: recruiters_company_path do |f| %>
<div>
<h3>gender</i></h3>
<ul>
<% CategoryGender.all.each do |gender| %>
<li>
<%= check_box_tag('q[category_gender_name_eq_any][]', gender.name) %>
<%= gender.name %> (<%= gender.users.count %>)
</li>
<% end %>
</ul>
</div>
<button><%= f.submit 'search' %></button>
<% end %>
Since you are searching for Users. Then, .ransack method should be called on User model and not on Company model.
Try the following in your recruiters method:
def recruiters
#company = Company.find(params[:id])
#users = User.ransack(params[:q]).result.where(company: #company)
#user = current_user
end
1st line: I used #company = Company.find(params[:id]) instead of #company = #user.company because you are using resources pathing for Company, in which recruiter method is a "member" of (as you defined in your routes.rb).
The problem with #user.company is that say for example a user browses /companies/1/recruiters, the #company that will be assigned is going to be the same as when browsing /companies/3/recruiters. But by convention, #company in these two paths should already be different because of the id changing.
2nd line: I just added a the predicate .where(company: #company) so that the results will be filtered to the respective company.
Then in your recruiters.html.erb view file, you could just add something like the following:
...
...
<% #users.each do |user| %>
<%= user.name %>
<% end %>
...
...
Im getting this weird error when i want to update my model (model name carts).
Error : The action 'update' could not be found for CartsController
this is my carts_controller.rb :
class CartsController < ApplicationController
include CartForcable
before_action :scoped_cart, only: [:show, :update]
def show
end
private
def scoped_cart
force_cart! lambda {|r| r.includes(:entries => {:sign => [:dimensions, :substrates]})}
end
def update
#cart = #cart.find(params[:id])
if #cart.update_attributes(cart_params)
flash[:notice] = translate 'flash.notice'
else
flash[:error] = translate 'flash.error'
end
support_ajax_flashes!
respond_to do |format|
format.html # renders view
format.json { render json: #entry }
end
end
end
and these are my routes.rb :
resources :categories, only: [:index] do
resources :signs, shallow: true, only: [:index]
end
resources :carts, only: [:show, :update]#, param: :cart_permalink
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
resources :cart_entries, only: [:index, :create, :update, :destroy], as: 'entries', path: 'entries'
end
resource :user, only: [:edit, :show, :update], as: 'current_user', path: 'profile'
resources :signs, only: [:show]
resources :pages, only: [:show], param: :permalink
and my show.html.erb has this form :
<%= form_for #cart, :url => {:controller => "carts", :action => "update" } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :regnum %>
<%= f.text_field :regnum %></br>
<%= button_to "Order/update", {:controller => "carts",:action =>
'update', :id => #cart.id }, :method => :update %>
<% end %>
After i enter name or number (for example), and click update button or something, it doesnt upload any data into model carts(which has the right columns).
Thanks,
Michael
Your update method is private; controller actions need to be public. Move
def update
#cart = Cart.find(params[:id])
...
end
to be above the
private
line. You need to do the find on the Cart model, not a #cart instance.
You can use a show action although edit would be more standard and fits right in with the RESTful routes without the need to override convention. The Rails routing guide should help here: http://guides.rubyonrails.org/routing.html
Additionally, you need to define the value of #cart in your edit action:
def edit
#cart = Cart.find(params[:id])
end
This will ensure it has a value on the edit form and so comes back into your update action through the parameters.
So if you try to edit a cart with something like /carts/12345/edit (where 12345 is the id of the cart you want to update) it should all hang together.
This looks wrong:
#cart = #cart.find...
don't you mean...?:
#cart = Cart.find...
And it seems you are using "cart_permalink" instead of "id" on your routes
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
Check the server log and see the name of the parameter that holds the ID, also run "bundle exec rake routes" to double check.
I am new to rails and am trying create a forum. The forum has many topics, topics belong to a forum and have many microposts, and microposts belong to both topics and users. However, no matter what I try, the posts will not be created. Currently when I try to post, I get the routing error "No route matches [GET] "/topics""
My routes.rb file:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy]
resources :forums, only: [:index, :show]
resources :topics, only: [:show]
_micropost_form.html.erb
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.hidden_field :topic_id, value: #topic.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.text_field :summary, placeholder: "One-line summary..." %>
<%= f.text_area :content, placeholder: "Compose a new post..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
microposts_controller.rb
class MicropostsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
##topic = Topic.find_by_id(params[:topic_id])
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Your solution has been posted!"
redirect_to topic_path(#topic)
else
redirect_to topic_path(#topic)
end
end
def destroy
#micropost.destroy
redirect_to root_url
end
private
def micropost_params
params.require(:micropost).permit(:summary, :content, :user_id)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end
end
As you can see, I commented out the first line in my create function because I've tried posting based on the the micropost's relationship to the topic to no avail. Thanks in advance and let me know if it would help if I posted more code!
In your :topics resource, you didn't defined the index method, that's why you won't be able to get to topic's list or index page. Try to change your route like this:
resources :topics, only: [:index, :show]
or remove only attribute from resources, it will automatically include all your methods by default.
resources :topics
Also if you have relationship between models, you should define nested routes in your routes
file, For example, you can define them like this, you can change them accordingly:
try to change your route file like this:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :forums do
resources :topics do
resources :microposts, only: [:new, :create, :destroy]
end
end
In above case, you can access your forums like this:
http://localhost:3000/forums
you can access your topics like this:
http://localhost:3000/forums/id/topics
you can access your microposts like this:
http://localhost:3000/forums/id/topics/id/microposts
If you want to access /microposts directly you have to put it outside any resource.
resources :microposts, only: [:index]
now you will be able to access it:
http://localhost:3000/microposts
Hope it will help. Thanks.