Updating Roles in rolify using ActiveAdmin - ruby-on-rails-4

Using Rails 4.0, ActiveAdmin, Rolify and CanCan, Adding and removing user roles via the activeadmin panel does not save (commit to database).
The my ActiveAdmin User and User Model look okay as I can list all the roles that apply to a user using check_boxes. Although when adding any roles or removing any roles via the checkboxes the changes do not get applied.
I get a notification that the user was updated successfully but as I look through the database or render the page, the roles have not been updated.
How can I get the roles to update when the form is saved?
Edit:
Using Devise also.

The solution here is to allow the ActiveAdmin controller to update the role ids related to the user.
ActiveAdmin.register User do
permit_params :email, :password, :password_confirmation, role_ids: []
Here's a form that shows a check box for each global role.
form do |f|
f.inputs "User Details" do
f.input :email
f.input :password
f.input :password_confirmation
f.input :roles, as: :check_boxes
end
f.actions
end
While we're at it, we might as well make it possible to update the user without entering their password:
# Allow form to be submitted without a password
controller do
def update
if params[:user][:password].blank?
params[:user].delete "password"
params[:user].delete "password_confirmation"
end
super
end
end
Put all of this in the app/admin/user.rb.

I just ran into this issue. The problem was with validation of presence (which I think is occurring in the database ":null => :false"). In order for the update to save, I had to fill out all fields (including password/password confirmation).

