Make ruby script use local gems, instead of common - ruby

I'm deploying my rails project to production server. There is only 1.9.3 version of ruby (I developed on 2.1.2) so there is few compatibility problems in gems versions. More over, I downloaded one of gems to vendor/gem_name and made necessary fixes in its sources, so I need to use exactly my version of that gem and, as you understand, It's not possible to update it.
in Gemfile
require 'gem_name', :path => 'vendor/gem_name'
So after cloning project to server I run
bundle install --path vendor/bundle
and it created bundle directory in vendor folder with gems versions, needed to me, inside it.
After that I tried to run fetching script to fill db with some data by command
ruby *_fetch.rb
inside *_fetch.rb:
require 'gem_name'
And it fails with error
You have already activated gem_name older_version, but your Gemfile requires
gem_name newest_version. Using bundle exec may solve this. (Gem::LoadError)
from /usr/local/rvm/gems/ruby-1.9.3-p374/gems/bundler-1.3.5/lib/bundler/runtime.rb:19:in `setup'
So how could I specify script to require my edited local gem?

Run it with bundle exec That's exactly what bundle exec is for.

Related

Can't install gem using Bundler's Rakefile install task when developing a custom gem

I'm developing a couple of private gems and I think I don't understand correctly the PATH/GEM_PATH and/or Bundler/RVM installation flow, would love if someone could chip in.
I have a repository with two gems (A & B for simplicity sake). I've developed the gems using the scaffolding + following the guidelines provided by this bundler tutorial.
Thanks to the Bundler project I have a few Rakefile tasks like rake build, rake install, rake install:local and rake release. Because of the private nature of these gems I can't release them to RubyGems (and we haven't looked into hosting our rubygems).
My machines are using RVM to manage ruby versions and Bundler version 1.15.1
What I want to do: Assuming a new machine/developer trying out the project, ideally we would cd into each of the subfolders (currently 2, gem A and gem B), run rake install and after that we should have the gems available system wide for the current user.
What is happening: The gems are built and work properly, but they are only available inside the subfolder of each gem i.e. gem A is only available inside the subfolder A and gem B is only available inside subfolder B.
What I've tried: So, after rake build/install/install:local a new .gem file is generated under pkg. I've tried to manually install the "compiled" file using gem install pkg/A.gem, gem install --local pkg/A.gem and gem install --local --user-install pkg/A.gem without success. (there are plenty of SO questions/answers about this)
I believe this has something to do with the PATH variables, but like I said before I don't fully understand the way they are managed. I get the following results from these commands:
# Our gem
> gem which A
/home/ubuntu/.rvm/gems/ruby-2.4.0/gems/A-0.1.8/lib/A.rb
# Pry, available globally
> gem which pry
/home/ubuntu/.rvm/gems/ruby-2.4.0/gems/pry-0.11.1/lib/pry.rb
I've been lost and frustrated for far too long now, any help is appreciated. Also open to hear suggestions of better private gem installation flows :)
Yes, it has something to do with your PATH variables. Your installation seems to be good.
I advise you to first affirm your gems installation path with:
echo $GEM_HOME
The double check your PATH to ensure its present and also confirm that the GEM home is also where the gem got installed into from the rake install
echo $PATH
If not, put it in your path and you should be fine with something like this:
echo PATH=$PATH:$GEM_HOME >> ~/.bashrc
source ~/.bashrc
Build your gem as per that guide you linked. You should end up with a gem file. Distribute this as you see fit (I use rsync/crontab to download newer gem versions but anything goes). User can install the gem as follows:
gem install --user-install /path/to/your/file.gem
This will install the gem in the user's ~/.gem/ruby/<version>/gems/<your-gem-name> directory.
Tried it with an empty gem (foodie, as in that example guide) and it works fine. But if you don't specify the --user-install parameter it will try to install in the system ruby dir (/usr/lib/ruby/gems...)

Bundler: using shared gem when it exists, rather than downloading from gem server

Say, I have Gemfile like following.
source "GEM_REPOSITORY"
gem 'gem_A'
# gem_A has no additional dependency
gem 'gem_B'
# gem_B depends on gem_B_1 and gem_B_2
When I run bundle install, I want Bundler to do the following.
If a gem already exists in "local system-wide gems", it copies the gem from local.
If a gem doesn't exist in local, it looks for GEM_REPOSITORY.
I looked for some related articles, and found some of likely-answers like
Ruby Bundler multiple sources in Gemfile
SOURCE PRIORITY
But none of the above looks like the answer for me.
Using source repository priority does't work. Because in the example above, if dependent gem (say, gem_B_1) exits in local but the target gem (gem_B) doesn't exist in local, it'll download both of above from the remote repository.
Are there any work around for doing this?
If not, don't you guys think it's necessary considering the cost of the implementation and the effect?
This is the current behavior.
When running gem install, directly or via bundle install, gem will first build a dependency graph with all the needed gems. If the gem is found locally it will use it, otherwise it will try to download it from the specified source.
If you want, try it yourself.
bundle gem gem_a
bundle gem gem_b
cd gem_a
vim gem_a.gemspec
add
spec.add_dependency 'multi_json', '~> 1.10.1'
or any dependency you want to the gem and run bundle install.
cd ../gem_b
vim Gemfile
and add
gem 'gem_a', path: '../gem_a'
then run
bundle install --verbose
you will see that the multi_json or whatever dependency of gem_a uses the local version and does not download anything.
This is of course also true for gems from remote sources.

When developing a gem, do I have to keep installing after I make updates?

I am making a gem in the folder:
/Users/me/projects/ruby/gems/mygamename/
And its layout is like:
/Users/me/projects/ruby/gems/mygamename/mygamename.gemspec
/Users/me/projects/ruby/gems/mygamename/mygemname.rb
/Users/me/projects/ruby/gems/mygamename/lib/mygemname/file1.rb
/Users/me/projects/ruby/gems/mygamename/lib/mygemname/file2.rb
Now in my other ruby project, I want to reference this gem in my Gemfile so I did:
gem 'mygemname', :path => "/Users/me/projects/ruby/gems/mygamename"
I know I should be writing tests for my gem to test for functionaly, but I was curious if I could also do integration tests from my other ruby project (where I am referencing it in my Gemfile).
Do I have to re-run bundle if I make updates to the gem? Or does it keep reading from that folder and it will pickup the changes?
Any other advise on how I can test it from my other ruby project?
Again I will be writing tests in the gem itself, but wondering how I can do I this way also in case I want to.
You will have to run bundle install only once. Bundler does not cache or package your gem, it points to your folder directly.
Restarting rules are the same as in the main app. If you make changes to views, assets or autoloaded ruby files, they will be reloaded automatically. However, you will have to restart your app if you required some lib files in the app and changed them. By default Bundler will require "lib/mygemname.rb" of gem "mygemname", so this file will not be reloadable.
Gem classes/modules are cached so every time you make a change to your gemfile, you will have to restart your app.
Also, if you dont update gem version in your gemspec than you dont have to re-bundle. if you update gem version, than you might need to execute bundle update gemname
What I typically do when developing a gem is create Gemfile in a directory and work from there. In the Gemfile, I add a reference to my gem using the :path option.
gem 'mygemname', :path => '/path/to/my/gem'
Then I run bundle install. This way I don't have to keep reinstalling my gem.
Keep in mind that you need to use bundle exec when running my gem.

Create gem locally and skip rake install

I'm creating a ruby gem using Bundler for a simple rackup app (not Rails). It's a real pain to run rake install and then restart the webserver everytime. For most part it's ok because I test everything using rspec but not design. My gem contains a whole lot of design and everytime I update my gem I have to go threw the same procedure.
Is it possible to build gems locally without having to run rake install and then restart my rack server every single time?
If you're using Bundler to manage the gems in your application, you can use Bundler's path directive to use a gem that's currently in development.
In your Gemfile:
# My awesome gem that I'm developing
gem 'some-awesome-gem', :path => '~/Projects/some_awesome_gem'
Essentially, just point the path at the directory where your gem resides and you won't have to package new versions of the gem while you're actively developing it.
See the Bundler homepage and Gemfile Manual for more details.

Using Bundler in deployment

Pretty fundamental question but I'm trying to understand how best to use Bundler in a deployment situation.
I'm working on a Sinatra application that has about 20 dependent gems. During development, I'm using RVM with a custom gemset for the application, and I run bundle install to update the gemset in accordance with the gemfile.
When it comes to deployment (manually for now, so I can understand how it all works before using a tool like capistrano), I need to do bundle install --development right? This downloads the gems and places them in vendor/bundle.
My question is what else do I need to do? I'm using Unicorn on the server - do I just bundle exec unicorn ... and everything just works? (i.e. bundler finds the vendor directory and uses the gems from there?)
Should unicorn be a vendored gem in the application or a separate 'system' gem on the server that all applications share?
You need --deployment key, not --development: http://gembundler.com/man/bundle-install.1.html#DEPLOYMENT-MODE
On first run bundler creates config in .bundle directory. You can check it by running bundle config or just cat .bundle/config in project's directory. So bundle exec unicorn is enough since bundler knows where gems are installed. On development machine you can also install gems to arbitrary directory using --path key. For more details see manpage for bundle install (link above or bundle help install).

Resources