An array updatable?

Hi again,

I have this class:

class A
     attr_reader :name, :cars
     attr_writer :name

     def initialize(name)
         @name = name
         @cars = Array.new()
     end
end

a = A.new("xxxx")
a.cars << "vectra" << "megane"
puts a

output:

vectra
megane

Why can I access cars and modify it if I have not declared it how
attr_writer. Must I simply declare it private?

attr_reader means you can get a reference to the cars collection, to which
then you are adding items. attr_reader doesn't imply that the thing it's
returning will be treated as read only. That it means is that the user is
not allowed to replace the collection itself.

···

On 7/25/05, EdUarDo <eduardo.yanezNOSPAM@nospamgmail.com> wrote:

Hi again,

I have this class:

class A
attr_reader :name, :cars
attr_writer :name

def initialize(name)
@name = name
@cars = Array.new()
end
end

a = A.new("xxxx")
a.cars << "vectra" << "megane"
puts a

output:

vectra
megane

Why can I access cars and modify it if I have not declared it how
attr_writer. Must I simply declare it private?

--
Brad Wilson
http://www.dotnetdevs.com/
http://www.agileprogrammer.com/dotnetguy/

Peter: "Oh my god, Brian, there's a message in my Alphabits. It says,
'Oooooo.' "
Brian: "Peter, those are Cheerios."
- Family Guy

EdUarDo wrote:

Hi again,

I have this class:

class A
    attr_reader :name, :cars
    attr_writer :name

    def initialize(name)
        @name = name
        @cars = Array.new()
    end
end

a = A.new("xxxx")
a.cars << "vectra" << "megane"
puts a

output:

vectra
megane

Why can I access cars and modify it if I have not declared it how
attr_writer. Must I simply declare it private?

a.cars = nil # This gives an error

The restriction is on the variable, not on the object that is bound to
the variable. To restrict that object, you can #freeze it, but that will
affect all access, including within the class A.

Making a writer for it would mean you could set cars with code like:

a.cars = Hash.new

The code you show is not changing cars, it's just calling methods on the Array cars holds.

If you want to disallow this, you'll need to do some forwarding, for the methods you wish to allow. However, I say think and make sure this is strictly necessary before you bolt down the gates. Don't kill yourself securing what doesn't strictly need securing. If I type a.cars.clear I either know what I'm doing or deserve to see things explode. It's my opinion that neither of those are your problem. :wink:

James Edward Gray II

···

On Jul 25, 2005, at 12:45 PM, EdUarDo wrote:

Hi again,

I have this class:

class A
    attr_reader :name, :cars
    attr_writer :name

    def initialize(name)
        @name = name
        @cars = Array.new()
    end
end

a = A.new("xxxx")
a.cars << "vectra" << "megane"
puts a

output:

vectra
megane

Why can I access cars and modify it if I have not declared it how
attr_writer. Must I simply declare it private?

EdUarDo wrote:

class A
    attr_reader :name, :cars
    attr_writer :name

    def initialize(name)
        @name = name
        @cars = Array.new()
    end
end

<snip>

Why can I access cars and modify it if I have not declared it how
attr_writer. Must I simply declare it private?

How about this?

class A
    attr :name

    def cars
        @cars.dup.freeze
    end

    def initialize(name)
        @name = name
        @cars = Array.new()
    end
end

Hi EdUarDo,

Why can I access cars and modify it if I have not declared
it [with] attr_writer?

The code you show is not changing cars, it's just
calling methods on the Array cars holds. If you want
to disallow this, you'll need to do some forwarding,
for the methods you wish to allow. However, I say
think and make sure this is strictly necessary before
you bolt down the gates.

You might also ask yourself whether you really need to
expose the array object itself. You may be able to get away
with defining #each and including Enumerable, for instance.

   class BunchaCars
     include Enumerable
   
     def initialize(name)
       @cars = Array.new
       @name = name
     end
   
     def each
       @cars.each { |x| yield x }
     end
   end

This approach may or may not be appropriate in your case.
(We'd need more information to be able to tell.)

···

--
Daniel Brockman <daniel@brockman.se>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.

Joe Cheng schrieb:
Joe Cheng wrote:

How about this?

class A
    attr :name

    def cars
        @cars.dup.freeze
    end

    def initialize(name)
        @name = name
        @cars = Array.new()
    end
end

foo = A.new
# ...
foo.cars.first.replace 'xyzzy'

:wink: