How do I add a "New" button to ALL ActiveAdmin show pages? - ruby-on-rails

When creating objects in ActiveAdmin I typically need to add multiple, and wish there was an option to add another object on the show page (which appears after submitting the new object).
I have been doing this model by model:
ActiveAdmin.register Color do
action_item :add, only: :show do
link_to "New", new_administration_color_path
end
end

Add this to admin/administration_color.rb
controller do
def create
create! do |format|
format.html { redirect_to admin_administration_color_path(resource, add_more: true) }
end
end
end
and some modified your code
action_item :add, only: :show do
link_to "New", new_administration_color_path if params['add_more'] == true
end

Related

ActiveRecord::RecordNotFound in PermitsController#show, Couldn't find Permit with 'id'=

Hi guys I keep get this error when i try to access to SHOW action(Manage permit button on user/show.html.erb) which should display all the permits of a specific user, i search through all my code and couldn't find the bug. Can you guys help me to check which part i did wrong? Btw I'm implementing a website using Ruby on rails
This is my permits_controller.rb
class PermitsController < ApplicationController
before_action :set_permit, only: [:show, :destroy]
def index
#permits = Permit.where(:user_id => current_user.id)
end
def new
#permits = Permit.new
end
def create
#permits = current_user.permits.build(permit_params)
if #permits.save
redirect_to invoice_path
else
render 'new'
end
end
def destroy
Permit.destroy_all(user_id: current_user)
respond_to do |format|
format.html { redirect_to root_path, notice: 'Permit was successfully canceled.' }
format.json { head :no_content }
end
end
def confirm
#fields = %i[vehicle_type, carplate, studentid, name, department, permitstart, permitend]
#permit = current_user.permits.build(permit_params)
render :new and return unless #permit.valid?
end
def show
#permits = Permit.where(:user_id => current_user.id)
end
def update
#permits = Permit.where(user_id: current_user).take
respond_to do |format|
if #permits.update(permit_params)
format.html { redirect_to root_path}
flash[:success] = "Permit successfully updated"
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def edit
#permits = Permit.find(params[:id])
##permits = Permit.find_or_initialize_by(user_id: params[:id])
end
private
# Use callbacks to share common setup or constraints between actions.
def set_permit
#permits = Permit.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def permit_params
params.require(:permit).permit(:vehicle_type, :name, :studentid, :department, :carplate, :duration, :permitstart, :permitend)
end
end
This is my permits/show.html.erb
<%= #permits.name %>
This is my route.db
Rails.application.routes.draw do
resources :users
resources :permits do
collection do
post :confirm
end
end
resources :visitor_permits
root 'static_pages#home'
get 'invoice' => 'permits#invoice'
get 'payment' =>'transaction#new'
get 'show_visitor_permit' =>'visitor_permits#show'
get 'show_permit' =>'permits#show'
get 'visitorpermit' => 'visitor_permits#new'
post 'createpermit' => 'permits#create'
get 'homepage/index'
post 'permits' => 'permits#create'
get 'permitapplication' => 'permits#new'
get 'adminlogin' => 'admin_controller#index'
get 'patrollogin' => 'patrol_officer_controller#index'
get 'createcitation' => 'citations#new'
get 'contact'=> 'static_pages#contact'
get 'about' => 'static_pages#about'
get 'signup' => 'users#new'
get 'help' => 'static_pages#help'
post 'users' => 'users#create'
get 'login' => 'sessions#new' #Page for a new session
post 'login' => 'sessions#create' #Create a new session
delete 'logout'=>'sessions#destroy' #Delete a session
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
users/show.html.erb
<!DOCTYPE html>
<html>
<body>
<div id="sidebar">
<ul id="mySidenav" class="sidenav">
<li><%= link_to "New Parking Permit", permitapplication_path %></li>
<li><%= link_to "Manage Permit", show_permit_path(#permit) %></li>
<li><%= link_to "New Visitor Parking Permit", visitorpermit_path %></li>
<li><%= link_to "Manage Visitor Permit", "#" %></li>
<li><%= link_to "Check Fines", "#" %></li>
<li><%= link_to "New Health and Safety Report", "#" %></li>
<li><%= link_to "Manage Health and Safety Report", "#" %></li>
</ul>
</div>
</body>
</html>
First, you can remove the :show method in before_action as it will invoke two queries.
Then if you add where condition in active record query (like below), it will results the array of records instead of single record.
#permits = Permit.where(:user_id => current_user.id)
So you can not directly put the below line in your view as array of objects will not permit access the name parameter directly.
<%= #permits.name %>
May be you can iterate all the records like below and populate the name of every Permit object.
<% #permits.each do |permit| %>
<%= permit.name %>
<% end %>
Or you can do some thing like below to show it as comma separated.
<%= #permits.map { |f| f.name }.join ',' %>
If you want the first object alone in the #permits you can use like below.
<%= #permits[0].name %>
P.S: Note that it will throw an exception if the first object doesn't exist.
In PermitsController you have have wrote the before action to set the permits
before_action :set_permit, only: [:show, :destroy]
def set_permit
#permits = Permit.find(params[:id])
end
So, while setting permits if permit is not present with specific id in DB it will raise an exception ActiveRecord::RecordNotFound
you are also setting permits in show action
def show
#permits = Permit.where(:user_id => current_user.id)
end
not sure why you are setting permits twice for show action.
- first time in set_permit method
- and second time in show method
Can you please add parameters in question which are passing with the request.
Assigning record twice is not good practice. In your case in before action you are finding Permit by id and if record for that specific id is not present in the database then it will raise a ActiveRecord::RecordNotFound exception. so you have to check what you are passing in params[:id].
If you want to set permits based on the current user you can skip set_permit for show action.
And in show action if you are assigning activerecord collection instead of active record in that case in view you have to iterate over the collection object to print the permit details. Or if there is only one permit for user you can set active record object in show action instead of active record collection

Rails 4 form_for nested resources issue

I have researched similar questions however I don't feel link they have addressed my particular issue:
Rails form_for results in POST instead of PUT when trying to edit
form_for with nested resources
I'm a novice with Rails (using Rails 4.2.5) an am attempting my first application. My issue is two fold: (1) When a user goes to edit a user story the fields of the form do not populate with previously inputted data (2) When the form is resubmitted, a new entry is created, opposed to editing the old data.
I have a feeling that my form_for for user_stories/edit.html.erb is the issue. When I take out the .build method from the form I get the following error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
The projects/_form.html.erb for my project's view does not have the .build method and functions correctly. However the only way I can get the `user_stories/_form.html.erb form to work is if I attach the build method.
Here is my code:
user_story.rb
class UserStory < ActiveRecord::Base
belongs_to :project
belongs_to :user
include RankedModel
ranks :row_order
end
project.rb
class Project < ActiveRecord::Base
has_many :user_stories
belongs_to :user
end
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :projects do
resources :user_stories
end
end
resources :user_stories do
post :update_row_order, on: :collection
end
root 'welcome#index'
end
user_stories/_form.html.erb
<%= form_for([#project, #user_story.build]) do |f| %>
<div class="form-group">
<p>As a ...</p>
<%= f.text_field :param1, placeholder: "type of user", class: "form-control" %>
</div>
<div class="form-group">
<p>I want ...</p>
<%= f.text_field :param2, placeholder: "desired functionality", class: "form-control" %>
</div>
<div class="form-group">
<p>so that...</p>
<%= f.text_field :param3, placeholder: "reason for desired functionality", class: "form-control" %>
</div>
<div class="actions">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
user_stories_controller.rb
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:create]
def index
#user_story = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deletd"
end
redirect_to #project
end
def complete
user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
#user_story = #project.user_stories(params[:id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end
There are just a few changes needed (tweaks, really), and I'll go through them top-to-bottom.
1) before_action :set_user_story
This will use the param[:id] to find the proper #user_story model object and automatically make it available to the proper methods. In this case it's being excepted for :create, but should also exclude other methods that don't have an :id in the route. Use this instead:
before_action :set_user_story, except: [:index, :new, :create]
This will solve (or prevent) some annoying and persistent ActiveRecord failures.
2) The index action
In this method, the name of the variable is non-standard by Rails naming conventions. The variable is currently singular, but represents a list of UserAction model object, which typically uses a plural name. Use this, instead:
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
This change will cause a break in the app/views/user_stories/index.html.erb view, where any use of the #user_story variable would need to be changed to #user_stories. Keeping with naming conventions has many immediate and long-term benefits, so it's worth making the extra effort to change this to be consistent.
Note: the index action typically doesn't have a singular model object to work with, as this action is used to provide a list of the model objects.
3) The new action
The new action is used to create and initialize a new model object for editing. As the before_action :set_user_story is no longer being called for the new action, the #user_story model object has to be created here. This code will do that correctly:
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
And at this point, you should be able to successfully create a new UserStory model object, ready to be edited by the user.
4) The edit action
As the before_action :set_user_story handler is already being called for the edit action, there's no need to query for #user_story from within the body of the edit action; that line can be removed:
def edit
#project = Project.find(params[:project_id])
end
This will actually fix the original issue that was reported, as this form of find will (unfortunately for this situation) return multiple records, which means that you get a collection back, and not a single record. This is the actual cause of this error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
Assigning the #user_story within the edit action overwrote the value that had previously been assigned from the before_action handler, and replaced it with an improper query result.
5) The complete action
The complete action is a custom member action, which means that it depends on the :id, just like many of the other actions. The code is almost correct, except that the user_story variable used within the body of the method is actually missing the #; this is originally retrieved by the before_action handler.
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
It's likely that this method had not been called yet during testing, as the edit action was an upstream test that failed. This should work when you get to testing this method.
6) Teh codez
Changing those few details will finalize the UserStoriesController, which was in pretty great shape to begin with. Adding in those changes, this is the final controller code:
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:index, :new, :create]
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deleted"
end
redirect_to #project
end
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end

