Skip to content

Instantly share code, notes, and snippets.

@selman
Created December 17, 2010 15:32
Show Gist options
  • Save selman/745115 to your computer and use it in GitHub Desktop.
Save selman/745115 to your computer and use it in GitHub Desktop.
self referential relationship
require 'data_mapper'
DataMapper::Logger.new(STDOUT, :debug)
DataMapper.setup(:default, "sqlite:memory:")
class Person
class Link
include DataMapper::Resource
storage_names[:default] = 'people_links'
# the person who is following someone
belongs_to :follower, 'Person', :key => true
# the person who is followed by someone
belongs_to :followed, 'Person', :key => true
end
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
# If we want to know all the people that John follows, we need to look
# at every 'Link' where John is a :follower. Knowing these, we know all
# the people that are :followed by John.
#
# If we want to know all the people that follow Jane, we need to look
# at every 'Link' where Jane is :followed. Knowing these, we know all
# the people that are a :follower of Jane.
#
# This means that we need to establish two different relationships to
# the 'Link' model. One where the person's role is :follower and one
# where the person's role is to be :followed by someone.
# In this relationship, the person is the follower
has n, :links_to_followed_people, 'Person::Link', :child_key => [:follower_id]
# In this relationship, the person is the one followed by someone
has n, :links_to_followers, 'Person::Link', :child_key => [:followed_id]
# We can then use these two relationships to relate any person to
# either the people followed by the person, or to the people this
# person follows.
# Every 'Link' where John is a :follower points to a person that
# is :followed by John.
has n, :followed_people, self,
:through => :links_to_followed_people, # The person is a follower
:via => :followed
# Every 'Link' where Jane is :followed points to a person that
# is a :follower by Jane.
has n, :followers, self,
:through => :links_to_followers, # The person is followed by someone
:via => :follower
# Follow one or more other people
def follow(others)
followed_people.concat(Array(others))
save
self
end
# Unfollow one or more other people
def unfollow(others)
links_to_followed_people.all(:followed => Array(others)).destroy!
reload
self
end
end
DataMapper.finalize
DataMapper.auto_migrate!
require 'minitest/autorun'
describe Person do
before do
@me = Person.create :name => 'me'
@friend = Person.create :name => 'friend'
@me.follow @friend
end
it "friend in my follow list" do
@me.followed_people.count.must_equal 1
@me.followed_people.first.must_equal @friend
end
it "friend must see me as follower" do
@friend.followers.count.must_equal 1
@friend.followers.first.must_equal @me
end
it "I have no followers" do
@me.followers.count.must_equal 0
end
it "I unfollowed friend" do
@me.unfollow @friend
@me.followed_people.count.must_equal 0
@friend.followers.count.must_equal 0
end
end
__END__
~ (0.000354) PRAGMA table_info("people_links")
~ (0.000027) PRAGMA table_info("people")
~ (0.000035) SELECT sqlite_version(*)
~ (0.080309) DROP TABLE IF EXISTS "people_links"
~ (0.000029) PRAGMA table_info("people_links")
~ (0.073655) CREATE TABLE "people_links" ("follower_id" INTEGER NOT NULL, "followed_id" INTEGER NOT NULL, PRIMARY KEY("follower_id", "followed_id"))
~ (0.074480) CREATE INDEX "index_people_links_follower" ON "people_links" ("follower_id")
~ (0.074465) CREATE INDEX "index_people_links_followed" ON "people_links" ("followed_id")
~ (0.074290) DROP TABLE IF EXISTS "people"
~ (0.000028) PRAGMA table_info("people")
~ (0.073867) CREATE TABLE "people" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" VARCHAR(50) NOT NULL)
Loaded suite dmassoc
Started
PersonSpec#test_0001_friend_in_my_follow_list: ~ (0.071468) INSERT INTO "people" ("name") VALUES ('me')
~ (0.090111) INSERT INTO "people" ("name") VALUES ('friend')
~ (0.000085) SELECT "follower_id", "followed_id" FROM "people_links" WHERE ("follower_id" = 1 AND "followed_id" = 2) ORDER BY "follower_id", "followed_id" LIMIT 1
~ (0.075941) INSERT INTO "people_links" ("follower_id", "followed_id") VALUES (1, 2)
~ (0.000101) SELECT COUNT(*) FROM "people" INNER JOIN "people_links" ON "people"."id" = "people_links"."followed_id" INNER JOIN "people" "people_1" ON "people_links"."follower_id" = "people_1"."id" WHERE "people_links"."follower_id" = 1
~ (0.000116) SELECT "people"."id", "people"."name" FROM "people" INNER JOIN "people_links" ON "people"."id" = "people_links"."followed_id" INNER JOIN "people" "people_1" ON "people_links"."follower_id" = "people_1"."id" WHERE "people_links"."follower_id" = 1 GROUP BY "people"."id", "people"."name" ORDER BY "people"."id" LIMIT 1
0.25 s: .
PersonSpec#test_0003_i_have_no_followers: ~ (0.069603) INSERT INTO "people" ("name") VALUES ('me')
~ (0.073712) INSERT INTO "people" ("name") VALUES ('friend')
~ (0.000084) SELECT "follower_id", "followed_id" FROM "people_links" WHERE ("follower_id" = 3 AND "followed_id" = 4) ORDER BY "follower_id", "followed_id" LIMIT 1
~ (0.067833) INSERT INTO "people_links" ("follower_id", "followed_id") VALUES (3, 4)
~ (0.000115) SELECT COUNT(*) FROM "people" INNER JOIN "people_links" ON "people"."id" = "people_links"."follower_id" INNER JOIN "people" "people_1" ON "people_links"."followed_id" = "people_1"."id" WHERE "people_links"."followed_id" = 3
0.22 s: .
PersonSpec#test_0002_friend_must_see_me_as_follower: ~ (0.069754) INSERT INTO "people" ("name") VALUES ('me')
~ (0.065375) INSERT INTO "people" ("name") VALUES ('friend')
~ (0.000119) SELECT "follower_id", "followed_id" FROM "people_links" WHERE ("follower_id" = 5 AND "followed_id" = 6) ORDER BY "follower_id", "followed_id" LIMIT 1
~ (0.076342) INSERT INTO "people_links" ("follower_id", "followed_id") VALUES (5, 6)
~ (0.000109) SELECT COUNT(*) FROM "people" INNER JOIN "people_links" ON "people"."id" = "people_links"."follower_id" INNER JOIN "people" "people_1" ON "people_links"."followed_id" = "people_1"."id" WHERE "people_links"."followed_id" = 6
~ (0.000121) SELECT "people"."id", "people"."name" FROM "people" INNER JOIN "people_links" ON "people"."id" = "people_links"."follower_id" INNER JOIN "people" "people_1" ON "people_links"."followed_id" = "people_1"."id" WHERE "people_links"."followed_id" = 6 GROUP BY "people"."id", "people"."name" ORDER BY "people"."id" LIMIT 1
0.23 s: .
PersonSpec#test_0004_i_unfollowed_friend: ~ (0.076265) INSERT INTO "people" ("name") VALUES ('me')
~ (0.073703) INSERT INTO "people" ("name") VALUES ('friend')
~ (0.000094) SELECT "follower_id", "followed_id" FROM "people_links" WHERE ("follower_id" = 7 AND "followed_id" = 8) ORDER BY "follower_id", "followed_id" LIMIT 1
~ (0.084370) INSERT INTO "people_links" ("follower_id", "followed_id") VALUES (7, 8)
~ (0.081478) DELETE FROM "people_links" WHERE ("follower_id" = 7 AND "followed_id" = 8)
~ (0.000107) SELECT COUNT(*) FROM "people" INNER JOIN "people_links" ON "people"."id" = "people_links"."followed_id" INNER JOIN "people" "people_1" ON "people_links"."follower_id" = "people_1"."id" WHERE "people_links"."follower_id" = 7
~ (0.000111) SELECT COUNT(*) FROM "people" INNER JOIN "people_links" ON "people"."id" = "people_links"."follower_id" INNER JOIN "people" "people_1" ON "people_links"."followed_id" = "people_1"."id" WHERE "people_links"."followed_id" = 8
0.33 s: .
Finished in 1.034276 seconds.
4 tests, 7 assertions, 0 failures, 0 errors, 0 skips
Test run options: --seed 54307 --verbose
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment