Vừa rồi mình đi Euruko 2017 ở Budapest, một số bài nói cũng khá thú vị nên mình sẽ note lại ở đây.
A few days ago I encountered a strange behavior of Bundler so this post notes down how my experience with it was.
We know what
Bundle.setup to deal with
dependencies grouping/requiring in our project. With
Bundle.setup we can explicitly
specify which gem groups we want to add to
Says we have an Gemfile
gem "rack" gem "sinatra" gem "puma", group: :production
As the API tells, we can make only “development” gems available for requiring.
# config.ru require 'bundler' Bundler.setup(:default, :development) require 'sinatra/base' class MyApp < ::Sinatra::Base; end run MyApp
Then boot up the server with
bundle install bundle exec rackup # Puma starting in single mode... # * Version 3.8.2 (ruby 2.2.2-p95), codename: Sassy Salamander # * Min threads: 0, max threads: 16 # * Environment: development # * Listening on tcp://localhost:9292 # Use Ctrl-C to stop
KABOOM ! Puma is booted! Why?
By default Rack will boot WEBrick if there’s no server found. But it will detect for a more powerful server like Puma or Thin and if there is one, Rack will prioritize to load that instead.
But isn’t Puma un-requireable? Haven’t we grouped that server to only appear on
Bundler.setup should have their job done well?
The tickle of curiosity drove me to look through the code of
bundle exec. It
turned out in this line of code,
Bundler tries to setup and brings everything we have in
This part of code is executed way in prior to our
Bundler.setup and once a
is called, all latter ones are void (see my PR below).
Rack could see
Puma and therefor booted the server up.
Additionally, we can require any gem we have in
config.ru despite of its group
I actually created an issue on Bundler’s Github and also a pull request to clarify it. Interestingly @segiddins, the repo owner, advised the behavior that strange to me (and some people I assume) is sort of … intentional.
What’d be the possible solution for this case?
bundle config without
bundle install alone, run it with
bundle install --without production
With this option
Bundler will not download the gems in production group.
Bundler is smart enough to remember our config and use it for future calls.
Build your own
# bin/bundle-exec require "rubygems" require "bundler" Bundle.setup(:default, ENV.fetch("RACK_ENV", "development").to_sym)
Bundler’s codebase here and there, I realize that we can use
Bundle.reset! to reset everything, then use
Bundle.setup to set up again.
This doesn’t solve the
Puma problem we have above but at least from our
there will no gem from other groups can be required.
# config.ru require "rubygems" require "bundler" Bundle.reset! Bundle.setup(:default, :development)
Anyway, this method is untested, in fact I never try using or testify it. Use it at your own risk, I take no responsible for it.
Personally I think this behavior of Bundler might cause some false positives.
What if we accidentally loaded up some gem that’s merely for development or test?
What if that gem was …
Also there’s no option in
bundle exec for us to specify the group either.
So sorry for nagging but once more.
Never ever run
bundle installalone, use it with