I've got the following three models:
class Tag < AR::Base
has_many :mention_tags, dependent: :destroy
has_many :mentions, through: :mention_tags
before_destroy :destroy_mentions
def destroy_mentions
mentions.each do |mention|
mention.destroy if mention.tags.count == 1
end
end
end
class MentionTag < AR::Base
belongs_to :tag
belongs_to :mention
end
class Mention < AR::Base
has_many :mention_tags
has_many :tags, through: :mention_tags
end
It seems the destroy_mentions method isn't calling .destroy on mentions that have a tag count of 1 when that tag is being destroyed and I can't seem to figure out why.
Here's a passing spec:
describe 'mention with this and other tags' do
before do
#tag1 = Tag.create(text: 'whatever', conditional: 'yes')
#tag2 = Tag.create(text: 'whatever2', conditional: 'yes')
#mention = Mention.create(text: 'whatever', tags: [#tag1, #tag2])
end
it 'should not delete the mention when deleting the tag' do
#tag1.destroy
Mention.exists?(id: #mention.id).should be_true
end
end
and this spec fails:
describe 'mention with only this tag' do
before do
#tag = Tag.create(text: 'whatever', conditional: 'yes')
#mention = Mention.create(text: 'whatever', tags: [#tag])
end
it 'should delete the mention when deleting the tag' do
#tag.destroy
Mention.exists?(id: #mention.id).should be_false
end
end
Related
When I use the following:
class CreateJoinTableTagsPosts < ActiveRecord::Migration
def change
create_join_table :tags, :posts do |t|
t.index [:tag_id, :post_id]
end
end
end
or the following:
class CreateJoinTableTagsPosts < ActiveRecord::Migration
def change
create_join_table :posts, :tags do |t|
t.index [:post_id, :tag_id]
end
end
end
I always get a table that is posts_tags and as a result helper methods in the posts model:
class Post < ActiveRecord::Base
belongs_to :blogs
has_and_belongs_to_many :tags, join_table: 'tags_posts'
has_and_belongs_to_many :categories, join_table: 'categories_posts'
has_many :comments
validates :title, presence: true
def has_tag?(tag_name)
tag == tag_name
end
def assign_tag=(tag_name)
tag = Tag.find_by(name: tag_name) || Tag.create(name: tag_name)
self.tag = [tag] if tag
end
end
Don't actually work. As you can see the assign_tag method wont actually work, tests that are done as such:
it "should create a page for a post" do
#post.assign_tag = 'Sample Tag'
end
fails because relation tags doesn't exist. I believe this can be solved by creating the appropriate join table of tags_posts instead of the one it always creates posts_tags
ideas?
create_join_table uses the lexical order of the arguments to name the table.
You can override this using a table_name option:
create_join_table :posts, :tags, table_name: 'tags_posts' do |t|
t.index [:post_id, :tag_id]
end
when update, i got error like this.
Couldn't find PropertyAcceptanceCriterion with ID=14 for Property with ID=1
this error occur when off the checkbox, and update(save).
what should i do next,
model definition is
class PropertyAcceptance < ActiveRecord::Base
belongs_to :property
belongs_to :property_acceptance_criterion
end
class PropertyAcceptanceCriterion < ActiveRecord::Base
attr_accessible :name
has_many :property_acceptances, dependent: :destroy
has_many :properties, through: :property_acceptances
end
class Property < ActiveRecord::Base
attr_accessible :rent
attr_accessible :room_no
attr_accessible :property_acceptance_criterions_attributes
attr_accessible :property_acceptance_criterion_ids
has_many :property_acceptances, dependent: :destroy
has_many :property_acceptance_criterions, through: :property_acceptances
accepts_nested_attributes_for :property_acceptance_criterions, reject_if: lambda { |a| a[:name].blank? }
end
view definition is
= simple_nested_form_for #property do |f|
= f.input :room_no, input_html: {class: 'span2'}
= f.input :rent, input_html: {class: 'span2'}
= f.association :property_acceptance_criterions, as: :check_boxes
= f.simple_fields_for :property_acceptance_criterions do |c|
= c.input :name, label: "add for #{t('activerecord.attributes.property.property_acceptance_criterions')}" if c.object.new_record?
controller definition is
class Insurance::PropertiesController < Insurance::InsuranceController
before_filter :load_property, only: [:edit, :update]
before_filter :new_property, only: [:new, :create]
def new
#property.property_acceptance_criterions.build
end
def create
#property.attributes = params[:property]
if #property.save
redirect_to #property, success: t('activerecord.flash.property.actions.create.success')
else
render :new
end
end
def edit
#property.property_acceptance_criterions.build
end
def update
if #property.update_attributes(params[:property]) # ← error occur
redirect_to #property, success: t('activerecord.flash.property.actions.update.success')
else
render :edit
end
end
private
def load_property
#property = Property.find(params[:id])
end
def new_property
#property = Property.new
end
end
error is
Couldn't find PropertyAcceptanceCriterion with ID=14 for Property with ID=1
params is
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"+Zx7l7mAbX12PSO873x5NDxNOIeEe6bEDEdVnys+a98=",
"property"=>{
"room_no"=>"000",
"rent"=>"80000",
"property_acceptance_criterion_ids"=>["13", "25", ""],
"property_acceptance_criterions_attributes"=>{
"0"=>{"id"=>"13"}, "1"=>{"id"=>"14"}, "2"=>{"id"=>"25"}, "3"=>{"name"=>""}
},
"commit"=>"update",
"id"=>"1"}
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 a many to many relationship between two models as follows:
#users.rb
has_many :users_to_roles
has_many :roles, through: :users_to_roles
#users_to_roles.rb
belongs_to :user
belongs_to :role
#roles.rb
has_many :users_to_roles
has_many :users, through: :users_to_roles
I want to disable the deletion of roles if there are users who are "in this role". Here I have found two options who should do the work:
:restrict_with_exception causes an exception to be raised if there are
any associated records :restrict_with_error causes an error to be
added to the owner if there are any associated objects
but there is no example with the syntax of this and how it should work.
Could you help to make this valid:
#roles.rb
has_many :users_to_roles
has_many :users, through: :users_to_roles, dependent: restrict_with_exception
Such operations can be easily do using Callbacks. In my case, I have added the following method in my model:
# callbacks
before_destroy :check_for_users_in_this_role
def check_for_users_in_this_role
status = true
if self.security_users.count > 0
self.errors[:deletion_status] = 'Cannot delete security role with active users in it.'
status = false
else
self.errors[:deletion_status] = 'OK.'
end
status
end
Alternatively, you can rescue the exception in your controller. In this example, a contact may own interest, i.e.
class Interest < ActiveRecord::Base
belongs_to :contact
end
class Contact < ActiveRecord::Base
has_many :interests, :dependent => :restrict
end
Then in the controller:
def destroy
#contact = Contact.find(params[:id])
begin
#contact.destroy
rescue
flash[:msg] = "Can't delete - owns interest"
end
respond_to do |format|
format.html { redirect_to(:back) }
format.xml { head :ok }
end
end
The flash message will be displayed in the calling page.
The correct rails way is to do the following:
users.rb:
has_many :users_to_roles, dependant: :destroy # don't keep the join table entry if the user is gone
has_many :roles, through: :users_to_roles
Make sure that your join does not have redundant entries (in which either column is null or orphaned).
users_to_roles.rb:
belongs_to :user
belongs_to :role
# add validations presence of both user and role
# in both model and database.
Bonus, from rails 4.2 you can add forigen_key: true in your migration for referential integrity
Now in your role (I am assuming you name your models singularly and made a typo in the question), you add this:
role.rb:
has_many :users_to_roles, dependant: :restrict_with_error
has_many :users, through: :users_to_roles
I made it with my classes like this:
app/models/guest_chat_token.rb
class GuestChatToken < ApplicationRecord
has_many :chat_messages, as: :sendable, dependent: :restrict_with_exception
end
app/controllers/admin/application_controller.rb
class Admin::ApplicationController < ApplicationController
....
rescue_from ActiveRecord::DeleteRestrictionError do |exception|
redirect_to :back, notice:
"Be aware: #{exception.message}."
end
end
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