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

cli: add "amber exec" command for executing one-liners (#352) #371

Merged
merged 24 commits into from Nov 11, 2017

Conversation

sam0x17
Copy link
Contributor

@sam0x17 sam0x17 commented Nov 9, 2017

Description of the Change

Adds a simple CLI command for executing one-liner pieces of crystal code within the application scope and printing the result.

Syntax:

$ amber exec 'puts User.first'
#<User:0xc04f80>
$ amber x # opens file and runs it on save/exit.
$ amber x -b 1 # runs the previous saved command.
$ amber x file.cr # copies file contents to new tmp file opens in editor and runs on exit.

update:

  • now supports amber encrypt-style syntax using your preferred text editor
  • now supports specifying a .cr file instead of code

related issue #352

Alternate Designs

  1. ICR: bad because it's a fake REPL and can cause unintended side effects since all lines are re-run each time a new line is added
  2. reworking amber play: bad because it requires a web browser and suffers from similar re-running issues

Benefits

  • extremely simple
  • enables effective devops by allowing code to be executed against the preferred amber environment
  • e.g. you could do AMBER_ENV=production amber exec 'code here'
  • does not suffer from code-rerunning issues, since we are only running what you can cram onto a single

Possible Drawbacks

  • implementation would be better if it automatically detected whether or not you are currently in an amber project directory, instead of assuming you are in the root of a project. I'm open to suggestions on how we should handle this
  • would be cool if we could cache some of the compilation somehow so successive calls to amber exec only need to re-compile changed files, but this might be overkill

Testing

I have manually tested this on my system. I'd be happy to write unit tests -- not familiar with specs in amber but I can dive in and figure it out if required. If someone could provide some high level advice on writing specs for this I'd greatly appreciate it (i.e. testing this command requires a dummy project skeleton to be in place).

Copy link
Member

@elorest elorest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also probably need to load the models.

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 9, 2017

@elorest interestingly it seems I don't, at least in the app I'm testing e.g.:

$ amber exec "u = User.first; puts u.first_name if u"
Sam

@elorest
Copy link
Member

elorest commented Nov 9, 2017

I was planning on building a concept similar to this that opened up an editor and let you type whatever code you wanted. On exit it would run and print the output. Similar in a way to edit in pry but with the REPL.

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 9, 2017

@elorest I really like that idea, so maybe we should just wait for you to do that

@elorest
Copy link
Member

elorest commented Nov 9, 2017

This command could probably do both of those. The specs in Amber are just using crystal spec. Fairly easy to wrap your head around. Look at some of the other CLI specs. And copy them. You really just need to verify that calling it returns the output you expect.

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 9, 2017

@elorest kind of the way git commit works?

@elorest
Copy link
Member

elorest commented Nov 9, 2017 via email

@elorest
Copy link
Member

elorest commented Nov 9, 2017 via email

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 9, 2017

@elorest I'd agree with that but I think it also might be nice maybe as a separate PR to make it so that you can specify an AMBER_EDITOR environment variable so amber encrypt and this could use whatever editor without having to specify each time. nano guy here heh

@veelenga
Copy link
Contributor

veelenga commented Nov 9, 2017

@elorest @sam0x17 i was trying to build a similar concept. You should give it a look https://github.com/veelenga/vicr. I often use it to answer questions on SO :)

Brew installation is broken, but you can just run it locally with make build && ./out/vicr

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 9, 2017

@elorest ok so maybe the zero-arg would open the editor, and if you provide an arg then it works as above -- I'll see what I can do and base it partially off what (I'm guessing you?) did for for amber encrypt

@veelenga I'll check it out for sure!

@sam0x17 sam0x17 force-pushed the amber-exec branch 3 times, most recently from b7e9e02 to 18a8bda Compare November 9, 2017 09:03
@elorest
Copy link
Member

elorest commented Nov 9, 2017

Yeah you could also look at an ENV var but if not ENV or flag is provided I would default to vim.

ENV["EDITOR"] || options.editor

@elorest
Copy link
Member

elorest commented Nov 9, 2017

@veelenga What I'm thinking of is really simple, maybe like 5 additional lines of code. I'll look at that code you linked though.

@eliasjpr
Copy link
Contributor

eliasjpr commented Nov 9, 2017

Just throwing this to see what you guys think

Workflow:
1 - amber exec -e nvim (This creates a hidden cr file something like .console.cr)
2 - Edit (Add code remove code etc)
3 - saves and build printing results

I basically did a proof of concept, kinda within an amber project dir
1 - touch .console.cr & nvim ./.console.cr
2 - Added puts "Hello World!" of course
3 - crystal build ./.console.cr
4 ./.console => "Hello World!"

If you happen to type amber exec again it will open the previous session. I would have to .gitignore the .console.cr file.

With this exec who needs a console! Nice work @sam0x17

@veelenga I really like what you did!

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 10, 2017

@eliasjpr thanks -- they way I dealt with that particular issue if you look at my force-push from last night is I delete the file right after it is compiled, so no need to add it to .gitignore since it only exists on the filesystem for a few milliseconds.

You will notice I also added support for doing a one-liner inline without opening the editor, and I added support for specifying a path to an existing .cr file. I'm doing some specs for this tonight so after that should be ready to review.

Note that this .cr file detection is performed by checking if the argument to amber exec is a string ending in '.cr', and if it is, it checks if it is a file that exists, otherwise the argument is treated as crystal code. I've been using this for a day now and it seems to work quite well.

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 10, 2017

side note: I would be curious to know what trick ICR is using to pretty print models from granite-orm. e.g. in ICR when I print a model, I get all the attributes, but with this I just get <ModelName:memory_address>

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 10, 2017

@eliasjpr do you think it would be useful to include a --persist option that will create the behavior you describe above where the file stays after executing so you can re-edit it and potentially run again?

