What is the difference between rails and rake? - ruby-on-rails

What is the difference between commands rake and rails in Ruby?
Which one is faster and why?

The difference is in what binary is being called.
If you were to call bundle exec which rake within your Rails app root directory, you'd get something like /home/[USERNAME]/.rbenv/versions/2.5.5/bin/rake and for bundle exec which rails, you'd get /home/[USERNAME]/.rbenv/versions/2.5.5/bin/rails. From there you can cat (cat /home/[USERNAME]/.rbenv/versions/2.5.5/bin/rake) both these paths and see similar code is being ran for each, but the end of the files is different.
rails
gem "railties", version
load Gem.bin_path("railties", "rails", version)
rake
gem "rake", version
load Gem.bin_path("rake", "rake", version)
Here they're both calling load on Gem.bin_path but with different arguments, which are attempting to load separate gems in. You can follow the code further by running a irb/pry/rails console, and setting up the needed require 'rubygems' and version = ">= 0.a", then run Gem.bin_path("railties", "rails", version) and Gem.bin_path("rake", "rake", version) to see what the load is actually trying to run. I'll admit it'll become a bit of a rabbit hole before you come across the logic that eventually ends up identifying a rake task argument passed to rails and it proxy's it to Rake and stop there and defer to this SO answer for the rest.
When rails is ran and passed arguments which were intended to be ran by rake, it will attempt to first find if it was an actual argument intended to be given to the rails command, determine that it wasn't, then attempt to run it as a rake command for you for overall naming simplicity added in by the Rails team in Rails v4.
So which is faster to run? rake for actual rake tasks, as it'll bypass the extra logic in needing to determine it was being passed rake arguments. But also rails specific arguments cannot be ran with rake e.g. bundle exec rake generate will not work (unless you have a generate task). If in doubt, run bundle exec rails --help and in at least Rails v5, it'll output which arguments are rails specific and which are rake specific.

rake is a Make-like program implemented in Ruby.
rails is a web framework, which also has some rake tasks.
This means that you can have a ruby program with rake but without rails, but not the other way around.
By itself, rake will be faster because you don't need to load the whole rails application.
But when launching a rake task, it can have dependencies, for example the :environment dependency in a rails app, which tells rake to load the rails environment and quite a bit of your application depending on the current environment.
In this case, the initialization of a rake task may take as long as a rails command.
Please note that the actual task run needs also to be taken into account, it can be very short or take several minutes.
For example, rake db:migrate, which is a rails task available by default, runs the migrations on the database, which can be time-consuming if the database is already populated and/or you have a lot of migrations

Related

FATAL: database «sampleapp_production» does not exits

First time here, I hope do it right.
I'm following the railstutorial 3.2 and I'm in section 5.4, in the paragraph above to listing 5.32.
(In fact, you can just type rake by itself; the default behavior of rake is to
run the test suite.)
I used rake by itselft but I have a error since then. When I run:
"$ bundle exec rake spec" I get this error.
http://pastebin.com/F0wrEkT1
My database.yml is:
http://pastebin.com/tWAgeFTV
My problem is that I don't know why it is asking for production database when I didn't use it yet. And whe I look for the issue I don't find topics about (almost I didn't find it).
Do you have some clues to star looking or to know what is happening?
Thanks a lot.
I'm not sure why it is looking for the production DB but you may want to skip ahead. Just read a little further:
(In fact, you can just type rake by itself; the default behavior of rake is to run the test suite.) The only caveat is that using rake to run the tests for the current sample application will raise an error since it requires the test database to be prepared properly, a step we’ll defer to Section 6.2.1.
It recommends running:
bundle exec rake db:test:prepare

Running gems rake tasks from application rake tasks

I'm trying to add a rake task from a gem (geocoder https://github.com/alexreisner/geocoder) from a rake task of my application, as i would like it to be run just after the database is built.
So i have this code inside a rakefile
task :geolocal do
spec = Gem::Specification.find_by_name 'geocoder'
load "#{spec.gem_dir}/lib/tasks/geocoder.rake"
puts '##################GEOCODING##############'
Rake::Task["geocode:all CLASS=ProposedAccomodation"].execute
end
just following this question Ruby Rake load tasks from a gem
but i keep getting the same error
Don't know how to build task 'geocode:all CLASS=ProposedAccomodation'
any clue what i'm doing wrong?
so there are several things to be taken into consideration here.
for the record, this is how you call a task with arguments
Rake::Task["taskname"].execute(args)
in your case, i did not realize first, that it actually uses environment variables instead of task arguments that are read as ENV['CLASS'].
that would answer your question, so you can either set it ENV['CLASS'] = 'ClassName' or pass it along to your call to the rake task rake geolocal CLASS=ProposedAccomodation.
which brings me to a following question: why are you not just calling the original rake task, there is nothing you add to it.

Use bundle exec rake or just rake?

