[yast-devel] Help: Lost in Ruby

Hi, I would like to use some classes in the converted ruby code but I have problems with function lookup. I have figured out how to call functions (e.g. ArrangeButtons from include/partitioning/ep-lib.rb in my class method by using "module_function :ArrangeButtons" in ep-lib.rb. The problem now is that the method deep_copy cannot be found inside ArrangeButtons. It works if I use "include PartitioningEpLibInclude" inside my class but I don't like to extend the classes with lots of general utility functions. So what's the correct way here? Attached is a patch. The error message is: Client call failed with undefined method `deep_copy' for Yast::PartitioningEpLibInclude:Module and backtrace ["/usr/share/YaST2/include/partitioning/ep-lib.rb:503:in `ArrangeButtons'", "/usr/share/YaST2/include/partitioning/ep-all.rb:183:in `create'", ... ciao Arvin

Hi, Dne 7.8.2013 12:01, Arvin Schnell napsal(a):>
Hi,
I would like to use some classes in the converted ruby code but I have problems with function lookup.
[...]
So what's the correct way here?
I didn't examine your patch in detail, but it seems to me that you are mixing Ruby concepts (classes, modules) with ones inherited from YaST (mainly includes). My general recommendations are the following: * If you create a new class, put it into a separate file, don't make it inherit from anything in Yast:: and don't make it part of any YaST client, module or include. That will ensure separation of concepts. One class per file is also usual convention in Ruby world. * If you need to use a YaST module in your class, then just import it and use it -- that should work well. * If you need to use a YaST include in the class, include it via Ruby include as a Ruby module and call its initialize_* method (if any) manually in the constructor. You correctly note that this is ugly. The better way is probably to extract the include functionality you need into a separate Ruby class/module, use it in your class, and just delegate to the extracted part in the include. * If you need to use any part of Ruby bindings (e.g. UI shortcuts) in your class, just include the appropriate module. This may lead to some duplication if you do it in many classes, which we will probably need to address soon (in order to prevent everybody doing his own thing). I know this advice is pretty general, but I hope it helps at least a little. We need to have guidelines and conventions for all this, and improve any friction points. Josef planned to work on that (he wanted to do an example refactoring), but he is on vacation now. Once he returns, I am sure he'll be able to provide better advice than me and we'll drive the work on best YaST development practices forward. -- David Majda SUSE Studio developer http://susestudio.com/ -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org

On Fri, Aug 09, 2013 at 04:58:22PM +0200, David Majda wrote:
Hi,
Dne 7.8.2013 12:01, Arvin Schnell napsal(a):>
Hi,
I would like to use some classes in the converted ruby code but I have problems with function lookup.
[...]
So what's the correct way here?
I didn't examine your patch in detail, but it seems to me that you are mixing Ruby concepts (classes, modules) with ones inherited from YaST (mainly includes). My general recommendations are the following:
* If you create a new class, put it into a separate file, don't make it inherit from anything in Yast:: and don't make it part of any YaST client, module or include. That will ensure separation of concepts. One class per file is also usual convention in Ruby world.
Where should the new file be placed and how do I "include" it? Remember, I'm new to Ruby.
* If you need to use a YaST module in your class, then just import it and use it -- that should work well.
With Yast.import, right?
* If you need to use a YaST include in the class, include it via Ruby include as a Ruby module and call its initialize_* method (if any) manually in the constructor.
You correctly note that this is ugly. The better way is probably to extract the include functionality you need into a separate Ruby class/module, use it in your class, and just delegate to the extracted part in the include.
That sound like it involves moving lots of code, more or less all ep-*.rb files. And even then all calls to the extracted functions must be changed to include a namespace, e.g. from doit() to Foo.doit(). Since all mistakes only show up at runtime that's hopeless.
* If you need to use any part of Ruby bindings (e.g. UI shortcuts) in your class, just include the appropriate module. This may lead to some duplication if you do it in many classes, which we will probably need to address soon (in order to prevent everybody doing his own thing).
I would do so in a dozen of classes. Thanks for the help, but I think for the near future I have to put my ideas on ice. ciao Arvin -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org

On Fri, Aug 09, 2013 at 05:35:44PM +0200, Arvin Schnell wrote:
On Fri, Aug 09, 2013 at 04:58:22PM +0200, David Majda wrote:
I didn't examine your patch in detail, but it seems to me that you are mixing Ruby concepts (classes, modules) with ones inherited from YaST (mainly includes). My general recommendations are the following:
* If you create a new class, put it into a separate file, don't make it inherit from anything in Yast:: and don't make it part of any YaST client, module or include. That will ensure separation of concepts. One class per file is also usual convention in Ruby world.
Where should the new file be placed and how do I "include" it? Remember, I'm new to Ruby.
Put it under lib, src/lib/partitioning/ep-all.rb require "partitioning/ep-all" see also http://lists.opensuse.org/yast-devel/2013-07/msg00034.html
* If you need to use a YaST module in your class, then just import it and use it -- that should work well.
With Yast.import, right?
Yes.
* If you need to use a YaST include in the class, include it via Ruby include as a Ruby module and call its initialize_* method (if any) manually in the constructor.
You correctly note that this is ugly. The better way is probably to extract the include functionality you need into a separate Ruby class/module, use it in your class, and just delegate to the extracted part in the include.
That sound like it involves moving lots of code, more or less all ep-*.rb files. And even then all calls to the extracted functions must be changed to include a namespace, e.g. from doit() to Foo.doit().
Fair point. Prefixing calls with namespaces is a pain. We need to look at the concrete example to find out how to avoid it.
Since all mistakes only show up at runtime that's hopeless.
This is a general problem which has a standard answer: automated tests. Ruby should be greatly superior to YCP with respect to testing tools, let's put it to practice. (yeah, easy to say)
* If you need to use any part of Ruby bindings (e.g. UI shortcuts) in your class, just include the appropriate module. This may lead to some duplication if you do it in many classes, which we will probably need to address soon (in order to prevent everybody doing his own thing).
I would do so in a dozen of classes.
Thanks for the help, but I think for the near future I have to put my ideas on ice.
Step by step, we will get there. -- Martin Vidner, Cloud & Systems Management Team http://en.opensuse.org/User:Mvidner Kuracke oddeleni v restauraci je jako fekalni oddeleni v bazenu

On Wed, 7 Aug 2013 12:01:18 +0200 Arvin Schnell <aschnell@suse.de> wrote:
Hi,
Hi Arvin,
I would like to use some classes in the converted ruby code but I have problems with function lookup.
I have figured out how to call functions (e.g. ArrangeButtons from include/partitioning/ep-lib.rb in my class method by using "module_function :ArrangeButtons" in ep-lib.rb. The problem now is that the method deep_copy cannot be found inside ArrangeButtons.
OK, I think there is needed some explanations. At first link to explanation how modules works in ruby http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html With this background information it is easy to see where is your problem. We convert includes to mixins and such mixins depends on methods in given class, especially on other mixins in Client or Module [1]. And you want to use Module just like namespace for method, but such namespace doesn't have given methods.
It works if I use "include PartitioningEpLibInclude" inside my class but I don't like to extend the classes with lots of general utility functions.
So what's the correct way here?
Well, I check such method and you can remove deep copy as ret is newly generated and buttons is not modified, so you can pass it as reference. Just note that if you use another method for module you also need to make it available, but it is not this case. But for me correct way is to move it outside. So you can create in lib directory something like storage/ui_ext.rb Then have there something like: module Yast module UI def self.ArrangeButtons buttons ... end end end and in your code then require "storage/ui_ext" module Yast class Foo def m UI.ArrangeButtons end end end This show nice ruby feature, that allows extend classes. Of course another solution can be add it directly ui bindings or if you find it useful, I can add it to ruby bindings, so everyone can use it. If you have another issue or if something is not clear do not hesitate to ask. I can also provide more links that explain how ruby works. Josef
Attached is a patch.
The error message is:
Client call failed with undefined method `deep_copy' for Yast::PartitioningEpLibInclude:Module and backtrace ["/usr/share/YaST2/include/partitioning/ep-lib.rb:503:in `ArrangeButtons'", "/usr/share/YaST2/include/partitioning/ep-all.rb:183:in `create'", ...
ciao Arvin
-- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org

On Tue, Aug 20, 2013 at 11:15:12AM +0200, Josef Reidinger wrote:
Arvin Schnell <aschnell@suse.de> wrote:
I would like to use some classes in the converted ruby code but I have problems with function lookup.
I have figured out how to call functions (e.g. ArrangeButtons from include/partitioning/ep-lib.rb in my class method by using "module_function :ArrangeButtons" in ep-lib.rb. The problem now is that the method deep_copy cannot be found inside ArrangeButtons.
OK, I think there is needed some explanations. At first link to explanation how modules works in ruby http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
With this background information it is easy to see where is your problem. We convert includes to mixins and such mixins depends on methods in given class, especially on other mixins in Client or Module [1]. And you want to use Module just like namespace for method, but such namespace doesn't have given methods.
It works if I use "include PartitioningEpLibInclude" inside my class but I don't like to extend the classes with lots of general utility functions.
But for me correct way is to move it outside. So you can create in lib directory something like storage/ui_ext.rb
Then have there something like:
module Yast module UI def self.ArrangeButtons buttons ... end end end
and in your code then require "storage/ui_ext"
module Yast class Foo def m UI.ArrangeButtons end end end
But this requires moving more or less all files and functions of the expert partitioner around as I already replied to Martin. It's simply not worth the effort (and new bugs) - remember that all I want is to use existing functions from class functions without polluting the classes through mixin. Regards, Arvin -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org

On Tue, 20 Aug 2013 11:50:05 +0200 Arvin Schnell <aschnell@suse.de> wrote:
On Tue, Aug 20, 2013 at 11:15:12AM +0200, Josef Reidinger wrote:
Arvin Schnell <aschnell@suse.de> wrote:
I would like to use some classes in the converted ruby code but I have problems with function lookup.
I have figured out how to call functions (e.g. ArrangeButtons from include/partitioning/ep-lib.rb in my class method by using "module_function :ArrangeButtons" in ep-lib.rb. The problem now is that the method deep_copy cannot be found inside ArrangeButtons.
OK, I think there is needed some explanations. At first link to explanation how modules works in ruby http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
With this background information it is easy to see where is your problem. We convert includes to mixins and such mixins depends on methods in given class, especially on other mixins in Client or Module [1]. And you want to use Module just like namespace for method, but such namespace doesn't have given methods.
It works if I use "include PartitioningEpLibInclude" inside my class but I don't like to extend the classes with lots of general utility functions.
But for me correct way is to move it outside. So you can create in lib directory something like storage/ui_ext.rb
Then have there something like:
module Yast module UI def self.ArrangeButtons buttons ... end end end
and in your code then require "storage/ui_ext"
module Yast class Foo def m UI.ArrangeButtons end end end
But this requires moving more or less all files and functions of the expert partitioner around as I already replied to Martin. It's simply not worth the effort (and new bugs) - remember that all I want is to use existing functions from class functions without polluting the classes through mixin.
Regards, Arvin
You can make a small step instead :) you can keep ArrangeButtons also in partitioner and just have inside def ArrangeButtons(buttons) UI::ArrangeButtons(buttons) end then you are backward compatible, so all your ep stuff works and you just move one method that I think belongs to different place. This is how easy is to have backward compatible code in ruby. Josef -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org

Hi, I write my idea into code. I create pull request with your modified patch[1]. Just few notes to my code: 1) I remove your interface Panel. In ruby interface is often useless, because ruby depends only on method presence. 2) I refactoring code of arrange_buttons to looks more like ruby, I just keep few builtins that operate over buttons, because I don't know if nil can be passed, so to preserve behavior I keep it, but if you know that nil is not passed, you can change to to ruby native method. 3) I write simple test for code. There is just one failing, because for me it looks like bug, if you get 2 or 6 buttons it add one useless HBox. If something in pull request is not clear, just ask I can explain it how it works. I hope you see how code can looks better in ruby then in YCP. Josef [1] https://github.com/yast/yast-storage/pull/9 -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
participants (4)
-
Arvin Schnell
-
David Majda
-
Josef Reidinger
-
Martin Vidner