require_relative and utility methods - ruby

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

Related

Ruby including Errors module in another module - uninitialized constant Errors

I've got NON-RAILS app where I want to include Errors module to Formatter module to access the method error_class from Errors. Like below:
#lib/formatter.rb
module Formatter
module_function
include ::Errors
def format(number)
number.delete!(' ')
raise error_class(:invalid_number), 'Invalid phone number, check your entries' unless valid?(number)
end
end
#lib/errors.rb
module Errors
class FormatterExceptionError < StandardError; end
PhoneNumberInvalid = Class.new(FormatterExceptionError)
def error_class(status)
case status
when :invalid_number
PhoneNumberInvalid
end
end
end
With this code I'm getting an error:
Failure/Error: include ::Errors
NameError:
uninitialized constant Errors
Did you mean? Errno
Non-rails apps doesn't have auto-load classes, so you need to manually require it.
Also one thing of your code, I don't know why you are using :: before constant, I don't see any naming-confusion in order to use it. It is used only when you have your Errors modules on different namespace, and you need to resolve the one from your namespace.
# lib_formatter
require 'errors'
module Formatter
extend Errors
# ...
end
The second issue with your code, that you have defined instance method, but where you include your code there class method (module method)
So errors class should be adjusted to
module Errors
# ...
module_function
# ...
end
The third issue is include/extend misusage.
Include used to make all methods from module you using to become instance methods.
Extend used to make all methods from module you using to become class methods.
include/extend info

Why must I write 'require' to access module-wide methods in Ruby?

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.

requiring a file within a method

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.

referring to module level variables from within module

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.

Extending Enumerable in Rails 3

UPDATE TO QUESTION
Here is what I have done based on some research and findings.
STEP 1 - I have this module in my Rails 3 project and place it in my lib folder
# lib/enumerable.rb
module Enumerable
def sum
return self.inject(0){|acc,i|acc +i}
end
def average
return self.sum/self.length.to_f
end
def sample_variance
avg=self.average
sum=self.inject(0){|acc,i|acc +(i-avg)**2}
return(1/self.length.to_f*sum)
end
def standard_deviation
return Math.sqrt(self.sample_variance)
end
end
STEP 2 - According to this blog article, in Rails 3 your lib folder will not get loaded automatically. In order to load this module you need to go to your config / application.rb and type this in:
config.autoload_paths += %W(#{config.root}/lib)
STEP 3 - Then in your model my understanding is you type this in to get the module picked up.
class MyModel < ActiveRecord::Base
include Enumerable
end
STEP 4 - I then try restart the rails server and try this out and I get false when I would expect it to be true.
MyModel.respond_to?('sample_variance')
# false, when it should be true
What am I doing wrong? Should I not be getting true?
Your inclusion of the main Enumerable module (not your extension) undoubtedly worked, and you can test it by simply checking for any of the methods that were mixed in. The problem is, your 'Include Enumerable' may not have included your file, but rather the main module.
One suggestion is to rename the file name for your extension, and have it loaded through an initializer with a
require 'my_enumerable.rb'
That way you for sure get both Enumerable and your extension to Enumerable loaded.
If I understand what you're driving at, you're trying to use Enumerable's sum method in ActiveRecord. You can do that by converting the current object to an array, then calling Enumerable's sum method on that array.
One more thing: you don't need to use return like you are using it. Ruby will return the last calculated thing from your method. You don't need to use self like that either -- in Ruby, self is the current object.
So if you have a method:
def charlie
inject{|i, j| i + j + 1}
end
and you call it like this:
(1..2).charlie
self is the current object (1..2).
The output will be 4, with no self or return.
I highly recommend Dave Thomas' lecture on Ruby metaprogramming, I tried to find it, but I could not, it's out there on the web somewhere.
You might want to take a look at this:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
You can include a module in a class, and thereby make that module's methods available to that class.
If you include Enumerable into a Rails model, then its methods would be available to that model. But since Enumerable's methods are already available to certain types of objects inside your Rails project, and those objects are available to be instantiated from inside your model, I don't see why you might do that, because Enumerable's methods are working just fine for the purposes they were designed.
Anyway, you might find that one of the following might work for you:
-- use Activerecord's sum method
-- convert your object to an array, and use Enumerable's sum method
-- write your own method, but don't call it sum, because you don't want to confuse yourself.
Try commenting out the second occurrence of module Neuone in the following snippet, and see what happens. Then try commenting out the Charlie.one method, and see what happens.
module Neuone
def one
'neuone one'
end
def two
'neuone two'
end
end
module Neuone
def two
'neuone two two'
end
end
class Charlie
include Neuone
def one
'charlie one'
end
end
c = Charlie.new
p c.one
p c.two

Resources