An idiom I like... modifiable defaults

H

Hal Fulton

Just thought I'd share a little concept that I find
useful. Your comments are welcome.

Sometimes objects are created with certain defaults.
One way to override them is with default values in
the constructor (and often corresponding writer methods).

But sometimes I "don't like" the default and want to
change it (for this program/session).

Often I use class-level accessors for that purpose.

Here's a contrived example...


Cheers,
Hal


class Text

class << self
attr_accessor :color
Text.color = "black"
end

attr_accessor :color

def initialize(txt, color="black")
# Hint: You can improve this further by saying
# def initialize(txt, color=Text.color)
puts "#{color} text..."
end
end


# The old way...

a = Text.new("some") # black
b = Text.new("random","blue") # blue
c = Text.new("text") # black
c.color = "blue" # but now it's blue

# The new way...

Text.color = "blue"

e = Text.new("Ruby is cool") # blue
f = Text.new("as dry ice") # blue
 
R

Robert Klemme

Hal said:
Just thought I'd share a little concept that I find
useful. Your comments are welcome.

Sometimes objects are created with certain defaults.
One way to override them is with default values in
the constructor (and often corresponding writer methods).

But sometimes I "don't like" the default and want to
change it (for this program/session).

Often I use class-level accessors for that purpose.

Here's a contrived example...


Cheers,
Hal


class Text

class << self
attr_accessor :color
Text.color = "black"
end

attr_accessor :color

def initialize(txt, color="black")
# Hint: You can improve this further by saying
# def initialize(txt, color=Text.color)
puts "#{color} text..."
end
end


# The old way...

a = Text.new("some") # black
b = Text.new("random","blue") # blue
c = Text.new("text") # black
c.color = "blue" # but now it's blue

# The new way...

Text.color = "blue"

e = Text.new("Ruby is cool") # blue
f = Text.new("as dry ice") # blue

Thinking in code - some other approach:

def hash_replace(hash, replacement)
tmp = hash.dup

begin
hash.update replacement
return yield
ensure
hash.clear
hash.update tmp
end
end

{1=>2, :foo=>"bar"}
=> nil{1=>2, :foo=>"replaced"}
=> nil{1=>2, :foo=>"bar"}
=> nil

Hm...

Kind regards

robert
 
I

Ilmari Heikkinen

Just thought I'd share a little concept that I find
useful. Your comments are welcome.

Sometimes objects are created with certain defaults.
One way to override them is with default values in
the constructor (and often corresponding writer methods).

But sometimes I "don't like" the default and want to
change it (for this program/session).

Often I use class-level accessors for that purpose.

Here's a contrived example...


Cheers,
Hal

Hi,
I quite like this, but wonder if it works with inheritance?

On a related note, it seems a lot of people (you, me, Ara Howard,
probably many others) are coming up with these configuration idioms,
maybe we could find a way that fulfills most needs (and perhaps even
try to get it into ruby core)?

Here's a quick list of features for discussion, feel free to add your
own:
- named arguments (ie. hash argument way)
- auto-assigning configuration vars to instance variables
- works with inheritance (this is probably the most difficult -- and
useful -- one)
- auto-generated accessors
- configuration by block
- maybe some way to co-operate with the regular positional params


Would look something like this in use:

class Image
include TheAutoConfigModule
config_accessor{
name # defaults to nil
width = 100
height = 100
depth = 32
}
def bytes
@width * @height * @depth / 8
end
end

class BWImage < Image
config_accessor{ depth = 8 }
end

i = Image.new:)height => 200, :depth => 16)
i.width == 100 and i.height == 200 and i.depth == 16
#=> true
i.name
#=> nil
i.bytes
#=> 40000

bwi = BWImage.new( 'holiday shots', 10, 10 )
bwi.width == 10 and bwi.height == 10 and bwi.depth == 8
#=> true
bwi.name
#=> 'holiday shots'
bwi.bytes
#=> 100

Image.config
#=> {:name => nil, :width => 100, :height => 100, :depth => 32}