I had to add some controller code to make this work, I hope this helps:
Remember to permit the attributes you're accepting in active_admin as such (this will create the permitted_params method I'll be referring to in a bit.
Note that since we're not saving role_ids directly to the user, it doesn't even have to be a permitted param.
permit_params :email, :password, :etc
Create a private method we could call from the create and update controllers. This method will just iterate over non-empty ids, find the associated roles, and add them to user.roles.
The create and update methods simply find call the add_roles method prior to continuing execution. ActiveAdmin makes the the existing user available through resource, but not through create. We have to find it ourselves in create!
controller do
def create
#user = User.new(permitted_params[:user])
add_roles(#user)
create!
end
def update
add_roles(resource)
update!
end
private
def add_roles(resource)
resource.roles = []
params[:user][:role_ids].each { |r| resource.roles.push(Role.find(r)) unless r.blank? }
end
end
In the activeadmin form, you can add:
form do |f|
# other inputs
f.input :roles, as: :select, multiple: true, collection: Role.all
end
And in the show page, you can display the roles as such:
show do
attributes_table do
# other rows
row :roles do |r|
r.roles.map { |role| role.name }.join(", ")
end
end
Hope this helps!

Related

Rails 4.2+: Mapping PostgreSQL JSONB nested values to form fields

I have a users table with a settings field of type JSONB (using PostgreSQL 9.5).
I'm trying to create a form on a settings page to update user.settings["notifications"][...] values.
class User < ActiveRecord::Base
end
user = User.create(settings: { notifications: { post_created: true } })
user.settings["notifications"]["post_created"] # => true
To map the nested JSONB values to a form, however, I have to do this:
# views/form.html.erb
<input type="check" name="user[settings][notifications][post_created]" checked="<%= current_user.settings['notifications']['post_created']" %>
class SettingsController
def update
current_user.settings["notifications"]["post_created"] = params["user"]["settings"]["notifications"]["post_created"]
current_user.save
end
end
Is there anyway to utilize the power of Rails form builders such that I can do:
# will not work, undefined attribute settings['notifications']['post_created']...
<%= form_for current_user, url: settings_path, method: "PUT" do |f| %>
<%= f.check_box "settings['notifications']['post_created']" %>
<% end %>
I understand that Rails is trying to map an attribute from the current_user object, and there isn't really an "attribute" named settings['notifications']['post_created'].
But how does one go about mapping nested JSONB values to a form field for CRUD activity?
A workable (but not really feasible) approach is to created virtual attributes for every single nested value I want to work with:
class User
def settings_notifications_post_created
settings["notifications"]["post_created"]
end
def settings_notifications_post_created=(value)
settings["notifications"]["post_created"] = value
end
end
# view...
<%= f.check_box :settings_notifications_post_created %>
But this loses any benefit of a conventional system since I'm manually typing out every attribute. May as well write raw HTML fields and all the getter/setter methods myself...Googling and Stack Overflow haven't been very helpful so far, it seems there aren't very many with experience doing this kind of stuff yet...

Activeddmin i18n. Translate form fields values

I'm ussing activeadmin (1.0.0.pre2).
In my model, I have fields, filled with tags. I translate this tags in the app using the method "translate_name" in form field values (like this .. i18n for select boxes)
In activeadmin I'm using custom form to choose only some fields.
This is my activeadmin code...
index do
column :user
column :user_surname
column :service
column :patient_type
column :description
actions
end
form do |f|
if f.object.errors.size >= 1
f.inputs "Errors" do
f.object.errors.full_messages.join('|')
end
end
f.semantic_errors # shows errors on :base
f.inputs :user
f.inputs :service
f.inputs :patient_type
f.inputs :postal_code
f.inputs :availability
f.inputs :date
f.inputs :estimated_period
f.inputs :description
f.actions # adds the 'Submit' and 'Cancel' buttons
end
service y patient_type are nested resources exactly like this ActiveAdmin customizing the form for belongs_to
activeadmin don't traslate this tags.
Any idea to solve this?
Thanks.
In Formtastic you can define a to_label method on your models which will be
used when rendering the select field options. Also, ActiveAdmin will call a
variety of methods when searching for a name for your object, like
display_name. See Index as a Table docs.
Adding to_label and display_name methods to your Service class should
properly translate the values highlighted above:
class Service < ActiveRecord::Base
def translated_name
I18n.t(name)
end
# method used when creating Formtastic select options
def to_label
translated_name
end
# method used for displaying model names in ActiveAdmin
def display_name
translated_name
end
end
ActiveAdmin checks objects for the following methods to show a name. Most often
the name method is used.
display_name
full_name
name
username
login
title
email
to_s

ActiveAdmin specify filter form url

I am using active-admin(AA, 1.0.0) in a rails 4.2 app. I am displaying a list of online users. I have to display a list of users with scopes for each type of user and filters as per the User model fields. Also, I need to display a list of online users on another page.
User(dropdown)
=> Accounts
=> Online Users
To display the users list, i have
ActiveAdmin.register User, as: 'User' do
menu :parent => "Users", :label => "Accounts"
...
end
To display the online users list, i have
ActiveAdmin.register User, as: 'Online User' do
menu parent: "Users", label: "Online Users", url: '/admin/online_users'
actions :index
filter :id
filter :name
filter :email
...
end
Now,the filters are getting displayed properly on the "Online Users" page, but when the filter form is submitted, its submitted to "/admin/users" rather than to "/admin/online_users". Can I pass the url to the filter form? If not, what is the right solution to this problem.
Thanks.
You can redefine the collection_path method which is used by the filter form builder like this:
controller do
def collection_path
admin_online_users_path
end
end

Rails 4 - Update Join Table Attributes with has_many through

This is a follow-up of my question: Rails 4 - Access Join Table Value in views
Now I know how to retrieve join table attributes and show them in view. But still I can't find the right approach to edit and update the attribute.
Models:
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
has_many :ingredients, through: :recipe_ingredients
end
class Ingredient < ActiveRecord::Base
has_many :recipe_ingredients
has_many :recipes, through: :recipe_ingredients
end
class RecipeIngredient < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
#there is an attribute t.text :amount
end
Recipe#Edit:
...
<%= simple_form_for #recipe do |f| %>
<div class="form-group">
<%= f.input :name, input_html: { class: "form-control"} %>
<%= f.input :description, input_html: { class: "form-control"} %>
<%= f.collection_select(:ingredient_ids, Ingredient.all, :id, :title, {}, {multiple: true}) %>
<!-- reserve for recipe_ingredients.amount -->
</div>
<%= f.submit "Submit", disable_with: 'Submitting...', class: "btn btn-primary"%>
<% end %>
...
As shown above, this is a many-to-many relationship, each recipe may have several ingredients. It works fine and I can choose the right models to associate with (using collection_select). But I have left the work to edit and update join table attributes undone. I have two ideas in my mind:
Make another edit page for editing join model attributes
Ajax? But not quite familiar with
I know the question seems to be trivial, but many solutions are out-dated(Rails 3). Desperate for a Rails 4 solution.
Edit
Intention: I would like to make the creation of Recipe and Ingredient models on their own, which is done. User will see which recipe using the ingredient in Ingredient#show action, which is done also. As to Recipe#show action, there is a slight difference, where user will see which ingredient it is using as well as the amount(join table attribute), which is done in my previous question. The last problem is, I would like to make this attribute editable.
You may want to look into doing nested models combined with the cocoon gem. This will allow you to have a form which updates multiple models at once and it takes care of all the ajax for you. https://github.com/nathanvda/cocoon. I used this gem on a system where the user needed to be able to add one or more answers to a trivia question via dynamic form fields. I think it could work for you as well with giving the user the ability to add one or many RecipeIngredient relations for a recipe. You would be able to add selects dynamically to your form via Cocoon containing a select with the Ingredient model records.

Rails Cocoon Delete/Destroy Single Instance

I am using Cocoon gem to associate resume fields to one resume, and each user has ONE resume and have everything working the correct way via cocoon documentation. However, I would like the ability for users to be able to edit resume fields/nested models directly on the profile page without being redirected to the cocoon nested form page. Further more I would like users to have the ability to add and or delete a SINGLE field/entry. Is there any way at all to accomplish this?
In my console I have been able to successfully delete an entire resume nested model using
User.last.resume.resume_edus.destroy_all
Where 'resume.edus' is the nested model inside of resume complete with 3 other 'text_fields'. But as stated I would only like to be able to edit/delete a single instance of resume_edus. Any ideas?
User.rb
has_one :profile
has_one :resume
Resume.rb
belongs_to :user
has_many :resume_edus
accepts_nested_attributes_for :resume_edus,
reject_if: :all_blank,
allow_destroy: true
Resume_edu
belongs_to :resume
Resume Controller
params.require(:resume).permit(:user_id, :cover,
resume_edus_attributes:
[:id, :title, :date, :description, :_destroy])
Figured out the answer! I passed the resume_edus 'ID' in the delete action
<%= link_to "Delete", resume_path(r.id), method: :delete %>
Then was able to search for said ID in the current users resume_edu modal to specifically isolate and delete it without deleting the other instances of the model!
def destroy
#instance = current_user.resume.resume_edus.find(params[:id])
debugger
#instance.destroy
flash[:notice] = "Resume Field Was Deleted"
redirect_to profile_path(current_user)
end
accepts_nested_attributes_for :order_line_items, allow_destroy: true
<tr class="nested-fields">
<%= f.hidden_field :_destroy %>
<%= link_to_remove_association "Delete", f %>
</tr>
Add _destroy in controller params:
order_line_items_attributes: %i[id product_id quantity price discount total _destroy]