I user namespace to create some module in Rails. It works fine in controllers, models, but something is wrong with presenters which are in a presenters path.
This is one of presenters, without namespace:
class MainPresenter < Struct.new(:main, :current_user)
extend Ext::CollectionPresenter
def as_json
{
something: SomeNamespace::SomePresenter(main.something)
}
end
end
And this is the presenter in presenters/some_namespace/some_presenter.rb
class SomeNamespace::SomePresenter < Struct.new(:something, :options)
extend Ext::CollectionPresenter
def as_json
# some hash here
end
end
I get undefined method 'SomePresenter' for SomeNamespace:Module error. What could be the problem.
SomePresenter is a class and you are using it as a method. Hence, the error.
Use it as below :
something: SomeNamespace::SomePresenter.new(main.something)
Related
Trying to make available the methods I have stored in a Module which is located in app/models (side note: not sure if this is the correct place for modules?).
Module:
module MyModule
class MyClass
def some_method
# do something
end
end
end
User Model:
class User < ApplicationRecord
include MyModule
def another_method
some_method
end
end
I am getting a NoMethodError:
NoMethodError (undefined method 'some_method' for #<User:0x00007f6a3ce452c0>
You seem to have missunderstood what what modules and classes do in Ruby. In Ruby a module is simply an object that wraps a set of methods and constants.
A module can extend other modules, classes and objects and can be included in classes thus implementing multiple inheritance. Modules in Ruby fill the role that traits, namespaces and singletons do in other languages.
Classes are actually modules (Module is part of the ancestors chain of Class) with the key difference that you can make instances of a class and that class can inherit from a single other class and cannot extend other objects or be included.
The code example here actually doesn't make sense. If you want to declare a method that will be available to classes that include a module you want to declare it in the module itself:
module MyModule
def some_method
# do something
end
end
When you then call User#another_method it will look in the ancestors chain of the User class until it finds the method which is defined in MyModule.
module MyModule
class MyClass
def some_method
# do something
end
end
end
Will actually definte the class MyClass with an instance method that is only available to instances of MyClass. The only thing that the module does here is change the module nesting so that the class is defined in MyModule instead of the global namespace.
If you want to mix in a method from a method into your class then just put the methods directly in the module (without an intermediate class).
Module:
module MyModule
def some_method
# do something
end
end
User Model:
class User < ApplicationRecord
include MyModule
def another_method
some_method
end
end
Have a look at this answer, you need to instantiate your Class first. Or if you want to
class User < ApplicationRecord
include MyModule
def another_method
my_instance = MyClass.new
my_instance.some_method
end
end
As for a place to store your Module, have a look at this guide about service objects, it gave me some inspiration when it comes to different modules.
I have the following classes in my rails 4 app:
class ReceiptDocument < ActiveRecord::Base
#foo
self.inheritance_column = :document_type
has_and_belongs_to_many :donations
protected
def self.inherited(subklass)
subklass.inherit_attributes(#foo)
end
def self.inherit_attributes(foo)
#foo = foo
end
end
class ReceiptRequest < ReceiptDocument; end
class ReceiptResult < ReceiptDocument; end
The class instance variable #foo is set once at class definition, and should have the same value in all subclasses. I added the inherited override so that value would be accessible from both ReceiptRequest and ReceiptResult.
However, now when I call ReceiptRequest.new, I get:
pry(main)> DonationReceiptRequest.new
NoMethodError: undefined method `[]' for nil:NilClass
from /gems/activerecord-4.1.5/lib/active_record/relation/delegation.rb:9:in `relation_delegate_class'
If I comment out that override, things return to normal. However, I can no longer share that value across all subclasses of ReceiptDocument.
Any insight would be greatly appreciated.
Finally got this sorted.
The error seemed to be pointing me at some behind the scenes work that ActiveRecord was doing, so I dug in a bit further.
It seems that setting self.inheritance_column most likely overrides the inherited method. Honestly, I'm not sure what work is being done that my override was stomping on. But redefining that method as below fixed the issue:
def self.inherited(subklass)
super # <--
subklass.inherit_attributes(#foo)
end
I'm trying to call a method in one controller helper (a module) from another controller helper. It seems to be not possible, even if that method is under the module_function.
I guess I'm missing a fundamental principle in Ruby since I'm pretty newbie. Also it feels like I'm missing the point of how to write right OOP under Rails.
Update: here is an example:
I have FirstController and SecondController, and helper module for each
module FirstHelper
module_function
def methodA
...
end
end
module SecondHelper
def methodB
FirstHelper.methodA
end
end
The call for FirstHelper.methodA from SecondHelper is returning an error:
undefined method `methodA' for SecondHelper:Module
A module is a collection of methods and constants. It basically provides a namespace and prevents name clashes. You need to include or extend your First module inside your Second module.
Include is for adding methods to an instance of a class and Extend is for adding class methods. Read this for more information or this. In your case you can do something like this:
module FirstHelper
def self.methodA
...
end
end
module SecondHelper
include FirstHelper
def methodB
FirstHelper.methodA
end
end
Helper methods are instance methods and cannot be accessed via module, but only vie classes they are included in. All the helpers are included within the view context object, so you should be able to access them simply by name:
module SecondHelper
def methodB
methodA
end
end
use require instead of include it will work
module FirstHelper
class << self
def methodA
...
end
end
end
require 'lib/first_helper'
module SecondHelper
def methodB
FirstHelper.methodA
end
end
I have a small problem that I can't quite get my head around. Since I want to reuse a lot of the methods defined in my Class i decided to put them into an Helper, which I can easily include whenever needed. The basic Class looks like this:
class MyClass
include Helper::MyHelper
def self.do_something input
helper_method(input)
end
end
And here is the Helper:
module Helper
module MyHelper
def helper_method input
input.titleize
end
end
end
Right now I can't call "helper_method" from my Class because of what I think is a scope issue? What am I doing wrong?
I guess that is because self pointer inside of do_something input is InternshipInputFormatter, and not the instance of InternshipInputFormatter. so proper alias to call helper_method(input) will be self.helper_method(input), however you have included the Helper::MyHelper into the InternshipInputFormatter class as an instance methods, not a singleton, so try to extend the class with the instance methods of the module as the signelton methods for the class:
class InternshipInputFormatter
extend Helper::MyHelper
def self.do_something input
helper_method(input)
end
end
InternshipInputFormatter.do_something 1
# NoMethodError: undefined method `titleize' for 1:Fixnum
As you can see, the call has stopped the execution inside the helper_method. Please refer to the document to see the detailed difference between include, and extend.
I've got a module in my project in lib/. it's content is like this :
module Search
module Score
def get_score
return 'something'
end
end
end
This Search has many different modules I need to use Score. I realize I need to add require in my model (I'm trying to use this from model). So here is my code (model) :
require 'search'
class User < ActiveRecord::Base
def get_user_score
#tried this :
p Search::Score.get_score #error
#this as well
score_instance = Score.new #error
score = Search::Score.get_score # error undefined method `get_score'
end
end
So how do I reuse the code I have in other class (module)?
To get it working you can either mix the module into your class:
require 'search'
class User < ActiveRecord::Base
include Search::Score
def get_user_score
p get_score # => "something"
end
end
Or you can define the method inside your module similar to class methods:
module Search
module Score
def self.get_score
return 'something'
end
end
end
If you do that, you can call get_score like expected:
require 'search'
class User < ActiveRecord::Base
def get_user_score
p Search::Score.get_score # => "something"
end
end
See this tutorial for a more in depth explanation about modules in Ruby.
First, see "Best Practices for reusing code between controllers in Ruby on Rails".
About reuse code as a module, take a look at "Rethinking code reuse with Modularity for Ruby".
"Modules are crippled classes"
Modules are like crippled classes in Ruby. If you look into the inheritance chain you see that a Class actually inherits from Module.
Module cannot be instanciated. So the call to .new is not working.
What you CAN do however is to specify your method as a 'class' method (I know I said it is not a class...)
So you would add a self in front like this:
module Search
module Score
def self.get_score
return 'something'
end
end
end
Then you can call this method as a class method like you tried in your code example
Search::Score is a module and not a class, so Score.new will not work.
You can try to change the signature of the get_score function to self.get_score.
In addition to def self.get_score in the above answers, there is also extend self, like so:
module Search
module Score
extend self
def get_score
return 'something'
end
end
end
and module_function:
module Search
module Score
module_function
def get_score
return 'something'
end
end
end
The latter is actually the preferred method in RuboCop (source), though in practice I personally have not seen it so often.