How to build several objects while adding association? - ruby-on-rails

Rails 4.2.1, Ruby 2.2.1
Relations are:
class Region < ActiveRecord::Base
has_many :translations, dependent: :destroy
has_many :custom_properties, dependent: :destroy
has_many :languages, through: :translations
has_many :options, through: :custom_properties
accepts_nested_attributes_for :custom_properties, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :translations, reject_if: :all_blank, allow_destroy: true
end
class CustomProperty < ActiveRecord::Base
belongs_to :region
has_many :options, dependent: :destroy
has_many :custom_property_translations, dependent: :destroy
accepts_nested_attributes_for :options, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :custom_property_translations, reject_if: :all_blank, allow_destroy: true
end
class CustomPropertyTranslation < ActiveRecord::Base
belongs_to :custom_property
belongs_to :language
end
class Option < ActiveRecord::Base
belongs_to :custom_property
has_many :option_translations, dependent: :destroy
accepts_nested_attributes_for :option_translations, reject_if: :all_blank, allow_destroy: true
end
class OptionTranslation < ActiveRecord::Base
belongs_to :option
belongs_to :language
end
In region form I'm using cocoon for nested fields.
= f.simple_fields_for :custom_properties do |custom_property|
= render 'custom_property_fields', f: custom_property
.links
= link_to_add_association 'add property', f, :custom_properties
And nested form for CustomPropertyTranslation and OptionTranslation.
= f.simple_fields_for :custom_property_translations do |custom_property_translation|
= render 'custom_property_translation_fields', f: custom_property_translation
.links
= link_to_add_association t('.add_translation'), f, :custom_property_translations
I wan't to automatically build several CustomPropertyTranslation and OptionTranslation depending on how many languages are the region has.
I tried to use after_initialize callback to build necessary associations but it worked only for existing custom properties. How do I build several associations at once on click add translation ?

you can use the count key in html_options of link_to_add_association helper to determine how many new objects you want to create
= f.simple_fields_for :custom_property_translations do |custom_property_translation|
= render 'custom_property_translation_fields', f: custom_property_translation
.links
= link_to_add_association t('.add_translation'), f, :custom_property_translations, {count: 3}
More on the available options here: https://github.com/nathanvda/cocoon/blob/be59abd99027b0cce25dc4246c86d60b51c5e6f2/lib/cocoon/view_helpers.rb

Related

HTML table form with multiple inheritance, in cocoon?

I have a table with cells.
Using cocoon how would I make a form in which each cell belongs_to the row, column, and table?
For Example:
# Table
class Roster < ApplicationRecord
has_many :timeslots, inverse_of: :roster
has_many :games, inverse_of: :roster
accepts_nested_attributes_for :timeslots, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :games, reject_if: :all_blank, allow_destroy: true
end
# Column
class Court < ApplicationRecord
belongs_to :roster
has_many :games
accepts_nested_attributes_for :games, reject_if: :all_blank, allow_destroy: true
end
# Row
class Timeslot < ApplicationRecord
belongs_to :roster
has_many :games
accepts_nested_attributes_for :games, reject_if: :all_blank, allow_destroy: true
end
# Cell
class Game < ApplicationRecord
belongs_to :timeslot
belongs_to :roster
end
I am trying now with a hidden <input> for each game's :timeslot_id and :court_id the only problem is you cannot get the id before the timeslot and court is saved. The other idea I ma working on is to for each game to have a hidden <input> of the row/column they are in.
I finally figured it out:
Each cell had two hidden inputs:
<%= cell.hidden_field :row %>
<%= cell.hidden_field :column %>
The cell belongs_to association had to be optional:
class Cell < ApplicationRecord
belongs_to :table
belongs_to :column, optional: true
belongs_to :row, optional: true
end
And the Table had an after_save callback:
after_save do |table|
table.rows.each_with_index do |row,row_number|
table.cells.each do |cell|
if cell.row-1 == row_number
cell.row = row
cell.save()
end
end
end
table.columns.each_with_index do |column,column_number|
table.cells.each do |cell|
if cell.column-1 == column_number
cell.column = column
cell.save()
end
end
end
end
There is probably a better way to do this but I think this is the simplest.
You will need to add an extra two columns to your database: row and column (The main drawback)

Rails 4 has_many through - Cannot modify association