I learned Rails using just the rake command like rake db:migrate; however, I read that I should be using the bundle exec rake ... instead of just plain rake. Now I'm confused about which to use.
Should I be using bundle exec rake instead of just plain rake or is it just a preference thing? Any insight would be much appreciated! Thanks!
bundle exec executes a command in the context of your bundle.
That means it uses the gems specified in your Gemfile. Much of the time, running bundle exec rake foo has the same results as if you just ran rake foo, especially if you have the same gems installed systemwide as in your Gemfile. However, some applications may specify different versions of gems than the ones you have installed systemwide, and may want those exact gems and versions to be able to run correctly. If you just run without bundle exec, you may get some weird errors.
Using bundle exec guarantees that the program is run with the environment specified in the gemfile, which hopefully means it is the environment that the creators of the program want it to be run in, which hopefully means it should run correctly no matter what weird setup you have on your computer.
It basically standardizes the environment under which the program is run. This helps avoid version hell and makes life much easier.
See http://bundler.io/v1.3/man/bundle-exec.1.html for more info.
$ bundle exec rake db:migrate
Uses the version of rake specified in the Gemfile to execute the rake task db:migrate.
But there is no rake gem specified in the Gemfile!
Yes, but a rake gem was installed as a dependency of some other gem--look in Gemfile.lock. So the rule must be: Uses the version of rake specified in Gemfile.lock.
But Gemfile.lock doesn't specify a specific version--it specifies a version greater than x.y!
Then the rule must be: Uses the version of rake that was installed in the current gemset.
$ rake db:migrate
Normally, when you issue a command on the command line, e.g. rake, your system searches for the command in the list of directories specified in your PATH environment variable. The first directory that contains the command is the version of the command that is used. To see which directory that is, you can do:
$ which rake
So if you execute,
$ rake db:migrate
that may use a different rake gem than the one you installed with bundle install. But, even if your system finds the same rake version as bundle exec, any gems required by the rake source code will be searched for in places outside your project's gemset. Therefore, there are many ways that just:
$ rake db:migrate
can screw things up.
According to the Ruby on Rails Tutorial Book(free online), section 3.6, if you are using rvm 1.11.x+ then you do not need to preface commands with bundle exec.
running any exacutable without bundle exec will have Rubygems fetching the latest version of the gem installed in your system.
By adding the bundle exec prefix instead will have the executable running in the context of your Gemfile.lock, which means that will be run using the version defined in the gem file.

Rails: Running more than one rake task at once?

Been loving rails, but still struggling with how long it takes to run tasks from the command line.
I'm wondering if it's possible to combine multiple rake tasks without reloading the environment each time. For example, if I run
rails generate rspec:install && rails generate model test_model title:string && rake db:migrate
it will spend 10 seconds loading up rails to run the rspec install, then another ten seconds to load up the environment for generate model, then another for the migration. Is there a way to keep the same environment open for all 3 commands?
Take a look at the rails-sh gem - it boots the Rails environment, and then presents a command line interface, so you can run your command within it. There are limitations when using it (I don't think you can change your RAILS_ENV), but for your use-case, it should be perfect.
If your commands are just rake tasks, you can use spaces to separate them, e.g.:
rake db:migrate && rake db:test:clone_structure
would become
rake db:migrate db:test:clone_structure

Under what circumstances would a Ruby $LOAD_PATH be acquired from a parent process?

In my cucumber scenarios, if I call rake db:schema:load within a target Rails app folder, I get the cucumber process's $LOAD_PATH and not the Rails app's own Gemfile/load path. I think this is very weird.
The consequence is that I get the following error:
no such file to load -- rails/all
I can't reproduce it outside of my cucumber scenario.
ruby -rubygems -e "system 'rake -T'"
works normally -> the 'rake -T' has the application's own Gemfile-based $LOAD_PATH; and doesn't generate the error above.
Can anyone think why a child process (rake -T or rake db:schema:load or rails runner...; invoked by either system, exec, %x[...] or backtick; would start with the parent processes' $LOAD_PATH (from the cucumber scenario's Gemfile) instead of its own $LOAD_PATH (from the Rails app's Gemfile)?
When you use bundler, either via bundle exec or require 'bundler/setup', it finds your Gemfile, then puts its location in ENV["BUNDLE_GEMFILE"]. However, if this is already set, then bundler just reuses the value. This is what causes your Rails app to use the cucumber process's Gemfile.
If you want to execute something in the context of a different Gemfile, clear out ENV["BUNDLE_GEMFILE"] first. Bundler provides the method Bundler.with_clean_env(&blk) that may be helpful; it executes your block with the environment how it was before Bundler was loaded. Of course, you can also clear it out by hand with something like system("env -u BUNDLE_GEMFILE rake sometask").
The parent process's environment (ENV) is passed down to sub-shells. Either cucumber itself, how you are running cucumber (e.g. bundle exec cucumber), your scenarios, or code the scenario loads (e.g. the app, and therefore bundler), is messing with your ENV. Environment variables like RUBYLIB, GEM_PATH, and BUNDLE_GEMFILE can have a significant impact on what your sub-shelled Ruby processes can load / will behave.
Try printing out your ENV variable in your scenario and comparing it to what you get when you do it with ruby -rubygems -rpp -e "pp ENV", or just env on the command line.
For what it's worth, a possible alternative would be to load and invoke the rake task directly, e.g., Rake::Task['db:schema:load'].invoke, without using a sub-shell. Depends on what you're trying to do, though.

Resources