Limit globalize_accessor locales to arguments from controller/view - ruby-on-rails

I'm using the globalize_accessor gem to render a form that has multilingual inputs for a :name field, and would like to limit the languages a user can input based on which Languages have been passed as arguments to the globalize_attribute_names method. Is something like this possible?
Here's what I have so far:
chart.rb
class Chart < ActiveRecord::Base
belongs_to :chart_type
belongs_to :section
has_many :data_points, dependent: :destroy
translates :name
globalize_accessors :attributes => [:name]
validates :name, presence: true
accepts_nested_attributes_for :data_points
end
charts/new.html.haml
= simple_form_for chart, html: {multipart: true} do |f|
- Chart.globalize_attribute_names.each do |lang|
= f.input lang
= f.input :chart_type, collection: #chart_types
= f.input :section, collection: #sections, label_method: :heading
= f.simple_fields_for :data_points, chart.data_points.build do |c|
= c.input :file, as: :file
= f.submit
UPDATE
To my knowledge, the only way to define locales for translation is to pass them in explicitly at the model level. When trying to use a method to define these however, I see an undefined local variable or method error. The method I'm using currently is for experimentation, and an ideal use case would have this method call the Chart's parent and return the acceptable languages for it. Here's the updated code:
class Chart < ActiveRecord::Base
belongs_to :chart_type
belongs_to :section
has_many :data_points, dependent: :destroy
translates :name
globalize_accessors :attributes => [:name], locales: languages
validates :name, presence: true
accepts_nested_attributes_for :data_points
def self.languages
[:en, :fr]
end

You're seeing an undefined local variable or method error because you are defining the method languages after you use it in the class. Just define the method above the globalize_accessors call and you should be fine:
class Chart < ActiveRecord::Base
...
def self.languages # <= define this first
[:en, :fr]
end
globalize_accessors :attributes => [:name], locales: languages

Related

Nested resource with 2-deep association

