How can I list all Ruby classes instantiated? - ruby-on-rails

I'd like to list all classes instantiated during one Ruby on Rails API query.
Is there a benchmarking tool or a profiler I could use?

Check out Object_space#each_object:
ObjectSpace.each_object.to_a

As an addition to ndn's answer:
ObjectSpace#each_object allows you to iterate through all instances of a specific class. For example:
ObjectSpace.each_object(String) do |object|
p object
end
ObjectSpace#count_objects shows number of instances of each class.
p ObjectSpace.count_objects
# Result:
# {:TOTAL=>30163,
# :FREE=>1007,
# :T_OBJECT=>39,
# :T_CLASS=>534,
# :T_MODULE=>24,
Check out this wonderful repository for more useful ruby tricks.

Related

How to iterate over deep nested hash without known depth in Ruby

I have multiple YAML (localization) files. I parse them and convert to hash in Ruby.
For example this is one of them:
hello: Hallo
messages:
alerts:
yay: Da!
no: Nein
deep:
nested:
another:
level:
hi: Hi!
test: Test!
Basically, this is look like a locale file in Rails App using YAML.
What I want to do is iterate this Hash recursively and get key and value. So that i can translate values one-by-one from API Endpoint like Google Translate. I want to keep nested hashes in same schema so that Rails can find by keys.
I know i can use nested loops but there is no guarantee that nested hashes is a known number of. How can I iterate this hash recursively so i can manipulate values (translate/replace)?
Expected Result: (after used translation service from API call)
hello: Hello
messages:
alerts:
yay: Yup!
no: No
deep:
nested:
another:
level:
hi: Hi!
test: Test!
What I've tried so far:
hash = YAML.load('de.yml') # parse source Deutsch locale
new_hash = {}
hash.each |key, value| do
new_hash[key] = translate_func(value) # here... translate value then assign very same key including parents.
# Do more loops....
end
# Now write this new_hash to yaml file...
But this only manipulate hello only. To get work with others I have to make a loop. But how many keys are nested is unknown.
How can I iterate over all values of locale hash and keep the schema intact?
And if possible but not mandatory, I would be very happy if we can keep the order of keys on final result. That would be awesome to find missing keys later when manually reviewed.
I am very new to ruby.
I am using Ruby 2.7.2
Conclusion / Resolve
All answers are correct and I love all of them. However, I would like to be able to control both keys and values. Not just transform by values. Therefore, I accepted an answer that fits to my needs. I was able to do my intention with selected answer.
You can use deep_transform_values! on your hash object to change the values recursively. (Or its non-destructive version deep_transform_values which returns a new hash instead of changing the original hash.)
hash.deep_transform_values! { |value| translate_func(value) }
Note: deep_transform_values! is a Rails method. See the source code here for inspiration if you're not using Rails.
If you want a simple non-rails solution then you can just create a recursive method:
def recurse(hash)
hash.transform_values do |v|
case v
when String
v.reverse # Just for the sake of the example
when Hash
recurse(v)
else
v
end
end
end
Output:
{"hello"=>"ollaH", "messages"=>{"alerts"=>{"yay"=>"!aD", false=>"nieN"}, "deep"=>{"nested"=>{"another"=>{"level"=>{"hi"=>"!iH"}}}}}, "test"=>"!tseT"}
However this might be a case of reinventing the wheel - you can use the i18n gem for translations and i18n-tasks to prefill your YAML files with translations from the Google Translate API.
So, you want to parse recursively until there are no more levels to parse into.
It’s super common in software and referred to as “recursion”. Have a search around to learn more about it - it’ll come up again and again in your journey. Welcome to ruby btw!
As for your actual current problem. Have a read of https://mrxpalmeiras.wordpress.com/2017/03/30/how-to-parse-a-nested-yaml-config-file-in-python-and-ruby/
But also, consider the i18n gem. See this answer https://stackoverflow.com/a/51216931/1777331 and the docs for the gem https://github.com/ruby-i18n/i18n This might fix your problem of handling internationalisation without you having to get into the details of handling yaml files.

