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/