enhancing the global environment task rails - ruby-on-rails

On the application I am upgrading from Rails 3.2.22.4 to Rails 4.0.13, the following block of code for enhancing the global environment task has become a road-block by not working on the target Rails version:
Rails.application.class.rake_tasks do
Rake::Task["environment"].enhance do
...
end
end
This works fine on 3.2, but fails with Don't know how to build task 'environment' error message in 4.0.
In 3.2, Rails.application.class.rake_tasks returns a Proc object ( [#<Proc:0x007f978602a470#.../lib/rails/application.rb:301>] ) pointing to this line in the rails codebase. On 4.0, it returns an empty array.
The line referred to in the above Proc object seems to be removed in this commit.
What would the preferred way to enhance the environment rake task be in Rails 4.x?
The above piece of code is in the lib/subdomain/rake.rb file, and it is include with the following code in lib/subdomain/engine.rb:
module Subdomain
class Engine < Rails::Engine
...
rake_tasks do |_app|
require 'subdomain/rake'
end
...
end
end
Rake tasks can't be executed as the command fails with this error. rails server|console commands work ok.

Option 1
If I'm understanding the question properly, something like this should work by placing these tasks in a standard location like lib/tasks/environment.rake. Note: None of this is particularly Rails-specific.
# Re-opening the task gives the ability to extend the task
task :environment do
puts "One way to prepend behavior on the :environment rake task..."
end
task custom: :environment do
puts "This is a custom task that depends on :environment..."
end
task :environment_extension do
puts "This is another way to prepend behavior on the :environment rake task..."
end
# You can "enhance" the rake task directly too
Rake::Task[:environment].enhance [:environment_extension]
The output of this would be:
$ rake custom
This is another way to prepend behavior on the :environment rake task...
One way to prepend behavior on the :environment rake task...
This is a custom task that depends on :environment...
Option 2
However, the question remains as to why :environment needed to be extended. If it's to trigger something before, say, a db:migrate, you might be better off just re-opening the task in question and adding another dependency to that particular task. For example:
task custom: :environment do
puts "This is a custom task that depends on :environment..."
end
task :custom_extension do
puts "This is a new dependency..."
end
# Re-opening the task in question allows you to added dependencies
task custom: :custom_extension
The result of this is:
$ rake custom
This is a new dependency on :custom
This is a custom task that depends on :environment...
C-C-C-Combo Breaker!!
Combining everything, the output would look like this:
$ rake custom
This is another way to prepend behavior on the :environment rake task...
One way to prepend behavior on the :environment rake task...
This is a new dependency on :custom
This is a custom task that depends on :environment...

Related

Run Rake task programmatically with specified environment

I'm setting up a second database with my Ruby on Rails (3) application, so I want to create a rake task to create the second development database. I'm trying to overwrite the rake db:create task such that it does all the database creation that I need. However, it seems I can't find a suitable way to perform this task. I've tried a few approaches - establishing a connection to the database from the URL:
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
# database.yml contains an entry for secondary_development, this works, as confirmed from rails console
ActiveRecord::Base.establish_connection "postgresql://localhost/secondary_development"
Rake::Task["db:create"].invoke # this does nothing
end
# invoke original db_create task - this works
db_create.invoke
end
end
Another approach was to do:
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
Rails.env = "secondary_development"
Rake::Task["db:create"].invoke
end
# invoke original db_create task - this doesn't work like this
db_create.invoke
end
end
This time only the secondary_development db:create works and the database is created as desired, but the development database is no longer created using this approach.
From one answer I found elsewhere, I thought that reenabling the task would be necessary, but that didn't change anything here and appears not to be the issue.
Finally, an approach that has worked is:
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
system("rake db:create RAILS_ENV=secondary_development")
end
db_create.invoke
end
end
The only issue here is that because the rake task is being run via system, the Rails application has to load before being executed, so I'm essentially loading the application twice fully just to run the task - this will be 3 times when I add a test database into the mix.
So, the actual question(s):
Is it possible to run Rake::Task["..."] programmatically with a specified environment?
Why doesn't ActiveRecord::Base.establish_connection work in this way when creating the database? I had success when running this from Rails console.
I managed to find a solution to this. I believe the reason is that .invoke will not always invoke the task, but it will first determine whether it is necessary. Given that rake db:create is run on several occasions within the same task, .invoke deems the subsequent invocations as unnecessary and therefore does not run them. For the desired behaviour, .execute should be used instead.
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
Rails.env = "secondary_development"
Rake::Task["db:create"].execute # execute rather than invoke
end
# Reset the Rails env to 'development', otherwise it remains as 'secondary_development', which is not what we want (or move this above the if)
Rails.env = "development"
db_create.execute
end
end

rake task not running sub-tasks in order specified

In a rails 4.2 app, in Rakefile, I have this:
task(:default).clear
task :default => [:test, 'bundle:audit']
The output, always has bundle:audit running first. Why is that?
I read in some places that rake executes tasks as dependencies arise, but bundle:audit, as far as I can tell, does not depend on test. It is defined here:
https://github.com/rubysec/bundler-audit/blob/master/lib/bundler/audit/task.rb
To quote a comment discussing the same problem in Rake's GitHub repository:
It turns out that your problem is due to the way rails creates its test tasks:
desc "Run tests quickly by merging all types and not resetting db"
Rails::TestTask.new(:all) do |t|
t.pattern = "test/**/*_test.rb"
end
https://github.com/rails/rails/blob/v4.2.7.1/railties/lib/rails/test_unit/testing.rake#L24-L27
Here Rails uses Rails::TestTask for the test:all target which will load all test files.
def define
task #name do
if ENV['TESTOPTS']
ARGV.replace Shellwords.split ENV['TESTOPTS']
end
libs = #libs - $LOAD_PATH
$LOAD_PATH.unshift(*libs)
file_list.each { |fl|
FileList[fl].to_a.each { |f| require File.expand_path f }
}
end
end
https://github.com/rails/rails/blob/v4.2.7.1/railties/lib/rails/test_unit/sub_test_task.rb#L106-L118
But unlike Rake::TestTask, which immediately runs the tests, Rails::TestTask only requires the files necessary to run the tests then relies on the at_exit handler in Minitest to run the tests. This means rake dependencies are completely ignored for running tests.
I updated the links to the source code, because the discussion was about Rails 4.1.8, but the problem still exists the source code of Rails 4.2.7.1.
This problem was reported as an issue to the Rails team on GitHub and it was fixed in this PR.
That said: This problem should be fixed since Rails 5.0.0.

Rake task failing to load :environment properly

I'm running a custom rake task...
namespace :import do
desc "Import terms of service as HTML from stdin"
task :terms => :environment do
html = STDIN.read
settings = ApplicationWideSetting.first
settings.terms_and_conditions = html
if settings.save
puts "Updated terms of service"
else
puts "There was an error updating terms of service"
end
end
end
The model ApplicationWideSetting is reported as undefined when running the task in the production environment. However, when running the task on other environments (ie. development, staging, test.) the task runs fine.
Running the process in rails console, in all environments, completes ok.
Does anyone know what's going on, things I could check?
note: I ran the task with
puts Rails.env
To check the shell environment var RAILS_ENV was getting set/read correctly. I've also tried both with and without the square brackets around the :environment dependency declaration.
additional info: Rails v3.2.14
further info: I've setup a completely fresh rails app, and the script works fine in any environment. Since the install in question is a real production environment, I'll have to setup another deploy and check it thoroughly. More info as I find it.
In a nutshell, Rails doesn't eager load models (or anything else) when running rake tasks on Production.
The simplest way to work with a model is to require it when you begin the rake task, and it should work as expected, in this case:
# explicitly require model
require 'application_wide_setting'
It's possible to eager load the entire rails app with:
Rails.application.eager_load!
However, you may have issues with some initializers (ie. devise)

How can I create a rake task that will always run when any Rake task is ran?

From what I remember, in the documentation is specified that in the test environment, the database is always cleared even when you run rake ( with no arguments ). I'd like to achieve such a thing, so that it doesn't matter if I run a task or not, when I run rake, there's always a Rake task being executed. Is this possible? Is this where the default task kicks in?
Create a file called rakefile in the directory you want to run the task from.
This code will make it so that if you just type "rake" my_default_task will run:
task :default => 'my_default_task'
task :my_default_task do
puts "Now I am doing the task that Tempus wants done when he/she types 'rake' in the console."
end
task :my_not_default_task do
puts "This isn't the default task."
end
However, if you typed rake my_not_default_task, then my_default_task would NOT run. If you want it to run regardless here is one thing you can do:
task :default => 'my_default_task'
task :my_default_task do
puts "This is the default task"
end
task :my_not_default_task do
puts "This isn't the default task."
end
Rake::Task['my_default_task'].invoke
The last line in this code ensures that my_default_task runs even when you call some other task, so if you typed rake my_not_default_task the my_default_task'would also run.
EDIT:
When you're working with rails you can put the tasks above in a file in the lib/tasks folder with an extension of .rake and rails will automagically run them when you do rake
Jason Seifer has a real nice tutorial on rake.

Ruby on Rails: How to run a rake task for the env.rb?

in features/support/env.rb in cucumber, i would like to have a rake task run everytime i start my tests... but not before each scenario.. just.. once.
This is what i need to run
Rake::Task["db:test:prepare"].reenable
Rake::Task["db:test:prepare"].invoke
Assuming a standard cucumber install with the rake file cucumber.rake in app/lib/tasks.
Something like this should work
task :data_prep
Rake::Task["db:test:prepare"].reenable
Rake::Task["db:test:prepare"].invoke
end
task :all => [:data_prep,:ok,:wip]
task :default => [:data_prep,:cucumber]
All you do is define another task (:data_prep) and add a call to it for the existing cucumber tasks.
Not the cleanest of methods but I cannot remember if cucumber has a method to run on startup and not on a per scenario basis.

Resources