When and how is it useful to dynamically add a method to a Ruby object?

I have been trying to learn Ruby from 'The Well-grounded Rubyist' and I came across the idea of adding methods to an object at run-time:
obj = Object.new
obj.respond_to? "hello" # Returns false
def obj.hello
puts "something"
end
obj.respond_to? "hello" # Returns true
obj.hello() # Output is "something"
I have a background in Python and Java, and I cannot imagine any way for me to use this new idea. So, how is this useful? How does it fit into the spirit of object-oriented programming? Is it expensive to do this at run-time?
There's always a long list of things you can do in any language but shouldn't do without a good reason and extending a single object is certainly high on that list.
Normally you wouldn't define individual methods, but you might include a bunch of them:
module Extensions
def is_special?
true
end
end
obj = Object.new
obj.send(:extend, Extensions)
obj.is_special?
# => true
ActiveRecord from Rails does this to dynamically create methods for models based on whatever the schema is at the time the Rails instance is launched, so each column gets an associated method. This sort of dynamic programming can be used to make the code adapt seamlessly to a changing environment.
There's a lot of cases where you'll want to spell this out explicitly so your methods are well documented, but for cases where it doesn't matter and responding dynamically is better than maintaining two things, like schema and the associated methods in your code, then it could be the best option.

Random selecting for different databases in RoR

I need to select random records from db. In Sqlite3, which I use on development, there is a function called Random(). However, in Postgresql it's called Rand(). I don't remember about MySql, but probably it's called so there.
So if I have a code of (for Sqlite3)
data = Items.where(pubshied: is_pubshied).order("RANDOM()").limit(count)
how do I ensure that it will work with different databases?
Rails doesn't support this out of the box. I believe I achieved this with a model extension (I dont use it anymore because I force the use of Postgresql), but something like this could work:
module Randomize
extend ActiveSupport::Concern
included do
scope :random, -> { order(rand_cmd) }
end
module ClassMethods
def rand_cmd
if connection.adapter_name =~ /mysql/i
'rand()'
else
'random()'
end
end
end
end
You can then do
class Item
include Randomize
end
Item.where(...).random.limit(...)
For a performant, non-adapter-specific way to order randomly, populate a random column, put an index on it and call it something like:
Foo.order("random_column > #{rand}").limit(1)
From the comments from the post that waldyr.ar mentions in his comment: https://stackoverflow.com/a/12038506/16784.
Tl;dr: you can use Items.all.sample(count). Of course that retrieves the entire table and may not be useful for large tables.

How to create a deep copy of an object in Ruby?