I have DayItem model which has one SchoolProgram which has many seminars.
class DayItem < ActiveRecord::Base
has_one :school_program, dependent: :destroy
has_many :seminars, through: :school_program
accepts_nested_attributes_for :school_program
accepts_nested_attributes_for :seminars, reject_if: :all_blank
end
class SchoolProgram < ActiveRecord::Base
belongs_to :day_item
has_many :seminars, dependent: :destroy
accepts_nested_attributes_for :seminars, allow_destroy: true, reject_if: :all_blank
end
class Seminar < ActiveRecord::Base
belongs_to :school_program
end
I am using cocoon gem for dynamic nested forms as follows.
_form.html.haml:
= simple_form_for [#day, #day_item] do |f|
= f.input :start_time
= f.simple_fields_for :school_program do |form|
= form.input :school
= form.simple_fields_for :seminars do |seminar|
= render 'seminar_fields', :f => seminar, :parent => form
.links
= link_to_add_association 'add seminar', form, :seminars
_seminar_fields.html.haml:
.nested-fields.well.well-compact
.form-inline
= f.input :name
= link_to_remove_association "remove seminar", f
But when I try to add a seminar I get following exception.
 ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection in Etm::DayItemsController#update
Cannot modify association 'DayItem#seminars' because the source reflection class 'Seminar' is associated to 'SchoolProgram' via :has_many.
Any help is appreciated.
Circular reference in relationships (source reflection)
There may be more than one issue here, but the first one that should be addressed is that your relationship for Seminars creates a circular reference. It is declared in a has_many in DayItem and then as a has_one on SchoolProgram, which itself belongs to the parent class DayItem. Please try the change below to our DayItem model. Leave the other models as they are, and let me know how that goes.
class DayItem < ActiveRecord::Base
has_one :school_program, dependent: :destroy
accepts_nested_attributes_for :school_program
end

ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection Error

I have a model Job, which has_many Applications, and has_many Questions. An Answer belongs to both an Application and a Question.
I'm trying to make a factory method an admin can use to create applications, without users having to write anything.
To do this, I wrote --
def self.make_by_admin(params)
app = Application.new
app.user_id = params[:user_id]
app.job_id = params[:job_id]
app.questions.each do |question|
app.answers.new(question_id: question.id, content: 'N/A')
end
app
end
But, I get the error
#<ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'Application#questions' because the source reflection class 'Question' is associated to 'Job' via :has_many.>
What's weird though is that I'm not actually modifying questions. All I want to do is to generate blank answers for each question.
How would I go about doing that?
Full Models
class Job < ActiveRecord::Base
default_scope { order('jobs.created_at DESC') }
has_many :bullets, dependent: :destroy, inverse_of: :job
has_many :roles, dependent: :destroy, inverse_of: :job
has_many :questions, dependent: :destroy, inverse_of: :job
has_many :job_city_relations, inverse_of: :job, dependent: :destroy
has_many :cities, through: :job_city_relations
has_many :job_industry_relations, inverse_of: :job, dependent: :destroy
has_many :industries, through: :job_industry_relations
has_many :applications, inverse_of: :job, dependent: :destroy
has_many :users, through: :applications
validates :cities,
:job_title,
:job_summary,
:qualifications,
:industries,
:bullets,
:roles,
:questions,
presence: true
accepts_nested_attributes_for :bullets,
reject_if: :all_blank,
allow_destroy: true
accepts_nested_attributes_for :roles,
reject_if: :all_blank,
allow_destroy: true
accepts_nested_attributes_for :questions,
reject_if: :all_blank,
allow_destroy: true
scope :with_cities, ->(city) do
job = Job
.includes(:industries)
.includes(:cities)
.includes(:questions)
.includes(:bullets)
.includes(:roles)
job = job.where(cities: { id: city }) if city
job
end
scope :with_search, ->(search) do
job = Job.includes(:industries)
.includes(:cities)
.includes(:bullets)
.includes(:roles)
.includes(:questions)
if search
job = job.where('jobs.job_title LIKE ?', "%#{search}%")
end
job
end
scope :with_info, -> do
Job.includes(:industries)
.includes(:cities)
.includes(:bullets)
.includes(:roles)
.includes(:questions)
end
def self.build
job = Job.new
2.times {
job.bullets.build
job.roles.build
}
job.questions.build
job
end
def potentials
good_fits = User.includes(:source, :heat, :applications, common_app: [:cities, :industries])
.where('cities.id IN (?)', self.city_ids)
.where('industries.id IN (?)', self.industry_ids)
.where('users.id NOT IN (?)', self.users.map(&:id))
end
end
class Application < ActiveRecord::Base
STATUS_OPTIONS = ["Application Complete",
"Materials Submitted",
"Pending Interview",
"Second Interview"]
belongs_to :job, counter_cache: true
belongs_to :user, counter_cache: true
has_many :questions, through: :job
has_many :answers, inverse_of: :application, dependent: :destroy
validates :job_id, presence: true
validates :user_id, presence: true
validates :answers, presence: true
accepts_nested_attributes_for :answers,
allow_destroy: true, reject_if: :all_blank
scope :with_dependents, -> do
Application
.includes(:job)
.includes(:questions)
.includes(:answers)
end
scope :for_job, ->(job_id) do
Application
.includes(user: [:source, :heat, common_app: [:cities, :industries]])
.includes(questions: :answer)
.where('applications.job_id = ?', job_id)
end
def self.build(job, appl = Application.new)
job.questions.each do |question|
appl.answers.build(question_id: question.id)
end
appl
end
def self.make_by_admin(params)
app = Application.new
app.user_id = params[:user_id]
app.job_id = params[:job_id]
app.questions.each do |question|
app.answers.new(question_id: question.id, content: 'N/A')
end
fail
app
end
end
class Question < ActiveRecord::Base
belongs_to :job
has_one :answer
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :application
end
I ended up writing
def self.make_by_admin(params)
app = Application.new
app.user_id = params[:user_id]
app.job_id = params[:job_id]
app.questions.pluck(:id).each do |id|
app.answers.new(question_id: id, content: 'N/A')
end
app
end

Allow user to choose correct answer with accepts_nested_attributes_for?

I have the rails relations:
class Quiz < ActiveRecord::Base
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions,
reject_if: proc { |a| a[:content].blank? },
allow_destroy: true
end
class Question < ActiveRecord::Base
belongs_to :quiz
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers,
reject_if: proc { |a| a[:content].blank? },
allow_destroy: true
end
class Answer < ActiveRecord::Base
belongs_to :question
end
How would a structure the model so when the user is in Quiz#edit or Quiz#new they can select which answer (with radio buttons) is the correct answer?
In the answers table, add an additional attribute called 'is_correct_answer' that will be true only for one combination.

Nested attributes reject_if model has more than four children

I have the below scenario:
class Question < ActiveRecord::Base
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :allow_destroy => true, :reject_if => ""
end
class Answer < ActiveRecord::Base
belongs_to :question
end
How can I reject the creation if one question has more than 4 answers?
I hope it work for you.
class Question < ActiveRecord::Base
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => -> {|q| q['answers'].count > 4}, :allow_destroy => true
end
class Answer < ActiveRecord::Base
belongs_to :question
end

Resources