Quantcast
Channel: Hacker News
Viewing all articles
Browse latest Browse all 10943

Rails App Template Alternative - TechRabbit

$
0
0

Comments:"Rails App Template Alternative"

URL:http://tech.taskrabbit.com/blog/2013/02/08/rails-app-template-alternative/


We are making lots of new apps at TaskRabbit and have many shared components. All of this stuff (gems, best practices, test setup, etc) needs to get into the new project quickly so the team can focus on the business of providing the intended functionality. My first attempt to solve this problem was to create a Rails app template with all that stuff, but switched to a new way that involves a true template application.

Conventional app templates seem to work for a lot of people and Rails Composer looks to be a good option in this space. This method works by running Ruby code that puts down files. This level of indirection is likely a positive thing when projects vary (rspec one time, test-unit the next). However, we’re trying as hard as we can to be consistent across apps, so when I found that this indirection made working with the templates more difficult, I decided that it was not worth it.

The alternative method is pretty simple. I made a Rails app called Warren) that was exactly what I wanted to see new projects use. It contained all our our gems for authentication, shared styles, inter-app communication, and developer tools. You could basically log in, log out, and go to a page where you saw who was logged in. It had its own test suite to test these things along with all the normal stuff: yml files, initializers, .rvmrc, .gitigore, ApplicationController, favicon, etc. It was simply a working Rails app.

The only addition is the app.rake file that I put in the /lib/tasks directory. That provides this functionality:

% rake app:create[new_name]

This will create a new app called NewName in a peer folder to Warren. The full process looks something like this:

localhost:warren brian$ rake app:create[new_name]
localhost:warren brian$ cd ../new_name
=== RVM Message ===
Do you wish to trust this .rvmrc file? (/Users/brian/taskrabbit/new_name/.rvmrc)
y[es], n[o], v[iew], c[ancel]> yes
Using /Users/brian/.rvm/gems/ruby-1.9.3-p194 with gemset new_name
localhost:new_name brian$ bundle install
Fetching gem metadata from http://rubygems.org/......
Installing all-the-things (3.9.2) 
Your bundle is complete!
localhost:new_name brian$ rake db:create db:migrate db:test:prepare
localhost:new_name brian$ rails s>> Listening on 0.0.0.0:3000, CTRL+C to stop

And http://localhost:3000 works.

Everything is set up. As a small example, the application.rb and routes.rb have the right stuff in them.

1 2 3 4 5 6 7 8 9 module NewName class Application < Rails::Application # normal stuff end end NewName::Application.routes.draw do # routes end

As well as the database.yml

1 2 3 4 development: adapter: mysql2 database: new_name_development host: ...

And in application.html.erb

1 2 3<head><title>NewName</title></head>

There are obviously tons of places that are customized with NewName or new_name and the main point is that it’s the same places that were customized with Warren or warren. So there is no magic here. I just copy, find, and replace.

NewName works because Warren works. I know Warren works because I can actually use it (and test it with rspec). With app templates, I was regenerating every time that I made a change to a generator file to see if it worked.

I’m not sure where to check this in. I was going to make a gem/generator to add this one file to your template project, but that goes against the spirit of what I’m talking about. So I just put it here:

namespace :app do desc "create a new app with the given name -- for example, app:create[new_app_name]" task :create, [:name] => :environment do |t, args| underscore = get_underscore(args) name = underscore.camelize directory = get_directory(args) raise "#{directory} already exists!" if File.exists?(directory) puts "Creating app #{name} in #{directory}" run_command("cp -R #{Rails.root.join("./")} #{directory}") run_command("rm -rf `find #{directory} -type d -name .git`") run_command("rm -rf `find #{directory} -type f -name .DS_Store`") # clean up some files run_command("rm #{directory}/log/*.log") # don't need the logs run_command("rm #{directory}/lib/tasks/app.rake") # don't need this file run_command("rm -rf #{directory}/tmp") # all gone from tmp run_command("mkdir -p #{directory}/tmp/cache/assets") # new rails app has this one # change the stuff replace_all(directory, "Warren", name) replace_all(directory, "warren", underscore) dir_command(directory, "git init .") dir_command(directory, "git add .") dir_command(directory, "git commit -a -m 'initial app commit from warren'") end desc "remove the given app" task :remove, [:name] => :environment do |t, args| directory = get_directory(args) run_command("rm -rf #{directory}") end def run_command(cmd) cmd = [cmd].flatten cmd = cmd.join(" && ") puts "Running: #{cmd}" `#{cmd}` end def dir_command(directory, cmd) cmd = [cmd].flatten run_command(["cd #{directory}"] + cmd) end def replace_all(directory, find, replace) run_command("grep -rl #{find} #{directory} | xargs sed -i '' 's/#{find}/#{replace}/g'") end def get_underscore(args) args.with_defaults(:name => "") underscore = args.name.to_s.underscore if underscore.blank? raise "No name given -- Use app:create[new_app_name]" end underscore end def get_directory(args) underscore = get_underscore(args) Rails.root.join("../#{underscore}") end def rvm_stuff_that_does_not_work require 'rvm' # setup new gemset #run_command("source ~/.rvm/scripts/rvm") rvmrc = File.open("#{directory}/.rvmrc", 'rb') { |file| file.read } #rvmrc.gsub!("rvm", "~/.rvm/scripts/rvm") # rvm use ruby-1.9.3-p194@warren --install --create rvm_ruby = rvmrc[rvmrc.index("ruby")..rvmrc.index("@")-1] env = RVM::Environment.new(rvm_ruby) puts "Creating gemset #{underscore} in #{rvm_ruby}" env.gemset_create(underscore) puts "Now using gemset #{underscore}" env.gemset_use!(underscore) env.use_from_path!(directory) ENV['BUNDLE_GEMFILE'] = "#{directory}/Gemfile" puts "Installing bundler gem." env.system("gem", "install", "bundler") env.system("gem", "install", "rails") #puts "Installing other gems." #env.system("bundle install") end end

You’ll note that my instructions had me cding into the directory. I tried to do that in the script, too, but I couldn’t get the RVM stuff to work. If anyone wants to take a look at the rvm_stuff_that_does_not_work method, I’d love some help.


Viewing all articles
Browse latest Browse all 10943

Trending Articles