2

I have a simple Ruby (Sinatra) server that starts up without issue from the command line with ruby app.rb. But when I execute the same command via my command line app, either with `ruby app.rb` or with system("ruby app.rb"), I get this error:

app.rb:1:in `require': cannot load such file -- sinatra (LoadError)
    from app.rb:1:in `<main>'

The opening line of app.rb is simply require 'sinatra'. The Sinatra gem is installed in my system, of course; I don't understand why the interpreter is acting as if it's not there.

While troubleshooting, I decided to add Sinatra to the Gemfile of the command line app that is calling app.rb. Lo and behold, now that the parent process has access to Sinatra, now it works (i.e., system(app.rb) successfully starts the Sinatra server). But when I exit the command line app, a Sinatra server is always there, saying:

[2018-12-18 23:17:37] INFO  WEBrick 1.3.1
[2018-12-18 23:17:37] INFO  ruby 2.4.0 (2016-12-24) [x86_64-linux]
== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from WEBrick
[2018-12-18 23:17:37] INFO  WEBrick::HTTPServer#start: pid=27384 port=4567

So I have to Ctrl-c to exit the command line app.

Question: Is there a way to spawn an independent Sinatra process/server, as I was trying to do with system("ruby app.rb"), without installing it in the parent app (the command line app)? I also tried using Process.fork followed by Process.wait, but that didn't help.

globewalldesk
  • 544
  • 1
  • 3
  • 18
  • `system("ruby app.rb &")` to start it in a separate process – Raj Dec 19 '18 at 04:38
  • Thanks, but that above doesn't actually solve the problem. I'm still getting the same error message: `app.rb:1:in 'require': cannot load such file -- sinatra (LoadError) from app.rb:1:in '
    '`
    – globewalldesk Dec 19 '18 at 04:51
  • I would guess that the `system` call ends up not being in the directory that you expect (probably whatever folder that ruby is actually installed in), and you should instead provide an absolute path to the app.rb file. In the Rails world, I’d try prefixing it with `Rails.root`. Not sure if Sinatra has anything similar to get your app root dir easily. – Nate Dec 19 '18 at 04:53
  • 3
    Looks like a versioning issue. Do you use some ruby version manager, like `rvm`, `rbenv` or like? If yes, I’d suggest you should export the respective `PATH` from your script, since `system` would not load all your dotfiles. – Aleksei Matiushkin Dec 19 '18 at 06:09
  • I suspect @Nate is onto something. I'd check the environment for both. For example, when a bundle is installed with --path, bundler sets the GEM_HOME environment variable to point to the path given, tools maybe a factor. If Sinatra isn't in the current gem home then the interpreter isn't going to find it. You could add it to the load path yourself but that's obviously a bother. Forking won't change the environment. You might check by `cd`ing to the app dir first with your system command, or manipulating the load path in the Sinatra app just to see. – ian Dec 21 '18 at 03:05
  • @Nate: I tried `/Users/lsanger/.rvm/rubies/ruby-2.5.0/bin/ruby app.rb` -- is that what you mean? I also tried spelling out the full path of `app.rb` in case that's what you meant. It didn't make any difference in any case. The command line script (Revuu) does `cd` to the app directory before running `ruby` on the app file. – globewalldesk Dec 27 '18 at 19:59
  • @AlekseiMatiushkin, yes, I do use rvm. Sorry, but I just don't know how I'd go about exporting the respective `PATH` from my script. Can you explain a bit more so I can search about how to do what you're suggesting? – globewalldesk Dec 27 '18 at 20:05
  • @iain, as with Aleksei, I just don't know how to implement your suggestions. How do I check the environment? What would I be looking for? What does it mean to say Sinatra is or isn't in the "current gem home"? Clearly something is missing from my Ruby education...please give me a pointer to necessary background. TIA... – globewalldesk Dec 27 '18 at 20:11
  • @globewalldesk I was thinking you would want an absolute path on the “app.rb” part, since that’s the file that it can’t seem to find. But the point about the ruby file is interesting as well. Can you output `which ruby` from inside your system call? That would let you know the path to the ruby executable that it’s using (theoretically it’s the rvm one you posted in the comments). I’m not sure exactly what you’re trying to do, but if it were me, I’d use Ruby on Rails and a Rake task. I think it would make your life easier. I know that’s a big change though, and not what you are asking for. – Nate Dec 27 '18 at 23:21
  • @Nate I'm pretty sure it's finding app.rb, because it's complaining that it can't find `sinatra` in `app.rb:1`. Line 1 of app.rb is where Sinatra is required. So it's locating the file. `which ruby` output = `/home/globewalldesk/.rvm/rubies/ruby-2.4.0/bin/ruby` (on a diff computer from above). The app is [here](https://github.com/globewalldesk/revuu). I can easily set up a Rakefile and a Gemfile (I've tried the latter in the course of working on this issue). The app basically runs code snippets the user types in in answer to coding challenges. Some snippets start servers, so... – globewalldesk Dec 28 '18 at 00:38
  • Oh crap. How did I not realize this...? Finding app.rb was never the problem... So, how do you have Sinatra installed? Is it via bundler or a global gem install? Maybe you need to do `system('bundle exec ruby app.rb')`? – Nate Dec 28 '18 at 00:41
  • Makes sense. Let me try that out (again? I've tried a lot). Sinatra is a global gem install and I just require it on the first line of the Sinatra script. I can make a Gemfile and use `bundle exec`...thought I did this, but maybe not. As I said in the question, when I include Sinatra in the command line (Revuu) app's Gemfile, it actually works when I call system("ruby app.rb"), but this results in a Sinatra server running in the background of Revuu. Very weird... – globewalldesk Dec 28 '18 at 00:51
  • Update: I put the Sinatra requirement in a Gemfile, and the command now executed by `system()` is: `cd data/repos/movies&&bundle exec ruby app.rb`. I even put `bundle install` in there just before `bundle exec`, and it made no difference. Now the error is, unsurprisingly, `app.rb:3:in `
    ': undefined method `enable' for main:Object (NoMethodError)`. Line 3 = `enable :sessions`, a Sinatra method call.
    – globewalldesk Dec 28 '18 at 02:20
  • Update 2: To replicate the problem with a different gem: I required Colorize in the Gemfile and added a new line 3: `puts "I am yellow!".colorize(:yellow)`. As expected, the interpreter says, `app.rb:3:in `
    ': undefined method `colorize' for "I am yellow!":String (NoMethodError)`. So it's not a Sinatra issue.
    – globewalldesk Dec 28 '18 at 02:22
  • https://medium.com/@connorstack/understanding-ruby-load-require-gems-bundler-and-rails-autoloading-from-the-bottom-up-3b422902ca0 Scroll down to the “How does Rails load all my gems?” section. Can you try adding those two lines to your app.rb file before the require sinatra line? I think the real problem is that the gems are not loaded in yet, and these two lines should help. – Nate Dec 28 '18 at 03:16
  • Thanks for the help. I added those two lines (the `ENV` line was new), but...no difference. – globewalldesk Dec 28 '18 at 12:32
  • Hi again, globewalldesk. Some quick questions - 1) Is the revuu app installed as a gem (via Bundler or not) and then run from the same directory as the Sinatra app? 2) Have you sandboxed the Bundle? e.g. `bundle install --binstubs --path=vendor.noindex`. You could remove the `.bundle` and `Gemfile.lock` to start with a clean slate. – ian Dec 30 '18 at 03:32
  • To see the env either put `warn ENV.inspect` in the calling revuu's file, or you could use `echo $GEM_ROOT`, `echo $GEM_PATH` and `echo $GEM_HOME` in the directory you run it from. There's also `bundle env` to look at. https://stackoverflow.com/a/11277228/335847 – ian Dec 30 '18 at 03:34
  • @iain and all: Thanks very much. You set me on the right path. I figured it out. See my answer below. – globewalldesk Jan 07 '19 at 01:40

