I upgraded rails to 4.1.0 & installed gem "spring"
After it, I patched bundle exec spring binstub --all. Now, all my bin/* includes
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
But, if I run, saying time spring rake -T & time rake -T times 1 second & 3 seconds! It seems that spring not used by default in my application. What's going wrong? It's bad thing that I need to write annoying spring ..command.. before any rake task, rails server or rspec. What I need to do to avoid spring command with spring worked by default?
You don't need to prepend spring everytime. I'm not sure why you're getting those numbers but try the following.
# make sure that spring server is stopped
>> spring stop
# take note of the time here
>> time bin/rake routes
# start spring server
>> spring
# test routes again
>> time bin/rake routes
The second bin/rake routes should be very fast. In one of our apps, without spring, it takes 8 seconds to show the routes. With spring, it's less than a second.
For rspec, you need to install an additional gem for that. Look at the additional commands section of the spring gem
Related
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
I'd like to use Cucumber via Rake for BDD in a Rails 4. I've got it installed and hooked into spring to speed up tests but they take much longer than expected to run. The tests definitively run faster when Spring is active, but there is always ~2 seconds of startup time for Cucumber even if there are no feature/scenarios to run. For example:
$ bin/spring status
Spring is not running.
$ time bin/rake cucumber
/Users/alans/.rvm/rubies/ruby-2.1.1/bin/ruby -S bundle exec cucumber --profile default
Using the default profile...
0 scenarios
0 steps
0m0.000s
bin/rake cucumber 0.08s user 0.02s system 3% cpu 3.463 total
$ bin/spring status
Spring is running:
64383 spring server | cucumber_test | started 48 secs ago
64384 spring app | cucumber_test | started 48 secs ago | test mode
$ time bin/rake cucumber
/Users/alans/.rvm/rubies/ruby-2.1.1/bin/ruby -S bundle exec cucumber --profile default
Using the default profile...
0 scenarios
0 steps
0m0.000s
bin/rake cucumber 0.08s user 0.02s system 4% cpu 2.098 total
Using bin/rake for the run is what's defined in the Spring Readme. Running spring without rake produces the faster expected times.
$ time spring cucumber
Using the default profile...
0 scenarios
0 steps
0m0.000s
spring cucumber 0.06s user 0.01s system 13% cpu 0.537 total
The Question
How can I setup Cucumber, Spring and Rake so that tests run as quickly with Rake as without?
Further Details
My environment is an RVM install of ruby-2.1.1 and rails 4.1.1 on a Mac with an SSD running 10.9.3.
Here's what I'm doing:
Create an app with
rails new cucumber_test -T
Update the gem file with the following:
group :development, :test do
gem 'cucumber-rails', :require => false
gem 'database_cleaner'
gem 'rspec-rails'
gem 'spring'
gem 'spring-commands-cucumber'
end
Then running:
bundle install
(I also tried leaving the default listing for spring in just the :development environment but that didn't speed things up either.)
Run the Cucumber generator:
rails g cucumber:install
Update the Spring binstubs:
bundle exec spring binstub --all
Which returns:
* bin/rake: spring already present
* bin/cucumber: generated with spring
* bin/rails: spring already present
This is where I run the time tests. When Spring is stopped, Cucumber consistently takes longer to run. Even with Spring running it seems there is always a 2 second overhead before the test suite actually kicks off when using Rake. That's what I'm trying to eliminate.
Guard-RSpec mentions in the README that one could run specs using spring by specifying a cusom cmd:
guard :rspec, cmd: 'spring rspec' do
# ...
end
This used to work fine, until I did a spring binstub --all which changed my bin/spring from...
#!/usr/bin/env ruby
#
# This file was generated by Bundler.
#
# The application 'spring' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('spring', 'spring')
...to...
#!/usr/bin/env ruby
# This file loads spring without using Bundler, in order to be fast
# It gets overwritten when you run the `spring binstub` command
unless defined?(Spring)
require "rubygems"
require "bundler"
if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ spring \((.*?)\)$.*?^$/m)
ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
ENV["GEM_HOME"] = ""
Gem.paths = ENV
gem "spring", match[1]
require "spring/binstub"
end
end
Now when running guard and hitting enter, it simply tells me this:
[2] guard(main)> <<<<< pressing enter
14:35:35 - INFO - Run all
14:35:35 - INFO - Running all specs
And a notification like "RSpec results - Failed" appears.
When changing my Guardfile and removing spring from the RSpec's cmd like this...
guard :rspec, cmd: 'rspec' do
...the specs are run again, but apparently not using spring?
I also have to mention that when running spring from the OSX terminal, nothing seems to happen:
$ spring
$
So: how must I configure Guard and RSpec to use Spring?
Update
At the moment, I have reverted my bin/spring executable to the version before "binstubbing" it:
#!/usr/bin/env ruby
#
# This file was generated by Bundler.
#
# The application 'spring' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('spring', 'spring')
And the Guardfile looks like this:
guard :rspec, cmd: 'spring rspec' do ... end
This works, but I don't think it's faster than running bare rspec.
So I'm absolutely unsure now how to correctly run RSpec with Spring - using spring rspec or just rspec?
I was looking into this very issue earlier.
Binstubs are wrapper scripts around executables. In Rails they live
inside bin/. If you run spring binstub --all, your binstubs will be
using Spring.
http://makandracards.com/makandra/26083-3-ways-to-run-spring-the-rails-app-preloader-and-how-to-disable-it
Given this fact, you should be able to do something like this to use Rspec with Spring
guard :rspec, cmd: "bin/rspec" do
A little test to verify. Make sure that you have binstubbed rspec already.
bundle binstubs 'rspec-core'
Verify that Spring hasn't been bootstrapped in bin/rspec. The following block should not appear.
[bin/rspec]
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
Shut down Spring. Run bin/rspec and verify that Spring wasn't triggered.
spring stop
spring status
bin/rspec
spring status
If Spring wasn't triggered, you should see this
Spring is not running.
Now bootstrap your binstubs with Spring. Make sure that you have already bundle installed the necessary gems.
[Gemfile]
group :development, :test do
gem "rspec-rails", "~> 3.0"
gem 'spring-commands-rspec'
...
end
[terminal]
bundle install
Update binstubs to use Spring
spring binstub --all
Verify that Spring has been bootstrapped in bin/rspec. The following block should now appear.
[bin/rspec]
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
Shut down Spring. Run bin/rspec and verify that Spring was triggered.
spring stop
spring status
bin/rspec
spring status
Now check to see if the tests run faster after spring has loaded the environment for the tests.
spring stop
time bin/rspec
[output]
real 0m4.981s
user 0m0.144s
sys 0m0.032s
Spring should be running now. Let's see if it's doing its job.
time bin/rspec
[output]
real 0m0.831s
user 0m0.140s
sys 0m0.034s
Yep.
Bottom line, if your binstubs have been bootstrapped with Spring, calling the binstubs will include Spring. And, of course, only commands that have been registered with Spring can use Spring, which is why spring-commands-rspec was included in the Gemfile earlier to support Rspec.
Does anyone know how to deploy a rails engine on heroku? The engine that I am developing is self contained so I am trying to avoid making another wrapper rails application to deploy my engine.
FIXED
I had to add config.ru and Procfile in my engine root directory to so that heroku knows that its a rails application.
Thanks,
Ajay
I guess is more dependant on how you've built your engine. If you can run it standalone locally, it'll run just fine like that on Heroku - there's nothing about Heroku which would make running your code any harder other than the normal constraints
As the edit of the question states, config.ru and Procfile are needed. I just copied config.ru from a Rails app and changed the path:
# This file is used by Rack-based servers to start the application.
require ::File.expand_path('../test/dummy/config/environment', __FILE__)
run Rails.application
As for Procfile, the following will work:
web: bundle exec rackup -p $PORT
You may want to add a gem for web server, as it will default to WEBrick which is not suitable for production.
In order to get proper logs on Heroku, add the following to the Gemfile
gem 'rails_12factor', group: :production
Assets compilation won’t work unless you define the assets:precompile task (according to https://devcenter.heroku.com/articles/ruby-support#rails-4-x-applications-compile-phase).
I just added the following to engine’s Rakefile:
namespace :assets do
desc 'Precompile assets within dummy app'
task :precompile do
Dir.chdir('test/dummy') do
system('bundle exec rake assets:precompile')
end
end
end
I also disabled JS compression (or you can add uglifier gem to the Gemfile).
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.