In Ruby How to Use Class Level Local Variable? (A Ruby Newbie's Question)

Local variables for a class in ruby

The body of a class in Ruby is just executable Ruby code. These are indeed local variables (no quotation needed) and follow the "regular" rules being local variables. You can access them in the body of the class. If you literally want the scope where bar is defined, you can use Kernel.binding:

class Foo
bar = 42

@@scope = binding

def self.scope
@@scope
end
end

Foo.scope.local_variables # => [:bar]
Foo.scope.local_variable_get(:bar) # => 42

A thing to note - using def changes the scope, therefore, they won't be visible inside methods defined using def.

What are the Ruby Gotchas a newbie should be warned about?

Wikipedia Ruby gotchas

From the article:

  • Names which begin with a capital letter are treated as constants, so local variables should begin with a lowercase letter.
  • The characters $ and @ do not indicate variable data type as in Perl, but rather function as scope resolution operators.
  • To denote floating point numbers, one must follow with a zero digit (99.0) or an explicit conversion (99.to_f). It is insufficient to append a dot (99.), because numbers are susceptible to method syntax.
  • Boolean evaluation of non-boolean data is strict: 0, "" and [] are all evaluated to true. In C, the expression 0 ? 1 : 0 evaluates to 0 (i.e. false). In Ruby, however, it yields 1, as all numbers evaluate to true; only nil and false evaluate to false. A corollary to this rule is that Ruby methods by convention — for example, regular-expression searches — return numbers, strings, lists, or other non-false values on success, but nil on failure (e.g., mismatch). This convention is also used in Smalltalk, where only the special objects true and false can be used in a boolean expression.
  • Versions prior to 1.9 lack a character data type (compare to C, which provides type char for characters). This may cause surprises when slicing strings: "abc"[0] yields 97 (an integer, representing the ASCII code of the first character in the string); to obtain "a" use "abc"[0,1] (a substring of length 1) or "abc"[0].chr.
  • The notation statement until expression, unlike other languages' equivalent statements (e.g. do { statement } while (not(expression)); in C/C++/...), actually never runs the statement if the expression is already true. This is because statement until expression is actually syntactic sugar over

    until expression
    statement
    end

    , the equivalent of which in C/C++ is while (not(expression)) statement; just like statement if expression is an equivalent to

    if expression
    statement
    end

    However, the notation

    begin
    statement
    end until expression

    in Ruby will in fact run the statement once even if the expression is already true.

  • Because constants are references to objects, changing what a constant refers to generates a warning, but modifying the object itself does not. For example, Greeting << " world!" if Greeting == "Hello" does not generate an error or warning. This is similar to final variables in Java, but Ruby does also have the functionality to "freeze" an object, unlike Java.

Some features which differ notably from other languages:

  • The usual operators for conditional expressions, and and or, do not follow the normal rules of precedence: and does not bind tighter than or. Ruby also has expression operators || and && which work as expected.

  • def inside def doesn't do what a Python programmer might expect:

    def a_method
    x = 7
    def print_x; puts x end
    print_x
    end

    This gives an error about x not being defined. You need to use a Proc.

Language features

  • Omission of parentheses around method arguments may lead to unexpected results if the methods take multiple parameters. The Ruby developers have stated that omission of parentheses on multi-parameter methods may be disallowed in future Ruby versions; the current (November 2007) Ruby interpreter throws a warning which encourages the writer not to omit (), to avoid ambiguous meaning of code. Not using () is still common practice, and can be especially nice to use Ruby as a human readable domain-specific programming language itself, along with the method called method_missing().

Within a Ruby method, should I create a proc or a method?

It is not clear what your specific use-case is, but I would definitely go for procs or lambdas. There is less overhead when defining a proc or lambda dynamically, they are passable, so if needed you could return them and they could be used outside the function.

Using "def" exposes the method as an instance method outside of the current method scope (so in the containing class, which could be Object in your case). This may or may not be with you want. If you want to use an anonymous function only available in the local scope, use a lambda.

Also Proc vs Lambda: I generally prefer to use lambdas since they behave a little more "predictable", meaning: as you would expect (check passed variables, and return just returns from the lambda, proc returns from the called scope). But from your example it is hard to deduce what would apply. I think the key-difference is: lambas are ment to be passed around, and thus behave a little more sanely. If this is not your use-case, use Proc :) (a write-up of the difference).

undefined local variable or method `root_path' (Rspec Spork Guard)

You just have to visit root_path inside a before block: before { visit root_path }

Undefined local variable or method `current_user' for Observer?

You're assigning Devise's current_user to the User.current class accessor precisely because it is not available on the model (and observer) level. So instead of trying to use current_user there, use the class accessor you just created:

Engineer.create({ :user_id => User.current.id, :army_id => :army_id })

EDIT:

BTW, I don't think this is thread safe - unless you also implement the first part of this solution linked in one of the answers to the post you referenced.

Why is `yield self` helpful?

The second example works because of the specific circumstances where you have a local variable referring to the the Dog. As an alternative consider what happens if using an anonymous instance:

Dog.new.bark do
puts "Bark, bark, bark!"
# what goes here?? puts ????.breed
end

or maybe you want to declare your block somewhere else and pass it in e.g.

loud = lambda { |dog| puts "A #{dog.breed} doing some LOUD YAPPING" }

puts d.bark(&loud)

so essentially setting up with yield self gives flexibility in how your code can be used.



Related Topics



Leave a reply



Submit