ActiveAdmin actions

is there a way to specify in ActiveAdmin's index page of a model what actions are allowed, things like:
index do
actions :edit
end
index do
actions only: :edit
end
do not work. What's the correct syntax?
Appreciated.
bundle show activeadmin
/home/muichkine/.rvm/gems/ruby-2.1.2/bundler/gems/active_admin-9cfc45330e5a
Add whatever actions you want to be available by using actions (it is usually put under model definition):
ActiveAdmin.register YourModel do
actions :index, :show, :create, :edit, :update
If you want to specify the method for certain action, you can do
action_item only: :show do
link_to 'Edit', action: :edit # so link will only be available on show action
end
Example how to play with the action column. In this example I just re-implemented the default one, but you can do powerful coding here:
column :actions do |item|
links = []
links << link_to('Show', item_path(item))
links << link_to('Edit', edit_item_path(item))
links << link_to('Delete', item_path(item), method: :delete, confirm: 'Are you sure?')
links.join(' ').html_safe
end
Do this way,
ActiveAdmin.register Foobar do
actions :all, :except => [:destroy]
end
or
ActiveAdmin.register Foobar do
actions :only => :edit
end
Need to be specified at resource level not in method definition
According to source code, https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/views/index_as_table.rb#L80
if one want to change the actions in the index he should go with
actions defaults: false do |sample|
link_to t('active_admin.edit'), admin_sample_path(sample)
end
where you can replace the link title and the path for the action
For Example:
actions defaults: false do |user|
link_to t('active_admin.view'), admin_user_path(user)
end
Note:
Keep in mind that add the path correctly like for show it should be admin_user_path(:id) and for index it should be admin_users_path :)

