I wish to create a way to make Children classes express some business definitions on Class Level.
I tried to use Class Variables for that, but i found that they share state between all Classes, so once i define the Second Class, the "##attribute" class var changes its value for all adjacent Class instances.
class Parent
def self.type(value)
##_type = value
end
def render
puts ##_type
end
end
class Children < Parent
type "name"
end
Children.new.render # Result: name. Expected: name
class Children2 < Parent
type "title"
end
Children2.new.render # Result: title. Expected: title
Children.new.render # Result: title. Expected: name
How can i create this DSLs in the most simple and direct way?
This is a common pattern for several Ruby Gems, like HTTParty, Virtus, and etc.
I even tried to look at their source code to understand how its done, but it seems too much complex for what i want.
Thanks for your help!
Class variables are one of a triumvirate of Ruby tools that most experienced Rubiests rarely, if ever, use.1. Instead you want to use a class-level instance variable, Parent being an instance of the class Class.
class Parent
def self.type=(value)
#type = value
end
def self.type
#type
end
def render
puts self.class.type
end
end
class Children < Parent
self.type = "name"
end
Children.new.render
#=> "name"
class Children2 < Parent
self.type = "title"
end
Children2.new.render
#=> "title"
Children.new.render
#=> "name"
Firstly, the class method type= is called a "setter" and the class method "type" is called a "getter". You had a setter type, taking an argument. If you do that, how will you just get its value? To use it as a getter as well you'd have to do something like the following:
def self.type=(value=nil)
if value.nil?
#type
else
#type = value
end
end
Here it would make more sense to just define a getter
def self.type
#type
end
and having no setter, just writing, for example, #type = "name".
That is kludgy and only works if you don's want to set #type to nil. You could also leave your method as a setter and use self.class.instance_variable_get(:#type) to get its value, but that's equally awful. It's best to have a setter and a getter.
When using the setter we need to preface type with self. to tell Ruby we wish to invoked the getter and not set a local variable type to a given value. Of course we could instead just write, for example, `#type = "title".
The conventional way to create a setter and a getter is to write attr_accessor :type (invoking the class method Module#attr_accessor). As class methods are stored in a class' singleton class, that could be done as follows2:
class Parent
class << self
attr_accessor :type
end
def render
puts self.class.type
end
end
class Children < Parent
self.type = "name"
end
Children.new.render
#=> "name"
class Children2 < Parent
self.type = "title"
end
Now consider the instance method Parent#render. Being an instance method its receiver is an instance of the class (or a subclass), say parent = Parent.new. That means that when render is invoked within the method self equals parent. We want to invoke the class method type, however. We must therefore convert parent to Parent, which we do with self.class.
1. The other two (in my opinion, of course) are global variables and for loops. Their popularity among Ruby newbies is probably due to the fact that they tend to make their debut in Chapter 1 of many learning-Ruby books.
2. There are many ways to define attr_accessor in Parent's singleton class. Two others are singleton_class.instance_eval do { attr_accessor :type } and singleton_class.send(:attr_accessor, :type).
Ok, i have no idea why it worked, but it worked this way:
class Parent
class << self
attr_accessor :_type
def type(value)
self._type = value
end
end
def render
puts self.class._type
end
end
I really wish to understand why, but "self.class", and "class << self" seems a lot of dark to me.
Light?
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
I am trying to build a simple little template parser for self-learning purposes.
How do I build something "modular" and share data across it? The data doesn't need to be accessible from outside, it's just internal data. Here's what I have:
# template_parser.rb
module TemplateParser
attr_accessor :html
attr_accessor :test_value
class Base
def initialize(html)
#html = html
#test_value = "foo"
end
def parse!
#html.css('a').each do |node|
::TemplateParser::Tag:ATag.substitute! node
end
end
end
end
# template_parser/tag/a_tag.rb
module TemplateParser
module Tag
class ATag
def self.substitute!(node)
# I want to access +test_value+ from +TemplateParser+
node = #test_value # => nil
end
end
end
end
Edit based on Phrogz' comment
I am currently thinking about something like:
p = TemplateParser.new(html, *args) # or TemplateParser::Base.new(html, *args)
p.append_css(file_or_string)
parsed_html = p.parse!
There shouldn't be much exposed methods because the parser should solve a non-general problem and is not portable. At least not at this early stage. What I've tried is to peek a bit from Nokogiri about the structure.
With the example code you've given, I'd recommend using composition to pass in an instance of TemplateParser::Base to the parse! method like so:
# in TemplateParser::Base#parse!
::TemplateParser::Tag::ATag.substitute! node, self
# TemplateParser::Tag::ATag
def self.substitute!(node, obj)
node = obj.test_value
end
You will also need to move the attr_accessor calls into the Base class for this to work.
module TemplateParser
class Base
attr_accessor :html
attr_accessor :test_value
# ...
end
end
Any other way I can think of right now of accessing test_value will be fairly convoluted considering the fact that parse! is a class method trying to access a different class instance's attribute.
The above assumes #test_value needs to be unique per TemplateParser::Base instance. If that's not the case, you could simplify the process by using a class or module instance variable.
module TemplateParser
class Base
#test_value = "foo"
class << self
attr_accessor :test_value
end
# ...
end
end
# OR
module TemplateParser
#test_value = "foo"
class << self
attr_accessor :test_value
end
class Base
# ...
end
end
Then set or retrieve the value with TemplateParser::Base.test_value OR TemplateParser.test_value depending on implementation.
Also, to perhaps state the obvious, I'm assuming your pseudo-code you've included here doesn't accurately reflect your real application code. If it does, then the substitute! method is a very round about way to achieve simple assignment. Just use node = test_value inside TemplateParser::Base#parse! and skip the round trip. I'm sure you know this, but it seemed worth mentioning at least...
I'm using YARD to document my project. YARD document attributes created with
attr_accessor :some_attribute
in a separate section "Instance Attribute Summary". Now I have another attribute, but with custom setter and getter
def some_other_attribute
# ...
end
def some_other_attribute= value
# ...
end
so basically my question is, how can I get YARD to document this pair of setter/getter just like attr_accessor in the previous case, and list some_other_attribute within "Instance Attribute Summary"?
As of 0.8 (which is in pre-release right now), the #!attribute directive is the recommended way to denote that an object is an attribute. The #attr_* tags are deprecated in favour of this directive. You could also do (in 0.8.0+):
# #!parse attr_accessor :some_attribute
To parse code that isn't necessarily executed by Ruby. Prior to 0.8, you could just add the attr_accessor directly and then redefine the setter/getter as follows:
class MyClass
attr_accessor :foo
def foo; something_else end
def foo=(v) something_else(v) end
end
Ruby shouldn't mind, except that in ruby -w it will warn about method redefinitions. If this bugs you, you can add undef foo, foo= in there too. It's a little messy (if you care about -w), which is why we added things like #!parse and #!attribute.
You should be able to use the #attr tag on the class:
# #attr [String] name The name of this object.
class MyClass
def name
end
def name=
end
end
There are other tags (like #attr_reader and #attr_writer) than can also be helpful.
I can easily ascend the class hierarchy in Ruby:
String.ancestors # [String, Enumerable, Comparable, Object, Kernel]
Enumerable.ancestors # [Enumerable]
Comparable.ancestors # [Comparable]
Object.ancestors # [Object, Kernel]
Kernel.ancestors # [Kernel]
Is there any way to descend the hierarchy as well? I'd like to do this
Animal.descendants # [Dog, Cat, Human, ...]
Dog.descendants # [Labrador, GreatDane, Airedale, ...]
Enumerable.descendants # [String, Array, ...]
but there doesn't seem to be a descendants method.
(This question comes up because I want to find all the models in a Rails application that descend from a base class and list them; I have a controller that can work with any such model and I'd like to be able to add new models without having to modify the controller.)
Here is an example:
class Parent
def self.descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
class Child < Parent
end
class GrandChild < Child
end
puts Parent.descendants
puts Child.descendants
puts Parent.descendants gives you:
GrandChild
Child
puts Child.descendants gives you:
GrandChild
If you use Rails >= 3, you have two options in place. Use .descendants if you want more than one level depth of children classes, or use .subclasses for the first level of child classes.
Example:
class Animal
end
class Mammal < Animal
end
class Dog < Mammal
end
class Fish < Animal
end
Animal.subclasses #=> [Mammal, Fish]
Animal.descendants #=> [Dog, Mammal, Fish]
Ruby 1.9 (or 1.8.7) with nifty chained iterators:
#!/usr/bin/env ruby1.9
class Class
def descendants
ObjectSpace.each_object(::Class).select {|klass| klass < self }
end
end
Ruby pre-1.8.7:
#!/usr/bin/env ruby
class Class
def descendants
result = []
ObjectSpace.each_object(::Class) {|klass| result << klass if klass < self }
result
end
end
Use it like so:
#!/usr/bin/env ruby
p Animal.descendants
Override the class method named inherited. This method would be passed the subclass when it is created which you can track.
Alternatively (updated for ruby 1.9+):
ObjectSpace.each_object(YourRootClass.singleton_class)
Ruby 1.8 compatible way:
ObjectSpace.each_object(class<<YourRootClass;self;end)
Note that this won't work for modules. Also, YourRootClass will be included in the answer. You can use Array#- or another way to remove it.
Although using ObjectSpace works, the inherited class method seems to be better suitable here inherited(subclass) Ruby documentation
Objectspace is essentially a way to access anything and everything that's currently using allocated memory, so iterating over every single one of its elements to check if it is a sublass of the Animal class isn't ideal.
In the code below, the inherited Animal class method implements a callback that will add any newly created subclass to its descendants array.
class Animal
def self.inherited(subclass)
#descendants = []
#descendants << subclass
end
def self.descendants
puts #descendants
end
end
I know you are asking how to do this in inheritance but you can achieve this with directly in Ruby by name-spacing the class (Class or Module)
module DarthVader
module DarkForce
end
BlowUpDeathStar = Class.new(StandardError)
class Luck
end
class Lea
end
end
DarthVader.constants # => [:DarkForce, :BlowUpDeathStar, :Luck, :Lea]
DarthVader
.constants
.map { |class_symbol| DarthVader.const_get(class_symbol) }
.select { |c| !c.ancestors.include?(StandardError) && c.class != Module }
# => [DarthVader::Luck, DarthVader::Lea]
It's much faster this way than comparing to every class in ObjectSpace like other solutions propose.
If you seriously need this in a inheritance you can do something like this:
class DarthVader
def self.descendants
DarthVader
.constants
.map { |class_symbol| DarthVader.const_get(class_symbol) }
end
class Luck < DarthVader
# ...
end
class Lea < DarthVader
# ...
end
def force
'May the Force be with you'
end
end
benchmarks here:
http://www.eq8.eu/blogs/13-ruby-ancestors-descendants-and-other-annoying-relatives
update
in the end all you have to do is this
class DarthVader
def self.inherited(klass)
#descendants ||= []
#descendants << klass
end
def self.descendants
#descendants || []
end
end
class Foo < DarthVader
end
DarthVader.descendants #=> [Foo]
thank you #saturnflyer for suggestion
(Rails <= 3.0 ) Alternatively you could use ActiveSupport::DescendantsTracker to do the deed.
From source:
This module provides an internal implementation to track descendants which is faster than iterating through ObjectSpace.
Since it is modularize nicely, you could just 'cherry-pick' that particular module for your Ruby app.
A simple version that give an array of all the descendants of a class:
def descendants(klass)
all_classes = klass.subclasses
(all_classes + all_classes.map { |c| descendants(c) }.reject(&:empty?)).flatten
end
Ruby Facets has Class#descendants,
require 'facets/class/descendants'
It also supports a generational distance parameter.
Class#subclasses (Ruby 3.1+)
Starting from Ruby 3.1, there is a built-in method - Class#subclasses.
It returns an array of classes where the receiver is the direct superclass of the class, excluding singleton classes.
As a result, there is no more need to depend on ActiveSupport or write monkey-patches in order to use it.
class A; end
class B < A; end
class C < B; end
class D < A; end
A.subclasses #=> [D, B]
B.subclasses #=> [C]
C.subclasses #=> []
Sources:
Class#subclasses from official Ruby docs.
Add Class#descendants.
Add Class#subclasses.
Feature #14394.
Ruby 3.1 adds Class#subclasses.
Rails provides a subclasses method for every object, but it's not well documented, and I don't know where it's defined. It returns an array of class names as strings.
You can require 'active_support/core_ext' and use the descendants method. Check out the doc, and give it a shot in IRB or pry. Can be used without Rails.
Building on other answers (particularly those recommending subclasses and descendants), you may find that in Rails.env.development, things get confusing. This is due to eager loading turned off (by default) in development.
If you're fooling around in rails console, you can just name the class, and it will be loaded. From then on out, it will show up in subclasses.
In some situations, you may need to force the loading of classes in code. This is particularly true of Single Table Inheritance (STI), where your code rarely mentions the subclasses directly. I've run into one or two situations where I had to iterate all the STI subclasses ... which does not work very well in development.
Here's my hack to load just those classes, just for development:
if Rails.env.development?
## These are required for STI and subclasses() to eager load in development:
require_dependency Rails.root.join('app', 'models', 'color', 'green.rb')
require_dependency Rails.root.join('app', 'models', 'color', 'blue.rb')
require_dependency Rails.root.join('app', 'models', 'color', 'yellow.rb')
end
After that, subclasses work as expected:
> Color.subclasses
=> [Color::Green, Color::Blue, Color::Yellow]
Note that this is not required in production, as all classes are eager loaded up front.
And yes, there's all kinds of code smell here. Take it or leave it...it allows you to leave eager loading off in development, while still exercising dynamic class manipulation. Once in prod, this has no performance impact.
Using descendants_tracker gem may help. The following example is copied from the gem's doc:
class Foo
extend DescendantsTracker
end
class Bar < Foo
end
Foo.descendants # => [Bar]
This gem is used by the popular virtus gem, so I think it's pretty solid.
This method will return a multidimensional hash of all of an Object's descendants.
def descendants_mapper(klass)
klass.subclasses.reduce({}){ |memo, subclass|
memo[subclass] = descendants_mapper(subclass); memo
}
end
{ MasterClass => descendants_mapper(MasterClass) }
To compute the transitive hull of an arbitrary class
def descendants(parent: Object)
outSet = []
lastLength = 0
outSet = ObjectSpace.each_object(Class).select { |child| child < parent }
return if outSet.empty?
while outSet.length == last_length
temp = []
last_length = outSet.length()
outSet.each do |parent|
temp = ObjectSpace.each_object(Class).select { |child| child < parent }
end
outSet.concat temp
outSet.uniq
temp = nil
end
outSet
end
end
For Ruby 3.1+ Class#subclasses is available. Class#descendants is not implemented:
class A; end
class B < A; end
class C < B; end
class D < A; end
A.subclasses => [B, D]
A.descendants => NoMethodError: undefined method 'descendants' for A:Class
A.methods.grep('descendants') => []
For Ruby < 3.1 this is slightly faster than the Rails implementation:
def descendants
ObjectSpace.each_object(singleton_class).reduce([]) do |des, k|
des.unshift k unless k.singleton_class? || k == self
des
end
end
The Ruby 3.1+ #subclasses appears much faster than the descendants method given above.
If you have access to code before any subclass is loaded then you can use inherited method.
If you don't (which is not a case but it might be useful for anyone who found this post) you can just write:
x = {}
ObjectSpace.each_object(Class) do |klass|
x[klass.superclass] ||= []
x[klass.superclass].push klass
end
x[String]
Sorry if I missed the syntax but idea should be clear (I don't have access to ruby at this moment).