DataMapper Self Referential Model - ruby

I have a model:
class Comment
include DataMapper::Resource
property :id, Serial
property :comment, Text, :required => true
belongs_to :user
belongs_to :lecture
has n, :replies, :child_key => [:source_id]
has n, :comments, self, :through => :replies, :via => :target
end
And I want to add a comment as a reply to another, already created comment. When I try:
lecture = Lecture.get(params[:lecture_id])
comment = Comment.new(:comment => params[:text])
#user.comments << comment
lecture.comments << comment
if !params[:parent].nil? then
parent = Comment.get(params[:parent])
parent.replies << comment
end
The line parent.replies << comment throws the error:
NoMethodError - undefined method source_id=' for #<Comment #id=nil #comment="asd" #user_id=1 #lecture_id=1>
My Reply model is:
class Reply
include DataMapper::Resource
belongs_to :source, 'Comment', :key => true
belongs_to :target, 'Comment', :key => true
end
How do I correctly add a comment as a 'reply'? Thanks.

Are you sure you want Reply model? The comment tree can be build on just one model Comment that has self-association.
class Comment
...
has n, :replies, 'Comment', :child_key => :source_id
belongs_to :source, 'Comment', :required => false
end

Related

DataMapper `has n` for different types of a model

I would like to have a model with different types of has n, for example:
class Blog
include DataMapper::Resource
property :id, Serial
has 1, :owner # of type user...
has n, :authors # of type user...
end
class User
include DataMapper::Resource
property :id, Serial
has n, :blogs # owns some number
has n, :blogs # is a member of some number
end
I don't, however, want to use the Discriminator, since then I need to make new Owner or Author objects of old User objects and that would be ridiculous.
How can I best achieve this?
Try this:
class User
include DataMapper::Resource
property :id, Serial
has n, :blog_authors, 'BlogAuthor'
has n, :authored_blogs, 'Blog', :through => :blog_authors, :via => :blog
has n, :blog_owners, 'BlogOwner'
has n, :owned_blogs, 'Blog', :through => :blog_owners, :via => :blog
end
class Blog
include DataMapper::Resource
property :id, Serial
has n, :blog_authors, 'BlogAuthor'
has n, :authors, 'User', :through => :blog_authors
has 1, :blog_owner, 'BlogOwner'
end
class BlogAuthor
include DataMapper::Resource
belongs_to :blog, :key => true
belongs_to :author, 'User', :key => true
end
class BlogOwner
include DataMapper::Resource
belongs_to :blog, :key => true
belongs_to :owner, 'User', :key => true
end

Rails - has_many build method not saving association

