views:

92

answers:

3

I'm new to Ruby and working through some tutorials/screencasts. I've reached the section where they're discusisng the before_filter callback, and it's using some syntax that's a little weird for me. I don't know if it's a feature of ruby, of if it's some rails magic, and was hoping someone here could set me straight or point me in the right direction w/r/t the manual

This is a code fragment from the screencast I'm watching

class MachinesController < ApplicationController
    #...
    before_filter :login_required, :only => [:report]
    #...    
    def index
        #etc...
    end

    def login_required
        #etc...
    end
end

In the context of rails, I understand that before_filter is a callback that will fire login_required method when the report action is called. However, it's not clear to me what it is within the context of ruby. In other languages classes typically contain methods, properties, class variables and constants defined within the braces.

However, this looks like its a function call inside the class, and some experiments have show that you can put code in your class definitions and have it called when the program runs. Is this correct? If so, are there special contextual rules for code that's put inline into a class like that? (i.e. would the before_filter function in rails know what class it was called from) If not, what magic is rails doing here?

A: 

Ruby is so cool. You can definitely send messages from within a class block. As I understand it, what things like class do, other than the obvious, is control the identity of self. Thus, you should be able to call any method of the class or included modules from there.

Jonathan Sterling
Ah, I hadn't encountered the self keyword yet or learned how to define static/class methods. This makes a lot more sense now (but is still hugely weird)
Alan Storm
You cannot invoke a method defined on an 'included module' from within a class definition. In order to invoke a method provided by a module within the class definition the module must be `extend`ed not `include`d in the class.
banister
A: 

I'm not sure exactly what your question is, but here's my interpretation:

before_filter

This is a class method call see ActionController

:login_required

This is a parameter to the before_filter method, and used as a callback using Object#send

:only => [:report]

This is an additional Hash parameter to before_filter see ActionController again.

I'd also suggest looking at the implementation of the before_filter method for insight.

hobodave
My question/confusion is centered around the fact that you're running code other than assignments or method definitions from within a class block. You can't do that in languages like Java or PHP and I'm looking for an explanation/tutorial on how code running from withing a class (not a class method) differs from code running outside a class definition.
Alan Storm
Ahhh, I see now. :)
hobodave
Heh, I just realized that's something I've always taken for granted and never questioned. Now, I'm curious and trying to find the explanation myself. You should update your question to make it a little clearer what you need, your comment was clear - the question not.
hobodave
@Alan: It doesn't. That's the beauty of it. Class bodies are just executable code, just like method bodies or script bodies. They even return a value (the return value of the last expression evaluated inside the class body, again, just like method bodies). IOW: a class "definition" isn't a class *definition*, it's a *script*, which *creates* a class definition. This allows such beautiful things as `if OS == :win then def foo; this_way end else def foo; another_way end end`.
Jörg W Mittag
Makes perfect sense Jörg, its just a different way of thinking about your code when you're coming from a more boring/traditional space.
Alan Storm
+3  A: 

before_filter is a not actually a callback. It's a class method of ActiveRecord::Base that sets up a callback when you call it. So in this example:

before_filter :login_required, :only => [:report]

When the class is loaded, the method is called, and it adds :login_required to the filter chain for the report method.

The convention for these types of calls is to drop parens, but it would work just fine (and would be more easily identifiable as a method call) if you did this:

before_filter(:login_required, :only => [:report])
Sarah Mei
+1 best answer so far
banister