option_groups_from_collection_for_select in single table - ruby-on-rails

I need to group option-tags, but the groups and values are in the same table.
Lets say i have the model "Person" with the methods :age and :name and i want to group by age and list all names for the corresponding age. This would be something like:
option_groups_from_collection_for_select(#people, :name, :age, :id, :name, 1)
Obviously this does not work because :name is a single value. Is there a simple solution? Maybe by creating a method ":names" for Person, but how do i set the connection to :age?
-Bump (Noone an idea? Maybe i can just build a string with all those groups and options?)

I think your are missing the group_label_method parameter of the option_groups_from_collection_for_select method as shown here.
Try:
option_groups_from_collection_for_select(#people, :name, :name, :age, :id, :name, 1)

Related

Model failing to save

I searched for a solution for the problem, but I cannot find the root cause of the issue.
I have a couple of models in my Rails application, two of which, Category and Activity, have a many-to-many relationship. In both the Category and Activity models, I defined this relationship using has_and_belongs_to_many. I created a joining table between the two. Please find my DB scheme here: https://pastebin.com/wc4TsPQQ.
The form to create a new Activity contains a field to select one or more categories to match:
<%= f.collection_select :category_id, Category.all, :id, :name, {include_hidden: false}, {:multiple=>'true', :class=>'form-control'} %>
When I try to submit the form the activities.category_id field seems to not be "registred" (not sure about the right terminology). Rails either throws a
"category cannot be blank"
error or, when I temporary disable presence validation on Category, the error
SQLite3::ConstraintException: NOT NULL constraint failed: activities.category_id
Looking in the request parameters however, the category ID is being sent by the form:
{"authenticity_token"=>"MD5eM5H7DG6RkjD+/QSZ5RqkAnJupJ3L/V044r+QP6s0651mv4hoTSSa8NXB3x959dpwKsSQhBTi58idDMm9hA==",
"activity"=>{"name"=>"fsdffdf", "description"=>"sddfdsf", "location"=>"sdfsdf", "category_id"=>["3"], "user_id"=>"3"},
"commit"=>"Save Activity"}
First off, you seem to have created a activities.category_id column with a not-null constraint which is not needed here. You might want to re-read "The has_and_belongs_to_many Association" documentation.
To get rid of the column you can either roll back and alter the migration that created the table in the first place or create a migration to remove the column:
class RemoveCategoryIdFromActivities < ActiveRecord::Migration[6.0]
def change
remove_reference :activities, :category, index: true, foreign_key: true
end
end
Also remove the belongs_to :category association in your Activity model which causes the "category cannot be blank" validation error.
Then you should be using category_ids and not the singular _id.
<%= f.collection_select :category_ids, Category.all, :id, :name, {include_hidden: false}, {multiple: 'true', class: 'form-control' } %>
And make sure you correctly whitelist it:
params.require(:activity)
.permit(:name, :description, :current_location, category_ids: [])
You want category "ids" not "id" since you have multiple ids to set.
Change to f.collection_select :category_ids, ....., make sure you are permitting an array for that attribute on your controller too.

Rails custom validation on 3 attributes

I want to create a validation that makes sure no other object in a table has the same combination of 3 attributes.
So say my code looks like the below:
class Dog
attr_accessor :color, :name, :height, :weight
end
I want to create a custom rails validation on the Dog class that makes sure there is no other dog in the database that has the same color, name, and weight. Is something like this possible?
Thanks!
You could use validates_uniqueness_of:
validates_uniqueness_of :color, scope: [:name, :weight]

Simple association architecture and implementation in ruby on rails

Quick question about a simple problem I am facing (and I want to use as a way to understand a few things about associations and rails in a deeper level). Here goes:
The two associated models are
class Employee < ActiveRecord::Base
attr_accessible :name
attr_accessible :age
belongs_to :role
attr_accessible :role_id
end
class Role < ActiveRecord::Base
attr_accessible :title
attr_accessible :salary
has_many :employees
end
so that every new employee has a fixed salary, according to his role (which is the case most of the times). However, what if I want to set a different salary for a specific employee?
Using simple_form I have so far written the following:
<%= f.input :name, label: 'Employee Name', :required => true %>
<%= f.association :role, as: :radio_buttons, :required => true %>
<%= f.input :salary, label: 'Employee Salary', :input_html => { :value => 0 }, :required => true %>
Which of course gives me a can't mass assign protected attributes: salary error.
To fix that, I added attr_accessible :salary to the Employee model but that just changed the error to unknown attribute: salary.
From what I understand I have to first change something in the new employee and then also in the employee model and controller so it accepts a value for the salary and knows how to handle it, right?
I 've also seen accepts_nested_attributes_for used but I am not entirely sure in which side of the association it should go - as I am not entirely sure the association is architectured in the best way either.
You need to add a salary column to your employees table if you are in fact wanting to allow a custom salary to be specified on the Employee. In your terminal, create a new migration and apply it
rails generate migration AddSalaryToEmployees salary:integer
RAILS_ENV=development rake db:migrate
By the way, you don't need to call attr_accessible multiple times; it accepts an arbitrary # of symbols
attr_accessible :name, :age, :role_id, :salary
Also, since you mentioned it, I'll comment on it: accepts_nested_attributes_for currently has no place in your models (given the code you've shown so far).
To answer the questions raised in your comment:
Isn't that duplication of code (having salary in both models I mean)?
No, they serve two different purposes. :salary in Role is the default salary applied to all Employees associated with that Role. :salary on Employee is an 'override' for special circumstances where an Employee's salary doesn't fit the mold of the Role they're associated with.
It wouldn't make sense to create a custom Role just for this purpose (assuming the custom salary is the only difference for the Employee)
You can't change the salary on the Role itself, because that would affect the salary of the other Employees associated with that Role
And doesn't that need another method (to make sure that the role salary is set as the employee's salary if none is specifically set)?
Another method? No. Customizing the existing attr_reader for salary on Employee to return the default from the Role if an 'override' hasn't been set? If you want
def salary
return role.salary if read_attribute(:salary).blank?
read_attribute(:salary)
end

