Returning dynamically generated module from a method - ruby

I have a bunch of classes with similiar logic like this
class ApiWrapper
class << self
attr_accessor :app_id, :app_key
def configure
yield self
end
end
end
I want to extract this logic to a module similar to Ruby Struct class to be able to do something like this
class ApiWrapper
include Configurable.instance :app_id, :app_key
end
How can I do this?

From documentation
fred = Module.new do
def meth1
"hello"
end
def meth2
"bye"
end
end

Related

How to best abstract class variables in Ruby

I've got a class such as
# this has been simplified for the example
class MyClass
##private_attributes = []
def self.private_attributes(*args)
##private_attributes = args
end
def private_attributes
#private_attributes ||= ##private_attributes
end
end
It works great. I've this ##private_attributes set at class level which's then used at instance level in multiple ways.
I want to abstract this logic somewhere else to simplify my class, something like that
class MyClass
include PrivateAttributes
end
When I create the module PrivateAttributes, however I shape it, the ##private_attributes isn't understood at MyClass level.
I tried many things but here's the latest code attempt
module PrivateAttributes
include ProcessAttributes
def self.included(base)
base.extend(ClassMethods)
base.include(InstanceMethods)
end
module ClassMethods
##private_attributes = []
def private_attributes(*args)
##private_attributes = args
end
end
module InstanceMethods
def private_attributes
#private_attributes ||= process_attributes_from(##private_attributes)
end
def private_attributes?
instance_options[:scope] == :private
end
end
end
It crashes with this error
NameError:
uninitialized class variable ##private_attributes in PrivateAttributes::InstanceMethods
Did you mean? private_constant
In short, the ##private_attributes isn't transferred throughout the code, but looks like it stays at the module level.
What's the best way to abstract this logic from my original class ?
Working solution
An easy workaround is to use mattr_accessor on the class level or anything similar to communicate our data around. I preferred to write down my own methods in this case:
module PrivateAttributes
include ProcessAttributes
def self.included(base)
base.extend(ClassMethods)
base.include(InstanceMethods)
end
module ClassMethods
##private_attributes_memory = []
def private_attributes(*args)
##private_attributes_memory = args
end
def private_attributes_memory
##private_attributes_memory
end
end
module InstanceMethods
def private_attributes
#private_attributes ||= process_attributes_from private_attributes_memory
end
# you can add diverse methods here
# which will be used in MyClass once included
private
def private_attributes_memory
self.class.private_attributes_memory
end
end
end

How to define an original name scope in module/class with Ruby

How to define an original name scope in module/class with Ruby
I want to implement class like the following:
module SomeModule
extend OriginalNameScope
scope(:some) do
def method1
puts 1
end
def method2
puts 2
end
end
end
class SomeClass
include SomeModule
end
c = SomeClass.new
# I want to call methods like the following:
c.some_method1
c.some_method2
How to implement the OriginalNameScope module? I found out to get the method definitions in this method, but I don't know how to redefine methods with a prefix scope.
module OriginalNameScope
def scope(name, &method_definition)
puts method_definition.class
# => Proc
end
end
This is actually just a combination of some simple standard Ruby metaprogramming patterns and idioms:
module OriginalNameScope
def scope(name)
singleton_class.prepend(Module.new do
define_method(:method_added) do |meth|
if name && !#__recursion_guard__
#__recursion_guard__ = meth
method = instance_method(meth)
undef_method(meth)
define_method(:"#{name}_#{meth}") do |*args, &block|
method.bind(self).(*args, &block)
end
end
#__recursion_guard__ = nil
super(meth)
end
end)
yield
end
end
I just slapped this together, there's probably a lot that can be improved (e.g. use Refinements) and simplified.

ruby pass self of caller of method into method being called automaticly

