├── VERSION ├── .gitignore ├── lib ├── beet │ ├── recipes │ │ ├── rails │ │ │ ├── shoulda.rb │ │ │ ├── clean_files.rb │ │ │ ├── rspec.rb │ │ │ ├── css │ │ │ │ ├── blueprint.rb │ │ │ │ └── reset.rb │ │ │ ├── git.rb │ │ │ ├── auth │ │ │ │ ├── devise.rb │ │ │ │ ├── clearance.rb │ │ │ │ └── authlogic.rb │ │ │ ├── jquery.rb │ │ │ ├── cms │ │ │ │ ├── bcms_blog.rb │ │ │ │ └── bcms_event.rb │ │ │ └── db │ │ │ │ ├── mysql.rb │ │ │ │ └── postgres.rb │ │ ├── rack │ │ │ └── middleware.rb │ │ └── passenger │ │ │ └── vhost.rb │ ├── scm │ │ ├── svn.rb │ │ └── git.rb │ ├── capistrano.rb │ ├── interaction.rb │ ├── scm.rb │ ├── logger.rb │ ├── template_location_map.rb │ ├── execution.rb │ ├── gem_location_map.rb │ ├── file_system.rb │ ├── rails.rb │ └── executor.rb └── beet.rb ├── test ├── test_helper.rb ├── executor_test.rb └── file_system_test.rb ├── features ├── step_definitions │ ├── beet_steps.rb │ └── common_steps.rb ├── support │ ├── env.rb │ └── common.rb └── generating.feature ├── README.rdoc ├── TODO ├── LICENSE ├── Rakefile ├── beet.gemspec └── bin └── beet /VERSION: -------------------------------------------------------------------------------- 1 | 0.4.1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | *output 3 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/shoulda.rb: -------------------------------------------------------------------------------- 1 | gem 'thoughtbot-shoulda', :lib => 'shoulda', :source => 'http://gems.github.com' 2 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/clean_files.rb: -------------------------------------------------------------------------------- 1 | run "rm README" 2 | run "rm public/index.html" 3 | run "rm public/favicon.ico" 4 | run "rm public/robots.txt" 5 | -------------------------------------------------------------------------------- /lib/beet/scm/svn.rb: -------------------------------------------------------------------------------- 1 | class Svn < Scm 2 | def self.checkout(repos, branch = nil) 3 | `svn checkout #{repos}/#{branch || "trunk"}` 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/rspec.rb: -------------------------------------------------------------------------------- 1 | ruby script/plugin install git://github.com/dchelimsky/rspec.git -r 'refs/tags/1.2.6' 2 | ruby script/plugin install git://github.com/dchelimsky/rspec-rails.git -r 'refs/tags/1.2.6' 3 | ruby script/generate rspec 4 | -------------------------------------------------------------------------------- /lib/beet/capistrano.rb: -------------------------------------------------------------------------------- 1 | module Beet 2 | module Capistrano 3 | # Just run the capify command in root 4 | # 5 | # ==== Example 6 | # 7 | # capify! 8 | # 9 | def capify! 10 | log 'capifying' 11 | in_root { run('capify .', false) } 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/css/blueprint.rb: -------------------------------------------------------------------------------- 1 | FileUtils.mkdir_p("public/stylesheets/blueprint") 2 | %w(forms.css grid.css grid.png ie.css print.css reset.css typography.css).each do |file| 3 | run "curl -L http://github.com/joshuaclayton/blueprint-css/blob/master/blueprint/src/#{file} \ 4 | > public/stylesheets/blueprint/#{file}" 5 | end 6 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'test/unit' 3 | require 'shoulda' 4 | 5 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 6 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 7 | require 'beet' 8 | if RUBY_VERSION <= '1.8.6' 9 | require 'ruby-debug' 10 | end 11 | class Test::Unit::TestCase 12 | end 13 | -------------------------------------------------------------------------------- /lib/beet/scm/git.rb: -------------------------------------------------------------------------------- 1 | class Git < Scm 2 | def self.clone(repos, branch=nil) 3 | system("git clone #{repos}") 4 | 5 | if branch 6 | system("cd #{repos.split('/').last}/") 7 | system("git checkout #{branch}") 8 | end 9 | end 10 | 11 | def self.run(command) 12 | system("git #{command}") 13 | end 14 | end 15 | 16 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/css/reset.rb: -------------------------------------------------------------------------------- 1 | reset_file_location = "http://developer.yahoo.com/yui/build/reset/reset.css" 2 | unless File.exists?('public/stylesheets/reset.css') 3 | begin 4 | file "public/stylesheets/reset.css" do 5 | open(reset_file_location).read 6 | end 7 | rescue 8 | "Couldn't load #{reset_file_location}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/beet.rb: -------------------------------------------------------------------------------- 1 | # TODO similarly, figure out how to require things as needed by the template 2 | require 'beet/execution' 3 | require 'beet/file_system' 4 | require 'beet/interaction' 5 | require 'beet/capistrano' 6 | require 'beet/rails' 7 | require 'beet/scm' 8 | require 'beet/executor' 9 | require 'beet/gem_location_map' 10 | require 'beet/template_location_map' 11 | -------------------------------------------------------------------------------- /lib/beet/recipes/rack/middleware.rb: -------------------------------------------------------------------------------- 1 | file project_name + ".rb" do 2 | %{ 3 | module Rack 4 | class #{project_name} 5 | 6 | def initialize(app) 7 | @app = app 8 | end 9 | 10 | def call(env) 11 | [200, {'Content-Type' => 'text/html', 'Content-Length' => 100}, ['Hello World']] 12 | end 13 | end 14 | end 15 | }.strip 16 | end 17 | 18 | -------------------------------------------------------------------------------- /features/step_definitions/beet_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^"(.*)" should be a rails app$/ do |directory| 2 | File.exists?(File.join(@active_project_folder, 'app')) 3 | File.exists?(File.join(@active_project_folder, 'config')) 4 | File.exists?(File.join(@active_project_folder, 'db')) 5 | File.exists?(File.join(@active_project_folder, 'public')) 6 | File.exists?(File.join(@active_project_folder, 'vendor')) 7 | end 8 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = beet 2 | 3 | Beet is a simple project generator, with its roots and underlying infrastructure heavily influenced from Rails Templates. 4 | 5 | Please visit the homepage to learn more: http://jackdempsey.github.com/beet 6 | 7 | = How to 8 | to work with ruby 1.9 recommend to use wycats-thor 9 | example: beet -g new_app --recipes rails/jquery,rails/authlogic,rails/git 10 | 11 | == Copyright 12 | 13 | Copyright (c) 2009 Jack Dempsey. See LICENSE for details. 14 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/git.rb: -------------------------------------------------------------------------------- 1 | ["./tmp/pids", "./tmp/sessions", "./tmp/sockets", "./tmp/cache"].each do |f| 2 | run("rmdir ./#{f}") 3 | end 4 | 5 | run("find . \\( -type d -empty \\) -and \\( -not -regex ./\\.git.* \\) -exec touch {}/.gitignore \\;") 6 | 7 | git :init 8 | 9 | file '.gitignore', <<-CODE 10 | log/*.* 11 | db/*.db 12 | db/*.sqlite3 13 | db/schema.rb 14 | tmp/*.* 15 | .DS_Store 16 | doc/api 17 | doc/app 18 | config/database.yml 19 | CODE 20 | 21 | git :add => "." 22 | 23 | git :commit => "-m 'Initial commit. Enjoy.'" 24 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/auth/devise.rb: -------------------------------------------------------------------------------- 1 | gem 'warden', :version => '~> 0.5.1' 2 | gem 'devise', :version => '~> 0.4.1' 3 | 4 | generate "devise_install" 5 | generate "devise User" 6 | generate "devise_views" 7 | 8 | append_file 'config/environments/development.rb', "\nconfig.action_mailer.default_url_options = { :host => 'localhost:3000' }\n" 9 | append_file 'config/environment.rb', "\nDeviseMailer.sender = 'test@example.com'\n" 10 | add_after 'config/routes.rb', '# map.root :controller => "welcome"', "\nmap.root :controller => 'home'\n" 11 | 12 | rake "db:migrate" 13 | -------------------------------------------------------------------------------- /lib/beet/recipes/passenger/vhost.rb: -------------------------------------------------------------------------------- 1 | file "#{project_name}.local.vhost.conf" do 2 | %{ 3 | 4 | ServerName #{project_name}.local 5 | DocumentRoot "#{root}/public" 6 | RackEnv development 7 | 8 | Order allow,deny 9 | Allow from all 10 | 11 | 12 | }.strip 13 | end 14 | default_to = "/etc/apache2/passenger_pane_vhosts" 15 | answer = ask "Where should we write out vhost config: [#{default_to} is default]" 16 | filename = answer.empty? ? default_to : answer 17 | sudo "mv ./#{project_name}.local.vhost.conf #{filename}" 18 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | gem 'cucumber' 2 | require 'cucumber' 3 | gem 'rspec' 4 | require 'spec' 5 | 6 | Before do 7 | @tmp_root = File.dirname(__FILE__) + "/../../tmp" 8 | @home_path = File.expand_path(File.join(@tmp_root, "home")) 9 | FileUtils.rm_rf @tmp_root 10 | FileUtils.mkdir_p @home_path 11 | ENV['HOME'] = @home_path 12 | end 13 | 14 | After do 15 | unless ENV['LEAVE_CUCUMBER_GENERATED_OUTPUT'] 16 | FileUtils.rm_rf @active_project_folder 17 | end 18 | end 19 | 20 | gem "fakeweb" 21 | require "fakeweb" 22 | 23 | Before do 24 | FakeWeb.allow_net_connect = false 25 | end 26 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/jquery.rb: -------------------------------------------------------------------------------- 1 | FileUtils.rm_r Dir.glob("public/javascripts/*.js") 2 | 3 | run "curl -L http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js > public/javascripts/jquery.js" 4 | run "curl -L http://jqueryjs.googlecode.com/svn/trunk/plugins/form/jquery.form.js > public/javascripts/jquery.form.js" 5 | 6 | # Thanks to Chris Wanstrath and Doug Ramsay. The below will make it so that Rails recognizes form requests 7 | file 'public/javascripts/application.js', <<-CODE 8 | jQuery.ajaxSetup({ 9 | 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")} 10 | }); 11 | CODE 12 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/auth/clearance.rb: -------------------------------------------------------------------------------- 1 | gem 'thoughtbot-clearance', :lib => 'clearance',:source => 'http://gems.github.com', :version => '~> 0.6' 2 | gem 'webrat' 3 | gem 'cucumber' 4 | gem 'thoughtbot-factory_girl', :lib => 'factory_girl', :source => "http://gems.github.com" 5 | 6 | 7 | rake "gems:install", :sudo => true 8 | rake "gems:unpack" 9 | rake "db:create:all" 10 | 11 | generate "clearance" 12 | generate "cucumber" 13 | generate "clearance_features" 14 | rake "db:migrate" 15 | 16 | environment "HOST = 'localhost'", :env => "development" 17 | environment "HOST = 'localhost'", :env => "test" 18 | environment "DO_NOT_REPLY = \"donotreply@example.com\"" 19 | route "map.root :controller => 'sessions', :action => 'new'" 20 | 21 | run "cp -fr vendor/gems/thoughtbot-clearance*/app/views app/" 22 | 23 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/cms/bcms_blog.rb: -------------------------------------------------------------------------------- 1 | # install the gem 2 | if `gem list bcms_blog | grep bcms_blog`.empty? 3 | # Run this inside or outside your bcms_blog directory, otherwise download the code 4 | git "clone git://github.com/browsermedia/bcms_blog.git" unless File.exists?('./bcms_blog') || File.basename(Dir.pwd) == 'bcms_blog' 5 | FileUtils.chdir 'bcms_blog' do 6 | system "gem build *.gemspec" 7 | sudo "gem install bcms_blog*.gem" 8 | end 9 | if yes? "Should I delete bcms_blog/" 10 | FileUtils.rm_rf('bcms_blog') 11 | end 12 | end 13 | 14 | # add to project 15 | add_after 'config/environment.rb', "config.gem 'browsercms'" do 16 | "\tconfig.gem 'bcms_blog'" 17 | end 18 | 19 | add_before 'config/routes.rb', 'map.routes_for_browser_cms' do 20 | "\tmap.routes_for_bcms_blog" 21 | end 22 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * refactor methods in bin/beet to use each other (lots of duplication between generate and just_recipe 2 | ( maybe just make generate a boolean option rather than a method ) 3 | * refactor so you can use template names in the recipe calls along with local paths and remote paths 4 | 5 | * fixup places where its obviously just rails stuff, like add_gems 6 | * generate command in bin/beet should have executor stuff pulled out of case, ie, it should always run 7 | * create a better way to mixin things from rails/whatever as needed inside Beet::Executor 8 | 9 | * figure out if its worth it to use similar step definitions as done in Jeweler for gems that generate stuff 10 | and want to verify things worked correctly 11 | 12 | * write some recipes for browsercms 13 | 14 | * work on ubuntu install ideas 15 | 16 | * create a "if gem_exists?('browsercms')" type method 17 | 18 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/cms/bcms_event.rb: -------------------------------------------------------------------------------- 1 | # install the gem 2 | if `gem list bcms_event | grep bcms_event`.empty? 3 | # Run this inside or outside your bcms_event directory, otherwise download the code 4 | git "clone git://github.com/browsermedia/bcms_event.git" unless File.exists?('./bcms_event') || File.basename(Dir.pwd) == 'bcms_event' 5 | FileUtils.chdir 'bcms_event' do 6 | system "gem build *.gemspec" 7 | sudo "gem install bcms_event*.gem" 8 | end 9 | if yes? "Should I delete bcms_event/" 10 | FileUtils.rm_rf('bcms_event') 11 | end 12 | end 13 | 14 | # add to project 15 | add_after 'config/environment.rb', "config.gem 'browsercms'" do 16 | "\tconfig.gem 'bcms_event'" 17 | end 18 | 19 | add_before 'config/routes.rb', 'map.routes_for_browser_cms' do 20 | "\tmap.routes_for_bcms_event\n" 21 | end 22 | 23 | generate "browser_cms" 24 | rake "db:migrate" 25 | -------------------------------------------------------------------------------- /test/executor_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ExecutorShouldaTest < Test::Unit::TestCase 4 | 5 | should "set project root to Dir.pwd + project_name in general case" do 6 | executor = Beet::Executor.new('foobar') 7 | assert_equal Dir.pwd + "/foobar", executor.root 8 | end 9 | 10 | should "set project root to project name if project name looks like /Users/jack/something" do 11 | executor = Beet::Executor.new('/Users/jack/foobar') 12 | assert_equal '/Users/jack/foobar', executor.root 13 | end 14 | 15 | should "set project root to Dir.pwd if directory one level above is the same as the project name" do 16 | FileUtils.mkdir_p 'foobar' 17 | FileUtils.chdir 'foobar' do 18 | executor = Beet::Executor.new('foobar') 19 | assert_equal Dir.pwd, executor.root 20 | end 21 | FileUtils.rm_rf 'foobar' 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /lib/beet/interaction.rb: -------------------------------------------------------------------------------- 1 | module Beet 2 | module Interaction 3 | # Get a user's input 4 | # 5 | # ==== Example 6 | # 7 | # answer = ask("Should I freeze the latest Rails?") 8 | # freeze! if ask("Should I freeze the latest Rails?") == "yes" 9 | # 10 | def ask(string) 11 | log '', string 12 | print '> ' 13 | STDIN.gets.strip 14 | end 15 | 16 | # Helper to test if the user says yes(y)? 17 | # 18 | # ==== Example 19 | # 20 | # freeze! if yes?("Should I freeze the latest Rails?") 21 | # 22 | def yes?(question) 23 | answer = ask(question).downcase 24 | answer == "y" || answer == "yes" 25 | end 26 | 27 | # Helper to test if the user does NOT say yes(y)? 28 | # 29 | # ==== Example 30 | # 31 | # capify! if no?("Will you be using vlad to deploy your application?") 32 | # 33 | def no?(question) 34 | !yes?(question) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/beet/scm.rb: -------------------------------------------------------------------------------- 1 | class Scm 2 | 3 | private 4 | 5 | def self.hash_to_parameters(hash) 6 | hash.collect { |key, value| "--#{key} #{(value.kind_of?(String) ? value : "")}"}.join(" ") 7 | end 8 | end 9 | require File.dirname(__FILE__) + '/scm/git' 10 | require File.dirname(__FILE__) + '/scm/svn' 11 | 12 | module Beet 13 | module SCM 14 | # Run a command in git. 15 | # 16 | # ==== Examples 17 | # 18 | # git :init 19 | # git :add => "this.file that.rb" 20 | # git :add => "onefile.rb", :rm => "badfile.cxx" 21 | # 22 | def git(command = {}) 23 | in_root do 24 | if command.is_a?(Symbol) 25 | log 'running', "git #{command}" 26 | Git.run(command.to_s) 27 | else 28 | command.each do |command, options| 29 | log 'running', "git #{command} #{options}" 30 | Git.run("#{command} #{options}") 31 | end 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/beet/logger.rb: -------------------------------------------------------------------------------- 1 | module Beet 2 | class Logger # :nodoc: 3 | attr_reader :out 4 | attr_accessor :quiet 5 | 6 | def initialize(out = $stdout) 7 | @out = out 8 | @quiet = false 9 | @level = 0 10 | end 11 | 12 | def log(status, message, &block) 13 | @out.print("%12s %s%s\n" % [status, ' ' * @level, message]) unless quiet 14 | indent(&block) if block_given? 15 | end 16 | 17 | def indent(&block) 18 | @level += 1 19 | if block_given? 20 | begin 21 | block.call 22 | ensure 23 | outdent 24 | end 25 | end 26 | end 27 | 28 | def outdent 29 | @level -= 1 30 | if block_given? 31 | begin 32 | block.call 33 | ensure 34 | indent 35 | end 36 | end 37 | end 38 | 39 | private 40 | def method_missing(method, *args, &block) 41 | log(method.to_s, args.first, &block) 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /features/support/common.rb: -------------------------------------------------------------------------------- 1 | module CommonHelpers 2 | def in_tmp_folder(&block) 3 | FileUtils.chdir(@tmp_root, &block) 4 | end 5 | 6 | def in_project_folder(&block) 7 | project_folder = @active_project_folder || @tmp_root 8 | FileUtils.chdir(project_folder, &block) 9 | end 10 | 11 | def in_project_subfolder(folder, &block) 12 | FileUtils.chdir(File.join(@active_project_folder, folder), &block) 13 | end 14 | 15 | def in_home_folder(&block) 16 | FileUtils.chdir(@home_path, &block) 17 | end 18 | 19 | def force_local_lib_override(project_name = @project_name) 20 | rakefile = File.read(File.join(project_name, 'Rakefile')) 21 | File.open(File.join(project_name, 'Rakefile'), "w+") do |f| 22 | f << "$:.unshift('#{@lib_path}')\n" 23 | f << rakefile 24 | end 25 | end 26 | 27 | def setup_active_project_folder project_name 28 | @active_project_folder = File.join(@tmp_root, project_name) 29 | @project_name = project_name 30 | end 31 | end 32 | 33 | World(CommonHelpers) 34 | -------------------------------------------------------------------------------- /features/generating.feature: -------------------------------------------------------------------------------- 1 | Feature: Generating Projects 2 | In order to reduce the time needed to build new rails apps 3 | As a developer 4 | I want to generate apps with beet 5 | 6 | Background: 7 | Given this will run inside a clean "generated_output" directory 8 | 9 | Scenario: Generating a rails app 10 | When I run local executable "beet" with arguments "-g rails_app" 11 | Then folder "rails_app" is created 12 | And "rails_app" should be a rails app 13 | 14 | Scenario: Generating a rails app with rails/git recipe 15 | When I run local executable "beet" with arguments "-g rails_app -r rails/git" 16 | Then folder "rails_app" is created 17 | And "rails_app" should be a rails app 18 | And folder "rails_app/.git" is created 19 | 20 | Scenario: Generating a rails app then running rails/git recipe separately 21 | When I run local executable "beet" with arguments "-g rails_app" 22 | And inside "rails_app" I run local executable "beet" with arguments "-j -r rails/git" 23 | And folder "rails_app/.git" is created" 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Jack Dempsey 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/beet/template_location_map.rb: -------------------------------------------------------------------------------- 1 | TEMPLATE_LOCATIONS = { 2 | 'browsercms' => 'http://github.com/browsermedia/browsercms/raw/master/templates/blank.rb', 3 | 'browsercms_demo' => 'http://github.com/browsermedia/browsercms/raw/master/templates/demo.rb', 4 | 'bort' => 'http://github.com/jeremymcanally/rails-templates/raw/master/bort.rb', 5 | 'bundler' => 'http://github.com/tomafro/dotfiles/raw/master/resources/rails/bundler.rb', 6 | 'compass' => 'http://github.com/chriseppstein/compass/raw/master/lib/compass/app_integration/rails/templates/compass-install-rails.rb', 7 | 'daring' => 'http://github.com/jeremymcanally/rails-templates/raw/master/daring.rb', 8 | 'datamapper' => 'http://github.com/jeremymcanally/rails-templates/raw/master/datamapper.rb', 9 | 'entp' => 'http://github.com/jeremymcanally/rails-templates/raw/master/entp.rb', 10 | 'facebook' => 'http://github.com/jeremymcanally/rails-templates/raw/master/facebook.rb', 11 | 'google_app_engine' => 'http://github.com/jeremymcanally/rails-templates/raw/master/google_app_engine.rb', 12 | 'newgit' => 'http://github.com/jeremymcanally/rails-templates/raw/master/newgit.rb', 13 | 'sethbc' => 'http://github.com/jeremymcanally/rails-templates/raw/master/sethbc.rb', 14 | 'suspenders' => 'http://github.com/jeremymcanally/rails-templates/raw/master/suspenders.rb' 15 | } 16 | -------------------------------------------------------------------------------- /lib/beet/execution.rb: -------------------------------------------------------------------------------- 1 | module Beet 2 | module Execution 3 | # Executes a command 4 | # 5 | # ==== Example 6 | # 7 | # inside('vendor') do 8 | # run('ln -s ~/edge rails) 9 | # end 10 | # 11 | def run(command, log_action = true) 12 | log 'executing', "#{command} from #{Dir.pwd}" if log_action 13 | system(command) 14 | end 15 | 16 | # Executes a command with sudo 17 | # 18 | # ==== Example 19 | # 20 | # inside('vendor') do 21 | # sudo('mkdir /var/log/something') 22 | # end 23 | # 24 | def sudo(command, log_action = true) 25 | command = "#{SUDO}#{command}" 26 | run(command,log_action) 27 | end 28 | 29 | # Executes a ruby script (taking into account WIN32 platform quirks) 30 | def run_ruby_script(command, log_action = true) 31 | ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : '' 32 | run("#{ruby_command}#{command}", log_action) 33 | end 34 | 35 | # Runs the supplied rake task 36 | # 37 | # ==== Example 38 | # 39 | # rake("db:migrate") 40 | # rake("db:migrate", :env => "production") 41 | # rake("gems:install", :sudo => true) 42 | # 43 | def rake(command, options = {}) 44 | log 'rake', command 45 | env = options[:env] || 'development' 46 | sudo = options[:sudo] ? 'sudo ' : '' 47 | in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) } 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/db/mysql.rb: -------------------------------------------------------------------------------- 1 | file 'config/database.yml' do 2 | %{ 3 | # MySQL. Versions 4.1 and 5.0 are recommended. 4 | # 5 | # Install the MySQL driver: 6 | # gem install mysql 7 | # On Mac OS X: 8 | # sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql 9 | # On Mac OS X Leopard: 10 | # sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config 11 | # This sets the ARCHFLAGS environment variable to your native architecture 12 | # On Windows: 13 | # gem install mysql 14 | # Choose the win32 build. 15 | # Install MySQL and put its /bin directory on your path. 16 | # 17 | # And be sure to use new-style password hashing: 18 | # http://dev.mysql.com/doc/refman/5.0/en/old-client.html 19 | --- 20 | development: &defaults 21 | adapter: mysql 22 | encoding: utf8 23 | reconnect: false 24 | database: #{project_name}_development 25 | pool: 5 26 | username: root 27 | password: 28 | 29 | # Warning: The database defined as "test" will be erased and 30 | # re-generated from your development database when you run "rake". 31 | # Do not set this db to the same as development or production. 32 | test: 33 | <<: *defaults 34 | database: #{project_name}_test 35 | 36 | production: 37 | <<: *defaults 38 | database: #{project_name}_production 39 | } 40 | end 41 | 42 | FileUtils.copy "config/database.yml", "config/database.yml.example" 43 | 44 | if yes?("Create databases using rake db:create:all?") 45 | rake "db:create:all" 46 | end 47 | 48 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/db/postgres.rb: -------------------------------------------------------------------------------- 1 | file 'config/database.yml' do 2 | %{ 3 | # PostgreSQL. Versions 7.4 and 8.x are supported. 4 | # 5 | # Install the ruby-postgres driver: 6 | # gem install pg 7 | # On Mac OS X: 8 | # gem install ruby-postgres -- --include=/usr/local/pgsql 9 | # On Windows: 10 | # gem install ruby-postgres 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | 14 | development: &defaults 15 | adapter: postgresql 16 | encoding: unicode 17 | database: #{project_name}_development 18 | pool: 5 19 | username: root 20 | password: 21 | 22 | # Connect on a TCP socket. Omitted by default since the client uses a 23 | # domain socket that doesn't need configuration. Windows does not have 24 | # domain sockets, so uncomment these lines. 25 | #host: localhost 26 | #port: 5432 27 | 28 | # Schema search path. The server defaults to $user,public 29 | #schema_search_path: myapp,sharedapp,public 30 | 31 | # Minimum log levels, in increasing order: 32 | # debug5, debug4, debug3, debug2, debug1, 33 | # log, notice, warning, error, fatal, and panic 34 | # The server defaults to notice. 35 | #min_messages: warning 36 | 37 | 38 | # Warning: The database defined as "test" will be erased and 39 | # re-generated from your development database when you run "rake". 40 | # Do not set this db to the same as development or production. 41 | test: 42 | <<: *defaults 43 | database: #{project_name}_test 44 | 45 | production: 46 | <<: *defaults 47 | database: #{project_name}_production 48 | } 49 | end 50 | 51 | FileUtils.copy "config/database.yml", "config/database.yml.example" 52 | 53 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | 4 | begin 5 | require 'jeweler' 6 | Jeweler::Tasks.new do |gem| 7 | gem.name = "beet" 8 | gem.summary = %Q{A gem to help with easily generating projects} 9 | gem.email = "jack.dempsey@gmail.com" 10 | gem.homepage = "http://github.com/jackdempsey/beet" 11 | gem.authors = ["Jack Dempsey"] 12 | gem.add_dependency "thor", "~> 0.11.6" 13 | # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings 14 | end 15 | 16 | Jeweler::GemcutterTasks.new 17 | 18 | rescue LoadError 19 | puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" 20 | end 21 | 22 | require 'rake/testtask' 23 | Rake::TestTask.new(:test) do |test| 24 | test.libs << 'lib' << 'test' 25 | test.pattern = 'test/**/*_test.rb' 26 | test.verbose = true 27 | end 28 | 29 | begin 30 | require 'rcov/rcovtask' 31 | Rcov::RcovTask.new do |test| 32 | test.libs << 'test' 33 | test.pattern = 'test/**/*_test.rb' 34 | test.verbose = true 35 | end 36 | rescue LoadError 37 | task :rcov do 38 | abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" 39 | end 40 | end 41 | 42 | 43 | task :default => :test 44 | 45 | require 'rake/rdoctask' 46 | Rake::RDocTask.new do |rdoc| 47 | if File.exist?('VERSION.yml') 48 | config = YAML.load(File.read('VERSION.yml')) 49 | version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}" 50 | else 51 | version = "" 52 | end 53 | 54 | rdoc.rdoc_dir = 'rdoc' 55 | rdoc.title = "beet_shoulda #{version}" 56 | rdoc.rdoc_files.include('README*') 57 | rdoc.rdoc_files.include('lib/**/*.rb') 58 | end 59 | 60 | -------------------------------------------------------------------------------- /test/file_system_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | include Beet::FileSystem 4 | 5 | # define some methods that file_system recipes expect to exist 6 | def root; '.'; end 7 | class FileSystemTest < Test::Unit::TestCase 8 | context "#add_after" do 9 | setup do 10 | @filename = 'test.file' 11 | File.open(@filename,'w') do |f| 12 | f.puts "first line" 13 | f.puts "second line" 14 | end 15 | end 16 | 17 | teardown do 18 | File.unlink(@filename) 19 | end 20 | 21 | should "add the given text after the specified text" do 22 | add_after @filename, "first line", "middle line" 23 | assert_equal "first line\nmiddle line\nsecond line\n", File.read(@filename) 24 | end 25 | 26 | should "not add the given text if it already exists after the specified text" do 27 | add_after @filename, "first line", "middle line" 28 | assert_equal "first line\nmiddle line\nsecond line\n", File.read(@filename) 29 | add_after @filename, "first line", "middle line" 30 | assert_equal "first line\nmiddle line\nsecond line\n", File.read(@filename) 31 | end 32 | 33 | should "add the given text if it exists but only elsewhere in the specified text" do 34 | add_after @filename, "first line", "middle line" 35 | assert_equal "first line\nmiddle line\nsecond line\n", File.read(@filename) 36 | add_after @filename, "first line", "second line" 37 | assert_equal "first line\nsecond line\nmiddle line\nsecond line\n", File.read(@filename) 38 | end 39 | end 40 | 41 | context "#append_file" do 42 | setup do 43 | @filename = 'test.file' 44 | File.open(@filename,'w') do |f| 45 | f.puts "first line" 46 | f.puts "second line" 47 | end 48 | end 49 | 50 | teardown do 51 | File.unlink(@filename) 52 | end 53 | 54 | should "add text to the end of the file" do 55 | append_file @filename, "third line" 56 | assert_equal "first line\nsecond line\nthird line", File.read(@filename) 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /beet.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec` 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{beet} 8 | s.version = "0.4.0" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Jack Dempsey"] 12 | s.date = %q{2009-11-13} 13 | s.default_executable = %q{beet} 14 | s.email = %q{jack.dempsey@gmail.com} 15 | s.executables = ["beet"] 16 | s.extra_rdoc_files = [ 17 | "LICENSE", 18 | "README.rdoc" 19 | ] 20 | s.files = [ 21 | ".gitignore", 22 | "LICENSE", 23 | "README.rdoc", 24 | "Rakefile", 25 | "TODO", 26 | "VERSION", 27 | "beet.gemspec", 28 | "bin/beet", 29 | "features/generating.feature", 30 | "features/step_definitions/beet_steps.rb", 31 | "features/step_definitions/common_steps.rb", 32 | "features/support/common.rb", 33 | "features/support/env.rb", 34 | "lib/beet.rb", 35 | "lib/beet/capistrano.rb", 36 | "lib/beet/execution.rb", 37 | "lib/beet/executor.rb", 38 | "lib/beet/file_system.rb", 39 | "lib/beet/gem_location_map.rb", 40 | "lib/beet/interaction.rb", 41 | "lib/beet/logger.rb", 42 | "lib/beet/rails.rb", 43 | "lib/beet/recipes/passenger/vhost.rb", 44 | "lib/beet/recipes/rack/middleware.rb", 45 | "lib/beet/recipes/rails/auth/authlogic.rb", 46 | "lib/beet/recipes/rails/auth/clearance.rb", 47 | "lib/beet/recipes/rails/auth/devise.rb", 48 | "lib/beet/recipes/rails/clean_files.rb", 49 | "lib/beet/recipes/rails/cms/bcms_blog.rb", 50 | "lib/beet/recipes/rails/cms/bcms_event.rb", 51 | "lib/beet/recipes/rails/css/blueprint.rb", 52 | "lib/beet/recipes/rails/css/reset.rb", 53 | "lib/beet/recipes/rails/db/mysql.rb", 54 | "lib/beet/recipes/rails/db/postgres.rb", 55 | "lib/beet/recipes/rails/git.rb", 56 | "lib/beet/recipes/rails/jquery.rb", 57 | "lib/beet/recipes/rails/rspec.rb", 58 | "lib/beet/recipes/rails/shoulda.rb", 59 | "lib/beet/scm.rb", 60 | "lib/beet/scm/git.rb", 61 | "lib/beet/scm/svn.rb", 62 | "lib/beet/template_location_map.rb", 63 | "test/executor_test.rb", 64 | "test/file_system_test.rb", 65 | "test/test_helper.rb" 66 | ] 67 | s.homepage = %q{http://github.com/jackdempsey/beet} 68 | s.rdoc_options = ["--charset=UTF-8"] 69 | s.require_paths = ["lib"] 70 | s.rubygems_version = %q{1.3.5} 71 | s.summary = %q{A gem to help with easily generating projects} 72 | s.test_files = [ 73 | "test/executor_test.rb", 74 | "test/file_system_test.rb", 75 | "test/test_helper.rb" 76 | ] 77 | 78 | if s.respond_to? :specification_version then 79 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 80 | s.specification_version = 3 81 | 82 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then 83 | s.add_runtime_dependency(%q, ["~> 0.11.6"]) 84 | else 85 | s.add_dependency(%q, ["~> 0.11.6"]) 86 | end 87 | else 88 | s.add_dependency(%q, ["~> 0.11.6"]) 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /bin/beet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' 4 | require 'thor' 5 | begin 6 | if RUBY_VERSION <= '1.8.6' 7 | require 'ruby-debug' 8 | end 9 | rescue LoadError 10 | end 11 | $:.unshift(File.dirname(__FILE__) + '/../lib') 12 | require 'beet' 13 | require 'pp' 14 | 15 | WIN32 = (RUBY_PLATFORM =~ /win32|mingw|bccwin|cygwin/) rescue nil 16 | SUDO = (WIN32 || ENV['SUDOLESS']) ? '': 'sudo ' 17 | 18 | class BeetRunner < Thor 19 | map "-g" => :generate 20 | map "-j" => :just_recipe 21 | map "-h" => :help 22 | map "-v" => :version 23 | map "-l" => :list 24 | map "-d" => :display 25 | map "--list" => :list 26 | map "--display" => :display 27 | 28 | desc 'generate [app_name]', "the main app generate method" 29 | method_options %w(recipes -r) => :string, %w(gems) => :string, %w(template -t) => :string, %w(save -s) => :string, %w(use -u) => :string 30 | def generate(app_name, project_type=:rails) 31 | executor = Beet::Executor.new(app_name, options.merge('project_type' => project_type)) 32 | executor.start 33 | end 34 | 35 | desc 'just_recipe', "when you just need a recipe" 36 | method_options %w(recipes -r) => :string, %w(gems) => :string, %w(save -s) => :string, %w(use -u) => :string 37 | def just_recipe(app_name='.') 38 | executor = Beet::Executor.new(app_name, options.merge('generate' => false)) 39 | executor.start 40 | end 41 | 42 | desc 'list', "list what recipes and templates beet knows about" 43 | def list 44 | paths = [] 45 | paths << File.dirname(__FILE__) + '/../lib/beet/recipes/**/*.rb' 46 | paths << ENV['BEET_RECIPES_DIR'] if ENV['BEET_RECIPES_DIR'] 47 | recipes = paths.map do |path| 48 | Dir.glob(path) 49 | end.flatten.compact 50 | puts "\nRecipes: (e.g. beet -g app -r rails/git)" 51 | pp recipes 52 | puts "\nTemplates: (e.g. beet -g app -t bort)" 53 | pp TEMPLATE_LOCATIONS.sort.map {|array| array[0] + " => " + array[1]} 54 | end 55 | 56 | desc 'display', "Display the code for the recipes/templates" 57 | method_options %w(recipes -r) => :string, %w(template -t) => :string 58 | def display(app_name='.') 59 | executor = Beet::Executor.new(app_name,options.merge('generate' => false, 'display' => true)) 60 | executor.start 61 | end 62 | 63 | desc 'version', "the current version of beet" 64 | def version 65 | version_file = File.dirname(__FILE__) + '/../VERSION' 66 | if File.exists?(version_file) and version = File.read(version_file) 67 | puts "Beet version: #{version}" 68 | end 69 | end 70 | 71 | desc 'help', 'help output' 72 | def help 73 | puts %{ 74 | Usage: #{$0} /path/to/your/app [options] 75 | 76 | Options: 77 | -g, --generate Run the generate command to build a project 78 | -j, --just_recipe Run the just_recipe command to only run specified recipes, templates, etc. 79 | -d, --display Instead of running, show the template or recipe body 80 | -r, --recipes Recipes to use 81 | -s, --save Save current set of options 82 | -u, --use Use a saved set of options 83 | --gems Gems to include 84 | 85 | Beet Info: 86 | -v, --version Show the Beet version number and quit. 87 | -l, --list Show the various recipes known to Beet and quit. 88 | -h, --help Show this help message and quit. 89 | 90 | General Options: 91 | 92 | Description: 93 | Beet is used to quickly generate applications. 94 | 95 | Example: 96 | beet generate example_app --recipes="rails/authlogic, rails/clean_files, rails/git" 97 | 98 | Same thing but shorter: 99 | 100 | beet -g example_app -r=rails/authlogic,rails/clean_files,rails/git 101 | } 102 | end 103 | end 104 | def method_missing(*args) 105 | unless @activesupport_required 106 | require 'activesupport' 107 | @activesupport_required = true 108 | m = args.shift 109 | send(m, *args) 110 | else 111 | super 112 | end 113 | end 114 | 115 | BeetRunner.start 116 | 117 | -------------------------------------------------------------------------------- /lib/beet/gem_location_map.rb: -------------------------------------------------------------------------------- 1 | GEM_LOCATIONS = { 2 | 'restful-authentcation' => 'git://github.com/technoweenie/restful-authentication.git', 3 | 'authlogic' => 'git://github.com/binarylogic/authlogic.git', 4 | 'clearance' => 'git://github.com/thoughtbot/clearance.git', 5 | 'will_paginate' => 'git://github.com/mislav/will_paginate.git', 6 | 'paperclip' => 'git://github.com/thoughtbot/paperclip.git', 7 | 'attachment_fu' => 'git://github.com/technoweenie/attachment_fu.git', 8 | 'rspec' => 'git://github.com/dchelimsky/rspec.git', 9 | 'rspec-rails' => 'git://github.com/dchelimsky/rspec-rails.git', 10 | 'cucumber' => 'git://github.com/aslakhellesoy/cucumber.git', 11 | 'shoulda' => 'git://github.com/thoughtbot/shoulda.git', 12 | 'matchy' => 'git://github.com/jeremymcanally/matchy.git', 13 | 'context' => 'git://github.com/jeremymcanally/context.git', 14 | 'exception-notifier' => 'git://github.com/Lipsiasoft/exception-notifier.git', 15 | 'asset_packager' => 'git://github.com/sbecker/asset_packager.git', 16 | 'acts_as_list' => 'git://github.com/rails/acts_as_list.git', 17 | 'acts_as_tree' => 'git://github.com/rails/acts_as_tree.git', 18 | 'webrat' => 'git://github.com/brynary/webrat.git', 19 | 'rcov' => 'git://github.com/relevance/rcov.git', 20 | 'rcov_plugin' => 'git://github.com/commondream/rcov_plugin.git', 21 | 'flog' => 'git://github.com/seattlerb/flog.git', 22 | 'flay' => 'git://github.com/seattlerb/flay.git', 23 | 'hoe' => 'git://github.com/seattlerb/hoe.git', 24 | 'integrity' => 'git://github.com/integrity/integrity.git', 25 | 'active_merchant' => 'git://github.com/Shopify/active_merchant.git', 26 | 'spree' => 'git://github.com/railsdog/spree.git', 27 | 'exception_notification' => 'git://github.com/rails/exception_notification.git', 28 | 'hoptoad_notifier' => 'git://github.com/thoughtbot/hoptoad_notifier.git', 29 | 'exception_logger' => 'git://github.com/defunkt/exception_logger.git', 30 | 'jeweler' => 'git://github.com/technicalpickles/jeweler.git', 31 | 'newgem' => 'git://github.com/drnic/newgem.git', 32 | 'bones' => 'git://github.com/TwP/bones.git', 33 | 'echoe' => 'git://github.com/fauna/echoe.git', 34 | 'nokogiri' => 'git://github.com/tenderlove/nokogiri.git', 35 | 'hpricot' => 'git://github.com/why/hpricot.git', 36 | 'httparty' => 'git://github.com/jnunemaker/httparty.git', 37 | 'rest-client' => 'git://github.com/adamwiggins/rest-client.git', 38 | 'typoeus' => 'git://github.com/pauldix/typhoeus.git', 39 | 'redcloth' => 'git://github.com/jgarber/redcloth.git', 40 | 'rdiscount' => 'git://github.com/rtomayko/rdiscount.git', 41 | 'bluecloth' => 'git://github.com/mislav/bluecloth.git', 42 | 'rr' => 'git://github.com/btakita/rr.git', 43 | 'mocha' => 'git://github.com/floehopper/mocha.git', 44 | 'stump' => 'git://github.com/jeremymcanally/stump.git', 45 | 'active_scaffold' => 'git://github.com/activescaffold/active_scaffold.git', 46 | 'delayed_job' => 'git://github.com/tobi/delayed_job.git', 47 | 'starling' => 'git://github.com/starling/starling.git', 48 | 'amqp' => 'git://github.com/tmm1/amqp.git', 49 | 'negative-captcha' => 'git://github.com/subwindow/negative-captcha.git', 50 | 'factory_girl' => 'git://github.com/thoughtbot/factory_girl.git', 51 | 'machinist' => 'git://github.com/notahat/machinist.git', 52 | 'thinking-sphinx' => 'git://github.com/freelancing-god/thinking-sphinx.git', 53 | 'acts-as-taggable-on' => 'git://github.com/mbleigh/acts-as-taggable-on.git', 54 | 'is_taggable' => 'git://github.com/giraffesoft/is_taggable.git', 55 | 'forgery' => 'git://github.com/sevenwire/forgery.git', 56 | 'faker' => 'git://github.com/yyyc514/faker.git', 57 | 'whenever' => 'git://github.com/javan/whenever.git', 58 | 'aasm' => 'git://github.com/rubyist/aasm.git', 59 | 'workflow' => 'git://github.com/ryan-allen/workflow.git', 60 | 'chef' => 'git://github.com/opscode/chef.git', 61 | 'passenger' => 'git://github.com/FooBarWidget/passenger.git', 62 | 'thin' => 'git://github.com/macournoyer/thin.git', 63 | 'happymapper' => 'git://github.com/jnunemaker/happymapper.git', 64 | 'simple-navigation' => {:source => 'git://github.com/andi/simple-navigation.git', :lib => 'simple_navigation'} 65 | } 66 | -------------------------------------------------------------------------------- /lib/beet/file_system.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | module Beet 3 | module FileSystem 4 | # Create a new file in the project folder. Specify the 5 | # relative path from the project's root. Data is the return value of a block 6 | # or a data string. 7 | # 8 | # ==== Examples 9 | # 10 | # file("lib/fun_party.rb") do 11 | # hostname = ask("What is the virtual hostname I should use?") 12 | # "vhost.name = #{hostname}" 13 | # end 14 | # 15 | # file("config/apach.conf", "your apache config") 16 | # 17 | def file(filename, data = nil, log_action = true, &block) 18 | log 'file', filename if log_action 19 | dir, file = [File.dirname(filename), File.basename(filename)] 20 | 21 | inside(dir) do 22 | File.open(file, "w") do |f| 23 | if block_given? 24 | f.write(block.call) 25 | else 26 | f.write(data) 27 | end 28 | end 29 | end 30 | end 31 | 32 | # Create a new file in the lib/ directory. Code can be specified 33 | # in a block or a data string can be given. 34 | # 35 | # ==== Examples 36 | # 37 | # lib("crypto.rb") do 38 | # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" 39 | # end 40 | # 41 | # lib("foreign.rb", "# Foreign code is fun") 42 | # 43 | def lib(filename, data = nil, &block) 44 | log 'lib', filename 45 | file("lib/#{filename}", data, false, &block) 46 | end 47 | 48 | # Create a new Rakefile with the provided code (either in a block or a string). 49 | # 50 | # ==== Examples 51 | # 52 | # rakefile("bootstrap.rake") do 53 | # project = ask("What is the UNIX name of your project?") 54 | # 55 | # <<-TASK 56 | # namespace :#{project} do 57 | # task :bootstrap do 58 | # puts "i like boots!" 59 | # end 60 | # end 61 | # TASK 62 | # end 63 | # 64 | # rakefile("seed.rake", "puts 'im plantin ur seedz'") 65 | # 66 | def rakefile(filename, data = nil, &block) 67 | log 'rakefile', filename 68 | file("lib/tasks/#{filename}", data, false, &block) 69 | end 70 | 71 | # Do something in the root of the project or 72 | # a provided subfolder; the full path is yielded to the block you provide. 73 | # The path is set back to the previous path when the method exits. 74 | def inside(dir = '', &block) 75 | folder = File.join(root, dir) 76 | FileUtils.mkdir_p(folder) unless File.exist?(folder) 77 | FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield } 78 | end 79 | 80 | def in_root 81 | FileUtils.cd(root) { yield } 82 | end 83 | 84 | # Run a regular expression replacement on a file 85 | # 86 | # ==== Example 87 | # 88 | # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' 89 | # 90 | def gsub_file(relative_destination, regexp, *args, &block) 91 | #path = destination_path(relative_destination) 92 | path = relative_destination 93 | content = File.read(path) 94 | check_for = args.first || yield('') 95 | regex = Regexp.new(regexp.source + Regexp.escape(check_for)) 96 | return if content =~ regex # if we can match the text and its leadin regex, don't add again 97 | content = content.gsub(regexp, *args, &block) 98 | File.open(path, 'wb') { |file| file.write(content) } 99 | end 100 | 101 | # Append text to a file 102 | # 103 | # ==== Example 104 | # 105 | # append_file 'config/environments/test.rb', 'config.gem "rspec"' 106 | # 107 | def append_file(relative_destination, data) 108 | path = destination_path(relative_destination) 109 | File.open(path, 'ab') { |file| file.write(data) } 110 | end 111 | 112 | # Add text after matching line 113 | # 114 | # ==== Example 115 | # 116 | # add_after 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"' 117 | # 118 | def add_after(filename, matching_text, data=nil, &block) 119 | gsub_file filename, /(\s*#{Regexp.escape(matching_text)})/mi do |match| 120 | "#{match}\n#{data || block.call}" 121 | end 122 | end 123 | 124 | # Add text before matching line 125 | # 126 | # ==== Example 127 | # 128 | # add_before 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"' 129 | # 130 | def add_before(filename, matching_text, data=nil, &block) 131 | gsub_file filename, /^(\s*#{Regexp.escape(matching_text)})/mi do |match| 132 | "#{data || block.call}#{match}" 133 | end 134 | end 135 | 136 | protected 137 | 138 | def destination_path(relative_destination) 139 | File.join(root, relative_destination) 140 | end 141 | 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /lib/beet/rails.rb: -------------------------------------------------------------------------------- 1 | module Beet 2 | module Rails 3 | # Make an entry in Rails routing file conifg/routes.rb 4 | # 5 | # === Example 6 | # 7 | # route "map.root :controller => :welcome" 8 | # 9 | def route(routing_code) 10 | log 'route', routing_code 11 | sentinel = 'ActionController::Routing::Routes.draw do |map|' 12 | 13 | in_root do 14 | gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| 15 | "#{match}\n #{routing_code}\n" 16 | end 17 | end 18 | end 19 | 20 | # Add Rails to /vendor/rails 21 | # 22 | # ==== Example 23 | # 24 | # freeze! 25 | # 26 | def freeze!(args = {}) 27 | log 'vendor', 'rails edge' 28 | in_root { run('rake rails:freeze:edge', false) } 29 | end 30 | 31 | # Install a plugin. You must provide either a Subversion url or Git url. 32 | # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned. 33 | # 34 | # ==== Examples 35 | # 36 | # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' 37 | # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true 38 | # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' 39 | # 40 | def plugin(name, options) 41 | log 'plugin', name 42 | 43 | if options[:git] && options[:submodule] 44 | in_root do 45 | Git.run("submodule add #{options[:git]} vendor/plugins/#{name}") 46 | end 47 | elsif options[:git] || options[:svn] 48 | in_root do 49 | run_ruby_script("script/plugin install #{options[:svn] || options[:git]}", false) 50 | end 51 | else 52 | log "! no git or svn provided for #{name}. skipping..." 53 | end 54 | end 55 | 56 | # Adds an entry into config/environment.rb for the supplied gem : 57 | def gem(name, options = {}) 58 | log 'gem', name 59 | env = options.delete(:env) 60 | 61 | gems_code = "config.gem '#{name}'" 62 | 63 | if options.any? 64 | opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ") 65 | gems_code << ", #{opts}" 66 | end 67 | 68 | environment gems_code, :env => env 69 | end 70 | 71 | # Adds a line inside the Initializer block for config/environment.rb. Used by #gem 72 | # If options :env is specified, the line is appended to the corresponding 73 | # file in config/environments/#{env}.rb 74 | def environment(data = nil, options = {}, &block) 75 | sentinel = 'Rails::Initializer.run do |config|' 76 | 77 | data = block.call if !data && block_given? 78 | 79 | in_root do 80 | if options[:env].nil? 81 | gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match| 82 | "#{match}\n " << data 83 | end 84 | else 85 | Array.wrap(options[:env]).each do|env| 86 | append_file "config/environments/#{env}.rb", "\n#{data}" 87 | end 88 | end 89 | end 90 | end 91 | 92 | # Create a new file in the vendor/ directory. Code can be specified 93 | # in a block or a data string can be given. 94 | # 95 | # ==== Examples 96 | # 97 | # vendor("sekrit.rb") do 98 | # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" 99 | # "salt = '#{sekrit_salt}'" 100 | # end 101 | # 102 | # vendor("foreign.rb", "# Foreign code is fun") 103 | # 104 | def vendor(filename, data = nil, &block) 105 | log 'vendoring', filename 106 | file("vendor/#{filename}", data, false, &block) 107 | end 108 | 109 | 110 | # Create a new initializer with the provided code (either in a block or a string). 111 | # 112 | # ==== Examples 113 | # 114 | # initializer("globals.rb") do 115 | # data = "" 116 | # 117 | # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do 118 | # data << "#{const} = :entp" 119 | # end 120 | # 121 | # data 122 | # end 123 | # 124 | # initializer("api.rb", "API_KEY = '123456'") 125 | # 126 | def initializer(filename, data = nil, &block) 127 | log 'initializer', filename 128 | file("config/initializers/#{filename}", data, false, &block) 129 | end 130 | 131 | # Generate something using a generator from Rails or a plugin. 132 | # The second parameter is the argument string that is passed to 133 | # the generator or an Array that is joined. 134 | # 135 | # ==== Example 136 | # 137 | # generate(:authenticated, "user session") 138 | # 139 | def generate(what, *args) 140 | log 'generating', what 141 | argument = args.map {|arg| arg.to_s }.flatten.join(" ") 142 | 143 | in_root { run_ruby_script("script/generate #{what} #{argument}", false) } 144 | end 145 | end # Rails 146 | end # Beet 147 | -------------------------------------------------------------------------------- /lib/beet/executor.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | require 'beet/logger' 3 | 4 | module Beet 5 | class Executor 6 | BEET_DATA_FILE = "~/.beet.yml" 7 | include Beet::Execution 8 | include Beet::FileSystem 9 | include Beet::Interaction 10 | 11 | include Beet::Rails 12 | include Beet::Capistrano 13 | include Beet::SCM 14 | 15 | attr_reader :root, :logger, :options, :template 16 | attr_accessor :recipes, :project_name, :gems, :todo_items 17 | 18 | def initialize(project_name, options={}) # :nodoc: 19 | @root = calculate_project_root(project_name) 20 | @project_name = ((project_name == '.') ? File.basename(Dir.pwd) : project_name) 21 | @logger = Beet::Logger.new 22 | @gems = [] 23 | @template = options[:template] 24 | @options = options 25 | @todo_items = '' 26 | @recipes = [] 27 | @project_type = options[:project_type] 28 | @generate = true unless options[:generate] == false 29 | @display = options[:display] 30 | extract_commands_from_options 31 | end 32 | 33 | def start 34 | if @options[:use] 35 | puts "Loading saved configuration: #{@options[:use]}" 36 | data = load_saved_recipe_file 37 | if config = data[@options[:use]] 38 | @gems.concat(config[:gems]) if config[:gems] 39 | @template = config[:template] if config[:template] 40 | @recipes.concat(config[:recipes]) if config[:recipes] 41 | end 42 | end 43 | 44 | if @display && @template 45 | puts open(TEMPLATE_LOCATIONS[@template]).read 46 | else 47 | case @project_type 48 | when :rails 49 | if @generate 50 | puts "Generating rails project #{project_name}..." 51 | if @template 52 | system("rails #{project_name} -m #{TEMPLATE_LOCATIONS[@template]}") 53 | else 54 | system("rails #{project_name}") 55 | end 56 | end 57 | end 58 | 59 | add_gems 60 | 61 | print_todo 62 | 63 | if @options[:save] 64 | save_run 65 | end 66 | end 67 | 68 | run_recipes 69 | 70 | end 71 | 72 | def run_recipes 73 | @recipes.each do |recipe| 74 | begin 75 | code = open(recipe).read 76 | if @display 77 | puts code 78 | else 79 | in_root { instance_eval(code)} 80 | end 81 | rescue LoadError, Errno::ENOENT => e 82 | raise "The recipe [#{recipe}] could not be loaded. Error: #{e}" 83 | end 84 | end 85 | end 86 | 87 | def log(*args) 88 | logger.log(*args) 89 | end 90 | 91 | def todo(string=nil, &block) 92 | self.todo_items << (string || block.call) 93 | end 94 | 95 | private 96 | 97 | def print_todo 98 | unless todo_items.empty? 99 | puts '#' * 30 100 | puts "TODO Items:" 101 | puts todo_items 102 | puts '#' * 30 103 | end 104 | end 105 | 106 | def calculate_project_root(project_name) 107 | # if the name looks like ~/projects/foobar then thats the root 108 | if project_name.include?('/') 109 | project_name 110 | # if we're running inside the app, then current dir is it 111 | elsif File.basename(Dir.pwd) == project_name 112 | Dir.pwd 113 | # assume the root is ./project_name 114 | else 115 | File.join(Dir.pwd, project_name) 116 | end 117 | end 118 | 119 | def add_gems 120 | if @gems 121 | @gems.each do |gem_data| 122 | duped_data = gem_data.clone # to avoid removing :name from @gems 123 | name = duped_data.delete(:name) 124 | gem(name, duped_data) 125 | end 126 | end 127 | end 128 | 129 | def extract_commands_from_options 130 | if @options[:gems] 131 | @options[:gems].split(/[\s,]+/).each do |gem| 132 | if gem_info = gem_location(gem) 133 | if gem_info.is_a?(Hash) 134 | @gems << {:name => gem}.merge(gem_info) 135 | else 136 | @gems << {:name => gem, :source => gem_info} 137 | end 138 | else 139 | @gems << {:name => gem} 140 | end 141 | end 142 | end 143 | if @options[:recipes] 144 | @options[:recipes].split(/[\s,]+/).each do |recipe| 145 | if file = recipe_location(recipe) 146 | @recipes << file 147 | else 148 | puts "Can't find recipe #{recipe}" 149 | end 150 | end 151 | end 152 | end 153 | 154 | def save_run 155 | require 'yaml' 156 | name = if options[:save] == true 157 | ask("Enter a name for this configuration: ") 158 | else 159 | options[:save] 160 | end 161 | data = load_saved_recipe_file 162 | data[name] = {:gems => @gems, :recipes => @recipes, :template => @template} 163 | write_saved_recipe_file(data) 164 | end 165 | 166 | def beet_data_file 167 | File.expand_path(BEET_DATA_FILE) 168 | end 169 | 170 | def load_saved_recipe_file 171 | if File.exists?(beet_data_file) 172 | YAML.load_file(beet_data_file) 173 | else 174 | {} 175 | end 176 | end 177 | 178 | def write_saved_recipe_file(data) 179 | File.open(beet_data_file, "wb") do |f| 180 | f.write(YAML::dump(data)) 181 | end 182 | end 183 | 184 | def gem_location(gem_name) 185 | GEM_LOCATIONS[gem_name] 186 | end 187 | 188 | def recipe_location(recipe) 189 | return recipe if File.exists?(recipe) or recipe.include?('http://') 190 | locations = [] 191 | locations << File.expand_path(ENV['BEET_RECIPES_DIR']) if ENV['BEET_RECIPES_DIR'] 192 | locations << File.expand_path(File.join(File.dirname(__FILE__), 'recipes')) 193 | locations.each do |location| 194 | filename = File.join(location, "#{recipe}.rb") 195 | return filename if File.exists?(filename) 196 | end 197 | nil 198 | end 199 | end 200 | end 201 | 202 | -------------------------------------------------------------------------------- /features/step_definitions/common_steps.rb: -------------------------------------------------------------------------------- 1 | Given /this will run inside a clean "(.*)" directory/ do |directory| 2 | @active_project_folder = File.expand_path File.join(File.dirname(__FILE__), '..', '..', directory) 3 | FileUtils.rm_rf @active_project_folder 4 | FileUtils.mkdir_p @active_project_folder 5 | end 6 | 7 | Given /^env variable \$([\w_]+) set to "(.*)"/ do |env_var, value| 8 | ENV[env_var] = value 9 | end 10 | 11 | Given /"(.*)" folder is deleted/ do |folder| 12 | in_project_folder { FileUtils.rm_rf folder } 13 | end 14 | 15 | When /^inside "(.*)" I run local executable "(.*)" with arguments "(.*)"$/ do |directory, executable, arguments| 16 | @stdout = File.expand_path(File.join(@tmp_root, "executable.out")) 17 | executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable)) 18 | in_project_subfolder(directory) do 19 | system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}" 20 | end 21 | end 22 | 23 | When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, arguments| 24 | @stdout = StringIO.new 25 | in_project_folder do 26 | if Object.const_defined?("APP_ROOT") 27 | APP_ROOT.replace(FileUtils.pwd) 28 | else 29 | APP_ROOT = FileUtils.pwd 30 | end 31 | run_generator(generator, arguments.split(' '), SOURCES, :stdout => @stdout) 32 | end 33 | File.open(File.join(@tmp_root, "generator.out"), "w") do |f| 34 | @stdout.rewind 35 | f << @stdout.read 36 | end 37 | end 38 | 39 | When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, arguments| 40 | @stdout = File.expand_path(File.join(@tmp_root, "executable.out")) 41 | in_project_folder do 42 | system "#{executable} #{arguments} > #{@stdout} 2> #{@stdout}" 43 | end 44 | end 45 | 46 | When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, arguments| 47 | @stdout = File.expand_path(File.join(@tmp_root, "executable.out")) 48 | in_project_folder do 49 | system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}" 50 | end 51 | end 52 | 53 | When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, arguments| 54 | @stdout = File.expand_path(File.join(@tmp_root, "executable.out")) 55 | executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable)) 56 | in_project_folder do 57 | `ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}` 58 | end 59 | end 60 | 61 | When /^I invoke task "rake (.*)"/ do |task| 62 | @stdout = File.expand_path(File.join(@tmp_root, "tests.out")) 63 | in_project_folder do 64 | system "rake #{task} --trace > #{@stdout} 2> #{@stdout}" 65 | end 66 | end 67 | 68 | Then /^folder "(.*)" (is|is not) created/ do |folder, is| 69 | in_project_folder do 70 | File.exists?(folder).should(is == 'is' ? be_true : be_false) 71 | end 72 | end 73 | 74 | Then /^file "(.*)" (is|is not) created/ do |file, is| 75 | in_project_folder do 76 | File.exists?(file).should(is == 'is' ? be_true : be_false) 77 | end 78 | end 79 | 80 | Then /^file with name matching "(.*)" is created/ do |pattern| 81 | in_project_folder do 82 | Dir[pattern].should_not be_empty 83 | end 84 | end 85 | 86 | Then /^file "(.*)" contents (does|does not) match \/(.*)\// do |file, does, regex| 87 | in_project_folder do 88 | actual_output = File.read(file) 89 | (does == 'does') ? 90 | actual_output.should(match(/#{regex}/)) : 91 | actual_output.should_not(match(/#{regex}/)) 92 | end 93 | end 94 | 95 | Then /gem file "(.*)" and generated file "(.*)" should be the same/ do |gem_file, project_file| 96 | File.exists?(gem_file).should be_true 97 | File.exists?(project_file).should be_true 98 | gem_file_contents = File.read(File.dirname(__FILE__) + "/../../#{gem_file}") 99 | project_file_contents = File.read(File.join(@active_project_folder, project_file)) 100 | project_file_contents.should == gem_file_contents 101 | end 102 | 103 | Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator| 104 | actual_output = File.read(@stdout) 105 | does_invoke == "does" ? 106 | actual_output.should(match(/dependency\s+#{generator}/)) : 107 | actual_output.should_not(match(/dependency\s+#{generator}/)) 108 | end 109 | 110 | Then /I should see help option "(.*)"/ do |opt| 111 | actual_output = File.read(@stdout) 112 | actual_output.should match(/#{opt}/) 113 | end 114 | 115 | Then /^I should see$/ do |text| 116 | actual_output = File.read(@stdout) 117 | actual_output.should contain(text) 118 | end 119 | 120 | Then /^I should not see$/ do |text| 121 | actual_output = File.read(@stdout) 122 | actual_output.should_not contain(text) 123 | end 124 | 125 | Then /^I should see exactly$/ do |text| 126 | actual_output = File.read(@stdout) 127 | actual_output.should == text 128 | end 129 | 130 | Then /^I should see all (\d+) tests pass/ do |expected_test_count| 131 | expected = %r{^#{expected_test_count} tests, \d+ assertions, 0 failures, 0 errors} 132 | actual_output = File.read(@stdout) 133 | actual_output.should match(expected) 134 | end 135 | 136 | Then /^I should see all (\d+) examples pass/ do |expected_test_count| 137 | expected = %r{^#{expected_test_count} examples?, 0 failures} 138 | actual_output = File.read(@stdout) 139 | actual_output.should match(expected) 140 | end 141 | 142 | Then /^yaml file "(.*)" contains (\{.*\})/ do |file, yaml| 143 | in_project_folder do 144 | yaml = eval yaml 145 | YAML.load(File.read(file)).should == yaml 146 | end 147 | end 148 | 149 | Then /^Rakefile can display tasks successfully/ do 150 | @stdout = File.expand_path(File.join(@tmp_root, "rakefile.out")) 151 | in_project_folder do 152 | system "rake -T > #{@stdout} 2> #{@stdout}" 153 | end 154 | actual_output = File.read(@stdout) 155 | actual_output.should match(/^rake\s+\w+\s+#\s.*/) 156 | end 157 | 158 | Then /^task "rake (.*)" is executed successfully/ do |task| 159 | @stdout.should_not be_nil 160 | actual_output = File.read(@stdout) 161 | actual_output.should_not match(/^Don't know how to build task '#{task}'/) 162 | actual_output.should_not match(/Error/i) 163 | end 164 | 165 | Then /^gem spec key "(.*)" contains \/(.*)\// do |key, regex| 166 | in_project_folder do 167 | gem_file = Dir["pkg/*.gem"].first 168 | gem_spec = Gem::Specification.from_yaml(`gem spec #{gem_file}`) 169 | spec_value = gem_spec.send(key.to_sym) 170 | spec_value.to_s.should match(/#{regex}/) 171 | end 172 | end 173 | -------------------------------------------------------------------------------- /lib/beet/recipes/rails/auth/authlogic.rb: -------------------------------------------------------------------------------- 1 | file "app/models/user_session.rb" do 2 | %{ 3 | class UserSession < Authlogic::Session::Base 4 | logout_on_timeout true # default is false 5 | end 6 | }.strip 7 | end 8 | 9 | file "app/models/user.rb" do 10 | %{ 11 | class User < ActiveRecord::Base 12 | acts_as_authentic do |c| 13 | c.logged_in_timeout = 10.minutes # default is 10.minutes 14 | end 15 | end 16 | }.strip 17 | end 18 | 19 | file "app/controllers/user_sessions_controller.rb" do 20 | %{ 21 | class UserSessionsController < ApplicationController 22 | before_filter :require_no_user, :only => [:new, :create] 23 | before_filter :require_user, :only => :destroy 24 | 25 | def new 26 | @user_session = UserSession.new 27 | end 28 | 29 | def create 30 | @user_session = UserSession.new(params[:user_session]) 31 | if @user_session.save 32 | flash[:notice] = "Login successful!" 33 | redirect_back_or_default account_url 34 | else 35 | render :action => :new 36 | end 37 | end 38 | 39 | def destroy 40 | current_user_session.destroy 41 | flash[:notice] = "Logout successful!" 42 | redirect_back_or_default new_user_session_url 43 | end 44 | end 45 | }.strip 46 | end 47 | 48 | 49 | file "app/views/user_sessions/new.html.erb" do 50 | %{ 51 |

