On 27.06.2010 21:26, Intransition wrote: > Sorry about the delay... you know how it is! No problem. > On Jun 25, 8:30 am, Robert Klemme<shortcut... / googlemail.com> wrote: >> I always thought the proper idiom was >> >> # lib/foo.rb >> >> class Foo >> ... >> end >> >> # optionally: >> require 'foo/baz' >> >> # lib/foo/baz.rb >> >> require 'foo' >> >> class Foo::Baz >> ... >> end > > Not always feasible, especially if you are loading a whole directory > of "pluggable" files. In which case you either bottom require of use > a separate file. I just as soon not have to worry about it either way > and if class and module where the same type of object then we wouldn't > have to. Hmm, personally I would prefer loading those pluggable files explicitly - even if via a special method that only passes a directory name. Library code typically resides in a different location than user extensions so for me this makes more sense. In that case I would do class Foo def self.load_plugins(dir_or_file) ... end end and then require 'foo' Foo.load_plugins ENV["HOME"] + "/foo_extension" You could even make it more implicit by defaulting to an environment variable. class Foo def self.load_plugins(dir_or_file = ENV['FOO_PLUGINS']) ... end end >> Alternative: >> >> # lib/foo.rb >> >> class Foo >> ... >> autoload :Baz, 'foo/baz' >> end > > I do not ever use autoload, as much as I might like to. It has a known > bug --it will not use a customized require. I used a customized > require in my development environment, so it is useless to me. (and > really the one thing that pisses the most... but thats' another story) Indeed, that's a downside. OTOH, I'd rather opt for fixing that then neglecting autoload. It is apparent: I don't do gem development and I rarely use gems. :-) >> # lib/foo/baz.rb >> (as above) >> >>>>> 2) The developer is forced to choose between two arbitrarily limited >>>>> mechanisms for modeling behavior. Classes can only re-used once per >>>>> subclass and not at all via modules. Modules can be reused extensively >>>>> but do not naturally include their singleton methods. These >>>>> limitations lead to a number of code smells from overly limited use of >>>>> inheritance to included/ClassMethods hooks. >> >>>> Why is not including their singleton methods a strong limitation? >> >>> Well it is strong. >> >> That's a strange answer to my question... >> >> Let my first try to understand what you mean. If I get you properly you want >> >> module A >> def self.wok >> puts "I work for the class" >> end >> >> def foo >> puts "happy instance" >> self.class.wok >> end >> end >> >> class X >> include A >> end >> >> X.new.foo -> >> "happy instance" >> "I work for the class" >> >> I personally would write >> >> def foo >> puts "happy instance" >> A.wok >> end >> >> and be done so I suspect you mean something else. > > Well that's one thing. But look what happens: > > class Y< X > def self.wok > "ain't going to happen" > end > end > > You've strapped the implementation to a specific module, so there is > no way to override behavior in a subclass (short of overriding every > method that uses #wok). Hmm, I see your point. I am wondering though whether that might not be too much magic. >>> If it were the other way around, and the singleton methods >>> were passed along, but you didn't want them to be... well I would like >>> to know under what circumstances that it would really be a problem. If >>> those singleton methods were simply being used as functions (M.f) then >>> they can easily be placed in a separate module. If they were being >>> used as a DSL for the module itself, why would the including class be >>> any worse off by gaining the DSL too? Maybe there is some reason one >>> can find, but even so it seems to me that it must be the rarity. >> >> If I get your drift you want to use a single module to define a DSL >> and apply it at the same time, e.g. >> >> module DSL >> def self.smart_attribute(sym) >> class_eval "def #{sym}; puts 'Look Ma, how smart I am!'; @#{sym}; end" >> end >> >> smart_attribute :name >> end >> >> and then >> >> class Foo >> include DSL >> >> smart_attribute :age >> end >> >> f = Foo.new >> f.name >> f.age >> >> I personally would separate the DSL out into another module because >> that is more modular. Not all classes might need smart attribute >> "name" so this would be a better choice IMHO. > > Well, you could do that. But that's not really the main issue. > Probably the most significant issue is with super: > > module M > def self.x; "M"; end > end > > class X > include M > def self.x > super > end > end > > You get an error. If it wasn't for this class inheritable attributes > would be completely trivial to implement. Ah, I see! It seems for the class level methods one needs a second module which is automatically used to extend a class that includes the visible module. I agree, that's a bit clumsy. module X module Xcl def bar puts "X::bar" end end def self.included cl cl.extend Xcl end def foo puts "foo" self.class.bar end end class A include X def self.bar puts "A::bar" super end end irb(main):042:0* A.new.foo foo A::bar X::bar => nil But not really hard. > Another more clear case is where the DSL defines a method that uses > another method the instance level as an overridable piece of > functionality. > > module DSL > def self.smart_attribute(sym) > class_eval "def #{sym}; puts message + ' ' + @#{sym}.to_s; end" > end > > # Default message is 'Look Ma, a smart:'. > def message > 'Look Ma, a smart:' > end > end > > Not being able to do this, you have to do more difficult meta- > programming tricks to achieve the same result. > >>> all my use of modules I can't think of case where having the singleton >>> level would have been a problem and in many, if not most, I actually >>> wanted it and had to resort to some hoop jumping. >> >>> In fact, and I think this is worth pointing out... I never ever design >>> a module to be utilized via #extend. In such usecases I actually do >>> this: >> >>> module M >>> def self.append_features(base) >>> base.extend self >>> end >>> end >> >>> Of course, rather than fuss about it, we could also have the best of >>> both worlds (imagine that). I, for one, would be happy with a new >>> method to go along with #include and #extend that provided both >>> singleton and instance levels. >> >> I believe automatically propagating class methods might cause >> confusion because it is not obvious. At least I haven't had the need >> for this (which might be due to the different tasks we tackle with >> Ruby). > > To me, not propagating them is "not obvious" --anything that creates > more work for me, when the alternative has no significant issues, > seems "not obvious" ;) :-) I think we agree to disagree here. >>>>> 3) Using tools for conceptually different cases does not require that >>>>> their be physically different tools for each case. I wrench is just as >>>>> good for tightening a nut as it is for loosening one. If I had to use >>>>> two separate tools it would be quite annoying. >> >>>> I believe this is a bad analogy. If at all you would have to take >>>> "tightening and loosing nuts" and "hammering". In that case it's >>>> quite obvious that you would not normally use the same tool for the >>>> job. > >> I believe this points into the right direction. I'm not doing as much >> meta programming - probably because I am not involved in creating >> frameworks in Ruby. I rather use the language to solve day to day >> problems. So far my need for DSL's has been scarce. So where were >> you really hurt by not automatically propagating class methods? I >> can't think of a case but obviously you have one in mind. > > Look at Anise, look at Facets Inheritor. And wherever you find the > ClassMethods hack, that is a case, and there are many of those around. Ok, I'll try to. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/