I'm trying to set up Active Admin so an AdminUser (logged in) can view a list of all ServiceUsers they are allocated, then click on a link next to a specific ServiceUser to see all support sessions they have created (for that specific ServiceUser).
Model:
class ServiceUser
has_many :support_allocations
has_many :admin_users, through: :support_allocations
has_many :support_budgets
class AdminUser < ApplicationRecord
has_many :support_allocations
has_many :service_users, through: :support_allocations
class SupportAllocation < ApplicationRecord
belongs_to :admin_user
belongs_to :service_user
has_many :support_sessions
class SupportSession < ApplicationRecord
belongs_to :invoice, optional: true
belongs_to :support_allocation
has_one :service_user, through: :support_allocation
has_one :admin_user, through: :support_allocation
I'm guessing I need to make SupportSessions a nested resource of ServiceUser so I can follow restful principles (i.e with a route to e.g. /service_users/1/support_sessions). I've managed to nest the resource like this:
ActiveAdmin.register SupportSession do
belongs_to :service_user
But I realise that omits the crucial join table SupportAllocation. Also, it doesn't work! Error message is:
NoMethodError in SupportSessionsController#index
undefined method `support_sessions' for #<ServiceUser:0x00007fe6316b9030> Did you mean? support_allocations
Previously I could view all SupportSessions for all ServiceUsers allocated to an AdminUser by using the code:
ActiveAdmin.register SupportSession do
super.includes(support_allocation: [:service_user, :admin_user])
This issue is I'd like to work with a resource in the context of the SupportAllocation relationship between a ServiceUser and an AdminUser. So the logged in AdminUser can see an index list of all ServiceUsers they are allocated (via SupportAllocation), then move to an index list of all SupportSessions for a selected ServiceUser (from that first list).
Is a nested resource the right solution here or is there a simpler way to just have a generic SupportSession resource page that can either show SupportSessions for ALL ServiceUsers or a specific ServiceUser (by 'id' in the URL)? How do I work with this tricky two-level association with an intermediary table?
Thank you so much for your help.
Here's the code for my support_sessions.rb controller in app/admin/
ActiveAdmin.register SupportSession do
menu label: 'My sessions'
#belongs_to :support_allocation
belongs_to :service_user
# Eager loading to speed up queries
includes :service_user, :admin_user, :support_allocation
sidebar :help, priority: 0 do
"Need help? Email us at help#example.com"
end
# Default sort order
config.sort_order = 'support_sessions.date_held_asc'
controller do
def scoped_collection
#super.includes :support_allocation
super.includes(support_allocation: [:service_user, :admin_user])
#SupportSession.includes(support_allocation: [:service_user, :admin_user])
#super.includes :service_user, :admin_user
end
end
scope :all, :default => true
scope :invoiced
scope :uninvoiced
index do
selectable_column
column 'Date held', :date_held
#column 'Service user', :full_name, :sortable => 'service_users.family_name' do |ss|
#service_user = ServiceUser.find(ss.support_allocation.service_user.service_user_id).full_name
#ss.support_allocation.service_user
#end
#column 'Based at', sortable: 'support_allocation.service_user.organisation.org_name' do |ss|
#ss.support_allocation.service_user.organisation.org_name
#end
column 'Venue', :venue
#column 'Support worker', :full_name, :sortable => 'admin_users.family_name' do |ss|
#support_worker = AdminUser.find(ss.admin_user_id).full_name
#ss.support_allocation.admin_user
#end
actions
end
permit_params(:admin_user_id, :service_user_id, :venue, :mode_of_delivery, :date_held, :time_start, :time_end, :total_breaks, :status)
form do |f|
f.semantic_errors *f.object.errors.keys
f.inputs 'Support session details' do
# Code to check if creating a new record or editing an existing record:
#if f.object.new_record?
#if current_admin_user.super_admin == false
# Pre-select only current support worker (so no other choices)
#f.input :admin_user, :label => 'Support worker', as: :select, collection: AdminUser.where(super_admin: 0, #id: current_admin_user.id), include_blank: false
#else
# Otherwise list all support workers
#f.input :admin_user, :label => 'Support worker', as: :select, collection: AdminUser.where(super_admin: 0), include_blank: true, allow_blank: false
#end
#f.input :support_allocation
#f.input :service_user, :label => 'Service user', as: :select, collection: ServiceUser.all, include_blank: true, allow_blank: false
f.input :mode_of_delivery, as: :select, collection: SupportSession.mode_of_deliveries.keys, include_blank: true, allow_blank: false
f.input :venue, :label => 'Venue'
f.input :date_held, :label => 'Date held', :as => :datepicker
f.input :time_start, :label => 'Start time'
f.input :time_end, :label => 'End time'
f.input :total_breaks, :label => 'Total breaks (in minutes)', :input_html => { :value => '0' }
if current_admin_user.super_admin == false
f.input :status, as: :select, collection: SupportSession.statuses.keys, include_blank: true, allow_blank: false
end
end
f.actions
end
end
I believe your ActiveAdmin registrations need to be reflected the same way as they are in your models.
For example, you have:
ActiveAdmin.register SupportSession do
belongs_to :service_user
but your SupportSession model is defined as:
class SupportSession < ApplicationRecord
...
has_one :service_user, through: :support_allocation
...
Assuming your model is correct, the ActiveAdmin registration should also be a has_one relationship.
Additionally, it looks like you have not fully defined your model relationships. Your ServiceUser model does not have a corresponding belongs_to relationship.
Thanks for the tip on my model.
To fix my original problem I nested my SupportSession ActiveAdmin controller within the SupportAllocation:
ActiveAdmin.register SupportSession do
menu label: 'My sessions'
belongs_to :support_allocation
This creates a new nested route for SupportSession within the context of SupportAllocation (which defines the relationship between a ServiceUser and a SupportSession) letting me create a link (e.g. /support_allocations/2/support_sessions) from SupportAllocation directly to all SupportSessions for an individual ServiceUser, using the usual path helper:
actions defaults: false do |support_allocation|
link_to 'Manage support sessions', support_allocation_support_sessions_path(support_allocation_id: support_allocation.id), class: 'member_link'
end

validation and accepts_nested_attributes_for

i have two models :
class Article < ActiveRecord::Base
belongs_to :league
has_many :photos, :dependent => :destroy
attr_accessible :content, :lead, :title, :title_slug,
:created_at, :updated_at,
:league_id, :photos_attributes
accepts_nested_attributes_for :photos
validates :content, :league, :presence => true
validates :lead , :length => {maximum: 1000}, :presence => true
validates :title ,:length => {maximum: 200}, :presence => true
validates_associated :photos
and
class Photo < ActiveRecord::Base
belongs_to :article
attr_accessible :photo
validates :photo, presence: true
has_attached_file :photo , :styles => { :medium => '440x312#', :small => '209x105!'}
end
My ArticlesController is
...
def new
#article = Article.new
#article.photos.build
end
def create
#article = Article.new(params[:article])
if #article.save
redirect_to([:admin,#article])
else
render 'new'
end
end
...
form view is :
= form_for([:admin,#article] , :html => {:multipart => true}) do |f|
- if #article.errors.any?
= render 'errors'
= f.fields_for :photos do |builder|
= builder.label :photo
= builder.file_field :photo
...
i have some question about it :
1) I dont want to save an article without empty photo but now when i dont choose a file my article saves.
2) When i have some errors on article's fields and render 'new' ,my photo field dissapear , what is the rails way to resolve it.
3) in the future i want to add another model: photo_type and assosciate it with photo. Each article will have two photo fields , each with own type (for example: small , big) . I wonder how to render that fields and what can i do to save article with two photos with different types.
Answer for 1: Use validates_associated :photos. Documentation
Answer for 2: I guess that is an file attachment field. For that, this is generally done by setting up a hidden cache field and by using some callbacks. MountUploader uses the same principle.
Answer for 3: Little skeptical, but I guess something will work along this way:
In your Article model, have two associations with Photo as:
has_one :small_photo, :class_name => "Photo"
has_one :big_photo, :class_name => "Photo"
This will enable you to have two sub-form fields present for both types while opening up the form for Article.
Hope it helps. Do comment if last one can work for you in this way. It looks like the good deal to me :)

Rails 3 association error: undefined method

I've been puzzling over this for quite some time now and can't figure it out.
I've got 2 models:
class Vehicle < ActiveRecord::Base
attr_accessible :year, :capacity,
:size, :body, :model_id, :maker_id, :parameters_attributes
validates :year, numericality: { greater_than: 1900 }
validates :year, :capacity, :size, :body, presence: true
belongs_to :model
belongs_to :maker
has_many :parameters
accepts_nested_attributes_for :parameters
end
and
class Parameter < ActiveRecord::Base
attr_accessible :tag, :value
validates :tag, :value, presence: true
belongs_to :vehicle
end
in new vehicle view i've got:
= form_for [:admin, #vehicle], html: { multipart: true } do |f|
=# some other stuff in between
= f.text_field :value, size: 4
I get this error
undefined method `value'
Just can't seem to get it working. Help, anyone?
EDIT
routes.rb
resources :vehicles
resources :parameters
resources :makers do
resources :models
end
If you are using nested form, you should have something like
f.fields_for :parameters do |parameter|
and than:
parameter.text_field :value, size: 4
Also, remember to create the some parameters in the controller, for example:
def new
#vehicle = Vehicle.new
2.times { #vehicle.parameters.build } #it will create 2 parameters
...
end
f refers to #vehicle, it seems only Parameter bears this field. That's why it fails.
Sidenotes:
In Vehicle you have accepts_nested_attributes_for :parameters but you don't have parameters_attributes in the attr_accessible, can't be good.
If you want to call the relationship in the form consider using fields_for
Ok, I've made a mess of things.
Firstly I've been trying to
def new
#vehicle = #vehicle.parameters.build
end
hence the error undefined method. After a while I got to the correct syntax, which is the one gabrielhilal added after a while.
def new
#vehicle = Vehicle.new
#vehicle.parameters.build
end
No matter ;) Still had problems, because after clicking "create" he wouldn't add records in the database. Turned out that I've set the validates presence: true for tag, but didn't assign any value to it. After fixing that, it worked like a charm. Thanks a lot for all the help.
On to the next puzzle.

