Update a single attribute in a model using Rails 3 - ruby-on-rails

I have two models, Rules and Ruleset that both have a has_to_and_belong_to_many relationship. Rules are individual rules, and Rulesets are a specific collection of rules.
The user's dashboard shows all the rules the user created. I have a button for each rule to "Add Rule to Ruleset". By clicking the button, it should load a select form where the user can select their existing rulesets and hit submit, and voila, its added to the ruleset.
I just don't know how to make this work, as I'm pretty new to rails. If I call the update action on rules, it loads the entire update form, which I don't want. I just want to have the ability for a user to select a ruleset and then add that rule to the ruleset. Here are my models:
class Rule < ActiveRecord::Base
attr_accessible :description, :user_id, :game_id, :ruleset_id
has_and_belongs_to_many :rulesets
belongs_to :user
belongs_to :game
validates :description, presence: true
validates :user_id, presence: true
validates :game_id, presence: true
end
class Ruleset < ActiveRecord::Base
attr_accessible :title, :game_id, :user_id, :rule_id
validates :game_id, presence: true
validates :user_id, presence: true
validates :title, presence: true
belongs_to :user
belongs_to :game
has_and_belongs_to_many :rules
end

You should declare a specific action in the controller for adding rules to rulesets.
When a person selects a ruleset, it will be passed as a parameter and you can catch it in your newly declared action. Also, use a hidden_field_tag where you will store your rule_id.
In the newly declared action, create something like this:
def add_rule_to_ruleset
#ruleset = Ruleset.find(params[:ruleset_id])
#rule = Rule.find(params[:rule_id])
#ruleset.rules << #rule
redirect_to current_user.rules
end
Also fetch all the rulesets in the select box with current_user.rulesets, but i guess you will have to check if that ruleset has that rule already (you don't want the same rule twice or more times in the same ruleset, do you? ), so you should modify it.
Check that with something like current_user.rulesets.include?(rule)
Routes.rb:
resources :rules do
member do
put 'add_rule_to_ruleset'
end
end

Related

Rails Models: Where does this object come from?

Following code:
class Product < ApplicationRecord
validates :name, presence: true
validates :price, numericality: {
greater_than_or_equal_to: 0.0
}
validates :description, presence: true
belongs_to :user
def owned_by? owner
user == owner # Where does the user-obj. come from?
end
end
It works. What I don't get is: Where does the "user"-object come from? Please see the line with comment!
"user" is nowhere declared / assigned a value.
Does someone know how that works and can it explain to me?
From the ActiveRecord::Associations::ClassMethods#belongs_to API docs:
Methods will be added for retrieval and query for a single associated
object, for which this object holds an id:
association is a placeholder for the symbol passed as the name
argument, so belongs_to :author would add among others author.nil?.
Example
A Post class declares belongs_to :author, which will add:
Post#author (similar to Author.find(author_id))
...
So in your case, after declaring the belongs_to :user relationship you get that bunch of methods, among them user.

Rails - new User builds dependent Email and validates both?

I'm building a quick Rails project that allows users to manage their email addresses. Users can have many emails, but one (and only one) of those emails has to be marked as 'primary' (for login), and a user cannot exist without a primary email.
I've been struggling to get this to work right - it seems so circular to me. I need to build a User, and then the Email, but I don't want to save the User into the database unless the Email is valid, which it won't be until the User is saved (because of the validates :user, presence: true constraint).
Accepts nested resources for doesn't seem to work with .new (works fine with .create), and if my Email fails its validations, the User still shows as valid.
Been having a difficult time trying to find good resources (or SO questions) for building/validating multiple/dependent models from a single form.
What's the most Rails way to do this?
User
has_many :emails
has_one :primary_email, -> { where(primary: true) }, class_name: "Email"
accepts_nested_attributes_for :primary_email
validates :first_name, presence: true
validates :last_name, presence: true
validates :birthday, presence: true
validates :password_digest, presence: true
Email
belongs_to :user
validates :user, presence: true
validates :address, presence: true, uniqueness: {
case_sensitive: false
}
UsersController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
# do something
else
# show #user.errors
end
end
private
def user_params
params.require(:user).permit(
:first_name,
:last_name,
:birthday,
:password,
:password_confirmation,
:primary_email_attributes => [:address]
)
end
EDIT
The Email model also contains the following fields:
label = string, eg. 'Personal', 'Work', etc
primary = boolean, whether it's marked as primary email or not
confirmation_code = autogenerated on creation, used to confirm ownership
confirmed = boolean, whether it's been confirmed or not
class User
user has_many :emails
user has_one :primary_email, -> { where(primary: true) }, class_name: "Email", autosave: true
after_initialize {
build_primary_email if new_record?
}
end
class Email
# use gem https://github.com/balexand/email_validator
validates :my_email_attribute, :email => true
end
So after a user initialized its building a primary_email so that record is already associated, or at least it will be if it can be saved. the autosave is working pretty cool - if the primary-email can't be saved due validation error, the user can't neither. should work out of the box, im in a bus right now, can't check it. cheers
futher information: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
If validations for any of the associations fail, their error messages will be applied to the parent. That means, the Parent Model (in your case User) is having errors, and thats why the saving is not possible! that's what you are looking for.
I would store a primary email as a common field and additional emails some another way. I would prefer to store additional emails in another field too that is Array rather than in an associated table. You shouldn't store a primary email in another table. Just imagine, every time you need authorize user or just get his email you will perform an extra request to db.
Meant to post this months ago.
The solution, keeping users and emails normalized across different models without storing a primary email as an attribute on the user, is to use inverse_of:
User.rb
class User < ActiveRecord::Base
has_many :emails, inverse_of: :user, dependent: :destroy
accepts_nested_attributes_for :emails
validates :emails, presence: true
end
Email.rb
class Email < ActiveRecord::Base
belongs_to :user, inverse_of: :emails
validates :user, presence: true
end
This allows validations to be performed using in-memory objects, rather than via database calls (ie the object associations are being validated, rather than the presence of an id/record in the database). Therefore they both pass validation and can both be saved in the same transaction.
See: https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations

Rails validations and belongs_to association

In my rails projects I have a lot of association tables. And I have some validations. Nothing really difficult, and it works almost every times.
But from time to time (like tonight), I have to switch from
validates_presence_of :project_id
validates_presence_of :tag_id
validates_uniqueness_of :project_id, :scope => [:tag_id]
to
validates_presence_of :project
validates_presence_of :tag
validates_uniqueness_of :project, :scope => [:tag]
Do you know the difference ? Do you if one is better than the other ?
From the Rails Guides: http://guides.rubyonrails.org/active_record_validations.html#presence
2.9 presence This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either
nil or a blank string, that is, a string that is either empty or
consists of whitespace.
class Person < ActiveRecord::Base
validates :name, :login, :email, presence: true
end
If you want to be sure that an association is present, you'll need to
test whether the associated object itself is present, and not the
foreign key used to map the association.
class LineItem < ActiveRecord::Base
belongs_to :order
validates :order, presence: true
end
So, you should use the second example you gave, which tests if the associated object itself is present, and not the first example, which only tests if the foreign key used to map the association is present.

How to validate that a dependent row belongs to a user

I have a model called Category;
class Category < ActiveRecord::Base
belongs_to :user
belongs_to :group
validates :name, presence: true
validates :user_id, presence: true
end
And, I have a model called Group:
class Group < ActiveRecord::Base
belongs_to :user
has_many :categories
validates :name, presence: true
validates :user_id, presence: true
end
As you can see, a group can have many categories. When a user adds a category or updates it's group_id value, I want to check that the group belongs to that user. I don't want users adding to and update categories to another user's group. What is best practise to validate this prior to saving? Thanks
validate :ownership_of_group
def ownership_of_group
ids = []
ids << self.group_id
ids << self.group_id_was if self.group_id_was.present?
if(Group.find(ids).reject {|group| group.user_id == self.user_id}.present?)
# if all of them is owned by the user the array will return an empty array
errors.add(:user_id, 'You cant edit a category that is not yours')
end
end
If we say group_id we get the current value that is being set by the use.
If we say group_id_was it get the old value before the update.
In the update we need to handle both in the create we have no previous value.

Rails: Looking up foreign key id using virtual attribute failing

My app allows users to add words from a master words table into their own custom list. So, a word list contains multiple custom words each of which link to a master word.
In my view, I have a field called word_text (virtual attribute) where I let users enter a word, and in my model I am trying to look up the master_word_id and set it on the custom word table. I am unable to access the #word_text value in the model. I always seem to get an error that the master word is a required field (because the look up is failing).
class CustomWord < ActiveRecord::Base
attr_accessible :master_word_id, :word_list_id, :word_text
attr_accessor :word_text
belongs_to :word_list
belongs_to :master_word
validates :word_list, presence: true
validates :master_word, presence: true
before_validation :set_master_word
private
def set_master_word
logger.debug "Received word text #{#word_text}"
_mw_id = nil
if !#word_text.nil?
master_word = MasterWord.find_word(#word_text)
if master_word.nil?
errors.add("#{#word_text} is not a valid word")
else
_mw_id = master_word.id
end
end
self.master_word_id = _mw_id
end
end
I sincerely appreciate any suggestions as to how I can set the value of the master_word_id correctly.
There are several things to fix:
class CustomWord < ActiveRecord::Base
attr_accessible :master_word_id, :word_list_id, :word_text
attr_accessor :word_text
belongs_to :word_list
belongs_to :master_word
validates :word_list, presence: true
#validates :master_word, presence: true <= This will always throw error
validates :word_text, presence: true
validates :master_word_id, presence: true
before_validation :set_master_word
private
def set_master_word
logger.debug "Received word text #{self.word_text}"
self.master_word_id = MasterWord.find_by_word_text(self.word_text).id
end
end
Not sure if it will work because I don't know the rest of your app but I hope it points you in the right direction.

Resources