Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Crushing It With Rake Tasks

Crushing It With Rake Tasks

Although bundle exec rake db:migrate is probably the single biggest killer feature in Rails, there is a lot more to rake.

Rails offers several rake tasks to help with everyday project management, like redoing a migration because you changed your mind on one of the columns, clearing your log files because they get so big, and listing out the TODOs and FIXMEs.

What's even more awesome that all that is that you can create your own rake tasks. Got a tedious command-line process? Write a rake task for it!

Barrett Clark

May 06, 2016
Tweet

More Decks by Barrett Clark

Other Decks in Programming

Transcript

  1. class CreatePosts < ActiveRecord::Migration[5.0] def change create_table :posts do |t|

    t.string :title t.string :body t.timestamps end end end RailsConf 2016 @barrettclark
  2. $ bundle exec rake db:migrate == 20160418142215 CreatePosts: migrating ======================================

    -- create_table(:posts) -> 0.0180s == 20160418142215 CreatePosts: migrated (0.0181s) ============================= RailsConf 2016 @barrettclark
  3. class CreatePosts < ActiveRecord::Migration[5.0] def change create_table :posts do |t|

    t.string :title t.string :body t.string :permalink t.timestamps end end end RailsConf 2016 @barrettclark
  4. $ bundle exec rake db:rollback == 20160418142215 CreatePosts: reverting ======================================

    -- drop_table(:posts) -> 0.0094s == 20160418142215 CreatePosts: reverted (0.0137s) ============================= RailsConf 2016 @barrettclark
  5. $ bundle exec rake db:migrate:redo == 20160418142215 CreatePosts: reverting ======================================

    -- drop_table(:posts) -> 0.0094s == 20160418142215 CreatePosts: reverted (0.0137s) ============================= == 20160418142215 CreatePosts: migrating ====================================== -- create_table(:posts) -> 0.0135s == 20160418142215 CreatePosts: migrated (0.0139s) ============================= RailsConf 2016 @barrettclark
  6. class CreatePosts < ActiveRecord::Migration[5.0] def change create_table :posts do |t|

    t.string :title t.string :body t.string :permalink t.timestamps end add_index :posts, :permalink end end RailsConf 2016 @barrettclark
  7. Q: CAN I CHANGE AND RERUN A MIGRATION? A: IT

    DEPENDS RailsConf 2016 @barrettclark
  8. DB Rollback Limitations • New fields are OK • New

    objects (index, foreign key, etc) are not OK • DO NOT change any migration you've pushed RailsConf 2016 @barrettclark
  9. Barrett Clark I'll tweak a migration when I'm first creating

    something. After that changes get their own migration. RailsConf 2016 @barrettclark
  10. # This file should contain all the record creation needed

    to seed the database with its default values. # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). # # Examples: # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) RailsConf 2016 @barrettclark
  11. # This file should contain all the record creation needed

    to seed the database with its default values. # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). # # Examples: # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) Post.where( :title => "First Blog Post", :body => "This is the body of the post.", :permalink => "http://localhost:3000" ).first_or_create RailsConf 2016 @barrettclark
  12. require 'csv' CSV::Converters[:blank_to_nil] = lambda do |field| field && field.empty?

    ? nil : field end namespace :db do namespace :seed do desc "Import airport data" task :import_airports => :environment do if Airport.count == 0 filename = Rails.root.join('db', 'data_files', 'airports.csv') CSV.foreach(filename, :headers => true, :header_converters => :symbol, :converters => [:blank_to_nil]) do |row| Airport.create(row.to_hash) end end end end end RailsConf 2016 @barrettclark
  13. require 'csv' CSV::Converters[:blank_to_nil] = lambda do |field| field && field.empty?

    ? nil : field end namespace :db do namespace :seed do desc "Import airport data" task :import_airports => :environment do if Airport.count == 0 filename = Rails.root.join('db', 'data_files', 'airports.csv') CSV.foreach(filename, :headers => true, :header_converters => :symbol, :converters => [:blank_to_nil]) do |row| Airport.create(row.to_hash) end end end end end RailsConf 2016 @barrettclark
  14. require 'csv' CSV::Converters[:blank_to_nil] = lambda do |field| field && field.empty?

    ? nil : field end namespace :db do namespace :seed do desc "Import airport data" task :import_airports => :environment do if Airport.count == 0 filename = Rails.root.join('db', 'data_files', 'airports.csv') CSV.foreach(filename, :headers => true, :header_converters => :symbol, :converters => [:blank_to_nil]) do |row| Airport.create(row.to_hash) end end end end end RailsConf 2016 @barrettclark
  15. require 'csv' CSV::Converters[:blank_to_nil] = lambda do |field| field && field.empty?

    ? nil : field end namespace :db do namespace :seed do desc "Import airport data" task :import_airports => :environment do if Airport.count == 0 filename = Rails.root.join('db', 'data_files', 'airports.csv') CSV.foreach(filename, :headers => true, :header_converters => :symbol, :converters => [:blank_to_nil]) do |row| Airport.create(row.to_hash) end end end end end RailsConf 2016 @barrettclark
  16. require 'csv' CSV::Converters[:blank_to_nil] = lambda do |field| field && field.empty?

    ? nil : field end namespace :db do namespace :seed do desc "Import airport data" task :import_airports => :environment do if Airport.count == 0 filename = Rails.root.join('db', 'data_files', 'airports.csv') CSV.foreach(filename, :headers => true, :header_converters => :symbol, :converters => [:blank_to_nil]) do |row| Airport.create(row.to_hash) end end end end end RailsConf 2016 @barrettclark
  17. require 'csv' CSV::Converters[:blank_to_nil] = lambda do |field| field && field.empty?

    ? nil : field end namespace :db do namespace :seed do desc "Import airport data" task :import_airports => :environment do if Airport.count == 0 filename = Rails.root.join('db', 'data_files', 'airports.csv') CSV.foreach(filename, :headers => true, :header_converters => :symbol, :converters => [:blank_to_nil]) do |row| Airport.create(row.to_hash) end end end end end RailsConf 2016 @barrettclark
  18. require 'csv' CSV::Converters[:blank_to_nil] = lambda do |field| field && field.empty?

    ? nil : field end namespace :db do namespace :seed do desc "Import airport data" task :import_airports => :environment do if Airport.count == 0 filename = Rails.root.join('db', 'data_files', 'airports.csv') CSV.foreach(filename, :headers => true, :header_converters => :symbol, :converters => [:blank_to_nil]) do |row| Airport.create(row.to_hash) end end end end end RailsConf 2016 @barrettclark
  19. $ ls -lh log/ total 12345 -rw-r--r-- 1 barrettclark staff

    2.2M Feb 3 2015 development.log -rw-r--r-- 1 barrettclark staff 105K Jan 29 2015 newrelic_agent.log -rw-r--r-- 1 barrettclark staff 99M Jan 29 2015 test.log RailsConf 2016 @barrettclark
  20. $ bundle exec rake notes app/models/post.rb: * [2] [TODO] Example

    todo * [3] [FIXME] This is an example FIXME db/seeds.rb: * [9] [TODO] Need some Post seeds RailsConf 2016 @barrettclark
  21. $ bundle exec rake routes Prefix Verb URI Pattern Controller#Action

    gate_manifest GET /gate/manifest(.:format) gate#manifest gate_passenger GET /gate/passenger(.:format) gate#passenger readings GET /readings(.:format) readings#index POST /readings(.:format) readings#create new_reading GET /readings/new(.:format) readings#new edit_reading GET /readings/:id/edit(.:format) readings#edit reading GET /readings/:id(.:format) readings#show PATCH /readings/:id(.:format) readings#update PUT /readings/:id(.:format) readings#update DELETE /readings/:id(.:format) readings#destroy root GET / readings#index RailsConf 2016 @barrettclark
  22. $ bundle exec rake routes | grep gate gate_manifest GET

    /gate/manifest(.:format) gate#manifest gate_passenger GET /gate/passenger(.:format) gate#passenger RailsConf 2016 @barrettclark
  23. $ bundle exec rake rails:update identical config/boot.rb exist config identical

    config/routes.rb identical config/application.rb identical config/environment.rb conflict config/secrets.yml Overwrite /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/secrets.yml? (enter "h" for help) [Ynaqdh] n skip config/secrets.yml identical config/cable.yml identical config/puma.rb exist config/environments identical config/environments/development.rb Overwrite /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb? (enter "h" for help) [Ynaqdh] d --- /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb 2016-04-18 08:23:59.000000000 -0500 +++ /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb20160419-81872-16xjoaj 2016-04-19 22:47:56.000000000 -0500 @@ -65,7 +65,7 @@ # Use a real queuing backend for Active Job (and separate queues per environment) # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "RailsConf2016_#{Rails.env}" + # config.active_job.queue_name_prefix = "rails_conf2016_#{Rails.env}" config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. Retrying... Overwrite /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb? (enter "h" for help) [Ynaqdh] Y RailsConf 2016 @barrettclark
  24. $ bundle exec rake rails:update identical config/boot.rb exist config identical

    config/routes.rb identical config/application.rb identical config/environment.rb conflict config/secrets.yml Overwrite /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/secrets.yml? (enter "h" for help) [Ynaqdh] n skip config/secrets.yml identical config/cable.yml identical config/puma.rb exist config/environments identical config/environments/development.rb Overwrite /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb? (enter "h" for help) [Ynaqdh] d --- /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb 2016-04-18 08:23:59.000000000 -0500 +++ /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb20160419-81872-16xjoaj 2016-04-19 22:47:56.000000000 -0500 @@ -65,7 +65,7 @@ # Use a real queuing backend for Active Job (and separate queues per environment) # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "RailsConf2016_#{Rails.env}" + # config.active_job.queue_name_prefix = "rails_conf2016_#{Rails.env}" config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. Retrying... Overwrite /Volumes/Card/Projects/SCRATCH/RUBY/RailsConf2016/config/environments/production.rb? (enter "h" for help) [Ynaqdh] Y RailsConf 2016 @barrettclark
  25. Y - yes, overwrite n - no, do not overwrite

    a - all, overwrite this and all others q - quit, abort d - diff, show the differences between the old and the new h - help, show this help RailsConf 2016 @barrettclark
  26. namespace :railsconf do desc "Say hello" task :hello do puts

    "Hello" end desc "Say world" task :world do puts "World!" end end RailsConf 2016 @barrettclark
  27. namespace :railsconf do desc "Say hello" task :hello do puts

    "Hello" end desc "Say world" task :world do puts "World!" end desc "Say hello world" task :phrase => [:hello, :world] end RailsConf 2016 @barrettclark
  28. namespace :railsconf do desc "Say hello" task :hello do puts

    "Hello" end desc "Say world" task :world do puts "World!" end desc "Say hello world" task :phrase => [:hello, :world] desc "Take parameter" task :custom_hello, [:name] do |t, args| puts "Hello, #{args[:name]}" end end RailsConf 2016 @barrettclark
  29. namespace :railsconf do desc "Say hello" task :hello do puts

    "Hello" end desc "Say world" task :world do puts "World!" end desc "Say hello world" task :phrase => [:hello, :world] desc "Take parameter" task :custom_hello, [:name] do |t, args| puts "Hello, #{args[:name]}" end end task :default => ["railsconf:hello"] RailsConf 2016 @barrettclark
  30. $ rake -T rake railsconf:custom_hello[name] # Take parameter rake railsconf:hello

    # Say hello rake railsconf:phrase # Say hello world rake railsconf:world # Say world RailsConf 2016 @barrettclark
  31. rake about # List versions of all Rails frameworks and

    the environment rake db:create # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databas... rake db:drop # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in ... rake db:environment:set # Set the environment value for the database rake db:fixtures:load # Loads fixtures into the current environment's database rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog) rake db:migrate:status # Display status of migrations rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n) rake db:schema:cache:clear # Clears a db/schema_cache.dump file rake db:schema:cache:dump # Creates a db/schema_cache.dump file rake db:schema:dump # Creates a db/schema.rb file that is portable against any DB supported by Active Record rake db:schema:load # Loads a schema.rb file into the database rake db:seed # Loads the seed data from db/seeds.rb rake db:setup # Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first) rake db:structure:dump # Dumps the database structure to db/structure.sql rake db:structure:load # Recreates the databases from the structure.sql file rake db:version # Retrieves the current schema version number rake dev:cache # Toggle development mode caching on/off rake initializers # Print out all defined initializers in the order they are invoked by Railss | rake log:clear # Truncates all/specified *.log files in log/ to zero bytes (specify which logs with LOGS=test,development) rake middleware # Prints out your Rack middleware stack rake notes # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus) rake notes:custom # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM rake rails:template # Applies the template supplied by LOCATION=(/path/to/template) or URL rake rails:update # Update configs and some other initially generated files (or use just update:configs or update:bin) rake restart # Restart app by touching tmp/restart.txt rake routes # Print out all defined routes in match order, with names rake secret # Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions) rake stats # Report code statistics (KLOCs, etc) from the application or engine rake test # Runs all tests in test folder rake test:db # Run tests quickly, but also reset db rake time:zones:all # Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6 rake tmp:clear # Clear cache and socket files from tmp/ (narrow w/ tmp:cache:clear, tmp:sockets:clear) rake tmp:create # Creates tmp directories for cache, sockets, and pids RailsConf 2016 @barrettclark
  32. " ============================================================================ " Netrw Directory Listing (netrw v155) " /Users/barrettclark/.rvm/gems/ruby-2.2.3@RailConf2016/gems/railties-5.0.0.beta3/lib/rails/tasks

    " Sorted by name " Sort sequence: [\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$ " Quick Help: <F1>:help -:go up dir D:delete R:rename s:sort-by x:special " ============================================================================== ../ ./ annotations.rake dev.rake engine.rake framework.rake initializers.rake log.rake middleware.rake misc.rake restart.rake routes.rake statistics.rake tmp.rake RailsConf 2016 @barrettclark
  33. namespace :log do desc "Truncates all/specified *.log files in log/

    to zero bytes (specify which logs with LOGS=test,development)" task :clear do log_files.each do |file| clear_log_file(file) end end def log_files if ENV['LOGS'] == 'all' FileList["log/*.log"] elsif ENV['LOGS'] log_files_to_truncate(ENV['LOGS']) else log_files_to_truncate("development,test,production") end end def log_files_to_truncate(envs) envs.split(',') .map { |file| "log/#{file.strip}.log" } .select { |file| File.exist?(file) } end def clear_log_file(file) f = File.open(file, "w") f.close end end RailsConf 2016 @barrettclark
  34. $ rails g task Usage: rails generate task NAME [action

    action] [options] Options: [--skip-namespace], [--no-skip-namespace] # Skip namespace (affects only isolated applications) Runtime options: -f, [--force] # Overwrite files that already exist -p, [--pretend], [--no-pretend] # Run but do not make any changes -q, [--quiet], [--no-quiet] # Suppress status output -s, [--skip], [--no-skip] # Skip files that already exist Description: Stubs out a new Rake task. Pass the namespace name, and a list of tasks as arguments. This generates a task file in lib/tasks. Example: `rails generate task feeds fetch erase add` Task: lib/tasks/feeds.rake RailsConf 2016 @barrettclark
  35. namespace :db do desc "db:migrate, db:seed, log:clear tasks streamlined" task

    :streamlined => ["db:migrate", "db:seed", "log:clear"] end RailsConf 2016 @barrettclark
  36. $ bundle exec rake -T db rake db:create # Creates

    the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config) rake db:drop # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config) rake db:environment:set # Set the environment value for the database rake db:fixtures:load # Loads fixtures into the current environment's database rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog) rake db:migrate:status # Display status of migrations rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n) rake db:schema:cache:clear # Clears a db/schema_cache.dump file rake db:schema:cache:dump # Creates a db/schema_cache.dump file rake db:schema:dump # Creates a db/schema.rb file that is portable against any DB supported by Active Record rake db:schema:load # Loads a schema.rb file into the database rake db:seed # Loads the seed data from db/seeds.rb rake db:setup # Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first) rake db:streamlined # db:migrate, db:seed, log:clear tasks streamlined rake db:structure:dump # Dumps the database structure to db/structure.sql rake db:structure:load # Recreates the databases from the structure.sql file rake db:version # Retrieves the current schema version number rake test:db # Run tests quickly, but also reset db RailsConf 2016 @barrettclark
  37. ⭐ $ bundle exec rake db:streamlined == 20160418142215 CreatePosts: migrating

    ====================================== -- create_table(:posts) -> 0.0120s == 20160418142215 CreatePosts: migrated (0.0122s) ============================= $ ls -lh log/ total 0 -rw-r--r-- 1 barrettclark staff 0B Apr 28 17:25 development.log RailsConf 2016 @barrettclark
  38. Streamlining Tedium • Calculate auth token • Docker management (in

    Fig days) • Load data into a database • Seed from CSV • Full production database RailsConf 2016 @barrettclark
  39. namespace :db do namespace :heroku do desc "capture DB Backup"

    task :capture_backup => :environment do if Rails.env == 'development' Bundler.with_clean_env do system "heroku pg:backups capture" end end end desc "Pull DB Backup" task :download_backup => :capture_backup do if Rails.env == 'development' Bundler.with_clean_env do system "curl -o latest.dump `heroku pg:backups public-url`" end end end desc "Load the PROD database from Heroku to the local dev database" task :load => :download_backup do if Rails.env == 'development' Bundler.with_clean_env do config = Rails.configuration.database_configuration[Rails.env] system <<-CMD pg_restore --verbose --clean --no-acl --no-owner -h localhost \ -U #{config["username"]} -d #{config["database"]} latest.dump rm -rf latest.dump CMD end end end end end RailsConf 2016 @barrettclark
  40. desc "capture DB Backup" task :capture_backup => :environment do if

    Rails.env == 'development' Bundler.with_clean_env do system "heroku pg:backups capture" end end end RailsConf 2016 @barrettclark
  41. desc "Pull DB Backup" task :download_backup => :capture_backup do if

    Rails.env == 'development' Bundler.with_clean_env do system "curl -o latest.dump `heroku pg:backups public-url`" end end end RailsConf 2016 @barrettclark
  42. desc "Load the PROD database from Heroku to the local

    dev database" task :load => :download_backup do if Rails.env == 'development' Bundler.with_clean_env do config = Rails.configuration.database_configuration[Rails.env] system <<-CMD pg_restore --verbose --clean --no-acl --no-owner -h localhost \ -U #{config["username"]} -d #{config["database"]} latest.dump rm -rf latest.dump CMD end end end RailsConf 2016 @barrettclark
  43. namespace :db do namespace :heroku do desc "capture DB Backup"

    task :capture_backup => :environment do if Rails.env == 'development' Bundler.with_clean_env do system "heroku pg:backups capture" end end end desc "Pull DB Backup" task :download_backup => :capture_backup do if Rails.env == 'development' Bundler.with_clean_env do system "curl -o latest.dump `heroku pg:backups public-url`" end end end desc "Load the PROD database from Heroku to the local dev database" task :load => :download_backup do if Rails.env == 'development' Bundler.with_clean_env do config = Rails.configuration.database_configuration[Rails.env] system <<-CMD pg_restore --verbose --clean --no-acl --no-owner -h localhost \ -U #{config["username"]} -d #{config["database"]} latest.dump rm -rf latest.dump CMD end end end end end RailsConf 2016 @barrettclark
  44. Recap • Database management • Project maintenance • Plain old

    ruby projects RailsConf 2016 @barrettclark
  45. DATA VISUALIZATION TOOLKIT CLARK exceptional elegance made Ruby on Rails

    meworks, it can be l, Second Edition, is eloper Michael Hartl of your own complete web development. using Twitter’s ng Sprockets and Sass; d RSpec; better auto- hentication n to Gherkin o for the essential d when developing ue solves a real-world that’s simple enough r your previous web ue Rails mastery. ment ow to pattern e mic ones elopers need ms, including at GitHub $44.99 US / $46.99 CANADA i n f o r m i t . c o m / r u b y i n f o r m i t . c o m / a w r u b y . r a i l s t u t o r i a l . o r g “Ruby on Rails™ Tutorial by Michael Hartl has become a must-read for developers learning how to build Rails apps.” — PETER COOPER, Editor of Ruby Inside MICHAEL HARTL is a programmer, educator, and entrepreneur. He wrote the first edition of Ruby on Rails™ Tutorial (Addison-Wesley, 2011), coauthored RailsSpace (Addison-Wesley, 2008), and was cofounder and lead developer of Insoshi, a popular social networking platform based on Rails. He previously taught theoretical and computational physics at the California Institute of Technology, earning the Caltech Lifetime Achievement Award for Excellence in Teaching. Michael is a graduate of Harvard College, holds a Ph.D. in physics from Caltech, and is an alumnus of the Y Combinator entrepreneur program. DATA VISUALIZATION TOOLKIT A d d i s o n - W e s l e y P r o f e s s i o n a l R u b y S e r i e s Using JavaScript, Rails, and Postgres to Present Data and Geospatial Information BARRETT AUSTIN CLARK RailsConf 2016 @barrettclark