Chain of Responsibility and alias_method problems in Ruby - ruby

I'm trying to implement the chain of responsibility pattern in Ruby and ActiveRecord for a polymorphic object. I'm having a few problems.
Sometimes I get an error that a method is not defined when I try to alias_method it, I think this is because the class isn't loaded or something so I explicity do a send to get the method
I get a bunch of infinite chains where the aliased function (original_method) calls method which calls original_method. I'm wondering if this is because when you alias a method that's already been overwritten, you're in essence making "original_method" a copy of the aliased method.
I'm currently working around this by having a function like "chained" return a sub-class of Setting with all the defined methods but curious why there were so many problems with alias_method right in the class.
Here's an example:
class Hospital
has_one :setting, :as => :settable
belongs_to :administrative_area
def next_link
adminstrative_area
end
def usable_setting
setting ? setting : next_link.usable_setting
end
end
Then, I have a Setting object:
class Setting < ActiveRecord::Base
belongs_to :settable, :polymorphic => true
def chained
%w(api_key active_days).each do |method|
# this is here because otherwise the method isn't defined,
# it's almost as while it's going up, the metaclass doesn't have the columns
# until it loads, probably will be fixed if we cache classes
self.send method.to_sym
(class << self; self; end).class_eval do
define_method method do |*args|
alias_method "original_#{method}", method
my_setting = send("original_#{method}")
if my_setting.nil? or my_setting.empty?
settable.next_link.usable_setting.chained.send(method)
else
return my_setting
end
end
end
end
self
end
end

You seem to be overcomplicating. Seems that you're trying to see if api_key and active_days exists, and if not, get it from somewhere else.
Here's the right way to do it, assuming that api_key and active_days are columns in your table:
class Setting < ActiveRecord::Base
belongs_to :settable, :polymorphic => true
def api_key
super || settable.next_link.usable_setting.api_key
end
def active_days
super || settable.next_link.usable_setting.active_days
end
end
You can refactor it a bit to keep clarity and remove duplication.
class Setting < ActiveRecord::Base
belongs_to :settable, :polymorphic => true
def api_key
super || next_usable_setting.api_key
end
def active_days
super || next_usable_setting.active_days
end
private
def next_usable_setting
settable.next_link.usable_setting
end
end
So in this case notice — if you have api_key/active_days available, it will get returned. Otehrwise, it will go fetch usable_setting from next_link. If that one has api_key/active_days, it will get returned, otherwise it will fetch usable_setting from next_link. Etc.

Related

inheriting attr_accessor inside a constant

I have a class with attr_accessor set like this:
class Human
ATTRIBUTES = [:name]
attr_accessor *ATTRIBUTES
end
it works like a charm, allows me to keep attributes inside ATTRIBUTES constant. Problem is I would like to have a class Student inheriting from a Human class, without the need to put attr_accessor every time.
Basically what i would like to have is this:
class Student < Human
ATTRIBUTES = [:name, :school]
end
unfortunately when i do
Student.new.school
i get no method error, because attr_accessor is loaded from Human and not a Student. What construction should i use to accomplish my goal?
I personally agree with #lcguida's answer, but I came up with a little experiment if you insist on following the pattern you proposed. The other answers already covered why your solution didn't work, so I'm not getting into that here.
The first thing that came to mind was to call attr_accessor on the self.inherited callback on the parent class, but unfortunately the child's body is not loaded until later. Even so, where there's a will, there's a way. If you're using Ruby 2.0 or later, the following implementation will work.
module LazyAttrAccessorizer
def self.extended(obj)
TracePoint.trace(:end) do |t|
if obj == t.self
obj.send :attr_accessor, *obj::ATTRIBUTES
t.disable
end
end
end
end
class Human
extend LazyAttrAccessorizer
ATTRIBUTES = [:name]
def self.inherited(subclass)
subclass.extend LazyAttrAccessorizer
end
end
class Student < Human
ATTRIBUTES = [:name, :school]
# ATTRIBUTES = [:school] would also work as expected, but I think you'd like to be literal there.
end
> Student.new.respond_to?(:name)
=> true
> Student.new.respond_to?(:school)
=> true
Well, while I don't get the need to keep the attributes in a array, Student class will already inherit the attr_accessor's defined in it's parent class.
For example:
class Human
attr_accessor :name, :gender
end
class Student < Human
attr_accessor :school
end
Student class now has :name, :gender and :school attr_accessor's:
> Student.new.respond_to?(:name)
=> true
> Student.new.respond_to?(:name=)
=> true
> Student.new.respond_to?(:school)
=> true
> Student.new.respond_to?(:school=)
=> true
Human also responds to :name and :gender
> Human.new.respond_to?(:name)
=> true
> Human.new.respond_to?(:gender)
=> true
But not to school
> Human.new.respond_to?(:school)
=> false
It's cleaner, it's the ruby way, easier to understand.

