Rails user to user messages -


i'm new rails please detailed in responses. i'm building web app uses devise authentication. part i'm stuck on right user user messaging system. idea user logs app , can visit user b's profile, , on user b's profile can click on link allows user compose message user b. user b can log app , visit inbox user a's message found.

i believe i'm having trouble defining sender , recipient roles here, right i'm trying display form users compose message in. can see i'm doing wrong here? following error. i've read thing add user_id field table, i'm hoping link messages using sender_id , recipient_id, both equal user_id (e.g. user 1[sender] sends message user 2 [recipient]):

unknown attribute: user_id

def new @message = current_user.messages.new recipient_id: params[:sender_id] end

additionally, rails experts or has done similar this, can advise whether or not i'm going in right direction, or offer guidance? i'm sort of coding blind here , trying make go along. guidance hugely appreciated , save me lot of time i'm sure. code below:

users migration

class devisecreateusers < activerecord::migration   def change     create_table(:users) |t|       t.string :first_name       t.string :last_name       t.string :email,              null: false, default: ""       t.string :encrypted_password, null: false, default: ""        t.string   :reset_password_token       t.datetime :reset_password_sent_at        t.datetime :remember_created_at        t.integer  :sign_in_count, default: 0, null: false       t.datetime :current_sign_in_at       t.datetime :last_sign_in_at       t.string   :current_sign_in_ip       t.string   :last_sign_in_ip        t.timestamps     end      add_index :users, :email,                unique: true     add_index :users, :reset_password_token, unique: true    end end 

messages migration

class createmessages < activerecord::migration   def change     create_table :messages |t|       t.string :content       t.integer :sender_id       t.integer :recipient_id       t.timestamps     end   end end 

schema.rb

activerecord::schema.define(version: 20140909174718)    create_table "messages", force: true |t|     t.string   "content"     t.integer  "sender_id"     t.integer  "recipient_id"     t.datetime "created_at"     t.datetime "updated_at"   end    create_table "users", force: true |t|     t.string   "first_name"     t.string   "last_name"     t.string   "email",                     default: "", null: false     t.string   "encrypted_password",        default: "", null: false     t.string   "reset_password_token"     t.datetime "reset_password_sent_at"     t.datetime "remember_created_at"     t.integer  "sign_in_count",             default: 0,  null: false     t.datetime "current_sign_in_at"     t.datetime "last_sign_in_at"     t.string   "current_sign_in_ip"     t.string   "last_sign_in_ip"     t.datetime "created_at"     t.datetime "updated_at"     t.string   "current_industry"     t.integer  "years_in_current_industry"     t.string   "hobbies"   end    add_index "users", ["email"], name: "index_users_on_email", unique: true   add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true  end 

routes.rb

catalyst::application.routes.draw    devise_for :users, :controllers => { :registrations => "registrations" }    devise_scope :user     'register', to: 'devise/registrations#new'     'login',    to: 'devise/sessions#new',     as: :login     'logout',   to: 'devise/sessions#destroy', as: :logout   end    resources :users     member       'edit_profile'     end     resources :messages, only: [:new, :create]   end    resources :messages, only: [:index, :show, :destroy]    root to: "home#index"   match '/about',   to: 'static_pages#about',   via: 'get'   match '/contact', to: 'static_pages#contact', via: 'get'    match '/help',    to: 'static_pages#help',    via: 'get'   match '/legal',   to: 'static_pages#legal',   via: 'get'  end 

users_controller

class userscontroller < applicationcontroller   before_filter :authenticate_user!     def index       @users = user.all     end      def show       @user = user.find(params[:id])     end      def new     end      def create     end      def edit     end      def update       @user = user.find(params[:id])       @user.update!(user_params)       redirect_to @user     end      def destroy     end      def edit_profile       @user = user.find(params[:id])     end      def user_params       params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :current_industry, :years_in_current_industry, :hobbies)     end      def sender       @user = user.find(params[:id])     end      def recipient       @user = user.find(params[:id])     end    end 

messages_controller

class messagescontroller < applicationcontroller   before_action :set_recipient    def new     @message = message.new     @recipient = user.find(params[:user_id])   end    def create     @message = message.new message_params     if @message.save       flash[:success] = "your message has been sent!"       redirect_to user_messages_path     else       flash[:failure] = "please try again."       redirect_to users_path     end   end    private    def message_params     params.require(:message).permit(:content, :sender_id, :recipient_id)   end end 

user.rb

class user < activerecord::base   has_many :from_messages, class_name: 'message', :foreign_key => "sender_id"   has_many :to_messages, class_name: 'message', :foreign_key => "recipient_id"    devise :database_authenticatable, :registerable,      :recoverable, :rememberable, :trackable, :validatable    attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :remember_me, :current_industry, :years_in_current_industry, :hobbies  end 

message.rb

class message < activerecord::base   belongs_to :sender, class_name: "user"   belongs_to :recipient, class_name: "user"    validates :content, presence: true, length: { maximum: 500 }   validates :sender_id, presence: true   validates :recipient_id, presence: true end 

messages/index.html.erb

<h2>inbox</h2> 

messages/new.html.erb

<h1>create message</h1>  <%= form_for [@recipient, @message] |f| %>      <%= f.hidden_field :recipient_id, value: @recipient.id %>      <%= f.label "enter message below" %><br />     <%= f.text_area :content %>      <%= f.submit "send" %> <% end %> 

rake routes