I'm having some trouble trying to make association works.
My models looks like:
advertise.rb
class Advertise < ActiveRecord::Base
belongs_to :user
belongs_to :country
has_many :targets
# has_many :hss, :through => :targets
validates :description, :presence => true
validates :url, :presence => true
validates :country_id, :presence => true
validates :kind, :presence => true
attr_accessible :description, :hits, :url, :active, :country_id, :kind
KINDS = {
'1' => 'Commoditie',
'2' => 'Service',
}
HS = {
'1' => 'Section',
'2' => 'Chapter',
'4' => 'Heading',
'5' => 'SubHeading 1',
'6' => 'SubHeading 2',
}
end
hs.rb
class Hs < ActiveRecord::Base
attr_accessible :code, :kind
has_many :targets
has_many :advertises, :through => :targets
end
target.rb
class Target < ActiveRecord::Base
attr_accessible :advertise_id, :hs_id
belongs_to :advertise
belongs_to :hs
end
advertises_controller.rb
def new
#advertise = Advertise.new
#countries = Country.all
end
def create
#advertise = current_user.advertises.build(params[:advertise])
if #advertise.save
flash[:notice] = 'Advertise created with successful'
redirect_to root_path
else
render :new
end
end
the form for creating a new Ad.
/advertises/new.html.haml
%table.table.table-striped
%tr
%td{:colspan => 2}= advertise.input :url, :required => true
%tr
%td{:colspan => 2}= advertise.input :description, :required => true, :as => :text, :input_html => { :cols => 50, :rows => 3 }
%tr
%td{:colspan => 2}= advertise.input :country_id, :collection => #countries, :as => :select, :label => 'Origin', :required => true
%tr
%td{:colspan => 2}= advertise.input :kind, :collection => Advertise::KINDS.map(&:reverse), :as => :select, :label => 'Type', :required => true
%tr
%td{:colspan => 2}= advertise.input_field :active, as: :boolean, inline_label: 'Active'
=fields_for :targets do |target|
%tr
%td= select_tag :hs_kind, options_for_select(Advertise::HS.map(&:reverse).insert(0,'') )
%td= target.select :hs_id, ''
The hash params:
[3] pry(#<AdvertisesController>)> params
=> {"utf8"=>"✓",
"authenticity_token"=>"fOdn4NYLg/4HXruWURZPf9DYVT4EQzbaTRTKZvX1ugY=",
"advertise"=>
{"url"=>"http://test.com",
"description"=>"test",
"country_id"=>"17",
"kind"=>"2",
"active"=>"1"},
"hs_kind"=>"2",
"targets"=>{"hs_id"=>"487"},
"commit"=>"Create Advertise",
"action"=>"create",
"controller"=>"advertises"}
The hash seems ok to me, but it creates only the Advertise, and not creates the target for the advertise associated.
Any though? Maybe a wrong model association.
Thanks in advance.
try changing
=fields_for :targets do |target|
to
= advertise.fields_for :targets do |target|
and add the following to advertise.rb
accepts_nested_attributes_for :targets
attr_accessible :targets_attributes # just add targets_attributes to attr_accessible
be warned that when you do this, advertise objects that has no targets will not show the fields for targets. you have to build a new target object associated to advertise in order to show the fields
# example: on the new action of the controller
#advertise = Advertise.new
#advertise.targets.build

Rails 3 Polymorphic Associations, User model with multiple content items?

In Rails 3 I'm trying to model a user content system, where the user can post different types of content, for example, note, photo, url etc.
From a Java/C# OO perspective, I would use a polymorphic relationship between the User and an interface representing a Content item, e.g. something called IUserContent.
I'm struggling to find an example that works how I expect it to, here's what I tried first, in short I'm getting confused about the implementation of polymorphic associations in ActiveRecord.
# user.rb model - includes...
has_many :notes, :as => :postable, :dependent => :destroy, :inverse_of => :postable
has_many :urls, :as => :postable, :dependent => :destroy, :inverse_of => :postable
has_many :photos, :as => :postable, :dependent => :destroy, :inverse_of => :postable
# url.rb ...
belongs_to :postable, :polymorphic => true, :inverse_of => :urls
# photo.rb
belongs_to :postable, :polymorphic => true, :inverse_of => :photos
# note.rb
belongs_to :postable, :polymorphic => true, :inverse_of => :notes
I'm still just following examples I've found, and frankly this feels like User is the polymorphic target, not the content.
I think I want something like this...
# within user.rb
has_many :postable, :as => :postable, dependent => :destroy, :inverse_of => :users
# photo.rb
# url.rb
# note.rb
# all have the following...
belongs_to :user, :polymorphic => true, :inverse_of => :postable
... looking for a few pointers in the right direction.
Thank you.
The only way you can do that is if all of these classes inherit from the same base class, as in:
class User < ActiveRecord::Base
has_many :postable, :as => :postable, :dependent => :destroy, :class_name => 'Postable'
end
class Postable < ActiveRecord::Base
belongs_to :user, :polymorphic => true
end
class Photo < Postable
end
class Url < Postable
end
class Note < Postable
end
So, you have to use the ActiveRecord single-table inheritance to model such a relationship.

simple_form, bug with :has_many :through relation?

I have 3 models like these:
class CertificatorBtwInstructor < ActiveRecord::Base
belongs_to :certificator
belongs_to :instructor
# other code ...
end
class Certificator < ActiveRecord::Base
has_many :btw_instructors, :class_name => "CertificatorBtwInstructor"
has_many :instructors, :through => :btw_instructors
# other code ...
end
class Instructor < ActiveRecord::Base
has_many :btw_certificators, :class_name => "CertificatorBtwInstructor"
has_many :certificators, :through => :btw_certificators
# other code ...
end
on my Instructor new form I have setting:
= simple_form_for( [:cm, #instructor], :html => { :class => "fAdm" } ) do |f|
= f.object.errors
- others fields ...
= f.input :certificator_ids, :required => false, :collection => Certificator.choices_relation_select, :input_html => { :multiple => true, :size => 12 }
%div.row.buttons
= f.button :submit
then, when i submit the form without select any Certificator from the 'certificator_ids' input collection, the new record is created without problem.
But, when I choose any item on the 'certificator_ids' input collection, an error appear (i visualize it with the = f.object.errors ), and the error is this:
{:certificators=>["is not valid"]}
but on my form i haven't setting the field 'certificators', so I don't undestand why they got this attribute.
Change it to
<%= f.association :certificators, :as => :check_boxes %>
this will show to you all your certificators on check boxes, you should take a look at this
https://github.com/plataformatec/simple_form
and this
http://railscasts.com/episodes/47-two-many-to-many

How do I abstract similar validations/relations to parent class in Ruby/Rails?

I have these VERY similar classes:
class DeliveryDocument < CommercialDocument
# Relations
belongs_to :biller, :class_name => 'Company'
belongs_to :customer, :class_name => 'Company'
belongs_to :customer_center, :class_name => 'Center'
has_many :delivery_document_lines, :dependent => :destroy
alias_attribute :lines, :delivery_document_lines
# Some configuration
accepts_nested_attributes_for :delivery_document_lines
acts_as_freezable :only_dependencies => true,
:has_many => [:delivery_document_lines],
:belongs_to => [:biller, :customer, :customer_center]
acts_as_clonable :has_many => [:delivery_document_lines]
validates_each :lines do |record, attr, value|
# ...
end
end
And:
class InvoiceDocument < CommercialDocument
self.
# Relations
belongs_to :biller, :class_name => 'Company'
belongs_to :customer, :class_name => 'Company'
belongs_to :customer_center, :class_name => 'Center'
has_many :invoice_document_lines, :dependent => :destroy
alias_attribute :lines, :invoice_document_lines
# Some configuration
accepts_nested_attributes_for :invoice_document_lines
acts_as_freezable :only_dependencies => true,
:has_many => [:invoice_document_lines],
:belongs_to => [:biller, :customer, :customer_center]
acts_as_clonable :has_many => [:invoice_document_lines]
# Validations
validates_each :lines do |record, attr, value|
# ...
end
end
I also have some methods I didn't paste that could be extracted to the parent. I only need to know the class name in the parent. When I do this:
class CommercialDocument < Document # document inherits from AR::Base
# ...
has_many :"#{self.to_s.underscore}_lines", :dependent => :destroy
# ...
accepts_nested_attributes_for :"#{self.to_s.underscore}_lines"
# ...
end
it doesn't work, because self.to_s is CommercialDocument.
How would you refactor this behavior in parent class?
I could put things in a module and import it, but then the whole hierarchy of documents becomes almost useless.
I already have the documents' hierarchy, so if I can, I want to use it if there is a way.
You could try using Class.inherited

Resources