Print out the instance methods defined class? or the path where the current class is?

As we know, in ruby, we have many introspection methods in runtime, e.g. Object#instance_methods, Module.class_methods etc …

As a static language, there may not those methods exists in runtime, but, i consider, print all instance methods/class methods which defined on one class on compile time, is possible, right?

Following is a example:

require "bip39"
require "secp256k1"

m0 = Bip0039::Mnemonic.new 256
priv = Secp256k1::Num.new m0.to_hex
key = Secp256k1::Key.new priv
p! typeof(key) # => Secp256k1::Key

Because LSP support for crystal is poor, there is no easy way to jump to definition when i cursor on the key variable, and pressing some keybinding or click on mouse, i have to open source code to find it out.

so, i consider if there have a more easy way to do this? e.g. consider above example,
I really want to know if there is a easy way to print out where Secp256k1::Key is defined, or even more, print all instance methods defined on Secp256k1::Key directly?

e.g.
how to know a method named private_hex is defined in this key object quickly?

key.private_hex # => "08647a626a6c96bfa9f5847a7d53f78b4cd23234a4b8fa22c489e838af4cb94e"

I’ve been thinking about this a lot these past days.

It’s tricky because Ruby is dynamic so everything is a VALUE and there’s a lot of runtime information that isn’t present in Crystal.

That said, I wonder if it would be nice for Class to provide methods like subclasses, methods, etc., that return runtime values. These can be used to quickly introspect types without having to open a browser or source code to see the API. Or they can be used to maybe generate documentation or transform the code to something else, like a schema. It’s tricky because type restrictions aren’t solved, so for now they can just be represented as strings.

So, the idea would be that Class#sublcasses doesn’t return an Array of Class. That’s kind of impossible. Same with Class#methods, there’s no existing type that we could return there.

But we could introduce intermediary types.

Here’s what I got so far:

https://play.crystal-lang.org/#/r/dfjf

Thoughts?

The nice thing about this is that all of this runtime information is only produced if asked. If you don’t call any of that nothing of that will exist at runtime.

You could also use this in the interpreter to quickly find out about methods and types (I just tried it and all the code runs fine in interpreted mode).

10 Likes

I know @ftarulla wanted to add .subclasses and .all_subclasses (our implementation was more naïve).

1 Like

Really really Cool !! for those awesome macro.

i am still not deep investigate this code, there seem like exists a typo in play.

so, those type code never be used, right?

BTW: i am curious if there exists a way to always run those macro definition before my project code is start?

Let me still explain use Ruby anyway, step by step.

  1. assume i create a file, /home/zw963/foo/mixin_helper.rb, it content like this:
module Kernel
  # view a object mixed in module.
  def mixed_in
    if self.class == Class
      ancestors[1..-1] - [BasicObject, Object, Kernel]
    else
      singleton_class.ancestors[1..-1] - [BasicObject, Object, Kernel]
    end
  end
end

as you can see, i define a method, for check all mixed class for a class or object in ruby.

  1. I add this file path into $RUBYLIB environment variable in my login shell, e.g. .bashrc.
RUBYLIB=$RUBYLIB:/home/zw963/foo

it same as use this ruby command line arg:

-Idirectory     specify $LOAD_PATH directory (may be used more than once)
  1. I set another env, $RUBYOPT, as: -rmixin_helper.

it same as use this ruby command line arg:

-rlibrary       require the library before executing your script
  1. After i done those steps, mixed_in methods was injected into any Object instance before my project start, i can always use like: SomeClass.mixed_in or some_object.mixed_in

Hope i explain clearly for this, if instead use Crystal, what i want is basically same.

I want to run all the code in https://play.crystal-lang.org/#/r/dfjf , from line 1 to line 187, before start any crystal program, as such,
i can always use pp! Foo.methods in my project, right?

if this is possible?

Thank you!

It’s possible if we include that functionality in the standard library, out of the box.

There’s no easy way to inject code to all programs. Crystal is not as flexible as Ruby here, and I think I prefer less flexibility here.

I also had a typo in my code but since it’s not called it’s not caught.

2 Likes

Yes, me too, though use years, but i really don’t like ruby on rails, for this reason, i really don’t like class_eval, eval, and dynamic arguments forwarding in ruby (e.g. optional parameter + options hash), even, pry is not so helpful in some cases, Sequel + Roda is better, the code is really robust, beautiful (as Crystal), But, when search pattern in source code use tools rg for Crystal project, is almost painless than Ruby Project because Crystal less flexibility.