tag:blogger.com,1999:blog-12467669.post4238237000328724901..comments2023-04-29T07:23:25.825-04:00Comments on Jay Fields' Thoughts: Ruby: Lazily Initialized AttributesJayhttp://www.blogger.com/profile/14491442812573747680noreply@blogger.comBlogger16125tag:blogger.com,1999:blog-12467669.post-20221713966934108242007-08-13T13:36:00.000-04:002007-08-13T13:36:00.000-04:00require 'traits'class Employee ha...require '<A HREF="http://www.codeforpeople.com/lib/ruby/traits/traits-0.9.2/README" REL="nofollow">traits</A>'<BR/>class Employee<BR/> has :emails => []<BR/> has :assistant {<BR/> Employee.find_by_boss_id(self.id)<BR/> }<BR/>endAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-38730920425923768392007-08-07T06:49:00.000-04:002007-08-07T06:49:00.000-04:00I would say that using instance_variable_defined? ...I would say that using instance_variable_defined? is overly verbose in this case. Why not just use<BR/><BR/> defined? @emails<BR/><BR/>... But just as I write this, I realize you can't dynamically generate a statement using defined? in this way. So instance_variable_defined? is probably a better solution after all.Ola Binihttps://www.blogger.com/profile/15793488672952593953noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-42347238814884895902007-08-01T05:42:00.000-04:002007-08-01T05:42:00.000-04:00damn, this is extremely obvious and insignificant....damn, this is extremely obvious and insignificant. what's next, an entry on conditional statements? oh wait...<BR/><BR/>seriously, it's no wonder why ruby programmers have a bad rep.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-104480891400197262007-07-30T09:55:00.000-04:002007-07-30T09:55:00.000-04:00Orion,def emails@emails = [] unless instance_varia...Orion,<BR/><BR/>def emails<BR/>@emails = [] unless instance_variable_defined?:@emails<BR/>return @emails<BR/>end<BR/><BR/>is *not* exactly the same as this<BR/><BR/>def emails<BR/>@emails = [] unless instance_variable_defined?:@emails<BR/>end<BR/><BR/>The 2nd example returns nil when @emails is defined.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-26428513095050807462007-07-30T07:29:00.000-04:002007-07-30T07:29:00.000-04:00Someone's probably posted this already, but everyt...Someone's probably posted this already, but everything in ruby is an expression, so get rid of your unneccessary code.<BR/><BR/>In other words, this:<BR/><BR/>def emails<BR/> @emails = [] unless instance_variable_defined?:@emails<BR/> return @emails<BR/>end<BR/><BR/>is *exactly* the same as this<BR/><BR/>def emails<BR/> @emails = [] unless instance_variable_defined?:@emails<BR/>end<BR/><BR/>Also, given that @emails is supposed to be an array, and as such there is no sane reason for it to be false, you could just use the guard operator, and do this:<BR/><BR/>def emails<BR/> @emails ||= []<BR/>end<BR/><BR/>Shorter code has less opportunity for bugs, and is easier to maintain<BR/><BR/>You can do yourself a massive favour by learning the ins and outs of whatever programming language you are using so that you too can write shorter code. Your QA department, boss, and customers will thank you for itOrion Edwardshttps://www.blogger.com/profile/15865664564356161586noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-73529930724559914062007-07-30T05:45:00.000-04:002007-07-30T05:45:00.000-04:00why not just:class Employee def emails @emails...why not just:<BR/><BR/>class Employee<BR/> def emails<BR/> @emails ||= []<BR/> end<BR/>endSharon Rosnerhttps://www.blogger.com/profile/11590619388606831929noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-72365433707300442332007-07-30T03:17:00.000-04:002007-07-30T03:17:00.000-04:00Oh yes, I forgot to mention, another win for the a...Oh yes, I forgot to mention, another win for the attributes(&block) style of definition is that it would be relatively easy to extend along the lines of:<BR/><BR/>attributes {<BR/> emails.default(&{[]}).type(Array)<BR/> contents.valid do |instance,newval| <BR/> ! newval.blank?<BR/> end<BR/> whatever do<BR/> type(Array)<BR/> valid {...}<BR/> end<BR/>}<BR/><BR/>I'm becoming more and more convinced that, if you want to see how to write effective DSL type code in Ruby you should be looking far more closely at RSpec than at ActiveRecord's class methods.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-76901895835343677232007-07-30T03:07:00.000-04:002007-07-30T03:07:00.000-04:00I tend to agree that the motivation's a bit dodgy,...I tend to agree that the motivation's a bit dodgy, especially where you're hand rolling the accessors. A RubyQuiz67 solution that meant you could write:<BR/><BR/>attribute :emails {[]}<BR/>attribute :content => "Replace this"<BR/><BR/>Or, (harder to implement, but revealing more in the way of intent.)<BR/><BR/>attributes {<BR/> emails.default {[]}<BR/> content.default "Replace this"<BR/>}<BR/><BR/>then you get declarative code that reveals your intent clearly.<BR/><BR/>Without a class method to build the accessors for you, you're arguably better with a composed initialize along the lines of:<BR/><BR/>def initialize(args = {})<BR/> initialize_defaults<BR/> initialize_attributes(args)<BR/> yield self if block_given?<BR/>end<BR/><BR/>def initialize_defaults<BR/> @emails = []<BR/> @content = "Replace this"<BR/>end<BR/><BR/>The big win for lazy initialization comes when the code for generating the default is expensive and/or you expect that you won't be falling back on the default very often.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-78547345570226719282007-07-30T01:06:00.000-04:002007-07-30T01:06:00.000-04:00Mario, you can if the variable cannot be false or ...Mario, you can if the variable cannot be false or nil ( as in the example ). However, to accomidate all situations the implementation provided by Piers is perhaps more appropriate.<BR/><BR/>Cheers, JayAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-68084919967670070422007-07-30T00:45:00.000-04:002007-07-30T00:45:00.000-04:00> But when accessing the attributes you'll have to...> But when accessing the attributes you'll have to use the getter.<BR/><BR/>That's just a bonus :) Always use the getter...it's an attribute, leave the variable alone.<BR/><BR/>> Since with lazy initialization there is no longer a single spot to see how all variables are initialized.<BR/><BR/>Another place where really thinking of our_obj.emails as an attribute instead of a variable helps. It happens to use an instance variable to store itself, but that really is an implementation detail. Ideally, the only code touching @emails is in emails()iveyhttps://www.blogger.com/profile/08304497346415676299noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-8785237181630648852007-07-30T00:39:00.000-04:002007-07-30T00:39:00.000-04:00"The motivation for converting attributes to be la..."The motivation for converting attributes to be lazily initialized is for code readability purposes." <BR/><BR/>I thought the purpose of Lazy Initialization was to make the code more flexible while sacrificing some readability. Since with lazy initialization there is no longer a single spot to see how all variables are initialized. Also, getters take on two roles (getting and initializing) instead of just one.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-14784739119966287312007-07-29T22:59:00.000-04:002007-07-29T22:59:00.000-04:00I am sure I am missing something... Why can't you...I am sure I am missing something... Why can't you just say:<BR/><BR/>class Employee<BR/> def emails<BR/> @emails ||= []<BR/> @emails<BR/> end<BR/>endMariohttps://www.blogger.com/profile/09735459767789832285noreply@blogger.comtag:blogger.com,1999:blog-12467669.post-9034331481204162132007-07-29T22:34:00.000-04:002007-07-29T22:34:00.000-04:00Nice quiz. It's quite good fun solving it so that ...Nice quiz. It's quite good fun solving it so that you don't need any conditional code in the body of your generated methods...Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-90917233030903961832007-07-29T21:14:00.000-04:002007-07-29T21:14:00.000-04:00Piers,Thanks for the tip on instance_variable_defi...Piers,<BR/>Thanks for the tip on instance_variable_defined?. I'm currently still on 1.8.5, but I went ahead and updated the example since I expect many people have moved on to 1.8.6<BR/><BR/>As far as creating an attr_accessor with a default value: I'd suggest solving this RubyQuiz (http://rubyquiz.com/quiz67.html) if you want a really bullet proof version.<BR/><BR/>I was looking for the simple version to include in <I>Refactoring, Ruby Edition</I>, which was my actual motivation for this post.<BR/><BR/>Cheers, JayAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-25333121961441933112007-07-29T18:14:00.000-04:002007-07-29T18:14:00.000-04:00That's not quite bullet proof though. It breaks wh...That's not quite bullet proof though. It breaks where false is a legal value, but isn't the default, and when nil is a legal (but not default) value. The bulletproof approach is:<BR/><BR/>def emails<BR/> unless instance_variable_defined? :@emails<BR/> @emails = []<BR/> end<BR/> return @emails<BR/>end<BR/><BR/>It'd be very handy if you could write:<BR/><BR/>attr_accessor :emails {|instance| ... }<BR/><BR/>which would use the block to generate the default value for the attribute. It's a simple matter of programming I suppose...Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-12467669.post-50503841597692354872007-07-29T16:20:00.000-04:002007-07-29T16:20:00.000-04:00I really like that idea. But when accessing the at...I really like that idea. But when accessing the attributes you'll have to use the getter. Otherwise the attribute might still not be initialized. (I always use the getter. Seems good style to me.)<BR/>So: great idea!Anonymousnoreply@blogger.com