user_messages post   /users/:user_id/messages(.:format)     messages#create     new_user_message    /users/:user_id/messages/new(.:format) messages#new                users    /users(.:format)                       users#index                      post   /users(.:format)                       users#create             new_user    /users/new(.:format)                   users#new            edit_user    /users/:id/edit(.:format)              users#edit                 user    /users/:id(.:format)                   users#show                      patch  /users/:id(.:format)                   users#update                      put    /users/:id(.:format)                   users#update                      delete /users/:id(.:format)                   users#destroy             messages    /messages(.:format)                    messages#index              message    /messages/:id(.:format)                messages#show                      delete /messages/:id(.:format)                messages#destroy 

models

#app/models/user.rb class user < activerecord::base    has_many :messages, class_name: "message", foreign_key: "recipient_id"    has_many :sent_messages, class_name: "message", foreign_key: "sender_id" end  #app/models/message.rb class message < activerecord::base    belongs_to :recipient, class_name: "user", foreign_key: "recipient_id"    belongs_to :sender, class_name: "user", foreign_key: "sender_id"    scope :unread, -> { read: false } end 

this should give ability create messages "belong" user (ie recipient), , can associate "sender" profile messages.

--

controllers

this give ability call following:

#app/controllers/messages_controller.rb class messagescontroller < applicationcontroller    before_action :set_recipient, only: [:new, :create]     def new       @message = current_user.sent_messages.new    end     def create       @message = current_user.sent_messages.new message_params       @message.recipient_id = @recipient.id       @message.save    end     def index       @messages = current_user.messages    end     def destroy       @message = current_user.messages.destroy params[:id]    end     def show       @message = current_user.messages.find params[:id]    end     private     def message_params       params.require(:message).permit(:content, :recipient_id, :sender_id)    end     def set_recipient        @recipient = user.find params[:user_id]    end end 

--

routes

#config/routes.rb devise_for :users, path: "", controllers: { :registrations => "registrations" }, path_names: {sign_up: "register", sign_in: "login", sign_out: "logout"}  resources :users    :profile    resources :messages, only: [:new, :create] #-> domain.com/users/:user_id/messages/new end resources :messages, only: [:index, :show, :destroy] #-> domain.com/messages/:id 

--

views

this give ability use following links:

#app/views/users/show.html.erb (user send message to) <%= link_to "send message", user_messages_path(@user.id) %>  #app/views/messages/new.html.erb <%= form_for [@recipient, @user] |f| %>      <%= f.text_field :content %>      <%= f.submit %> <% end %>  #app/views/messages/index.html.erb <h2>inbox</h2> <% @messages.each |message| %>    <%= message.content %> <% end %> 

--

fix

i've read thing add user_id field table, i'm hoping link messages using sender_id , recipient_id, both equal user_id (e.g. user 1[sender] sends message user 2 [recipient])

you don't need add user_id table. user_id merely foreign_key, you've overridden in models.

all need set recipient_id , sender_id, we're doing in create method:

def create    @message = current_user.message.new message_params    @message.recipient_id = @recipient.id    @message.save end 

you've done clever things here.

firstly, have implicitly set sender_id foreign key calling current_user.messages. if had called message.new, have been different story (having set sender_id)

secondly, because you're using nested routes, you'll able use @recipient variable you've set in before_action method give id recipient_id.

this should work you. won't need use inverse_of unless trying access "parent" model data in child / nested model.


recommendations

what you're doing valid

the core trick make sure message model separate & independent user. achieved setup, allowing create various objects require.

the other aspect need consider how you're going ensure you're able provide users ability have "threaded" messages. you'll achieve using 1 of hierarchy gems, either ancestry or closure_tree

adding functionality little more in-depth. can provide information if require (just leave comment)


threading

the hierarchy gems relatively simple use.

the trick "treading" messages use 1 of these gems (either ancestry or closure_tree), provide "methods" can call on items. work creating several columns in database, populating them save / create objects desire

the "threading" issue big one, without "hierarchy" gems, won't able call "child" objects of record want, preventing threading occurring. here's good railscast on how achieve it:

enter image description here

the trick use called "recursion"

recursion create "indefinite" loop, far how "recursive" data is. eg if have object children, you'll have cycle through children, , children of children, recursively until reach point of showing data:

recursion process of repeating items in self-similar way. instance, when surfaces of 2 mirrors parallel each other, nested images occur form of infinite recursion.

as such, here's how it:

  1. make sure save objects correct parents
  2. to display "threaded" conversation, loop through parents
  3. use recursion loop through children

we use ancestry gem, stores hierarchy differently closure_tree gem we've since discovered (intend use closure tree gem soon).

you firstly have therefore save hierarchy yourself:

enter image description here

this allow save various "parents" object. means when load object, , wish cycle through descendent, you'll able use ancestry object methods:

enter image description here

which means you'll able use following:

#app/views/comments/index.html.erb <%= render partial: "comments", locals: { collection: @comments } %>  #app/comments/_comments.html.erb <% collection.arrange.each |comment, sub_item| %>     <%= link_to comment.title, comment_path(comment) %>      <% if category.has_children? %>         <%= render partial: "category", locals: { collection: category.children } %>     <% end %> <% end %> 

Comments

Popular posts from this blog

javascript - RequestAnimationFrame not working when exiting fullscreen switching space on Safari -

jsf - How to ajax update an item in the footer of a PrimeFaces dataTable? -

django - CSRF verification failed. Request aborted. CSRF cookie not set -