Rails polymorphic @ mentions create action

222 Views Asked by At

I'm trying to create a simple @ mentions model similar to twitters for my app. I've started building it, but I don't know how I would handle the actual creation of the mention. I need some way to scan let's say a status before it's created for any @ symbols, then checking the text following against the database for any matching usernames. If there's a match then a mention gets created along with the status. Can someone point me in the right direction?

Here's what I have so far:

db/migrate/create_mentions.rb

class CreateMentions < ActiveRecord::Migration
    def change
      create_table :mentions do |t|
        t.belongs_to :mentionable, polymorphic: true
        t.timestamps
      end
      add_index :mentions, [:mentionable_id, :mentionable_type]
    end
end

models/mention.rb

class Mention < ActiveRecord::Base
    belongs_to :mentionable, polymorphic: true
end

models/status.rb

class Status < ActiveRecord::Base
    attr_accessible :content
    has_many :mentions, dependent: :destroy
end

models/member.rb

class Member < ActiveRecord::Base
    has_many :mentions, as: :mentionable, dependent: :destroy
end 

controllers/mentions_controller.rb

class MentionsController < ApplicationController

    before_filter :authenticate_member!
    before_filter :load_mentionable
    before_filter :find_member

    def new
        @mention = @mentionable.mentions.new
    end

    def create
        @mention = @mentionable.mentions.new(params[:mention])
        respond_to do |format|
          if @mention.save
            format.html { redirect_to :back }
          else
            format.html { redirect_to :back }
          end
        end
    end 

    private 

    def load_mentionable
        klass = [Status].detect { |c| params["#{c.name.underscore}_id"] }
        @mentionable = klass.find(params["#{klass.name.underscore}_id"])
    end

    def find_member
        @member = Member.find_by_user_name(params[:user_name])
    end 

end

config/routes.rb

resources :statuses do
    resources :mentions
end 
1

There are 1 best solutions below

0
On BEST ANSWER

Thanks to this question: parse a post for @username I was able to get this working. My set up:

db/migrate/create_mentions.rb

class CreateMentions < ActiveRecord::Migration
    def change
      create_table :mentions do |t|
        t.belongs_to :mentionable, polymorphic: true
        t.belongs_to :mentioner, polymorphic: true
        t.integer :status_id
        t.integer :comment_id
        t.timestamps
      end
      add_index :mentions, [:mentionable_id, :mentionable_type], :name => "ments_on_ables_id_and_type"
      add_index :mentions, [:mentioner_id, :mentioner_type], :name => "ments_on_ers_id_and_type"
    end
end

models/mention.rb

class Mention < ActiveRecord::Base
    attr_accessible :mentioner_id, :mentioner_type, :mentionable_type, :mentionable_id, :status_id, :comment_id

    belongs_to :mentioner, polymorphic: true
    belongs_to :mentionable, polymorphic: true

end

models/member.rb

class Member < ActiveRecord::Base
    has_many :mentions, as: :mentionable, dependent: :destroy
end

models/status.rb

class Status < ActiveRecord::Base

  attr_accessor :mention
  has_many :mentions, as: :mentioner, dependent: :destroy
  after_save :save_mentions

  USERNAME_REGEX = /@\w+/i


  private

  def save_mentions
    return unless mention?

    people_mentioned.each do |member|
      Mention.create!(:status_id => self.id, :mentioner_id => self.id, :mentioner_type => 'Status', :mentionable_id => member.id, :mentionable_type => 'Member')
    end
  end

  def mention?
    self.content.match( USERNAME_REGEX )
  end

  def people_mentioned
    members = []
    self.content.clone.gsub!( USERNAME_REGEX ).each do |user_name|
      member = Member.find_by_user_name(user_name[1..-1])
      members << member if member
    end
    members.uniq
  end

end

config/routes.rb

resources :statuses do
  resources :mentions
end

helpers/mentions_helper.rb

module MentionsHelper

    def statuses_with_mentions(status)
        status.content_html.gsub(/@\w+/).each do |user_name|
            member = Member.find_by_user_name(user_name[1..-1])
            if member
              link_to user_name, profile_path(member.user_name)
            else
              user_name
            end
        end 
    end

end