Rails autoloading example - ruby-on-rails

I am newbie to Ruby on Rails and I broken my head at Rails autoloading mechanisms.
I have code like that in my /app/helpers/posts_helper.rb :
module PostsHelper
def markdown_render(data)
Utils::Markdown.render data
end
end
And I have /app/utils/markdown.rb with something like:
module Utils
class Markdown
class << self
def render(data)
# render some dataz
end
end
end
end
According to http://guides.rubyonrails.org/autoloading_and_reloading_constants.html, Utils::Markdown will be loaded from /app/utils/markdown.rb, but instead I recieve:
ActionView::Template::Error:
uninitialized constant PostsHelper::Utils
I understand that the problem lies in referencing Utils from helper module, but I have zero ideas how to properly make that reference.
Please, help me before my head crashes! =-)

Try:
module PostsHelper
def markdown_render(data)
::Utils::Markdown.render data
end
end
that is, put a double-colon in front of Utils::Markdown. It basically means "look in the root namespace," rather than "look within the namespace I'm currently in".

In markdown.rb, just define Markdown as a top level class (i.e. not in any namespaces).

Okay, I sorta figured it out.
The app/some_name path is "chewed" from module/class finding, so Rails do not expect to find in app/some_name/some_class.rb SomeName::SomeClass, it simply expect to find SomeClass.
I restructured the file ierarchy, so now it is: app/modules/utils/my_markdown.rb, and it correctly autoloads Utils::MyMarkdown from my_markdown.rb =-)

Related

Why does Ruby not throw an error in an instance where the class name and file name of the class are mismatched?

Why isn't the Ruby interpreter throwing a NameError in this instance here?
class OrangeTurtle
self.table_name = 'turtles'
end
Filename: orange_turtles.rb
This answer might sound like a cop out, but it doesn't throw an error because Ruby doesn't care even the slightest what your filenames are called.
e.g. in file asdfasdf.no_rb_ending_here we can have
#!/usr/bin/env ruby
module Something
class Test
def test
puts 'test'
end
end
end
class SomethingElse
def otherThings
puts 'haha'
end
end
Then to make things even weirder, I can have a separate file that modifies (monkey patches) the classes defined in that file.
in more_stuff.rb
#!/usr/bin/env ruby
require_relative 'asdfasdf.no_rb_ending_here'
module Something
class Test
def test2
test()
puts '2'
end
end
end
class SomethingElse
def moreThings
otherThings()
puts 'MOAR'
end
end
Something::Test.new.test2()
# test
# 2
SomethingElse.new.moreThings()
# haha
# MOAR
Ruby is pretty cool - you don't get errors for things that don't NEED to cause an error.
The name error, or uninitialized constant error only appears in Rails. The reason for that is, that active record (which is also a general design pattern) is mapping the tables in the database with the models (or with objects in general).
Active Record can only make that connection via the naming conventions for files and the classes.
As mentioned in the other answer, pure ruby doesn't need to comply with these conventions. However, it is a general rule to name the files like the classes they contain to have better organised code.

Uninitialized Constant when adding helper module to Model Test

I'm trying to do something fairly simple - add a module with helper methods to a Model test, but I keep getting the following error
uninitialized constant NeighborhoodTest::NeighboorhoodTestHelper
The module is located in test/helpers/neighborhood_test_helper.rb
module NeighborhoodTestHelper
def create_polygon
points = self.geolocate
boundary = Geokit::Polygon.new(points)
end
.
.
end
Per the recommendation in this SO answer, did the following inside test/models/neighborhood_test.rb:
require 'test_helper'
require 'helpers/neighborhood_test_helper'
class NeighborhoodTest < ActiveSupport::TestCase
include NeighboorhoodTestHelper
def setup
#crime = crimes(:arrest)
#neighborhood = neighborhoods(:one)
end
test "neighborhood should contain crime" do
neighborhood_boundary = #neighborhood.create_polygon
crime_location = #crime.geolocate
assert neighborhood_boundary.contains?(crime_location)
end
end
I also tried this SO that didn't work. Anyone know why this approach doesnt work in Models?
tests/some_helper.rb
module SomeHelper
def method1
-----------
-----------
end
def method2
-----------
-----------
end
end
tests/test_helper.rb
require some_helper.rb
You can now access method1 and method2 in any of your test cases. Hope it helps you .
I ran into this today with rspec 3.8, and with other helper tests working just fine, I was very curious to know what made this one spec so special.
In my case, it turned out the spec file name, for whatever reason, was given the same file name as the helper module itself. When trying to load the constant it was looking in the spec file, since it had taken the place of "my_helper" and in turn ignoring the actual module. Adding a _spec at the end of the spec file name allowed this error to go away, and that spec ran as intended after.
I know this is a simple issue, but if you have hundreds upon hundreds of spec files, you may not be constantly looking at the file names.

