How to avoid to update record in self referenced model due to not refers to itself and also not to be duplicate references - validation

I have self referenced model:
class Component < ActiveRecord::Base
has_and_belongs_to_many :allergens
has_many :cqnames, as: :cqnable
has_many :inclusions
has_many :ingredients, :through => :inclusions
accepts_nested_attributes_for :cqnames, allow_destroy: true
accepts_nested_attributes_for :ingredients
accepts_nested_attributes_for :inclusions, allow_destroy: true
translates :name, :description
validates :name, presence: true, uniqueness: true
def self.get_children(ing)
#tree ||= []
if ing.ingredients.nil?
return #tree
else
#tree << ing.ingredients
get_children(ing.ingredients)
end
end
end
I have to avoid self referencing to the same record and also to the records already referenced when the respecting record is updating.
Experiment with before_validation...:
class Component < ActiveRecord::Base
before_validation :hokus
has_and_belongs_to_many :allergens
has_many :cqnames, as: :cqnable
has_many :inclusions
has_many :ingredients, :through => :inclusions
has_many :inverse_inclusions, :class_name => "Inclusion", :foreign_key => "ingredient_id"
has_many :composites, :through => :inverse_inclusions, :source => :component
accepts_nested_attributes_for :cqnames, allow_destroy: true
accepts_nested_attributes_for :ingredients
accepts_nested_attributes_for :inclusions, allow_destroy: true
translates :name, :description
validates :name, presence: true, uniqueness: true
require 'set'
def self.pokus(ing)
avoid = Set.new([self])
q = true
if ing.ingredients.present?
ing.ingredients.each do |record|
q = false if avoid.include? record
end
end
q
end
def hokus
Component.pokus(self)
end
end

If you want a simpler version that should get the job done then this may do.
require 'set'
def self.get_children(ing)
tree = Set.new
recursive = lambda {|recs|
recs.ingredients.each do |record|
recursive.call(record.ingredients) if tree.add?(record)
end
}
recursive.call(ing.ingredients)
tree.to_a
end

I think this is a bit of a mess. But I'll give you a pointer on how you may build your list without duplicates.
require 'set'
def self.get_children(ing)
tree = Set.new
avoid = Set.new([self])
recursive = lambda {|recs|
recs.ingredients.each do |record|
pass = false
if avoid.include? record
pass = true
else
tree << record
avoid << record
end
recursive.call(record.ingredients) unless pass
end
}
recursive.call(ing.ingredients)
tree.to_a
end
I've done a fare bit of recursive work like this so I believe that will do what you're hoping to do. Although I haven't tested this particular piece of code.
If you'd like to see a depth first record duplication method I've written you can see it in my gem poly_belongs_to . I use a variation on Set I built which basically does the same thing I demonstrated here with the avoid Set. I explain Set in my blog post here Different Collection Types in Ruby

Related

How is the following Rails model should look like in Sequel?

I've the following two models:
class Dispute < ApplicationRecord
belongs_to :accuser, class_name: 'User', optional: true
belongs_to :defendant, class_name: 'User', optional: true
end
class User < ApplicationRecord
end
Here's the migration for Dispute:
class CreateDisputes < ActiveRecord::Migration[5.0]
def change
create_table :disputes do |t|
t.references :accuser
t.references :defendant
end
end
end
This is how they behave in Rails:
Dispute.first.accuser
# => <# User>
Dispute.first.defendant
# => <# User>
In Sequel, I'm supposed to use many_to_one, but does that mean that Sequel User model should have a corresponding one_to_many? Can't seem to get it to work.
This should work:
Sequel.migration do
change do
create_table(:disputes) do
primary_key :id
foreign_key :accuser_id, :users
foreign_key :defendant_id, :users
end
end
end
class Dispute < Sequel::Model
many_to_one :accuser, :class=>:User
many_to_one :defendant, :class=>:User
end

Extracting common ActiveRecord code for similar clases