I'm trying to create a method that passes the caller as the default last argument. According to this, I only need:
class A
def initialize(object = self)
# work with object
end
end
so that in:
class B
def initialize
A.new # self is a B instance here
end
end
self will be B rather than A;
However, this doesn't seem to work. Here's some test code:
class A
def self.test test, t=self
puts t
end
end
class B
def test test,t=self
puts t
end
end
class T
def a
A.test 'hey'
end
def b
B.new.test 'hey'
end
def self.a
A.test 'hey'
end
def self.b
B.new.test'hey'
end
end
and I get:
T.new.a # => A
T.new.b # => #<B:0x000000015fef00>
T.a # => A
T.b # => #<B:0x000000015fed98>
whereas I expect it to be T or #<T:0x000000015fdf08>. Is there a way to set the default last argument to the caller?
EDIT:
class Registry
class << self
def add(component, base=self)
self.send(component).update( base.to_s.split('::').last => base)
end
end
end
The idea is pretty simple, you would use it like this
class Asset_Manager
Registry.add :utilities
end
and you access it like:
include Registry.utilities 'Debugger'
I'm trying to de-couple classes by having a middle-man management type class that takes care of inter-class communications, auto-loading of missing classes and erroring when it doesn't exist, it works but I just want to be able to use the above rather than:
class Asset_Manager
Registry.add :utilities, self
end
It just feels cleaner, that and I wanted to know if such a thing was possible.
You can't escape the explicit self. But you can hide it with some ruby magic.
class Registry
def self.add(group, klass)
puts "registering #{klass} in #{group}"
end
end
module Registrable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def register_in(group)
Registry.add(group, self)
end
end
end
class AssetManager
include Registrable
register_in :utilities
end
# >> registering AssetManager in utilities
In short, you can't.
Ruby resolves the default arguments in the context of the receiver. That is, the object before the . in a method call. What you called the receiver should be the caller, actually.
class A
def test1(value = a)
puts a
end
def test2(value = b)
puts b
end
def a
"a"
end
end
a = A.new
a.test1 #=> a
def a.b; "b" end
a.test2 #=> b
If I were you, I would use the extended (or included) hook, where both the extending class and the extended module can be accessed. You can program what ever logic you want based on the information.
module Registry
module Utilities
def self.extended(cls)
#puts cls
::Registry.send(component).update( cls.to_s.split('::').last => cls)
end
end
end
class Asset_Manager
extend Registry::Utilities
end

Ruby Base.send calling it like instance method in model when using include and extend

Ruby library code
module Yaffle
module ActsAsYaffle
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def acts_as_yaffle(options = {})
cattr_accessor :yaffle_text_field
self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
include Yaffle::ActsAsYaffle::LocalInstanceMethods
end
end
...
more code
...
end
end
ActiveRecord::Base.send(:include, ActiveRecord::Acts::Taggable)
Why does when i call the acts_as_yaffle in the model, its being used like
class Hickwall < ActiveRecord::Base
acts_as_yaffle
end
Does the ActiveRecord::Base.send(:include,...) included the ClassMethods as instance eventhough the base is extended (base.extend(ClassMethods) ?
The acts_as_yaffle was declared as a ClassMethods.
It is a class method. Consider this example:
class C
def my_instance_method
puts 'hello from instance'
end
def self.my_class_method
puts 'hello from class'
end
my_instance_method
# NoMethodError
my_class_method
# hello from class
end
inside class C ... end you can only call class methods, because there is no instance to send the method call to. The library code is written using a common pattern to put class methods inside a module. For better understanding, the example class I wrote above is equivalent to this:
module M
def my_instance_method
puts 'hello from instance'
end
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_class_method
puts 'hello from class'
end
end
end

Shorthand for defining class accessors in module

I find myself writing code like the following a lot:
module SomeModule
module ClassMethods
def some_attribute
#some_attribute
end
def some_attribute=(val)
#some_attribute = val
end
end
def self.included(other)
other.extend ClassMethods
end
end
class MyClass
include SomeModule
self.some_attribute = "a value"
end
Is there a shorthand for the two class methods defined above? Something like attr_accessor but for class methods.
[EDIT]
Based on sawa's answer the self.included method can be changed to:
def self.included(other)
other.singleton_class.class_eval{attr_accessor :some_attribute}
end
You can try this:
module SomeModule
def self.included(other)
class << other
attr_accessor :some_attribute
end
end
end
class MyClass
include SomeModule
self.some_attribute = "a value"
end
Note: Don't forget the self before calling some_attribute=. You need the explicit receiver on writter methods or Ruby will think you're trying to assign a variable somewhere =P
Hope it helps !
class MyClass
singleton_class.class_eval{attr_accessor :some_attribute}
end
It can also be done like this:
module SomeModule
attr_accessor :some_attribute
end
class MyClass
extend SomeModule
#some_attribute = "life is grand"
end
MyClass.some_attribute #=> "life is grand"
But why not just the usual way?
class MyClass
class << self
attr_accessor :some_attribute
end
end
MyClass.some_attribute = "life is grand"
MyClass.some_attribute #=> "life is grand"

Resources