From: Jonathan Leighton Date: 2006-02-20T02:57:14+09:00 Subject: Re: Classes and OO design - help I know it doesn't answer your question, but wouldn't it be way, WAY easier to use a database to store your data? 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/