Datamapper: Create two relationships (has n and has 1) to the same model

I've got two models, that link to each other.
class User
include DataMapper::Resource
property :id, Serial
has n, :mail_addresses
end
class MailAddress
include DataMapper::Resource
property :id, Serial
property :email, String, :required => true, :unique => true, :format => :email_address
belongs_to :user
end
Now I want to add a primary mail address to a user. So it can do things like
some_user.primary_mail_address = some_user.mail_addresses.first
I've been trying to do things like this on the user model, but without any luck.
property :primary_mail_address_id, Integer, required: false
has 1, :primary_mail_address, model: 'MailAddress', parent_key: [:primary_mail_address_id], child_key: [:id]
The above trick allows me to execute the code sample, but the primary_mail_address_id won't get updated when I do that.
How to do it?
Found own solution.
The problem is that I confused has 1 with belongs_to. has 1 is actually a one_to_many relationship that tries to create a property on the MailAddress model, instead of on the User model.
The trick was:
belongs_to :primary_mail_address, MailAddress, required: false

Rails, How can I combine multiple model attributes to create a unique permalink using permalink_fu?

Can Permalink_fu combine 2 or more model attributes to create a unique permalink?
Let's say I have a Business Model, this model contains :name, :address, :phone, :city, :state, :country etc. attributes.
Right now I have permalink set up in this model only for :name
has_permalink :name
So I would get "/biz/name". However I would like to combine the Business name, city, and a incremental number if there are more than 1 location in the city for that business.
For example I would like to use:
"/biz/joes-coffee-shack-chicago" for the permalink
or if a multple location business
"/biz/starbucks-chicago-92"
Is this possible with the current permalink_fu plugin or some fork of permalink_fu? Or will this require some modification to the permalink_fu plugin?
You can set the attributes as an array:
has_permalink [:one, :two, :three]
They will be automatically joined by -. Permalink_fu also automatically adds a suffix if there's already a record with that permalink.
Add a virtual attribute to your Business model.
class Business < ActiveRecord::Base
attr_accessor :perma_link_attr
has_permalink :perma_link_attr
def perma_link_attr
suffix = 1
[:name, :city, suffix].join("-")
end
end

Resources