Get class/module name - ruby

Is there a better way to get class/module name viz, C from A::B::C, B from A::B::C, and A From A::B::C. The following code uses string and split to get "Stegosaurus" from Cowsay::Character::Stegosaurus, How to do away with string and split?
require 'cowsay'
x = Cowsay.random_character().class
x.name.split("::")[2]
require 'cowsay'
true
x = Cowsay.random_character().class
Cowsay::Character::Stegosaurus
x.name.split("::")[2]
"Stegosaurus"

I don't think there's anything for handling this in core/standard library.
As an alternative to custom written methods there is always activesupport:
require 'active_support/core_ext/string/inflections'
Cowsay::Character::Stegosaurus.name.demodulize
#=> "Stegosaurus"
Cowsay::Character::Stegosaurus.name.deconstantize
#=> "Cowsay::Character"
These methods are implemented as follows:
def demodulize(path)
path = path.to_s
if i = path.rindex('::')
path[(i+2)..-1]
else
path
end
end
def deconstantize(path)
path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
end
Take a look into docs if interested in more methods.
As to
A From A::B::C
If you'd require the whole activesupport, you'd get bunch of Module methods, including parent:
require 'active_support'
Cowsay::Character::Stegosaurus.parent
#=> Cowsay
If you're not going to use it extensively, I recommend to just grab the needed methods from activesupport and put it into some helper, because loading it whole might be an overkill.

Related

Parsed_response in Ruby from HTTP