How can I patch the inherited hook in ruby?

Just a simple example.
class Base
def self.inherited(child)
p 'Base.inherited'
end
end
class User < Base
p 'User'
end
This produces me
"Base.inherited"
"User"
This works fine but how can I patch the inherited hook of Base class?
Let's say I want my result to be
"Base.inherited"
"Something inherited"
"User"
and still have my User class inherit the Base.
Any ideas, workarounds?
Thanks!
Updating question to be more specific.
I need to run some code exactly at the time when class User inherits the Base without modifying User class.
Let's say I have Base class with it's defined inherited method. From one hand I don't know what other classes will inherit Base. From another hand I cannot modify the original inherited method of Base class.
So how can I patch that method?
Thanks!
module Foo
def self.included(child)
p "Something inherited"
end
end
class Base
def self.inherited(child)
p 'Base.inherited'
end
end
class User < Base
include Foo
p 'User'
end
# >> "Base.inherited"
# >> "Something inherited"
# >> "User"
Found the answer.
Alias chaining works fine in this case. For some reason I thought it works with common methods but not with ruby callbacks.
class Base
def self.inherited(child)
p 'Base.inherited'
end
end
Base.class_eval do
class << self
alias_method :chained_inherited, :inherited
def inherited(child)
chained_inherited(child)
p 'Inherited'
end
end
end
class User < Base
p 'User'
end
# => "Base.inherited"
# => "Inherited"
# => "User"

Variable scope definitions and inheritance