Login

52 | 53 | <% form_for @user_session, :url => user_session_path do |f| %> 54 | <%= f.error_messages %> 55 | <%= f.label :email %>
56 | <%= f.text_field :email %>
57 |
58 | <%= f.label :password %>
59 | <%= f.password_field :password %>
60 |
61 | <%= f.check_box :remember_me %><%= f.label :remember_me %>
62 |
63 | <%= f.submit "Login" %> 64 | <% end %> 65 | }.strip 66 | end 67 | 68 | # Setup some routes 69 | route 'map.resource :user_session' 70 | route 'map.resource :account, :controller => "users"' 71 | route 'map.resources :users' 72 | route 'map.register "/register", :controller => "users", :action => "new"' 73 | route 'map.login "/login", :controller => "user_sessions", :action => "new"' 74 | route 'map.logout "/logout", :controller => "user_sessions", :action => "destroy"' 75 | 76 | file "app/controllers/application_controller.rb" do 77 | %{ 78 | # Filters added to this controller apply to all controllers in the application. 79 | # Likewise, all the methods added will be available for all controllers. 80 | 81 | class ApplicationController < ActionController::Base 82 | helper :all 83 | helper_method :current_user_session, :current_user 84 | filter_parameter_logging :password, :password_confirmation 85 | 86 | private 87 | def current_user_session 88 | return @current_user_session if defined?(@current_user_session) 89 | @current_user_session = UserSession.find 90 | end 91 | 92 | def current_user 93 | return @current_user if defined?(@current_user) 94 | @current_user = current_user_session && current_user_session.record 95 | end 96 | 97 | def require_user 98 | unless current_user 99 | store_location 100 | flash[:notice] = "You must be logged in to access this page" 101 | redirect_to new_user_session_url 102 | return false 103 | end 104 | end 105 | 106 | def require_no_user 107 | if current_user 108 | store_location 109 | flash[:notice] = "You must be logged out to access this page" 110 | redirect_to account_url 111 | return false 112 | end 113 | end 114 | 115 | def store_location 116 | session[:return_to] = request.request_uri 117 | end 118 | 119 | def redirect_back_or_default(default) 120 | redirect_to(session[:return_to] || default) 121 | session[:return_to] = nil 122 | end 123 | end 124 | }.strip 125 | end 126 | 127 | file "app/controllers/users_controller.rb" do 128 | %{ 129 | class UsersController < ApplicationController 130 | before_filter :require_no_user, :only => [:new, :create] 131 | before_filter :require_user, :only => [:show, :edit, :update] 132 | 133 | def new 134 | @user = User.new 135 | end 136 | 137 | def create 138 | @user = User.new(params[:user]) 139 | if @user.save 140 | flash[:notice] = "Account registered!" 141 | redirect_back_or_default account_url 142 | else 143 | render :action => :new 144 | end 145 | end 146 | 147 | def show 148 | @user = @current_user 149 | end 150 | 151 | def edit 152 | @user = @current_user 153 | end 154 | 155 | def update 156 | @user = @current_user # makes our views "cleaner" and more consistent 157 | if @user.update_attributes(params[:user]) 158 | flash[:notice] = "Account updated!" 159 | redirect_to account_url 160 | else 161 | render :action => :edit 162 | end 163 | end 164 | end 165 | }.strip 166 | end 167 | 168 | file "app/views/users/_form.html.erb" do 169 | %{ 170 | <%= form.label :email %>
171 | <%= form.text_field :email %>
172 |
173 | <%= form.label :password, form.object.new_record? ? nil : "Change password" %>
174 | <%= form.password_field :password %>
175 |
176 | <%= form.label :password_confirmation %>
177 | <%= form.password_field :password_confirmation %>
178 | }.strip 179 | end 180 | 181 | file "app/views/users/edit.html.erb" do 182 | %{ 183 |

Edit My Account

184 | 185 | <% form_for @user, :url => account_path do |f| %> 186 | <%= f.error_messages %> 187 | <%= render :partial => "form", :object => f %> 188 | <%= f.submit "Update" %> 189 | <% end %> 190 | 191 |
<%= link_to "My Profile", account_path %> 192 | }.strip 193 | end 194 | 195 | file "app/views/users/new.html.erb" do 196 | %{ 197 |

Register

198 | 199 | <% form_for @user, :url => account_path do |f| %> 200 | <%= f.error_messages %> 201 | <%= render :partial => "form", :object => f %> 202 | <%= f.submit "Register" %> 203 | <% end %> 204 | }.strip 205 | end 206 | 207 | file "app/views/users/show.html.erb" do 208 | %{ 209 |

210 | Email: 211 | <%=h @user.email %> 212 |

213 | 214 |

215 | Login count: 216 | <%=h @user.login_count %> 217 |

218 | 219 |

220 | Last request at: 221 | <%=h @user.last_request_at %> 222 |

223 | 224 |

225 | Last login at: 226 | <%=h @user.last_login_at %> 227 |

228 | 229 |

230 | Current login at: 231 | <%=h @user.current_login_at %> 232 |

233 | 234 |

235 | Last login ip: 236 | <%=h @user.last_login_ip %> 237 |

238 | 239 |

240 | Current login ip: 241 | <%=h @user.current_login_ip %> 242 |

243 | 244 | 245 | <%= link_to 'Edit', edit_account_path %> 246 | }.strip 247 | end 248 | 249 | # can't rely on internal rails migration generation, so we do it this way 250 | 251 | #Dir.chdir("script") #for ruby 1.9.2 08/07/2009 . no need for ruby1.9.1p129 252 | #run "./generate migration beet_authlogic_create_user" # for ruby 1.9.2 08/07/2009. no need for ruby1.9.1p129 253 | 254 | run "script/generate migration beet_authlogic_create_user" 255 | 256 | #now open it 257 | #Dir.chdir("..") # for ruby 1.9.2 08/07/2009. no need for ruby1.9.1p129 258 | 259 | file(Dir.glob('db/migrate/*beet_authlogic_create_user*').first) do 260 | %{ 261 | class BeetAuthlogicCreateUser < ActiveRecord::Migration 262 | def self.up 263 | unless table_exists?(:users) 264 | create_table :users do |t| 265 | t.string :email, :null => false # optional, you can use login instead, or both 266 | t.string :crypted_password, :null => false # optional, see below 267 | t.string :password_salt, :null => false # optional, but highly recommended 268 | t.string :persistence_token, :null => false # required 269 | t.string :single_access_token, :null => false # optional, see Authlogic::Session::Params 270 | t.string :perishable_token, :null => false # optional, see Authlogic::Session::Perishability 271 | 272 | # Magic columns, just like ActiveRecord's created_at and updated_at. These are automatically maintained by Authlogic if they are present. 273 | t.integer :login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns 274 | t.integer :failed_login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns 275 | t.datetime :last_request_at # optional, see Authlogic::Session::MagicColumns 276 | t.datetime :current_login_at # optional, see Authlogic::Session::MagicColumns 277 | t.datetime :last_login_at # optional, see Authlogic::Session::MagicColumns 278 | t.string :current_login_ip # optional, see Authlogic::Session::MagicColumns 279 | t.string :last_login_ip # optional, see Authlogic::Session::MagicColumns 280 | end 281 | end 282 | end 283 | 284 | def self.down 285 | drop_table :users 286 | end 287 | end 288 | }.strip 289 | end 290 | 291 | 292 | gem 'authlogic', :version => '~> 2.0.0' 293 | 294 | if yes?("Install using sudo?") 295 | rake "gems:install", :sudo => true 296 | else 297 | rake "gems:install" 298 | end 299 | 300 | rake "db:create:all" 301 | rake "db:migrate" 302 | 303 | --------------------------------------------------------------------------------