How to Debug a Rackup App With Visual Studio Code - ruby

This question has been asked before; however, I have not found the solution that I came up with anywhere else after posting the question.
Debugging a Rack app (which is, of course, written in Ruby) from Visual Studio Code is problematic because Rack is actually a bash script, and the rebornix.Ruby debugger does not know how to handle it.
I have found many examples of how to do this (here is one), but I experienced errors like:
Fast Debugger (ruby-debug-ide 0.7.3, debase 0.2.5.beta2, file filtering is supported) listens on 127.0.0.1:1234
Uncaught exception: /home/mslinn/.rbenv/shims/rackup:3: syntax error, unexpected string literal, expecting `do' or '{' or '('
[ -n "$RBENV_DEBUG" ] && set -x
^
/home/mslinn/.rbenv/shims/rackup:3: syntax error, unexpected ']', expecting end-of-input
[ -n "$RBENV_DEBUG" ] && set -x
^
When not debugging, I launch my Rack app with bundler:
$ bundle exec rackup
This was my vscode launch configuration:
{
"args": [
"-p", "9292",
"-E", "development"
],
"cwd": "${workspaceRoot}",
"name": "Debug rackup application",
"pathToBundler": "${userHome}/.rbenv/shims/bundle",
"pathToRDebugIDE": "${userHome}/.rbenv/shims/rdebug-ide",
"program": "${userHome}/.rbenv/shims/rackup",
"request": "launch",
"showDebuggerOutput": true,
"type": "Ruby",
"useBundler": true,
},
The problem with the above launch configuration is that the rbenv shims are bash scripts. They invoke gems; however, I want to avoid hard-coding the path to the gems in the launch configuration because that will break the launch configuration when the gems used in the project are updated.
Gemfile is:
source 'https://rubygems.org'
gem 'data_mapper'
gem 'dm-sqlite-adapter'
gem 'sinatra', '>=3.0.5'
gem 'sinatra-flash'
gem 'warden'
gem 'debase', require: false
gem 'rackup', require: false
gem 'ruby-debug-ide', require: false
gem 'rubocop', require: false
gem 'rubocop-rake', require: false
gem 'rubocop-rspec', require: false
gem 'shotgun', require: false
gem 'thin', require: false

A manually created binstub for rackup turned out to be the way forward. Here is mine, saved in ${workspaceRoot}/bin/rackup:
#!/usr/bin/env ruby
# Prepares the $LOAD_PATH by adding to it lib directories of all gems in the
# project's bundle:
require 'bundler/setup'
load Gem.bin_path('rack', 'rackup')
The above binstub worked once I modified the launch configuration to the following:
{
"args": [
"-p", "9292",
"-E", "development"
],
"cwd": "${workspaceRoot}",
"name": "Debug rackup application",
"pathToBundler": "${userHome}/.rbenv/shims/bundle",
"pathToRDebugIDE": "${userHome}/.rbenv/shims/rdebug-ide",
"program": "${workspaceRoot}/bin/rackup",
"request": "launch",
"showDebuggerOutput": true,
"type": "Ruby",
"useBundler": true,
},
Problem solved! The manually created binstub should survive updates to rackup, without requiring any modifications.

Related

heroku bundler: failed to load command: rackup

I'm trying to deploy to heroku a modular sinatra app that works fine locally. This is in the heroku logs:
2020-12-28T21:05:15.907560+00:00 heroku[web.1]: Starting process with command `bundle exec rackup config.ru -p 58645`
2020-12-28T21:05:18.738254+00:00 app[web.1]: bundler: failed to load command: rackup (/app/vendor/bundle/ruby/2.7.0/bin/rackup)
2020-12-28T21:05:18.738283+00:00 app[web.1]: Gem::Exception: can't find executable rackup for gem rack. rack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?
The command bundle exec rackup config.ru -p 58645 runs fine locally.
This is my config.ru
require_relative './config/environment.rb'
use EntreeController
use UserController
run ApplicationController
and environment.rb
APP_ENV = ENV["RACK_ENV"] || "development"
ENV['SINATRA_ENV'] ||= "development"
require 'require_all'
require 'bundler/setup'
Bundler.require(:default, ENV['SINATRA_ENV'])
require_all 'app'
require_all 'app/models'
require_all 'app/helpers'
And the Procfile:
web: bundle exec rackup config.ru -p $PORT
I'll post my solution, if ever someone bumps into the same problem. I had followed the indication on the Rakefile here : https://github.com/sinatra-activerecord/sinatra-activerecord.
One solution was to entirely deleted the Rakefile when deploying to Heroku. The other solution is to put only this in the Rakefile :
require "sinatra/activerecord"
require "sinatra/activerecord/rake"
require "./app" # or whereever your app is
Switching to Bundler 2.1.4 solved the problem in my case.
For the longer run one will have to install bundler 2.1.4 and use it, but for the sake of test I just manually edited that line in Gemfile.lock:
BUNDLED WITH
2.1.4
I was using Ruby 2.7.2 and Bundler 2.2.8 — Heroku buildpack provided Bundler 2.1.4.
Funny that Heroku’s Bundler bugs list had nothing about 2.2.8.

Why do I get NameError with `bundle exec ruby [file]` given that the necessary gem is in Gemfile?

I'm doing some messing around to try to understand better how bundler works. I have just three files in my working directory:
Gemfile Gemfile.lock test.rb
All the Gemfile has is gem "slop" and test.rb looks like this:
puts Slop.parse
When I run bundle exec test.rb I get a NameError due to not having a require statement:
[ec2-user#xx my_app]$ bundle exec ruby test.rb
test.rb:1:in `<main>': uninitialized constant Slop (NameError)
But if I run bundle console, Bundler loads the gem correctly and I can run Slop.parse from the console without having to explicitly type require "slop":
[ec2-user#xx my_app]$ bundle console
irb(main):001:0> Slop.parse
=> #<Slop::Result:0x00000001339838...
So what am I missing? I was under the impression that since I don't have require: false in my Gemfile, Slop should be loaded when I run bundle exec ruby test.rb and I shouldn't need to put the require "slop" line in the file.
You need to config bundler to require all gems on your Gemfile like this:
require 'rubygems'
require 'bundler/setup'
Bundler.require(:default)
Check the docs at http://bundler.io/v1.12/bundler_setup.html
I was under the impression that since I don't have require: false in
my Gemfile, Slop should be loaded when I run bundle exec ruby test.rb
and I shouldn't need to put the require "slop" line in the file.
The bundler docs say:
Specify your dependencies in a Gemfile in your project's root:
source 'https://rubygems.org'
gem 'nokogiri' #<======HERE
Inside your app, load up the bundled environment:
require 'rubygems'
require 'bundler/setup'
# require your gems as usual
require 'nokogiri' #<========AND HERE
As for this:
I was under the impression that since I don't have require: false in
my Gemfile, Slop should be loaded when I run bundle exec ruby test.rb
and I shouldn't need to put the require "slop" line in the file.
The bundler docs are horrible on this point. As far as I can tell, :require => false is a Rails specific thing, which is used to decrease load times on project startup. In a rails app, specifying require: false means that the gem won't be loaded until you manually require the gem. If you don't specify :require => false, then the gem will be loaded automatically--however that is because rails code is written to do that automatic loading. Your app has no code that performs a similar function.
Edit: Made a mistake while testing. So here's the way it works: In a non rails app, e.g. in your test.rb, if you want to automatically require all the gems specified in your Gemfile, you need to write:
Bundler.require :default
The Bundler docs mention that in the fine print here:
Specify :require => false to prevent bundler from requiring the gem,
but still install it and maintain dependencies.
gem 'rspec', :require => false
gem 'sqlite3'
In order to require gems in your Gemfile, you will need to call Bundler.require in your application.
I'm not sure why that requirement was only mentioned in conjunction with require: false instead of being stated at the outset.
And, in your Gemfile if you specify:
gem 'slop', :require => false
(as well as Bundler.require :default in test.rb), then you also have to explicitly require the slop gem in test.rb:
require 'slop'
In other words, Bundler.require :default automatically requires all the gems in your Gemfile except the gems marked with require: false. For gems marked with require: false, you have to manually write require 'gem_name' in your app.
Therefore, neydroid posted the correct solution.
* In your Gemfile, you can nest gems in groups, which affects how Bundler.require() works. See the Bundler docs on groups.
You should add the require "slop" inside your test.rb

Heroku Production - ActionView::Template::Error (undefined method `directory' for #<Sprockets::Manifest:number>)

We have a react-rails app. Unfortunately, the app works on local development but not when deployed to heroku. When going to our default path on the app, we get the following error:
ActionView::Template::Error (undefined method 'directory' for #<Sprockets::Manifest:0x007fef13200aa8>)
We've figured out that it happens at this line in our view:
<%= react_component('NavBar', {}, {prerender: true}) %>
A few things about our app:
It uses browserify to compile our js.jsx.
We precompile using RAILS_ENV=production bundle exec rake assets:precompile after deleting the public/assets folder.
Works locally with both rails s and foreman start
We are using boostrap-sprockets. Even when removed, we still have this issue.
Here is our npm dependencies:
"dependencies": {
"browserify": "^10.2.4",
"browserify-incremental": "^1.5.0",
"classnames": "^2.2.3",
"reactify": "^1.1.0"
}
Here is our Gemfile
source 'https://rubygems.org'
gem 'rails', '4.2.3'
gem 'rails-api'
gem 'spring', :group => :development
gem 'active_model_serializers', '~> 0.10.0.rc1'
gem 'pg'
gem 'devise'
gem 'puma'
gem 'twitter'
gem 'react-rails', '~> 1.0'
gem 'browserify-rails', '~> 0.9.1'
gem 'bootstrap-sass', '~> 3.2.0'
gem 'sass-rails', '~> 5.0', '>= 5.0.4'
gem 'autoprefixer-rails'
group :test do
gem 'rspec-rails'
gem 'pry'
gem 'faker'
gem 'webmock'
end
group :development, :test do
gem 'factory_girl_rails'
end
group :production do
gem 'uglifier'
gem 'rails_12factor'
gem 'rspec'
end
ruby '2.2.4'
We would appreciate all help.
The solution from hours of searching on StackOverflow and github issues seems to be to remove
//= require react_ujs
from your application.js in your assets folder.
I ran into this same issue just today and followed the suggestions in: https://github.com/reactjs/react-rails/issues/443#issuecomment-180544359. I was still running into an error so for now I modified my heroku configurations (config/environments/staging.rb & config/environments/production.rb) to use
config.assets.compile = true
for now and server-side rendering worked fine. The react-rails folks said they have a pull request completed to fix this issue but I dont think it has been released yet.
Potentially relevant: rake assets:precompile undefined method directory? for nil:NilClass
This is an error that occurs when the Rails asset compiler (Sprockets) cannot find a directory that you've specified as the location of an asset for your project. My suggestion is to make sure that all your assets are being successfully deployed to heroku, and then check that your paths are set up correctly when you reference assets in your project for inclusion in a page.
Also see Eric C's answer pertaining to React-Rails specifically.
There was a bug in react-rails 1.6.0 which should be fixed in 1.6.1
Here's the patch:
https://github.com/reactjs/react-rails/pull/478
If the latest version doesn't work for you, please open an issue on the react-rails repo!

Bundler.with_clean_env does not find a JavaScript Runtime

I try to create a simple heroku app which clones a git repository, invokes bundle install, a rake dist task and then uploads the created files to a GitHub repository. The build task of the cloned repository uses Rake Pipeline, which uses the execjs gem for building the binary.
I've created a sample app located at https://github.com/pangratz/github-uploader-test, which illustrates the problem. The directory structure of the application is the following:
/upload.rb
/project
Rakefile
Assetfile
Gemfile
Gemfile.lock
The app itself is a simple sinatra app with a get '/' route, which looks like this:
upload.rb
get '/' do
Dir.chdir "project" do
Bundler.with_clean_env do
system "bundle install"
system "bundle exec rake dist"
end
end
end
For demonstration purpose, the project folder simulates the cloned git repository. It contains a Rakefile, Assetfile, Gemfile and Gemfile.lock.
project/Rakefile
desc "Build"
task :dist do
Rake::Pipeline::Project.new("Assetfile").invoke
end
project/Assetfile
require "rake-pipeline-web-filters"
require "json"
require "uglifier"
require "execjs"
puts ExecJS.eval "'red yellow blue'.split(' ')"
project/Gemfile
source "http://rubygems.org"
gem "rake-pipeline", :git => "https://github.com/livingsocial/rake-pipeline.git"
gem "rake-pipeline-web-filters", :git => "https://github.com/wycats/rake-pipeline-web-filters.git"
gem "colored"
gem "uglifier", :git => "https://github.com/lautis/uglifier.git"
group :development do
gem "rack"
gem "rest-client"
gem "github_api"
gem "ember-docs", :git => "https://github.com/emberjs/docs-generator.git"
gem "kicker"
end
The invocation of bundle install seems to work. The problem is that rake dist is failing with the error Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes..
The heroku app itself is created with the option --stack cedar.
I've also created a route '/test' which uses Execjs and this does not fail. So it seems
that there is a problem with the Bundler.with_clean_env and not finding the installed JavaScript Runtime ...
upload.rb
get '/test' do
puts ExecJS.eval "'red yellow blue'.split(' ')"
end
You could also try setting ENV['BUNDLE_GEMFILE']=/path/to/project/Gemfile. That should ensure that install and exec find the correct Gemfile.
I've opened an issue on Heroku and I could solve the issue. The problem was that the Dir.chdir("project") in combination with the Bundler.with_clean_env resulted in an invalid PATH which is needed to locate the JavaScript Runtime. Long story short, here is the working solution:
get '/' do
Dir.chdir "project" do
Bundler.with_clean_env do
ENV["PATH"] = "/app/bin:#{ENV['PATH']}"
system "bundle install --without WATWAT"
system "bundle exec rake dist"
end
end
end
A note on the solution: the --without WATWAT is needed because Heroku installs your app with bundle install --without development. Since the --without is remembered for successive calls, and the gems of development group are needed in the Assetfile, this option has to be overwritten. Using --without '' somehow doesn't work so I'm using the nonexistent group WATWAT to include all groups in the Gemfile.

bundler not working with rack application

Here is my code
# config.ru
require "rubygems"
require "bundler"
Bundler.setup
Bundler.require
FooApp = Proc.new do |env|
[200, {'Content-Type' => 'text/plain'}, "foo"]
end
Routes = Rack::Mount::RouteSet.new do |set|
set.add_route FooApp, { :request_method => 'GET', :path_info => %r{^/foo$} }, {}, :foo
end
run Routes
My Gemfile looks like this
source :rubygems
gem 'rack-mount'
I did bundle install which produced Gemfile.lock.
When I run
rackup config.ru
I get following error
uninitialized constant Rack::Mount (NameError)
The code works if I remove dependency on bundler. However I want bundler to work . This code is a small form of large application.
Bundler.require tries to load a file called rack-mount.rb (same as the name of the gem) inside the gem's lib directory. That's a Rubygems convention. With most Rack gem's this does not work, because they don't contain such a file.
Instead it's all about namespacing. rack-mount's lib folder for example contains a rack folder which contains a file called mount.rb (see rack-mount/tree/master/lib on GitHub).
To fix the problem, you have to tell Bundler which file to require:
source :rubygems
gem "rack-mount", :require => "rack/mount"
The Bundler documentation contains further information about how to write a Gemfile.

Resources