I am developing a Rails application and would like to understand when to use self.for.
Here is the code of a method that I would like to fully understand. If it is possible I would like to have an alternative to this code so it would make things more clear.
def self.for(facebook_id)
User.create_by_facebook_id(facebook_id)
end
self refers to the current object.
Within a class, self is used to define a class-level method.
class Foo
def self.for(facebook_id)
User.create_by_facebook_id(facebook_id)
end
end
defines a class method for in class Foo. It is invoked:
Foo.for(facebook_id)
You can google for class methods to learn more.
It could be that a part of Rails or a plugin/gem is expecting that some classes will have a class method "for" More context would be helpful in this regard.
What the method is doing
As is common for class methods, it is creating an instance of a class. For example, the ActiveRecord class has a class method "create" which attempts to create an instance of the model class that has been stored in the database. Thus User.create will return an instance of the User class that has been stored in the database.
In your example code, it is calling a class method "create_by_facebook_id" that is provided by the User class in the application.
Looks like the "for" method is being used for information hiding since all it's doing is making another method call (to User.create_by_facebook)
Added:
By the way, the default return value from Ruby methods is the value of the last statement. So your example method will return the user instance newly created from the supplied facebook_id.
It looks to me like self.for is just an alias for creating a user for a facebook id. I think self.for should actually be:
def self.for(facebook_id)
User.find_or_create_by_facebook_id(facebook_id)
end
That way it searches for the user with that facebook id, and if one isn't found, creates that record and returns it. Then, self.for means "return the user for this facebook id."
Short answer: self always refers to the current object. So within an instance method, self is the instance, within a class method, self is the class and within a class definition (like in your example), self is the class...
For more information on class methods and the code snippet you posted, see the answer by Larry K.
If that code is inside of a class named Foo, then the alternative could be:
def Foo.for(facebook_id)
User.create_by_facebook_id(facebook_id)
end
self in this context is necessary if you have some generic class level methods that you want to be able to use across multiple classes. You add them to a Module using self. to scope them (as you don't know the name of the actual class that they are going to a part of), then include that module as part of your class.
Related
I have a class User that inherits from ApplicationRecord. When I check User.ancestors.include?(Class), the result is false but for User.class, the result is Class.
What's the use case for the information supplied by the class method in such cases? What does it really tell me to be of practical use? It doesn't seem to have anything to do with inheritance.
What's the use case for the information supplied by the class method in such cases? What does it really tell me to be of practical use? It doesn't seem to have anything to do with inheritance.
class works the same way for every object. Calling class on a class provides the same information as calling class on an instance. That's because in Ruby, classes are instances, too.
'foo'.class returns String because 'foo' is an instance of String. Likewise, User.class returns Class because User is an instance of Class.
In particular, User is not an instance of ApplicationRecord.
It might not be obvious that User is an instance of Class when creating it via the class keyword:
class User < ApplicationRecord; end
But it becomes very obvious when you create it explicitly via Class.new: (both examples produce the same result)
User = Class.new(ApplicationRecord)
User.class #=> Class
Because the above is just like: (using String.new for demonstration purposes)
foo = String.new('foo')
foo.class #=> String
User is a class. All classes are instances of the class Class (i.e., User.class #=> Class). Therefore, all instance methods defined on Class, or on Class's ancestors, can be invoked on User (i.e., every instance method of Class is a class method of User).
For example, one of Class's instance methods is instance_methods. One can therefore execute User.instance_methods to obtain an array of User's instance methods.
After reading another StackOverflow post, I realized that it can only be understood by keeping the following in mind:
User class has a double role. It plays Class as well as Object.
As Class, it lets other classes inherit from it.
As Object, it inherits the class method all the way up from the Object class.
If I wanted my User.class to behave on the traditional lines (that is, User.class shows ApplicationRecord), then I have to override
it as follows:
class User
def self.class
self.superclass
end
end
Classes in Ruby are first-class objects—each is an instance of class
Class
User is an object of class Class (which inherits from Object).
class is an instance method of Object. It returns the class of the object. Therefore, User.class returns Class. Every object will have this method, and it is useful to know what the object is (e.g., for debugging). Consider this code:
1.class
# => Fixnum
"q".class
# => String
The reason why User.ancestors does not return Class is because it returns the modules included in the module. For example, Class.ancestors will include Class, but not User because User is an object of Class, which includes its own modules.
I am watching Code School Rails testing course. There is an instance of the class zombie. The zombie model has a method:
def avatar_url
...
end
Within the test, .rb file has the following:
z.avatar_url
When I call a method like this, how does Rails distinguish if I'm calling a controller or model method? I hadn't thought of calling a model method from other than a controller, and only like Model.method and not object.method.
If both my controller and my model have a method with the same name, how would Rails know which one to call?
Update:
Lets take the class String as example, it is not a model, right?
So I could say:
s = String.new
s.capitalize
If this call doesn't go to a model and not to a controller, where does it go then? Where would a class like String be defined in the Rails directory?
A method inside a controller can only be called via URL.
Example:
/things/super_action
Should call the def super_action inside ThingsController.
As for Model methods, they can be accessed anywhere. Just note if they are instance or class methods:
Model.ultra_method
This is a class method call, it is probably defined as def self.ultra_method.
m = Model.new
m.instance_method
This is a instance method call, and it is probably defined as def ultra_method.
UPDATE
String is a core class of ruby language. As is Array, Number, etc. In your example you are creating an instance of String and calling an instance method of the String class.
It seems like you're new to Ruby as well as Rails. In Ruby, a class is sort of like a description of a type of object (although the class itself is an object, too). Whenever there is a class defined, you can create new instances of it, as with String.new. Note that classes always have capitalized names.
Class methods are methods that work on the class itself. You can tell when a method is a class method because it will be attached to the capitalized name of the class (just like String.new). On the other hand, instance methods only work on an instance of the class, not on the class itself (eg str = String.new; str.capitalize!). Usually there are more instance methods than class methods, because instances are the things that you're actually working with (new is the most common class method you'll see).
As others have mentioned here, String is not a Rails model; it's a basic Ruby class. When you're working in Rails, you have access to all the regular Ruby classes as well as other classes and methods that are defined within Rails' source code. So String is not defined in Rails itself, but Rails does provide some useful instance methods for strings (eg str.to_date).
A model in Rails is really just a Ruby class. To understand the workings of a model, you should make sure you understand how Ruby classes work. What makes Rails models special is that they inherit from a class defined in Rails' source code known as ActiveRecord (any class in Ruby can inherit from another class, this is just one example of that). ActiveRecord has a number of class and instance methods, which are also available to your models because they inherit from ActiveRecord. For example, if you have a class (model) called Person, you can automatically use the Person.find(id) class method to look up a particular instance of the Person class in the database. You also have the person.save instance method to save the instance to the database.
All of this was confusing to me when I first started, so my best advice is to familiarize yourself with Ruby as you learn Rails.
You can call Model class/instance methods from anywhere in Rails. Model are just the mapping to your database and acts as a proxy for your database. When you are calling
z.avatar_url
you are calling a method on "z" model instance. So not matter from where you call it will always call the model's method.
If you define a method with same name in both controller and model, you would always be calling a model's method with model instance or model class. Controller methods are simply action in Rails they are never referred directly from anywhere. They are used for Rails routing.
Hope I am clear.
This is a fairly basic Ruby/Rails question but I'm not sure why it makes a difference if you call a class of an object in some circumstances vs calling an instance of that object in different places of the framework.
Say you have a model e.g. Product and you call Product.new you have a new instance of the class. But if you have certain methods that are defined in the model I only seem to be able to access these if I call the Class rather than an instance e.g. Product.where(param, param). But I cannot call product.where(param, param) - why is this?
There are two types of methods: Class methods, and instance methods. You must call the appropriate method on the right object.
class Product
def self.foo
# class method, only callable on Product
end
def name
# instance method, callable on an instance of Product.
end
end
If you attempt to call an instance method on a class, or vice versa, you'll see an undefined method error.
To use someone else's analogy, imagine a house and a blue print; the class is a blue print for an object, while a house would represent the instance. An instance of that class will have its own set of attributes (wall colour, window type, etc...).
What would this mean?
p = Product.find(1)
p.where('something == 2')
That doesn't make any sense, you have an instance, what are you querying for? Good API design results in methods defined where they make sense.
Recently I had to add a method to Redmine's core class. I was unable to use inheritance, so I've done something like this:
require_dependency 'time_entry_query'
class TimeEntryQuery < Query
def my_new_method(foo, bar)
end
end
and it works perfectly - my method is added to all new objects. However, I've seen someone declaring the new method in their own module instead and then sending :include to class, so it become a mixin. Here's an example:
module Patches
module SomeClassPatch
def my_new_method
end
end
and somewhere in app's initialization:
SomeClass.send(:include, Patches::SomeClassPatch) unless SomeClass.include? (Patches::SomeClassPatch)
What's difference between these two methods and which one should I use?
There are two differences:
When you use a mixin, there is a clear place where your "patch" methods can live. If I wonder "Hmm, where's this my_new_method" coming from, and I look at, say, TimeEntryQuery.ancestors or TimeEntryQuery.instance_method(:my_new_method).owner, that will return Patches::SomeClassPatch. So I know I have to look for a file named lib/patches/some_class_patch.rb somewhere to find where it is probably defined. (I could try source_location as well, but that is not always reliable.)
Mixing in a module into a class makes the module the superclass of the class it is being mixed into. So, if there already is a my_new_method defined in TimeEntryQuery, your first option will overwrite it, whereas in your second option, your method will become the super method of that method. IOW: with your second option, your new method won't be called unless the already existing method calls super.
Preface: This is in the context of a Rails application. The question, however, is specific to Ruby.
Let's say I have a Media object.
class Media < ActiveRecord::Base
end
I've extended it in a few subclasses:
class Image < Media
def show
# logic
end
end
class Video < Media
def show
# logic
end
end
From within the Media class, I want to call the implementation of show from the proper subclass. So, from Media, if self is a Video, then it would call Video's show method. If self is instead an Image, it would call Image's show method.
Coming from a Java background, the first thing that popped into my head was 'create an abstract method in the superclass'. However, I've read in several places (including Stack Overflow) that abstract methods aren't the best way to deal with this in Ruby.
With that in mind, I started researching typecasting and discovered that this is also a relic of Java thinking that I need to banish from my mind when dealing with Ruby.
Defeated, I started coding something that looked like this:
def superclass_method
# logic
this_media = self.type.constantize.find(self.id)
this_media.show
end
I've been coding in Ruby/Rails for a while now, but since this was my first time trying out this behavior and existing resources didn't answer my question directly, I wanted to get feedback from more-seasoned developers on how to accomplish my task.
So, how can I call a subclass's implementation of a method from the superclass in Rails? Is there a better way than what I ended up (almost) implementing?
Good question, but you are making it too complicated. Keep in mind a few principles and it should all be clear...
The types will be resolved dynamically, so if a show exists anywhere in the object's class hierarchy at the moment it is actually called then Ruby will find it and call it. You are welcome to type in method calls to anything that may or may not exist in the future and it's legal ruby syntax and it will parse. You can type in an expression that includes a reference to this_will_never_be_implemented and no one will care unless it actually gets called.
Even in Java, there is only one actual object. Yes, you may have a method in the superclass that's calling a method, but it is an instance of the derived class (as well as an instance of the base class) and so you can count on the new show being called.
In a sense, every Ruby class is an abstract class containing stubs for every possible method that might be defined in the future. You can call anything without access qualifiers in the base class or derived class.
If you want a null superclass implementation, you may want to define one that does nothing or raises an exception.
Update: Possibly, I should have just said "call show like any other method" and left it at that, but having come this far I want to add: You can also implement show with Ruby's version of multiple inheritance: include SomeModule. Since you are obviously interested in Ruby's object model, you might implement your attribute with a mixin just for fun.
As you know having a superclass know about subclass functionality is a big no-no, which is why you wanted the abstract method.
What you want to do is define show in your superclass. Then you can call it in the superclass and the subclass will call its own version but the superclass won't throw an error.
class Media < ActiveRecord::Base
def show
# This method should be overloaded in a subclass
puts "Called from Media"
end
def do_something
show
end
end
class Image < Media
def show
puts "Called from Image"
end
end
class Video < Media
def show
puts "Called from Video"
end
end
i = Image.new
i.do_something
=> Called from Image
v = Video.new
v.do_something
=> Called from Video
Simple answer. Just call it. Ruby does not have compile-time checking so there is no one to complain that show isn't defined on Media. If #example is an instance of Image, then any call to #example.show will be sent to Image#show first, wherever it is made. Only if Image#show doesn't exist then the call will be passed on to Media, even if the call originated from code defined in Media
If you want to call show on self from within a method of Media, simply do it. However, make sure self responds to the method call.
class Media < ActiveRecord::Base
def foo
if self.respond_to?(:show)
self.show
else
... // *
end
end
...
end
To avoid the branch, implement show on Media, using the * as the body of show
class Media < ActiveRecord::Base
def foo
self.show
end
def show
...
end
end