I'm not sure what the problem is here to know what exactly I'm asking but I'll give it my best shot. I tried to bold where I think the issues is to make it clearer.
I'm working with ajax for the first time. In the app a user(1) searches for user(2), upon finding user(2) user(1) can click a button 'add relationship' which sends a relationship request to user(2). the button 'add relationship' should change to 'relationship requested' straight away and then upon refreshing the page, the button should change to 'edit relationship'.
'add relationship' = no relationship exists yet 'relationship requested' = pending relationship exists, acts like a success flash message 'edit relationship' = pending relationship exists or accepted relationship exists
It's all working except the last part where the button should change to 'edit relationship' upon refreshing the browser page. 'add relationship' appears instead
Any ideas this 'edit relationship' button wont appear? I am using state machine, draper and js-routes gems.
view/users/index:
where upon searching for user(2) their name appears and the button to 'add relationship', 'edit relationship' or 'relationship requested' buttons appear next to the name
the if statement here is what's not working I think. Why isn't it finding the relationship based on if it is pending or accepted?
<% if logged_in? %>
<ul>
<% @users.each do |user| %>
<li>
<%= user.name %>
<div id="relationship-status">
<% if current_user.following.include?(user.id) || current_user.pending_following.include?(user.id) %>
<%= link_to "Edit Relationship", edit_relationship_path(followed_id: user.id), class: "btn btn-primary" %>
<% else %>
<%= link_to "Add Relationship", new_relationship_path(followed_id: user.id), class: "btn btn-primary", id: 'add-relationship', data: { followed_id: user.id.to_param } %>
<% end %>
</div>
</li>
<% end %>
</ul>
<% end %>
controller/users:
def index
@users = User.search(params[:search])
end
relationship.js:
$(document).ready(function() {
$('#add-relationship').click(function(event) {
event.preventDefault();
var addRelationshipBtn = $(this);
$.ajax({
url: Routes.relationships_path({relationship: { followed_id: addRelationshipBtn.data('followedId') }}),
dataType: 'json',
type: 'POST',
success: function(e) {
addRelationshipBtn.hide();
$('#relationship-status').html("<a href='#' class='btn btn-success'>Relationship Requested</a>");
}
});
});
});
model/user:
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
has_many :pending_relationships, class_name: "Relationship",
foreign_key: "follower_id"
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, -> { where(relationships: { state: "accepted" } ) }, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
has_many :pending_following, -> { where(relationships: { state: "pending" } ) }, through: :pending_relationships, source: :followed
relationship/decorator:
class RelationshipDecorator < Draper::Decorator
delegate_all
decorates :relationship
def relationship_state
model.state.titleize
end
def sub_message
case model.state
when 'pending'
"Relationship request pending"
when 'accepted'
"You are now connected with #{model.followed.name}"
end
end
end
EDIT:
db/migrate:
class AddStateToRelationships < ActiveRecord::Migration
def change
add_column :relationships, :state, :string
add_index :relationships, :state
end
end
model/relationship:
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
after_destroy :delete_mutual_relationship!
state_machine :state, initial: :pending do
after_transition on: :accept, do: [:send_acceptance_email, :accept_mutual_relationship!]
state :requested
event :accept do
transition any => :accepted
end
end
terminal output:
Started POST "/relationships?relationship%5Bfollowed_id%5D=25" for 127.0.0.1 at 2014-11-06 16:35:26 +1100
Processing by RelationshipsController#create as JSON
Parameters: {"relationship"=>{"followed_id"=>"25"}}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 25]]
(0.1ms) begin transaction
SQL (5.4ms) INSERT INTO "relationships" ("created_at", "followed_id", "follower_id", "state", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", "2014-11-06 05:35:26.360104"], ["followed_id", 25], ["follower_id", 1], ["state", "pending"], ["updated_at", "2014-11-06 05:35:26.360104"]]
SQL (0.2ms) INSERT INTO "relationships" ("created_at", "followed_id", "follower_id", "state", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", "2014-11-06 05:35:26.368921"], ["followed_id", 1], ["follower_id", 25], ["state", "requested"], ["updated_at", "2014-11-06 05:35:26.368921"]]
Relationship Load (0.1ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = ? LIMIT 1 [["id", 49]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 25]]
Rendered user_mailer/relationship_requested.html.erb (0.2ms)
UserMailer#relationship_requested: processed outbound mail in 27.9ms
Sent mail to example-24@example.com (14.2ms)
Date: Thu, 06 Nov 2014 16:35:26 +1100
From: noreply@example.com
To: example-24@example.com
Message-ID: <545b089e6206e_6c313ff72cf9cf78434189f@example.local.mail>
Subject: Firstname Surname wants to follow you. Please log in to accept
this request
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Hi example-24,
Firstname Surname wants to follow you.
(7.7ms) commit transaction
Completed 200 OK in 71ms (Views: 0.3ms | ActiveRecord: 14.4ms)
what happens when I (user 1) search for a user example-24 (user id = 25) and then press the 'add relationship' button (as shown in sqlitebrowser: (see the bottom of the image for the 2 rows relevant to this example)
EDIT:
users/controller:
def create
if params[:relationship] && params[:relationship].has_key?(:followed_id)
@followed = User.find(params[:relationship][:followed_id])
# @followed = User.where(name: params[:relationship][:followed_id]).first
@relationship = Relationship.request(current_user, @followed)
respond_to do |format|
if @relationship.new_record?
format.html do
flash[:danger] = "There was a problem creating that relationship request"
redirect_to followed_path(@followed)
end
format.json { render json: @relationship.to_json, status: :precondition_failed }
else
format.html do
flash[:success] = "Friend request sent"
redirect_to followed_path(@followed)
end
format.json { render json: @relationship.to_json }
end
end
else
flash[:danger] = "Friend Required"
redirect_to users_path
end
end
Although I can't fix this error of yours, You can do it like this.
form_for
or link_to
with an option remote true.controller
and a route
to respond to your action
For example:
In your routes.rb
resources :relationships, only: [:create, :destroy]
And in your relationships_controller.rb
def create
//find the user_id of the to_be_followed user
// like User.find(params[:relationship][:user_id]
// this :relationship comes from your controller
// and current_user.relationsihps.create(followed_id: other_user_id)
respond_to do |format|
format.html { redirect_to user_path(other_user) }
format.js
end
end
//similarly
def destroy
//find the other_user, if relationship exists, destroy it
end
And then in your html
__ _follow.html.erb
<%= form_for(:relationship, url: relationships_path, remote: true) do |f| %>
<div><%= f.hidden_field :followed_id, value: @user.id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
similarly a partial _unfollow.html.erb with the value of the submit button changed to Unfollow
.
(Although I am not a big fan of hidden fields. You could set up nested routes too.)
So now you can have a method in your model say is_following(user)
, which would check if the current_user
and the `other_user have a relationship.
And say in the show page
<% if current_user.is_following(other_user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
And then you need your js.erb
files to handle the JavaScript response for you.
In this case name create.js.erb
and edit.js.erb
for example:
$("some_selector_name).html("<%= j render('shared/follow') %>")
Regarding the state machine (which i feel is kindof overkill for a two state relation) can be used in your users model maybe like
state_machine :state, initial: :unfollow
event :confirm_follow do
transition to: :follow, from: :unfollow
end
state :follow do
def is_following?(user)
// do some checks
//!!relationships.find(user.id)
end
end
end