Uninitialized constant inside controller namespace

I'm trying to solve the following issue but I just can't get my head around the autoloading in Rails yet. I have the following controller located in app/api/v1/registration_controller.
class Api::V1::RegistrationController < ApplicationController
include ::Foo::Bar
def index
end
end
And the following module/class in app/foo/bar.rb:
module Foo
class Bar
def some_method
end
end
end
However, I'm getting the following error message:
uninitialized constant Foo
I'm trying to get my head around this and already searched Stack Overflow for solutions, but I wasn't able to find the right one. If the solution is already written down elsewhere, please close this topic.
Hopefully someone can help me. Thanks!
The Rails autoloader searches a number of paths to try to locate constants - app/ isn't one of them. You can add it by appending it in application.rb, like:
config.autoload_paths << File.join(config.root, "app")
Or just move foo/bar.rb into lib/, which is already in the loader path.

Bug in rails autoload_paths?

I am encountering a strange bug in my code. I have a rails application with the following two files in the lib:
lib/module_one/module_two/class_one.rb
module ModuleOne
module Moduletwo
class ClassOne
class << self
def test
puts 'Class one'
ClassTwo.test
end
end
end
end
end
and
lib/module_one/module_two/class_two.rb
module ModuleOne
module ModuleTwo
class ClassTwo
def self.test
puts 'Class two'
end
end
end
end
Now my problem is, that when I go into the console and write:
ModuleOne::ModuleTwo::ClassOne.test
it throws the following: NameError: uninitialized constant ClassTwo
The strange thing is, that the problem seems to be connected to the use of class << self instead of self.method. If I change the class_one.rb file like this it works!:
module ModuleOne
module ModuleTwo
class ClassOne
def self.test
puts 'Class one'
ClassTwo.test
end
end
end
end
Im loading the files in application.rb like this:
config.autoload_paths += %W(#{config.root}/lib)
Is this a bug in rails, or is it just me getting something all wrong?
Im using rails 3.1.3 btw
Constant lookup
So first off, the basic process for constant resolution is that ruby first searches the lexical scope of the receiver (the class or module enclosing the reference) for ClassTwo and when it can't find it, it goes up a level (Module.nesting returns this search path) and so on. In your case that means looking for ModuleOne::ModuleTwo::ClassOne:ClassTwo, then ModuleOne::ModuleTwo::ClassTwo, then ModuleOne::ClassTwo and so on.
If that fails the ruby looks in the inheritance hierarchy of the enclosing class/module (e.g. has the superclass of ClassOne defined something. The ancestors method on module returns this search path. Lastly, the top level constants are searched.
Rails autoloading
Back to rails's magic loading. Here a const_missing hook is added by rails that is called when ruby can't find the class, which basically tries to replicate this search logic, seeing at each step whether a file could have been loaded which would contain the missing constant.
Ideally ruby would pass the search path (i.e. the nesting) to search through, but unfortunately it doesn't: when you reference ClassTwo, const_missing gets called with just 'ClassTwo'.
Rails guesses the nesting by prepending that with the name of the class on which const_missing is being called (i.e. the class enclosing the access to the constant). For example, in your second example it ends up with ModuleOne::ModuleTwo::ClassOne::ClassTwo. You can see this easily enough by defining const_missing to log what it's called with
class Object
def self.const_missing missing_name
puts "qualified name is #{self.name}::#{missing_name}"
super
end
end
Rails then strips off 'ClassOne' and tries ModuleOne::ModuleTwo::ClassTwo and so on up the chain.
So why does class << self make a difference? If you repeat your first case with the const_missing logging you'd see that the logged qualified name is now just ::ClassTwo. const_missing is now being called on ClassOne's metaclass, and because class << self hasn't been assigned to a constant, it has no name and thus rails' attempt to fudge the nesting doesn't work.
This opens the door to a horrible workaround:
module ModuleOne
module ModuleTwo
class ClassOne
class << self
def test
puts 'Class one'
ClassTwo.test
end
end
FOO = class << self; self; end
end
end
end
Because the class that const_missing gets called on now has a name (ModuleOne::ModuleTwo::ClassOne::FOO) rails' workaround now works.
Dave's workaround works I think because const_missing gets called on ModuleOne::ModuleTwo::ClassOne rather than the anonymous eigenclass/metaclass.
The real fix would be for ruby to pass const_missing a nesting. There is a bug logged against ruby to this effect although it has been open for a long time. So yes, this could be considered a bug in the magic loading stuff (there are other edge cases) but the underlying reason is a weakness in the ruby api that forces the use of brittle workarounds.
(Only a partial answer, but need formatting.)
It's because of how class << self works.
For example, if you change it to:
class << self
def test
self::ClassTwo.test
end
end
it works fine.
Edit; too long for reasonable comment.
I'm poking around a bit... On an intuitive level it makes sense to me, I'm just not sure why yet. Don't know if I knew a real reason once, or if I'm just making it up.
I'm not sure why self seems to refer to the module, though; the "Programming Ruby 1.9" book doesn't go in to enough depth on the class << semantics. I'll tweet something and refer to this question and someone smarter will create a real answer.

Rails: I can't call a function in a module in /lib - what am I doing wrong?

I have a module saved in /lib as test_functions.rb that looks like this
module TestFunctions
def abc
puts 123
end
end
Going into ruby script/runner, I can see that the module is loading automatically (good ol' convention over configuration and all that...)
>> TestFunctions.instance_methods
=> ["abc"]
so the method is known, let's try calling it
>> TestFunctions.abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):3
Nope. How about this?
>> TestFunctions::abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):4
Test
Nope again.
defined?(TestFunctions::abc) #=> nil, but
TestFunctions.method_defined? :abc #=> true
Like I said at the top, I know I'm being dumb, can anyone de-dumb me?
If you want Module-level functions, define them in any of these ways:
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
end
All of these ways will make the methods available as Foo.method_one or Foo::method_one etc
As other people have mentioned, instance methods in Modules are the methods which are available in places where you've included the Module
I'm going to try to summarise the various answers myself, since each had something valuable to say, but none really got to what I now realise is probably the best response:
I was asking the wrong question because I was doing it wrong.
For reasons I can no longer explain, I wanted a set of completely stand-alone functions in a library, which represented methods I was trying to DRY out of my classes. That can be achieved, using things like
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
def method_four
end
module_function :method_four
end
I could also include my module, either within a class, in which case the methods become part of the class or outside, in which case they are defined on whatever class I'm running inside (Object? Kernel? Irb, if I'm interactive? Probably not a great idea, then)
The thing is, there was no good reason not to have a class in the first place - I'd somehow got on to a train of thought that took me down an seldom-used and frankly slightly weird branch line. Probably a flashback to the days before OO became mainstream (I'm old enough that up to today I've spent a lot more years writing procedural code).
So the functions have moved into a class, where they seem pretty happy, and the class methods thus exposed are being cheerfully used wherever necessary.
You can also use module_function like so:
module TestFunctions
def abc
puts 123
end
module_function :abc
end
TestFunctions.abc # => 123
Now you can include TestFunctions in class and call "abc" from within TestFunctions module.
I messed with this for a while and learned several things. Hopefully this will help someone else out. I am running Rails 3.2.8.
My module (utilities.rb) looks like this and is in the /lib directory of my rails app:
module Utilities
def compute_hello(input_string)
return "Hello #{input_string}"
end
end
My test (my_test.rb) looks like this and is in the /test/unit directory of my rails app:
require "test_helper"
require "utilities"
class MyTest < ActiveSupport::TestCase
include Utilities
def test_compute_hello
x = compute_hello(input_string="Miles")
print x
assert x=="Hello Miles", "Incorrect Response"
end
end
Here are a few things to note: My test extends ActiveSupport::TestCase. This is important because ActiveSupport adds /lib to the $LOAD_PATH. (seehttp://stackoverflow.com/questions/1073076/rails-lib-modules-and)
Secondly, I needed to both "require" my module file, and also "include" the module. Lastly, it is important to note that the stuff that gets included from the module essentially gets placed in the test class. So... be careful that the module that you include doesn't start with "test_". Otherwise, Rails will attempt to run your module method as a test.
You can't call a method in a Module directly. You need to include it in a class. Try this:
>> class MyTest
>> include TestFunctions
>> end
=> MyTest
>> MyTest.new.abc
123
=> nil
You need to include the module
include Testfunctions
Then 'abc' will return something.
You need to prefix your function with the module name because modules are not classes:
Your /lib/test_functions.rb:
module TestFunctions
def TestFunctions.abc
puts 123
end
end
Your code using the module method:
require 'test_functions'
TestFunctions.abc
Today you can do it using module_function notation.
module TestFunctions
def abc
puts 123
end
end
Now TestFunctions.abc prints "123"
A little more about module_function: https://apidock.com/ruby/Module/module_function
Try this code:
service = Class.new { extend TestFunctions }
service.method_four

Resources