I have the following classes defined that have a lot of common code with minor variations.
class ThirdPartyComponent < ActiveRecord::Base
belongs_to :prev_version, :class_name => 'ThirdPartyComponent', :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => 'ThirdPartyComponent', :foreign_key => 'prev_version_id'
attr_accessible :name, :version, :installer, :install_script
mount_uploader :installer, ComponentFileUploader
mount_uploader :install_script, ComponentFileUploader
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validates :installer, :presence => true
validates :install_script, :presence => true
validate :increased_version
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
class RegistryComponent < ActiveRecord::Base
belongs_to :prev_version, :class_name => 'RegistryComponent', :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => 'RegistryComponent', :foreign_key => 'prev_version_id'
attr_accessible :name, :version, :registry_file
mount_uploader :registry_file, ComponentFileUploader
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validates :registry_file, :presence => true
validate :increased_version
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
I'm also looking at adding some other components in the future, again with very similar functionality.
I want to extract the common code from these classes into a single file (including the ActiveRecord method calls such as validates, etc.) and then just reference them in the concrete classes.
So far I've tried,
inheritance - I created a base class that inherited from ActiveRecord and then each class inherited from the base class. Result: Rails complained that it couldn't find a database table whose name matched the base class.
inheritance - I considered creating the base class as tableless model instead (see http://railscasts.com/episodes/219-active-model) but then I realised that the concrete classes would also be lacking the full ActiveRecord functionality
composition - I tried defining the common code in a module and then using include or extend in the concrete classes to access it as shown below.
module ComponentBase
belongs_to :prev_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
attr_accessible :name, :version
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validate :increased_version
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
class RegistryComponent < ActiveRecord::Base
include ComponentBase
attr_accessible :registry_file
mount_uploader :registry_file, ComponentFileUploader
validates :registry_file, :presence => true
end
This resulted in an error that the belongs_to method is not defined for ComponentBase. This looks the most promising solution but is there any way to execute the ActiveRecord class methods within the context of the class that includes them? Alternatively, is there a better way for me to achieve the same aims?
Your first option was actually the best option. Rails uses Single Table Inheritance, which means the data for all of your subclasses is kept in the same table which is why you got the error you did.
What you should do is create a new model called Component and add to it all the fields that are common across all of your components as well as one extra field called type that should be a string field.
Your Component model will then have all the common fields, logic, and validations.
class Component < ActiveRecord::Base
...
end
Then have have each of your compontent classes subclass Component.
class ThirdPartyComponent < Component
...
end
I stumbled across the following answer at Extending a Ruby class with a standalone piece of code and with a bit of experimentation got it to work. So the final code I ended up with was,
module ComponentBase
def self.included(base)
base.class_eval do
belongs_to :prev_version, :class_name => base, :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => base, :foreign_key => 'prev_version_id'
attr_accessible :name, :version
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validate :increased_version
end
end
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
class RegistryComponent < ActiveRecord::Base
include ComponentBase
attr_accessible :registry_file
mount_uploader :registry_file, ComponentFileUploader
validates :registry_file, :presence => true
end
The solution was to use the included callback which is called each time the module is included somewhere else. Then, call class_eval on the base module to run the methods in the class context (i.e. as class methods). The trickiest part was getting the class name in this context but it simply turned out that I could use base (not entirely sure why this is the case but it works).
The issue is that you need the belongs_to method to run on the class that is including the module, not the module itself.
Check out module#included http://www.ruby-doc.org/core-2.0/Module.html#method-i-included, which will allow you to run code on the module you are including your module into. Remmeber that Module is an ancestor of Class so this works for classes and modules.
In this case you will want to run belongs_to on the class that the module is being included into, so something like the following should act as an example you can work off of:
module ComponentBase
def self.included(mod)
mod.class_eval do
belongs_to :prev_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
end
end
end

How to have an ordered model association allowing duplicates

I have two models, Song and Show. A Show is an ordered list of Songs, in which the same Song can be listed multiple times.
That is, there should be an ordered array (or hash or anything) somewhere in Show that can contain Song1, Song2, Song1, Song3 and allow re-ordering, inserting, or deleting from that array.
I cannot figure out how to model this with ActiveRecord associations. I'm guessing I need some sort of special join table with a column for the index, but apart from starting to code my SQL directly, is there a way to do this with Rails associations?
Some code as I have it now (but doesn't work properly):
class Song < ActiveRecord::Base
attr_accessible :title
has_and_belongs_to_many :shows
end
class Show < ActiveRecord::Base
attr_accessible :date
has_and_belongs_to_many :songs
end
song1 = Song.create(title: 'Foo')
song2 = Song.create(title: 'Bar')
show1 = Show.create(date: 'Tomorrow')
show1.songs << song1 << song2 << song1
puts "show1 size = #{show1.songs.size}" # 3
show1.delete_at(0) # Should delete the first instance of song1, but leave the second instance
puts "show1 size = #{show1.songs.size}" # 2
show1.reload
puts "show1 size = #{show1.songs.size}" # 3 again, annoyingly
Inserting might look like:
show1.songs # Foo, Bar, Foo
song3 = Song.create(title: 'Baz')
show1.insert(1, song3)
show1.songs # Foo, Baz, Bar, Foo
And reordering might (with a little magic) look something like:
show1.songs # Foo, Bar, Foo
show1.move_song_from(0, to: 1)
show1.songs # Bar, Foo, Foo
You're on the right track with the join table idea:
class Song < ActiveRecord::Base
attr_accessible :title
has_many :playlist_items
has_many :shows, :through => :playlist_items
end
class PlaylistItem < ActiveRecord::Base
belongs_to :shows #foreign_key show_id
belongs_to :songs #foreign_key song_id
end
class Show < ActiveRecord::Base
attr_accessible :date
has_many :playlist_items
has_many :songs, :through => :playlist_items
end
Then you can do stuff like user.playlist_items.create :song => Song.last
My current solution to this is a combination of has_many :through and acts_as_list. It was not the easiest thing to find information on combining the two correctly. One of the hurdles, for example, was that acts_as_list uses an index starting at 1, while the array-like methods created by the ActiveRecord association start at 0.
Here's how my code ended up. Note that I had to specify explicit methods to modify the join table (for most of them anyway); I'm not sure if there's a cleaner way to make those work.
class Song < ActiveRecord::Base
attr_accessible :title
has_many :playlist_items, :order => :position
has_many :shows, :through => :playlist_items
end
class PlaylistItem < ActiveRecord::Base
attr_accessible :position, :show_id, :song_id
belongs_to :shows
belongs_to :songs
acts_as_list :scope => :show
end
class Show < ActiveRecord::Base
attr_accessible :date
has_many :playlist_items, :order => :position
has_many :songs, :through => :playlist_items, :order => :position
def song_at(index)
self.songs.find_by_id(self.playlist_items[index].song_id)
end
def move_song(index, options={})
raise "A :to option is required." unless options.has_key? :to
self.playlist_items[index].insert_at(options[:to] + 1) # Compensate for acts_as_list starting at 1
end
def add_song(location)
self.songs << location
end
def remove_song_at(index)
self.playlist_items.delete(self.playlist_items[index])
end
end
I added a 'position' column to my 'playlist_items' table, as per the instructions that came with acts_as_list. It's worth noting that I had to dig into the API for acts_as_list to find the insert_at method.

how to autobuild an associated polymorphic activerecord object in rails 3

class ItemSource < ActiveRecord::Base
belongs_to :product, :polymorphic => true
end
class RandomProduct < ActiveRecord::Base
has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy
end
What I'd like to do is is call:
a = RandomProduct.find(1)
a.item_source
and if item_source doesn't already exist (= nil), then build it automatically (build_item_source).
previously, I did this with alias_chain_method, but that's not supported in Rails 3.
oh, and I also tried this to no avail:
class RandomProduct < ActiveRecord::Base
has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy
module AutoBuildItemSource
def item_source
super || build_item_source
end
end
include AutoBuildItemSource
end
In Rails 3, alias_method_chain (and alias_method, and alias) work fine:
class User < ActiveRecord::Base
has_one :profile, :inverse_of => :user
# This works:
#
# def profile_with_build
# profile_without_build || build_profile
# end
# alias_method_chain :profile, :build
#
# But so does this:
alias profile_without_build profile
def profile
profile_without_build || build_profile
end
end
But there's always accept_nested_attributes_for as an alternative, which calls build when profile_attributes are set. Combine it with delegate (optional) and you won't have to worry if the record exists or not:
class User < ActiveRecord::Base
has_one :profile, :inverse_of => :user
delegate :website, :to => :profile, :allow_nil => true
accepts_nested_attributes_for :profile
end
User.new.profile # => nil
User.new.website # => nil
u = User.new :profile_attributes => { :website => "http://example.com" }
u.profile # => #<Profile id: nil, user_id: nil, website: "http://example.com"...>
If the association is always created, delegation isn't necessary (but may be helpful, anyhow).
(Note: I set :inverse_of to make Profile.validates_presence_of :user work and to generally save queries.)
(Rails 4, FYI)
I personally prefer setting it up with after_initialize
after_initialize :after_initialize
def after_initialize
build_item_source if item_source.nil?
end
This also works well because you can automatically use forms with what would otherwise be an empty association (HAML because it's nicer):
= form_for #product do |f|
= f.fields_for :item_source do |isf|
= isf.label :prop1
= isf.text_field :prop1
If you didn't have the item_source built already, the label and text field wouldn't render at all.
How about creating the item_source when the RandomProduct is created:
class RandomProduct << ActiveRecord::Base
after_create :create_item_source
end
Of course, if you need to pass specific arguments to the item source, you could do something like this, instead:
class RandomProduct << ActiveRecord::Base
after_create :set_up_item_source
protected
def set_up_item_source
create_item_source(
:my => "options",
:go => "here"
)
end
end
Not that you really need a gem for this since it's so simple to do yourself, but here's a gem that makes it even easier to declare an auto-build:
https://github.com/TylerRick/active_record_auto_build_associations

Polymorphic has_many self-referential

I have A number of models (Article, Video, Photo)
Now I am trying to create a related_to association, such that
An article can have many other articles, videos and photos related to it. As can videos and photos.
Heres what I have tried:
module ActsAsRelatable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def acts_as_relatable
has_many :related_items, :as => :related
has_many :source_items, :as => :source, :class_name => 'RelatedItem'
end
end
end
class RelatedItem < ActiveRecord::Base
belongs_to :source, :polymorphic => true
belongs_to :related, :polymorphic => true
end
Then I have added acts_as_relatable to my three models (Article, Video, Photo) and included the module in ActiveRecord::Base
When trying in ./script/console I get it to add the related items and the ids work correctly however the source_type and related_type are always the same (the object that related_items was called from) I want the related_item to be the other model name.
Any ideas anyone?
I would use the has many polymorphs plugin since it supports double sided polymorphism you can do something like this:
class Relating < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
belongs_to :relative, :polymorphic => true
acts_as_double_polymorphic_join(
:owners => [:articles, :videos, :photos],
:relatives => [:articles, :videos, :photos]
)
end
and don't forget the db migration:
class CreateRelatings < ActiveRecord::Migration
def self.up
create_table :relating do |t|
t.references :owner, :polymorphic => true
t.references :relative, :polymorphic => true
end
end
def self.down
drop_table :relatings
end
end
I don't know if "Relating" is a good name, but you get the idea. Now an article, video and photo can be related to another article, video or photo.

Resources