ActiveAdmin customize view for has_many :through - ruby-on-rails-3

I'm working on a ActiveAdmin app with this models :
User
class User < ActiveRecord::Base
# A User has many roles for interact on a project
has_many :roles, :dependent => :destroy
has_many :projects, :through => :role
end
Role
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
Project
class Project < ActiveRecord::Base
# A project has many roles for interact
has_many :roles, :dependent => :destroy
has_many :users, :through => :role
accepts_nested_attributes_for :roles
end
To add users with a role on each project I make this form :
form do |f|
f.inputs "Details" do # Project's fields
f.input :title
f.input :code
end
f.has_many :roles do |app_f|
app_f.inputs do
if !app_f.object.nil?
app_f.input :_destroy, :as => :boolean, :label => "Destroy?"
end
app_f.input :user
app_f.input :senior_author
end
end
f.buttons
end
My first question is how can I make a with user.firstname + user.lastname. Actually I have something like this :
#<User:0x007fb98a7d6568>
Second question is my Role model is a list of boolean attributes :
:senior_author
:first_author
:viewer
....
Can I make a with that ?

Another solution would be to just define to_s in the model:
def to_s
"#{email} | #{firstname} #{lastname}"
end
No need to set :label_method.

Just add :label_method => lambda:
app_f.input :user, :label_method => lambda{|u| "#{u.email} | #{u.firstname} #{u.lastname}" }

I fix it by adding this method to models/user.rb
# format label for formtastic dropdown menu
def to_label
"#{email} | #{firstname} #{lastname}"
end
And I use it like this :
app_f.input :user, :include_blank => false, :label_method => :to_label

Related

ActiveAdmin Ratyrate unable to find current_user OR Alternative

