Hello Everybody,
First of all, thank you so much for participating to this list and
help poor Ruby newbies like me!
I feel the need to start this email with an apology. I am a terrible
programmer.
Ruby is the only thing that could possibly convince me to start
programming again. I hadn’t fallen in love with a computer language
in a long time!
I can say that I “know” Ruby. I have a very good memory, which helped
me along the way. I don’t know it well, and I lack the necessary
experience to be comfortable with it, but I “know” it.
However, I am absolutely hopeless at designing anything in terms of
objects and OOP. This is why I have the feeling I am just about to
embarrass myself. But hey…
I started writing my first “real” program in Ruby, and here I am,
thinking: is this correct? Would a “real” programmer do this like way?
So, here I am. I have a file system, with the following contents:
[…]/A/
[…]/B/
[…]/C/
[…]/D
Under /A/, there is:
In each directory, there are the following files:
name
surname
password
state
unconfirmed_flag
moderator_flag ← The file can exist (flag = true ) or not exist
(flag = false)
I know there are much better ways of doing so, and that this approach
creates a lot of tiny files, but unfortunately, at least for now, I
am stuck with it.
If you are curious, this is how the subscribers’ information is
stored for Free Software Magazine (http://www.freesoftwaremagazine.com).
I wrote (see: I didn’t write “designed”! ) a class to access this
information on the file system.
Here it is:
#!/usr/local/bin/ruby -w
class Subscribers
# Get the config file's contents
#
begin
@@config_data_dir=IO.read("#{ENV['HOME']}/.subs/
data_dir")
@@config_data_dir.chomp!
rescue SystemCallError
STDERR.puts(“WARNING! Can’t find the config file!”);
@@config_data_dir=“”
end
def initialize()
@good_state=false # This might be completely useless
@first_letter=""
@full_path=""
@attr_values={}
end
# This is the same as initialize... for now!
# (who knows...)
#
def de_initialize
@good_state=false
@first_letter=""
@full_path=""
@attr_values={}
end
# This just checks that the directory actually
# exists. It creates a "link"
#
def link_to_fs(email)
# Gets the person's information
#
@good_state=true
@first_letter=email[0,1].upcase
@full_path=@@config_data_dir+"/current/"+
@first_letter+"/"+email+"/"
@attr_values={}
@attr_values[:email]=email
# Hang on: if the file doesn't exist, undo everything
#
if ! File.exist?(@full_path)
de_initialize()
return false
end
true
end
def get_flag(flag)
File.exist?(@full_path+flag.to_s)
end
def get_field(field)
# Email is special: it's not in a file
#
if( field == :email)
return @attr_values[:email]
end
# Either return the existing @attr_values[field], or
# (if it's nil) reads it from the file system.
# CACHE!
#
begin
@attr_values[field]||=IO::read(@full_path
+field.to_s)
rescue SystemCallError
nil
end
end
def set_field(field,value)
# Email cannot be set
#
if(field == :email)
return nil
end
# Open the file
#
begin
ios=File::open(@full_path+field.to_s,"w")
rescue SystemCallError
return nil
end
# Set the value to nil. This is to reflect the
# "real" state of the variable (the file has just been
# cleared up by the previous call)
#
@attr_values[field]=nil
begin
ios.print(value)
rescue SystemCallError
ios.close
return nil
end
# OK, it worked: assign the new value
#
ios.close
@attr_values[field]=value
end
def set_flag(flag,value)
# NOT DONE YET. Ask the mailing list if I wrote a pile
# of crap first... :-|
end
# TODO: methods to create a new entry (just creates the directory),
# methods to get ALL of the parameters in one go in a Hash, etc.
end
a_subscriber=Subscribers.new()
puts a_subscriber.link_to_fs(“[email protected]”)
puts a_subscriber.get_field(:email)
puts a_subscriber.get_flag(:premium_flag)
puts a_subscriber.get_flag(:moderator_flag)
puts “OK:”
puts a_subscriber.set_field(:name,“BLAH!!!”)
puts a_subscriber.get_field(:name)
Now… here are my questions:
-
Is it sane for the class to set the class variable
@@config_data_dir? @@config_data_dir=IO.read(“#{ENV[‘HOME’]}/.subs/
data_dir”) -
Would it make more sense to create a new person with the
method ::new? In this case, what would you call the method to access
an existing person? -
Is this solution OO sound?
I can’t think of a way, in Ruby, to do access the information like
this: a_subscriber.get_field[:name] or a_subscriber.get_flag
[:moderator_flag]. This is the tricky part, and that’s where my lack
of understanding of OOP shows blatantly.
a.subscriber.get_field[:name] is the equivalent of saying
a.subscriber.get_field.
This implies that get_field returns an object of some kind able to
respond to []. If this were the way to go design-wise (which I doubt,
but now I am confused, so…), where would such an object be created?
How would it access the class information such as @@config_data_dir,
or the subscriber’s instance variable?
- I am thinking about a container class for this:
SubscribersContainer. The class would implement the method each(), so
that I can scan through the list of people stored (creating a
Subscriber object for iteration). Is this a sane approach?
I am just a little scared of doing anything right now. Did I design
it all wrong? Should I have created the classes SubscribersFlags and
SubscribersAttributes, and have two instance variables in Subscribers
derived from SubscribersFlags and SubscribersAttributes?
- The most important of all: can you suggest a book or a web site
which would help me design more decent classes? Possibly something
that is Ruby-centric…
Thanks a lot!
Merc.