From: Tony Mobily Date: 2006-02-20T09:32:35+09:00 Subject: Re: Classes and OO design - help Hi, > I know it doesn't answer your question, but wouldn't it be way, WAY > easier to use a database to store your data? Short answer: yes :-) It's a long story - I can't do it right now... Merc. > > Jon > > On Mon, 2006-02-20 at 01:19 +0900, Tony Mobily wrote: >> 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: >> >> /A/abcexample@iinet.net.au/ >> >> 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("merc2@mobily.com") >> 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.[](name) >> >> 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. >> > -- > Jonathan Leighton > http://turnipspatch.com/ | http://jonathanleighton.com/ | http:// > digital-proof.org/ >