I am having a problem with a Ruby SQL look up function: it always returns a null.
Here is the terminal output:
SELECT COUNT("point_store_items"."point_cost")
FROM "point_store_items"
INNER JOIN "point_store_items_point_store_orders"
ON "point_store_items"."id" = "point_store_items_point_store_orders"."point_store_item_id"
WHERE "point_store_items_point_store_orders"."point_store_order_id" IS NULL
The code causing this problem is:
def self.pointcost
self.count(:point_cost)
end
If this code is changed to say
def self.pointcost
40
end
it has no issues except the prices always listed is 40, not what should come from the database.
Relevant Model Code:
class PointStoreItem < ActiveRecord::Base
attr_accessible :product_id, :point_cost
has_and_belongs_to_many :orders, :class_name => "PointStoreOrder"
has_many :users, :through => :orders
belongs_to :product
def to_param
"#{id}-#{product.name.parameterize}"
end
def self.pointcost
self.count(:point_cost)
end
end
The model that calls for the pointcost
class PointStoreOrder < ActiveRecord::Base
include Workflow
attr_accessible :point_transaction_id, :user_id, :workflow_state , :shipping_provider, :tracking_number, :items
has_and_belongs_to_many :items, :class_name => "PointStoreItem"
belongs_to :user
belongs_to :point_transaction
accepts_nested_attributes_for :items
workflow do
state :cart do
event :purchase, :transitions_to => :purchased
event :cancel, :transitions_to => :cancelled
end
state :purchased do
event :ship, :transitions_to => :shipped
end
state :shipped
state :cancelled
end
def purchase
unless self.user.point_balance >= total
halt "Insufficient point balance"
else
self.point_transaction = user.point_transactions.new
self.point_transaction.point_count = total
self.point_transaction.save!
self.save!
end
end
def total
self.items.pointcost
end
def on_cancelled_entry(old_state, event, *args)
end
end
Related
This one is a bit confusing.
I think the line that is problematic is in the controller and it's this line in particular:
recipe_tools = (recipe.recipe_tools + RecipeTool.generic)
My models:
class Recipe < ActiveRecord::Base
...
has_many :recipe_tools, dependent: :destroy
...
end
class RecipeTool < ActiveRecord::Base
belongs_to :story
end
class Story < ActiveRecord::Base
...
has_many :recipe_tools, dependent: :destroy
..
end
This is my controller:
module Api
module Recipes
class RecipeToolsController < Api::BaseController
before_filter :set_cache_buster
def index
# expires_in 30.minutes, public: true
recipe = Recipe.find(params[:recipe_id])
recipe_tools = (recipe.recipe_tools + RecipeTool.generic)
binding.pry
render json: recipe_tools, each_serializer: Api::V20150315::RecipeToolSerializer
end
end
end
end
This is my serializer:
module Api
module V20150315
class RecipeToolSerializer < ActiveModel::Serializer
cached
delegate :cache_key, to: :object
attributes :id,
:display_name,
:images,
:display_price,
:description,
:main_image,
:subtitle
def display_name
object.display_name
end
def images
object.story.get_spree_product.master.images
end
def display_price
object.story.get_spree_product.master.display_price
end
def description
object.story.description
end
def main_image
object.story.main_image
end
def subtitle
object.story.get_spree_product.subtitle
end
def spree_product
binding.pry
spree_product.nil? ? nil : spree_product.to_hash
end
private
def recipe_tool_spree_product
#spree_product ||= object.story.get_spree_product
end
end
end
end
This is my RecipeTool model:
class RecipeTool < ActiveRecord::Base
...
scope :generic, -> { where(generic: true) }
end
In the controller, we call recipe.recipe_tool only once and so I don't think we need to includes recipe_tool. We're not iterating through a collection of recipes and calling recipe_tool on each one so no N+1 problem.
However, we are creating a collection of recipe_tools in the controller by concatenating two collections of recipe_tools together. Recipe.generic is also a SQL query that generates generic recipe_tools.
I think the N+1 problem is happening in generating the JSON response via the serializer. We call recipe_tool.story a lot which would generate a SQL queries each time we call #story and we do that on a collection of recipe_tools.
First, I would fix your associations using :inverse_of, so that I wouldn't have to worry about rails reloading the objects if it happened to traverse back up to a parent object. ie
class Recipe < ActiveRecord::Base
...
has_many :recipe_tools, dependent: :destroy, :inverse_of=>:recipe
...
end
class RecipeTool < ActiveRecord::Base
belongs_to :story, :inverse_of => :recipe_tools
belongs_to :recipe, :inverse_of => :recipe_tools ## this one was missing???
end
class Story < ActiveRecord::Base
...
has_many :recipe_tools, dependent: :destroy, :inverse_of=>:story
..
end
Next I would eager_load the appropriate associations in the controller, something like:
ActiveRecord::Associations::Preloader.new.preload(recipe_tools, :story =>:recipe_tools, :recipe=>:recipe_tools)
before calling the serializer.
I have the following two models:
class Process < ActiveRecord::Base
has_many :activities, inverse_of: :artifact, dependent: :destroy
attr_accessible :name, :activities_attributes
def update_status!
if self.activities.all? {|a| a.completed? }
self.status = 'completed'
elsif self.activities.any? {|a| a.completed? }
self.status = 'in_progress'
else
self.status = 'not_started'
end
save!
end
end
class Activity < ActiveRecord::Base
belongs_to :process, inverse_of: :activities
attr_accessible :name,:completed_date
scope :completed, where("completed_date is not null")
end
Then in my Controller:
#activity = Activity.find(params[:id])
#activity.completed_date = Time.now
#activity.process.update_status!
If I put a debugger directly after this line, and print out #activity.completed it returns true, however #artifact.status is still "not_started" (assume no other activities).
However, if I add the following line before the update:
#activity.process.activities[#activity.process.activities.index(#activity)] = #activity
The status is updated correctly.
Why doesn't the change to #activity propagate into process.activities? And how can I make it propagate?
I don't this inverse_of works with has_many through. See this article: ActiveRecord :inverse_of does not work on has_many :through on the join model on create
Here is the relevant blurb from the RailsGuides:
There are a few limitations to inverse_of support:
They do not work with :through associations. They do not work with
:polymorphic associations. They do not work with :as associations. For
belongs_to associations, has_many inverse associations are ignored.
I have 2 models:
Sale:
class Sale < ActiveRecord::Base
attr_accessible :sale_item_ids, :subtotal, :tax_charge, :total
has_many :sale_items
end
SaleItem:
class SaleItem < ActiveRecord::Base
attr_accessible :discount, :price, :product, :quantity, :sale_id, :sum, :code
belongs_to :sale
end
First i create an empty sale and then i start adding sale_items to that sale.
How can i validate the uniqueness of the sale_item_ids column? i mean, how can i make it so the same sale_item cannot be created twice with the same sale_id?
i have tried:
has_many :sale_items, uniq: true
but didnt work, i have also tried:
validates_uniqueness_of :sale_item_ids
but no success.
I believe you have to scope it. http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Try this:
validates_uniqueness_of :sale_items_ids, :scope => :sale_id
I was wrong since the beginning, sale_item_ids will always be different because its a new record in the database, so it will always be unique.
So what i have done is validate some other column.
class SaleItem < ActiveRecord::Base
validate :check_presence, :on => :create
attr_accessible :code, :discount, :price, :product, :quantity, :sum, :sale_id
belongs_to :sale
def check_presence
# Get the sale where the item sale will be saved
sale = Sale.find(self.sale_id)
# Compare the code column
sale.sale_items.each do |s|
if s.code == self.code
errors.add(:base, "Item already in the sale form.")
end
end
end
end
I have a model called Vote that has a polymorphic association with other two models: Microposts and Comments.
Here is their association:
micropost.rb:
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :comments, :dependent => :destroy
has_many :votes, :as => :votable, :dependent => :destroy
end
comment.rb:
class Comment < ActiveRecord::Base
belongs_to :micropost, :counter_cache => true
belongs_to :user
has_many :votes, :as => :votable, :dependent => :destroy
end
vote.rb:
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
end
The Vote model has a column called polarity (for voting up +1 and voting down -1).
I would like to add a total (the sum of the polarity of all the votes of a post or comment) to each instances of the Post and Comment model.
What's the best way of accomplishing this?
Example:
Post (:id = 2)
Vote (:polarity = 1) Vote (:polarity = -1) Vote (:polarity = 2)
Post with ID 2 has a :total of 2 votes
You can create an instance method in Post and Comment models like:
def total
self.votes.map {|v| v.polarity }.sum
end
Then put #post.total or #comment.total
Or you can create a helper which can do the same:
def total object
object.votes.map {|v| v.polarity }.sum
end
Then put <%= total #post %> or <%= total #comment %>
My models look like this:
class Movie < ActiveRecord::Base
attr_accessible :title, :year, :rotten_id, :audience_score,
:critics_score, :runtime, :synopsis, :link, :image
has_many :jobs, :dependent => :destroy
has_many :actors, :through => :jobs
end
class Actor < ActiveRecord::Base
attr_accessible :name
has_many :movies, :through => :jobs
has_many :jobs, :dependent => :destroy
end
class Job < ActiveRecord::Base
attr_accessible :movie_id, :actor_id
belongs_to :movie
belongs_to :actor
end
When I'm displaying my index of Actors, I'd like to show the number of movies each actor has starred in. I can do this with #actor.movies.count, however this generates an SQL query for each actor. With, say, 30 actors, this will result in 30 extra queries in addition to the initial.
Is there any way to include the count of movies each actor has participated in, in the initial Actor.all call? And thereby getting things done with only one call. Extra bonus if this was sorted by said count.
Update:
All answers provided has been helpful, and though it turned into some dirt-slinging-contest at some point, it worked out well. I did a mish-mash of all your suggestions. I added a movies_counter column to my Actor model. In my Job model I added belongs_to :actor, :counter_cache => :movies_counter. This works brilliantly, and is automatically updated when i create or destroy a movie, without me adding any further code.
As #Sam noticed, you should add new column to actors table movies_counter
rails g migration add_movies_counter_to_actor movies_counter:integer
Now you can edit your migration
class AddMoviesCounterToActor < ActiveRecord::Migration
def self.up
add_column :actors, :movies_counter, :integer, :default => 0
Actor.reset_column_information
Actor.all.each do |a|
a.update_attribute :movies_counter, a.movies.count
end
end
def self.down
remove_column :actors, :movies_counter
end
end
And run it
rake db:migrate
Then you should add two callbacks: after_save and after_destroy
class Movie < ActiveRecord::Base
attr_accessible :title, :year, :rotten_id, :audience_score,
:critics_score, :runtime, :synopsis, :link, :image
has_many :jobs, :dependent => :destroy
has_many :actors, :through => :jobs
after_save :update_movie_counter
after_destroy :update_movie_counter
private
def update_movie_counter
self.actors.each do |actor|
actor.update_attribute(:movie_count, actor.movies.count)
end
end
end
Then you can call some_actor.movies_counter
Add a column to your Actor table called 'movie_count'. Then add a call back in your Actor model that updates that column.
class Movie < ActiveRecord::Base
has_many :actors, :through => :jobs
before_save :update_movie_count
def update_movie_count
self.actor.update_attribute(:movie_count, self.movies.size)
end
end
That way your just have an integer that gets updated instead of calling all records.