Maintaining DataMapper Ordered Many to Many Association - ruby

I have a DataMapper many to many relationship, friends, that needs to be kept in the
order. Whats the best way to maintain the order? I placed an order property
in the join model, but cannot seem to figure out a good way to update it.
My code:
class Person
include DataMapper::Resource
property :id, Serial
property :name , String, :required => true
has n, :friendships, :child_key => [ :source_id ]
has n, :friends, self, :through => :friendships, :via => :target
end
class Friendship
include DataMapper::Resource
property :source_id, Integer, :key => true, :min => 1
property :target_id, Integer, :key => true, :min => 1
property :order, Integer
belongs_to :source, 'Person', :key => true
belongs_to :target, 'Person', :key => true
end
a = Person.new
b = Person.new
c = Person.new
a.friends = [b, c] # Keep in this order
a.friends == [b, c] # This should be true
a.friends != [c, b]
a.save
Saving person a should create a friendship between a and b with order value = 1
as well as a second friendship between a and c with order value = 2.
I've been having trouble setting the order values. From what I can tell, the friendships don't actually get created until a is saved, so I can't update them before saving. And I can't updated them after saving because the order is lost.
Any ideas?

You could do this:
Friendship.create(:source => a, :target=> b)
Friendship.create(:source => a, :target=> c)
a.reload
a.friends == [b,c]
This should give you the desired effect, assuming that the order column in your database backend auto increments. If it doesn't then you need to add something like this to your Friendship model:
before :create do
self.order = Friendship.first(:order=>[:order.desc]).order + 1 rescue 0
end
If you care about the order of these friendships then you also need to do this in the Person model:
has n, :friendships, :child_key => [ :source_id ], :order => [:order.asc]

Related

Save callbacks not running after append

I'm having a problem trying to append a relationship using the << operator. If I save after the append, my callback does not seem to run.
I have two models:
Fabric
class Fabric
include DataMapper::Resource
property :id, Serial
property :name, String
property :fixed_color, Boolean
property :active, Boolean, :default => true
has 1, :cut
has n, :colors, :through => Resource
after :save, :add_to_fishbowl
def add_to_fishbowl
puts self.colors.size
end
end
Color
class Color
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :cut
has n, :fabrics, :through => Resource
end
I create two colors and a fabric:
yellow = Color.new(:name => "yellow")
red = Color.new(:name => "red")
f = Fabric.create(:name => "tricot", :fixed_color => false)
If I used the append operator, my callback is not run:
f.colors << red
f.save
f.colors << yellow
f.save
puts f.colors.size
=> 0
=> 2
If I add arrays, it is:
f.colors = f.colors + [red]
f.save
f.colors = f.colors + [yellow]
f.save
puts f.colors.size
=> 0
=> 1
=> 2
=> 2
I'm running ruby 1.9.3p392 and data_mapper (1.2.0).
You missing relations, has n, :cuts
class Color
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :cuts
has n, :fabrics, :through => Resource
end

Strange DataMapper Issue

I have a associative model, when I try to delete a record it returns true but the record still exists, The Model has 2 columns which are keys, while the third column is an enum, also a key.Any tips relating to updating/deleting a associative model in DataMapper
class A
#some property definition
end
class B
#some property definition
end
class C
include DataMapper::Resource
include DataMapper::Grape::Resource
expose :type
property :type, Enum[:a, :b], :key => true, :unique => false
belongs_to :a, :key => true
belongs_to :b, :key => true
end
Inside racksh
element = C.get(:a,1,1)
# => returns Hash Object
# Fine till now
element.destroyed?
# => true
# I dont know why did it return true, I am 100% sure I havent performed destroy or destroy! on element
element.type
# => :a
element.type = :b
# => DataMapper::ImmutableError: Immutable resource cannot be modified
element.destroy!
# => true
C.get(:a,1,1)
# => the same Hash Object rather than returning nil

Unable to destroy a parent object

I have a large and complicated User model that looks something like this:
class User
class Link
include DataMapper::Resource
property :id, Serial, :key => false
belongs_to :follower, :model => 'User', :key => true
belongs_to :followed, :model => 'User', :key => true
end
include DataMapper::Resource
property :id, Serial
property :username, String, :required => true
has n, :links_to_followers, :model => 'User::Link', :child_key => [:followed_id]
has n, :links_to_followed, :model => 'User::Link', :child_key => [:follower_id]
has n, :comments
has 1, :profile_image
end
My problem is that Datamapper is not letting me Destroy it. I thought this was a result of Datamapper not wanting to destroy an object with un-destroyed child objects so I put in a method destroy_deep that calls destroy on the links_to_followers, links_to_followed, underlying comments, and the profile image (these are all destroyed correctly).
However, even if I call user.destroy after that, the user is not destroyed. There are no error messages of any kind. Is there some kind of cascading delete command that I am missing?
I resolved this.
Apparently to debug destroy, object.errors isn't useful. Instead track exceptions like:
begin
u.destroy
rescue Exception => e
p e
end
The solution was that one of the children fields didn't map back to User. I had a class Like that belonged to User, but User didn't have n Likes.

DataMapper Nested conditions: count issue

Here is the models:
class Foo
include DataMapper::Resource
property :id, Serial
has n, :foo_bars
has n, :bars, :through => :foo_bars
end
class Bar
include DataMapper::Resource
property :id, Serial
has n, :foo_bars
has n, :foos, :through => :foo_bars
end
class FooBar
include DataMapper::Resource
belongs_to :foo, :key => true
belongs_to :bar, :key => true
end
Inserting some data:
f = Foo.create
b1 = Bar.create
b2 = Bar.create
b3 = Bar.create
f.bars = [b1, b2, b3]
f.save
So, now I have one foo, three bars, and the foo has all the bars. Everything is fine.
Now I want to request some foos having bar#1 and bar#3:
Foo.all(Foo.bars.id => [1,3])
=> [#<Foo #id=1>] #ok
Foo.all(Foo.bars.id => [1,3]).count
=> 2 #why?
And here is the question: why array length is 1 and collection count is 2? How can I get both 1? I'd like to stick to the request with the nested conditions. Is it a bug or a misuse?
DM 1.1.0
Unfortunately you've hit a bug. I just reported an issue with your example attached: https://github.com/datamapper/dm-aggregates/issues/3
I think you should be able to get correct result by doing this for now:
Foo.count(Foo.bars.id => [1,3])

creating Models with sqlite3 + datamapper + ruby

How do you build a model with the following associations (i tried but couldn't get it to work):
each Order has: a Customer, a SalesRep, many OrderLine that each has an item.
I tried: when I do: Customer.all(Customer.orders.order_lines.item.sku.like => "%BLUE%")
the output is :[]
instead of: '[#<"Customer #id=1 #name="Dan Kubb">]'
When I delete SalesRep: it works.
Customer
has n, :orders
has n, :items, :through => :order
SalesRep
has n, :orders
has n, :items, :through => :order
Order
belongs_to :customer
belongs_to :technician
has n, :order_lines
has n, :items, :through => :order_line
OrderLine
belongs_to :order
belongs_to :item
Item
has n, :order_lines
Since the output isn't an error, it could be that you don't actually have the data in your database.
Try the following with irb -r your_models_file.rb:
- c = Customer.create(:name => "Dan Kubb")
o = Order.new(:customer => c) # Create and add technician unless it's :required => false
i = Item.create(:sku => "BLUE") # Plus any other required fields
ol = OrderLine.create(:order => o, :item => i)
o.order_lines << ol; o.save
That should create the records needed for this to work. Try this out, and if it doesn't work, post your entire models file so we can get a better idea of what's up.

Resources