Rake task to change all occurances of a method in a project - ruby-on-rails

I am trying to write a rake task to rename all occurrences of a method in a ruby project. I have achieved this using the following command from the command line.
Basically
retention.group_by('bla').count
needs to be changed to
retention.group_by('bla').size
I managed to achieve this uisng the following from the command line
find . -name \*.rb -exec ruby -i -p -e "gsub(/(group_by(\(([^\)]+)\))).count/, '\1.size')" \;
I am now trying to do this from a rake task to make it straight forward to change in all our projects. Which is the easiest / most elegant way to do this ? Think I am close, its just selecting all files in a project directory I am stuck on.

This did the trick
namespace :rename do
task :gb_count_rename do
Dir.glob("**/*.rb").each do |file_name|
text = File.read(file_name)
content = text.gsub(/(group_by(\(([^\)]+)\))).count/, '\1.size')
File.open(file_name, "w") { |file| file << content }
end
end
end

Related

Capture output of shell command, line by line, into an array

I want to capture the total number of rubocop offenses to determine whether my codebase is getting better or worse. I have almost no experience with ruby scripting.
This is my script so far:
#!/usr/bin/env ruby
#script/code_coverage
var = `rubocop ../ -f fuubar -o ../coverage/rubocop_results.txt -f offenses`
puts var
So I ./rails-app/script/code_coverage and my terminal displays
...
--
6844 Total
When I var.inspect I get a long string. I want to either read the output of rubocop ../ -f ... line by line (preferred) or capture each line of output into an array.
I would like to be able to do something like this:
var.each |line| do
if line.contains('total')
...
total = ...
end
Is this possible? I guess this would be similar to writing the output to a file and and then reading the file in line by line.
If you want to keep it simple, you can simply split your var string.
var = `rubocop ../ -f fuubar -o ../coverage/rubocop_results.txt -f offenses`.split("\n")
Then you can iterate on var like you wanted to.
use open3 library of ruby. Open3 grants you access to stdin, stdout, stderr and a thread to wait the child process when running another program. You can specify various attributes, redirections, current directory, etc., of the program as Process.spawn.
http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
require 'open3'
# Run lynx on this file.
cmd = "lynx -crawl -dump /data/feed/#{file_name}.html > /data/feed/#{file_name}"
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
cmdout = stdout.read
$logger.debug "stdout is:" + stdout.read
$logger.debug "stderr is:" + stderr.read
end

Rename project in Ruby on Rails 4.0.2

I'm aware of the Rename plugin for rails (https://github.com/get/Rename), but does anyone know of a way to easily rename a project in Rails 4.0.2 seeing as plugins are deprecated beginning with Rails 4?
Just consider you have used
rails new blog
This will create a blog application. Now if you want to rename the folder blog, just use
$mv blog blog_new
This will just rename the folder and the application will run with no issues as external folder name changes will not affect the application. Else you need to change each file as specified by srt32 but i don't see any specific reason to change project name from inside.
Assuming your app name is my_app you can run something like grep -r 'my_app' . from the root of your project and find all the places where the app name is referenced. It shouldn't be that bad to go update them. The list of places should look something like:
config/application.rb
config/environment.rb
config/environments/development.rb
config/environments/test.rb
config/environments/production.rb
config/initializers/secret_token.rb
config/routes.rb
Rakefile
Enter following commands
$ rails new ProjectToRename
$ cd ProjectToRename
$ grep -ri 'project_?to_?rename'
Finally done.
You'll need to rename the top-level directory yourself:
$ cd ..
$ mv ProjectToRename SomeNewName
I've written the following script to do just that. You can see it also at https://gist.github.com/danielpclark/8dfcdd7ac63149323bbc
#!/usr/bin/ruby
# Rename Rails Project (File: rename_rails)
# Copyright 6ft Dan(TM) / MIT License
# Check the config/application.rb for capital usage in project name by model OldProjectName
# Usage: rename_rails OldProjectName NewAwesomeName
# Replace string instances of project name
`grep -lR #{ARGV[0]} | xargs sed -i 's/#{ARGV[0]}/#{ARGV[1]}/g'`
`grep -lR #{ARGV[0].downcase} | xargs sed -i 's/#{ARGV[0].downcase}/#{ARGV[1].downcase}/g'`
# Rename Rails directory if it exists
if File.directory?(ARGV[0])
`mv #{ARGV[0]} #{ARGV[1]}`
drc = ARGV[1]
elsif File.directory?(ARGV[0].downcase)
`mv #{ARGV[0].downcase} #{ARGV[1]}`
drc = ARGV[1]
end
# Delete temporary files (helps prevent errors)
drc ||= ''
if ['cache','pids','sessions','sockets'].all? {
|direc| File.directory?(File.join(drc,'tmp', direc)) }
FileUtils.rm_rf(File.join(drc,'tmp'))
end
And I've created a howto video on YouTube. http://youtu.be/dDw2RmczcDA

