I have a Rails app that repeatedly talks to another Web server through a wrapper, and I'd like to stick the wrapper in a Singleton class so it's not recreated for every request. Easy enough, I thought:
class AppWrapper < Wrapper
include Singleton
end
...
wrapper = AppWrapper.instance "url"
Only it doesn't work:
wrong number of arguments (0 for 1)
/usr/lib/ruby/1.8/singleton.rb:94:in `initialize'
/usr/lib/ruby/1.8/singleton.rb:94:in `new'
/usr/lib/ruby/1.8/singleton.rb:94:in `instance'
Wrapper.initialize needs an argument, and apparently it's not getting passed through, since line 94 in question says
#__instance__ = new # look Ma, no argument
How do I work around this? Redefining initialize in AppWrapper doesn't seem to help, and
mucking around with Wrapper to separate "set URL" from "initialize" seems suboptimal.
Passing argument to singleton
class Parameterized_Singleton
def initialize(a)
#pdf = a
puts #pdf
end
def self.instance(p)
begin
##instance =Parameterized_Singleton.new(p)
private_class_method :new
rescue NoMethodError
# return ##instance # or you can return previous object
puts "Object Already Created"
exit
end
return ##instance
end
def scanwith(b)
puts "scan"
end
def show_frequence_distribution
puts "fd"
end
def show_object_number(a)
puts "no"
end
end
Parameterized_Singleton.instance(20).show_object_number(10)
Parameterized_Singleton.instance(10).show_object_number(20)
Are you sure you need a singleton and not a factory . Refer this
I asked this question while I was still getting my head around Ruby, and it seems so naive now. The easy solution is to just store the Wrapper object in a member variable and use ||= to initialize it only if it hasn't been set yet:
class WrapperUserClass
def initialize
#wrapper = nil # Strictly speaking unnecessary, but it's a bit clearer this way
end
def wrapper
#wrapper ||= Wrapper.new(foobar)
end
def do_something
wrapper.booyakasha
end
end
Since you mention something about editing Wrapper as a solution, can't you just use Wrapper directly and do this?
class Wrapper; include Singleton; end
If not, you could use something like this, which will just make sure AppWrapper.new isn't called more than once:
class AppWrapper
def self.new(*args)
class << app_wrapper = Wrapper.new(*args)
include Singleton
end
app_wrapper
end
end
If you need the singleton "Klass.instance" method, you'll have to take either take out the parameter in Wrapper#initialize, or just redefine Singleton#instance to take arguments optionally and passes them to the call to new on line 94.
Related
In a Rails controller you can pass a symbol to the layout method that corresponds to a method in you controller that will return the layout name like this:
layout :my_method
def my_method
'layout_1'
end
I want to have a similar functionality to likewise pass a symbol to my classes method and that class should call the corresponding function and use its return value, like this
myClass.foo :my_method
def my_method
'layout_1'
end
I've read posts[1] that tell me I need to pass
myClass.foo(method(:my_method))
which I find ugly and inconvenient. How is rails here different allowing to pass just the symbol without any wrapper? Can this be achieved like Rails does it?
[1] How to implement a "callback" in Ruby?
If you want to only pass a :symbol into the method, then you have to make assumptions about which method named :symbol is the one you want called for you. Probably it's either defined in the class of the caller, or some outer scope. Using the binding_of_caller gem, we can snag that information easily and evaluate the code in that context.
This surely has security implications, but those issues are up to you! :)
require 'binding_of_caller'
class Test
def foo(sym)
binding.of_caller(1).eval("method(:#{sym})").call
end
end
class Other
def blork
t = Test.new
p t.foo(:bar)
p t.foo(:quxx)
end
def bar
'baz'
end
end
def quxx
'quxx'
end
o = Other.new
o.blork
> "baz"
> "quxx"
I still don't understand, what is author asking about. He's saying about "callbacks", but only wrote how he wants to pass parameter to some method. What that method(foo) should do - i have no idea.
So I tried to predict it's implementation. On class initialising it gets the name of method and create private method, that should be called somewhere under the hood. It possible not to create new method, but store method name in class variable and then call it somewhere.
module Foo
extend ActiveSupport::Concern
module ClassMethods
def foo(method_name)
define_method :_foo do
send method_name
end
end
end
end
class BaseClass
include Foo
end
class MyClass < BaseClass
foo :my_method
private
def my_method
"Hello world"
end
end
MyClass.new.send(:_foo)
#=> "Hello world"
And really, everything is much clearer when you're not just wondering how it works in rails, but viewing the source code: layout.rb
Sorry if this is too simple. I'm looking for a way to make my ruby code dry : I want to call a number of methods on the same instance variable #var = Model.new(param) :
#var.method1
#var.method2
#var.method3
...
Is it possible to use the send method to write one line of code ? Btw, is it possible to call a block on Model.new to produce some more concise code ?
I believe that DRY should be used to make your code more maintainable, and more readable. I don't think it should be used to shorten the number of characters you type, or show-off your code acrobatics.
Both #Arup's and #p11y's solutions are great, within a context, but as a general rule (before knowing anything about your class or methods), I believe that writing
#var.method1
#var.method2
#var.method3
is more readable and maintainable than writing either
%i[method1 method2 method3].each(&#var.method(:send))
(you need to be fluent in advanced ruby to understand this)
or
#var.method1
.method2
.method3
(again the vanishing act is more confusing to the future reader than helpful)
Always think about who will read your code in 6 months, and what will be the clearest way for him to understand what's happening.
If you build method1, method2, etc. such that they return the instance itself using self, you can build a chainable interface. For example:
class Foo
def method1
# do something
self
end
def method2
# do something
self
end
def method3
# do something
self
end
# more methods...
end
#var = Foo.new
#var.method1.method2.method3
# or if this gets too long
#var.method1
.method2
.method3
Do as below :
%i[method1 method2 method3].each { |m| #var.send(m) }
If you want to make it more short,use :
%i[method1 method2 method3].each(&#var.method(:send))
When I wrote my original answer, I missed the last sentence in your question:
Btw, is it possible to call a block on Model.new to produce some more concise code ?
And the answer to this question is YES. This pattern is a builder pattern, which is implemented in several gems in ruby (such as tire).
The pattern states that the initialize method receives a block, which is run in the context of the created object, using instance_eval. If the block receives a parameter, the instance object is passed to it instead of changing the block's scope:
class Model
def initialize(name, &block)
#name = name
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
end
def method1
# something
end
def method2
# something
end
def method3
# something
end
end
And its usage will be something either like this:
#var = Model.new('model') do
method1
method2
method3
end
or, alternatively:
#var = Model.new('model') do |m|
m.method1
m.method2
m.method3
end
What i stumbled upon just now - how to easily limit verity of classes that are passed to one method to only one class type ? ex. code:
class S
attr_reader :s
def initialize(s = nil)
#s = s || 14
end
end
class Gets
def self.read(s)
s.s
end
end
s=S.new
p Gets.read(s) # 14
Let's say that class S has a more complex structure and i want to be sure that only that class could be passed to Gets#read method, how can i do that?
While to solution of sawa definitely is valid and does exactly what you want. In dynamic languages like ruby it's actually more common to use duck typing.
The idea is to just assert to which messages the attribute must respond to. This allows to easily pass in e.g. a different implementation.
class Gets
def self.read(obj)
raise ArgumentError, "must implement #s" unless obj.respond_to?(:s)
obj.s
end
end
class Gets
def self.read(s)
raise ArgumentError unless s.kind_of?(S)
s.s
end
end
Given the Thread class with it current method.
Now inside a test, I want to do this:
def test_alter_current_thread
Thread.current = a_stubbed_method
# do something that involve the work of Thread.current
Thread.current = default_thread_current
end
Basically, I want to alter the method of a class inside a test method and recover it after that.
I know it sound complex for another language, like Java & C# (in Java, only powerful mock framework can do it). But it's ruby and I hope such nasty stuff would be available
You might want to take a look at a Ruby mocking framework like Mocha, but in terms of using plain Ruby it can be done using alias_method (documentation here) e.g.
beforehand:
class Thread
class << self
alias_method :old_current, :current
end
end
then define your new method
class Thread
def self.current
# implementation here
end
end
then afterwards restore the old method:
class Thread
class << self
alias_method :current, :old_current
end
end
Update to illustrate doing this from within a test
If you want to do this from within a test you could define some helper methods as follows:
def replace_class_method(cls, meth, new_impl)
cls.class_eval("class << self; alias_method :old_#{meth}, :#{meth}; end")
cls.class_eval(new_impl)
end
def restore_class_method(cls, meth)
cls.class_eval("class << self; alias_method :#{meth}, :old_#{meth}; end")
end
replace_class_method is expecting a class constant, the name of a class method and the new method definition as a string. restore_class_method takes the class and the method name and then aliases the original method back in place.
Your test would then be along the lines of:
def test
new_impl = <<EOM
def self.current
"replaced!"
end
EOM
replace_class_method(Thread, 'current', s)
puts "Replaced method call: #{Thread.current}"
restore_class_method(Thread, 'current')
puts "Restored method call: #{Thread.current}"
end
You could also write a little wrapper method which would replace a method, yield to a block and then ensure that the original method was reinstated afterwards e.g.
def with_replaced_method(cls, meth, new_impl)
replace_class_method(cls, meth, new_impl)
begin
result = yield
ensure
restore_class_method(cls, meth)
end
return result
end
Inside your test method this could then be used as:
with_replaced_method(Thread, 'current', new_impl) do
# test code using the replaced method goes here
end
# after this point the original method definition is restored
As mentioned in the original answer, you can probably find a framework to do this for you but hopefully the above code is interesting and useful anyway.
I'm having trouble removing some duplication I've introduced in a rails plugin.
The code below modifies the find and calculate methods of ActiveRecord in the same way, but I've been unable to remove the duplication.
The find and calculate methods below make use of the super keyword which is one hurdle as the super keyword can only be used to call a method sharing the same name as the calling method, so I can't move the super keyword to a method shared by find and calculate.
So next I tried aliasing the find and calculate class methods from the superclass ActiveRecord, however, I've not been able to get the syntax right for the aliasing. If someone could show me that, it would be a great help.
If you've got a better way entirely of doing this I'd love for you to post that too.
Below I've trimmed the code down a little to highlight the problem:
module Geocodable #:nodoc:
def self.included(mod)
mod.extend(ClassMethods)
end
module ClassMethods
def acts_as_geocodable(options = {})
extend Geocodable::SingletonMethods
end
end
module SingletonMethods
def find(*args)
some_method_1
super *args.push(options)
some_method_2
end
# TODO: Remove duplication of find above and calculate below.
def calculate(*args)
some_method_1
super *args.push(options)
some_method_2
end
end
end
Your best way to refactor this code is to leave find and calculate unchanged, and add apply the wrapping using a class-level function.
Here's rough sketch, without your module and mixin logic:
class A
def find x
puts 'finding'
end
def calculate x
puts 'calculating'
end
end
class B < A
def self.make_wrapper_method name
define_method name do |*args|
puts "entering"
result = super *args
puts "exiting"
result
end
end
make_wrapper_method :find
make_wrapper_method :calculate
end
Note that this will need to be modified if B has already overridden find or calculate.
To use this code, first make your version work correctly, then modify it to use define_method. (And if you need extremely high performance, you may need to use one of the *_eval functions to create the wrappers instead of define_method.)
This is the option I went for in the end, thanks to emk for guidance to get to this point!
module Geocodable
def self.included(mod)
mod.extend(ClassMethods)
end
module ClassMethods
def acts_as_geocodable(options = {})
geoify_query_methods
end
private
# This is where the duplication has been removed
def geoify_query_methods
class << self
[:calculate, :find].each do |method_name|
define_method method_name do |*args|
some_method_1
super *args.push(options)
some_method_2
end
end
end
end
end
end
To just alias the find method:
module SingletonMethods
def find(*args)
some_method_1
super *args.push(options)
some_method_2
end
alias :calculate :find
end