Routing Error: No route matches [PUT] "/goals/4/edit"

I am trying to create a button to change a model record attribute from false to true. I'm using a form_tag as follows:
=form_tag edit_goal_path(goal), method: :post do
=hidden_field_tag :purchased, value: true
=submit_tag "Purchase"
It's haml, but feel free to post suggestions with ERB. I'm getting the following error:
No route matches [POST] "/goals/4/edit"
Rails.root: /home/ben/rails_projects/hartwig
However, I already have the following route from resources:
PUT /goals/:id(.:format) goals#update
My controller looks as following:
def edit
#goal = Goal.find(params[:id])
end
def update
#goal = Goal.find(params[:id])
if #goal.update_attributes(goal_params)
redirect_to '/goals', notice: "Update successful!"
else
render '/'
end
end
def goal_params
params.require(:goal).permit(:item, :description, :picture, :purchased)
end
How do I get this to work? Or is there a better way to solve this?
Your question says:
I am trying to create a button to change a model record attribute from false to true
so why are you using a form for it? I think a better approach would be to create a link or button that will call an ajax method or a normal method with post route and update your attribute. You can achieve it by following these steps:
a. Create a route for your custom action where you'll update your attribute:
post 'purchase_update/:id' => "goal#update_purchase", as: update_purchase #post as you want to send your goal id
b. create your custom method inside your controller:
def update_purchase
#goal = Goal.find(params[:id])
#goal.update_attribute(:purchased, true)
respond_to do |format|
format.html {redirect_to your_path, notice: 'purchase updated'}
format.js {} #if you want to do something by ajax
end
end
c. Create your link that will call this method:
=link_to "Purchase", update_purchase_path(#goal), method: post
and if you want to do it by ajax then
=link_to "Purchase", update_purchase_path(#goal), method: post, remote: true
Another solution to your problem could be adding a new method to the Goal Controller:
in goals_controller.rb
def purchase
#goal.update_attribute(:purchased, true)
end
and also add on top (just add :purchase)
before_action :set_goal, only: [:show, :edit, :update, :destroy, :purchase]
in routes.rb change to
resources :goals do
member do
post 'purchase'
end
end
to add a new post routes to your goals
now you will have a purchase_goal_path that you can use in your view like this:
link_to 'Purchase', purchase_goal_path(#goal), method: :post

How do I update at attribute on multiple models in Rails?

I would like to perform this action with the click of a link.
def index
#books = Book.all
end
def update_read_books
#books.each do |book|
book.update_attribute(:read, true)
end
end
How can I update mark all books as read?
Rails has an update_all method. See link.
def mark_as_read
Book.update_all(read: true)
redirect_to books_path
end
Setup up route to give you /books/mark_as_read
resources :books do
get :mark_as_read, on: :collection
end
Then in your view:
= link_to "Mark all as Read", mark_as_read_books_path
If you really want to be Restful, you can make your route a put/patch method. Don't forget to change your link to the method you choose.
If you want this to be an Ajax request, you can add a remote: true to the link:
= link_to "Mark all as Read", mark_as_read_books_path, remote: true
That will make it async. Then you need to handle that response in the controller.
def mark_as_read
Book.update_all(read: true)
respond_to do |format|
format.html { redirect_to books_path }
format.js
end
end
... and add a template inside /views/books/update_all.js.erb and add some jQuery to remove the notification. For example:
$('#notification_count').hide();
First of all define your method outside index method.
def index
#books = Book.all
end
def update_read_books
Book.update_all(read: true)
end
define route:
resources :books do
put :update_read_books, on: :collection
end
Then in your view:
= form_for update_read_books ,:remote => true do |f|
= f.submit "All Read"
Try with this. Hope it will help.

Resources