HTML table form with multiple inheritance, in cocoon? - ruby-on-rails

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)

Related

Rails: Select all Children models of Parent and sort by date created in Active Record

In my rails app, I am trying to select all of the child models ( ‘pdfs’ ‘videos’ ‘infographics’ ) which have a reference to a parent model (‘category’). These three child models can belong to only one of three parent models (this is enforced using an Exclusive arc validation in the child models). So they have a one in three chance of belonging my parent model (‘category’).
Is there a way of selecting all of the children models ( ‘pdfs’ ‘videos’ ‘infographics’ ), which are attached to my specific parent model (‘category’)? If i am able to do this, is it possible to sort each of these instances of the children models by the date they were created? And finally, would I be able to have this ordered list of children instances as an array, so that I can iterate over it in the view?
Thank you for your help! Model set up below.
The parent model:
class Category < ApplicationRecord
belongs_to :navbar_base_folder
has_many :sub_categories, dependent: :destroy
has_many :infographics, dependent: :destroy
has_many :videos, dependent: :destroy
has_many :pdfs, dependent: :destroy
end
The three children:
class Pdf < ApplicationRecord
belongs_to :sub_category, optional: true
belongs_to :category, optional: true
belongs_to :secret_category, optional: true
belongs_to :secret_sub_category, optional: true
end
class Video < ApplicationRecord
belongs_to :sub_category, optional: true
belongs_to :category, optional: true
belongs_to :secret_category, optional: true
belongs_to :secret_sub_category, optional: true
end
class Infographic < ApplicationRecord
belongs_to :sub_category, optional: true
belongs_to :category, optional: true
belongs_to :secret_category, optional: true
belongs_to :secret_sub_category, optional: true
end
This is a perfect candidate for single table inheritance (STI).
class Category < ApplicationRecord
has_many :contents, dependent: :destroy
has_many :infographics
has_many :pdfs
has_many :videos
end
# db/migrate/create_content.rb
# Create a migration for Content
create_table :content do |t|
# .... add your attributes here ...
t.string :type, index: true
end
end
# app/models/content.rb
class Content < ApplicationRecord
belongs_to :category
# `has_many ... optional: true` is not invalid, but you should aim to
# keep your associations & validations separate. Instead put this as a
# validation:
validates :category, presence: true
end
# app/models/pdf.rb
class Pdf < Content; end
# app/models/infographic.rb
class Infographic; end
# app/models/video.rb
class Video < Content; end
Then find all content:
#category = Category.first
#contents = #category.contents.order(date: :desc)
Hope that helps :)
P.S: Your Category should be refactored to a parent/child pattern if possible:
class Category < ApplicationRecord
# app/models/category.rb
# Your `Category` table should be using a `parent_id` column, not creating a "sub_category" model.
belongs_to :parent_category, foreign_key: :parent_id, class_name: 'Category'
has_many :sub_categories, foreign_key: :parent_id, class_name: 'Category'
attribute :is_secret, Boolean
end

How to call an attribute from a class array

I'm trying to make a method that gets the date and adds onto it an amount of days specified.
At present I cannot call the days specified.
I have a Plant Class that has_many DaysTillSellables, The Period class has_many DaysTillSellable also.
When the user creates a plant they can add a DaysTillSellables and then select a period and then enter an amount of days.
I first need to check to see which period the date is in, then return that period. Currently attempting like so
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
Then Find the days till sellable that is connected to that period and finally call the days from that
Below is the code for the class I'm trying to call it in
class Potting < ApplicationRecord
belongs_to :batch, inverse_of: :pottings
validates :plant_count, :completed_at, presence: true
enum germination_result: { light: 0, medium: 1, full: 2 }
def pottings_completed_at
"Week #{completed_at.strftime('%U').to_i}/#{completed_at.strftime('%Y').to_i}"
end
def completed_at
super || Time.zone.today
end
def completed_at_week
completed_at.strftime('%U')
end
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
def period_days
plant.days_till_sellables.find_by(period: :current_period).&current_period.days
end
def ready_for_sale
completed_at + period_days
end
end
I've added more Code below to give better context for classes
class DaysTillSellable < ApplicationRecord
belongs_to :period, inverse_of: :days_till_sellables, foreign_key: :period_id
belongs_to :plant, inverse_of: :days_till_sellables, foreign_key: :plant_id
validates :days, presence: true
end
.
class Period < ApplicationRecord
has_many :days_till_sellables, dependent: :destroy, inverse_of: :period
belongs_to :organization
.
class Plant < ApplicationRecord
has_many :batches, dependent: :destroy
has_many :goals, dependent: :destroy
has_many :days_till_sellables, dependent: :destroy, inverse_of: :plant
belongs_to :organization
accepts_nested_attributes_for :days_till_sellables, allow_destroy: true
validates :genus, :species, :period_id, presence: true
end
I think you are looking for:
class Potting
belongs_to :plant, through: :batch
...
end

Rails before_update callback with nested attributes

I have two models (lets call then A and B).
A has_many bs and B belongs_to A.
class A < ApplicationRecord
has_many :bs, dependent: :destroy, inverse_of: :a
accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
validates_associated :bs
end
class B < ApplicationRecord
belongs_to :a, inverse_of: :bs
before_update :do_something, unless: Proc.new { |b| b.a.some_enum_value? if a }
def do_something
self.some_field = nil
end
end
Other than that, B has a before_update callback that sets some_field to nil if A has some_enum_value set.
Since this relation is used on a nested form, that before_update from B is only being called if I update a attribute form B. If I only change a value form A that callback is not called.
How can I call B's before_update when A is updated?
Thanks in advance.
For belongs to associations you can use the touch option:
class B < ApplicationRecord
belongs_to :a, inverse_of: :bs, touch: true
end
Which would update a.updated_at when you update B.
However this option does not exist for has_many relations since it could have potentially disastrous performance consequences (If an A has 1000s or more Bs).
You can roll your own however:
class A < ApplicationRecord
has_many :bs, dependent: :destroy, inverse_of: :a
accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
validates_associated :bs
after_update :cascade_update!
def cascade_update!
# http://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_each
bs.find_each(batch_size: 100) do |b|
b.update!(updated_at: Time.zone.now)
end
end
end

How to build several objects while adding association?

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

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.

Resources