I have been trying to integrate raytrate into ActiveAdmin when trying to include the partial into my Activeadmin supply_company.rb it apears to be unable to find the 'current_user'. I know that with a standard install of ActiveAdmin and devise I have left the admin user as admin_user so should be able to use current_admin_user to set the current user. I have a suspicion it the way in which I'm trying to include the render in the sidebar.
Or if anyone has an alternative model for rating that they know works in ActiveAdmin I would gladly take a look at it.
The error message is
ActionView::Template::Error (undefined local variable or method `current_user' for #<#:0x6b50a58>):
app/controllers_rater_controller.rb
class RaterController < ApplicationController
def create
if admin_user_signed_in?
obj = params[:klass].classify.constantize.find(params[:id])
obj.rate params[:score].to_f, current_admin_user, params[:dimension]
render :json => true
else
render :json => false
end
end
end
app/models/admin_user.rb
class AdminUser < ActiveRecord::Base
rolify
has_one :profile, foreign_key: :admin_user_id
accepts_nested_attributes_for :profile
has_many :addresses
has_many :address_types, :through => :addresses
accepts_nested_attributes_for :addresses
ratyrate_rater
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb =>
"100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
end
app/models/supply_companies.rb
class SupplyCompany < ActiveRecord::Base
has_many :products, :through => :product_supply_companies
has_many :product_supply_companies, :foreign_key => 'supply_company_id'
accepts_nested_attributes_for :products
accepts_nested_attributes_for :product_supply_companies, :allow_destroy => true
ratyrate_rateable "communication", "quality", "price"
end
/app/admin/supply_company.rb
ActiveAdmin.register SupplyCompany do
permit_params :id, :company_name,
products_attributes: [:id, :product_name, :product_description],
product_supply_companies_attributes: [:id, :product_id],
supply_company_ratings_attributes: [:id, :admin_user_id, :supply_company_id, :supply_company_rating ],
admin_user_attributes: [:id]
index do
column :id
column :company_name
column :products do |pt|
pt.products.collect {|c| c.product_name.capitalize }.to_sentence
end
actions
end
filter :company_name
form(:html => {:multipart => true}) do |f|
f.inputs "Company Details" do
f.input :company_name
end
f.actions
end
show title: :company_name do
attributes_table do
row :company_name
end
end
sidebar "Products", only: :show do
attributes_table_for supply_company do
row "Ratings" do
render 'supply_company_ratings'
end
row :products do |pt|
pt.products.collect {|c| link_to c.product_name.capitalize, admin_products_path + "\/" + c.id.to_s}.join(", ").html_safe
end
end
end
end
app/views/admin/supply_companies/_supply_companines_ratings.html.erb
<h2>Communication :</h2> <%= rating_for #supply_company, "communication" %>
<h2>Engine :</h2> <%= rating_for #supply_company, "quality" %>
<h2>Price :</h2> <%= rating_for #supply_company, "price" %>
Sound's like you have installed ratyrate with rails g ratyrate user, you need to install it with rails g ratyrate admin_user.

Nested Form: Can’t populate join table between parent and child if child exists / Couldn't find Child with ID=1 for ParentChildJoin with ID=

I cleaned up my code, it looks much nicer now, but still doesn’t work. It starts to be a pain…
I just can’t save a parent with an existing child in nested form with parent has_many childs :through joinmodel.
In my case a Project has_many contributing Teachers and many contributing Pupils, both are Join-Models to Users. A Project has_many Schools as well.
(May be I should better name the models Teacherize and Pupilize or ProjectTeacher and ProjectPupil.)
As long as all records are new it all works fine. As soon as I want to connect an existing User as new Teacher of a new Project I get the following error:
Couldn't find User with ID=1 for Teacher with ID=
(1 is the correct user ID)
The problem should be somewhere her in my helper to setup empty form fields:
At least I guess so...
module ProjectsHelper
def setup_project(project)
if project.teachers.length <= 0 # usually there is just one teacher, so add one if there isn't one
teacher = project.teachers.build(:role_in_project => 'master')
if user_signed_in?
#teacher = project.teachers.new(:role_in_project => 'master', :user => current_user)
teacher.user = current_user # associate first teacher with current_user
else
#teacher = project.teachers.build
teacher.user = User.new # associate first teacher with a new user instance
end
end
if project.project_schools.length <= 0 # usually there is just one school, so add one if there isn't one
project_school = project.project_schools.build
project_school.school = School.new
end
if project.pupils.length < 3 # There can be up to 3 people, so add a blank fieldset as long as there are less than 3
pupil = project.pupils.build
pupil.user = User.new
end
project
end
end
These are my params received:
{"utf8"=>"✓", "authenticity_token"=>"uCCMk/s3SpDfR7+fXcsCOHPvfvivBQv8pVFVhdh6iro=",
"project"=>{
"teachers_attributes"=>{
"0"=>{
"id"=>"",
"user_attributes"=>{
"id"=>"1",
"gender"=>"male",
"title"=>"",
"firstname"=>"Firstname1",
"name"=>"Lastname1",
"faculty"=>"",
"fon"=>"",
"fax"=>""}
}
},
"id"=>"",
"title"=>"First Project",
"description"=>"This is a foo bar project!",
"presentation_type"=>"experimentell",
"note"=>""
},
"commit"=>"Register Project",
"action"=>"create",
"controller"=>"projects"
}
The case isn’t too abstract; it has to be possible to achieve it.
It’s just to connect a new parent record with an existing child record!
In this article, which is very good, exactly the case is explaint:
http://rubysource.com/complex-rails-forms-with-nested-attributes
# app/helpers/form_helper
module FormHelper
def setup_user(user)
user.address ||= Address.new
(Interest.all - user.interests).each do |interest|
user.interest_users.build(:interest => interest)
end
user.interest_users.sort_by! {|x| x.interest.name }
user/tmp/clean-controllers.md.html
end
end
There the interest is existing and gets connected through a new record in interest_uesers.
Why do I get the error when trying to do the same thing?
project.teachers.build(:user => current_user)
I studied several articles and casts, but none of them connect existing childs.
http://railscasts.com/episodes/196-nested-model-form-revised
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Rails 3.1+ Nested Forms Issue: Can't mass-assign protected attributes
Trying to use accepts_nested_attributes_for and has_and_belongs_to_many but the join table is not being populated
Quote: “accepts_nested_fields_for is used to create and modify related objects in a form. It can be used to populate join table, which is kind of what you're trying to do. However, using accepts_nested_fields_for to populate the join table is impossible with a HABTM relationship.”
That’s what I wanna do! Populate the join table!
It starts to be frustrating and I’d be glad to get some help!
My Models
class Project < ActiveRecord::Base
attr_accessible :title, :description, :presentation_type, :note,
:project_schools_attributes, :schools_attributes, :teachers_attributes, :pupils_attributes,
:users_attributes
validates_presence_of :title
validates_presence_of :description
validates_presence_of :presentation_type
has_many :project_schools, :dependent => :destroy
accepts_nested_attributes_for :project_schools
has_many :schools, :through => :project_schools
#accepts_nested_attributes_for :schools, :reject_if => :all_blank
has_many :pupils, :dependent => :destroy
accepts_nested_attributes_for :pupils, :reject_if => :all_blank
has_many :users, :through => :pupils
has_many :teachers, :dependent => :destroy
accepts_nested_attributes_for :teachers, :reject_if => :all_blank
has_many :users, :through => :teachers
#accepts_nested_attributes_for :users, :reject_if => :all_blank
end
class ProjectSchool < ActiveRecord::Base
attr_accessible :role_in_project, :comment,
:school_attributes, :school_id, :project_id
belongs_to :school
accepts_nested_attributes_for :school
belongs_to :project
end
class School < ActiveRecord::Base
attr_accessible :email, :fax, :fon, :name, :place, :street, :type_of_school, :www, :zip
has_many :project_schools
has_many :projects, :through => :project_schools
has_many :users # in real live they are named teachers and pupils but in this case the association goes directly to a user_id, not to teacher/pupil model
validates_presence_of :name, :type_of_school, :street, :place, :zip, :fon
validates :email, :format => { :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create }, :allow_blank => true
end
class Teacher < ActiveRecord::Base
attr_accessible :role_in_project, :user, :user_attributes, :project_id, :user_id
belongs_to :project
belongs_to :user
accepts_nested_attributes_for :user
serialize :role_in_project
end
class Pupil < ActiveRecord::Base
attr_accessible :classname, :user_attributes #, :project_id, :user_id
belongs_to :project
belongs_to :user
accepts_nested_attributes_for :user
end
class User < ActiveRecord::Base
serialize :roles
belongs_to :school
has_many :teachers
has_many :pupils
has_many :projects, :through => :teachers
has_many :projects, :through => :pupils
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :gender, :firstname, :name, :street, :place, :title, :faculty, :assignment,
:classname, :zip, :fon, :fax, :school_id, :roles,
:password, :added_by_user_id, :password_confirmation, :remember_me
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable#, :validatable
after_initialize :init
def init
# the default guest user
self.roles ||= ['default'] #will set the default value only if it's nil
end
end
My controller
class ProjectsController < ApplicationController
require 'axlsx'
before_filter :load_page, only: [:show, :index, :destroy]
def new
#project = Project.new
end
def create
#project = Project.new(params[:project])
respond_to do |format|
if #project.save
sign_in(:user, #project.teachers[0].user) unless user_signed_in?
# TODO: send mail
# save as excel file in dropbox
save_in_dropbox(#project)
format.html { redirect_to #project, notice: t('project.was_created') }
else
logger.debug #project.errors.inspect
format.html { render action: "new" }
end
end
end
end
projects/_form.html.haml
%h1
= t('project.register_headline')
= simple_form_for( setup_project(#project), :html => {:class => 'form-horizontal'} )do |f|
= f.error_notification
#teachers-wrapper.well
%span.jumpanchor#lehrkraft_anchor
%fieldset.form-inputs
= f.simple_fields_for :teachers do |teacher|
= render "teacher_fields", :f => teacher
.school-wrapper.well
%span.jumpanchor#schule_anchor
%h2
Informationen zur Schule
%fieldset.form-inputs
= f.simple_fields_for :project_schools do |project_school|
= render "school_fields", :f => project_school
.project-wrapper.well
%span.jumpanchor#projekt_anchor
%h2
Informationen zum Projekt der Schüler
%fieldset.form-inputs
= f.hidden_field :id
= f.input :title, :input_html => { :class => 'span6' }
= f.input :description, :input_html => { :class => 'span6', rows: 5 }
= f.input :presentation_type, collection: ['theoretisch', 'experimentell'], as: :radio_buttons, :class => 'controls-row', :input_html => { :class => 'inline' }
.clearfix
= f.input :note, :input_html => { :class => 'span6', rows: 3 }
.pupils-wrapper.well
%span.jumpanchor#schuler_anchor
%fieldset.form-inputs
= f.simple_fields_for :pupils do |pupil|
= render "pupil_fields", :f => pupil
projects/_teacher_fields.html.haml
= f.simple_fields_for :user do |user|
=# render "teacher_user_fields", :f => user
%h2
Betreuende Lehrkraft
- if user_signed_in?
= user.input :email, :disabled => true, :input_html => {:class => 'email_validation'}
- else
= user.input :email, :autofocus => true, :input_html => {:class => 'email_validation'}, :hint => 'Dies muß Ihre eigene E-Mailadresse sein!'
.details
=# user.hidden_field :id
= user.input :id
= user.input :gender, collection: [:female, :male]
= user.input :title
= user.input :firstname
= user.input :name
= user.input :faculty
= user.input :fon
= user.input :fax
It is Rails 3.2 with Ruby 1.9.3

Is it possible to have associations alias in a Rails model?

I have a model with two has_many associations that serve one logical purpose, and I want to alternate between the two depending on some conditions. I can have a convenience method for that, but then I can't use it in has_many :through associations.
Is there a nice way out?
UPD: the model's code:
# encoding: UTF-8
class User < ActiveRecord::Base
set_table_name 'clients'
devise(:database_authenticatable,
#:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable,
#:token_authenticatable,
#:confirmable,
#:lockable
#:timeoutable,
#:omniauthable
)
def email_required?
false
end
# Setup accessible (or protected) attributes for your model
attr_accessible(:email, :login, :password, :password_confirmation, :remember_me, # used by devise
:address, :phone, :description)
attr_accessible :name, :address, :phone, :description
validates :name, :presence => true
has_many :slaves, :class_name => 'User', :foreign_key => 'master_id',
:inverse_of => :master, :dependent => :destroy
belongs_to :master, :class_name => 'User', :foreign_key => 'master_id',
:inverse_of => :slaves
def slave?
master.present?
end
def master?
not slave?
end
validate :slaves_cannot_have_slaves
has_many :master_facilities, :class_name => 'Facility', :foreign_key => 'client_id'
has_many :analytics_profiles, :class_name => 'AnalyticsProfile', :foreign_key => 'owner_id',
:inverse_of => :owner, :dependent => :destroy
has_many :facility_permissions
has_many :slave_facilities, through: :facility_permissions, source: :facility, autosave: true
has_many :units, :through => :facilities, :foreign_key => 'facility_id'
# masters and slaves have different ways of accessing their facilities
# BUT! It's not a true association so a lot of code (e.g. draper) will fail
def facilities
if master?
master_facilities
else
slave_facilities
end
end
def dead_units
self.units.keep_if(&:dead?)
end
private
def slaves_cannot_have_slaves
unless master? or slaves.empty?
errors.add :slaves, 'Slaves cannot have slaves'
end
end
end
If I understood the problem right and the problem is the facilities association, which is implemented via master_facilities or slave_facilities association - why not taking the base behavior out to a module/class and include/inherit it in your concrete classes which will implement the facilities method differently.

Active admin has_many through delete association

I'm currently making association like this :
show do
h3 project.title
panel "Utilisateurs" do
table_for project.roles do
column "Prenom" do |role|
role.user.firstname
end
column "Nom" do |role|
role.user.lastname
end
column "email" do |role|
role.user.email
end
column "Role" do |role|
role.role_name.name
end
end
end
end
# override default form
form do |f|
f.inputs "Details" do # Project's fields
f.input :title
f.input :code
end
f.has_many :roles do |app_f|
app_f.inputs do
# if object has id we can destroy it
if app_f.object.id
app_f.input :_destroy, :as => :boolean, :label => "Supprimer l'utilisateur du projet"
end
app_f.input :user, :include_blank => false, :label_method => :to_label
app_f.input :role_name, :include_blank => false
end
end
f.buttons
end
I have the following associations :
Project
has_many :roles, :dependent => :destroy
has_many :users, :through => :role
User
has_many :roles, :dependent => :destroy
has_many :projects, :through => :role
Role
belongs_to :user
belongs_to :project
belongs_to :role_name
RoleName
has_many :roles
When I try to destroy user association through my form nothing happen, any idea to solve this ?
Or to add delete link to my show block ?
Try to add accepts_nested_attributes_for to your Project model (and roles_attributes to attr_accessible):
class Project < ActiveRecord::Base
has_many :roles, :dependent => :destroy
has_many :users, :through => :role
accepts_nested_attributes_for :roles, :allow_destroy => true
attr_accessible :roles_attributes, (+ all you had here before)
...
end
allow_destroy: true is the root of this issue.

How to customize formtastic attribute :as=>:check_boxes

my problem is that I try to customize the formatastic view. But before I go into detail, I'll explain my model.
I have 2 objects with a n:m relation Shop and Category
Shop model looks like that:
has_many :shop_categories, :class_name => "ShopCategory", :foreign_key => "shop_id"
has_many :categories, :through => :shop_categories, :source => :categories
Category model looks like that:
has_many :shop_categories, :class_name => "ShopCategory", :foreign_key => "category_id"
has_many :shops, :through => :shop_categories, :source => :shops
And of course my m to n table looks like
belongs_to :shops, :class_name => "Shop", :foreign_key => "shop_id"
belongs_to :categories, :class_name => "Category", :foreign_key => "category_id"
validates :shop_id, :presence => true
validates :category_id, :presence => true
This works fine and the following command in my Shop view will list all elements from categories within checkboxes:
<%= f.input :categories, :as => :check_boxes, :id => 'shop_categories' %>
Here is my problem:
Within categories I have a name for the category and a picture. Now I want to display the picture next to the selectbox.
I also tryed to use <% f.fields_for :categories do |category| %> but rails wont go through all category elements.
Is there a way to handle all Category elements with automatic checked objects?
If you need more information, i will be glad to give all what you need to understand the problem.
Thank you for any hint.
Try overriding custom inputs:
# app/inputs/collection_check_boxes_input.rb
class CollectionCheckBoxesInput < SimpleForm::Inputs::CollectionCheckBoxesInput
# [...]
end