I am upgrading a Rails 3 app to rails 5.1. In Rails-3 model I have a condition like
has_one :current_contract, :class_name => 'Contract',
:conditions => Proc.new { "contract_vw.country = '#{country_id}'"
if country_id }
I guess previous developer follow this hack
https://makandracards.com/makandra/983-dynamic-conditions-for-belongs_to-has_many-and-has_one-associations
I am not sure how to convert it to Rails 5.1
Any help appreciated.
The Rails 4+ way is to write the scope like so:
has_one :account, -> (country_id) { where('contract_vw.country = ?', country_id) }, class_name: 'Contract'
You've written the if country_id into the association though, which seems real weird to me. Although where('contract_vw.country = ?', country_id) if country_id might work, I'd probably extract that into a method like:
def country?
country_id.present?
end
And then, wherever you need it:
#model.account if #model.country?
If I understand your use-case correctly you're not bound to using has_one and in this instance I think it should not be used, use a regular method instead.
def current_contract
contracts.where("contract_vw.country = ?", country_id).first if country_id.present?
# or ...
contracts.find_by(country: country_id)
end
Related
I'm having a problem with the Rails collection.build(attrs) method, specifically with how the framework instantiates a new record. For example, here is a much simplified view of my models:
class Document < ActiveRecord::Base
belongs_to :account
has_many :descriptions, before_add: :id_test
validates :account, presence: true
def id_test
puts self.account_id
end
end
When I do something like:
current_account.documents.build(:descriptions => [desc])
then id_test prints nothing. That is, the account_id is not set in the before_add callback (and yes, I've tried the after_add callback as well; account_id is not set in that case either).
If I do:
d = current_account.documents.build
d.assign_attributes(:descriptions => [desc])
Then everything works as expected. However, I would prefer a better alternative, since this would be a pain to implement in the controllers...
Is there a way to get Rails to add the foreign_key first, or is there some better way to set this up? I haven't gone back to check for sure, but this seems different than the way Rails 3 evaluated collection.build statements.
EDIT
Looking through the Rails code a bit, I see I can do:
current_account.documents.build do |record|
record.assign_attributes(:descriptions => [desc])
end
and everything works as expected. Although a bit more verbose, I guess this is technically more accurate anyway.
I would suggest using
Document.build(:account_id => current_account.id, :descriptions => [desc])
The two codes below are for updating the database.
The first version, which uses the 'update_attributes' property is not working
#existing_exp = Emiexperiment.find(:first, :conditions => [ "EMI_COM_ID = ? and EMI_SUB_FK = ?", "EMI_999", "7789"])
#existing_exp.update_attributes(
:EMI_STATUS => "present",
:EMI_ADD_STATUS => "weak"
)
However, the code below, which uses the 'update_All' attribute seems to work great.
Emiexperiment.update_all "EMI_STATUS = 'present', EMI_ADD_STATUS = 'moderate'", ["EMI_COM_ID = ? and EMI_SUB_FK = ?", "EMI_999", "7789"]
Here is the class code for Emiexpression:
class Emiexperiment < ActiveRecord::Base
set_table_name "EMI_EXPERIMENT"
set_primary_key "EMI_OID"
attr_accessible :EMI_STATUS, :EMI_ADD_STATUS, :EMI_COM_ID, :EMI_SUB_FK
belongs_to :sub, :foreign_key => "EMI_SUB_FK"
end
I am confused as to why this is so.
I am not using any validation in my 'Emiexperiment' model.
Any hint on this is most appreciated. Many many thanks for your help :)
You may want attr_accessor for those fields. attr_accessor is a ruby method that makes a getter and a setter.
attr_accessible is a Rails method that allows you to pass in values to a mass assignment:, e.g. update_attributes(attrs).
I'm having a stack level too deep error using Ruby 1.8.7 with Rails
3.0.4 and with the rails console I performed the following commands.
leo%>rails console
Loading development environment (Rails 3.0.4)
ruby-1.8.7-head > leo = Organization.find(1)
SystemStackError: stack level too deep
from /app/models/organization.rb:105:in `parents'
Here is the object that is having issues..
class Organization < ActiveRecord::Base
has_many :group_organizations, :dependent =>
:delete_all
has_many :groups, :through => :group_organizations
has_many :orders
has_many :product_contracts
has_many :people
accepts_nested_attributes_for :people
has_many :addresses
accepts_nested_attributes_for :addresses
has_many :organizations
has_many :departments
has_many :organization_credits
has_many :documents
validates_presence_of :name
def self.parents
#organizations = Organization.where("is_company = ?",true)
##organization_parents = []
select_choice = I18n.t("select") + " "+ I18n.t("segments.description")
#organization_parents = [select_choice]
for organization in #organizations
#organization_parents << [organization.name, organization.id]
end
return #organization_parents
end
This error generally happens when you accidentally recursively changing an attribute. If you have a username attribute in User model, and a virtual attribute named username, that is directly changing the username, you end up calling the virtual, the virtual calls the virtual again and so on.. Therefore, take a look on whether something like that happens somewhere in your code.
The stack level too deep error occurs also, if you want to destroy a record and you have an association with :dependent => :destroy to another model. If the other model has a association with :dependent => :destroy back to this model, the stack level is too deep, too.
I had a "stack-level too deep" issue too. it was due to recursiveness in one of my functions and had been caused by a typo as you can see from below:
def has_password?(submitted_password)
encrypt_password == encrypt(submitted_password)
end
private
def encrypt_password
self.salt = make_salt unless has_password?(password)
self.encrypted_password = encrypt(password)
end
I realised I had to change the second line to encrypted and it worked. Just checkout for recursion in your code it must be happening somewhere. Unfortunately I can't be of better use since I can't look at all your code files.
I was getting same stack level too deep error & it turns out that the issue was of recurring rendering of a partial.
I happened to call render a_partial in main view and then in the partial, I accidentally called the same partial again.
HTH
As you are not showing all the code, I can only speculate that you have defined inspect or to_s to build a string containing, among other things the parents.
Your current parents method doesn't seem to be doing anything reasonable, as it returns all organisations that are companies, no matter which association you start from. Thus, any company has itself as parent. Attempting to convert it to string will induce an infinite loop to try to show the parents' of the parents' of ...
In any case, the bulk of your parents method should be in a helper, called something like options_for_parents_select, because that's what it seems to be doing? Even then, the first empty choice should be passed as allow_null to select.
The fact that it sets instance variables is a code smell.
Good luck
I've found the solution to this issue...
I'm using Rails 3 and my class looks like this (and the problematic methods was this too)
class Organization < ActiveRecord::Base
def self.parents
#organizations = self.find :all, :conditions => ['is_company = ? ',true]
select_choice = I18n.t("select") + " "+ I18n.t("segments.description")
#organization_parents = [select_choice]
for organization in #organizations
#organization_parents << [organization.name, organization.id]
end
return #organization_parents
end
#...
end
I did have to hack a lot in the code to find out something was wrong with the named_scope on the line
#organizations = self.find :all, :conditions => ['is_company = ? ',true]
So I had to change it to something like this
#organizations = Organization.where("is_company = ?",true)
But it was wrong too.. So I decided to add an scope for this below the class name so the final code looks like this:
class Organization < ActiveRecord::Base
scope :company, where("is_company = ?",true)
def self.parents
#organizations = self.company
select_choice = I18n.t("select") + " "+ I18n.t("segments.description")
#organization_parents = [select_choice]
for organization in #organizations
#organization_parents << [organization.name, organization.id]
end
return #organization_parents
end
#...
end
So using this line with the scope
#organizations = self.company
it worked flawlessly in every part of the code.
I was wondering if the named_scope is deprecated when using class methods or they are not supported from now and throws an error and not a warning before
Thanks for your help
Leo
If you are getting this error it means rails version that you are using in your application is not compatible with Ruby Version.
Solutions you can use to solve this issue.
1) You need to downgrade the ruby version to older version.
2) or you need to upgrade Rails to latest version.
I got this error when incorrectly creating a has_many relationship like this:
has_many :foos, through: foo
So don't put the same model as 'through' or it will loop endlessly.
I've read this article, but it's for Rails 1.x.
I'd really like to create my own association methods:
user = User.find(1)
# Example of a normal association method
user.replies.create(:body => 'very informative. plz check out my site.')
# My association method
user.replies.find_by_spamminess(:likelihood => :very)
In Rails 3, what's the proper way of doing this?
The Rails 3 way of doing things is often to not use find methods, but rather scopes, which delays the actual database call until you start iterating over the collection.
Guessing at your first example, I would do:
in class Reply ...
scope :spaminess, lambda {|s| where(:likelyhood => s) }
and then using it:
spammy_messages = user.replies.spaminess(:very)
or to use it in a view
spammy_messages.each do |reply|
....
end
I think I found it!
If you search for "association extensions" the Rails API page for ActiveRecord::Assications, you'll see that this is the syntax (copied from that link):
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
I have a MailingList model that has_may :people
For most of my application, I only want to get people that are active
So #mailing_list.people should only return people that are active
In my model, I can't do
def people
self.people.find_all{ |p| !p.activated_at.nil? }
end
because that keeps calling itself. What is the ruby/rails way to automatically filter the people. Another possible issue is that I think self.people returns an array of active record objects where self.people.find_all... will return an array. This will cause some of my code to break. It's easy fixes but is there a way to return active record objects? It would be nice to have the option.
Thanks!
This is a perfect example for a named scope:
class Person < ActiveRecord::Base
named_scope :active, :conditions => 'activated_at is not null'
end
Then just call it:
# equivalent to Person.find(:all, :conditions => 'activated_at is not null')
#active_people = Person.active
You can also filter at the association level.
has_many :people, :conditions => {:activated => true}
You can used the standard find method or a dynamic finder. Your find might read as follows:
people.find(:all, :conditions => "activated_at = nil")
OR
people.find_all(:conditions => "activated_at = nil")
A dynamic version of this might read as:
people.find_by_activated_at(nil)