Understanding Ruby modules behaviour - ruby

So I've just recently started writing some Ruby and whilst I understand how modules work, the following behaviour still throws me off.
module ModuleA
def a_greet
'Hello from module A'
end
end
module ModuleB
def b_greet
'Hello from module B'
end
end
include ModuleA
include ModuleB
# WHY DOES THIS WORK !!!!!
p ModuleA.b_greet
I get that the functions from the modules can be called without specifying Module. and that I'd never write code in this way, but I cannot understand why you can call a method included from ModuleB when explicitly stating ModuleA?

Wait, there's more:
"Why does this work?".b_greet # => "Hello from module B"
You're including those modules in a top-level object main. It's a special object: all methods defined on it become available to all objects (see the line above, there's now b_greet method on a String). ModuleA is an object too, so, when you include ModuleB, ModuleA gets its methods. If you include those modules in a regular class/object, you won't get this "sharing" behaviour.

Related

Chef LWRP referencing relative resource

I am trying to create a LWRP to extend a supermarket cookbook 'webpshere'.
In my resource file, I am trying to extend this class with a base class found in parent cook book.
In the code below, 'WebsphereBase' is defined in the parent library 'websphere_base'. Can I get help on how to reference it?
Thanks
#require 'websphere_base'
module PIWebsphereCookBook
class WebsphereJbdc < WebsphereBase
require_relative 'helper'
In the source of the cookbook, you can see that the WebsphereBase class is defined inside the WebsphereCookbook module.
To reference this class from outside this module, you have to name the nesting so that Ruby is able to find the class you are referring to. With youur example, this can look similar to:
module PIWebsphereCookBook
class WebsphereJbdc < WebsphereCookbook::WebsphereBase
require_relative 'helper'
# ...
end
end
You don't need to require things coming from upstream cookbooks, nor can you (except for my weirdo cookbooks). All libs for cookbooks you depend on will be loaded by the time your library files run.

How can I use a Ruby module in another module?

This code doesn't seem to work - it can't figure out some_func was defined. Why not?
# in file 1
def ModuleA
def some_func
"boo"
end
end
# in file 2
def ModuleB
include ModuleA
MY_CONSTANT = some_func
end
You are declaring your modules wrong and you need to extend not include
module ModuleA
def some_func
"boo"
end
end
module ModuleB
extend ModuleA
MY_CONSTANT = some_func
end
In your code example, you use the include directive.
This means that all the methods defined in ModuleA are included into ModuleB as instance methods.
However, by then invoking some_func in the module body of ModuleB, you try to invoke it as if it was defined as a class method on ModuleB (which it is not, since you have used include before).
If you actually want to define (and call) it as a class method, then you need to use extend ModuleA inside ModuleB to add the method definition.

Using generated sources in a buildr project

I'm trying to use a code generator inside a buildr-based java project. I would like to...
call the generator
compile the generated classes, package them
eclipse to see the generated stuff (i.e. have the .classpath contain those sources)
I've spend lots of time googling around for a complete example, but to no avail. the example here: https://cwiki.apache.org/confluence/display/BUILDR/How+to+generate+sources+before+compilation
gives be plenty of errors. this is what I've tried:
define 'generator' do
compile.with ALL_COMMON_MODULES
end
define 'extras' do
p 'calling generator..'
Java.classpath << ALL_COMMON_MODULES
Java.classpath << projects('generator')
Java.org.foo.generator.Main.main(['../modules/daos', 'target/generated-sources'])
sources = FileList[_("src/main/jeannie/*.*")]
generate = file(_("target/generated-sources") => sources).to_s do |dir|
puts 'generating...'
mkdir_p dir.to_s # ensure directory is created
end
compile.from generate
end
gives me an error like this:
RuntimeError: Circular dependency detected: TOP ...
so I'm obviously doing something very very wrong. I'd be very glad to see a working example or project that uses code generation.
I finally got it working with help from the buildr mailinglist. for anyone interested: The example mentioned here contains a problem. this:
compile.from generate
should be:
compile.from generate.to_s
Now it works beautifully! buildr also automatically extends the .classpath for eclipse (and idea, in case you use that) if 'compile.from' points to another location.
You cause the circular dependency by calling the parent project in your 'extras' project.
At that line: Java.classpath << projects('generator')
Maybe you should put everything on the same level, which is also what the example shows.
In this scenario your "generate" reference wouldn't be available either as it is in the 'extras' scope.
Something like this (untested):
define "generator" do
generate = file(_("target/generated-sources") => sources).to_s do |dir|
p 'calling generator..'
Java.classpath << ALL_COMMON_MODULES
Java.classpath << projects('generator')
Java.org.foo.generator.Main.main(['../modules/daos', 'target/generated-sources'])
sources = FileList[_("src/main/jeannie/*.*")]
puts 'generating...'
mkdir_p dir.to_s # ensure directory is created
end
compile.with ALL
compile.from generate
end

What does ruby include when it encounters the "include module" statement?

If I have the following project structure
project/
lib/
subproject/
a.rb
b.rb
lib.rb
where lib.rb looks like this :-
module Subproject
def foo
do_some_stuff
end
end
and a.rb and b.rb both need to mixin some methods within lib.rb and are both namespaced within a module like so :-
require 'subproject/lib'
module Subproject
class A
include Subproject
def initialize()
foo()
end
end
end
What does ruby do when it encounters the include statement? How does it know that I want to only include the mixin from lib.rb rather than the whole module which includes both class A and class B, is this based purely on the require of subproject/lib or am I getting it wrong and it is including the whole of the module, including the definitions of Class A and B within themselves?
It is including the whole of the module. There is only one Subproject module, you've just reopened it in a.rb and b.rb and added classes A and B to it. I don't think require is related anyhow there.
BTW, you can always try it out in irb:
>> Subproject::A
=> Subproject::A
>> Subproject::A::A
=> Subproject::A

Including a Ruby class from a separate file

For a while I had been including an entire class inside of a Ruby module. Apparently this is not what I am supposed to do. It appears that the point of a module is to store functions which can then be included as methods in a new class.
I don't want this. I have a class that I want to keep in a separate file which I can access from other files. How can I do this?
Thanks.
Modules serve a dual purpose as a holder for functions and as a namespace. Keeping classes in modules is perfectly acceptable. To put a class in a separate file, just define the class as usual and then in the file where you wish to use the class, simply put require 'name_of_file_with_class' at the top. For instance, if I defined class Foo in foo.rb, in bar.rb I would have the line require 'foo'.
If you are using Rails, this include often happens automagically
Edit: clarification of file layout
#file: foo.rb
class Foo
def initialize
puts "foo"
end
end
...
#file: bar.rb
require 'foo'
Foo.new
If you are in Rails, put these classes in lib/ and use the naming convention for the files of lowercase underscored version of the class name, e.g. Foo -> foo.rb, FooBar -> foo_bar.rb, etc.
As of ruby version 1.9 you can use require_relative, to require files relatively to the file you are editing.
You can also use load. Also you use require relative if the file is in the same directory. Read this link for further understanding: http://rubylearning.com/satishtalim/including_other_files_in_ruby.html
When Using Require, inside the string indicate the full path name of the class you are refereing to unless its in the Root Folder of Ruby
MyApp
|_ app
|_ bin
|_ etc, etc
root_level_file.rb
You can just do:
require './root_level_file'.
RootLevelFile.new

Resources