I'm working on a extended search feature for my webpage.
I looked at ransack, however it's lacking some functionalities I need, makes the url-query string very long and has some bugs (reported).
Thus I started to implement my own hack.
First I want to present my idea, afterwards I want to ask kindly how to fix my issue and in the end if there are other ways to improve this.
The idea:
A model defines something like this (additionally, the model is inside an engine):
module EngineName
class Post < ActiveRecord::Base
search_for :name, :as => :string do |b, q|
b.where{name =~ "%#{q}%"}
end
end
end
:name is to define the query-param to use e.g. this would be ?q[name]=something
I know that this is not fully generic like ransack, but well...
:as is to build up the correct form-tag. :string would be for text_field, :integer for number_field and so on. I want to extend it further to implement auto-generating of collections for associations etc.
Now the block is a simple scope to use.
I run into several shortcomings with ransack when building up complex queries (like with count() etc.). Now I can specify my own optimized query in squeel.
I extended ActiveRecord::Base to set up the logic (the global one, not inside the engine. I want to use it everywhere).
I defined a scope :search so I can use Model.search(param[q]) like in ransack.
Also I tried to keep a list of keys which are "searchable" defined by the search_for calls.
class ActiveRecord::Base
##searchable_attributes = Hash.new({})
def self.search_for(name, *opts, &search_scope)
return unless search_scope
##searchable_attributes[name] = {
:type => opts[:as],
:condition => search_scope
}
unless ##searchable_attributes.has_key? :nil
##searchable_attributes[:nil] = Proc.new { scoped }
end
end
scope :search, lambda {|q|
next unless q.kind_of?(Hash)
base = ##searchable_attributes[:nil].call
q.each do |key, search|
next unless base.class.searchable_attributes.has_key?(key)
base = ##searchable_attributes[key][:condition].call(base, search)
end
base
}
end
Now the issues:
It has mostly to do with inheritance of the classes. But even after reading and trying 3, 4 it does not worked.
Please take a look at the second line in the scope :search.
There I'm calling the simple Proc I definied above which only includes "scoped"
This is to get arround the issue that self returns "ActiveRecord::Base" and not the model itself like "Post" or "Comment".
It's because the scope is called on the Base class on inheritance, however I did not find anything to fix this.
As search_for is called on the model itself (e.g. Post) the scope-model returned there is "the right one".
Does anyone know how to circumvent this?
The next question would be, how to store the list of "searchable" scopes. I used ##variables. But as they are shared within every subclass, this would be a no-go.
However, it needs to be static as the search_for is called without initialize a instance (isn't it?)
Last but not least, it is somekind horrible to always specify the base-model to use on every scope so that I can chain them together.
Is there any other possibilities to improve this?
Ok, it seems I got it finally myself my putting several other answers from other questions together.
Model:
module EngineName
class Post < ActiveRecord::Base
searchable
search_for :name, :as => :string do |b, q|
b.where{name =~ "%#{q}%"}
end
end
end
My "Plugin" currently as an initializer:
class ActiveRecord::Base
def self.searchable
include Searchable
end
end
module Searchable
def self.included(base)
base.class_eval {
##searchable_attributes = Hash.new({})
def self.search_for(name, opts)
return unless block_given?
##searchable_attributes[name] = {
:type => opts[:as],
:condition => Proc.new
}
end
# Named scopes
scope :search, lambda {|q|
next unless q.kind_of?(Hash)
base = self.scoped
q.each do |key, search|
key = key.to_sym
next unless ##searchable_attributes.has_key?(key)
base = ##searchable_attributes[key][:condition].call(base, search)
end
base
}
}
end
end
Hope it'll help some others working on the same problem.
Rails provides a helper for class_attribute. This provides inheritable class attributes, but allows subclassess to "change their own value and it will not impact parent class". However a hash which is mutated using []= for example would effect the parent, so you can ensure that a new copy is made when subclassing using rubys inherited method
Therefore you could declare and initialise on the base class like so:
module Searchable
extend ActiveSupport::Concern
included do
class_attribute :searchable_attributes
end
module ClassMethods
def inherited(subclass)
subclass.searchable_attributes = Hash.new({})
end
def search_for(name,opts)
return unless block_given?
searchable_attributes[name] = {
:type => opts[:as],
:condition => Proc.new
}
end
end
end
Note that I used ActiveSupport::Concern to gain the neater syntax for defining stuff directly on the class and also mixing in class methods. Then you can simply add this to active record base:
ActiveRecord::Base.send(:include, Searchable)
now any classes get their own attributes hash:
class Post < ActiveRecord::Base
search_for :name, :as => :string do |b, q|
b.where{name =~ "%#{q}%"}
end
end

How can I create fake has_many relationships, and have each one include class methods as instance methods?