BWImage.config.height = 200
BWImage.config
#=> {:name => nil, :width => 100, :height => 200, :depth => 8}

Thoughts?


Cheers,
Ilmari
 
A

Ara.T.Howard

Just thought I'd share a little concept that I find useful. Your comments
are welcome.

Sometimes objects are created with certain defaults. One way to override
them is with default values in the constructor (and often corresponding
writer methods).

But sometimes I "don't like" the default and want to change it (for this
program/session).

Often I use class-level accessors for that purpose.

Here's a contrived example...

Cheers,
Hal


class Text

class << self
attr_accessor :color
Text.color = "black"
end

attr_accessor :color

def initialize(txt, color="black")
# Hint: You can improve this further by saying
# def initialize(txt, color=Text.color)
puts "#{color} text..."
end
end


# The old way...

a = Text.new("some") # black
b = Text.new("random","blue") # blue
c = Text.new("text") # black
c.color = "blue" # but now it's blue

# The new way...

Text.color = "blue"

e = Text.new("Ruby is cool") # blue
f = Text.new("as dry ice") # blue

i do this so much i built it in to my new traits lib (currently being
updated). works like:

harp:~ > cat a.rb
require 'traits'

class Text
class_trait 'color' => 'black'
trait 'color'

def initialize opts = {}
color( opts['color'] || opts[:color] || Text.color)
end
end

Text::color = 'blue'
p Text::new


harp:~ > ruby a.rb
#<Text:0xb75bb744 @color="blue">

i generally also allow modifing classes via the environment, eg:

class Text
class_trait 'color' => (ENV['TEXT_COLOR'] || 'black')
trait 'color'
def initialize opts = {}
color( opts['color'] || opts[:color] || Text.color)
end
end

i'm using this in all cases except true constants - like PI.

cheers.


-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| renunciation is not getting rid of the things of this world, but accepting
| that they pass away. --aitken roshi
===============================================================================
 
G

Glenn Parker

Hal said:
Sometimes objects are created with certain defaults.
One way to override them is with default values in
the constructor (and often corresponding writer methods).

But sometimes I "don't like" the default and want to
change it (for this program/session).

Often I use class-level accessors for that purpose.

Not very thread-safe, at least not in your example's implementation.
 
A

Ara.T.Howard

Not very thread-safe, at least not in your example's implementation.

even this is not:

class C
attr_accessor :x
end

unless you make it.

FYI.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| renunciation is not getting rid of the things of this world, but accepting
| that they pass away. --aitken roshi
===============================================================================
 
G

Glenn Parker

even this is not:

class C
attr_accessor :x
end

Yes, if instances of class C are to be accessed from multiple threads,
then some extra care is needed, but that depends very much on the actual
uses of the class. A lot of classes never need to worry about it, even
in highly threaded applications.

In the OP's code, it's clear that all threads using class Text will be
in contention for Text.color. In that style of interface, a per-thread
variable is practically mandatory.
 
H

Hal Fulton

Glenn said:
Not very thread-safe, at least not in your example's implementation.

No. I suppose if I were using this with threads I would
wrap the thread-safety code around it rather than hiding
it inside.


Hal
 
M

Martin DeMello

Ilmari Heikkinen said:
I quite like this, but wonder if it works with inheritance?

This is actually my primary use case for class variables (@@var) -
inheriting configuration defaults down a class tree.

martin
 
I

Ilmari Heikkinen

This is actually my primary use case for class variables (@@var) -
inheriting configuration defaults down a class tree.

martin

The problem with class vars is that changing them in the subclass
affects the superclass aswell.

class A
@@foo = "A"
def foo; @@foo; end
end

class B < A
@@foo = "B"
end

A.new.foo
=> "B"
 
M

Martin DeMello

Ilmari Heikkinen said:
The problem with class vars is that changing them in the subclass
affects the superclass aswell.

Yes, but you're the programmer - simply don't change them in the
subclass.

martin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top