Why use Ruby's attr_accessor, attr_reader and attr_writer?
You may use the different accessors to communicate your intent to someone reading your code, and make it easier to write classes which will work correctly no matter how their public API is called.
class Person
attr_accessor :age
...
end
Here, I can see that I may both read and write the age.
class Person
attr_reader :age
...
end
Here, I can see that I may only read the age. Imagine that it is set by the constructor of this class and after that remains constant. If there were a mutator (writer) for age and the class were written assuming that age, once set, does not change, then a bug could result from code calling that mutator.
But what is happening behind the scenes?
If you write:
attr_writer :age
That gets translated into:
def age=(value)
@age = value
end
If you write:
attr_reader :age
That gets translated into:
def age
@age
end
If you write:
attr_accessor :age
That gets translated into:
def age=(value)
@age = value
end
def age
@age
end
Knowing that, here's another way to think about it: If you did not have the attr_... helpers, and had to write the accessors yourself, would you write any more accessors than your class needed? For example, if age only needed to be read, would you also write a method allowing it to be written?
Is it good that everything is an object?
A counterexample would be that in Java Integer
is an object but int
is not, which means different operations apply to both (admittedly in recent Java there is automatic conversion to/from the object version, but this can introduce unexpected performance issues). Objects are a little slower due to indirection, but more flexible; everything being an object means everything behaves consistently. Again Java would be an example: an array is not an object, and ArrayIterator
is something that is bolted on after the fact (with multiple third party implementations, even) and therefore not quite consistent with the way collection class iterators work.
Intitalizing Object with Array of objects from another class Ruby
Your problem is assigning a new value to @stars_array
variable on each iteration. There are multiple ways to deal with it:
@stars_array = (0..99).map { |i| Star.new('unknown_star',i) }
By the way, there is a couple of design issues (just for your attention):
Why variable is called
stars_array
, not juststars
?Why would ever instance of
Star
class have some object named@star
inside? Recursion? :) Seems like@name
would be proper and more clear attribute's name.Don't miss indentation.
EDIT: About DB-mapping. Most common way - inherit both classes from ActiveRecord::Base, and create one-to-many relation from solar system to stars. Each class will have it's own table. Takes absolutely no efforts.
best way to set a default value on a property without ActiveRecord?
class Foo
# class-level instance variable
# setting initial value (optional)
@class_var = 42
# making getter/setter methods on the class itself
class << self
attr_accessor :class_var
end
# instance-level variable getter/setter methods
attr_accessor :inst_var
def initialize
# setting initial value (optional)
@inst_var = 17
end
end
p Foo.class_var
#=> 42
Foo.class_var = 99
p Foo.class_var
#=> 99
f1 = Foo.new
f2 = Foo.new
f2.inst_var = 1
p [f1.inst_var, f2.inst_var]
#=> [17,1]
Object memory allocation
The answer depends on the actual implementation. Here I assume you are asking about MRI.
Ruby objects are allocated on the heap. There is no concept of the stack when talking about object allocations.
The heap is split up into pages, each consisting of 16kb. Each page is carved up into fixed size slots which can hold Ruby objects. A page can hold ~408 objects, since each object (which is an RVALUE
struct) occupies 40bytes.
All of this is managed by the VM (ie. YARV).
source: http://timetobleed.com/garbage-collection-slides-from-la-ruby-conference/
Regarding your example, variables just hold references to objects, so m
actually points to an allocated MyClass
object.
The C struct (RClass
) that backs up MyClass
internally, contains a pointer to a table with the user-defined methods like #mySecondMethod
and a pointer to a table with the names of the instance variables that its objects have.
Each object (which is backed up by RObject
since the Object
class is the default root of all objects) internally contains a pointer to the values of its instance variables.
The newly defined #mySecondMethod
is available because of the dynamic nature of the language and the fact that method lookup happens at runtime.
Determining type of an object in ruby
The proper way to determine the "type" of an object, which is a wobbly term in the Ruby world, is to call object.class
.
Since classes can inherit from other classes, if you want to determine if an object is "of a particular type" you might call object.is_a?(ClassName)
to see if object
is of type ClassName
or derived from it.
Normally type checking is not done in Ruby, but instead objects are assessed based on their ability to respond to particular methods, commonly called "Duck typing". In other words, if it responds to the methods you want, there's no reason to be particular about the type.
For example, object.is_a?(String)
is too rigid since another class might implement methods that convert it into a string, or make it behave identically to how String behaves. object.respond_to?(:to_s)
would be a better way to test that the object in question does what you want.
Can I use an instance method for a computed value in a rails model?
The problem is that you're trying to access an ActiveRecord attribute with the assumption that each data field is stored in an instance variable. This doesn't work as attributes are stored in @attributes
as a ActiveRecord::AttributeSet
structure.
You can take a look at the structure using segment.instance_method_get(:@attributes)
Manipulating the raw data within this structure is pretty cumbersome. So AR defines attribute methods for every attribute in the class. In your case, #min
, #min=
or #sec
, #sec=
(and a bunch of other methods).
It's good practice to access data through accessor methods instead of accessing the instance variable directly. This holds true whether you're calling the method from inside or outside the class.
If you're calling the method outside the class, you need to explicitly state the receiver of the accessor method, which will be your segment
object.
segment.min
Within your class, the receiver is implicitly defined as self
(as Michael Kohl mentioned), so you can call the accessor directly using
class Segment
def padded_min
min to_s.rjust(2,"0")
end
end
Implicit definition works a bit differently for methods which end with =
- Ruby assumes you're creating a local variable. You need to explicitly define the receiver if you're trying to call a setter method.
class Segment
def update_time(time)
self.time = (time)
end
end
Related Topics
Arbitrary Precision Arithmetic with Ruby
Error: "Fatal: I Don't Handle Protocol ''Git' When Using Bundle Install
Mongodb Group Using Ruby Driver
Activerecord User-Supplied Column Name
Have Delayed_Job Log "Puts", SQL Queries and Jobs Status
Ruby 'Require' Call Fails on Custom Code
Accessing JSON Objects in Ruby
How to Set Correct Ruby Version in Gem Environment
Ruby - Return Byte Array Containing Two's Complement Representation of Bignum/Fixnum
Getting Count of Elements by 'Created_At' by Day in a Given Month
Vagrant Ssh Not Working in Mobaxterm on Windows
Error Installing "Kgio-2.9.2" Gem on Windows
Rails Reload Dynamic Routes on Multiple Instances/Servers