I have a class, with some fake relationships I want to implement:
module FormStack
class Connection
def forms; end
def fields; end
end
end
I have metaprogramically generated classes for both forms, and fields (as they are RESTful resources, they share the same action names and params), and I want to include those methods in my fake relationships in my FormStack::Connection class. can this be done?
I essentially want <FromStack::Connection Instance>.forms to behave as if it were FormStack::Form, so I can do things like <connection>.forms.all or <connection>.forms.find(id).
Is this possible?
Any best practices I should maybe be looking at? (This seems a little strange to me, but I think it's an elegant way to have the methods implemented in a useful way, while still having an ActiveRecord-esque abstraction of the restful resources / objects).
Here is the code I'm working with, if you want to look: https://github.com/TinderBox/formstack/tree/connection_instances
Why not just use simple composition? Pass whatever object has the has_many FormStack::Form relation in when you initialize a new FormStack::Connection instance. Then you can directly invoke the #forms method on the FormStack::Form collection instance, or you can use delegation.
FormStack::Connection.new(FormStack::FormCollection.new(params[:form]) #sample class name -- obviously use whatever has the real has_many :forms
module FormStack
class Connection
def initialize(form_collection)
#form_collection = form_collection
end
def forms
#form_collection.forms
end
def fields
#form_collection.fields
end
end
end
Or
module FormStack
class Connection
extend Forwardable
def_delegators :#form_collection, :forms, :fields
def initialize(form_collection)
#form_collection = form_collection
end
end
end
Unless there is a better way, this is how I've solved my problem for now:
def method_missing(meth, *args, &block)
method_name = meth.to_s
if "forms" == method_name
FormStack::Form.connection = self
FormStack::Form
elsif ...
else
super
end
end
https://github.com/TinderBox/formstack/blob/082793bed97e97cc65c703c8ca3cb382cbdf743a/lib/formstack/connection.rb

how do I create a class that stores instances?

I want something like the following but would like it to be reusable for different classes.
How do I refactor this code, so with minimal effort it can be included in a class and that class will automatically be collecting instances whenever new is called?
I've tried all sorts of things like overriding new or initialize but just can't get the magic to happen.
class Person
##people_instances = []
def initialize
##people_instances << self
end
def self.instances
##people_instances
end
end
People.new
People.new
Poople.instances
=> [#<Person:0x000001071a7e28>, #<Person:0x000001071a3828>]
After some feedback below, I don't think the answer is to put the instances in a class variable as it will stay in memory forever. Rails cache is also not so appropriate as I don't need the instances to persist.
The following code uses class instance variables instead of class variables.
http://www.dzone.com/snippets/class-variables-vs-class
class Employee
class << self; attr_accessor :instances; end
def store
self.class.instances ||= []
self.class.instances << self
end
def initialize name
#name = name
end
end
class Overhead < Employee; end
class Programmer < Employee; end
Overhead.new('Martin').store
Overhead.new('Roy').store
Programmer.new('Erik').store
puts Overhead.instances.size # => 2
puts Programmer.instances.size # => 1
Will these instance variables be unique to every rails request or will they persist?
UPDATED ANSWER
If you want to keep it available during the request alone, none of the previous answers can do it. The solution for keeping it available only during the request-response cycle is to use a thread-local that is assigned in a controller method, example:
class YourController < ApplicationController
around_filter :cache_objects
protected
def cache_objects
Thread.current[:my_objects] = ['my-object', 'my-other-object']
yield
ensure
Thread.current[:my_objects]
end
end
Then, at the code that needs it, you just do Thread.current[:my_objects] and do whatever you would like to do with them. You need to use an around_filter because your web framework or server structure could try to reuse threads and the only real solution is to clean them up once the request is done to avoid memory leaks.
OLD ANSWER
Not sure what you're trying to do, but you can easily pick every single instance of a class using ObjectSpace:
ObjectSpace.each_object(String) { |s| puts(s) }
If what you need is as a database cache just use the Rails cache, load these objects once and then keep them in the cache. When using the Rails cache all you need to do is send your objects to the cache:
Rails.cache.write( "my_cached_objects", [ 'first-object', 'second-object' ] )
And then get them somewhere else:
Rails.cache.fetch("my_cached_objects") do
# generate your objects here if there was a cache miss
[ 'first-object', 'second-object' ]
end
As you can see, you don't even have to call cache.write, you can just use fetch and whenever there is a cache miss the block given will be called and your objects will be created.
You can read more about rails caching here and you can see all supported methods of the ActiveSupport::Cache::Store here.
Another method without using ObjectSpace but still with an ugly solution, now using alias_method:
module Counter
def self.included( base )
base.extend(ClassMethods)
base.class_eval do
alias_method :initialize_without_counter, :initialize
alias_method :initialize, :initialize_with_counter
end
end
def count_class_variable_name
:"###{self.class.name.downcase}_instances"
end
def initialize_with_counter( *args )
unless self.class.class_variable_defined?(count_class_variable_name)
self.class.class_variable_set(count_class_variable_name, [])
end
self.class.class_variable_get(count_class_variable_name) << self
initialize_without_counter(*args)
end
module ClassMethods
def all_instances
class_variable_get(:"###{name.downcase}_instances")
end
end
end
class Person
def initialize
puts 'new person'
end
include Counter
end
p1 = Person.new
p2 = Person.new
p3 = Person.new
puts Person.all_instances.size
lib/keeper.rb
def initialize
instance_eval "###{self.class.to_s.downcase}_instances ||= []"
instance_eval "###{self.class.to_s.downcase}_instances << self"
end
def self.instances
return class_eval "###{self.to_s.downcase}_instances"
end
person.rb
class Person
eval File.open('./lib/keeper.rb','rb').read
end
Then this works:
Person.new
Person.new
Person.instances

Resources