This is a hard one to search for, but I've got a line of Ruby in a class method where a variable is first declared by assigning it a bare new keyword and I'm not exactly sure what's going on here:
def self.html_render_controller(post)
controller = new
controller.action = 'show'
controller.post = post
controller.render_to_string
end
Coming from other languages, it looks to me like controller is implicitly being initialized as an object of some kind. And render_to_string is a Rails built-in, so my best guess is controller is initialized as some kind of Rails object?
I guess my question is really what is the value of controller after that first assignment? Or what kind of execution rules or even just documentation in Ruby would point me in the right direction for understanding this kind of shorthand?
It is not so weird as ruby code. It Is quite straight forward.
new is not a keyword is a method called on the implicit self.
You do not need parenthesis to call a method in Ruby.
If you call a method without an explicit receiver in Ruby, the receiver is going to be self, inside the body of the method self.html_render_controller, self references the class where the method is defined.
So executing new inside the method self.html_render_controller just return a new object of the class where the method is defined.
You can have more details on new from the documentation on Class, that is the class of any class in Ruby, being a class just an object of the class Class.
I'm not exactly sure what's going on here
It's the new method of your class, i.e. it returns a new instance of the class your html_render_controller method is defined in.
Since you come from other languages, it might be easier to think of controller = new() (which in fact works just fine in Ruby, but it's more idiomatic to omit empty parentheses unless needed)
what kind of execution rules or even just documentation in Ruby would point me in the right direction for understanding this kind of shorthand?
This is Ruby's default method calling mechanism when omitting a receiver: "self is the default receiver. If you don't specify any receiver self will be used."
In instance methods, self refers to the current instance. In class methods, self refers to the class the method is defined in.
These three code snippets are all equivalent: *
class MyController
def self.foo
controller = new
end
end
class MyController
def self.foo
controller = self.new
end
end
class MyController
def self.foo
controller = MyController.new
end
end
You could also replace the self after def with the class name:
class MyController
def MyController.foo
controller = MyController.new
end
end
And using the latter, most explicit form, the method could even be defined outside the class body:
class MyController
end
def MyController.foo
controller = MyController.new
end
* Note that the above examples are not identical. For example, with sub-classes, self.new would dynamically create an instance of the respective (sub-)class whereas MyController.new would always create an instance of MyController. And you usually want to define your methods within the class body to ensure proper constant lookup.
new is instantiating an object of the class where this html_render_controller method is.
You can change it to self.new instead of new, then it's clearer where this method is coming from. Since html_render_controller is a class method, self will be the class where the method is.
class Foobar
def self.foo
foobar = new
# `foobar` is a new instance of Foobar
end
end
Related
I'm completely new to ruby / ruby on rails, i'm just giving a sort of extreme assistance on an old existing project, so, forgive myself if this is a silly question ;)
I have this class:
class MyClass
def call
category_id = context.params['category_id']
tmp_context = context.clone
... stuff...
content_id
end
end
Called this way:
MyClass.call(params: params, current_user: current_user)
All i need to get is the return value of the function, the "content_id".
I've tried in several ways and all i can get from this function it's a reference to the context:
#<Interactor::Context params={...}, current_user=#<User ...>>
But no hint about the return value.
Any suggestion?
The call class method is the proper way to invoke an interactor. The
hash argument is converted to the interactor instance's context. The
call instance method is invoked along with any hooks that the
interactor might define. Finally, the context (along with any changes
made to it) is returned.
https://github.com/collectiveidea/interactor
So the proper way to assign a "return value" would be:
class MyClass
include Interactor
def call
category_id = context.params['category_id']
tmp_context = context.clone
# ...
context.content_id = content_id
end
end
MyClass.call.content_id # => some value
So to break this down the Interactor module (provided by the interactor gem). Declares a class method named call which creates an instance of MyClass with the "context" as an argument. It then calls the call instance method on the instance and returns the context.
You can get a better idea of how this works by looking at the source code.
This method:
def format_stations_and_date
from_station.titelize! if from_station.respond_to?(:titleize!)
to_station.titleize! if to_station.respond_to?(:titleize!)
if date.respond_to?(:to_date)
date = date.to_date
end
end
Fails with this error when date is nil:
NoMethodError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.to_date):
app/models/schedule.rb:87:in `format_stations_and_date'
app/controllers/schedules_controller.rb:15:in `show'
However, if I change date = date.to_date to self.date = self.date.to_date, the method works correctly.
What's going on? In general, when do I have to write self?
Edit: It's not related to the question, but please note that there is no "titleize!" method.
Whenever you want to invoke a setter method on self, you have to write self.foo = bar. If you just write foo = bar, the ruby parser recognizes that as a variable assignment and thinks of foo as a local variable from now on. For the parser to realize, that you want to invoke a setter method, and not assign a local variable, you have to write obj.foo = bar, so if the object is self, self.foo = bar
You disambiguiate between the instance method name and a local variable using self (it is allowed to have both with the same name in the same scope). In other words, there will be a method name resolution only if there is no local or block variable of the same name in scope. Behold:
class Foo
attr_accessor :boo
def do_boo
boo = 123
puts "Locvar: #{boo} Method: #{self.boo}"
end
end
Foo.new.do_boo
Here's why: imagine you have a module which implements a method. This method assigns something to it's internal local variable
"foo" which is used for some computation. If you skip the "self" part, the method will make a "foo=" method call on the object
whose class includes the module, which was not the intention of the author and can be downright disastrous.
class Foo
def bar=(new_value_of_bar)
set_off_nukes(new_value_of_bar / 3)
end
end
module InnocentModule # written by a different author elsewhere
def do_useful_stuff
...
bar = Math.sin(something) # we're dead
end
end
Foo.send(:include, InnocentModule)
Another crucial part where you have to use self is when invoking the Object#class method, because simply saying "class" means a class keyword for Ruby.
Note: This was the best title I could think of that wouldn't make this question seem like a dup. Please feel free to edit if it does not capture the point of the question
So I've been reading Advanced Rails and I had a question that wasn't fully answered by these posts:
When to use `self.foo` instead of `foo` in Ruby methods
In Ruby. How do I refer to the class from within class << self definition?
Why use "self" to access ActiveRecord/Rails model properties?
In my understanding, within a class definition, self refers to the object referenced by the pointer klass, so for the example of an eigenclass:
class A
class << self
def to.s
"Woot"
end
end
end
is the exact same as:
class << A
def to.s
"Woot"
end
end
This is because as you open up the class, ruby is creating a Class:A (virtual) class and assigning A's klass pointer to it. To my understanding this means that within the context of this class definition self == A
My question is (assuming my understanding as I've described it above is accurate) why then in an ActiveRecord::Base subclass in Rails does the use of self seem to be merely a disambiguation of the instance and not the class itself?
For example, if I have a class: A < ActiveRecord::Base with an attribute name, I cannot call A.name = "blah" outside the class definition. However, I CAN use ( and indeed MUST use ) self in assignment within the class definition like so: self.name = "blah".
If the use of self in Ruby refers to the Class object and not the instance, why does this work in ActiveRecord and how does Rails make the distinction?
Everything is a method
Anything you do with an object is a method. When you have a class like this:
class A
attr_accessor :foo
end
obj = A.new
obj.foo = 3
obj.foo #=> 3
It might feel weird, but you didn't make any assignment here. obj.foo = 3 is only a synthatic sugar for obj.send(:foo=, 3) so in fact, it executes foo= method. obj.foo is a method as well, it simply returns a value. This is because all the instance variables are private, and there is no other way to interact with them than inside the method. attr_accessor :foo is just a shortcut for:
def foo=(value)
#foo = value
end
def foo
#foo
end
Now, when ruby see any identifier like foo, it needs to find out what it is. Firstly it assumes it is a local variable, and if not, it tries to execute it as method on current self (see below). This means that:
class A
attr_accessor :foo
def bar
foo = 'bar'
end
end
a = A.new
a.bar #=> 'bar'
a.foo #=> nil
What happened? Interpretor first uses foo as an instance variable, it is not defined. But there is an assignment next to it, so it defines a new instance variable and make an assignment. If we want to use our setter we need to tell interpreter that it is not an instance varible:
class A
attr_accessor :foo
def bar
self.foo = 'bar'
end
end
a = A.new
a.bar #=> 'bar'
a.foo #=> 'bar'
I lied. Everything is an object
about self
self in ruby is very similar to this in javascript - it means different things in different context, and it can be easily changed at any point (obviously, not recommended).
First thing you need to know is that in Ruby every class is an object, namely it is an instance of class Class. When you do
class A
it is (almost, method below uses block and has access to external scope) equivalent to:
A = Class.new
self within a context of a class is always a class itself.
class A
self #=> A
def self.bar #=> this is class method
self #=> A
end
def foo #=> this is instance method
# We are no longer in context of class, but in context of the instance, hence
self #=> instance of A
end
end
Now, for any mutable object you can defined a singleton class. This is sort of weird concept - it is an extra subclass of the original class which only has a single instance. Since all the methods come from the class, this allows you to define extra methods on a particular instance of the method without affecting other object. In most of the cases, instance class is not needed and it is created when you access it for the first time. There are couple of ways to open it:
object.singleton_class do
self #=> instance class
end
class << object
self #=> instance class
end
Any method you defined within instance class is accessible only on that particular instance, hence this:
class A
self #=> A
class << A
# we are now in the instance class of A (which is an instance of Class class)
def foo
# So this is an instance method, however our instance is a class A
self #=> A
end
end
I'm just starting to learn rails. I have a test project and I added a instance method and a class method to a Post model class with some existing sample data.
class Post < ActiveRecord::Base
has_many :comments
attr_accessor :region
def sport
puts "Football"
end
def self.region
puts "West"
end
end
I correctly get "Football" when I run Post.first.sport
But I get nil when I run Post.first.region. Why doesn't rails console return "West"?
Thanks!
Since self.region is a defined as a Class method you should run Post.region to output "West"
See https://stackoverflow.com/a/11655868/1693764 for a good description of Class vs instance methods
It fails because you are using a class method on an instance object.
Do:
Post.region #=> 'west'
When you add 'self.' to a method it becomes a class method. Class method are invoked on the entire class. Instance methods on the other hand are invoked on a instance of the class.
Use class methods when you want methods that are applicable for the entire class. For example a method like find_post_with_most_comments.
Post.find_post_with_most_comments
Use instance method when you are dealing with a particular instance of the class. For example a method like first_comment
#post = Post.find(params[:post_id])
#post.first_comment
attr_accessor adds instance methods to the class.
As per your code, it creates an instance variable called "#region" during initialization of an object of Post, in the getter method.
def region
#region
end
and default value of instance variable is 'nil'. So when you access Post.first.region, it returns the default value of the instance variable.
In 'self.region' code, its defined as class method. So it can be called using the syntax 'Model.class_method'
Thus class methods are called on class objects while instance methods are called on instance objects.
Get a deeper understanding of ruby metaclasses for a complete picture, it will make you more curious about the ruby architecture, and will help you learn more.
I have a rails model class
class Model < ActiveRecord::Base
has_many :object_collection
def add_object(object)
object_collection.push object // works
#object_collection.push object // does not work
self.object_collection.push object // works
end
end
I was wondering if someone can please explain to me why the # does not work yet self does i thought these two meant the same
cheers
They are not the same. Consider the following Ruby code:
class Person
attr_accessor :employer
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"
The method attr_accessor conveniently generates an attribute reader and writer for you (employer= and employer). You can use these methods to read and write an attribute, which is stored in the instance variable #employer.
Now, we can rewrite the above to the following, which is functionally identical to the code above:
class Person
def employer=(new_employer)
#works_for = new_employer
end
def employer
#works_for
end
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"
Now, the instance variable #employer is no longer used. We chose to write the accessors manually, and have the freedom to pick a different name for the instance variable. In this particular example, the name of the instance variable is different than the name of the attribute accessors. There is nothing that prevents you from doing that.
This is similar to how ActiveRecord stores its attributes internally. They are not stored in instance variables of the same name, that is why your push call to #object_collection does not work.
As you may understand, attribute readers and writers offer a certain abstraction that can hide the implementation details from you. Reading and writing instance variables directly in subclasses is therefore generally considered bad practice.
#foo identifies an instance variable called #foo. foo identified a method called foo.
By default, instance variables in Ruby are private. It means you cannot access the value of an instance variable unless you have some public method that exposes the value.
Those methods are called setters and getters. By convenction, setter and getter have the same name of the instance variable, but this is not a requirement.
class MyClass
def initialize
#foo
end
def foo=(value)
#foo = foo
end
def foo
#foo
end
def an_other_foo=(value)
#foo = foo
end
def an_other_foo
#foo
end
end
Though methods and instance variables can have similar names, thery are different elements.
If this topic is not clear to you, you probably need to stop playing with Rails and go back studying how Ruby works.
In your specific case, object_collection doesn't exist as an instance variable because it's an association method.
They do not mean the same thing. One is an instance variable, the other is a method.
The #foo means "the value of the instance variable foo", where as self.foo means "the value of a call to the method foo on myself".
It is typical for a method foo= to set the #foo instance variable, so I can see how someone new to the language might be confused. I'd encourage you to pick up a book on the ruby language. There's one specifically for people who have done some rails but never learned ruby proper. You often can hack rails without understanding the language or what these statements mean, but you'll be far less productive than someone who spends the small amount of time it takes to learn the ruby language itself.
As a general rule, use the self.foo form whenever you can, as this is less sensitive to changes in the classes definition.