I did some searching found some different methods and posts about creating a deep copy operator.
Is there a quick and easy (built-in) way to deep copy objects in Ruby? The fields are not arrays or hashes.
Working in Ruby 1.9.2.
Deep copy isn't built into vanilla Ruby, but you can hack it by marshalling and unmarshalling the object:
Marshal.load(Marshal.dump(#object))
This isn't perfect though, and won't work for all objects. A more robust method:
class Object
def deep_clone
return #deep_cloning_obj if #deep_cloning
#deep_cloning_obj = clone
#deep_cloning_obj.instance_variables.each do |var|
val = #deep_cloning_obj.instance_variable_get(var)
begin
#deep_cloning = true
val = val.deep_clone
rescue TypeError
next
ensure
#deep_cloning = false
end
#deep_cloning_obj.instance_variable_set(var, val)
end
deep_cloning_obj = #deep_cloning_obj
#deep_cloning_obj = nil
deep_cloning_obj
end
end
Source:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/43424
I've created a native implementation to perform deep clones of ruby objects.
It's approximately 6 to 7 times faster than the Marshal approach.
https://github.com/balmma/ruby-deepclone
Note that this project is not maintained anymore (last commit in 2017, there are reported issues)
Rails has a recursive method named deep_dup that will return a deep copy of an object and, on the contrary of dup and clone, works even on composite objects (array/hash of arrays/hashes).
It's as easy as:
def deep_dup
map { |it| it.deep_dup }
end
There is a native implementation to perform deep clones of ruby objects: ruby_deep_clone
Install it with gem:
gem install ruby_deep_clone
Example usage:
require "deep_clone"
object = SomeComplexClass.new()
cloned_object = DeepClone.clone(object)
It's approximately 6 to 7 times faster than the Marshal approach and event works with frozen objects.
Note that this project is not maintained anymore (last commit in 2017, there are reported issues)
You can use duplicate gem for this.
It's a small pure ruby gem that able to recursively duplicate object
It will duplicate it's object references too to the new duplication.
require 'duplicate'
duplicate('target object')
https://rubygems.org/gems/duplicate
https://github.com/adamluzsi/duplicate.rb
Automatic deep clone is not always what you want. Often you need to define a selected few attributes to deep clone. A flexible way to do this is to implement the initialize_copy, initialize_dup and initialize_clone methods.
If you have a class:
class Foo
attr_accessor :a, :b
end
and you only want to only deep clone :b, you override the initialize_* method:
class Foo
attr_accessor :a, :b
def initialize_dup(source)
#b = #b.dup
super
end
end
Of course if you want #b to also deep clone some of its own attributes, you do the same in b's class.
Rails does this (see https://github.com/rails/rails/blob/0951306ca5edbaec10edf3440d5ba11062a4f2e5/activemodel/lib/active_model/errors.rb#L78)
For more complete explanation, I learned it here from this post https://aaronlasseigne.com/2014/07/20/know-ruby-clone-and-dup/
I would suggest you use the ActiveSupport gem which adds a lot of sugar to your native Ruby core, not just a deep clone method.
You can look into the documentation for more information regarding the methods that have been added.
You really don't need a Gem for this. This couldn't be much simpler than this, which is not worth the overhead of a Gem!
def deep_clone(obj)
obj.clone.tap do |new_obj|
new_obj.each do |key, val|
new_obj[key] = deep_clone(val) if val.is_a?(Hash)
end
end
end
Also check out deep_dive. This allows you to do controlled deep copies of your object graphs.
https://rubygems.org/gems/deep_dive

Is there a way in Ruby/Rails to execute code that is in a string?

So I have a database of different code samples (read snippets).
The code samples are created by users.
Is there a way in Rails to execute it?
So for example I have the following code in my database (with id=123):
return #var.reverse
Is there a way for me to execute it? Something like:
#var = 'Hello'
#result = exec(CodeSample.find(123))
So the result would be 'olleH'
You can use eval:
code = '#var.reverse'
#var = 'Hello'
#result = eval(code) # => "olleH"
But be very careful in doing so; you're giving that code full access to your system. Try out eval('exit()') and see what happens.
To the eval answer (which is the right one) I would add: get thee a copy of the Pickaxe Book (either Programming Ruby or Programming Ruby 1.9 depending on your Ruby version) and read the chapter called "Locking Ruby in the Safe." That chapter is all about Ruby's safe levels and tainted objects, and the chapter opens with exactly your use case and why you need to be paranoid about it.
There is also another approach which you can use if you have a very limited use case or to limit the use cases.
I had to use this approach to allow users to dynamically specify relative times e.g.3.months.ago
I used a regex to sanitize the input from the users like so
PERMITTED_OPERATIONS = /^\{\%([1-9]\.(day|year|month|hour|minute)(s\.|\.)ago|Time\.now)\%\}$/
def permit?(operation)
return !PERMITTED_OPERATIONS.match(operation.to_s).nil?
end
You could extend the regex to allow for from_now as well or create an array of regexes for permitted operations and loop over it.
Would welcome any comments on this approach.

Resources