I'm really new to Ruby. And by new - less than 16 hours, but my boss gave me some Ruby code to add to. However, I found it was one giant file and not modular at all, so I decided to clean it up. Now that I've broken it up into several files/classes (generally speaking, 1 class per file,) I'm having problems piecing it together for it to work again. Originally everything was part of the same class, so the calls worked, but it looked ugly and it took an entire work day just to figure it out. I want to avoid that for the future as this code will grow much larger before it is done.
My main issue looks like the following (simplified, obviously):
class TestDevice
def initialize
#loghash = { }
....
end
end
class Log
def self.msg(identifier, level, section, message)
...
#loghash[identifier] = { level => { section => message }}
...
end
end
device = TestDevice.new
After that, it calls out to other class methods, and those class methods reference back to the class Log for their logging needs. Of course, Log needs to access "device.loghash" somehow to log the information in that hash. But I can't figure out how to make that happen outside of passing the contents of "loghash" to every method, so that they, in turn, can pass it, and then return the value back to the origination point and then logging it at the end, but that seems really clumsy and awkward.
I'm hoping I am really just missing something.
To create accessors for instance variables the simple way, use attr_accessor.
class TestDevice
attr_accessor :loghash
def initialize
#loghash = { }
....
end
end
You can also manually define an accessor.
class TestDevice
def loghash
#loghash
end
def loghash=(val)
#loghash = val
end
end
This is effectively what attr_accessor does behind the scenes.
how about passing the device object as a parameter to the msg function? (I'm assuming that there can be many devices in your program, otherwise you can use singleton pattern).
class TestDevice
attr_accessor :loghash
def initialize
#loghash = { }
....
end
end
class Log
def self.msg(device, identifier, level, section, message)
...
device.loghash[identifier] = { level => { section => message }}
...
end
end
So you need to learn the rules of ruby scoping.
Ruby variables have different scope, depending on their prefix:
$global_variables start with a $, and are available to everyone.
#instance_variables start with a single #, and are stored with the current value of self. If two
scopes share the same value of self (they're both instance methods, for example),
then both share the same instance variables
##class_variable start with ##, and are stored with the class. They're
shared between all instances of a class - and all instances of subclasses
of that class.
Constants start with a capital letter, and may be all caps. Like class
variables, they're stored with the current self.class, but they also
trickle up the hierarchy - so if you have a class defined in a module,
the instances of the class can access the module's constants as well.
Constants defined outside of a class have global scope.
Note that a constant variable means that which object is bound to the constant
won't change, not that the object itself won't change internal state.
local_variables start with a lowercase letter
You can read more about scope here.
Local variables scoping rules are mainly standard - they're available in
all subscopes of the one in which they are defined except when we move into
a module, class, or method definition. So if we look at your code from your
answer
class TestDevice
attr_accessor :loghash
def initialize
#loghash = { }
end
end
device = TestDevice.new
class Somethingelse
def self.something
device.loghash='something here' # doesn't work
end
end
The scope of the device local variable defined at the toplevel does not include the Somethingelse.something
method definition. So the device local variable used in the Somethingelse.something method definition is a different (empty) variable. If you want the scoping to work that way, you should use a constant or a global variable.
class TestDevice
attr_accessor :loghash
def initialize
#loghash = { }
end
end
DEVICE = TestDevice.new
$has_logged = false
class Somethingelse
def self.something
DEVICE.loghash='something here'
$has_logged = true
end
end
p DEVICE.loghash # prints `{}`
p $has_logged # prints `false`
Somethingelse.something
p DEVICE.loghash # prints `"something here"`
p $has_logged # prints `true`
Related
I have been taught to declare my instance variables with def initialize. I have been under the impression that I could declare instance variables only within my initialize methods.
Nevertheless, I declared an instance variable #foo outside my initialize method, and made it work as I intended:
class FooBar
def initialize(bar)
#bar = bar
end
def foo_as_instance_var
#foo = #bar.split(' ')
#foo
end
end
x = "something wicked this way comes"
y = FooBar.new(x)
puts y.foo_as_instance_var
Why am I able to declare an instance variable outside of initialize method? Since I can declare instance variables in any method, is there a best practices rule I should follow, regarding where to declare instance variables (i.e., declare them within initialize) or does it not matter?
I have been taught to declare my instance variables with def initialize
Since initialize is the first instance method call in an object's life cycle, you typically declare your instance variables right there in order to ensure properly initialized variables. It's also the first place I'd expect instance variables to be defined when reading code.
I have been under the impression that I could declare instance variables only within my initialize methods.
There's no such restriction. You can declare instance variable anywhere within your instance.
A common use is memoization:
class FooBar
def foo
#foo ||= expensive_operation
end
end
On the first call, this would evaluate expensive_operation and assign the result to #foo. On subsequent calls, #foo is returned.
Another popular example is Rails which uses instance variables to pass data from the controller to its view:
class FooController < ApplicationController
def index
#foos = Foo.all
end
end
is there a best practices rule I should follow, regarding where to declare instance variables
It depends on their purpose (see above examples). As a general rule, declare them in a way that avoids undefined variables (nil errors) and structure your code so it is easy to read / follow.
Just to add to Stefan's excellent answer
I have been taught to declare my instance variables with def initialize
A common mistake that ruby newbies make is something like this:
class Person
#name = "John"
def introduce
puts "Hi, my name is #{#name}"
end
end
And then they wonder why their names are not printed. To make this work, one can set the variable #name in the initializer, just as the instruction says.
Lets start with the biggest misnomer - in Ruby there is no separate step of declaring variables - Variables are declared as you set them.
What the difference? Look at Java for example:
public class Bicycle {
private int cadence;
private int gear;
private int speed;
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
}
We have to declare all the instance variables before we set them in the initializer (Bicycle). The same code in Ruby reads:
class Bicycle
def initialize(cadence, speed, gear)
#cadence = cadence
#speed = speed
#gear = gear
end
end
There is no declaration - only assignment. Ruby will even let you access instance variables which have not been set without error.
irb(main):003:0> #not_set
=> nil
You can't do that (generally) in languages where variables must be defined*.
I have been taught to declare my instance variables with def
initialize. I have been under the impression that I could declare
instance variables only within my initialize methods.
Nonsense. You can assign instance variables anywhere. Its commonly done in everything from setters and mutators (methods that alter an object) to factory methods (class methods that return an instance) or anywhere that you are altering the state of an object.
class Book
def initialize(title, author)
#title = title
self.author = author # calls the setter.
end
# A factory method
def create_from_csv(filename)
# ...
end
# A very contrived setter
def author=(author)
#author = "#{author.forename.upcase}. #{author.surname}"
end
# a mutator
def out_of_print!
#out_of_print = true
#last_printed = Date.today
end
end
However the initialize method is where you should handle initializing your objects (duuh) and is thus the obvious place to set initial values.
I'm near the finish of the Ruby track in Code Academy, and I'm curious about a peculiar thing: I was under the impression that a class is a repository of constants, methods, etc... and that in order to access most of them, you would first need to create an instance of that class or in some cases the methods of themselves can be invoked (as in they are all technically part of the global object). And then I saw something like this:
#Worked
Time.now
I understood as this as the method [now] of instance of class [Time] being invoked. I then tried to invoke the method on its own:
#Failed
now
and that failed, and I assumed that while a method can be created in the general scope [as part of the global object], if it relies on initialized variables of "parent" class, it cannot be called on its own, because it would not know which object to search for those initialized variables. Following that I created a test class:
class Clock
def initialize
#hours = 1
#minutes = 30
end
def showTime
puts "The time is: #{#hours}:#{#minutes}"
end
end
#this worked
watch = Clock.new
watch.showTime
#this failed
showTime
I then just created a basic method (assuming it's in the global level)
def mymethod
puts "The mighty METHOD!"
end
#Works
mymethod
and calling this method the way I did, without referencing the global object worked. So... the questions I have are as follows:
How can [Time.now] be called in this fashion? Shouldn't there be an instance of Time first created?
Why can't I call the method [now] on its own? Am I right that it relies on resources that it cannot find when called this way?
Why could I not call the method showTime on its own? But if I define any method on the "global" level I can access it without referencing the global object
First of all, your intuition is correct.
Every methods must be an instance method of some receiver.
Global methods are defined as private instance methods on Object class and hence seem to be globally available. Why? From any context Object is always in the class hierarchy of self and hence private methods on Object are always callable without receiver.
def fuuuuuuuuuuun
end
Object.private_methods.include?(:fuuuuuuuuuuun)
# => true
Class methods are defined as instance methods on the "singleton class" of their class instance. Every object in Ruby has two classes, a "singleton class" with instance methods just for that one single object and a "normal class" with method for all objects of that class. Classes are no different, they are objects of the Class class and may have singleton methods.
class A
class << self # the singleton class
def example
end
end
end
A.singleton_class.instance_methods.include?(:example)
# => true
Alternative ways of defining class methods are
class A
def self.example
end
end
# or
def A.example
end
Fun fact, you can define singleton methods on any object (not just on class objects) using the same syntax def (receiver).(method name) as follows
str = "hello"
def str.square_size
size * size
end
str.square_size
# => 25
"any other string".square_size
# => raises NoMethodError
Some programming language history — Singleton classes are taken from the Smalltalk language where they are called "metaclasses". Basically all object-oriented features in Ruby (as well as the functional-style enumerators on Enumerable) are taken from the Smalltalk language. Smalltalk was an early class-based object-oriented language created in the 70ies. It was also the language that invented graphical user interfaces like overlapping windows and menus et cetera. If you love Ruby maybe also take a look at Smalltalk, you might fall in love yet again.
This is known as a class method. If CodeAcademy didn't cover it, that's a shame. Here's some examples:
# basic way
class Foo
def self.bar; :ok; end
end
Foo.bar # => :ok
# alternate syntax
class Foo
class << self
def bar; :ok; end
end
end
# alternate syntax, if Foo class already exists
def Foo.bar; :ok; end
# alternate approach if Foo class already exists
Foo.class_exec do
def bar; :ok; end
end
# to define a class method on an anonymous 'class' for a single instance
# you won't need to use this often
Foo.new.singleton_class.class_exec do
def bar; :ok; end
end
# to define a class method on an instance's actual class
Foo.new.class.class_exec do
def bar; :ok; end
end
Another way to get class methods is to extend a module.
module FooMethods
def bar; :ok; end
end
module Foo
extend FooMethods
end
Foo.bar # => :ok
Note that with Modules, the methods are always defined as instance methods. This way they can be either extended into class scope or included into instance scope. Modules can also have class methods, using the exact same syntax / examples as shown above with classes. However there's not such as easy to load a module's class methods via include or extend.
How can [Time.now] be called in this fashion? Shouldn't there be an
instance of Time first created?
The Time.now method is a class method, not an instance method and therefore can be called directly on the Time class rather than an instance of it Time.new
Class methods are defined on the class themselves using the self keyword:
class Time
def self.now
# code
end
end
Time.now # works
Why can't I call the method [now] on its own? Am I right that it
relies on resources that it cannot find when called this way?
When you call a method "on its own" you're actually implicitly calling it on self:
self.now
The above is the same as just doing:
now
Why could I not call the method showTime on its own? But if I define
any method on the "global" level I can access it without referencing
the global object
You defined the showTime method on a specific class so you have to send that method to that class. When you define a method in the "global" scope you're implicitly defining it on self and the subsequent call to mymethod is actually self.mymethod so it will work.
Time.now is a class method.
To define a class method, you need to define the method with self. : def self.method_name
class Clock
#hours = 1
#minutes = 30
def self.showTime
puts "The time is: #{#hours}:#{#minutes}"
end
end
Clock.showTime
#=> The time is: 1:30
If you want to call now on its own, you can do so inside Time class :
class Time
puts now
#=> 2017-01-19 22:17:29 +0100
end
Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Scope of a method is not inside the class. Each method has its own entirely new scope.
New scopes are created whenever you use the class, module, and def keywords. Using brackets, as in C, does not create a new scope, and in fact you cannot arbitrarily group lines of code using brackets. The brackets (or do...end) around a Ruby block create a block-level scope, where variables previously created in the surrounding scope are available, but variables created within the block scope do not escape into the surrounding scope afterward.
Instance methods share the scope of their instance variables with other instances methods. An instance variable defined in the scope of a class definition is available in class-level singleton methods, but not in instance methods of the class.
Illustration:
class Foo
x = 1 # available only here
#y = 2 # class-wide value
def self.class_x
#x # never set; nil value
end
def self.class_y
#y # class-wide value
end
def initialize(z)
x = 3 # available only here
#z = z # value for this instance only
end
def instance_x
#x # never set; nil
end
def instance_y
#y # never set; nil
end
def instance_z
#z # value for this instance only
end
end
Foo.class_x # => nil
Foo.class_y # => 2
Foo.new(0).instance_x # => nil
Foo.new(0).instance_y # => nil
foo3 = Foo.new(3)
foo4 = Foo.new(4)
foo3.instance_z # => 3
foo4.instance_z # => 4
You can access class-level instance variables from within instances using the class-level getter. Continuing the example above:
class Foo
def get_class_y
self.class.class_y
end
end
foo = Foo.new(0)
foo.get_class_y # => 2
There exists in Ruby the notion of a "class variable," which uses the ## sigil. In practice, there is almost never a reasonable use case for this language construct. Typically the goal can be better achieved using a class-level instance variable, as shown here.
Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
Correct. The class definition body is executed when it is read. It's just code like any other code, there is nothing special about class definition bodies.
Ask yourself: how would methods like attr_reader/attr_writer/attr_accessor, alias_method, public/protected/private work otherwise? Heck, how would def work otherwise if it didn't get executed when the class is defined? (After all, def is just an expression like any other expression!)
That's why you can do stuff like this:
class FileReader
if operating_system == :windows
def blah; end
else
def blubb; end
end
end
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
No, it is not. There are 4 scopes in Ruby: script scope, module/class definition scope, method definition scope, and block/lambda scope. Only blocks/lambdas nest, all the others create new scopes.
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
Honestly, I don't fully understand what you are saying, but no, class definition scope is not method definition scope, class definition scope is class definition scope, and method definition scope is method definition scope.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Like I said above: there are 4 scopes in Ruby. There is nothing like block scope in C. (The Ruby concept of "block" is something completely different than the C concept of "block.") The closest thing you can get is a JavaScript-inspired immediately-invoked lambda-literal, something like this:
foo = 1
-> {
bar = 2
foo + bar
}.()
# => 3
bar
# NameError
In general, that is not necessary in Ruby. In well-factored code, methods will be so small, that keeping track of local variables and their scopes and lifetimes is really not a big deal.
So just creating a class without any instances will lead to something
actually executing in runtime (even allocating may be)? That is very
not like C++. –
Check out this code:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
If you execute that code, there won't be any output, but something still happened. For instance, a global variable named Dog was created, and it has a value. Here's the proof:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
dog = Dog.new("Ralph")
puts dog.name
--output:--
Ralph
The assignment to the Dog constant above is equivalent to writing:
class Dog
...
...
end
And, in fact, ruby steps through each line inside the class definition and executes each line--unless the line of code is inside a def. The def is created but the code inside a def doesn't execute until the def is called.
A very common line you will see inside a class definition is:
attr_accessor :name
...which can be rewritten as:
attr_accessor(:name)
...which makes it obvious that it's a method call. Ruby executes that line--and calls the method--when you run a file containing the class definition. The attr_accessor method then dynamically creates and inserts a getter and a setter method into the class. At runtime. Yeah, this ain't C++ land anymore--welcome to NeverNever Land.
I get that class definition creates a scope, but why is it not
accessible in the method?
Because that is the way Matz decided things should be: a def creates a new scope, blocking visibility of variables outside the def. However, there are ways to open up the scope gates, so to speak: blocks can see the variables defined in the surrounding scope. Check out define_method():
class MyClass
x = 1
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
The block is everything between do...end. In ruby, a block is a closure, which means that when a block is created, it captures the variables in the surrounding scope, and carries those variables with it until the the block is executed. A block is like an anonymous function, which gets passed to a method as an argument.
Note that if you use the Class.new trick, you can open two scope gates:
x = 1
MyClass = Class.new do
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
Generally, ruby allows a programmer to do whatever they want, rules be damned.
Is it possible to do something like this in Ruby (1.9.2-p290)?
class SomeClass
include SomeModuleThatProvidesLotOfConstants
def build(&block)
singleton_class.instance_eval(&block)
end
end
obj = SomeClass.new
obj.build do
some_class_method SomeConstant, :an => :option
...
end
Where some_class_method is a method that is available to SomeClass (not to instances of it) and SomeConstant is a class/module that is in scope inside of SomeClass, but would have to be references as SomeClass::SomeConstant from outside.
I can get this working if I always pass fully-qualified class names inside my block, but I'm trying to effectively "re-scope" the block when it is invoked. Is this possible? I'm pretty sure RSpec and other such tools that make heavy use of blocks achieve something like this :)
Note that while I'm calling class methods from inside the block, I only want the changes to affect this individual singleton class, rather than propogate to all instances.
EDIT | Ok, here's the non-pseudo version of what I'm trying to achieve. I'm trying to add some DataMapper properties at runtime, but only to singleton classes... I don't want them to suddenly appear across all instances of the model.
class Post
include DataMapper::Resource
property :id, Serial
property :title, String
property :created_at, DateTime
... etc ...
def virtualize(&block)
singleton_class.instance_eval(&block)
self
end
end
def suspend_post
#post = Post.get!(1).virtualize do
property :delete_comments, Boolean
end
end
I know there are other ways to do virtual attributes (I'm currently using a couple of different approaches, depending on the complexity), but I'm just experimenting with a few ideas to avoid cluttering my model definitions with transient methods that are only used for transporting form data in one specific part of the site and don't mean anything when you're reading the source code of the model by itself. One or two virtual attributes are ok, but as they start to mount up on commonly used models I start to explore things like this ;)
In the above, the resource would have all of the standard properties defined in the concrete class, plus any that are added in the #virtualize method. It's the reference to Boolean without the DataMapper::Property:: prefix that's throwing it off.
You've already got what you want with respect to methods. If you define some_class_method like this:
def Foo.some_class_method(name)
define_method name do
puts("this is the method #{name}")
end
end
and do
f = Foo.new
f.build { some_class_method "new_method" }
f.singleton_methods # => [:new_method]
You've defined behavior on just that one instance.
However I don't think you can get what you're looking for with respect to constants. One option would be to use methods instead of constants for those arguments. Another would be to have the client code mix in whatever module defines the constants.
Do keep in mind this is pretty dense metaprogramming, so the complexity may not be justified.
What's wrong with this:
class SomeClass
SOME_CONSTANT = 42
class << self
def some_class_method
'foo'
end
end
def build &block
self.class.instance_eval(&block)
end
end
SomeClass.new.build do
puts "#{some_class_method} #{SOME_CONSTANT}"
end
#=>foo 42
I am writing a class in Ruby where I have instance variables (i.e. #person_summary_info, #name, #dob, #favorite_food) for the class.
To parse a piece of text, I have a public method that I call from outside the class (let's call it interpret).
This method calls some private class methods such as get_name that use #person_summary_info to extract the respective piece of information (in this case, the name of the person). Should those private methods:
a) use the instance #person_summary_info, or get that information through a parameter passed to them (i.e. get_name vs get_name(person_summary_info))
b) modify the instance variable directly and return nothing, or modify nothing outside the scope of the function, and return the result (i.e. inside get_name, set #name = 'John', or return 'John')?
What is the best practice here?
Thanks!
I have included my best representation of your question in code at the bottom of my answer, but I'd like to present my solution as I understand your dilemma first...
Do this if your name attribute is meant to be publicly accessible:
class Person
attr_accessor :name
def initialize(name)
#name = name
end
def interpret(text_to_parse)
# I have no idea what you are parsing in real life
self.name = text_to_parse.split.last
end
end
person = Person.new("Frederick")
puts person.name
# => "Frederick"
person.interpret("Please, call me Fred")
puts person.name
# => "Fred"
Do this if your name attribute should not be (easily) publicly accessible: (For what it's worth, pretty much anything can be accessed one way or another in Ruby. One of the many things that make it awesome!)
class Person
def initialize(name)
#name = name
end
def interpret(text_to_parse)
# I have no idea what you are parsing in real life
#name = text_to_parse.split.last
end
end
person = Person.new("Frederick")
puts person.instance_variable_get("#name")
# => "Frederick"
person.interpret("Please, call me Fred")
puts person.instance_variable_get("#name")
# => "Fred"
And, as mentioned above, here's my best translation of your question into code:
class Person
def initialize
#person_summary_info = { name: "foo" }
#name = "bar"
#dob = "baz"
#favorite_food = "beer"
end
def interpret(text_to_parse)
# Some kind of parsing?
get_name_1
# OR
get_name_2(#person_summary_info)
# OR
get_name_3
# OR
#name = get_name_4
end
private
def get_name_1
#person_summary_info[:name]
end
def get_name_2(person_summary_info)
person_summary_info[:name]
end
def get_name_3
#name = 'John'
end
def get_name_4
'John'
end
end
Hopefully, you can see why there's some confusion in the comments about what you are asking exactly. If nothing else, maybe seeing this will help you to form your question more clearly so we can help!
Finally, you should avoid writing your own getters/setters in Ruby unless you need to hook in some custom code to the getting/setting processes -- use the class-level attr_reader/attr_writer/attr_accessor macros to create them for you.
If interpret() is not meant to change the state of a particular instance of Person, then consider naming the method something like get_name_from_string(string) and possibly making it static, since it doesnt do anything to the state of the instance.
If you want interpret() to change the state of a particular instance of Person, then consider changing the name of the method, prefixing it with set and include the attribute name being set (set_name_from_string()). If several attributes are being set, then perhaps set_from_string() and include a code comment stating what instance variables are being modified. Internally the method could call get/set_name() as described below.
Typically, getter/setter methods are public and should be quite simple, doing what their name suggests:
- getName() returns the instance variable #name
- setName(name) sets or overwrites the instance variable #name with the value passed in and returns nothing
In Java, this is a type of POJO, specifically Java Beans (excluding the part about needing to be serializable) Its very common programming practice in several different languages to have public setter/getter methods for the instance variables and to also have a default constructor (one that takes no arguments) and another constructor allowing you to set the instance variables upon instantiation of the Object.
using #instance directly from another class is a good way how to get into troubles. Each class should have it's own variables and anything you would like to process or return back should be assigned/returned directly.. that means that way
#instance = my_class.get_name(person_summary_info)
and not
my_class.get_name
Just try to imagine how to test that code using #instance variables and chance to reuse that piece of code..
just my 2c