I have such action in my controller:
def my
#user = Ads::User.find current_user.id
#postings = Rails.cache.fetch("#user.postings.includes(:category)") do
#postings = #user.postings.includes(:category)
end
end
I'm trying to cache #postings and get such error:
Marshalling error for key '#user.postings.includes(:category)': can't dump anonymous class #<Module:0x000000048f9040>
You are trying to cache a Ruby object which cannot be serialized to memcached.
If I try to cache #postings without includes there are no errors.
Can't figure out what is the problem.
You can find relevant models in the bottom:
module Ads
class User < ::User
has_many :postings, dependent: :destroy
end
end
module Ads
class Posting < ActiveRecord::Base
belongs_to :user, counter_cache: true
belongs_to :category
end
end
module Ads
class Category < ActiveRecord::Base
has_many :postings, dependent: :destroy
end
end
The cache fetch code is all wrong. The parameter to fetch is a string that identifies the data you want. Your code is trying to use the same string for every user, so they would all see the same postings that would be saved by the first call of this method.
In my example below I used the user id and a string 'postings' to indicate all postings for a specific user.
It is incorrect to assign #postings inside the fetch block, the result of the block (the query result) is saved to #postings
Finally, ActiveRecord delays making the actual database call until absolutely necessary. The .all call at the end of the query will return the data, and the data is what you want cached, not the configuration data used to create a query.
Here is the correct code:
#postings = Rails.cache.fetch("#{#user.id}:postings") do
#user.postings.includes(:category).all
end
It's likely complaining about this:
class User < ::User
Is there any reason why you're not using:
class User < ActiveRecord::Base
Related
I have these 2 models as follow
class Application < ActiveRecord::Base
has_many :commitments, class_name: "Commitment", \
:source => :application, dependent: :destroy
accepts_nested_attributes_for :commitments
after_create: update_case_code
end
class Commitment < ActiveRecord::Base
belongs_to :application
after_create: send_notification
def send_notification
ap self.application.case_code
end
end
class ApplicationsController < ApplicationController
def create
#application = Application.new(params)
#application.save
end
end
In my application_controller whenever i create a new Application record,a new record is also created in the Commitment and it tries to get the case_code from the application record but the after_create method of the application model hasnt been executed yet.
Is there any way to optimize this code so that it works properly?
Probably there is. Probably you can also use another callback on the application model which happens before, there are plenty of them. See Active Record Callbacks
However this is exactly the case, which other people call rails callback hell
The best practice here would be just creating a form object, which creates the data in the order you need and remove the callbacks
class ApplicationCommitmentForm
include ActiveModel::Model
attr_accessor ...
def submit
a = Application.create ..
a.update_case_code
a.commitments.create ...
end
end
See ActiveModel Form Objects
Btw you could also wrap the submit code into a transactions ensuring that either all records are created or in case of any errors nothing at all.
Theory :- after create of a record in customer bill, i am sending two sets of data two different models. one set of data is sent to ledger and one set of data is sent to ledger_line_item. the complexity is that after sending of data i want the ledger_id to be stored in ledger_line_item. the code is as follows
code :-
class CustomerBill < ActiveRecord::Base
after_create :creating_ledger_line_items, :creating_ledger_items
def creating_ledger_items
CustomerLedger.create(:customer_id =>self.customer_id,/*rest of attributes*/)
end
def creating_ledger_line_items
CustomerLedgerLineItem.create(:customer_id =>self.customer_id,/*rest of attributes*/)
end
end
in ledger i have written
class CustomerLedger < ActiveRecord::Base
after_save :update_record_line_items
def update_record_line_items
a = CustomerLedgerLineItem.find_by_customer_id(self.customer_id)
a.update_attributes(:customer_ledger_id => self.id)
end
end
the above code works fine without error but the ledger_id is not been posted in ledger_line_items. i am not able to determine why this error is happening? is there any other way i can achieve my goal of posting ledger_id in ledger_line_items after a bill is created?
Guidance Required. Thanking you in advance.
You can change your models something as follows.:
I am assuming you have Customer Model.
class Customer < ActiveRecord::Base
has_one :customer_ledger
has_many :customer_ledger_line_items, :through => :customer_ledger
accepts_nested_attributes_for :customer_ledger
end
class CustomerLedger < ActiveRecord::Base
has_many :customer_ledger_line_items
accepts_nested_attributes_for :customer_ledger_line_items
end
class CustomerBill < ActiveRecord::Base
belongs_to :customer
after_create :creating_ledger_items, :creating_ledger_line_items
def creating_ledger_line_items
cl = self.customer.customer_ledger.build(your_attributes)
cl.save!
end
def creating_ledger_items
cli = self.customer.customer_ledger.customer_ledger_items.build(your_attributes)
cli.save!
end
end
In case you want to create the models on an *after_create* hook, I'll explain what's the problem.
When you create a model in rails, and you have hooks like *after_create*, *before_update*, etc. all the updates happens in a Transaction, so if any of them throws an exception, nothing is updated.
In this case, within a Transaction, you are trying to get the ID of a CustomerLedger that doesn't exists yet, because since everything is within a Transaction, the record is not saved to the database until the transaction is executed, and thats the reason that on CustomerLedger#update_record_line_items, self.id is always nil.
Using the nested attributes proposed by codeit is probably the best solution to your problem, but if you feel that nested attributes its an advance topic, you can do something like:
class CustomerBill < ActiveRecord::Base
after_create :created_leder_data
def create_ledger_data
customer_ledger = CustomerLedger.build(customer_id: self.customer_id, # Rest of attributes)
customer_ledger.customer_ledger_line_items.build(customer_id: self.customer_id, # Rest of attributes)
customer_ledger.save!
end
end
I'm working on an app that downloads meta tags from websites and saves then. The downloading happens in a model called Site. I'd like to save off the downloaded robots meta tags into a model called robots_tag which is connected to sites via a join table called meta_tag_sites.
But the method that I've written in the sites model to do this isn't working. When I try to call the method in the console, I get the following error.
undefined method `robots_meta=' for []:ActiveRecord::Relation
Any idea what I'm doing wrong?
class Site < ActiveRecord::Base
attr_accessible :domain
belongs_to :user
has_many :meta_tag_sites
has_many :robots_tags, through: :meta_tag_sites
accepts_nested_attributes_for :robots_tags
# ...
def download_robots_meta_tags
robots_tags = Nokogiri::HTML(Net::HTTP.get(self.domain, "/")).xpath("//meta[#name='robots']")
robots_tags.each do |tag|
self.robots_tags.robots_meta = tag
end
end
# ...
end
class RobotsTag < ActiveRecord::Base
attr_accessible :robots_meta
has_many :meta_tag_sites
has_many :sites, through: :meta_tag_sites
end
class MetaTagSite < ActiveRecord::Base
attr_accessible :site_id, :meta_tag_id
belongs_to :site
belongs_to :robots_tag
end
(BTW, this post is related to an earlier post: Web-scraping Rails App Getting Over-Modelled?).
The problem is here:
self.robots_tags.robots_meta = tag
self.robots_tags is a collection of objects defined by has_many :robots_tags, and you're attempting to assign a specific attribute to that entire collection. You can't do this. If you want to assign to an attribute on a specific object, you have to either iterate over the collection, or select a specific object from the collection via first or last or any of the other Enumerable methods.
By inspection, the offending line appears to be:
self.robots_tags.robots_meta = tag
You should iterate over self.robots_tags instead, with something like:
self.robots_tags.each do |robot_tag|
robot_tag.robots_meta = tag
end
I have two models
class User < ActiveRecord::Base
has_many :posts
searchable do
text :post_titles
end
def post_titles
posts.map &:title
end
end
class Post < ActiveRecord::Base
belongs_to :user
end
the problem is that when I update title of the Post sunspot doesn't update index for related user and it is not searchable by new data. If I do User.index it solves problem but takes too much time. Are there any better solutions to update parent record index on child record change(like reindex just parent record and not all users)?
Sunspot provides an instance index() method, for indexing one record.
What I did was
class Post
belongs_to :user
after_save :update_user_index
private
def update_user_index
user.index
end
end
If you are running this in console, and want to see results immediately, call Sunspot.commit
got 2 models:
class User < ActiveRecord::Base
has_many :posts
end
and
class Post < ActiveRecord::Base
belongs_to :user
end
the Posts table has a column: u_hash. This is supposed to be a randomly generated identifying hash (for public viewing). What is the best way to generate this hash and how can I add it to the table? The idea is that all this will happen in the background and not be visible to the user (no hidden field in the form). The database used is MySQL if that could help me out somehow.
Thanks in advance!
J
You most likely need before_validation_on_create callback for your Post model. This callback is internally called by ActiveRecord functionality when you save a new Post record into database.
A good callback reference and a hint of the order callbacks are called in you can find here.
Here's a code, that explains why it is before_validation_on_create that you need to use:
class Post < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :u_hash
before_validation_on_create :generate_u_hash
def generate_u_hash
begin
new_u_hash = "random hash here"
end while Post.find_by_u_hash(new_u_hash)
self.u_hash = new_u_hash
end
end
This sounds like a job for ActiveRecord callbacks.
If your posts tables has a before_create callback, you can create and set a value automatically every time a new post instance is created.
e.g.:
class Post < ActiveRecord::Base
belongs_to :user
before_create :set_uhash_column
private
def set_uhash_column
#your code here - something like self.uhash = ...
end
end