@elorest
Copy link
Member

elorest commented Nov 10, 2017

@sam0x17

side note: I would be curious to know what trick ICR is using to pretty print models from granite-orm. e.g. in ICR when I print a model, I get all the attributes, but with this I just get ModelName:memory_address

Use model.inspect to get all the information.

@eliasjpr do you think it would be useful to include a --persist option that will create the behavior you describe above where the file stays after executing so you can re-edit it and potentially run again?

My suggestion would be to add temp files to ./tmp/console_[timestamp].cr. This is already added to the .gitignore. That way you could have a back flag and run the files
from before. amber exec --back 1 or amber exec --back 2 to run last one or one before that.

@faustinoaq
Copy link
Contributor

my suggestion would be to add temp files to ./tmp/console_[timestamp].cr

@elorest I think we can use https://crystal-lang.org/api/0.23.1/Tempfile.html here

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 10, 2017

@faustinoaq @elorest ok I'll change it to use Tempfile -- btw just pushed changes that add the --persist option I spoke of

@elorest
Copy link
Member

elorest commented Nov 10, 2017

@sam0x17 I don't think that a persist option is the best route. Just store all files with a timestamp in ./tmp That way anyone can be accessed with a flag like with pry in ruby.

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 10, 2017

@elorest that makes sense -- feel free to remove the --persist option in your commit when you add the timestamp stuff

@sam0x17
Copy link
Contributor Author

sam0x17 commented Nov 10, 2017

thoughts about enclosing the code for one-liners in a puts (code).inspect e.g. crystal eval 'require "../config/*"; puts (#{args.code}).inspect' so that it behaves more like a one-liner in ICR?

I just went to use amber exec a few mins ago and forgot it won't print anything unless I explicitly puts

Copy link
Contributor

@marksiemers marksiemers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall - just have the spec file wrapped in the check for the build flag, and add that context wrapper.

describe "amber exec" do
cleanup
scaffold_app(TESTING_APP)
`shards`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this test requires a build, it should be wrapped checking for the run_build_tests flag.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot about one of the implications of this, you have to change a line in the Dockerfile to make these tests run on Travis. Right now, the test suite is a false green because the tests for this PR are skipped.

https://github.com/sam0x17/amber/blob/amber-exec/Dockerfile#L9
Needs to be CMD ["crystal", "spec", "-D", "run_build_tests"]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been a pending change from here: https://github.com/amberframework/amber/pull/354/files#diff-654a8a2c7db020a0d9cf8c58982c3864R9

That's the big Spec PR that has been held up for various reasons.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're breaking my heart man.

# This one weird trick, to make the specs pass in linux leaves scientists puzzled.
Dir.glob("tmp/*")
logs = Dir.glob("./tmp/*_console_result.log")
File.read(logs.last?.to_s).should eq expected_result
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We get more helpful failures, rather than errors with a nil check:

logs.last?.should_not be nil
(File.read(logs.last?.to_s).should eq expected_result) if logs.last?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is optional.

Dir.glob("tmp/*")
logs = Dir.glob("./tmp/*_console_result.log")
File.read(logs.last?.to_s).should eq "1000\n"
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap the two above with context "with edit flag '-e'" do ... end

MainCommand.run(["exec", "-e", "tail", "-b", "1"])
logs = `ls tmp/*_console_result.log`.strip.split(/\s/)
File.read(logs.last?.to_s).should eq "1000\n"
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap lines 28-38 in a context "in editor mode" do ... end

@elorest elorest merged commit 8dce363 into amberframework:master Nov 11, 2017
elorest added a commit that referenced this pull request Nov 11, 2017
elorest added a commit that referenced this pull request Nov 11, 2017
elorest pushed a commit that referenced this pull request Nov 17, 2017
* cli: add "amber exec" command for executing one-liners (#352)

* this now behaves as close to a console as we can make it

* finishing touches for amber exec

* do not run editor unless in editor mode
* always return a result
* minor wording change in error message

* amber exec specs

* cleaned up code a bit and copied existing file to new temp file for edits

* specs pass yay. MainCommand.run always returns nil which makes testing hard

* new push to see if travis passes. Pass locally so I'm confused.

* debugging to figure out why travis doesn't pass

* added sleeps to see if that works

* one last thing

* what the what?

* i hope this will clear it up

* weird hack that i think fixes specs on linux

* commented weird hack

* native ls had better work

* formatted and fixed tests probably

* removed some vestigual code.

* removed macro flag for now

* test47 of travis

* namespaces specs

* sorted ls results
elorest added a commit that referenced this pull request Nov 17, 2017
elorest pushed a commit that referenced this pull request Nov 17, 2017
* cli: add "amber exec" command for executing one-liners (#352)

* this now behaves as close to a console as we can make it

* finishing touches for amber exec

* do not run editor unless in editor mode
* always return a result
* minor wording change in error message

* amber exec specs

* cleaned up code a bit and copied existing file to new temp file for edits

* specs pass yay. MainCommand.run always returns nil which makes testing hard

* new push to see if travis passes. Pass locally so I'm confused.

* debugging to figure out why travis doesn't pass

* added sleeps to see if that works

* one last thing

* what the what?

* i hope this will clear it up

* weird hack that i think fixes specs on linux

* commented weird hack

* native ls had better work

* formatted and fixed tests probably

* removed some vestigual code.

* removed macro flag for now

* test47 of travis

* namespaces specs

* sorted ls results


Former-commit-id: 3a55d2a
elorest added a commit that referenced this pull request Nov 17, 2017
@faustinoaq faustinoaq added this to Done in Framework 2018 May 5, 2018
@faustinoaq faustinoaq removed this from Done in Framework 2018 Jun 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

6 participants