I was looking at the contents of the Ransack ruby gem. Basically, it calls a method called require_constants. And that method itself requires a file:
# ransack.rb
require 'ransack/adapters'
Ransack::Adapters.object_mapper.require_constants
# adapters.rb
module Ransack
module Adapters
def self.object_mapper
#object_mapper ||= instantiate_object_mapper
end
def self.instantiate_object_mapper
if defined?(::ActiveRecord::Base)
ActiveRecordAdapter.new
elsif defined?(::Mongoid)
MongoidAdapter.new
end
end
class ActiveRecordAdapter
def require_constants
require 'ransack/adapters/active_record/ransack/constants'
end
...
# constants.rb
module Ransack
module Constants
The first require copies the content of adapters.rb in ransack.rb, I believe. Hence, we can then reference Ransack::Adapters without an undefined error.
However, when we call require_constants, it appears to copy the contents of Ransack::Constants into the method definition of require_constants.
I find that kind of confusing. We are copying a module inside of a method. What benefit do we get of copying a module inside of a method, rather than just doing it like the other require? Second, I know the module is not a local variable, but I couldn't even define a module in the console when I tried it:
class A
def a
module B end
end
end
SyntaxError: (irb):14: module definition in method body
So what is require doing that does not cause the syntax error?
"Copy" is the wrong word here. require does not copy anything. It reads the source code in the given file and executes that code in Ruby's global ("main") context (unless the file has already been required; then it does nothing and returns false). To quote the docs (emphasis mine):
Any constants or globals within the loaded source file will be available in the calling program’s global namespace.
require does not behave differently inside a method call, or inside a module, or anywhere else, than it does at the top of a file.
When you see require inside a method call the reason is usually that the module is only needed in a particular scenario, and loading it before that scenario occurs would be wasteful (because, for example, the module takes a long time to load or accesses, during loading, resources that are only available in that scenario—think different database drivers or OSes). It does not mean that the module is being loaded "into" the method or the surrounding code, because that's not what require does.
To demonstrate, suppose we have a module like this:
# baby_module.rb
module BabyModule
NAME = "Baby"
end
And suppose we run the following program:
module TheCorner
def self.load_baby_module
require File.expand_path("baby_module", __dir__)
end
end
TheCorner.load_baby_module
if defined?(TheCorner::BabyModule)
puts "#{TheCorner::BabyModule::NAME} is in TheCorner"
elsif defined?(BabyModule)
puts "Nobody puts #{BabyModule::NAME} in TheCorner"
end
As you have perhaps already guessed, this program's output will be:
Nobody puts Baby in TheCorner
So what is require doing that does not cause the syntax error?
Simple: it runs the file.
Related
I have a Ruby module in a file called my_module.rb:
module My_module
def my_module_method
puts 'inside my method'
end
end
In a file my_class.rb in the same folder, I have a class contained within the module.
module My_module
class My_class
def my_object_method
My_module.my_module_method
end
end
end
My_module::My_class.new.my_object_method => 'undefined method 'my_module_method''
I was not expecting this error. I assumed that Ruby would run into the line 'My_module.my_module_method' and search for a module called 'My_module' and a method within it called 'my_module_method.' This is what Java does, for example. However, Ruby does not do this. In order to get my_object_method to work, I have to write in my_class.rb:
require 'my_module.rb'
Why doesn't Ruby search for My_module when I call my_object_method? It seems obvious what it should search for and therefore redundant to require the programmer to explicitly write 'yes, Ruby, please allow me to make calls to module-wide methods.' What am I missing?
Ruby doesn't automatically load files. If you need a code from some file, you have to load it (by calling require) explicitly.
Thus, when you run "ruby my_class.rb" it loads only this file and you have to define dependencies between files by yourself.
You seem to have a misunderstanding of how to define a class method. In order to make your method call work, you could define it as def self.my_method_name.
In both classes and modules, methods work the same when you define them as class methods using self. or alternatively the class << self syntax. However instance methods (methods without the self.) work differently in these 2 cases. In classes, as you seem to understand, they're accessible once you instantiate the class using .new. In modules, they're only accessible if you include or extend.
See also:
difference between class method , instance method , instance variable , class variable?
http://www.rortuts.com/ruby/ruby-include-vs-extend/
Oh any by the way. Ruby doesn't enforce any convention where you have 1 file per class (named identically). You need to manually require files wherever you need them. Although there are some frameworks such as Rails which auto-require files, and enforce naming conventions.
I wanted to separate some methods into a module for abstraction purposes but I am getting a nomethod error when testing the first function in my module.
functions.rb
module Functions
def avg_ticket(vol,count)
(vol.to_f/count).round(2)
end
end
example.rb
require_relative 'functions'
vol = 5000
count = 2500
avg_ticket = Functions.avg_ticket(vol,count)
I am getting a undefined method 'avg_ticket' for functions:Module (NoMethodError)
both files are in the same folder so I am using require_relative if that makes a difference. this may be basic, but can anyone help me understand why the method is undefined here?
edit: changed module functions to module Functions in question
You named your module functions, but you're trying to call Functions. Names are case sensitive. Also, you need to name your module with an upper case first letter anyway. Also, to be defined on the module itself, you want to use def self.avg_ticket, see the following:
module Functions
def self.avg_ticket(vol,count)
(vol.to_f/count).round(2)
end
end
And using it:
p Functions.avg_ticket(2, 25)
> 0.08
Usually, I put most of my require statements at the top of a file. While reading the source code for Poltergeist, I noticed the following
module Capybara
module Poltergeist
require 'capybara/poltergeist/utility'
require 'capybara/poltergeist/driver'
require 'capybara/poltergeist/browser'
# more requires
end
end
Actual source
What are the advantages of using require this way?
The advantage in this case is that the Capybara::Poltergeist module exists before those modules are required. Since those modules all extend the Capybara::Poltergeist module, this is just a way to ensure that they aren't loaded before the module is actually available. Placing the require statements after the module definition would have the same effect.
Consider the following:
# foobar.rb
require './bar_module'
module Foo
module Bar
end
end
# bar_module.rb
module Foo::Bar
def baz
"hi!"
end
end
This setup will fail because the non-nested Foo::Bar syntax will expect Foo to already exist by the time this module is called. By changing the first file to:
module Foo
module Bar
require './bar_module'
end
end
The require will work, since Foo::Bar will exist by the time that bar_module starts doing its thing.
In this particular instance, it doesn't have much practical effect, since Poltergeist uses the nested module syntax (module Foo; module Bar) rather than the collapsed syntax (module Foo::Bar), but it's a good practice that basically delineates "these requires expect this module to exist".
I don't know what's the advantage in your example.
I sometimes use require inside a method definition.
I do this for methods which are used rarely, but need large libraries. The advantage: The large library is only loaded, when it is really needed.
require checks, if the library is already loaded. So I have no problem with double loading a library.
I am in a position at the moment where I have a plugins folder where there there could be 1 or 100 plugins to be loaded.
Now the problem is, each plugin requires an instance of a class defined within the startup ruby file.
A really simplified example would be:
#startup.rb
def load_plugins
#... get each plugin file
require each_plugin
end
class MuchUsedClass
def do_something
#...
end
end
muchUsedInstance = MuchUsedClass.new
load_plugins
#some_plugin.rb
class SomePluginClass
def initialize(muchUsedInstance)
#muchUsedInstance = muchUsedInstance
end
def do_something_with_instance
#muchUsedInstance.do_something
end
end
somePluginInstance = SomePluginClass.new(muchUsedInstance)
somePluginInstance.do_something_with_instance
The main problem is that when you call require, it doesn't have any clue about what has happened before it is being required. So I find it nasty making a global variable within the startup file just to satisfy all other required files, but it seems like one of the only ways to be able to pass some data down to an included file, I could also make a singleton class to expose some of this, but that also seems a bit nasty.
As I am still new to ruby and am still looking through the statically typed glasses, I will probably be missing a decent pattern to solve this, in C# I would opt for dependency injection and just hook everything up that way...
Your example code does not have a global variable. Global variables have names that start with $. The code as you wrote it won't work, because muchUsedInstance is just a local variable and will not be shared between different Ruby files.
If you are not going to change the instance ever, you could easily store it as a constant:
MuchUsedInstance = MuchUsedClass.new
You could store it as a nested constant inside the class:
MuchUsedClass::Instance = MuchUsedClass.new
You could store it as an instance variable inside the class object, with a getter method that automatically creates it if it isn't there already:
def MuchUsedClass.instance
#instance ||= MuchUsedClass.new
end
I'm having some difficulty with referring to module-level variables in ruby. Say I have a situation like this, where I'm referring to M.a internally:
module M
##a=1
def self.a
##a
end
class A
def x
M.a
end
end
end
Now, this example works fine for me but it is failing in a slightly more complicated context (where the module is spread over a number of files installed in a local gem - but my understanding is that that should not effect the way the code is executed) with an error like this: undefined method `a' for M::M (NoMethodError).
So, is this the correct way to refer to module level variables in context? is there a simpler/more idiomatic way?
If the module is spread out over other files, you need to ensure that your initialization is run before the method is called. If they are in the same file, this should be as much as guaranteed, but if you somehow split them there could be trouble.
I've found you can usually get away with this:
module M
def self.a
#a ||= 1
end
end
If this variable is subject to change, you will need a mutator method. Rails provides mattr_accessor that basically does what you want, part of ActiveSupport.