sender class in ruby?
This would be impossible. You shouldn't be specialising your behaviour in a method based on the calling class anyway.
Think about it this way - the caller could be an anonymous function (proc
) created in one class, then given to another one and invoked from a third place. You wouldn't get anything useful.
Instead, I'd look at what you're trying to achieve here, and think of another way to get there! :)
How to understand sender and receiver in Ruby?
A core concept in Object Orientation is messaging and early conceptualization borrowed much from the Actor Model of computation. Alan Kay, the guy who coined the term Object Oriented and invented one of the first OO languages SmallTalk, has voiced regret at using a term which put the focus on objects instead of on messages, which he considered the stronger idea.
When talking about a message, there's a natural "sender" and "receiver" of the message. The sender is the object which invokes a method, the receiver is the object whose method is invoked. In Ruby, if one calls a method without explicitly naming an object, that sends the method name and its args as a message to the default receiver self
.
In OO, "making a call", "invoking a method", and "sending a message" are equivalent concepts. Similarly "being called", "having one's method invoked", and "receiving a message" are equivalent.
Ruby on Rails Params set recipient and sender with users_id
Try changing your form to this:
<%= form_for @message do |f| %>
<%= f.hidden_field :sender_id, value: current_user.id %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
Get caller class
After futzing about with caller
for a while, it's probably not going to do it for you, and neither is caller_locations
. It's possible to track the classes of the last objects instantiated on the current thread, e.g.
class Class
alias :_new :new
def new *args
Thread.current.thread_variable_set :classes, ((Thread.current.thread_variable_get(:classes) || []) << self).last(10)
_new *args
end
end
This retains the classes of the last 10 objects, but this isn't directly equivalent to a hierarchy e.g.
class X
def initialize
puts Thread.current.thread_variable_get(:classes)
end
end
class Y
end
class Z
def initialize
@y = Y.new
@x = X.new
end
end
X.new outputs the following (in a console session)
RubyToken::TkNL
RubyToken::TkEND
RubyToken::TkNL
RubyToken::TkCONSTANT
RubyToken::TkDOT
RubyToken::TkIDENTIFIER
RubyToken::TkNL
Y
Z
X
Can I call a referring class in a service object?
I don't think you can do it natively, but there is a gem for it according to sender class in ruby?
Ruby protected visibility calling from superclass
Although it does not work on Parent Class which know nothing of the protected method, it works on the Subclasses of Subclass which define the protected method. Eg.
class A
def n(other)
other.m
end
end
class B < A
def m
1
end
protected :m
end
class C < B
end
class D < C
end
a = A.new
b = B.new
c = C.new
d = C.new
c.n b #=> 1 -- sender C is a subclass of B
b.n b #=> 1 -- m called on defining class
a.n b # raises NoMethodError although reciever B is a subclass of sender A
b.n c #=> 1 -- reciever C is subclass of sender B
c.n d #=> 1 -- reciever D is sublcass of sender C
We can probably conclude that the behaviour is something like " either the sender or the reciever must have inherited the method". With that behivaour, we can explain that since neither A (which does not know the existence of m) nor B (which knows the existence but not inherited it) ineherited the method, it raises the error.
Although there is also a possibility that this could be a bug.
undefined method `sender_id=' for nil:NilClass
That error is because you don't have @message
defined before setting @message.sender_id
in your create
action. Move @message = Message.new(message_params)
to the top of the create
action before setting @message.sender_id
as follows:
class MessagesController < ApplicationController
...
def create
@message = Message.new(message_params)
@message.sender_id = current_user
@message.recipient_id = current_user.friendships.friend_id
if @message.save?
flash[:success] = 'Message sent successfully'
redirect_to welcome_profile_path
else
render 'new'
end
end
private
def message_params
params.require(:message).permit(:body, :sender_id, :recipient_id, :user_id)
end
end
The second part of the question - current_user
is available in your controllers, so you don't need a hidden field for that. You can call @message.sender_id = current_user.id
in your create
action as you currently have. Next, to set @message.recipient_id
, you could update your new.html.erb
as follows:
# new.html.erb
<h1>Create New Message</h1>
<%= simple_form_for @message do |f| %>
<%= f.input :body %>
<%# Updated the following line. You could chose to create a hidden field here, or however you wish to implement this in your view as long as you specify `recipient` as the association or `recipient_id` as the field. %>
<%= f.association :recipient, :include_blank => false %>
<%= f.button :submit, "Send Message", class: "btn btn-secondary" %>
<% end %>
With the above setup your create
action could be updated to:
# app/controllers/messages_controller.rb
def create
@message = Message.new(message_params)
@message.sender_id = current_user
if @message.save?
flash[:success] = 'Message sent successfully'
redirect_to welcome_profile_path
else
render 'new'
end
end
Validation failed: Sender must exist, Recipient must exist
I think you are not rendering the template in UsersController#show. Try keeping it simple and let it render the show template, then you can use the link with post
method to create conversation
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
end
Also teacher
is not defined in your views, try replacing it with @user
Pass in self when I initiate the class
This is straightforward. All you have to do is write:
class A
def initialize(object = self)
# work with object
end
end
There is always a value for self
in Ruby. In the example you provided, it will evaluate to the B
class. To get it to be an instance of the class, just call the method during initialization:
class B
def initialize
A.new # self is a B instance here
end
end
Related Topics
Ruby on Rails - Link_To Button/Css
Using SASS with User-Specified Colors
How to Set a Variable from a Helper Method for Inclusion in a SASS SCSS Stylesheet
Using Phonegap as a Native Container for a Rails 3 App
Ruby on Rails - Doesn't Create Script/Server
Nokogiri: Searching for <Div> Using Xpath
Consequences of Implementing To_Int and To_Str in Ruby
How to Create Dynamic CSS in Rails
How to Strip Leading and Trailing Quote from String, in Ruby