Kernel.system call from rails - ruby-on-rails

I am invoking a shell script using Kernel.system from my Rails controller. The shell script might invoke another Ruby script based on some conditions. This Ruby script requires the twitter gem. My Rails app is running in apache using Passenger. Now when this Ruby script is invoked from my Rails app, I get the following error in apache logs.
/var/www/webapps/test/twitter/twitter_post.rb:2:in `require': no such file to load -- twitter (LoadError)
from /var/www/webapps/test/twitter/twitter_post.rb:2
The same Ruby script runs fine from the Linux shell. Now, if I list the twitter gem in my Gemfile, it works perfectly. Kernel.system is supposed to invoke the commands in a subshell, so is Rails modifying any environment variables in its execution shell?

A subshell has the same environment as the process that spawned it, so the right thing is occurring here, since Bundler overwrites Ruby's load path with its own to ensure only the gems in the Gemfile get loaded.
If your app depends on this process running, and that process depends on the twitter gem, why is it not in your Gemfile anyway? Further, why are you executing Ruby in a subshell from Ruby in the first place? There's usually no reason you shouldn't just have that Ruby code within your app.

you should not be making a system call to another ruby script, you should require or include the contents of the other ruby script.
That being said, your current rubygems environment when you invoke the rails server will be that of the rails application's bundle. If the rails application's bundle does not include the gems that your other ruby script requires, then your other ruby script will not be able to require that library.
So, add to your rails app's Gemfile the the library that the other script is complaining it is missing and I think that should do ya.
In your external ruby script try:
require 'bundler/setup'
taken from bundler setup docs

Related

Bundler::RubyVersionMisMatch when Rails controller invokes external script

Ubuntu 20 > Apache 2.4 > Passenger 6 > ruby 2.7.4 > Rails 6.1 > `ruby script`
I'm invoking a ruby script via backticks from within a Rails controller (and collecting a return integer).
In production, somwhere in this chain, something is insisting on trying to run the ruby script with ruby 2.7.0 (the native system version) and complaining that it should be 2.7.4 (which it should).
The Rails app itself runs fine until I invoke a script.
When invoking the script, Passenger (I think?) is logging in the Apache error.log with
/home/rcm/.rbenv/versions/2.7.4/lib/ruby/2.7.0/bundler/definition.rb:495:in `validate_ruby!':
Your Ruby version is 2.7.0, but your Gemfile specified 2.7.4 (Bundler::RubyVersionMisMatch)
I have searched and tried numerous suggestions (most of which were already done).
All scripts use: #!/usr/bin/env ruby
$PATH includes /home/rcm/.rbenv/shims:/usr/local/sbin:... and the usual suspects (I see that's rather explicit with /home/rcm/.rbenv instead of ~/.rbenv --- does it matter?)
The apache .conf file has: PassengerRuby /home/rcm/.rbenv/versions/2.7.4/bin/ruby
I have .ruby-version files at every layer I can think of specifying 2.7.4
I have used rbenv to declare global 2.7.4.
I have updated bundler.
I have run bundle install.
Restarted apache, rebooted the system, etc.
I can't figure out where in the chain anything could be trying to run the script with 2.7.0.
It all works fine in dev mode on macOS and Ubuntu, and even works fine running the project in e -production mode with the built in Puma on both macOS and Ubuntu. Only in the full Apache > Passenger production mode is there a problem. So, I'm thinking it has to be Passenger that's confused, but it's running the Rails app itself just fine -- which makes me confused.
The screwy part is, this was all working fine, and "all of a sudden" I'm getting this failure. (No idea which bit I twiddled.)
Would appreciate any ideas to give me something new to chase. Thanks.
ruby is not necessarily the same as the ruby running Rails. It will run whatever the first one it finds in its PATH. Since this is running on a webserver, this will be different from the PATH of your own user.
Use bundle exec ruby to run the Ruby associated with the application.
The best thing to do is to not run a script, instead incorporate the script code as a class in your Rails project. This is faster and easier to test.

How do the rails executed in bash

Today I wanted to get how do the rails db:drop works. How did I thought: rails is a name of executive file that executed by Linux system when I type rails db:drop and db:drop is an argument for this file.
This file could be created in different languages, it even could be a binary. But I suppose it is created by a Ruby. So I went to the rails repo. And start reading the code of rails.gemspec but did not find any hints about how do the system parses cli requests.
As I suppose the parsing of rails arguments is work of the rake. But where is the code that points that rake should be started when we call rails?
And what is the brief structure of rails execution?
There must be a rails file, in some directory that mentioned in the PATH variable. And system seek for this rails file, select appropriate interpreter ans start to execute code in this file. Where is the code that create this rails file, and what is the code of this file itself?
File rails is placed in <your_application_root>/bin/ folder.
bin/rails contains someting like this:
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'
Although, this ^ might be a little outdated, this is just what I found by quick search as an example. Better you check the file in your own application with your version of rails.
Here is some useful information about rails console utilities:
https://edgeguides.rubyonrails.org/command_line.html
Also, from my experience, it's good to have something like RubyMine IDE for learning Ruby on Rails: https://www.jetbrains.com/ruby/download/
It's not free, but there is a 30-days trial which might be enough for understanding most of the basics of the framework.
The main advantage of this IDE is that it has very good integration with ruby version managers such as RVM, where you can add ruby sources and rails gems to the search indexing and easily navigate to methods/files/constants/etc. definitions right from the code. This is much faster than searching/opening a particular file on GitHub.
How to the rails works from the Linux shell point of view?
"rails" command is a part of the Railties gem
Start rails file that stored at the ruby_instalation_dir/bin. This file is generated by RubyGems from the Railties gemsec file. (https://stackoverflow.com/a/59269405/8574922)
rails file is a Ruby file that calls railaties/rails file
and the railaties/rails file requires rails/cli.rb
Yes rails is an executive file that stores in ruby_destination/bin folder. This is a Ruby file generated by RubyGems.
And this files calls for another Gem called Railties that can be installed in the same bin folder, or in the Gems folder, that can be found by a bundle show --paths command. Here it is this executable file that is called from rails.
So the journey begins from the require "rails/cli" of the Railties.

What is the difference between rails console and bundle console?

Can anyone explain me or give me a resource where I can learn the differences between rails console and bundle console? Is there way to load all the gems automatically in irb instead of require gem?
Here is a good explanation: What's the Difference Between irb, bundle exec irb, bundle console, and rails console?
irb is the basic Ruby console. It ignores your Gemfile, and only core
Ruby classes are accessible without require-ing them. It can’t easily
load gems that Bundler installs outside of RubyGems’ load path.
bundle exec irb is like irb, if you also required bundler/setup. You
can only easily require gems that are in your Gemfile.lock, but you
can load those gems no matter where Bundler put them.
bundle console is like bundle exec irb, if you also called
Bundler.require. All of the gems in your Gemfile, except the ones
marked require: false, can be used without requiring them. It’s really
convenient when you’re writing your own gems, or working on non-Rails
code.
rails console is like running bundle console inside a Rails app, if
you also required config/environment.rb. You can play with your entire
Rails app, autoloads and database connections work, and everything’s
hooked up the way you’d expect. If you’re working in a Rails app, this
is the most helpful kind of console.
The answer from Aleksandr is great.
I just wanted to add there is also the option for running
bundle exec rails console
which combines everything from rails console and bundle exec irb answer from Aleksandr's answer.

How to get location of gem using bundler or Rails from within Ruby?

I need to get the current working directory of a gem from within a Rails application.
I currently use
`bundle show foo`.strip
This works great in my environment, but:
it's slow because it requires loading a shell
it will probably break when somebody tries to run the app on Windows or on JRuby or when their $PATH points at a different ruby than the one used to start the app
So I'd like a way to do this without invoking a subshell.
The RDOC for Bundler hasn't been helpful. You used to be able to get this sort of information from Rails itself in Rails 2, but it appears that Rails 3 lets Bundler handle it.
If a gem is in your Gemfile/Gemfile.lock, its path appears in the $LOAD_PATH global variable. You can take advantage of this fact:
$:.grep(/activerecord/).first
$: is the same as $LOAD_PATH. You can use either.
Another approach:
Bundler.load.specs.find{|s| s.name == 'activerecord' }.try(:full_gem_path)

What is the logic behind tasks in Rake versus under `rails`

I try to grasp the logic behind some tasks being "rails" whereas others, the majority, is found as rake task. Why rails server and not rake server for example?
I can understand that the bootstrapping cannot be done in rake: after all, you first need a rakefile and other requirements before you can start using rake. So creating the project with a rails binary seems only practical.
But why generate, server, console, yet not migrate or assets? I don't see the logic. Is there any?
IMO the rails scripts are for "live" console usage, like during development.
The rake tasks are more "automated" tasks, for example, that might be run as part of a build or deploy cycle, like by a CI server. Some rake tasks might group rails/etc. commands together (like tests).
A rake script is a utility/build tool for some common tasks when developing. For example, you need to do deployment, run test, database stuffs, truncate log files, compile assets .... You can create your own custom rake scripts.
A rails script is ruby file located under script directory for the purpose of the gem rails. This is what the gem does. Rails is a ruby web framework, so the command rails is for starting the rails apps, go to rails console, generate files. It's bundled when you install the gem.
You can think of rails command like bundle command for bundler. bundle install, bundle update ... all are related to resolving gem dependencies. rspec command for running tests...
Some gems has an executable script such as rails, bundler, capistrano, whenever, rspec. Some other gems doesn't have such as builder, will_paginate....
You can check this out for how to add executable to a gem, http://guides.rubygems.org/make-your-own-gem/#adding-an-executable

Resources