How can i get parsed_response from here?
require 'HTTParty'
require 'httparty/request'
require 'httparty/response/headers'
class CRUD
include HTTParty
def retrieve
##response = CRUD.get('http://dummy.restapiexample.com/api/v1/employee/id')
end
end
{"id":"719","employee_name":"test","employee_salary":"123","employee_age":"23","profile_image":""}
puts #manter_user.retrieve.parsed_response['employee_name'] -- dont work
puts CRUD.class_variable_get(:##response).parsed_response['employee_name'] -- dont work
It's an instance method, it means that you need to create an instance. And you don't need global variable. And it is bad idea to name class with all uppercase letters - this style is used for constants. Classes and modules use MixedCase and have no underscores, each word starts with an uppercase letter.
class Crud
include HTTParty
def retrieve
self.class.get('http://dummy.restapiexample.com/api/v1/employee/id')
end
end
> Crud.new.retrieve.parsed_response
Since you are getting the JSON response, you can parsed it back as
require 'json'
foo = JSON['{"id":"719","employee_name":"test","employee_salary":"123","employee_age":"23","profile_image":""}']
puts foo['employee_name'] # => test

ruby catch classes as they are defined

Building tile games or simulations in Ruby Gosu always makes me end upp with a list of all available tiles, saved by their class. For example [Pipe, PipeJunktion, Box, Pump] and so on. Each class is defined in one of a few separate files, which i required from the main program. For now i have to add the class myself to this list every time I add a new tile to the game. I was wondering if there was a way to catch all loading classes from a file.
Something along the lines of:
allTiles = []
require_relative 'tiles.rb'.each_class {|class| allTiles << class}
would be handy.
Or can this be solved with modules in some way?
Checking which classes were added by a file is not something that's easily or commonly done. A better approach would be to put all the tile classes under a single namespace. Since classes can be re-opened, these can be split among multiple files.
class Tiles
class Pipe
# ...
end
end
class Tiles
class Box
# ...
end
end
Then Tiles.constants could would return an array of symbols: [:Pipe, :Box], and could be used to get a list of class references using Tiles.constants.map { |const| Tiles.const_get const } or Tiles.constants.map &Tiles.method(:const_get)
If for whatever reason it was really important to know which constants were added by a specific file, the following code shows an approach:
constants1 = Object.constants
require "./tiles.rb"
constants2 = Object.constants
added_constants = constants2 - constants1
If tiles.rb had class definitions for Pipe and Box, then added_constants would be [:Pipe, :Box].
The problem with this approach is that might show constants added by gems, for example:
constants1 = Object.constants
require 'mechanize'
class Foo
end
constants2 = Object.constants
added_constants = constants2 - constants1
Since I called require 'mechanize', the added_constants list will be quite long and include much more than just Foo.
You can do something like this:
Dir['tiles/*.rb'].each { |file| require file }
What would collect all files from a tiles subfolder and requires it.
In a next step load all classes by their file names:
all_tiles = Dir['tiles/*.rb'].map do |file|
file_name = File.basename(x, '.*')
camel_cased_name = file_name.split('_').collect(&:capitalize).join
Object.const_get(camel_cased_name)
end
Btw the same can be done in Rails like this:
all_tiles = Dir['tiles/*.rb'].map do |file|
File.basename(x, '.*').camelize.constantize
end
I suspect there are pitfalls with the following approach, but I will put it out and invite comments.
First, use ObjectSpace::each_object to compile a list of all classes that exist before any custom classes have been created:
base_classes = ObjectSpace.each_object(Class).to_a
For my version of Ruby (2.4.0), within IRB, base_classes.size #=> 490. Now load the code with require's etc. Suppose that causes three classes to be created:
class A; end
class B; end
class C; end
Now compile a list of all classes that now exist and subtract base_classes:
ObjectSpace.each_object(Class).to_a - base_classes
#=> [A, B, C]
This returns an array of classes that have been added by my code.
Of course this does not show classes in base_classes that are overridden by my code or show which classes are defined by required gems.

How to know if I have required a library

Is there any way to do something like this pseudo-code?
puts was_required?('my-library')
=> false
require('my-library')
puts was_required?('my-library')
=> true
I want to be able to test if I have required some library, whether is a Gem o or a Rake task, by it's require-name.
I could test a gem testing if the Class the gem loads is available, but I would really love to test using the library name, no the class name.
For rake tasks is the same. I can find in Rake.tasks if there is a task from the library I have required, by I would like to test with the library name, no for tasks names
thanks
Perhaps you want to play around with $LOADED_FEATURES (alias of $"), that array that contains the module names loaded by require?
def was_required?(file)
rex = Regexp.new("/#{Regexp.quote(file)}\.(so|o|sl|rb)?")
$LOADED_FEATURES.find { |f| f =~ rex }
end
was_required?('uri')
#=> false
require 'uri'
#=> true
was_required?('uri')
#=> true
Btw this is exactly what Ruby does in the require method.

How can I mimic Node.js's require function in Ruby?

In node.js you can write:
var lib = require('lib');
but in Ruby the require function simply runs the code in the file and true is returned.
Currently, I'm using a very dirty solution:
main.rb:
$stuff = []
require './file1.rb'
require './file2.rb'
# and so on
file1.rb:
$stuff << something
and so on.
How can I eliminate the use of a global variable?
eg:
main.rb:
$stuff = []
$stuff << cool_require './file1.rb'
# etc
file1.rb:
exports.what = something
One of the biggest errors when working with a language, is trying to make the language working like a different one.
Ruby is not NodeJs, there are features built-in into each language that are unique to the language and cannot be reproduced easily.
In other words, there is no way to implement the NodeJS require behavior in Ruby because in Ruby there is no notion of export. When you require a file, every method/class included in the required file are made available to the scope.
In Ruby there are objects and method visibility. The way you have to make a method visible or not is to declare it as public or private/protected.
Well, first consider that Ruby is not Node.js. As Simone Carletti said, there are some features that are unique to each language. Sometimes it's good to take from other language but sometimes it's bad.
There are few things that you must keep in mind:
meth is method invocation, to pass method you use method method: method(:meth) or package it into module/class
you can use class/module by assigning it to some 2nd variable:
class A;
def self.aa; puts 'aa'; end;
end;
New_a = A;
New_a.aa # aa;
eval is dangerous method(you can evaluate unknown code)
Method:
Here is one way you can do. It is not idiot-proof tough. It is not 100% safe(eval). :
file1.rb:
Module.new do
def self.meth1
42
end
def self.meth2
'meth2'
end
end
This file contain module with 2 methods.
I am using Module.new because it returns object that you want. You can assign it later into variable/constant.
I am using self.meth* so you don't have to include but run instantly it like this: module_name.meth1()
req.rb:
def cool_require name
eval(File.read name)
end
Some_variable = cool_require('req.rb')
puts Some_variable.meth1 # 42
puts Some_variable.meth2 # meth2
cool_require reads filename(argument name) and evaluate it(it is just like you would type it in irb/pry)
Some_variable is constant. It won't disappear that easily.
2 last line is how it works. As fair I remember, that's how node.js' require works.

Adding #to_yaml to DataMapper models

I am using DataMapper for Database access. My goal is to send the models to an webservice as read-only object. This is my current try:
class User
include DataMapper::Resource
def to_yaml(opts = {})
mini_me = OpenStruct.new
instance_variables.each do |var|
next if /^#_/ =~ var.to_s
mini_me.send("#{var.to_s.gsub(/^#/, '')}=", instance_variable_get(var))
end
mini_me.to_yaml(opts)
end
....
end
YAML::ENGINE.yamler = 'psych'
u = User.get("hulk")
p u.to_yaml
# => "--- !ruby/object:OpenStruct\ntable:\n :uid: hulk\n :uidNumber: 1000\n :gidNumber: 1001\n :email: hulk#example.com\n :dn: uid=hulk,ou=People,o=example\n :name: Hulk\n :displayName: Hulk\n :description: Hulk\n :homeDirectory: /home/hulk\n :accountFlags: ! '[U ]'\n :sambaSID: S-1-5-21-......\nmodifiable: true\n"
p [ u ].to_yaml # TypeError: can't dump anonymous class Class
Any ideas how to make this work and get rid of the exception?
Thanks,
krissi
Using to_yaml is deprecated in Psych, and from my testing it seems to be actually broken in cases like this.
When you call to_yaml directly on your object, your method gets called and you get the result you expect. When you call it on the array containing your object, Psych serializes it but doesn’t correctly handle your to_yaml method, and ends up falling back onto the default serialization. In your case this results in an attempt to serialize an anonymous Class which causes the error.
To fix this, you should use the encode_with method instead. If it’s important that the serialized form is tagged as an OpenStruct object in the generated yaml you can use the represent_object (that first nil parameter doesn’t seem to be used):
def encode_with(coder)
mini_me = OpenStruct.new
instance_variables.each do |var|
next if /^#_/ =~ var.to_s
mini_me.send("#{var.to_s.gsub(/^#/, '')}=", instance_variable_get(var))
end
coder.represent_object(nil, mini_me)
end
If you were just using OpenStruct for convenience, an alternative could be something like:
def encode_with(coder)
instance_variables.each do |var|
next if /^#_/ =~ var.to_s
coder[var.to_s.gsub(/^#/, '')]= instance_variable_get(var)
end
end
Note that Datamapper has its own serializer plugin that provides yaml serialization for models, it might be worth looking into.

Resources