I have a model named runningmenu i want to inactive it on destroy but not hard delete, and also i want to inactive all its dependent records e.g orders
i am using ruby 2.4.0 and rails 5 i have tried using active record concerns, on before destroy i am calling concern stop destroy method to flip delete_status and throw abort. when i abort on dependent records it sends failed to destroy exception to runningmenu destroy action. But it just aborted on first record and not make all dependent records inactive.
module Deletable
extend ActiveSupport::Concern
def stop_destroy
self.deleted!
throw(:abort)
end
def stop_destroy_for_orders
self.update_column(:status, Order.statuses[:cancelled])
throw(:abort)
end
end
ON Orders model i have:
before_destroy -> { stop_destroy_for_orders }
on runningmenu model i have:
has_many :orders, dependent: :destroy
before_destroy -> { stop_destroy }
expected result is to make all runningmenu dependent childs inactive on delete but not hard delete. But now only first instance status set to cancelled and roll backed.
I don't know if you're happy using gems or not, but one of the more popular ones is paranoia:
https://github.com/rubysherpas/paranoia
bin/rails generate migration AddDeletedAtToOrders deleted_at:datetime:index
Add the deleted_at column to your orders, then run rake db:migrate to add the column to your database. In your orders model:
class Order < ActiveRecord::Base
acts_as_paranoid
# ...
end
Now when you call Order.destroy, the record will not be deleted, but will have its deleted_at column updated with the time of 'deletion'. This will add a default scope to Orders, meaning if you do Order.all, it will only return orders with a nil deleted_at. More commands for how to really delete a record and include it in scopes is included in their github above.
In my app, I have several clients, and they have several elements (via has_many_through association) depending on a certain BusinessType to which Client belongs to so that instead of manually adding all the elements to the Client, I can just select the BusinessType and everything gets added automatically (business_type in Client is attr_readonly). BusinessType HABTM elements.
Here's the catch, after creation with the default BusinessType, the clients can update their elements and remove or add as they please (mostly add), so what I'm trying to do is the following:
Suppose one business_type has elements [1,2,3] and is assigned to one client, then, the following elements are added manually to the client = [4,5,6] so it ends up having [1,2,3,4,5,6], ok everything's fine here.
But after this, the business_type gets updated and has element 2 removed, so it ends up being [1,3]. Here's the deal, I want the client to be updated by removing the 2, but not the [4,5,6] that do not correspond to the business_type in question so that it ends up [1,3,4,5,6], I'm using an after_update callback to update the clients' elements but the _was method doesn't work for HABTM relationships (to get the old business_type's elements.
I've tried using a before_update callback to first to client.elements = client.elements - business_type.elements to store momentarily in the DB [1,2,3,4,5,6] - [1,2,3] = [4,5,6], and in the after_update do client.elements = client.elements + business_type.elements to get [4,5,6] + [1,3] = [1,3,4,5,6]but this has already the new value of [1,3]. How can I get the old business_type.elements value in the before_update or after_update?
Thanks in advance for your help!
I had a similar problem in an app, and the only solution I could come up with was to store the values before doing update_attributes in the controller.
Example code:
Models
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories, :join_table => "categories_products"
def remember_prev_values(values)
#prev_values = values
end
def before_update_do_something
puts #prev_values - self.category_ids # Any categories removed?
puts self.category_ids - #prev_values # Any categories added?
end
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => "categories_products"
end
In the update method in the products controller I do the following:
class ProductsController < ApplicationController
...
def update
#product.remember_prev_values(#product.category_ids)
if #product.update_attributes(params[:product])
flash[:notice] = "Product was successfully updated."
redirect_to(product_path(#product))
else
render :action => "edit"
end
end
...
end
It is not ideal, but it is then possible to "catch" the habtm inserts/removes before they are executed.
I do think it is possible to do in a callback, but you might need to "hack" into ActiveRecord.
I did not spend much time on trying to dig into ActiveRecord internals, as this is a simple implementation that works.
You should use after_initialize callback to store previous values.
after_initialize do #previous_elements = elements.map{|x| x} end
Note that here we make a copy of assosiations by map function call.
I have a nested resource like this
resources :projects do
resources :tasks
end
The tasks have a field named number. Whenever I create a task I would like to give it a squential number within the parent project.
This is my model class
class Task < ActiveRecord :: Base
belongs_to :project
validate_presence_of :title
before_create :generate_number
private
def generate_number
if project.tasks.nil? || project.tasks.count < 1
self.number = 1
else
self.number = list.topics.count+1
end
end
end
I am not sure about certain things:
Does this logic belongs in my Task Model or in my Project model or in a seperate class/module?
What is the best before filter. (before_create, before_validation, validation)?
Because there are many ways how to create a task. With a list, in a list, alone and then attach it to a list...
And which filter would work in my tests so that I could setup some Fakes for example with factory girl... Because right now FactoryGirl does not always executes generate number...
This is my factory
FactoryGirl.define do
factory :project do
name "Hello world"
end
trait :with_tasks do
ignore do
number_of_tasks 3
end
after :create do |project,evaluator|
#project.Factory.create_list :taks, evaluator.number_of_tasks, :project => project
end
end
end
What would be the best. reliable way to generate a sequential custom taks number depending on the project which works in my specs as well as in production?
Any best practise tips would be appreciated.
I would keep the before_create callback in the Task model, which would call the generate_number function. This should work in Factory girl where it would add the number if you use Factory.create, but not when you use Factory.build.
I am developing an API using Rails 3. A user can have several contact items, like phones, emails, websites and address. Each item got its own model.
I need to do caching in my iPhone app that is using this API therefore I need to get the date when the latest update to any of the items occured and match it against the timestamp of the cache file.
How can I get the most updated items (when comparing all the item tables)?
I am getting the most recent item for each item table like this. Is this really good?
#phones = #user.phones.order("updated_at desc").limit(1)
Thankful for all help!
You can make use of ActiveRecord's touch method. This is especially useful if you have one parent record with many child records. Every time the child records are saved or destroyed the parent record will have it's updated_at field set to the current time.
class User < ApplicationRecord
has_many :phones
has_many :addresses
end
class Phone < ApplicationRecord
belongs_to :user, touch: true
end
class Address < ApplicationRecord
belongs_to :user, touch: true
end
Now any time an address or a phone is updated, the User's updated_at will be set to the current time.
To check when the last updated for the current user took place, over all their tables, you now do:
#last_updated = #user.updated_at
For a small overhead in writes you gain a lot with simpler queries on checking your cache expiry. The documentation for this can be found under belongs_to options.
Suppose I have the following model relationship:
class Player < ActiveRecord::Base
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :player
end
I know from this question that Rails will return me a copy of the object representing a database row, meaning that:
p = Player.find(:first)
c = p.cards[0]
c.player.object_id == p.object_id # => false
...and therefore if the Player model modifies self, and the Card model modifies self.player in the same request, then the modifications won't take any notice of each other and the last-saved one will overwrite the others.
I'd like to work around this (presumably with some form of caching), so that all requests for a Player with a given id would return the same object (identical object_ids), thereby allowing both models to edit the same object without having to perform a database save-and-reload. I have three questions:
Is there already a plugin or gem to do this?
Are there good reasons why I shouldn't do this?
Can anyone give me some pointers on how to go about doing this?
This is supported in Rails 3.x. You can use the :inverse_of option for the has_many association for example. Documentation here (search for :inverse_of and Bi-directional associations).