diff --git a/.gitignore b/.gitignore
index 0bb2761..f53e842 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@ dist/*
.idea
.rvmrc
.DS_Store
+local_scrolls
+scroll_config.rb
\ No newline at end of file
diff --git a/README.md b/README.md
index 6a0387c..0bc7a07 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,28 @@ Or print out a list of scrolls for a specific category:
scrolls list persistence
+### User Scrolls
+
+You can define an environment variable APPSCROLLS_DIR with a path to your local app scrolls:
+
+ export APPSCROLLS_DIR=~/.scrolls
+
+### Scroll Configuration
+
+If you wish to skip any configuration questions, you can provide the path to a configuration file with the `-c` or `--config` option:
+
+ scrolls new APP_NAME -c ~/.scrolls_config.rb
+
+This config file is a script that can provide defaults for scrolls by setting config.SCROLL_NAME.OPTION_NAME. For example:
+```
+config.postgresql.pg_username = 'root'
+config.postgresql.pg_password = ''
+config.guard.guard_notifications = false
+```
+
+Or instead of passing a flag each time, just set the APPSCROLLS_CONFIG environment variable to include it every time:
+ export APPSCROLLS_CONFIG=~/.scrolls_config.rb
+
## Deployment Support
Web applications are boring if they aren't running proudly on the internet. The App Scrolls make this automatic for your favourite providers!
diff --git a/Rakefile b/Rakefile
index bdda232..ff94be6 100644
--- a/Rakefile
+++ b/Rakefile
@@ -20,7 +20,7 @@ task :run => :clean do
require 'tempfile'
require 'appscrolls'
- template = AppScrollsScrolls::Template.new(scrolls)
+ template = AppScrolls::Template.new(scrolls)
begin
dir = Dir.mktmpdir "rails_template"
@@ -42,17 +42,17 @@ task :print do
require 'appscrolls'
scrolls = ENV['SCROLLS'].split(',')
- puts AppScrollsScrolls::Template.new(scrolls).compile
+ puts AppScrolls::Template.new(scrolls).compile
end
namespace :list do
desc "Display scrolls by category"
task :categories do
require 'appscrolls'
- categories = AppScrollsScrolls::Scrolls.categories.sort
+ categories = AppScrolls::Scrolls.categories.sort
categories = (categories - ["other"]) + ["other"]
categories.each do |category|
- puts "#{category}: #{AppScrollsScrolls::Scrolls.for(category).join(", ")}"
+ puts "#{category}: #{AppScrolls::Scrolls.for(category).join(", ")}"
end
end
@@ -60,4 +60,4 @@ namespace :list do
# task :exclusions do
#
# end
-end
\ No newline at end of file
+end
diff --git a/appscrolls.gemspec b/appscrolls.gemspec
index 15414f5..8a44203 100644
--- a/appscrolls.gemspec
+++ b/appscrolls.gemspec
@@ -4,7 +4,7 @@ require File.dirname(__FILE__) + "/version"
Gem::Specification.new do |s|
s.name = "appscrolls"
- s.version = AppScrollsScrolls::VERSION
+ s.version = AppScrolls::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Michael Bleigh", "Dr Nic Williams"]
s.email = ["michael@intridea.com", "drnicwilliams@gmail.com"]
diff --git a/bin/appscrolls b/bin/appscrolls
index c304fcf..d958554 100755
--- a/bin/appscrolls
+++ b/bin/appscrolls
@@ -4,4 +4,4 @@ $:.push File.dirname(__FILE__) + '/../lib'
require 'rubygems'
require 'appscrolls/command'
-AppScrollsScrolls::Command.start
\ No newline at end of file
+AppScrolls::Command.start
\ No newline at end of file
diff --git a/bin/scroll b/bin/scroll
new file mode 100755
index 0000000..d958554
--- /dev/null
+++ b/bin/scroll
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+$:.push File.dirname(__FILE__) + '/../lib'
+
+require 'rubygems'
+require 'appscrolls/command'
+
+AppScrolls::Command.start
\ No newline at end of file
diff --git a/bin/scrolls b/bin/scrolls
index c304fcf..d958554 100755
--- a/bin/scrolls
+++ b/bin/scrolls
@@ -4,4 +4,4 @@ $:.push File.dirname(__FILE__) + '/../lib'
require 'rubygems'
require 'appscrolls/command'
-AppScrollsScrolls::Command.start
\ No newline at end of file
+AppScrolls::Command.start
\ No newline at end of file
diff --git a/lib/appscrolls.rb b/lib/appscrolls.rb
index 3a3e3d8..79be485 100644
--- a/lib/appscrolls.rb
+++ b/lib/appscrolls.rb
@@ -3,8 +3,24 @@
require 'appscrolls/config'
require 'appscrolls/template'
-Dir[File.dirname(__FILE__) + '/../scrolls/*.rb'].each do |path|
+def enroll_scroll_at(path)
key = File.basename(path, '.rb')
- scroll = AppScrollsScrolls::Scroll.generate(key, File.open(path))
- AppScrollsScrolls::Scrolls.add(scroll)
+ scroll_class_name = ActiveSupport::Inflector.camelize(key.gsub("-", "_"))
+
+ # default files of same keys as local scrolls are discarded
+ return if AppScrolls::Scrolls.const_defined?(scroll_class_name)
+
+ scroll = AppScrolls::Scroll.generate(key, File.open(path))
+ AppScrolls::Scrolls.add(scroll)
+end
+
+scroll_files = Dir[File.dirname(__FILE__) + '/../scrolls/*.rb']
+
+# set up local scrolls if available
+if dir = ENV['APPSCROLLS_DIR'] and dir != ""
+ scroll_files = Dir[dir+"/**{,/*/**}/*.rb"] + scroll_files
+end
+
+scroll_files.each do |path|
+ enroll_scroll_at(path)
end
diff --git a/lib/appscrolls/command.rb b/lib/appscrolls/command.rb
index 869cd3b..2ba4495 100644
--- a/lib/appscrolls/command.rb
+++ b/lib/appscrolls/command.rb
@@ -1,15 +1,24 @@
require 'appscrolls'
require 'thor'
+require File.dirname(__FILE__) + "/../../version"
-module AppScrollsScrolls
+module AppScrolls
class Command < Thor
include Thor::Actions
+
+ desc "version", "show version of currently installed AppScrolls gem"
+ def version
+ puts "AppScrolls version #{AppScrolls::VERSION} ready to make magic."
+ end
+
desc "new APP_NAME", "create a new Rails app"
method_option :scrolls, :type => :array, :aliases => "-s", :desc => "List scrolls, e.g. -s resque rails_basics jquery"
method_option :template, :type => :boolean, :aliases => "-t", :desc => "Only display template that would be used"
+ method_option :config, :type => :string, :aliases => "-c", :desc => "Path to config script to run before scrolls"
def new(name)
+ config_file = options[:config] || ENV['APPSCROLLS_CONFIG']
if options[:scrolls]
- run_template(name, options[:scrolls], options[:template])
+ run_template(name, options[:scrolls], :display_only => options[:template], :config_file => config_file)
else
@scrolls = []
@@ -19,7 +28,7 @@ def new(name)
@scrolls.delete(scroll)
puts
puts "> #{yellow}Removed '#{scroll}' from template.#{clear}"
- elsif AppScrollsScrolls::Scrolls.list.include?(scroll)
+ elsif AppScrolls::Scrolls.list.include?(scroll)
@scrolls << scroll
puts
puts "> #{green}Added '#{scroll}' to template.#{clear}"
@@ -29,22 +38,36 @@ def new(name)
end
end
- run_template(name, @scrolls)
+ run_template(name, @scrolls, :display_only => options[:template], :config_file => config_file)
end
end
desc "list [CATEGORY]", "list available scrolls (optionally by category)"
def list(category = nil)
if category
- scrolls = AppScrollsScrolls::Scrolls.for(category).map{|r| AppScrollsScrolls::Scroll.from_mongo(r) }
+ scrolls = AppScrolls::Scrolls.for(category).map{|r| AppScrolls::Scroll.from_mongo(r) }
else
- scrolls = AppScrollsScrolls::Scrolls.list_classes
+ scrolls = AppScrolls::Scrolls.list_classes
end
scrolls.each do |scroll|
puts scroll.key.ljust(15) + "# #{scroll.description}"
end
end
+
+ desc "memorize NEW_SCROLL", "saves last git commit as a patch and creates scroll to apply it (alpha feature)"
+ def memorize(scroll_name)
+ scrolls_path = File.expand_path("../../../scrolls", __FILE__)
+ patch_file = scrolls_path + "/#{scroll_name}.diff"
+ scroll_file = scrolls_path + "/#{scroll_name}.rb"
+ @name = scroll_name
+ self.class.source_root File.expand_path("../../../templates", __FILE__)
+ self.class.attr_reader :name
+ template "diff_patch.tt", patch_file
+ template "memorized_scroll.tt", scroll_file
+ puts "Memorized #{scroll_name} into scroll diff patch #{scroll_name}.diff and scroll #{scroll_file}"
+ `open #{patch_file} #{scroll_file}`
+ end
no_tasks do
def cyan; "\033[36m" end
@@ -60,7 +83,7 @@ def scrolls_message
message << "#{green}#{bold}Your Scrolls:#{clear} #{@scrolls.join(", ")}"
message << "\n\n"
end
- available_scrolls = AppScrollsScrolls::Scrolls.list - @scrolls
+ available_scrolls = AppScrolls::Scrolls.list - @scrolls
if available_scrolls.any?
message << "#{bold}#{cyan}Available Scrolls:#{clear} #{available_scrolls.join(', ')}"
message << "\n\n"
@@ -68,13 +91,15 @@ def scrolls_message
message
end
- def run_template(name, scrolls, display_only = false)
+ def run_template(name, scrolls, options = {})
puts
puts
puts "#{bold}Generating and Running Template...#{clear}"
puts
file = Tempfile.new('template')
- template = AppScrollsScrolls::Template.new(scrolls)
+ template_options = {}
+ template_options[:config_script] = File.read(options[:config_file]) if options[:config_file]
+ template = AppScrolls::Template.new(scrolls, template_options)
puts "Using the following scrolls:"
template.resolve_scrolls.map do |scroll|
@@ -85,14 +110,14 @@ def run_template(name, scrolls, display_only = false)
file.write template.compile
file.close
- if display_only
+ if options[:display_only]
puts "Template stored to #{file.path}"
puts File.read(file.path)
else
system "rails new #{name} -m #{file.path} #{template.args.join(' ')}"
end
ensure
- file.unlink
+ file.unlink unless options[:display_only]
end
end
end
diff --git a/lib/appscrolls/config.rb b/lib/appscrolls/config.rb
index 880a6d2..ee73661 100644
--- a/lib/appscrolls/config.rb
+++ b/lib/appscrolls/config.rb
@@ -1,6 +1,6 @@
require 'active_support/ordered_hash'
-module AppScrollsScrolls
+module AppScrolls
class Config
attr_reader :questions
@@ -18,7 +18,7 @@ def initialize(schema)
def compile(values = {})
result = []
- result << "config = #{values.inspect}"
+ result << "config.merge! #{values.inspect}" unless values.empty?
@questions.each_pair do |key, question|
result << "config['#{key}'] = #{question.compile} unless config.key?('#{key}')"
end
@@ -33,7 +33,7 @@ def initialize(details)
end
def compile
- "#{question} if #{conditions}"
+ "#{question}#{conditions}"
end
def question
@@ -41,7 +41,8 @@ def question
end
def conditions
- [config_conditions, scroll_conditions].join(' && ')
+ conditions_string = [config_conditions, scroll_conditions].reject{|v| v=='true'}.join(' && ')
+ " if " + conditions_string unless conditions_string.empty?
end
def config_conditions
diff --git a/lib/appscrolls/scroll.rb b/lib/appscrolls/scroll.rb
index 97be258..42b0110 100644
--- a/lib/appscrolls/scroll.rb
+++ b/lib/appscrolls/scroll.rb
@@ -4,7 +4,7 @@
require 'yaml'
require 'erb'
-module AppScrollsScrolls
+module AppScrolls
class Scroll
extend Comparable
@@ -38,7 +38,7 @@ def self.generate(key, template_or_file, attributes = {})
template = template_or_file
end
- scroll_class = Class.new(AppScrollsScrolls::Scroll)
+ scroll_class = Class.new(AppScrolls::Scroll)
scroll_class.attributes = attributes
scroll_class.template = template
scroll_class.key = key
@@ -72,8 +72,7 @@ def self.attributes=(hash)
end
def self.config
- return nil unless attributes[:config]
- AppScrollsScrolls::Config.new(attributes[:config])
+ AppScrolls::Config.new(attributes[:config]||[])
end
def attributes
@@ -81,7 +80,7 @@ def attributes
end
def self.compile
- "# >#{"[ #{name} ]".center(75,'-')}<\n\n# #{description}\nsay_scroll '#{name}'\n\n#{template}\n"
+ "# >#{"[ #{name} ]".center(75,'-')}<\n\n# #{description}\nsay_scroll '#{name.gsub("'", "\\'")}'\n\n#{template}\n"
end
def compile; self.class.compile end
@@ -95,8 +94,8 @@ def self.to_mongo(value)
end
def self.from_mongo(key)
- return key if key.respond_to?(:superclass) && key.superclass == AppScrollsScrolls::Scroll
- AppScrollsScrolls::Scrolls[key]
+ return key if key.respond_to?(:superclass) && key.superclass == AppScrolls::Scroll
+ AppScrolls::Scrolls[key]
end
def self.get_binding
diff --git a/lib/appscrolls/scrolls.rb b/lib/appscrolls/scrolls.rb
index 7852c73..8ea90f1 100644
--- a/lib/appscrolls/scrolls.rb
+++ b/lib/appscrolls/scrolls.rb
@@ -1,10 +1,12 @@
-module AppScrollsScrolls
+module AppScrolls
module Scrolls
@@categories = {}
@@list = {}
def self.add(scroll)
- AppScrollsScrolls::Scrolls.const_set ActiveSupport::Inflector.camelize(scroll.key.gsub("-", "_")), scroll
+ sym = ActiveSupport::Inflector.camelize(scroll.key.gsub("-", "_"))
+ return if AppScrolls::Scrolls.const_defined?(sym)
+ AppScrolls::Scrolls.const_set ActiveSupport::Inflector.camelize(scroll.key.gsub("-", "_")), scroll
@@list[scroll.key] = scroll
(@@categories[scroll.category.to_s] ||= []) << scroll.key
@@categories[scroll.category.to_s].uniq!
diff --git a/lib/appscrolls/template.rb b/lib/appscrolls/template.rb
index f9d3862..7f4c86f 100644
--- a/lib/appscrolls/template.rb
+++ b/lib/appscrolls/template.rb
@@ -1,11 +1,12 @@
-module AppScrollsScrolls
+module AppScrolls
class Template
- attr_reader :scrolls, :unknown_scroll_names
+ attr_reader :scrolls, :unknown_scroll_names, :config_script
- def initialize(scrolls)
+ def initialize(scrolls, options={})
@unknown_scroll_names = []
+ @config_script = options[:config_script]
@scrolls = scrolls.inject([]) do |list, name|
- scroll = AppScrollsScrolls::Scroll.from_mongo(name)
+ scroll = AppScrolls::Scroll.from_mongo(name)
if scroll
list << scroll
else
@@ -28,11 +29,38 @@ def render(template_name, binding = nil); self.class.render(template_name, bindi
def resolve_scrolls
- @resolve_scrolls ||= scrolls_with_dependencies.sort.sort
+ return @resolve_scrolls if @resolve_scrolls
+ priority_map = {} # for each scroll (key), array of scrolls that must run earlier
+ scrolls_with_dependencies.each do |scroll|
+ (priority_map[scroll.key] ||= []).push(*scroll.run_after)
+ scroll.run_before.each do |scroll_to_run_later|
+ if scrolls_with_dependencies.find{|s| s.key == scroll_to_run_later} # if a scroll must run before another included scroll
+ (priority_map[scroll_to_run_later] ||= []).push scroll.key # add a dependency to that scroll
+ end
+ end
+ end
+ priority_map.each_value do |precursors| # remove scrolls we're not using from the dependencies
+ precursors.reject! do |key|
+ !scrolls_with_dependencies.find{|s| s.key == key}
+ end
+ end
+ @resolve_scrolls = []
+ while scroll_without_precursors = priority_map.find{|key, value| value.empty?} # pop a scroll without dependencies
+ key, empty_array = scroll_without_precursors
+ priority_map.delete(key)
+ scroll = scrolls_with_dependencies.find{|s| s.key == key}
+ raise key if !scroll
+ @resolve_scrolls << scroll # stick it into our result queue
+ priority_map.each_value{|precursors| precursors.delete(key)} # take it off other scroll's dependency lists
+ end
+ unless priority_map.empty?
+ raise "circular dependency with run_after/run_before clauses: #{priority_map.inspect}"
+ end
+ @resolve_scrolls
end
def scroll_classes
- @scroll_classes ||= scrolls.map { |name| AppScrollsScrolls::Scroll.from_mongo(name) }
+ @scroll_classes ||= scrolls.map { |name| AppScrolls::Scroll.from_mongo(name) }
end
def scrolls_with_dependencies
@@ -41,7 +69,7 @@ def scrolls_with_dependencies
added_more = false
for scroll in scroll_classes
scroll.requires.each do |requirement|
- requirement = AppScrollsScrolls::Scroll.from_mongo(requirement)
+ requirement = AppScrolls::Scroll.from_mongo(requirement)
count = @scrolls_with_dependencies.size
(@scrolls_with_dependencies << requirement).uniq!
unless @scrolls_with_dependencies.size == count
@@ -60,7 +88,7 @@ def compile
def args
scrolls.map(&:args).uniq
end
-
+
def custom_code?; false end
def custom_code; nil end
end
diff --git a/scrolls/active_admin-devise.diff b/scrolls/active_admin-devise.diff
new file mode 100644
index 0000000..427f30a
--- /dev/null
+++ b/scrolls/active_admin-devise.diff
@@ -0,0 +1,62 @@
+diff --git a/app/admin/admin_users.rb b/app/admin/admin_users.rb
+new file mode 100644
+index 0000000..c575e78
+--- /dev/null
++++ b/app/admin/admin_users.rb
+@@ -0,0 +1,18 @@
++ActiveAdmin.register AdminUser do
++ index do
++ column :email
++ column :current_sign_in_ip
++ column :current_sign_in_at
++ column :last_sign_in_at
++ column :sign_in_count
++ default_actions
++ end
++
++ form do |f|
++ f.inputs "Admin Details" do
++ f.input :email
++ f.input :password
++ end
++ f.buttons
++ end
++end
+diff --git a/app/admin/users.rb b/app/admin/users.rb
+new file mode 100644
+index 0000000..931975d
+--- /dev/null
++++ b/app/admin/users.rb
+@@ -0,0 +1,32 @@
++ActiveAdmin.register User do
++ index do
++ column :created_at
++ column :email
++ column :sign_in_count
++ default_actions
++ end
++
++ show do |user|
++ attributes_table do
++ row :email
++ row :sign_in_count
++ row :current_sign_in_at
++ row :last_sign_in_at
++ row :current_sign_in_ip
++ row :last_sign_in_ip
++ row :remember_created_at
++ row :reset_password_sent_at
++ row :updated_at
++ row :created_at
++ end
++ active_admin_comments
++ end
++
++ form do |f|
++ f.inputs "User Details" do
++ f.input :email
++ f.input :password
++ end
++ f.buttons
++ end
++end
diff --git a/scrolls/active_admin-omniauth.diff b/scrolls/active_admin-omniauth.diff
new file mode 100644
index 0000000..eadde6e
--- /dev/null
+++ b/scrolls/active_admin-omniauth.diff
@@ -0,0 +1,36 @@
+diff --git a/app/admin/users.rb b/app/admin/users.rb
+index 931975d..4e3135b 100644
+--- a/app/admin/users.rb
++++ b/app/admin/users.rb
+@@ -1,14 +1,22 @@
+ ActiveAdmin.register User do
+ index do
+ column :created_at
++ column :name
+ column :email
+ column :sign_in_count
++ column :auth do |user|
++ user.authentications.map(&:provider).join(' ')
++ end
+ default_actions
+ end
+
+- show do |user|
++ show :title => :name do |user|
+ attributes_table do
++ row :name
+ row :email
++ row :authentications do
++ user.authentications.map(&:provider).join(' ')
++ end
+ row :sign_in_count
+ row :current_sign_in_at
+ row :last_sign_in_at
+@@ -24,6 +32,7 @@ ActiveAdmin.register User do
+
+ form do |f|
+ f.inputs "User Details" do
++ f.input :name
+ f.input :email
+ f.input :password
+ end
diff --git a/scrolls/active_admin.rb b/scrolls/active_admin.rb
index 22335c6..72a312f 100644
--- a/scrolls/active_admin.rb
+++ b/scrolls/active_admin.rb
@@ -1,10 +1,14 @@
-#unless scrolls.include? 'sass-rails'
-# gem 'sass-rails'
-#end
gem 'activeadmin'
-after_everything do
+after_bundler do
generate "active_admin:install"
+ inject_into_file "config/application.rb", "\n config.assets.precompile += ['active_admin.js', 'active_admin.css']", :before => "\n end"
+ if scrolls.include? 'devise'
+ apply_patch :devise
+ if scrolls.include? 'omniauth'
+ apply_patch :omniauth
+ end
+ end
end
__END__
@@ -17,3 +21,4 @@
exclusive: administration
category: administration
tags: [administration]
+run_after: [devise, omniauth]
\ No newline at end of file
diff --git a/scrolls/compass.rb b/scrolls/compass.rb
new file mode 100644
index 0000000..effecc3
--- /dev/null
+++ b/scrolls/compass.rb
@@ -0,0 +1,11 @@
+gem 'compass-rails', :group => :assets
+
+__END__
+
+name: Compass
+description: "Use Compass Stylesheet Authoring Framework for Ruby on Rails."
+
+requires: [sass]
+
+category: assets
+tags: [css, stylesheet]
diff --git a/scrolls/compass_twitter_bootstrap.rb b/scrolls/compass_twitter_bootstrap.rb
new file mode 100644
index 0000000..c80c86b
--- /dev/null
+++ b/scrolls/compass_twitter_bootstrap.rb
@@ -0,0 +1,49 @@
+gem 'compass_twitter_bootstrap', :group => :assets
+
+after_everything do
+ create_file "config/compass.rb", <<-END
+# Require any additional compass plugins here.
+project_type = :rails
+END
+
+ append_file "app/assets/stylesheets/application.css.scss", <<-END
+@import "bootstrap_and_overrides";
+END
+
+ create_file "app/assets/stylesheets/_bootstrap_variables.css.scss", <<-END
+// Variables to customize the look and feel of Bootstrap
+// Override any variables from Bootstrap in this file
+END
+
+ create_file "app/assets/stylesheets/_bootstrap_and_overrides.css.scss", <<-END
+@import "bootstrap_variables";
+@import "compass_twitter_bootstrap_awesome";
+@import "#{config["layout"]}"
+END
+
+ gsub_file "app/assets/javascripts/application.js", "//= require_tree .", <<-END
+//= require bootstrap-all
+//= require_tree .
+END
+
+ run "ln -s `bundle show compass_twitter_bootstrap`/stylesheets app/assets/stylesheets/bootstrap"
+ append_file ".gitignore", "\napp/assets/stylesheets/bootstrap" if scrolls.include? 'git'
+end
+
+__END__
+
+name: Compass Twitter Bootstrap Rails (Sass)
+description: Add Twitter Bootstrap CSS in Sass via Compass
+
+category: assets
+exclusive: stylesheet
+tags: [css, stylesheet]
+
+requires: [compass]
+
+config:
+ - layout:
+ prompt: "Responsive layout?"
+ type: multiple_choice
+ choices: [["Normal", "compass_twitter_bootstrap"], ["Responsive", "compass_twitter_bootstrap_responsive"]]
+
diff --git a/scrolls/delayed_job.rb b/scrolls/delayed_job.rb
index 66dffb8..e7a0b46 100644
--- a/scrolls/delayed_job.rb
+++ b/scrolls/delayed_job.rb
@@ -51,8 +51,12 @@ def delayed_job_admin_authentication
end
-after_bundler do
- generate 'delayed_job'
+after_everything do
+ if scroll?("sqlite3") || scroll?("postgresql") || scroll?("mysql")
+ generate 'delayed_job:active_record'
+ else
+ generate 'delayed_job'
+ end
if scroll? "eycloud_recipes_on_deploy"
say_wizard 'Installing deploy hooks to restart delayed_job after deploys'
diff --git a/scrolls/devise.rb b/scrolls/devise.rb
new file mode 100644
index 0000000..74d6830
--- /dev/null
+++ b/scrolls/devise.rb
@@ -0,0 +1,39 @@
+gem 'devise'
+
+inject_into_file 'config/environments/development.rb', "\n config.action_mailer.default_url_options = { :host => 'localhost', :port => 3000 }\n", :after => "Application.configure do"
+inject_into_file 'config/environments/test.rb', "\n config.action_mailer.default_url_options = { :host => 'localhost', :port => 3000 }\n", :after => "Application.configure do"
+inject_into_file 'config/environments/production.rb', "\n config.action_mailer.default_url_options = { :host => '#{app_name}.com' }\n", :after => "Application.configure do"
+
+if scrolls.include? 'heroku'
+ inject_into_file 'config/application.rb', "\n # Force application to not access DB or load models when precompiling your assets (Devise+heroku recommended)\n config.assets.initialize_on_precompile = false\n", :after => "class Application < Rails::Application"
+end
+
+unless scrolls.include? 'rails_basics'
+ route "root :to => 'home#index'"
+end
+
+after_bundler do
+ generate 'devise:install'
+
+ if scrolls.include? 'mongo_mapper'
+ gem 'mm-devise'
+ gsub_file 'config/initializers/devise.rb', 'devise/orm/', 'devise/orm/mongo_mapper_active_model'
+ generate 'mongo_mapper:devise User'
+ elsif scrolls.include? 'mongoid'
+ gsub_file 'config/initializers/devise.rb', 'devise/orm/active_record', 'devise/orm/mongoid'
+ end
+
+ generate 'devise user'
+ generate 'devise:views'
+
+ gsub_file "config/initializers/devise.rb", "please-change-me-at-config-initializers-devise@example.com", "help@#{app_name}.com"
+end
+
+__END__
+
+name: Devise
+description: Utilize Devise for authentication, automatically configured for your selected ORM.
+author: mbleigh
+
+category: authentication
+exclusive: authentication
\ No newline at end of file
diff --git a/scrolls/devise_haml-simple_form.diff b/scrolls/devise_haml-simple_form.diff
new file mode 100644
index 0000000..1736834
--- /dev/null
+++ b/scrolls/devise_haml-simple_form.diff
@@ -0,0 +1,439 @@
+diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb
+deleted file mode 100644
+index c488db7..0000000
+--- a/app/views/devise/confirmations/new.html.erb
++++ /dev/null
+@@ -1,15 +0,0 @@
+-
Resend confirmation instructions
+-
+-<%= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
+- <%= f.error_notification %>
+-
+-
+- <%= f.input :email, :required => true %>
+-
+-
+-
+- <%= f.button :submit, "Resend confirmation instructions" %>
+-
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
+new file mode 100644
+index 0000000..c5b31f5
+--- /dev/null
++++ b/app/views/devise/confirmations/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Resend confirmation instructions
++
++= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true
++
++ .form-actions
++ = f.button :submit, "Resend confirmation instructions"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb
+deleted file mode 100644
+index a5c4585..0000000
+--- a/app/views/devise/mailer/confirmation_instructions.html.erb
++++ /dev/null
+@@ -1,5 +0,0 @@
+-Welcome <%= @resource.email %>!
+-
+-You can confirm your account email through the link below:
+-
+-<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
+diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml
+new file mode 100644
+index 0000000..f66d06a
+--- /dev/null
++++ b/app/views/devise/mailer/confirmation_instructions.html.haml
+@@ -0,0 +1,5 @@
++%p Welcome #{@resource.email}!
++
++%p You can confirm your account email through the link below:
++
++%p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token)
+diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb
+deleted file mode 100644
+index ae9e888..0000000
+--- a/app/views/devise/mailer/reset_password_instructions.html.erb
++++ /dev/null
+@@ -1,8 +0,0 @@
+-Hello <%= @resource.email %>!
+-
+-Someone has requested a link to change your password, and you can do this through the link below.
+-
+-<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
+-
+-If you didn't request this, please ignore this email.
+-Your password won't change until you access the link above and create a new one.
+diff --git a/app/views/devise/mailer/reset_password_instructions.html.haml b/app/views/devise/mailer/reset_password_instructions.html.haml
+new file mode 100644
+index 0000000..73beb64
+--- /dev/null
++++ b/app/views/devise/mailer/reset_password_instructions.html.haml
+@@ -0,0 +1,8 @@
++%p Hello #{@resource.email}!
++
++%p Someone has requested a link to change your password, and you can do this through the link below.
++
++%p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token)
++
++%p If you didn't request this, please ignore this email.
++%p Your password won't change until you access the link above and create a new one.
+diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb
+deleted file mode 100644
+index 2263c21..0000000
+--- a/app/views/devise/mailer/unlock_instructions.html.erb
++++ /dev/null
+@@ -1,7 +0,0 @@
+-Hello <%= @resource.email %>!
+-
+-Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
+-
+-Click the link below to unlock your account:
+-
+-<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
+diff --git a/app/views/devise/mailer/unlock_instructions.html.haml b/app/views/devise/mailer/unlock_instructions.html.haml
+new file mode 100644
+index 0000000..2719e0e
+--- /dev/null
++++ b/app/views/devise/mailer/unlock_instructions.html.haml
+@@ -0,0 +1,7 @@
++%p Hello #{@resource.email}!
++
++%p Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
++
++%p Click the link below to unlock your account:
++
++%p= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token)
+diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb
+deleted file mode 100644
+index 149520c..0000000
+--- a/app/views/devise/passwords/edit.html.erb
++++ /dev/null
+@@ -1,19 +0,0 @@
+-Change your password
+-
+-<%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %>
+- <%= f.error_notification %>
+-
+- <%= f.input :reset_password_token, :as => :hidden %>
+- <%= f.full_error :reset_password_token %>
+-
+-
+- <%= f.input :password, :label => "New password", :required => true %>
+- <%= f.input :password_confirmation, :label => "Confirm your new password", :required => true %>
+-
+-
+-
+- <%= f.button :submit, "Change my password" %>
+-
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
+new file mode 100644
+index 0000000..156f8f4
+--- /dev/null
++++ b/app/views/devise/passwords/edit.html.haml
+@@ -0,0 +1,16 @@
++%h2 Change your password
++
++= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f|
++ = f.error_notification
++
++ = f.input :reset_password_token, :as => :hidden
++ = f.full_error :reset_password_token
++
++ .form-inputs
++ = f.input :password, :label => "New password", :required => true
++ = f.input :password_confirmation, :label => "Confirm your new password", :required => true
++
++ .form-actions
++ = f.button :submit, "Change my password"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb
+deleted file mode 100644
+index de33627..0000000
+--- a/app/views/devise/passwords/new.html.erb
++++ /dev/null
+@@ -1,15 +0,0 @@
+-Forgot your password?
+-
+-<%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
+- <%= f.error_notification %>
+-
+-
+- <%= f.input :email, :required => true %>
+-
+-
+-
+- <%= f.button :submit, "Send me reset password instructions" %>
+-
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml
+new file mode 100644
+index 0000000..dcefbf1
+--- /dev/null
++++ b/app/views/devise/passwords/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Forgot your password?
++
++= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true
++
++ .form-actions
++ = f.button :submit, "Send me reset password instructions"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
+deleted file mode 100644
+index 6a9a4b3..0000000
+--- a/app/views/devise/registrations/edit.html.erb
++++ /dev/null
+@@ -1,22 +0,0 @@
+-Edit <%= resource_name.to_s.humanize %>
+-
+-<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
+- <%= f.error_notification %>
+-
+-
+- <%= f.input :email, :required => true, :autofocus => true %>
+- <%= f.input :password, :autocomplete => "off", :hint => "leave it blank if you don't want to change it", :required => false %>
+- <%= f.input :password_confirmation, :required => false %>
+- <%= f.input :current_password, :hint => "we need your current password to confirm your changes", :required => true %>
+-
+-
+-
+- <%= f.button :submit, "Update" %>
+-
+-<% end %>
+-
+-Cancel my account
+-
+-Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.
+-
+-<%= link_to "Back", :back %>
+diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml
+new file mode 100644
+index 0000000..4c5e660
+--- /dev/null
++++ b/app/views/devise/registrations/edit.html.haml
+@@ -0,0 +1,21 @@
++%h2 Edit #{resource_name.to_s.humanize}
++
++= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true, :autofocus => true
++ = f.input :password, :autocomplete => "off", :hint => "leave it blank if you don't want to change it", :required => false
++ = f.input :password_confirmation, :required => false
++ = f.input :current_password, :hint => "we need your current password to confirm your changes", :required => true
++
++ .form-actions
++ = f.button :submit, "Update"
++
++%h3 Cancel my account
++
++%p
++ Unhappy?
++ = link_to "Cancel my account.", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete
++
++= link_to "Back", :back
+diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
+deleted file mode 100644
+index 2665b08..0000000
+--- a/app/views/devise/registrations/new.html.erb
++++ /dev/null
+@@ -1,17 +0,0 @@
+-Sign up
+-
+-<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
+- <%= f.error_notification %>
+-
+-
+- <%= f.input :email, :required => true, :autofocus => true %>
+- <%= f.input :password, :required => true %>
+- <%= f.input :password_confirmation, :required => true %>
+-
+-
+-
+- <%= f.button :submit, "Sign up" %>
+-
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
+new file mode 100644
+index 0000000..f8eb7f0
+--- /dev/null
++++ b/app/views/devise/registrations/new.html.haml
+@@ -0,0 +1,14 @@
++%h2 Sign up
++
++= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true, :autofocus => true
++ = f.input :password, :required => true
++ = f.input :password_confirmation, :required => true
++
++ .form-actions
++ = f.button :submit, "Sign up"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
+deleted file mode 100644
+index fa53ea8..0000000
+--- a/app/views/devise/sessions/new.html.erb
++++ /dev/null
+@@ -1,15 +0,0 @@
+-Sign in
+-
+-<%= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
+-
+- <%= f.input :email, :required => false, :autofocus => true %>
+- <%= f.input :password, :required => false %>
+- <%= f.input :remember_me, :as => :boolean if devise_mapping.rememberable? %>
+-
+-
+-
+- <%= f.button :submit, "Sign in" %>
+-
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
+new file mode 100644
+index 0000000..48eb148
+--- /dev/null
++++ b/app/views/devise/sessions/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Sign in
++
++= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
++ .form-inputs
++ = f.input :email, :required => false, :autofocus => true
++ = f.input :password, :required => false
++ = f.input :remember_me, :as => :boolean if devise_mapping.rememberable?
++
++ .form-actions
++ = f.button :submit, "Sign in"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/shared/_links.erb b/app/views/devise/shared/_links.erb
+deleted file mode 100644
+index eab783a..0000000
+--- a/app/views/devise/shared/_links.erb
++++ /dev/null
+@@ -1,25 +0,0 @@
+-<%- if controller_name != 'sessions' %>
+- <%= link_to "Sign in", new_session_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
+- <%= link_to "Sign up", new_registration_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
+- <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
+- <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
+- <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.omniauthable? %>
+- <%- resource_class.omniauth_providers.each do |provider| %>
+- <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
+- <% end -%>
+-<% end -%>
+\ No newline at end of file
+diff --git a/app/views/devise/shared/_links.haml b/app/views/devise/shared/_links.haml
+new file mode 100644
+index 0000000..4e2ca58
+--- /dev/null
++++ b/app/views/devise/shared/_links.haml
+@@ -0,0 +1,24 @@
++- if controller_name != 'sessions'
++ = link_to "Sign in", new_session_path(resource_name)
++ %br
++
++- if devise_mapping.registerable? && controller_name != 'registrations'
++ = link_to "Sign up", new_registration_path(resource_name)
++ %br
++
++- if devise_mapping.recoverable? && controller_name != 'passwords'
++ = link_to "Forgot your password?", new_password_path(resource_name)
++ %br
++
++- if devise_mapping.confirmable? && controller_name != 'confirmations'
++ = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
++ %br
++
++- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
++ = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name)
++ %br
++
++- if devise_mapping.omniauthable?
++ - resource_class.omniauth_providers.each do |provider|
++ = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider)
++ %br
+diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb
+deleted file mode 100644
+index 9cce4c3..0000000
+--- a/app/views/devise/unlocks/new.html.erb
++++ /dev/null
+@@ -1,15 +0,0 @@
+-Resend unlock instructions
+-
+-<%= simple_form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %>
+- <%= f.error_notification %>
+-
+-
+- <%= f.input :email, :required => true %>
+-
+-
+-
+- <%= f.button :submit, "Resend unlock instructions" %>
+-
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml
+new file mode 100644
+index 0000000..9da9d6f
+--- /dev/null
++++ b/app/views/devise/unlocks/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Resend unlock instructions
++
++= simple_form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true
++
++ .form-actions
++ = f.button :submit, "Resend unlock instructions"
++
++= render "devise/shared/links"
diff --git a/scrolls/devise_haml.diff b/scrolls/devise_haml.diff
new file mode 100644
index 0000000..8e7ac8c
--- /dev/null
+++ b/scrolls/devise_haml.diff
@@ -0,0 +1,433 @@
+diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb
+deleted file mode 100644
+index 81e4472..0000000
+--- a/app/views/devise/confirmations/new.html.erb
++++ /dev/null
+@@ -1,12 +0,0 @@
+-Resend confirmation instructions
+-
+-<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
+- <%= devise_error_messages! %>
+-
+- <%= f.label :email %>
+- <%= f.email_field :email %>
+-
+- <%= f.submit "Resend confirmation instructions" %>
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
+new file mode 100644
+index 0000000..c5b31f5
+--- /dev/null
++++ b/app/views/devise/confirmations/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Resend confirmation instructions
++
++= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true
++
++ .form-actions
++ = f.button :submit, "Resend confirmation instructions"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb
+deleted file mode 100644
+index a5c4585..0000000
+--- a/app/views/devise/mailer/confirmation_instructions.html.erb
++++ /dev/null
+@@ -1,5 +0,0 @@
+-Welcome <%= @resource.email %>!
+-
+-You can confirm your account email through the link below:
+-
+-<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
+diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml
+new file mode 100644
+index 0000000..f66d06a
+--- /dev/null
++++ b/app/views/devise/mailer/confirmation_instructions.html.haml
+@@ -0,0 +1,5 @@
++%p Welcome #{@resource.email}!
++
++%p You can confirm your account email through the link below:
++
++%p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token)
+diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb
+deleted file mode 100644
+index ae9e888..0000000
+--- a/app/views/devise/mailer/reset_password_instructions.html.erb
++++ /dev/null
+@@ -1,8 +0,0 @@
+-Hello <%= @resource.email %>!
+-
+-Someone has requested a link to change your password, and you can do this through the link below.
+-
+-<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
+-
+-If you didn't request this, please ignore this email.
+-Your password won't change until you access the link above and create a new one.
+diff --git a/app/views/devise/mailer/reset_password_instructions.html.haml b/app/views/devise/mailer/reset_password_instructions.html.haml
+new file mode 100644
+index 0000000..73beb64
+--- /dev/null
++++ b/app/views/devise/mailer/reset_password_instructions.html.haml
+@@ -0,0 +1,8 @@
++%p Hello #{@resource.email}!
++
++%p Someone has requested a link to change your password, and you can do this through the link below.
++
++%p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token)
++
++%p If you didn't request this, please ignore this email.
++%p Your password won't change until you access the link above and create a new one.
+diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb
+deleted file mode 100644
+index 2263c21..0000000
+--- a/app/views/devise/mailer/unlock_instructions.html.erb
++++ /dev/null
+@@ -1,7 +0,0 @@
+-Hello <%= @resource.email %>!
+-
+-Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
+-
+-Click the link below to unlock your account:
+-
+-<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
+diff --git a/app/views/devise/mailer/unlock_instructions.html.haml b/app/views/devise/mailer/unlock_instructions.html.haml
+new file mode 100644
+index 0000000..2719e0e
+--- /dev/null
++++ b/app/views/devise/mailer/unlock_instructions.html.haml
+@@ -0,0 +1,7 @@
++%p Hello #{@resource.email}!
++
++%p Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
++
++%p Click the link below to unlock your account:
++
++%p= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token)
+diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb
+deleted file mode 100644
+index fe620ef..0000000
+--- a/app/views/devise/passwords/edit.html.erb
++++ /dev/null
+@@ -1,16 +0,0 @@
+-Change your password
+-
+-<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %>
+- <%= devise_error_messages! %>
+- <%= f.hidden_field :reset_password_token %>
+-
+- <%= f.label :password, "New password" %>
+- <%= f.password_field :password %>
+-
+- <%= f.label :password_confirmation, "Confirm new password" %>
+- <%= f.password_field :password_confirmation %>
+-
+- <%= f.submit "Change my password" %>
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
+new file mode 100644
+index 0000000..156f8f4
+--- /dev/null
++++ b/app/views/devise/passwords/edit.html.haml
+@@ -0,0 +1,16 @@
++%h2 Change your password
++
++= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f|
++ = f.error_notification
++
++ = f.input :reset_password_token, :as => :hidden
++ = f.full_error :reset_password_token
++
++ .form-inputs
++ = f.input :password, :label => "New password", :required => true
++ = f.input :password_confirmation, :label => "Confirm your new password", :required => true
++
++ .form-actions
++ = f.button :submit, "Change my password"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb
+deleted file mode 100644
+index 2350164..0000000
+--- a/app/views/devise/passwords/new.html.erb
++++ /dev/null
+@@ -1,12 +0,0 @@
+-Forgot your password?
+-
+-<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
+- <%= devise_error_messages! %>
+-
+- <%= f.label :email %>
+- <%= f.email_field :email %>
+-
+- <%= f.submit "Send me reset password instructions" %>
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml
+new file mode 100644
+index 0000000..dcefbf1
+--- /dev/null
++++ b/app/views/devise/passwords/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Forgot your password?
++
++= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true
++
++ .form-actions
++ = f.button :submit, "Send me reset password instructions"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
+deleted file mode 100644
+index ebca9ed..0000000
+--- a/app/views/devise/registrations/edit.html.erb
++++ /dev/null
+@@ -1,25 +0,0 @@
+-Edit <%= resource_name.to_s.humanize %>
+-
+-<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
+- <%= devise_error_messages! %>
+-
+- <%= f.label :email %>
+- <%= f.email_field :email %>
+-
+- <%= f.label :password %> (leave blank if you don't want to change it)
+- <%= f.password_field :password, :autocomplete => "off" %>
+-
+- <%= f.label :password_confirmation %>
+- <%= f.password_field :password_confirmation %>
+-
+- <%= f.label :current_password %> (we need your current password to confirm your changes)
+- <%= f.password_field :current_password %>
+-
+- <%= f.submit "Update" %>
+-<% end %>
+-
+-Cancel my account
+-
+-Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.
+-
+-<%= link_to "Back", :back %>
+diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml
+new file mode 100644
+index 0000000..4c5e660
+--- /dev/null
++++ b/app/views/devise/registrations/edit.html.haml
+@@ -0,0 +1,21 @@
++%h2 Edit #{resource_name.to_s.humanize}
++
++= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true, :autofocus => true
++ = f.input :password, :autocomplete => "off", :hint => "leave it blank if you don't want to change it", :required => false
++ = f.input :password_confirmation, :required => false
++ = f.input :current_password, :hint => "we need your current password to confirm your changes", :required => true
++
++ .form-actions
++ = f.button :submit, "Update"
++
++%h3 Cancel my account
++
++%p
++ Unhappy?
++ = link_to "Cancel my account.", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete
++
++= link_to "Back", :back
+diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
+deleted file mode 100644
+index 9703db3..0000000
+--- a/app/views/devise/registrations/new.html.erb
++++ /dev/null
+@@ -1,18 +0,0 @@
+-Sign up
+-
+-<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
+- <%= devise_error_messages! %>
+-
+- <%= f.label :email %>
+- <%= f.email_field :email %>
+-
+- <%= f.label :password %>
+- <%= f.password_field :password %>
+-
+- <%= f.label :password_confirmation %>
+- <%= f.password_field :password_confirmation %>
+-
+- <%= f.submit "Sign up" %>
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
+new file mode 100644
+index 0000000..f8eb7f0
+--- /dev/null
++++ b/app/views/devise/registrations/new.html.haml
+@@ -0,0 +1,14 @@
++%h2 Sign up
++
++= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true, :autofocus => true
++ = f.input :password, :required => true
++ = f.input :password_confirmation, :required => true
++
++ .form-actions
++ = f.button :submit, "Sign up"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
+deleted file mode 100644
+index 7966ab9..0000000
+--- a/app/views/devise/sessions/new.html.erb
++++ /dev/null
+@@ -1,17 +0,0 @@
+-Sign in
+-
+-<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
+- <%= f.label :email %>
+- <%= f.email_field :email %>
+-
+- <%= f.label :password %>
+- <%= f.password_field :password %>
+-
+- <% if devise_mapping.rememberable? -%>
+- <%= f.check_box :remember_me %> <%= f.label :remember_me %>
+- <% end -%>
+-
+- <%= f.submit "Sign in" %>
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
+new file mode 100644
+index 0000000..48eb148
+--- /dev/null
++++ b/app/views/devise/sessions/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Sign in
++
++= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
++ .form-inputs
++ = f.input :email, :required => false, :autofocus => true
++ = f.input :password, :required => false
++ = f.input :remember_me, :as => :boolean if devise_mapping.rememberable?
++
++ .form-actions
++ = f.button :submit, "Sign in"
++
++= render "devise/shared/links"
+diff --git a/app/views/devise/shared/_links.erb b/app/views/devise/shared/_links.erb
+deleted file mode 100644
+index eab783a..0000000
+--- a/app/views/devise/shared/_links.erb
++++ /dev/null
+@@ -1,25 +0,0 @@
+-<%- if controller_name != 'sessions' %>
+- <%= link_to "Sign in", new_session_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
+- <%= link_to "Sign up", new_registration_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
+- <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
+- <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
+- <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+-<% end -%>
+-
+-<%- if devise_mapping.omniauthable? %>
+- <%- resource_class.omniauth_providers.each do |provider| %>
+- <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
+- <% end -%>
+-<% end -%>
+\ No newline at end of file
+diff --git a/app/views/devise/shared/_links.haml b/app/views/devise/shared/_links.haml
+new file mode 100644
+index 0000000..4e2ca58
+--- /dev/null
++++ b/app/views/devise/shared/_links.haml
+@@ -0,0 +1,24 @@
++- if controller_name != 'sessions'
++ = link_to "Sign in", new_session_path(resource_name)
++ %br
++
++- if devise_mapping.registerable? && controller_name != 'registrations'
++ = link_to "Sign up", new_registration_path(resource_name)
++ %br
++
++- if devise_mapping.recoverable? && controller_name != 'passwords'
++ = link_to "Forgot your password?", new_password_path(resource_name)
++ %br
++
++- if devise_mapping.confirmable? && controller_name != 'confirmations'
++ = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
++ %br
++
++- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
++ = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name)
++ %br
++
++- if devise_mapping.omniauthable?
++ - resource_class.omniauth_providers.each do |provider|
++ = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider)
++ %br
+diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb
+deleted file mode 100644
+index e55e82e..0000000
+--- a/app/views/devise/unlocks/new.html.erb
++++ /dev/null
+@@ -1,12 +0,0 @@
+-Resend unlock instructions
+-
+-<%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %>
+- <%= devise_error_messages! %>
+-
+- <%= f.label :email %>
+- <%= f.email_field :email %>
+-
+- <%= f.submit "Resend unlock instructions" %>
+-<% end %>
+-
+-<%= render "devise/shared/links" %>
+diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml
+new file mode 100644
+index 0000000..9da9d6f
+--- /dev/null
++++ b/app/views/devise/unlocks/new.html.haml
+@@ -0,0 +1,12 @@
++%h2 Resend unlock instructions
++
++= simple_form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f|
++ = f.error_notification
++
++ .form-inputs
++ = f.input :email, :required => true
++
++ .form-actions
++ = f.button :submit, "Resend unlock instructions"
++
++= render "devise/shared/links"
diff --git a/scrolls/devise_haml.rb b/scrolls/devise_haml.rb
new file mode 100644
index 0000000..75d0d49
--- /dev/null
+++ b/scrolls/devise_haml.rb
@@ -0,0 +1,16 @@
+after_everything do
+
+ if scrolls.include?('simple_form')
+ apply_patch :simple_form
+ else
+ apply_patch
+ end
+end
+
+__END__
+name: Devise HAML
+description: "Replace Devise views with HAML"
+author: allangrant
+category: templating
+requires: [devise, git, haml]
+run_after: [devise]
diff --git a/scrolls/exception_notification.rb b/scrolls/exception_notification.rb
new file mode 100644
index 0000000..ce07ed7
--- /dev/null
+++ b/scrolls/exception_notification.rb
@@ -0,0 +1,19 @@
+gem 'exception_notification', :group => 'production'
+
+initializer 'exception_notification.rb', <<-END
+if defined? ExceptionNotifier
+ Rails.application.config.middleware.use ExceptionNotifier,
+ :email_prefix => "[#{app_name}] ",
+ :sender_address => %{"notifier" },
+ :exception_recipients => %w{#{default_email}}
+end
+END
+
+__END__
+
+name: Exception Notification
+description: Exception Notifier Plugin for Rails
+
+category: exception_notification
+exclusive: exception_notification
+tags: [exception_notification]
diff --git a/scrolls/git.rb b/scrolls/git.rb
index 68b9459..aac87e7 100644
--- a/scrolls/git.rb
+++ b/scrolls/git.rb
@@ -13,5 +13,5 @@
exclusive: scm
category: deployment
-run_before: [git, eycloud, heroku]
+run_before: [eycloud, heroku]
diff --git a/scrolls/guard.rb b/scrolls/guard.rb
index d0c025b..72858c2 100644
--- a/scrolls/guard.rb
+++ b/scrolls/guard.rb
@@ -1,28 +1,19 @@
-prepend_file 'Gemfile' do <<-RUBY
require 'rbconfig'
HOST_OS = RbConfig::CONFIG['host_os']
-RUBY
-end
-
-append_file 'Gemfile' do <<-RUBY
-
-guard_notifications = #{config['guard_notifications'].inspect}
-group :development do
- case HOST_OS
- when /darwin/i
- gem 'rb-fsevent'
- gem 'ruby_gntp' if guard_notifications
- when /linux/i
- gem 'libnotify'
- gem 'rb-inotify'
- when /mswin|windows/i
- gem 'rb-fchange'
- gem 'win32console'
- gem 'rb-notifu' if guard_notifications
- end
-end
-RUBY
+append_file 'Gemfile', "\nguard_notifications = #{config['guard_notifications'].inspect}\n"
+
+case HOST_OS
+when /darwin/i
+ gem 'rb-fsevent', :group => :development
+ append_file 'Gemfile', "\ngem 'ruby_gntp', :group => :development if guard_notifications\n"
+when /linux/i
+ gem 'libnotify', :group => :development
+ gem 'rb-inotify', :group => :development
+when /mswin|windows/i
+ gem 'rb-fchange', :group => :development
+ gem 'win32console', :group => :development
+ append_file 'Gemfile', "\ngem 'rb-notifu' if guard_notifications\n"
end
diff --git a/scrolls/haml.rb b/scrolls/haml.rb
new file mode 100644
index 0000000..bece9c7
--- /dev/null
+++ b/scrolls/haml.rb
@@ -0,0 +1,30 @@
+gem 'haml-rails'
+
+after_everything do
+ create_file 'app/views/layouts/application.html.haml', <<-END
+!!! 5
+%html{html_attrs}
+ %head
+ %meta{:charset => 'utf-8'}
+ %title #{app_name}
+ = csrf_meta_tag
+ = stylesheet_link_tag "application", :media => "all"
+ = javascript_include_tag "application"
+ %body
+ .container
+ - flash.each do |name, msg|
+ .alert{:class => "alert-\#{name == :alert ? "error" : "success"}"}
+ %a.close{:"data-dismiss" => "alert"} ×
+ != msg
+ = yield
+END
+ run 'rm app/views/layouts/application.html.erb'
+end
+
+__END__
+
+name: HAML
+description: "Utilize HAML for templating."
+
+category: templating
+exclusive: templating
diff --git a/scrolls/untested/heroku.rb b/scrolls/heroku.rb
similarity index 81%
rename from scrolls/untested/heroku.rb
rename to scrolls/heroku.rb
index b87e641..ad17710 100644
--- a/scrolls/untested/heroku.rb
+++ b/scrolls/heroku.rb
@@ -1,13 +1,18 @@
heroku_name = app_name.gsub('_','')
+inject_into_file 'Gemfile', "\nruby '1.9.3'\n", :after => "source 'https://rubygems.org'"
+
after_everything do
if config['create']
- say_wizard "Creating Heroku app '#{heroku_name}.heroku.com'"
+ say_wizard "Creating Heroku app '#{heroku_name}.heroku.com'"
while !system("heroku create #{heroku_name}")
heroku_name = ask_wizard("What do you want to call your app? ")
end
+ else
+ git :remote => "add heroku git@heroku.com:#{heroku_name}.git"
end
+
if config['staging']
staging_name = "#{heroku_name}-staging"
say_wizard "Creating staging Heroku app '#{staging_name}.heroku.com'"
@@ -24,8 +29,13 @@
run "heroku addons:add custom_domains"
run "heroku domains:add #{config['domain']}"
end
+end
- git :push => "#{config['staging'] ? 'staging' : 'heroku'} master" if config['deploy']
+if config['deploy']
+ finally do
+ git :push => "#{config['staging'] ? 'staging' : 'heroku'} master"
+ run "heroku run rake db:migrate"
+ end
end
__END__
@@ -33,12 +43,11 @@
name: Heroku
description: Create Heroku application and instantly deploy.
author: mbleigh
-
-requires: [git]
run_after: [git]
exclusive: deployment
category: deployment
tags: [provider]
+requires: [git, postgresql]
config:
- create:
diff --git a/scrolls/mailgun.rb b/scrolls/mailgun.rb
new file mode 100644
index 0000000..5e16472
--- /dev/null
+++ b/scrolls/mailgun.rb
@@ -0,0 +1,56 @@
+after_bundler do
+ create_file 'config/mailer.yml', <<-END
+:address: 'smtp.mailgun.org'
+:port: 587
+:authentication: :plain
+:user_name: ''
+:password: ''
+:domain: ''
+END
+
+ append_file ".gitignore", "\nconfig/mailer.yml"
+
+ inject_into_file 'config/application.rb', :after => "class Application < Rails::Application" do
+<<-END
+
+ config.action_mailer.smtp_settings = YAML.load(File.open("\#{Rails.root}/config/mailer.yml")) unless Rails.env.production?
+ config.action_mailer.default_url_options = { :host => 'localhost', :port => 3000 }
+ routes.default_url_options = { :host => 'localhost', :port => 3000 }
+END
+ end
+
+ inject_into_file 'config/environments/production.rb', :after => "Application.configure do" do
+<<-END
+
+ routes.default_url_options = { :host => '#{app_name}.com' }
+ config.action_mailer.smtp_settings = {
+ :authentication => :plain,
+ :address => "smtp.mailgun.org",
+ :port => 587,
+ :domain => ENV["MAILGUN_DOMAIN"],
+ :user_name => ENV["MAILGUN_USERNAME"],
+ :password => ENV["MAILGUN_PASSWORD"]}
+END
+ end
+
+ generate "mailer notifier"
+
+ inject_into_file 'app/mailers/notifier.rb', :before => "\nend" do
+<<-END
+
+ def simple(params)
+ mail params
+ end
+
+ # sends a test email
+ def self.test!
+ simple(:to => '#{default_email}', :from => 'test@#{app_name}.com', :subject => 'Email delivery works', :body => 'Much success!').deliver
+ end
+END
+ end
+end
+
+__END__
+name: MailGun
+description: Sets up everything needed for shooting email with MailGun
+category: other
\ No newline at end of file
diff --git a/scrolls/omniauth.rb b/scrolls/omniauth.rb
new file mode 100644
index 0000000..2d4f975
--- /dev/null
+++ b/scrolls/omniauth.rb
@@ -0,0 +1,176 @@
+if config.key?("providers")
+ providers = config["providers"].split
+else
+ providers = []
+ while providers.size == 0
+ providers = ask_wizard("List desired omniauth strategies, space delimited (e.g. 'github facebook'; ENTER to see all):").split(' ')
+
+ if providers.size == 0
+ say_custom "omniauth", "Fetching list of all strategies"
+ strategy_gems ||= `gem list --remote omniauth-`.split("\n").map{|strategy_gem| strategy_gem.match(/omniauth-(\w*)/)[1].ljust(20)}.uniq
+ output_rows ||= (strategy_gems.length + (4 - (strategy_gems.length % 4)))/4 - 1 # 20 char per line
+ (0..output_rows).each {|i| puts strategy_gems[i*4..(i+1)*4].join ' '}
+ end
+ end
+end
+
+providers.each do |provider|
+ config["#{provider}_key"] = ask_wizard("#{provider.capitalize} key:") unless config.key?("#{provider}_key")
+ config["#{provider}_secret"] = ask_wizard("#{provider.capitalize} secret:") unless config.key?("#{provider}_secret")
+
+ case provider
+ when 'angellist'
+ gem "omniauth-angellist", :git => 'git://github.com/railsjedi/omniauth-angellist'
+ else
+ gem "omniauth-#{provider}"
+ end
+end
+
+after_bundler do
+ generate 'model authentications user_id:integer provider uid data:text'
+
+ authentications_migration = Dir[destination_root + '/db/migrate/*.rb'].find { |file| file=~/create_authentications/ }
+ gsub_file authentications_migration, ":user_id", ":user_id, :null => false"
+ gsub_file authentications_migration, ":provider", ":provider, :null => false"
+ gsub_file authentications_migration, ":uid", ":uid, :null => false"
+ gsub_file authentications_migration, ":data", ":data, :null => false, :default => \"--- {}\\n\""
+ inject_into_file authentications_migration, "\n add_index :authentications, [:provider, :uid], :unique => true", :before => "\n end"
+
+ devise_migration = Dir[destination_root + '/db/migrate/*.rb'].find { |file| file=~/devise_create_users/ }
+ gsub_file devise_migration, /t.string :email,\s*:null => false, :default => ""/, "t.string :email"
+ inject_into_file devise_migration, "\n t.string :name", :before => "\n t.timestamps"
+
+ providers.each do |provider|
+ inject_into_file 'config/initializers/devise.rb', "\n config.omniauth :#{provider}, Rails.configuration.#{provider}_key, Rails.configuration.#{provider}_secret, client_options", :after => " # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'"
+ inject_into_file 'config/application.rb', "\n config.#{provider}_key = '#{config["#{provider}_key"]}'\n config.#{provider}_secret = '#{config["#{provider}_secret"]}'\n", :after => "class Application < Rails::Application"
+ inject_into_file 'config/environments/production.rb', "\n config.#{provider}_key = ENV['#{provider.upcase}_KEY']\n config.#{provider}_secret = ENV['#{provider.upcase}_SECRET']\n", :after => "class Application < Rails::Application"
+ end
+
+ inject_into_file 'config/initializers/devise.rb', "\n client_options = { :client_options => { :ssl => { :ca_file => '/etc/ssl/certs/ca-certificates.crt'} } }", :after => " # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'"
+
+ gsub_file 'app/models/user.rb', ':registerable', ':registerable, :omniauthable'
+ user_authentication_code = <<-END
+
+ has_many :authentications, :dependent => :destroy
+
+ Authentication.providers.each do |provider|
+ define_method provider do
+ instance_variable_get("@\#{provider}") || instance_variable_set("@\#{provider}", authentications.where(:provider => provider).first)
+ end
+ define_method "\#{provider}_connected?" do
+ send(provider) != nil
+ end
+ end
+
+ def email_required?
+ authentications.empty?
+ end
+END
+ inject_into_file 'app/models/user.rb', user_authentication_code, :before => "\nend"
+
+ gsub_file 'config/routes.rb', 'devise_for :users', 'devise_for :users, :controllers => { :omniauth_callbacks => "authentications" }'
+
+ route <<-END
+devise_scope :user do
+ match 'users/unauth/:provider' => 'authentications#destroy', :as => 'unauth'
+ match 'users/auth/:provider' => 'user_omniauth#authorize', :as => 'auth'
+ match 'logout' => 'devise/sessions#destroy', :as => 'logout'
+ get 'xx' => 'devise/registrations#destroy'
+ end
+END
+
+ if scrolls.include?('rails_basics') && scrolls.include?('haml')
+ append_file 'app/views/home/index.html.haml', <<-END
+
+- if current_user
+ %p You are logged in as user #\#{current_user.id} \#{current_user.name} \#{current_user.email}
+ %p= link_to "settings", edit_user_registration_path
+ %p= link_to "log out", logout_path
+ %p= link_to "delete user", user_registration_path, :data => { :confirm => "Are you sure?" }, :method => :delete
+- unless current_user
+ %p= link_to "create account", new_user_registration_path
+ %p= link_to "login", new_user_session_path
+- Authentication.providers.each do |provider|
+ - if current_user && current_user.send("\#{provider}_connected?")
+ %p= link_to "disconnect \#{provider}", unauth_path(provider)
+ - else
+ %p= link_to "connect \#{provider}", auth_path(provider)
+END
+ end
+end
+
+
+create_file "app/models/authentication.rb", <<-END
+class Authentication < ActiveRecord::Base
+ attr_accessible :data, :provider, :uid, :user_id
+ belongs_to :user
+ serialize :data
+
+ def self.providers
+ Devise.omniauth_providers
+ end
+end
+END
+
+create_file "app/controllers/authentications_controller.rb", <<-END
+class AuthenticationsController < Devise::OmniauthCallbacksController
+ skip_before_filter :verify_authenticity_token
+
+ def method_missing(method, *args)
+ raise "Unknown Provider Method: \#{method}" unless Authentication.providers.include?(method)
+
+ omniauth = request.env['omniauth.auth']
+ provider = omniauth['provider']
+ uid = omniauth['uid']
+ email = omniauth['info']['email']
+ name = omniauth['info']['name']
+
+ @user = User.includes(:authentications).merge(Authentication.where(:provider => provider, :uid => uid.to_s)).first
+
+ if @user
+ sign_in_and_redirect(:user, @user)
+ flash[:notice] = "Welcome back."
+ elsif current_user
+ current_user.authentications.create(:provider => provider, :uid => uid, :data => omniauth)
+ redirect_to(root_url)
+ flash[:notice] = "\#{provider} successfully connected."
+ else
+ @user = User.find_by_email(email)
+ if !@user
+ @user = User.new
+ @user.email = email # add users email from the returned authentication hash
+ @user.password = (15..25).collect{(45..126).to_a[Kernel.rand(81)].chr}.join # randomize password for new users
+ end
+ @user.authentications.build(:provider => provider, :uid => uid, :data => omniauth)
+ @user.save!
+
+ sign_in_and_redirect(:user, @user)
+ flash[:notice] = "Welcome!"
+ end
+ current_user.update_attribute(:email, email) if current_user.email.blank? && !email.blank?
+ current_user.update_attribute(:name, name) if current_user.name.blank? && !name.blank?
+ end
+
+ def destroy
+ provider = params[:provider]
+ authentication = current_user.authentications.where(:provider => provider).first
+ if !authentication
+ flash[:notice] = "\#{provider} wasn't found."
+ else
+ authentication.destroy
+ flash[:notice] = "\#{provider} disconnected."
+ end
+ redirect_to root_url
+ end
+end
+END
+
+__END__
+name: OmniAuth
+description: "Adds multi-strategy OmniAuth to Devise"
+author: allangrant
+
+exclusive: authentication
+category: authentication
+requires: [devise]
+run_after: [devise, rails_basics]
diff --git a/scrolls/postgresql.rb b/scrolls/postgresql.rb
index 42d19b3..65fb7df 100644
--- a/scrolls/postgresql.rb
+++ b/scrolls/postgresql.rb
@@ -3,11 +3,7 @@
gsub_file "config/database.yml", /username: .*/, "username: #{config['pg_username']}"
gsub_file "config/database.yml", /password: .*/, "password: #{config['pg_password']}"
-after_bundler do
- rake "db:create:all"
-
- rakefile("sample.rake") do
-<<-RUBY
+rakefile "sample.rake", <<-RUBY
namespace :db do
desc "Populate the database with sample data"
task :sample => :environment do
@@ -16,8 +12,6 @@
task :populate => :sample
end
RUBY
- end
-end
__END__
diff --git a/scrolls/rails_basics.rb b/scrolls/rails_basics.rb
index a111671..a25d405 100644
--- a/scrolls/rails_basics.rb
+++ b/scrolls/rails_basics.rb
@@ -5,11 +5,8 @@
# clean up rails defaults
remove_file "public/index.html"
remove_file "public/images/rails.png"
+ remove_file "app/assets/images/rails.png"
generate "controller home index"
- gsub_file "app/controllers/home_controller.rb", /def index/, <<-RUBY
-def index
- flash.now[:notice] = "Welcome! - love App Scrolls"
-RUBY
route "root :to => 'home#index'"
run "mv README.rdoc RAILS_README.rdoc"
@@ -29,8 +26,9 @@ def index
The original scaffold for this application was created by [App Scrolls](http://appscrolls.org).
The project was created with the following scrolls:
-
-#{ scrolls.map {|r| "* #{r}"}.join("\n")}
+```
+appscrolls new #{app_name} #{ scrolls.join(" ") }
+```
README
@@ -50,5 +48,4 @@ def index
name: Rails Basics
description: Best practices for new Rails apps
author: drnic
-
run_before: [git]
\ No newline at end of file
diff --git a/scrolls/rvm.rb b/scrolls/rvm.rb
new file mode 100644
index 0000000..0100bd5
--- /dev/null
+++ b/scrolls/rvm.rb
@@ -0,0 +1,19 @@
+# run "rvm use 1.9.3 exec rvm gemset create #{config['gemset']}"
+
+create_file '.rvmrc', <<-END
+rvm use 1.9.3@#{config['gemset']}
+#{"git status -sb" if scrolls.include?('git') }
+END
+
+# run "rvm 1.9.3 do gem install bundler --pre"
+
+run 'rvm rvmrc trust .'
+
+__END__
+name: RVM
+description: Creates .rvmrc file and gemset
+category: other
+config:
+ - gemset:
+ prompt: "Specify gemset for RVM file:"
+ type: string
diff --git a/scrolls/sass.rb b/scrolls/sass.rb
new file mode 100644
index 0000000..d899a9c
--- /dev/null
+++ b/scrolls/sass.rb
@@ -0,0 +1,26 @@
+# gem 'sass-rails' # No longer needed because it's included by default
+
+run "mv app/assets/stylesheets/application.css app/assets/stylesheets/application.css.scss"
+
+gsub_file "app/assets/stylesheets/application.css.scss", "\n *= require_tree .", ""
+
+create_file "app/assets/stylesheets/_variables.css.scss", <<-END
+// Define your global Sass variables here, for example:
+// $black: #000 !default;
+END
+
+append_file "app/assets/stylesheets/application.css.scss", <<-RUBY
+
+// Import any Sass/SCSS files you need below.
+@import "variables";
+RUBY
+
+
+__END__
+
+name: SASS
+description: "Utilize SASS for really awesome stylesheets!"
+author: mbleigh
+
+category: assets
+tags: [css, stylesheet]
diff --git a/scrolls/simple_form.rb b/scrolls/simple_form.rb
index d6e7179..5807e6e 100644
--- a/scrolls/simple_form.rb
+++ b/scrolls/simple_form.rb
@@ -1,7 +1,7 @@
gem 'simple_form'
after_bundler do
- if scroll? "twitter_bootstrap"
+ if scroll?("twitter_bootstrap") || scroll?("compass_twitter_bootstrap")
generate "simple_form:install --bootstrap"
else
generate "simple_form:install"
diff --git a/scrolls/untested/devise.rb b/scrolls/untested/devise.rb
deleted file mode 100644
index 4665e85..0000000
--- a/scrolls/untested/devise.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-gem 'devise'
-
-inject_into_file 'config/environments/development.rb', "\nconfig.action_mailer.default_url_options = { :host => 'localhost:3000' }\n", :after => "Application.configure do"
-inject_into_file 'config/environments/test.rb', "\nconfig.action_mailer.default_url_options = { :host => 'localhost:7000' }\n", :after => "Application.configure do"
-inject_into_file 'config/environments/production.rb', "\nconfig.action_mailer.default_url_options = { :host => '#{app_name}.com' }\n", :after => "Application.configure do"
-
-inject_into_file 'config/routes.rb', "\nroot :to => 'home#index'\n", :after => "Testapp::Application.routes.draw do"
-
-after_bundler do
- generate 'devise:install'
-
- if scrolls.include? 'mongo_mapper'
- gem 'mm-devise'
- gsub_file 'config/initializers/devise.rb', 'devise/orm/', 'devise/orm/mongo_mapper_active_model'
- generate 'mongo_mapper:devise User'
- elsif scrolls.include? 'mongoid'
- gsub_file 'config/initializers/devise.rb', 'devise/orm/active_record', 'devise/orm/mongoid'
- end
-
- generate 'devise user'
- generate "devise:views"
-
- if config['add_app_helpers']
- new_helpers = <<-RB
-module ApplicationHelper
-
- def current_user
- @current_user
- end
-
- def logged_in?
- @current_user != nil
- end
-RB
- gsub_file 'app/helpers/application_helper.rb', 'module ApplicationHelper', new_helpers
- end
-end
-
-__END__
-
-name: Devise
-description: Utilize Devise for authentication, automatically configured for your selected ORM.
-author: mbleigh
-
-category: authentication
-exclusive: authentication
-
-config:
- - add_app_helpers:
- type: boolean
- prompt: "Add logged_in and current_user helpers?"
-
diff --git a/scrolls/untested/haml.rb b/scrolls/untested/haml.rb
deleted file mode 100644
index 0f2f3da..0000000
--- a/scrolls/untested/haml.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-gem 'haml', '>= 3.0.0'
-gem 'haml-rails'
-
-__END__
-
-name: HAML
-description: "Utilize HAML for templating."
-author: mbleigh
-
-category: templating
-exclusive: templating
diff --git a/scrolls/untested/sass.rb b/scrolls/untested/sass.rb
deleted file mode 100644
index d7ab1e4..0000000
--- a/scrolls/untested/sass.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-unless scrolls.include? 'haml'
- gem 'haml', '>= 3.0.0'
-end
-
-__END__
-
-name: SASS
-description: "Utilize SASS (through the HAML gem) for really awesome stylesheets!"
-author: mbleigh
-
-exclusive: css_replacement
-category: assets
-tags: [css, stylesheet]
diff --git a/spec/appscrolls/config_spec.rb b/spec/appscrolls/config_spec.rb
index 180ad25..e6263bb 100644
--- a/spec/appscrolls/config_spec.rb
+++ b/spec/appscrolls/config_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
-describe AppScrollsScrolls::Config do
+describe AppScrolls::Config do
describe '#initialize' do
- subject{ AppScrollsScrolls::Config.new(YAML.load(@schema)) }
+ subject{ AppScrolls::Config.new(YAML.load(@schema)) }
it 'should add a question key for each key of the schema' do
@schema = <<-YAML
- test:
@@ -20,9 +20,9 @@
- multiple_choice:
type: multiple_choice
YAML
- subject.questions['string'].should be_kind_of(AppScrollsScrolls::Config::Prompt)
- subject.questions['boolean'].should be_kind_of(AppScrollsScrolls::Config::TrueFalse)
- subject.questions['multiple_choice'].should be_kind_of(AppScrollsScrolls::Config::MultipleChoice)
+ subject.questions['string'].should be_kind_of(AppScrolls::Config::Prompt)
+ subject.questions['boolean'].should be_kind_of(AppScrolls::Config::TrueFalse)
+ subject.questions['multiple_choice'].should be_kind_of(AppScrolls::Config::MultipleChoice)
end
it 'should error on invalid question type' do
@@ -54,42 +54,42 @@
end
it 'should include all questions' do
- lines.size.should == 4
+ lines.size.should == 3
end
it 'should handle "if"' do
- lines[1].should be_include("config['is_true']")
+ lines[0].should be_include("config['is_true']")
end
it 'should handle "unless"' do
- lines[2].should be_include("!config['is_false']")
+ lines[1].should be_include("!config['is_false']")
end
it 'should handle "if_scroll"' do
- lines[2].should be_include("scroll?('awesome')")
+ lines[1].should be_include("scroll?('awesome')")
end
- it 'should handle "unelss_scroll"' do
- lines[3].should be_include("!scroll?('awesome')")
+ it 'should handle "unless_scroll"' do
+ lines[2].should be_include("!scroll?('awesome')")
end
end
- describe AppScrollsScrolls::Config::Prompt do
- subject{ AppScrollsScrolls::Config::Prompt }
+ describe AppScrolls::Config::Prompt do
+ subject{ AppScrolls::Config::Prompt }
it 'should compile to a prompt' do
subject.new({'prompt' => "What's your favorite color?"}).question.should == 'ask_wizard("What\'s your favorite color?")'
end
end
- describe AppScrollsScrolls::Config::TrueFalse do
- subject{ AppScrollsScrolls::Config::TrueFalse }
+ describe AppScrolls::Config::TrueFalse do
+ subject{ AppScrolls::Config::TrueFalse }
it 'should compile to a yes? question' do
subject.new({'prompt' => 'Yes yes?'}).question.should == 'yes_wizard?("Yes yes?")'
end
end
- describe AppScrollsScrolls::Config::MultipleChoice do
- subject{ AppScrollsScrolls::Config::MultipleChoice }
+ describe AppScrolls::Config::MultipleChoice do
+ subject{ AppScrolls::Config::MultipleChoice }
it 'should compile into a multiple_choice' do
subject.new({'prompt' => 'What kind of fruit?', 'choices' => [['Apples', 'apples'], ['Bananas', 'bananas']]}).question.should ==
'multiple_choice("What kind of fruit?", [["Apples", "apples"], ["Bananas", "bananas"]])'
diff --git a/spec/appscrolls/scroll_spec.rb b/spec/appscrolls/scroll_spec.rb
index 0ba4ece..1a57050 100644
--- a/spec/appscrolls/scroll_spec.rb
+++ b/spec/appscrolls/scroll_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe AppScrollsScrolls::Scroll do
+describe AppScrolls::Scroll do
context "with a generated scroll" do
- subject{ AppScrollsScrolls::Scroll.generate('scroll_example', "# this is a test", :category => 'example', :name => "AppScrolls Example") }
+ subject{ AppScrolls::Scroll.generate('scroll_example', "# this is a test", :category => 'example', :name => "AppScrolls Example") }
context 'string setter methods' do
- (AppScrollsScrolls::Scroll::ATTRIBUTES - ['config']).each do |setter|
+ (AppScrolls::Scroll::ATTRIBUTES - ['config']).each do |setter|
it "should be able to set #{setter} with an argument" do
subject.send(setter + '=', "test")
subject.send(setter).should == 'test'
@@ -26,8 +26,8 @@
describe '.generate' do
it 'should work with a string and hash as arguments' do
- scroll = AppScrollsScrolls::Scroll.generate('some_key', '# some code', :name => "Example")
- scroll.superclass.should == AppScrollsScrolls::Scroll
+ scroll = AppScrolls::Scroll.generate('some_key', '# some code', :name => "Example")
+ scroll.superclass.should == AppScrolls::Scroll
end
it 'should work with an IO object' do
@@ -40,7 +40,7 @@
name: This is an Example
description: You know it's an exmaple.
RUBY
- scroll = AppScrollsScrolls::Scroll.generate('just_a_test', file)
+ scroll = AppScrolls::Scroll.generate('just_a_test', file)
scroll.template.should == '# this is an example'
scroll.category.should == 'example'
scroll.name.should == 'This is an Example'
@@ -50,7 +50,7 @@
file = StringIO.new <<-RUBY
# just ruby, no YAML
RUBY
- lambda{AppScrollsScrolls::Scroll.generate('testing',file)}.should raise_error(ArgumentError)
+ lambda{AppScrolls::Scroll.generate('testing',file)}.should raise_error(ArgumentError)
end
end
@@ -68,15 +68,15 @@
end
it 'should set default attributes' do
- scroll = AppScrollsScrolls::Scroll.generate('abc','# test')
+ scroll = AppScrolls::Scroll.generate('abc','# test')
- AppScrollsScrolls::Scroll::DEFAULT_ATTRIBUTES.each_pair do |k,v|
+ AppScrolls::Scroll::DEFAULT_ATTRIBUTES.each_pair do |k,v|
scroll.send(k).should == v
end
end
context 'Comparable' do
- subject{ AppScrollsScrolls::Scroll }
+ subject{ AppScrolls::Scroll }
it 'a < b.run_after(a)' do
A = subject.generate('a', '#')
B = subject.generate('b', '#', :run_after => ['a'])
diff --git a/spec/appscrolls/scrolls/sanity_spec.rb b/spec/appscrolls/scrolls/sanity_spec.rb
index 16ae09c..361d580 100644
--- a/spec/appscrolls/scrolls/sanity_spec.rb
+++ b/spec/appscrolls/scrolls/sanity_spec.rb
@@ -3,7 +3,7 @@
# This is a simple set of tests to make sure that
# all of the scrolls conform to the base requirements.
-AppScrollsScrolls::Scrolls.list_classes.each do |scroll|
+AppScrolls::Scrolls.list_classes.each do |scroll|
describe scroll do
it("should have a name"){ scroll.name.should be_kind_of(String) }
it("should have a description"){ scroll.description.should be_kind_of(String) }
@@ -18,13 +18,13 @@
it "should have a Config or nil config" do
if scroll.config
- scroll.config.should be_kind_of(AppScrollsScrolls::Config)
+ scroll.config.should be_kind_of(AppScrolls::Config)
end
end
it "should be in the list" do
- AppScrollsScrolls::Scrolls.list_classes.should be_include(scroll)
- AppScrollsScrolls::Scrolls.list.should be_include(scroll.key)
+ AppScrolls::Scrolls.list_classes.should be_include(scroll)
+ AppScrolls::Scrolls.list.should be_include(scroll.key)
end
end
end
diff --git a/spec/appscrolls/scrolls_spec.rb b/spec/appscrolls/scrolls_spec.rb
index 3264372..7c6d2f2 100644
--- a/spec/appscrolls/scrolls_spec.rb
+++ b/spec/appscrolls/scrolls_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe AppScrollsScrolls::Scrolls do
- subject{ AppScrollsScrolls::Scrolls }
- let(:scroll){ AppScrollsScrolls::Scroll.generate("scroll_test", "# Testing", :name => "Test Scroll", :category => "test", :description => "Just a test.")}
+describe AppScrolls::Scrolls do
+ subject{ AppScrolls::Scrolls }
+ let(:scroll){ AppScrolls::Scroll.generate("scroll_test", "# Testing", :name => "Test Scroll", :category => "test", :description => "Just a test.")}
before(:all) do
- AppScrollsScrolls::Scrolls.add(scroll)
+ AppScrolls::Scrolls.add(scroll)
end
it '.list_classes should include scroll classes' do
@@ -16,9 +16,16 @@
subject.list.should be_include('scroll_test')
end
+ it '.add should not overwrite scroll of same key' do
+ new_scroll = AppScrolls::Scroll.generate("scroll_test", "# Overwrite Testing", :name => "New Test Scroll", :category => "test", :description => "Just an overwrite test.")
+ AppScrolls::Scrolls.add(new_scroll)
+ subject["scroll_test"].should eql(scroll)
+ subject["scroll_test"].should_not eql(new_scroll)
+ end
+
describe '.for' do
it 'should find for a given category' do
- AppScrollsScrolls::Scrolls.for('test').should be_include('scroll_test')
+ AppScrolls::Scrolls.for('test').should be_include('scroll_test')
end
end
end
diff --git a/spec/appscrolls/template_spec.rb b/spec/appscrolls/template_spec.rb
index b0940d1..98fc830 100644
--- a/spec/appscrolls/template_spec.rb
+++ b/spec/appscrolls/template_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
-describe AppScrollsScrolls::Template do
- subject{ AppScrollsScrolls::Template }
- let(:scroll){ AppScrollsScrolls::Scroll.generate('name','# test') }
+describe AppScrolls::Template do
+ subject{ AppScrolls::Template }
+ let(:scroll){ AppScrolls::Scroll.generate('name','# test') }
describe '#initialize' do
it 'should work with classes' do
@@ -12,15 +12,19 @@
describe '#scrolls_with_dependencies' do
def s(*deps)
- mock(:Class, :requires => deps, :superclass => AppScrollsScrolls::Scroll)
+ mock(:Class, :requires => deps, :superclass => AppScrolls::Scroll)
end
-
+
def scroll(name)
- AppScrollsScrolls::Scrolls[name]
+ AppScrolls::Scrolls[name]
end
+ def scrolls(names)
+ names.split.map(&method(:scroll))
+ end
+
subject do
- @template = AppScrollsScrolls::Template.new([])
+ @template = AppScrolls::Template.new([])
@template.stub!(:scrolls).and_return(@scrolls)
@template.stub!(:scroll_classes).and_return(@scrolls)
@template
@@ -50,8 +54,23 @@ def scroll(name)
end
it 'should resolve and sort' do
- template = AppScrollsScrolls::Template.new([scroll('eycloud')])
+ template = AppScrolls::Template.new([scroll('eycloud')])
template.resolve_scrolls.should == [scroll('eycloud_recipes_on_deploy'), scroll('git'), scroll('github'), scroll('eycloud')]
end
+
+ it 'should correctly sort long dependencies' do
+ template = AppScrolls::Template.new(scrolls('active_admin postgresql simple_form compass_twitter_bootstrap delayed_job guard rails_basics git thin haml exception_notification devise_haml omniauth tweaks rvm mailgun heroku'))
+ ordered = template.resolve_scrolls
+ ordered.each_with_index do |scroll, index|
+ scroll.run_after.each do |scroll_name|
+ earlier_scroll_index = ordered.index(scroll(scroll_name))
+ earlier_scroll_index.should be < index if earlier_scroll_index
+ end
+ scroll.run_before.each do |scroll_name|
+ later_scroll_index = ordered.index(scroll(scroll_name))
+ later_scroll_index.should be > index if later_scroll_index
+ end
+ end
+ end
end
end
diff --git a/templates/diff_patch.tt b/templates/diff_patch.tt
new file mode 100644
index 0000000..00ecf9d
--- /dev/null
+++ b/templates/diff_patch.tt
@@ -0,0 +1 @@
+<%= `git diff head^ head` %>
\ No newline at end of file
diff --git a/templates/helpers.erb b/templates/helpers.erb
index 594489b..2a3b1b6 100644
--- a/templates/helpers.erb
+++ b/templates/helpers.erb
@@ -2,7 +2,7 @@ def scrolls; @scrolls end
def scroll?(name); @scrolls.include?(name) end
def say_custom(tag, text); say "\033[1m\033[36m" + tag.to_s.rjust(10) + "\033[0m" + " #{text}" end
-def say_scroll(name); say "\033[1m\033[36m" + "scroll".rjust(10) + "\033[0m" + " Running #{name} scroll..." end
+def say_scroll(name, extra=nil); say "\033[1m\033[36m" + "scroll".rjust(10) + "\033[0m" + " Running #{name} scroll...#{extra}" end
def say_wizard(text); say_custom(@current_scroll || 'wizard', text) end
def ask_wizard(question)
@@ -28,31 +28,75 @@ def multiple_choice(question, choices)
values = {}
choices.each_with_index do |choice,i|
values[(i + 1).to_s] = choice[1]
- say_custom (i + 1).to_s + ')', choice[0]
+ say_custom((i + 1).to_s + ')', choice[0])
end
answer = ask_wizard("Enter your selection:") while !values.keys.include?(answer)
values[answer]
end
@current_scroll = nil
-@configs = {}
+@configs = config
-@before_everything_blocks = []
-def before_everything(&block); @before_everything_blocks << [@current_scroll, block]; end
-@after_blocks = []
-def after_bundler(&block); @after_blocks << [@current_scroll, block]; end
-@after_everything_blocks = []
-def after_everything(&block); @after_everything_blocks << [@current_scroll, block]; end
-@before_configs = {}
-def before_config(&block); @before_configs[@current_scroll] = block; end
+def define_callback(name)
+ var = "@#{name}_blocks"
+ instance_variable_set(var, [])
+ eigenclass = class << self; self; end
+ eigenclass.send :define_method, name.to_sym do |&block|
+ instance_variable_get(var) << [@current_scroll, block]
+ end
+end
+
+def execute_callbacks name
+ say_wizard "Running #{name} callbacks."
+ instance_variable_get("@#{name}_blocks").each do |current_scroll, block|
+ @current_scroll = current_scroll
+ say_scroll current_scroll, " (#{name})"
+ block.call
+ git_commit("appscroll : #{current_scroll} (#{name})") if @scrolls.include?('git')
+ end
+ @current_scroll = nil
+end
+
+define_callback :before_everything
+define_callback :after_bundler
+define_callback :after_everything
+define_callback :finally
def git_commit(message)
`git add .`
- `git commit -m "#{message}"`
+ `git commit -am "#{message}"`
end
-def execute_block(block)
- config = @configs[block[0]] || {}
- @current_scroll = block[0];
- block[1].call
+def scroll key, &block
+ @current_scroll = key
+ yield(@configs[key] ||= {})
end
+
+def apply_patch(suffix=nil)
+ diff_file = "#{@current_scroll + (suffix ? "-#{suffix}" : "")}.diff"
+ say_wizard "Applying patch #{diff_file}"
+ run "git apply #{@diff_path + diff_file}"
+end
+
+# So you can set options in config file: config.postgresql.pg_password = ''
+module ConfigMapper
+ def method_missing(method, *args, &block)
+ method = method.to_s
+ if method.gsub!(/=$/, '')
+ self[method] = args.first
+ else
+ if self.has_key?(method)
+ self[method].extend ConfigMapper if self[method].is_a? Hash
+ else
+ self[method] = {}
+ self[method].extend ConfigMapper
+ end
+ self[method]
+ end
+ end
+end
+config.extend ConfigMapper
+
+def default_email
+ @default_email ||= `git config -l`.match(/^user\.email=(.*)/)[1]
+end
\ No newline at end of file
diff --git a/templates/layout.erb b/templates/layout.erb
index 62072e5..898d980 100644
--- a/templates/layout.erb
+++ b/templates/layout.erb
@@ -19,20 +19,21 @@ end
RUBY
@scrolls = <%= resolve_scrolls.map(&:key).inspect %>
-use_git = @scrolls.include?('git')
+@diff_path = "<%= File.expand_path(Template.template_root + '/../scrolls') +'/' %>"
+using_git = @scrolls.include?('git')
+using_database = @scrolls.include?('mysql') || @scrolls.include?('postgresql')
<%= render "helpers" %>
+<% if config_script %># >----------------------------[ Config Script ]------------------------------<
+
+<%= config_script %><% end %>
+
<% resolve_scrolls.each do |scroll| %>
<%= render 'scroll', scroll.get_binding %>
<% end %>
-
-say_wizard "Running before_everything callbacks."
-@before_everything_blocks.each do |b|
- execute_block(b)
- git_commit("recipe : '#{b.first}'") if use_git
-end
+execute_callbacks :before_everything
<% if custom_code? %># >-----------------------------[ Custom Code ]-------------------------------<
@@ -44,19 +45,8 @@ end
say_wizard "Running Bundler install. This will take a while."
run 'bundle install'
-git_commit("bundle install") if use_git
-
-
-say_wizard "Running after Bundler callbacks."
-@after_blocks.each do |b|
- execute_block(b)
- git_commit("recipe : '#{b.first}'") if use_git
-end
-
-@current_scroll = nil
-say_wizard "Running after_everything callbacks."
-@after_everything_blocks.each do |b|
- execute_block(b)
- git_commit("recipe : '#{b.first}'") if use_git
-end
-
+git_commit("bundle install") if using_git
+rake "db:create:all" if using_database
+execute_callbacks :after_bundler
+execute_callbacks :after_everything
+execute_callbacks :finally
diff --git a/templates/memorized_scroll.tt b/templates/memorized_scroll.tt
new file mode 100644
index 0000000..62f3f13
--- /dev/null
+++ b/templates/memorized_scroll.tt
@@ -0,0 +1,22 @@
+after_everything do
+ apply_patch # applies a diff file with the same filename and .diff extension
+end
+
+__END__
+
+name: <%= name.humanize.capitalize %>
+description: A freshly memorized scroll that applies a diff patch.
+website:
+author: <%= `whoami`.strip %>
+
+requires: []
+run_after: []
+run_before: []
+
+category: other # authentication, testing, persistence, javascript, css, services, deployment, and templating
+# exclusive:
+
+# config:
+# - foo:
+# type: boolean
+# prompt: "Is foo true?"
diff --git a/templates/scroll.erb b/templates/scroll.erb
index 1804e6d..f208acb 100644
--- a/templates/scroll.erb
+++ b/templates/scroll.erb
@@ -1,10 +1,6 @@
# ><%= "[ #{name} ]".center(75,'-') %><
-
-@current_scroll = "<%= key %>"
-@before_configs["<%= key %>"].call if @before_configs["<%= key %>"]
-say_scroll '<%= name %>'
-
-<%= config.compile if config %>
-@configs[@current_scroll] = config
-
+scroll '<%= key %>' do |config|
+say_scroll '<%= name.gsub("'", "\\'") %>'
+<%= "\n" + config.compile + "\n" if config %>
<%= template %>
+end # <%= key %>
diff --git a/scrolls/untested/activerecord.rb b/untested_scrolls/activerecord.rb
similarity index 100%
rename from scrolls/untested/activerecord.rb
rename to untested_scrolls/activerecord.rb
diff --git a/scrolls/untested/cancan.rb b/untested_scrolls/cancan.rb
similarity index 100%
rename from scrolls/untested/cancan.rb
rename to untested_scrolls/cancan.rb
diff --git a/scrolls/untested/carrierwave.rb b/untested_scrolls/carrierwave.rb
similarity index 100%
rename from scrolls/untested/carrierwave.rb
rename to untested_scrolls/carrierwave.rb
diff --git a/scrolls/untested/carrierwave_direct.rb b/untested_scrolls/carrierwave_direct.rb
similarity index 100%
rename from scrolls/untested/carrierwave_direct.rb
rename to untested_scrolls/carrierwave_direct.rb
diff --git a/scrolls/untested/cartographer.rb b/untested_scrolls/cartographer.rb
similarity index 100%
rename from scrolls/untested/cartographer.rb
rename to untested_scrolls/cartographer.rb
diff --git a/scrolls/untested/devise_invitable.rb b/untested_scrolls/devise_invitable.rb
similarity index 100%
rename from scrolls/untested/devise_invitable.rb
rename to untested_scrolls/devise_invitable.rb
diff --git a/scrolls/untested/event_calendar.rb b/untested_scrolls/event_calendar.rb
similarity index 100%
rename from scrolls/untested/event_calendar.rb
rename to untested_scrolls/event_calendar.rb
diff --git a/scrolls/untested/factory_girl.rb b/untested_scrolls/factory_girl.rb
similarity index 100%
rename from scrolls/untested/factory_girl.rb
rename to untested_scrolls/factory_girl.rb
diff --git a/scrolls/untested/ffaker.rb b/untested_scrolls/ffaker.rb
similarity index 100%
rename from scrolls/untested/ffaker.rb
rename to untested_scrolls/ffaker.rb
diff --git a/scrolls/untested/fixture_builder.rb b/untested_scrolls/fixture_builder.rb
similarity index 100%
rename from scrolls/untested/fixture_builder.rb
rename to untested_scrolls/fixture_builder.rb
diff --git a/scrolls/untested/forgery.rb b/untested_scrolls/forgery.rb
similarity index 100%
rename from scrolls/untested/forgery.rb
rename to untested_scrolls/forgery.rb
diff --git a/scrolls/untested/hoptoad.rb b/untested_scrolls/hoptoad.rb
similarity index 100%
rename from scrolls/untested/hoptoad.rb
rename to untested_scrolls/hoptoad.rb
diff --git a/scrolls/untested/inherited_resources.rb b/untested_scrolls/inherited_resources.rb
similarity index 100%
rename from scrolls/untested/inherited_resources.rb
rename to untested_scrolls/inherited_resources.rb
diff --git a/scrolls/untested/intercom.rb b/untested_scrolls/intercom.rb
similarity index 100%
rename from scrolls/untested/intercom.rb
rename to untested_scrolls/intercom.rb
diff --git a/scrolls/untested/jammit.rb b/untested_scrolls/jammit.rb
similarity index 100%
rename from scrolls/untested/jammit.rb
rename to untested_scrolls/jammit.rb
diff --git a/scrolls/untested/jasmine.rb b/untested_scrolls/jasmine.rb
similarity index 100%
rename from scrolls/untested/jasmine.rb
rename to untested_scrolls/jasmine.rb
diff --git a/scrolls/untested/mini_magick.rb b/untested_scrolls/mini_magick.rb
similarity index 100%
rename from scrolls/untested/mini_magick.rb
rename to untested_scrolls/mini_magick.rb
diff --git a/scrolls/untested/mongo_mapper.rb b/untested_scrolls/mongo_mapper.rb
similarity index 100%
rename from scrolls/untested/mongo_mapper.rb
rename to untested_scrolls/mongo_mapper.rb
diff --git a/scrolls/untested/mongohq.rb b/untested_scrolls/mongohq.rb
similarity index 100%
rename from scrolls/untested/mongohq.rb
rename to untested_scrolls/mongohq.rb
diff --git a/scrolls/untested/mongoid.rb b/untested_scrolls/mongoid.rb
similarity index 100%
rename from scrolls/untested/mongoid.rb
rename to untested_scrolls/mongoid.rb
diff --git a/scrolls/untested/mootools.rb b/untested_scrolls/mootools.rb
similarity index 100%
rename from scrolls/untested/mootools.rb
rename to untested_scrolls/mootools.rb
diff --git a/scrolls/untested/newrelic.rb b/untested_scrolls/newrelic.rb
similarity index 100%
rename from scrolls/untested/newrelic.rb
rename to untested_scrolls/newrelic.rb
diff --git a/scrolls/untested/nifty_generators.rb b/untested_scrolls/nifty_generators.rb
similarity index 100%
rename from scrolls/untested/nifty_generators.rb
rename to untested_scrolls/nifty_generators.rb
diff --git a/scrolls/untested/oa_oauth.rb b/untested_scrolls/oa_oauth.rb
similarity index 100%
rename from scrolls/untested/oa_oauth.rb
rename to untested_scrolls/oa_oauth.rb
diff --git a/scrolls/untested/omniauth.rb b/untested_scrolls/omniauth.rb
similarity index 100%
rename from scrolls/untested/omniauth.rb
rename to untested_scrolls/omniauth.rb
diff --git a/scrolls/untested/paper_trail.rb b/untested_scrolls/paper_trail.rb
similarity index 100%
rename from scrolls/untested/paper_trail.rb
rename to untested_scrolls/paper_trail.rb
diff --git a/scrolls/untested/pow.rb b/untested_scrolls/pow.rb
similarity index 100%
rename from scrolls/untested/pow.rb
rename to untested_scrolls/pow.rb
diff --git a/scrolls/untested/puma.rb b/untested_scrolls/puma.rb
similarity index 100%
rename from scrolls/untested/puma.rb
rename to untested_scrolls/puma.rb
diff --git a/scrolls/untested/rails_dev_tweaks.rb b/untested_scrolls/rails_dev_tweaks.rb
similarity index 100%
rename from scrolls/untested/rails_dev_tweaks.rb
rename to untested_scrolls/rails_dev_tweaks.rb
diff --git a/scrolls/untested/rails_erd.rb b/untested_scrolls/rails_erd.rb
similarity index 100%
rename from scrolls/untested/rails_erd.rb
rename to untested_scrolls/rails_erd.rb
diff --git a/scrolls/untested/rails_footnotes.rb b/untested_scrolls/rails_footnotes.rb
similarity index 100%
rename from scrolls/untested/rails_footnotes.rb
rename to untested_scrolls/rails_footnotes.rb
diff --git a/scrolls/untested/ransack.rb b/untested_scrolls/ransack.rb
similarity index 100%
rename from scrolls/untested/ransack.rb
rename to untested_scrolls/ransack.rb
diff --git a/scrolls/untested/rmagick.rb b/untested_scrolls/rmagick.rb
similarity index 100%
rename from scrolls/untested/rmagick.rb
rename to untested_scrolls/rmagick.rb
diff --git a/scrolls/untested/sequel.rb b/untested_scrolls/sequel.rb
similarity index 100%
rename from scrolls/untested/sequel.rb
rename to untested_scrolls/sequel.rb
diff --git a/scrolls/untested/settingslogic.rb b/untested_scrolls/settingslogic.rb
similarity index 100%
rename from scrolls/untested/settingslogic.rb
rename to untested_scrolls/settingslogic.rb
diff --git a/scrolls/untested/shoulda_matchers.rb b/untested_scrolls/shoulda_matchers.rb
similarity index 100%
rename from scrolls/untested/shoulda_matchers.rb
rename to untested_scrolls/shoulda_matchers.rb
diff --git a/scrolls/untested/sidekiq.rb b/untested_scrolls/sidekiq.rb
similarity index 100%
rename from scrolls/untested/sidekiq.rb
rename to untested_scrolls/sidekiq.rb
diff --git a/scrolls/untested/slim.rb b/untested_scrolls/slim.rb
similarity index 100%
rename from scrolls/untested/slim.rb
rename to untested_scrolls/slim.rb
diff --git a/scrolls/untested/spork.rb b/untested_scrolls/spork.rb
similarity index 100%
rename from scrolls/untested/spork.rb
rename to untested_scrolls/spork.rb
diff --git a/scrolls/untested/thinking_sphinx.rb b/untested_scrolls/thinking_sphinx.rb
similarity index 100%
rename from scrolls/untested/thinking_sphinx.rb
rename to untested_scrolls/thinking_sphinx.rb
diff --git a/scrolls/untested/vanity.rb b/untested_scrolls/vanity.rb
similarity index 100%
rename from scrolls/untested/vanity.rb
rename to untested_scrolls/vanity.rb
diff --git a/version.rb b/version.rb
index 95eff93..2b5f931 100644
--- a/version.rb
+++ b/version.rb
@@ -1,3 +1,3 @@
-module AppScrollsScrolls
+module AppScrolls
VERSION = "0.8.4"
end