How can I properly configure rails 3 nested attributes

I have 2 models. Member and Survey
member.rb as follows
Class Member < ActiveRecord::Base
has_one :survey, :dependent => :destroy
accepts_nested_attributes_for :survey
attr_accessible :fname,:lname, :address, :city, :state, :zip, :email, :phone, :phone_alt, :e_contact, :e_contact_phone, :physician, :physician_phone, :chiropractor, :chiropractor_phone, :password, :password_confirmation, :remember_me, :survey_attributes
end
survey.rb as follows
Class Survey < ActiveRecord::base
belongs_to :member
end
however, whenever I try to create the member with the survey attributes I receive
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: surveys
I am testing this via the console.
With a has_one association the accessible call should read:
attr_accessible :survey_attributes
The params you're posting need to be nested, like so:
params = { :member => { :name => 'Jack', :survey_attributes => { :attribute => 'value' } } }
In the form make sure that you're building the nested relationship correctly, ie. you must use:
= form_for #member do |f|
...
= f.fields_for :survey do |s|
...
If you have those things setup like so it should work. If this isn't catching your error then please show a log of what you're trying in the console and isn't working.
See #accepts_nested_attributes_for in the Rails API for more info.

Polymorphism and forms in Ruby on Rails

I've been full of questions lately, but thanks to this awesome community, I'm learning a ton.
I got all the help I needed with polymorphic associations earlier and now I have a question about tackling forms with polymorphic models. For instance I have Phoneable and User, so when I create my form to register a user, I want to be able to assign a few phone numbers to the user (ie: cell, work, home).
class User < ActiveRecord::Base
has_many :phones, :as => :phoneable
end
class Phone < ActiveRecord::Base
belongs_to :phoneable, :polymorphic => true
end
class CreateUsers < ActiveRecord::Migration
t.column :name, :string
t.references :phoneable, :polymorphic => true
end
class CreatePhones < ActiveRecord::Migration
t.column :area_code, :integer
t.column :number, :integer
t.column :type, :string
t.references :phoneable, :polymorphic => true
end
Now when I create my form, I'm getting confused. Normally I would do the following:
- form_for :user, :html => {:multipart => true} do |form|
%fieldset
%label{:for => "name"} Name:
= form.text_field :name
##now how do I go about adding a phone number?
##typically I'd do this:
##%label{:for => "phone"} Phone:
##= form.text_field :phone
Using polymorphism, would I just approach the same way, but use fields_for?
- user_form.fields_for :phone do |phone| %>
%label{for => "area_code"} Area Code:
= phone.text_field :area_code
%label{for => "number"} Number:
= phone.text_field :number
Is this the correct approach in this instance?
Before we go further, I did notice one issue - you do not need the t.references with the has_many side of the association. So you do not need it in the create_user model. What that does is it creates the phonable_id and the phoneable_type columns, you only need that in the polymorphic model.
You are heading down the correct path with the fields_for approach. But in order to get that working, you need to tell the model how to handle those fields. You do that with the accepts_nested_attributes_for class method.
class User < ActiveRecord::Base
has_many :phones, :as => :phoneable
accepts_nested_attributes_for :phones
end
and one minor thing, you will need to have the fields_for point to the exact name of the association
- form_for #user do |user_form|
- user_form.fields_for :phones do |phone|
Instead of
- form_for #user do |user_form|
- user_form.fields_for :phone do |phone|
and make sure you remove your stray %> erb tag :)
More on accepts_nested_attributes_for: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I hope this helps!

Resources