I have a LabCollection:
class LabCollection < ApplicationRecord
# Relationships
belongs_to :lab_container, polymorphic: true, optional: true
has_many :lab_collection_labs
has_many :labs, -> { published }, through: :lab_collection_labs
has_many :lab_collection_inclusions, dependent: :destroy
end
It has many LabCollectionLabs:
class LabCollectionLab < ApplicationRecord
acts_as_list scope: :lab_collection_id, add_new_at: :bottom
belongs_to :lab_collection
belongs_to :lab
end
Which has a lab ID and belongs to a lab.
I have a spec which tests how new associations are created, and it's failling at the following point:
context 'when the lab container has labs already present' do
it 'removes the present labs and adds the new ones' do
subject = RawLabAdder
expect(populated_lab_collection.labs).to eq labs
subject.new(populated_lab_collection, [lab4.id, lab5.id]).perform
expect(populated_lab_collection.labs).not_to eq labs
binding.pry
expect(populated_lab_collection.labs).to eq [lab4, lab5]
end
end
If you need to see the internal working of the code let me know, however the issue seems to be with RSpec and refreshing associations. When I hit the binding.pry point and call populated_lab_collection.lab_collection_labs
populated_lab_collection.lab_collection_labs
=>
[lab_collection_lab_4, lab_collection_lab_5]
However when I call .labs instead:
populated_lab_collection.labs
=>
[]
Inspecting the lab_collection_labs, I can see that they each have a lab_id and that a lab exists for those ID's. I believe my problem is that I'm not refreshing the records correctly, however I've tried:
# populated_lab_collection.reload
# populated_lab_collection.lab_collection_labs.reload
# populated_lab_collection.lab_collection_labs.each do |x|
# x.lab.reload
# end
# populated_lab_collection.labs.reload
Any advice on how I can get RSpec to correctly read in a records nested associations is greatly appreciated. As I say when I inspect the record, it has 2 lab_inclusion_labs, which each have a lab, however the parent record apparently has no labs.
EDIT: RawLabAdder class:
module LabCollections
class RawLabAdder
def initialize(incoming_lab_collection, lab_ids = [])
#lab_ids = lab_ids
#lab_collection = incoming_lab_collection
end
def perform
remove_associated_labs
add_labs
end
private
def add_labs
#lab_ids.each do |x|
lab = Lab.find(x)
LabCollectionInclusionAdder.new(#lab_collection, lab).perform
end
end
def remove_associated_labs
#lab_collection.lab_collection_inclusions.map(&:destroy)
#lab_collection.lab_collection_labs.map(&:destroy)
end
end
end
If you create an instance in a before hook or in a spec and then perform some database related work on it the instance you have will no longer reference the up to date information.
Try reloading the populated_lab_collection before asserting.
expect(populated_lab_collection.reload.labs).not_to eq labs
I have two models:
class Basket < ActiveRecord::Base
has_many :products
def recalculate_price!
price = products.map(&:price).sum
save
end
end
class Product < ActiveRecord::Base
belongs_to :basket
after_save :update_basket
def update_basket
basket.recalculate_price!
end
end
and than I call it like this:
basket = Basket.new
basket.products.build(price)
basket.save
The problem: basket.price don't update.
I've investigated the problem and found out that there is some kind of caching in update_basket method. The problem could be solved with placing reload before basket.recalculate_price!.
Is there any option to leave the update_basket method untouched? I have many such cases in my application.
I would like to understand how it works and avoid similar problems in the future.
note: I recently upgraded rails from 3.2 to 4.2. In a previous version everything worked fine.
Is there any option to leave the update_basket method untouched?
Absolutely. You can place a condition on your after_create to avoid executing it some times:
after_save :update_basket if: :basked_should_be_updated
[...]
private
def basked_should_be_updated
[...]
# return true or false in here
end
I've got a cyclic dependency when worked with fabrication gem. Here I'll show you what I've did. Let's suppose I have 2 models:
class User < AR::Base
has_many :messages
class Message < AR::Base
belongs_to :user
So, the fabricators for them will be:
Fabricator(:user) do
# bla-bla-bla
messages(count: 5)
end
Fabricator(:message) do
# bla-bla-bla
user
end
It seems all right, yeah? But when I run Fabricate(:user) or Fabricate(:message) I get cyclic dependencies, because of fabricating of message fabricates new user, fabricating new user fabricates a messages for him and so on. How can I avoid this diabolic circle?
I would typically have two user fabricators in an instance like this.
Fabricator(:user)
Fabricator(:user_with_messages, from: :user) do
messages(count: 5)
end
You could alternatively do this to make what you have work.
Fabricator(:user) do
messages(count: 5) { Fabricate.build(:message, user: nil) }
end
The messages will be saved automatically by AR when the user is saved. It will handle setting up the correct references.
Hi hopefully somebody can help me out. I'm a bit stuck at the moment. I'm trying to create an app for a tracking system, I currently have a table called sdel_hashed. Following online videos I so far set up digest/sha1 to work partly. If I enter the following commands in the console:
sdel = Sdel.find(1)
sdel.hashed_sdel = Sdel.hash('secret')
sdel.save
And then view the record in the browser it show up as the hash and not secret, but if I try and enter the word secret through the new action it doesn't get hashed. I think there is maybe something missing in the create action but I cannot find answers anywhere. i would greatly appreciate any help. I'll include now what I have in my controller and model.
Thanks
model sdel
require 'digest/sha1'
class Sdel < ActiveRecord::Base
attr_accessible :hashed_sdel
def self.hash(sdel="")
Digest::SHA1.hexdigest(sdel)
end
end
controller sdels
class SdelsController < ApplicationController
def list
#sdel = Sdel.all
end
def new
#sdel = Sdel.new
end
def create
#sdel = Sdel.new(params[:sdel])
if #sdel.save
redirect_to(:action => 'list')
else
render('new')
end
end
end
Migration file
class CreateSdels < ActiveRecord::Migration
def change
create_table :sdels do |t|
t.string "hashed_sdel"
t.timestamps
end
end
end
Sounds like you may want to use a before_save filter to invoke the hash class method on the Sdel model prior to saving when the attribute has been modified. Perhaps something along the lines of this:
require 'digest/sha1'
class Sdel < ActiveRecord::Base
attr_accessible :hashed_sdel
before_save { self.hashed_sdel = self.class.hash(hashed_sdel) if hashed_sdel_changed? }
def self.hash(sdel="")
Digest::SHA1.hexdigest(sdel)
end
end
This way, if you have a form that has a text_field for your hashed_sdel attribute, it will automatically get run through the hash class method you have before it the record gets saved (assuming the attribute has changed from it's previous value).
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.