Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re runs all previous commands generates unexpected results #127

Open
owen2345 opened this issue Apr 10, 2021 · 6 comments
Open

Re runs all previous commands generates unexpected results #127

owen2345 opened this issue Apr 10, 2021 · 6 comments
Labels

Comments

@owen2345
Copy link

owen2345 commented Apr 10, 2021

Hi,
I am using this cli with Amber and having the following:

$ icr
$icr > require "./config/application.cr"
$icr > Article.count ==> 0
$icr > Article.create(title: "sample one") ==> # <Article:0x7fa53...
$icr > Article.count ==> 2
$icr > Article.count ==> 3
$icr > Article.count ==> 4
...

Every time I run any new command, all the previous commands are called again, in this case Article is being created.

@jwoertink
Copy link
Collaborator

Yup. That's actually how ICR works. I thought we had a message that printed when you ran icr to actually tell you this 🤔 this thing... not sure why it didn't show for you.

Since Crystal doesn't have an interpreter, the only way to evaluate the code is to shove it in to a file, compile that file, and run it. Each time you run some code, it appends to that temp file, compiles it and runs it. If it didn't do this, then variables wouldn't work line to line. So in your case, it's calling Article.create every time because each time you hit enter, it runs that code.

Unfortunately, until Crystal gets a built-in repl, this is just how it's going to work. Here's more discussion on it #79 if you'd like to chime in on that.

@owen2345
Copy link
Author

Hey @jwoertink
You are right!
I am working in a PR to improve this feature as the following:

  1. For regular commands, search for all command variables, sample: var1 = 10; var2 = Article.first; var3 = Article.create(name: "A1")
  2. Serialize all command variables, like: serialized_article = Crystalizer::JSON.serialize(var2)
  3. Save the command with its serialized variables:
      @commands << Command.new(type, command, vars_cmd: local_vars.map { |name, val|
        "#{name} = Crystalizer::JSON.deserialize(#{Crystalizer::JSON.serialize(value)}, #{typeof(val)})" }.join("; ")
      )  
  4. When rerunning commands, use vars_cmd for regular commands instead of the command entered by the user
  5. Example:
      $> require "./config/application.cr" # amber application
      $> require "crystalizer/json" # (de)serializer library
      $> a=Article.first; ss = Crystalizer::JSON.serialize(a); dd=Crystalizer::JSON.deserialize(ss, typeof(a)); puts [a && a.number, dd && dd.number] # ==> [10, 10]

All points are working as expected except #1 for which I could not find the way to retrieve all the local variables from the command, something like this: https://ruby-doc.org/core-3.0.1/Kernel.html#method-i-local_variables
I tried with instance_vars but it does not return the expected vars. Sample:

macro print_vars
  var1 = 10
  var2 = 10
  {% puts @type.instance_vars %}
end
print_vars # ==> []

Maybe you @jwoertink or some else can help me with this and then create the corresponding PR.

Best regards!

@asterite
Copy link

@owen2345 Could you explain how this will work when a variable is, for example, a file that points to an open file descriptor? The file descriptor will be gone.

You just have to accept the limitations of icr. The other way to do this would be:

  1. Implement a proper REPL directly in the crystal compiler
  2. Implement an interpreter for Crystal

I think there's no fix for icr that will work all the time.

Do you know about the command crystal play? Maybe that's something that would be more helpful to you in this case.

@asterite
Copy link

Also not every type is JSON serializable.

Finally, I saw you posted a request on the forums to get the local vars. You don't need that in the compiler. You can parse the source code provided to icr and gather local vars there.

@jwoertink
Copy link
Collaborator

Hey @owen2345, I applaud you for attempting this. I tried for a bit with the idea of using something like redis to store data between compiles, and memoize each call. Crystal doesn't have a way to Marshal data though, and like @asterite said, not every type is JSON serializable, so eventually my path was bunk.

I know it's not the best answer, but really I think there's not much you can do about the limitations. If using the crystal play doesn't work for you, an alternative could be to just add a sandbox file in your app that you can open up, add some quick code, and run as needed while in development. Maybe add that to your gitignore.

I don't want to discourage you, but I also would hate to have you waste a ton of hours and energy on something that could be better spent elsewhere.

@owen2345
Copy link
Author

owen2345 commented Apr 13, 2021

Hey @jwoertink @asterite thanks for answering.

Of course it is possible, check this in progress PR: #128
Note: As you mentioned before, I could not see a way to retrieve all declared variables inside a command (TODO). For now I am using a regex while a feature similar/alternative to this is implemented: https://ruby-doc.org/core-3.0.1/Kernel.html#method-i-local_variables

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants