Module: MostRelated
- Defined in:
- lib/most_related.rb,
lib/most_related/version.rb
Constant Summary collapse
- VERSION =
'0.1.0'
Class Method Summary collapse
Instance Method Summary collapse
-
#has_most_related(*many_to_many_associations, as: :most_related) ⇒ Object
‘most_related` returns those models that have the most associated models in common.
Class Method Details
.join_table_values(many_to_many_assocation) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/most_related.rb', line 76 def self.join_table_values(many_to_many_assocation) if many_to_many_assocation.macro == :has_and_belongs_to_many join_table = many_to_many_assocation.join_table foreign_key = many_to_many_assocation.foreign_key association_foreign_key = many_to_many_assocation.association_foreign_key elsif many_to_many_assocation.macro == :has_many join_table = many_to_many_assocation.through_reflection.table_name foreign_key = many_to_many_assocation.through_reflection.foreign_key association_foreign_key = many_to_many_assocation.foreign_key end [join_table, foreign_key, association_foreign_key] end |
Instance Method Details
#has_most_related(*many_to_many_associations, as: :most_related) ⇒ Object
‘most_related` returns those models that have the most
associated models in common
Post example:
class Post < ActiveRecord::Base
:authors
:tags, as: :most_related_by_tags
:authors, :tags, as: :most_related_by_author_or_tag
has_many :author_posts
has_many :authors, through: :author_posts
end
class Author < ActiveRecord::Base
has_many :author_posts
end
class AuthorPosts < ActiveRecord::Base
belongs_to :author
belongs_to :post
end
To return the posts with the most authors in common with ‘post`, in descending order:
post.
To return the posts with the most tags in common with ‘post`, in descending order:
post.
To return the posts with the most authors and tags in common with ‘post`, in descending order:
post.
The count of the associated models in common is accessible on each returned model
eg post., post. and post.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/most_related.rb', line 40 def (*many_to_many_associations, as: :most_related) # defaults to 'def most_related' define_method as do table_name = self.class.table_name association_scopes = [] many_to_many_associations.each do |many_to_many_association| assocation = self.class.reflect_on_association(many_to_many_association) join_table, foreign_key, association_foreign_key = self.class.join_table_values(assocation) association_scopes << self.class.where("#{join_table}.#{association_foreign_key} IN " + "(select #{join_table}.#{association_foreign_key} from #{join_table} " + "where #{join_table}.#{foreign_key} = :foreign_key)", foreign_key: self.id). joins("INNER JOIN #{join_table} ON #{join_table}.#{foreign_key} = #{table_name}.id") end scope = self.class.select("#{table_name}.*, count(#{table_name}.id) AS #{as}_count"). where.not(id: self.id).order("#{as}_count DESC") group_by_clause = 'id' # if there is only one many-to-many association no need to use UNION sql syntax if association_scopes.one? scope.merge(association_scopes.first).group(group_by_clause) else # with postgres the group by clause has to be different # http://dba.stackexchange.com/questions/88988/postgres-error-column-must-appear-in-the-group-by-clause-or-be-used-in-an-aggre if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' group_by_clause = self.class.column_names.join(', ') end # see http://blog.ubersense.com/2013/09/27/tech-talk-unioning-scoped-queries-in-rails/ scope.from("((#{association_scopes.map(&:to_sql).join(') UNION ALL (')})) AS #{table_name}").group(group_by_clause) end end def self.join_table_values(many_to_many_assocation) if many_to_many_assocation.macro == :has_and_belongs_to_many join_table = many_to_many_assocation.join_table foreign_key = many_to_many_assocation.foreign_key association_foreign_key = many_to_many_assocation.association_foreign_key elsif many_to_many_assocation.macro == :has_many join_table = many_to_many_assocation.through_reflection.table_name foreign_key = many_to_many_assocation.through_reflection.foreign_key association_foreign_key = many_to_many_assocation.foreign_key end [join_table, foreign_key, association_foreign_key] end end |