How long should "rake routes" run?

I just started out with Rails, so excuse my fairly basic question. I am already noticing that the rake routes command takes a while to execute everytime I run it. I have about 20 routes for 3 controllers and it takes about 40 seconds to execute.
Is that normal? How could I speed this up?
P.S.: I am on Windows 7 with Rails 3.1.3 (set up with Rails Installer).
The rake routes task depends on the environment task which loads your Rails environment and requires thousands of Ruby files.
The startup time of a Rails environment and the corresponding rake routes execution time are very close (on my Linux on-steroids-laptop with a Rails application with ~ 50 routes):
$ time ruby -r./config/environment.rb -e ''
real 0m5.065s
user 0m4.552s
sys 0m0.456s
$ time rake routes
real 0m4.955s
user 0m4.580s
sys 0m0.344s
There is no easy way to decrease startup time as it relies on the way your interpreter requires script files : http://rhnh.net/2011/05/28/speeding-up-rails-startup-time
I came up with a solution to rake routes taking about 8 seconds to run every time. It's a simple file based cache that runs bundle exec rake routes, stores the output in a file under tmp. The filename is the md5 hash of config/routes.rb, so if you make a change and change it back, it will use the old cached file.
I put the following bash functions in an executable file I call fastroutes:
if [ ! -f config/routes.rb ]; then
echo "Not in root of rails app"
exit 1
fi
cached_routes_filename="tmp/cached_routes_$(md5 -q config/routes.rb).txt"
function cache_routes {
bundle exec rake routes > $cached_routes_filename
}
function clear_cache {
for old_file in $(ls tmp/cache_routes*.txt); do
rm $old_file
done
}
function show_cache {
cat $cached_routes_filename
}
function show_current_filename {
echo $cached_routes_filename
}
function main {
if [ ! -f $cached_routes_filename ]; then
cache_routes
fi
show_cache
}
if [[ "$1" == "-f" ]]
then
show_current_filename
elif [[ "$1" == "-r" ]]
then
rm $cached_routes_filename
cache_routes
else
main
fi
Here's a github link too.
This way, you only have to generate the routes once, and then fastroutes will used the cached values.
That seems a bit long, but do you really need to run rake routes that often? On my system, OSX Lion/Rails 3.2.0, rake routes takes ~10s.
In your Rakefile:
#Ouptut stored output of rake routes
task :fast_routes => 'tmp/routes_output' do |t|
sh 'cat', t.source
end
#Update tmp/routes_output if its older than 'config/routes.rb'
file 'tmp/routes_output' => 'config/routes.rb' do |t|
outputf = File.open(t.name, 'w')
begin
$stdout = outputf
Rake.application['routes'].invoke
ensure
outputf.close
$stdout = STDOUT
end
end
Rails environment takes a huge more amount of time to be loaded on Windows. I recommend you to give Unix a try, like Ubuntu, as Windows is the worst environment in which you can run and develop Ruby on Rails applications. But if you are just trying Rails, Windows is enough :)

Capsitrano deploy recipe : after deploy file listing and modifications

