Skip to content

Commit

Permalink
Support for association aliasing (#444)
Browse files Browse the repository at this point in the history
  • Loading branch information
mereghost authored and jodosha committed Oct 13, 2017
1 parent 452b1f4 commit e85c3af
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 10 deletions.
13 changes: 10 additions & 3 deletions lib/hanami/model/associations/belongs_to.rb
Expand Up @@ -82,15 +82,22 @@ def foreign_key
# @since 1.1.0
# @api private
def association_keys
relation(source)
.associations[target]
association
.__send__(:join_key_map, container.relations)
end

# Return the ROM::Associations for the source relation
#
# @since x.x.x
# @api private
def association
relation(source).associations[target]
end

# @since 1.1.0
# @api private
def _build_scope
result = relation(target)
result = relation(association.target.to_sym)
result = result.where(foreign_key => subject.fetch(primary_key)) unless subject.nil?
result.as(Model::MappedRelation.mapper_name)
end
Expand Down
15 changes: 11 additions & 4 deletions lib/hanami/model/associations/has_many.rb
Expand Up @@ -7,7 +7,7 @@ module Associations
#
# @since 0.7.0
# @api private
class HasMany
class HasMany # rubocop:disable Metrics/ClassLength
# @since 0.7.0
# @api private
def self.schema_type(entity)
Expand Down Expand Up @@ -177,15 +177,22 @@ def foreign_key
# @since 0.7.0
# @api private
def association_keys
relation(source)
.associations[target]
target_association
.__send__(:join_key_map, container.relations)
end

# Returns the targeted association for a given source
#
# @since 0.7.0
# @api private
def target_association
relation(source).associations[target]
end

# @since 0.7.0
# @api private
def _build_scope
result = relation(target)
result = relation(target_association.target.to_sym)
result = result.where(foreign_key => subject.fetch(primary_key)) unless subject.nil?
result.as(Model::MappedRelation.mapper_name)
end
Expand Down
11 changes: 10 additions & 1 deletion lib/hanami/model/associations/many_to_many.rb
Expand Up @@ -158,11 +158,20 @@ def target_primary_key
association_keys[1].last
end

# Return the ROM::Associations for the source relation
#
# @since x.x.x
# @api private
def association
relation(source).associations[target]
end

# @since 1.1.0
#
# @api private
# rubocop:disable Metrics/AbcSize
def _build_scope
result = relation(target).qualified
result = relation(association.target.to_sym).qualified
unless subject.nil?
result = result
.join(through, target_foreign_key => target_primary_key)
Expand Down
2 changes: 1 addition & 1 deletion lib/hanami/model/sql/entity/schema.rb
Expand Up @@ -122,7 +122,7 @@ def build_attributes(relation, mapping)
# @api private
def build_associations(registry, associations)
associations.each_with_object({}) do |(name, association), result|
target = registry.fetch(name)
target = registry.fetch(association.target.to_sym)
result[name] = Association.lookup(association).schema_type(target)
end
end
Expand Down
61 changes: 61 additions & 0 deletions spec/integration/hanami/model/associations/relation_alias_spec.rb
@@ -0,0 +1,61 @@
RSpec.describe 'Alias (:as) support for associations' do
let(:users) { UserRepository.new }
let(:posts) { PostRepository.new }
let(:comments) { CommentRepository.new }

it 'the attribute is named after the association' do
user = users.create(name: 'Jules Verne')
post = posts.create(title: 'World Traveling made easy', user_id: user.id)

post_found = posts.find_with_author(post.id)
expect(post_found.author).to eq(user)

user_found = users.find_with_threads(user.id)
expect(user_found.threads).to match_array([post])
end

it 'it works with nested aggregates' do
user = users.create(name: 'Jules Verne')
post = posts.create(title: 'World Traveling made easy', user_id: user.id)
commenter = users.create(name: 'Thomas Reid')
comments.create(user_id: commenter.id, post_id: post.id)

found = posts.feed_for(post.id)
expect(found.author).to eq(user)
expect(found.comments[0].user).to eq(commenter)
end

context '#assoc support (calling assoc by the alias)' do
it 'for #belongs_to' do
user = users.create(name: 'Jules Verne')
post = posts.create(title: 'World Traveling made easy', user_id: user.id)
commenter = users.create(name: 'Thomas Reid')
comment = comments.create(user_id: commenter.id, post_id: post.id)

found_author = posts.author_for(post)
expect(found_author).to eq(user)

found_commenter = comments.commenter_for(comment)
expect(found_commenter).to eq(commenter)
end

it 'for #has_many' do
user = users.create(name: 'Jules Verne')
post = posts.create(title: 'World Traveling made easy', user_id: user.id)

found_threads = users.threads_for(user)
expect(found_threads).to match_array [post]
end

it 'for #has_many :through' do
user = users.create(name: 'Jules Verne')
post = posts.create(title: 'World Traveling made easy', user_id: user.id)
commenter = users.create(name: 'Thomas Reid')
comments.create(user_id: commenter.id, post_id: post.id)

commenters = posts.commenters_for(post)

expect(commenters).to match_array([commenter])
end
end
end
54 changes: 53 additions & 1 deletion spec/support/fixtures.rb
Expand Up @@ -25,7 +25,10 @@ class AccessToken < Hanami::Entity
class SourceFile < Hanami::Entity
end

class Avatar < Hanami::Entity
class Post < Hanami::Entity
end

class Comment < Hanami::Entity
end

class Warehouse < Hanami::Entity
Expand Down Expand Up @@ -77,6 +80,45 @@ class Color < Hanami::Entity
class Label < Hanami::Entity
end

class PostRepository < Hanami::Repository
associations do
belongs_to :user, as: :author
has_many :comments
has_many :users, through: :comments, as: :commenters
end

def find_with_commenters(id)
aggregate(:commenters).where(id: id).map_to(Post).to_a
end

def commenters_for(post)
assoc(:commenters, post).to_a
end

def find_with_author(id)
aggregate(:author).where(id: id).map_to(Post).one
end

def feed_for(id)
aggregate(:author, comments: :user).where(id: id).map_to(Post).one
end

def author_for(post)
assoc(:author, post).one
end
end

class CommentRepository < Hanami::Repository
associations do
belongs_to :post
belongs_to :user
end

def commenter_for(comment)
assoc(:user, comment).one
end
end

class AvatarRepository < Hanami::Repository
associations do
belongs_to :user
Expand All @@ -90,6 +132,16 @@ def by_user(id)
class UserRepository < Hanami::Repository
associations do
has_one :avatar
has_many :posts, as: :threads
has_many :comments
end

def find_with_threads(id)
aggregate(:threads).where(id: id).map_to(User).one
end

def threads_for(user)
assoc(:threads, user).to_a
end

def find_with_avatar(id)
Expand Down
@@ -0,0 +1,17 @@
Hanami::Model.migration do
change do
drop_table? :posts
create_table? :posts do
primary_key :id
column :title, String
foreign_key :user_id, :users, on_delete: :cascade, null: false
end
drop_table? :comments
create_table? :comments do
primary_key :id

foreign_key :user_id, :users, on_delete: :cascade, null: false
foreign_key :post_id, :posts, on_delete: :cascade, null: false
end
end
end

0 comments on commit e85c3af

Please sign in to comment.