Got to be a better way to code class variables....

D

Dave Howell

I'm trying to set up an object with overridable defaults, so it will act =
like this:
=3D> #<Smee:0x102007618 @middle=3Dnil, @front=3Dnil, =
@back=3Dnil>=3D> #<Smee:0x102007618 @middle=3D"hi", @front=3Dnil, =
@back=3Dnil>=3D> #<Smee:0x101ffdbb8 @middle=3Dnil, @front=3D"O", =
@back=3Dnil>=3D> "utu"

So I can set the properties of an instance of Smee, but if I don't set =
them, or if I set them back to nil, then the corresponding values that I =
set on the class will take their place.

This is the closest thing I've gotten so far and it seems ludicrously =
clunky.

class Smee
attr_writer :front, :middle, :back

class << self
attr_accessor :front, :middle, :back
end
=09
def front
@front || Smee.front
end

def middle
@middle || Smee.middle
end

def back
@back || Smee.back
end
end

I'd like to have other methods that worked the same on the class and the =
instance, but the only way I can figure out how is to type every single =
method twice. There has *got* to be some terribly clever ruby-esque way =
of including instance methods as class methods or vice versa, no?
 
J

Joel VanderWerf

Dave said:
I'm trying to set up an object with overridable defaults, so it will act like this:

>> s1 = Smee.new
=> #<Smee:0x102007618 @middle=nil, @front=nil, @back=nil>
>> s1.middle = 'hi'
=> "hi"
>> s1
=> #<Smee:0x102007618 @middle="hi", @front=nil, @back=nil>
>> Smee.front = "O"
=> "O"
>> s2 = Smee.new
=> #<Smee:0x101ffdbb8 @middle=nil, @front="O", @back=nil>
>> s1 => #<Smee:0x102007618 @middle="hi", @front="O", @back=nil>
>> Smee.middle = "utu"
=> "utu"
>> s1.middle
=> "hi"
>> s2.middle
=> "utu"

So I can set the properties of an instance of Smee, but if I don't set them, or if I set them back to nil, then the corresponding values that I set on the class will take their place.

This is the closest thing I've gotten so far and it seems ludicrously clunky.

class Smee
attr_writer :front, :middle, :back

class << self
attr_accessor :front, :middle, :back
end

def front
@front || Smee.front
end

def middle
@middle || Smee.middle
end

def back
@back || Smee.back
end
end

I'd like to have other methods that worked the same on the class and the instance, but the only way I can figure out how is to type every single method twice. There has *got* to be some terribly clever ruby-esque way of including instance methods as class methods or vice versa, no?


Some metaprogging...


module Overridable
def overridable(*names)
names.each do |name|
attr_writer name
(class << self; self; end).class_eval do
attr_accessor name
end
class_eval %{
def #{name}
@#{name} || self.class.#{name}
end
}
end
end
end

class Smee
extend Overridable

overridable :front
self.front = 12
end

sm0 = Smee.new

sm1 = Smee.new
sm1.front = 56

p sm0.front, sm1.front
 
R

Robert Klemme

like this:

Question is, do you really need this:
instance, but the only way I can figure out how is to type every single me=
thod twice. There has *got* to be some terribly clever ruby-esque way of in=
cluding instance methods as class methods or vice versa, no?

? You only need setters on the class if you want to be able to modify
the default. If not, you can use a different approach (see below).
Some metaprogging...

Some more metaprogramming:

http://gist.github.com/448155

Kind regards

robert
 
D

Dave Howell

act like this:
=20
Question is, do you really need this:

Good question. I'm not sure yet.
the instance, but the only way I can figure out how is to type every =
single method twice. There has *got* to be some terribly clever =
ruby-esque way of including instance methods as class methods or vice =
versa, no?
=20
? You only need setters on the class if you want to be able to modify
the default. If not, you can use a different approach (see below).

What I'm trying to do is save myself a bunch of time and effort coding a =
web site. The class will contain the 'default' web page, and then =
various routines will be able to tweak the title, or the main contents, =
to fit the specific page the user is on. But, I'd also like to be able =
to change the 'default' page properties as well, as conditions warrant. =
I could just store everything in some kind of cloud-y, tree-y, nest-y =
sort of structure, but the class/instance structure seemed like a good =
place to start.*

Then when I tried it, and ended up with rather goofy looking code, it =
became an opportunity to learn more about Ruby. Playing with Joel's code =
taught me something absolutely nifty-keen: the difference between =
"include" and "extend".=20

I made a brief mention in my original post that I also wanted to have =
some methods that would also appear on both the class and the instance. =
I ended up with this solution:


module SmeeCore
def pushmepullyu
@back =3D @front
end
def double
@front =3D @front * 2
end
end

class Smee
extend SmeeCore # including it for the _class_
include SmeeCore # including it for the _instances_
end



* I suspect I'm going to discover that the class properties are more =
global than I'd originally thought, and my instance properties are going =
to have to become a lot more sophisticated and start handling 'regional' =
defaults: less than global, more than local.=20
 
J

Joel VanderWerf

Dave said:
class Smee
extend SmeeCore # including it for the _class_
include SmeeCore # including it for the _instances_
end

If you want the extend to happen automatically when you include a
module, you can define an included method on M that does it:

module M
def foo
"FOO"
end
def self.included(c)
c.extend M
end
end

class C
include M
end

p C.foo
p C.new.foo
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top