1 Answers1

1

Self-answered, since I found a solution, and nobody else has answered it:

Instead of executing just ruby app.rb, which by itself runs in the same environment as the surrounding program and hence uses the same Gemfile (ignoring the one referred to in the spawned script), execute:

system("BUNDLE_GEMFILE='./Gemfile' && ruby app.rb")

That's all there was to it! This tells Ruby to load the correct gemfile when it runs the program.


Personal note: Many thanks to all of you in the discussion, above, on the question, as well as the local Ruby group, my best friend, and my 12-year-old coder son (who actually supplied the exact code). Now I just have to figure out how to stop the spawned process without stopping the surrounding program. Can't just use "Ctrl-C." But that's a different problem!

globewalldesk
  • 544
  • 1
  • 3
  • 18
  • 1
    Glad you found the answer (and should we feel proud or scared that a 12 year old beat us to the punch? (0_º) :) Take a look at [this flow chart](https://stackoverflow.com/a/37329716/335847) and [`Process.kill`](https://ruby-doc.org/core-2.6/Process.html#method-c-kill). – ian Jan 07 '19 at 02:45
  • 1
    Wow, that's a fantastic flow chart. OK, I think I know what I'm going to have to do. – globewalldesk Jan 07 '19 at 02:57
  • 1
    I probably use it daily, so helpful. I can also recommend Jesse Storimer's book Working with Unix Processes, but there's other good stuff out there on the subject. – ian Jan 07 '19 at 04:18