Take the 2-minute tour ×

How do you make a generator that alters a file.

Im trying to make it so that it finds a pattern in a file and adds come content to the line below it.

share|improve this question

2 Answers 2

up vote 10 down vote accepted

Rails' scaffold generator does this when it adds a route to config/routes.rb It does this by calling a very simple method:

def gsub_file(relative_destination, regexp, *args, &block)
  path = destination_path(relative_destination)
  content = File.read(path).gsub(regexp, *args, &block)
  File.open(path, 'wb') { |file| file.write(content) }

What it's doing is taking a path/file as the first argument, followed by a regexp pattern, gsub arguments, and the block. This is a protected method that you'll have to recreate in order to use. I'm not sure if destination_path is something you'll have access to, so you'll probably want to pass in the exact path and skip any conversion.

To use gsub_file, let's say you want to add tags to your user model. Here's how you would do it:

line = "class User < ActiveRecord::Base"
gsub_file 'app/models/user.rb', /(#{Regexp.escape(line)})/mi do |match|
  "#{match}\n  has_many :tags\n"

You're finding the specific line in the file, the class opener, and adding your has_many line right underneath.

Beware though, because this is the most brittle way to add content, which is why routing is one of the only places that uses it. The example above would normally be handled with a mix-in.

share|improve this answer

I like Jaime's answer. But, when I started to utilize it, I realized I needed to make some modifications. Here is the sample code I am using:


  def destination_path(path)
    File.join(destination_root, path)

  def sub_file(relative_file, search_text, replace_text)
    path = destination_path(relative_file)
    file_content = File.read(path)

    unless file_content.include? replace_text
      content = file_content.sub(/(#{Regexp.escape(search_text)})/mi, replace_text)


First, gsub will replace ALL instances of the search text; I only need one. Ther0fore I used sub instead.

Next, I needed to check if the replacement string was already in place. Otherwise, I'd be repeating the insert if my rails generator was ran multiple times. So I wrapped the code in an unless block.

Finally, I added the def destination_path() for you.

Now, how would you use this in a rails generator? Here is an example of how I make sure simplecov is installed for rspec and cucumber:

  def configure_simplecov
    code = "#Simple Coverage\nrequire 'simplecov'\nSimpleCov.start"

    sub_file 'spec/spec_helper.rb', search = "ENV[\"RAILS_ENV\"] ||= 'test'", "#{search}\n\n#{code}\n"
    sub_file 'features/support/env.rb', search = "require 'cucumber/rails'", "#{search}\n\n#{code}\n"

There is probably a more elegant and DRY-er way to do this. I really liked how you can add a block of text the Jamie's example. Hopefully my example adds a bit more functionality and error-checking.

share|improve this answer

Your Answer


