I'm working on rails app (3.2.11), and i'm implementing services as singleton, using the Ruby Library's one. I was trying to avoid calling :instance method every time I need it (SomethingService.instance.get(some_id)), solving my problem like this
class SomethingService
include Singleton
class << self
extend Forwardable
def_delegators :instance, *ShoppingListService.instance_methods(false)
end
end
So this solution was working perfectly fine but i've got a lot of services, and i don't want to add this code to all my classes! Instead i was trying to put in in a super class like this :
class ServiceBase
include Singleton
def self.inherited(subclass)
super(subclass) # needed because inherited is override by Singleton
class << subclass
extend Forwardable
def_delegators :instance, *self.instance_methods(false)
end
end
end
But this is giving me a stack level too deep error... Any ideas guys?
It's probably a better idea to use method_missing here. def_delegators is executed when the class is evaluated, and may happen before your methods are even defined, simply because you are inheriting from that base class.
You could try something like this instead, which forwards any undefined message onto the instance:
class ServiceBase
class << self
def method_missing(name, *args, &block)
instance.send(name, *args, &block)
end
end
end
It may look like a bit of a scattershot approach when compared to delegation. You could do a check to see if the instance method exists first, but I don't think that's necessary - you're simply transferring the undefined method handling to the instance rather than the class.
One final point/question - can you elaborate on what benefit you get from the Singleton class in the first place?
Related
The question can be stupid, but I always develop in C# and now I need to develop in Ruby.
And I don't really understand how to call a method from an another class.
I mean, I've this structure :
- model
|_________ my_model.rb
|_________ helper
|____ my_helper_class
my_model.rb
def self.create_new_ticket(member_to_update)
# I want to call here my_helper_class
MyHelperClass.generate_guid
end
my_helper_class :
class MyHelperClass
def generate_guid
return "So haaard"
end
end
And I don't have access to my method named generate_guid from my other class.
I've this type of error :
uninitialized constant
I would like to have an access with a static class or whatever. The initilize method doesn't work too (given argument problem ??)
So I think I understand bad something with Ruby and the manipulation of objects because of my habits in C#.
Can you help me please ? With some good documentations or an example here ?
Thanks a lot guys.
I think your error is straightforward. Your generate_guid is not a "static method" or "class method". You have to put self in front of it in the definition which will make it a class method.
Another important thing to notice is you have created /helper folder inside /model so you have to implement helper class inside a module named Helper which should be same as folder name.
Helper class should be
module Helper
class MyHelperClass
def self.generate_guid
return "So haaard"
end
end
end
An alternate way to define class methods would be:
module Helper
class MyHelperClass
class << self
def generate_guid
"So haaard"
end
def some_other_class_method
"some thing"
end
end
end
end
So, whenever you have to call static method you have to call it with full scope like Helper::MyHelperClass.generate_guid
I have a class in initializers in which I use Hash class and I would like to add 2 methods to Hash class. I know how to add methods to the class but I don't want to make the Hash class "dirty".
Is there a way that I can extend the Hash class with those two methods but only inside the class where I use them?
You could use refinements for this:
Due to Ruby's open classes you can redefine or add functionality to existing classes. This is called a “monkey patch”. Unfortunately the scope of such changes is global. All users of the monkey-patched class see the same changes. This can cause unintended side-effects or breakage of programs.
Refinements are designed to reduce the impact of monkey patching on other users of the monkey-patched class. Refinements provide a way to extend a class locally. Refinements can modify both classes and modules.
Something like this:
module HashPatches
refine Hash do
def new_hash_method
# ...
end
end
end
and then:
class YourClass
using HashPatches
def m
{}.new_hash_method
end
end
That would let you call YourClass.new.m (which would use new_hash_method) but it wouldn't pollute Hash globally so outside YourClass, some_hash.new_hash_method would be a NoMethodError.
Reading:
Official Refinements docs
Refinements spec
A less hacky way could be to use SimpleDelegator.
class Foo
class SuperHash < SimpleDelegator
def new_method
# do something with hash
# you can use __getobj__() or super
end
end
private_constant :SuperHash
def initialize
#hash = SuperHash.new({})
end
end
https://ruby-doc.org/stdlib-2.5.1/libdoc/delegate/rdoc/SimpleDelegator.html
I need to know if I can include a module to an instantiated model.
What works today :
in the controller
#m = MyModel.create(params)
in the model
class Doc < ActiveRecord::Base
after_save :set_include
def set_include
if bool
self.class.send(:include, Module1)
else
self.class.send(:include, Module2)
end
end
end
and this works, but I'm afraid that self.class actually include the module for the class model an not the instantiated model
In this case, this will work.
The module methods are call after the object is saved.
But in many case, the controller will call some modules methods.
I thought of called the method set_include (up there) in a before_action of the controller.
But I really thinks that is not a good idea...
Any idea how I can really do that with in a good way ?
thanks !
Answer to your direct question is no. Your code only appears to be working and is actually not modifying instance of a class, but the class itself. So all instances of it will be getting this "benefit". Probably not what you wanted. Let me demonstrate with simple ruby example: https://repl.it/BnLO
What you can do instead is use extend with instance like: https://repl.it/BnLO/2
Applied to your code it would be:
class Doc < ActiveRecord::Base
after_save :set_include
def set_include
if bool
extend(Module1)
else
extend(Module2)
end
end
end
Also, self is not necessary. https://repl.it/BnLO/3
You need to use instance class (a.k.a eigenklass):
def set_include
singleton_class.instance_eval do
include bool ? Module1 : Module2
end
end
However the fact that you want to do this is suspicious and might lead to a disaster. So the question is: what are you really trying to achieve here - there surely is the better way of doing so.
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.
Background here.
In the above link, the following example is given:
class << self
def by_author(author)
where(:author_id => author.id)
end
end
Aside from that syntax being foreign to a beginner like me — I had always thought class methods were defined with def self.my_class_method — where can I find documentation about class methods in Ruby on Rails?
As far as I know, class methods are always called on the class itself (MyClass.my_class_method), but if class methods in Rails are chainable, it seems as though something else must be going on here!
Edit:
I suppose I sort of cheated by making that comment about the syntax for class methods. I'm really asking how Rails makes a class method chainable — I understand how method chaining works, but not how Rails can allow you to chain class methods without actually returning the class object itself after each "link" in the chain.
Class methods in Ruby are really just members of the singleton class, and doing class << self involves opening the singleton class directly and adding to it, removing the need to declare it in each method definition.
This article on Ruby singletons does a good job explaining.
As far as class methods being chainable, that isn't something specific to class methods, the second method call is simply called on the object returned from the first. For example:
bar = foo.do_something.do_more
is equivalent to:
tmp = foo.do_something
bar = tmp.do_more
In Rails, this chainability is most often used for building SQL queries (e.g., with where or order, etc.). This is achieved because each of these methods returns an ActiveRecord Relation.
The reason
foo.scoped.my_foo_class_method
works is because of ActiveRecord::Relation#method_missing doing the following:
elsif #klass.respond_to?(method)
scoping { #klass.send(method, *args, &block) }
Which checks if the ActiveRecord class responds to the method called, and if so, calls that.
Having class << self is also another way to define your methods so that you do not have to call "def self.my_method" or "def MyClass.my_method" for every single method that you are defining as a class method. Instead of calling
def self.my_method1
end
def self.my_method2
end
class << self
def my_method1
end
def my_method2
end
end
Cheers!
The following two bits of code are equivalent.
Using self.method:
class Hello
def self.world
puts "Hello, World!"
end
end
Using class << self:
class Hello
class << self
def world
puts "Hello, World!"
end
end
end
The only difference is readability, as well as the ease in refactoring.
The class << self technique is often used when metaprogramming.
There is another thread that explains this. class << self vs self.method with Ruby: what's better?