Rails: Looking up foreign key id using virtual attribute failing - ruby-on-rails

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.

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.

before validation do something, no method error

I have an model for notifications.
An notification can be posted by an User or an Contact.
and the notification can go to either a business or a notification_area.
people has to be filled, so when there is no user added it has to fill in the Contact that is logged in.
the notification_to has to be filled to so when there is no business added it has to take the latitude and the longitude and add the right area.
I have written the code but it won't work.
First I added it in the controller. But after looking around on google and this site I found I had to add it to the model.
But it still won't work.
What do I do wrong?
I get an error
class Notification < ActiveRecord::Base
belongs_to :people, :polymorphic => true
belongs_to :notification_to, :polymorphic => true
belongs_to :App_of_area
belongs_to :kind
before_validation :if_empty_add_the_other
#validates :photo, presence: true
validates :message, :status, :people_id, :people_type, :notification_to_id, :notification_to_type, :App_of_area_id, :kinds_id, presence: true
def if_empty_add_the_other
unless self.people_type.present?
self.people = current_contact
end
unless self.notification_to_id.present?
if self.longitude && self.latitude
#noa = NotificationArea.where( "latitude_A <= #{self.latitude} AND latitude_B >= #{self.latitude} AND longitude_A <= #{self.longitude} AND longitude_B >= #{self.longitude}")
self.notification_to = #noa.first
end
end
end
end
end
First thing first you should DRY up your code:
belongs_to :business
belongs_to :app_of_area
has_many :people, source_type: "Notification"
has_many :api_keys
before_validation :if_empty_add_the_other
has_secure_password
validates :name, :email, :rights, :password_confirmation, :app_of_area_id, :business_id, presence: true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
validates :email, uniqueness: true
validates :password, confirmation: true, length: { minimum: 6 }
:as is used for polymorphic associations, I believe you want to use :source_type as shown above. This would allow you to perform self.people << current_contact, but I address this more below.
Why was :App_of_area capitalized? Same with :App_of_area_id?
Your if_empty_add_the_other validation method has a lot wrong with it.
Use unless rather than if not.
Can the two if statements testing latitude and longitude be combined to if self.longitude && self.latitude?
You have to ask yourself, how is current_contact being passed to this function? Also, you're trying to set self.people equal to this phantom current_contact; self.people would contain multiple records, an array if you will, so setting an array equal to an object won't work, hence the self.people << current_contact above.

How can you check if all of a model's attributes have a value set?

I currently have the following method:
def complete?
attributes.delete_if{|k,v| k == 'id' or k == 'user_id'}.values.each do |value|
return false if value.blank?
end
true
end
Is there a better way of doing this? I just want to know if, with my instance, all the attributes have been set apart from id and user_id. There must be a better way of doing this.
I'm on rails 3 and ruby 1.9.3 (just so people don't give answers that will work with newer versions)
There is the validates method to do that:
class User < ActiveRecord::Base
validates :full_name, :username, :email, :address, presence: true
If you want to validate the presence of every attributes except few ones:
class User < ActiveRecord::Base
validates *(self.column_names - ['id', 'created_at', 'updated_at']), presence: true
Above exemple extended:
class User < ActiveRecord::Base
validates *self.validable_columns, presence: true
def self.validable_columns
excluded_columns = ['id', 'created_at', 'updated_at'] # columns to be excluded in the `validates`
self.column_names - excluded_columns
end

Update a single attribute in a model using Rails 3

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

Model has_many OtherModel but localized, so different OtherModel for same Model based on locale: how?

I'm having hard time with this, it's not a direct problem of implementation but I don't understand which is the right way to do it, I have two options, but first, these are my models:
class Boat < ActiveRecord::Base
validates :name, presence: true, uniqueness: { case_sensitive: false }
has_many :tech_specs, order: 'position'
def visible?
self.visible
end
end
class TechSpec < ActiveRecord::Base
validates :boat_id, presence: true
validates :tech_spec_name_id, presence: true, uniqueness: { scope: :boat_id }
belongs_to :boat
belongs_to :tech_spec_name
before_destroy :destroy_name_if_required
acts_as_list scope: :boat
def as_json(options = {})
super(options.except!(:tech_spec_name_id).merge!(methods: [self.name]))
end
def name
self.tech_spec_name.try(:name) || ''
end
def name=(value)
self.tech_spec_name = TechSpecName.find_or_create_by_name(value)
end
def destroy_name_if_required
self.tech_spec_name.destroy if self.tech_spec_name.tech_specs.size <= 1
end
end
class TechSpecName < ActiveRecord::Base
validates :name, presence: true, uniqueness: { case_sensitive: false }
has_many :tech_specs
def self.with_name_like(str)
where('lower(name) LIKE lower(?)', "%#{ str }%")
end
end
The problem is that I want a page for a boat showing some tech specs when with a locale and when on a different locale, showing other tech specs.
Idea #1
My basic idea is to add to TechSpec globalize3 on tech_spec.value and on TechSpecName for field tech_spec_name.name
Idea #2
The other idea is to remove TechSpecName and, instead, use a field (tech_spec.name) that will "replace" completely TechSpecName. Notice that in this case, I'll still need to fetch names for autocomplete, but I will filter them in TechSpec instead of fetching all from TechSpecName. This field will use globalize3 again obviusly.
I don't know the downside of both approaches, so I need a suggestion.
Seems like idea #1 is ok, it works correctly and reduce the amount of repeated text inside Db.
I18n.with_locale helps a lot too, also Globalize.with_locale is helpful

Resources