I'm currently working on a multi-stage recipe for Capistrano that would, ideally, after deploy, make wise use of the yui compressor for all css and js.
Here's what I currently came up to :
after "deploy", "deploy:cleanup", "minifier:compress"
# Task to minify via Yui-compressor
# Uses compressor bundled with application in #{application}/lib/yuicompressor
namespace :minifier do
def minify(files)
files.each do |file|
cmd = "java -jar lib/yuicompressor/build/yuicompressor-2.4.6.jar #{file} -o #{file}"
puts cmd
ret = system(cmd)
raise "Minification failed for #{file}" if !ret
end
end
desc "minify"
task :compress do
minify_js
minify_css
end
desc "minify javascript"
task :minify_js do
minify(Filelist['public/js/**/*.js'])
end
desc "minify css"
task :minify_css do
minify(Filelist['public/css/**/*.css'])
end
end
What's really puzzling me is the
uninitialized constant Capistrano::Configuration::Filelist (NameError)
I get as soon as Capistrano gets to the point.
As a total newbie to Ruby, Rails, and Capistrano, I understand for some reason FileList isn't a common Capistrano method, but can't figure out what to replace it with.
Thanks for the help.
Your task is conceptually wrong, it will run on local system (the one from which you're deploying), because you're call system, you should use the run method which run commands remotely.
def minify(files)
files.each do |file|
cmd = "java -jar lib/yuicompressor/build/yuicompressor-2.4.6.jar #{file} -o #{file}"
puts cmd
ret = system(cmd) # *** SYSTEM RUN LOCAL COMMANDS ***
raise "Minification failed for #{file}" if !ret
end
end
That said, I will change that code with shell scripting, something like (untested)
task :minify
cmd = "java -jar lib/yuicompressor/build/yuicompressor-2.4.6.jar"
run "find #{current_path}/public/css/ -name '*.css' -print0 | xargs -0 -I file #{cmd} file -o file"
run "find #{current_path}/public/js/ -name '*.js' -print0 | xargs -0 -I file #{cmd} file -o file"
end
or if you prefer to use ruby to program it, you should move the code into a rake task (which you can try and debug locally) and then invoke it with Capistrano: How do I run a rake task from Capistrano?

rails - Redirecting console output to a file

On a bash console, if I do this:
cd mydir
ls -l > mydir.txt
The > operator captures the standard input and redirects it to a file; so I get the listing of files in mydir.txt instead of in the standard output.
Is there any way to do something similar on the rails console?
I've got a ruby statement that generates lots of prints (~8k lines) and I'd like to be able to see it completely, but the console only "remembers" the last 1024 lines or so. So I thought about redirecting to a file - If anyone knows a better option, I'm all ears.
A quick one-off solution:
irb:001> f = File.new('statements.xml', 'w')
irb:002> f << Account.find(1).statements.to_xml
irb:003> f.close
Create a JSON fixture:
irb:004> f = File.new(Rails.root + 'spec/fixtures/qbo/amy_cust.json', 'w')
irb:005> f << JSON.pretty_generate((q.get :customer, 1).as_json)
irb:006> f.close
You can use override $stdout to redirect the console output:
$stdout = File.new('console.out', 'w')
You may also need to call this once:
$stdout.sync = true
To restore:
$stdout = STDOUT
Apart from Veger's answer, there is one of more way to do it which also provides many other additional options.
Just open your rails project directory and enter the command:
rails c | tee output.txt
tee command also has many other options which you can check out by:
man tee
If you write the following code in your environment file, it should work.
if "irb" == $0
config.logger = Logger.new(Rails.root.join('path_to_log_file.txt'))
end
You can also rotate the log file using
config.logger = Logger.new(Rails.root.join('path_to_log_file.txt'), number_of_files, file_roation_size_threshold)
For logging only active record related operations, you can do
ActiveRecord::Base.logger = Logger.new(Rails.root.join('path_to_log_file.txt'))
This also lets you have different logger config/file for different environments.
Using Hirb, you can choose to log only the Hirb output to a text file.
That makes you able to still see the commands you type in into the console window, and just the model output will go to the file.
From the Hirb readme:
Although views by default are printed to STDOUT, they can be easily modified to write anywhere:
# Setup views to write to file 'console.log'.
>> Hirb::View.render_method = lambda {|output| File.open("console.log", 'w') {|f| f.write(output) } }
# Doesn't write to file because Symbol doesn't have a view and thus defaults to irb's echo mode.
>> :blah
=> :blah
# Go back to printing Hirb views to STDOUT.
>> Hirb::View.reset_render_method
Use hirb. It automatically pages any output in irb that is longer than a screenful. Put this in a console session to see this work:
>> require 'rubygems'
>> require 'hirb'
>> Hirb.enable
For more on how this works, read this post.
Try using script utility if you are on Unix-based OS.
script -c "rails runner -e development lib/scripts/my_script.rb" report.txt
That helped me capture a Rails runner script's very-very long output easily to a file.
I tried using redirecting to a file but it got written only at the end of script.
That didn't helped me because I had few interactive commands in my script.
Then I used just script and then ran the rails runner in script session but it didn't wrote everything. Then I found this script -c "runner command here" output_file and it saved all the output as was desired. This was on Ubuntu 14.04 LTS
References:
https://askubuntu.com/questions/290322/how-to-get-and-copy-a-too-long-output-completely-in-terminal#comment1668695_715798
Writing Ruby Console Output to Text File

Resources