adding passwords
git-svn-id: http://svn.barleysodas.com/barleysodas/trunk@84 0f7b21a7-9e3a-4941-bbeb-ce5c7c368fa7master
parent
a4552317d2
commit
34fe5817ed
|
@ -5,7 +5,7 @@ class SessionsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@people = People.find_by_title(params[:login]) rescue nil
|
@people = People.authenticate(params[:login], params[:password])
|
||||||
if @people
|
if @people
|
||||||
session[:people_title] = @people.title
|
session[:people_title] = @people.title
|
||||||
session[:people_id] = @people.id
|
session[:people_id] = @people.id
|
||||||
|
|
|
@ -9,6 +9,10 @@ class People < ActiveRecord::Base
|
||||||
has_many :updated_pages, :class_name => 'Page', :foreign_key => 'updated_by'
|
has_many :updated_pages, :class_name => 'Page', :foreign_key => 'updated_by'
|
||||||
validates_uniqueness_of :title
|
validates_uniqueness_of :title
|
||||||
|
|
||||||
|
make_authenticatable
|
||||||
|
validates_length_of :password, :minimum => 8, :if => :password_required?,
|
||||||
|
:message => 'must be at least 8 characters in length'
|
||||||
|
|
||||||
##
|
##
|
||||||
# Finds me.
|
# Finds me.
|
||||||
#
|
#
|
||||||
|
@ -16,4 +20,13 @@ class People < ActiveRecord::Base
|
||||||
@penguincoder ||= self.find_by_title('PenguinCoder') rescue nil
|
@penguincoder ||= self.find_by_title('PenguinCoder') rescue nil
|
||||||
@penguincoder
|
@penguincoder
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
##
|
||||||
|
# Determines if the password is needed.
|
||||||
|
#
|
||||||
|
def password_required?
|
||||||
|
self.encrypted_password.blank?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
<p>
|
<p>
|
||||||
<label for="people_title">Name</label> <%= text_field 'people', 'title' %>
|
<label for="people_title">Name</label> <%= text_field 'people', 'title' %>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="people_password">Password</label> <%= text_field 'people', 'password' %>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="people_password_confirmation">Password Confirmation</label> <%= text_field 'people', 'password_confirmation' %>
|
||||||
|
</p>
|
||||||
<%= render :partial => 'pages/page_form' %>
|
<%= render :partial => 'pages/page_form' %>
|
||||||
|
|
|
@ -2,5 +2,8 @@
|
||||||
<p>
|
<p>
|
||||||
<label for="login">People</label> <%= text_field_tag 'login' -%>
|
<label for="login">People</label> <%= text_field_tag 'login' -%>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="password">Password</label> <%= password_field_tag 'password' -%>
|
||||||
|
</p>
|
||||||
<%= submit_tag 'Login' %>
|
<%= submit_tag 'Login' %>
|
||||||
<% end -%>
|
<% end -%>
|
|
@ -3,10 +3,13 @@ class CreatePeoples < ActiveRecord::Migration
|
||||||
create_table :peoples do |t|
|
create_table :peoples do |t|
|
||||||
t.column :title, :string
|
t.column :title, :string
|
||||||
t.column :role_id, :integer
|
t.column :role_id, :integer
|
||||||
|
t.column :encrypted_password, :string, :limit => 512
|
||||||
|
t.column :salt, :string, :limit => 512
|
||||||
end
|
end
|
||||||
add_index :peoples, :title
|
add_index :peoples, :title
|
||||||
add_index :peoples, :role_id
|
add_index :peoples, :role_id
|
||||||
p = People.new :title => 'PenguinCoder', :page => Page.new
|
p = People.new :title => 'PenguinCoder', :page => Page.new,
|
||||||
|
:password => 'new_password', :password_confirmation => 'new_password'
|
||||||
p.role = Role.admin_role
|
p.role = Role.admin_role
|
||||||
p.save
|
p.save
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) <2006> <Luke Redpath>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,45 @@
|
||||||
|
Crypted Authentication Plugin for Rails
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
This is an ultra-lightweight, simple crypted authentication plugin for Rails.
|
||||||
|
|
||||||
|
It is extracted from several projects I've worked on - code that has been written many times over and over again making it an ideal candidate for extraction to a plugin. The plugin also has good test coverage. The tests were written as BDD-style specifications although I chose Test::Unit over RSpec for portability reasons
|
||||||
|
|
||||||
|
== Using the plugin
|
||||||
|
|
||||||
|
Using the plugin is really simple. Simply add the following call to your model:
|
||||||
|
|
||||||
|
class User < ActiveRecord::Base
|
||||||
|
make_authenticatable
|
||||||
|
end
|
||||||
|
|
||||||
|
The plugin doesn't assume too much. All it requires in your schema is username, encrypted_password and salt columns. The plugin does not handle validations for you - it leaves you to decide how you want to validate but here is a good example that I use in one of my applications:
|
||||||
|
|
||||||
|
class User < ActiveRecord::Base
|
||||||
|
make_authenticatable
|
||||||
|
|
||||||
|
validates_presence_of :username, :message => 'cannot be blank'
|
||||||
|
validates_length_of :password, :minimum => 6, :if => :password_required?,
|
||||||
|
:message => 'must be at least 6 characters in length'
|
||||||
|
|
||||||
|
protected
|
||||||
|
def password_required?
|
||||||
|
self.encrypted_password.blank?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
As well as the three attributes listed above, the plugin defines a standard object attribute called password. This stores the clear-text password and is not persisted to the database. The salt and encrypted_password attributes are marked as attr_protected which means you cannot assign values to them using mass assignment (such as new and create hashes). An authenticatable? instance method can be used in your own tests to assert that the plugin functionality has been added to your model.
|
||||||
|
|
||||||
|
== Authentication
|
||||||
|
|
||||||
|
Finally, a class-level authenticate method can be used to find a user for a given username and password. This function returns nil if no user is found.
|
||||||
|
|
||||||
|
if user = User.authenticate(params[:username], params[:password])
|
||||||
|
flash[:notice] = 'Logged in successfully']
|
||||||
|
session[:current_user] = user.id
|
||||||
|
redirect_to home_url
|
||||||
|
end
|
||||||
|
|
||||||
|
== Feedback
|
||||||
|
|
||||||
|
Please send any feedback to Luke Redpath <contact@lukeredpath.co.uk>. This plugin is licensed under the MIT license.
|
|
@ -0,0 +1,22 @@
|
||||||
|
require 'rake'
|
||||||
|
require 'rake/testtask'
|
||||||
|
require 'rake/rdoctask'
|
||||||
|
|
||||||
|
desc 'Default: run unit tests.'
|
||||||
|
task :default => :test
|
||||||
|
|
||||||
|
desc 'Test the crypted_authentication plugin.'
|
||||||
|
Rake::TestTask.new(:test) do |t|
|
||||||
|
t.libs << 'lib'
|
||||||
|
t.pattern = 'test/**/*_test.rb'
|
||||||
|
t.verbose = true
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Generate documentation for the crypted_authentication plugin.'
|
||||||
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||||
|
rdoc.rdoc_dir = 'rdoc'
|
||||||
|
rdoc.title = 'CryptedAuthentication'
|
||||||
|
rdoc.options << '--line-numbers' << '--inline-source'
|
||||||
|
rdoc.rdoc_files.include('README')
|
||||||
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: Luke Redpath
|
||||||
|
email: contact@lukeredpath.co.uk
|
||||||
|
license: MIT
|
||||||
|
homepage: http://opensource.agileevolved.com
|
||||||
|
summary: Yet Another Rails Authentication Plugin - or YARAP - a simple crypted authentication plugin complete with a full BDD-style test-suite. Adds basic authentication to your models.
|
|
@ -0,0 +1,2 @@
|
||||||
|
# mixin functionality to activerecord
|
||||||
|
ActiveRecord::Base.send(:include, CryptedAuthentication::Authenticator)
|
|
@ -0,0 +1 @@
|
||||||
|
# Install hook code here
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Crypted Authentication plugin
|
||||||
|
# (c) Luke Redpath <contact[AT]lukeredpath.co.uk>
|
||||||
|
#
|
||||||
|
# A simple crypted authentication plugin for Rails
|
||||||
|
require 'digest/sha1'
|
||||||
|
|
||||||
|
module CryptedAuthentication
|
||||||
|
module Authenticator
|
||||||
|
def self.included(base)
|
||||||
|
base.extend(ClassMethods)
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def make_authenticatable
|
||||||
|
attr_accessor :password, :password_confirmation
|
||||||
|
attr_protected :encrypted_password, :salt
|
||||||
|
before_save :encrypt_password
|
||||||
|
end
|
||||||
|
|
||||||
|
def authenticate(username, password)
|
||||||
|
user = self.find_by_title(username)
|
||||||
|
return nil if user.nil?
|
||||||
|
user.matches_password?(password) ? user : nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authenticatable?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def matches_password?(cleartext_password)
|
||||||
|
self.encrypted_password == encrypt(cleartext_password, self.salt)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def encrypt_password
|
||||||
|
return if self.password.blank? or self.password_confirmation.blank?
|
||||||
|
if self.password != self.password_confirmation
|
||||||
|
self.errors.add(:passwords, 'do not match')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
generate_salt
|
||||||
|
self.encrypted_password = encrypt(self.password, self.salt)
|
||||||
|
self.encrypted_password
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_salt
|
||||||
|
self.salt = encrypt(Time.now, rand.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypt(string, salt='')
|
||||||
|
Digest::SHA1.hexdigest("---#{string}---#{salt}---")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
4
vendor/plugins/crypted_authentication/tasks/crypted_authentication_tasks.rake
vendored
Normal file
4
vendor/plugins/crypted_authentication/tasks/crypted_authentication_tasks.rake
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# desc "Explaining what the task does"
|
||||||
|
# task :crypted_authentication do
|
||||||
|
# # Task goes here
|
||||||
|
# end
|
|
@ -0,0 +1,19 @@
|
||||||
|
test:
|
||||||
|
adapter: sqlite3
|
||||||
|
dbfile: test.sqlite3.db
|
||||||
|
|
||||||
|
# adapter: sqlite
|
||||||
|
# dbfile: test.sqlite.db
|
||||||
|
|
||||||
|
# adapter: mysql
|
||||||
|
# host: localhost
|
||||||
|
# username:
|
||||||
|
# password:
|
||||||
|
# database: test
|
||||||
|
|
||||||
|
# adapter: postgresql
|
||||||
|
# host: localhost
|
||||||
|
# username:
|
||||||
|
# password:
|
||||||
|
# database: test
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
Rails::Initializer.run do |config|
|
||||||
|
config.cache_classes = true
|
||||||
|
config.whiny_nils = true
|
||||||
|
config.action_controller.consider_all_requests_local = true
|
||||||
|
config.action_controller.perform_caching = false
|
||||||
|
config.action_mailer.delivery_method = :test
|
||||||
|
config.action_mailer.perform_deliveries = true
|
||||||
|
end
|
|
@ -0,0 +1,3 @@
|
||||||
|
ActionController::Routing::Routes.draw do |map|
|
||||||
|
map.connect ':controller/:action/:id'
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
ActiveRecord::Schema.define(:version => 2) do
|
||||||
|
|
||||||
|
create_table "test_users", :force => true do |t|
|
||||||
|
t.column "username", :string
|
||||||
|
t.column "encrypted_password", :string
|
||||||
|
t.column "salt", :string
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,121 @@
|
||||||
|
ENV['DB'] = 'test'
|
||||||
|
|
||||||
|
require 'test/unit'
|
||||||
|
require File.dirname(__FILE__) + '/ptk_helper'
|
||||||
|
require File.dirname(__FILE__) + '/../lib/crypted_authentication'
|
||||||
|
|
||||||
|
def encrypt(string, salt='')
|
||||||
|
Digest::SHA1.hexdigest("---#{string}---#{salt}---")
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestUser < ActiveRecord::Base
|
||||||
|
end
|
||||||
|
|
||||||
|
class ObjectWithCryptedAuthPluginIncluded < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
TestUser.send(:make_authenticatable)
|
||||||
|
@user = TestUser.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_be_authenticatable
|
||||||
|
assert @user.authenticatable?
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_not_allow_mass_assignment_of_encrypted_password
|
||||||
|
@user.attributes = {:encrypted_password => 'foo'}
|
||||||
|
assert_nil @user.encrypted_password
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_not_allow_mass_assignment_of_salt
|
||||||
|
@user.attributes = {:salt => 'foo'}
|
||||||
|
assert_nil @user.salt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NewUserWithCryptedAuthPluginIncluded < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
TestUser.send(:make_authenticatable)
|
||||||
|
@user = TestUser.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_have_a_nil_password
|
||||||
|
assert_nil @user.password
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class UserWithNoClearTextPassword < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
TestUser.send(:make_authenticatable)
|
||||||
|
@user = TestUser.new
|
||||||
|
@user.password = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_not_change_salt_when_calling_save
|
||||||
|
@user.salt = 'foo'
|
||||||
|
@user.save
|
||||||
|
assert_equal 'foo', @user.salt
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_not_change_encrypted_password_when_calling_save
|
||||||
|
@user.encrypted_password = 'password'
|
||||||
|
@user.save
|
||||||
|
assert_equal 'password', @user.encrypted_password
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class CryptedAuthUserWithClearTextPassword < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
TestUser.send(:make_authenticatable)
|
||||||
|
@user = TestUser.new
|
||||||
|
@user.password = 'password'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_have_a_random_salt_after_calling_save
|
||||||
|
@user.save
|
||||||
|
assert_not_nil @user.salt
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_have_an_encrypted_password_after_calling_save
|
||||||
|
@user.save
|
||||||
|
assert_equal @user.encrypted_password, encrypt('password', @user.salt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class UserWithEncryptedPassword < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
TestUser.send(:make_authenticatable)
|
||||||
|
@user = TestUser.new
|
||||||
|
@user.password = 'password'
|
||||||
|
@user.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_be_authenticatable_with_original_cleartext_password
|
||||||
|
assert @user.matches_password?('password')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_not_be_authenticatable_with_the_wrong_cleartext_password
|
||||||
|
assert !@user.matches_password?('wrongpass')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class UserAuthenticator < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
TestUser.send(:make_authenticatable)
|
||||||
|
@user = TestUser.new
|
||||||
|
@user.username = 'joebloggs'
|
||||||
|
@user.password = 'password'
|
||||||
|
@user.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_return_a_user_for_a_matching_username_and_password
|
||||||
|
assert_equal @user, TestUser.authenticate('joebloggs', 'password')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_return_nil_for_nonexistent_username
|
||||||
|
assert_nil TestUser.authenticate('nouser', 'password')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_return_nil_for_incorrect_password
|
||||||
|
assert_nil TestUser.authenticate('joebloggs', 'wrongpassword')
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,26 @@
|
||||||
|
# We are always a test environment and should never be anything else
|
||||||
|
ENV["RAILS_ENV"] ||= "test"
|
||||||
|
|
||||||
|
require File.join(File.dirname(__FILE__), 'ptk')
|
||||||
|
|
||||||
|
# Set up RAILS_ROOT to #{plugin_path}/test
|
||||||
|
unless defined?(RAILS_ROOT)
|
||||||
|
root_path = PTK::LoadPath.expand(__FILE__, '..', '..')
|
||||||
|
|
||||||
|
unless RUBY_PLATFORM =~ /mswin32/
|
||||||
|
require 'pathname'
|
||||||
|
root_path = Pathname.new(root_path).cleanpath(true).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
RAILS_ROOT = root_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# add #{plugin_path}/test/lib
|
||||||
|
PTK::LoadPath.add RAILS_ROOT, 'lib'
|
||||||
|
|
||||||
|
# add #{plugin_path}/lib
|
||||||
|
PTK::LoadPath.add RAILS_ROOT, '..', 'lib'
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'test/unit'
|
||||||
|
require 'active_support'
|
|
@ -0,0 +1,11 @@
|
||||||
|
require 'action_pack'
|
||||||
|
require 'action_controller'
|
||||||
|
require 'action_controller/test_process'
|
||||||
|
|
||||||
|
ActionController::Base.ignore_missing_templates = true
|
||||||
|
|
||||||
|
if PTK::Configuration.load :routes
|
||||||
|
ActionController::Routing::Routes.reload rescue nil
|
||||||
|
end
|
||||||
|
|
||||||
|
class ActionController::Base; def rescue_action(e) raise e end; end
|
|
@ -0,0 +1,4 @@
|
||||||
|
require 'action_mailer'
|
||||||
|
|
||||||
|
ActionMailer::Base.delivery_method = :test
|
||||||
|
ActionMailer::Base.perform_deliveries = true
|
|
@ -0,0 +1,27 @@
|
||||||
|
require 'active_record'
|
||||||
|
require 'active_record/fixtures'
|
||||||
|
|
||||||
|
ActiveRecord::Base.logger = Logger.new(File.join(RAILS_ROOT, 'test.log'))
|
||||||
|
|
||||||
|
# Load the database.yml from #{plugin_path}/test/config if it exists
|
||||||
|
if file = PTK::Configuration.find_path(:database)
|
||||||
|
|
||||||
|
config = YAML::load_file(file)
|
||||||
|
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
|
||||||
|
|
||||||
|
# Load a schema if it exists
|
||||||
|
if schema = PTK::Configuration.find_path(:schema)
|
||||||
|
|
||||||
|
load(schema)
|
||||||
|
|
||||||
|
# Setup fixtures if the directory exists
|
||||||
|
if fixtures = PTK::Configuration.find_path(:fixtures)
|
||||||
|
|
||||||
|
PTK::LoadPath.add fixtures
|
||||||
|
|
||||||
|
Test::Unit::TestCase.fixture_path = fixtures
|
||||||
|
Test::Unit::TestCase.use_instantiated_fixtures = false
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
# We require the initializer to setup the environment properly
|
||||||
|
unless defined?(Rails::Initializer)
|
||||||
|
if File.directory?("#{RAILS_ROOT}/../../../rails")
|
||||||
|
require "#{RAILS_ROOT}/../../../rails/railties/lib/initializer"
|
||||||
|
else
|
||||||
|
require 'rubygems'
|
||||||
|
require_gem 'rails'
|
||||||
|
require 'initializer'
|
||||||
|
end
|
||||||
|
Rails::Initializer.run(:set_load_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
# We overwrite load_environment so we can have only one file
|
||||||
|
module Rails
|
||||||
|
class Initializer
|
||||||
|
def load_environment
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# We overwrite the default log to be a directory up
|
||||||
|
module Rails
|
||||||
|
class Configuration
|
||||||
|
def default_log_path
|
||||||
|
File.join(root_path, "#{environment}.log")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# We then load it manually
|
||||||
|
PTK::Configuration.load :environment
|
|
@ -0,0 +1,148 @@
|
||||||
|
require 'singleton'
|
||||||
|
|
||||||
|
module PTK
|
||||||
|
|
||||||
|
class Configuration
|
||||||
|
class << self
|
||||||
|
def load(config, fatal = false)
|
||||||
|
if (file = PTK::PathSet.instance.send(config)) == :ignore then false
|
||||||
|
elsif File.exists?(file)
|
||||||
|
require file
|
||||||
|
true
|
||||||
|
elsif fatal then raise LoadError, "PTK could not find #{file}"
|
||||||
|
else
|
||||||
|
STDERR.puts "PTK::WARNING: could not find #{file}"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_path(config, fatal = false)
|
||||||
|
if (file = PTK::PathSet.instance.send(config)) == :ignore then false
|
||||||
|
elsif File.exists?(file) then file
|
||||||
|
elsif fatal then raise LoadError, "PTK could not find #{file}"
|
||||||
|
else
|
||||||
|
STDERR.puts "PTK::WARNING: could not find #{file}"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw
|
||||||
|
yield PTK::PathSet.instance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class PathSet
|
||||||
|
include Singleton
|
||||||
|
|
||||||
|
attr_accessor :ptk_prefix
|
||||||
|
attr_accessor :config
|
||||||
|
attr_accessor :fixtures
|
||||||
|
attr_accessor :environment
|
||||||
|
attr_accessor :schema
|
||||||
|
attr_accessor :database
|
||||||
|
attr_accessor :routes
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
self.ptk_prefix = 'ptk'
|
||||||
|
self.config = File.join(RAILS_ROOT, 'config')
|
||||||
|
self.fixtures = File.join(RAILS_ROOT, 'fixtures')
|
||||||
|
|
||||||
|
self.environment = File.join(self.config, 'environment.rb')
|
||||||
|
|
||||||
|
self.database = File.join(self.config, 'database.yml')
|
||||||
|
self.schema = File.join(self.config, 'schema.rb')
|
||||||
|
|
||||||
|
self.routes = File.join(self.config, 'routes.rb')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Initializer
|
||||||
|
|
||||||
|
# The init.rb in the root directory of the plugin will be loaded by default
|
||||||
|
attr_accessor :init
|
||||||
|
|
||||||
|
# The specific environmental frameworks of a plugin, such as needing the ActionController
|
||||||
|
# ActionMailer or ActiveRecord gems to be preloaded. A special requirement called
|
||||||
|
# 'environment' will load tests as though they were in the test environment of a normal
|
||||||
|
# Rails application.
|
||||||
|
attr_accessor :frameworks
|
||||||
|
def frameworks
|
||||||
|
[@frameworks].flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
# Suites are test extensions including assertions and various tools for easier testing
|
||||||
|
attr_accessor :suites
|
||||||
|
def suites
|
||||||
|
[@suites].flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
# A container for the PathSet instance
|
||||||
|
attr_reader :paths
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
self.init = true
|
||||||
|
self.frameworks = :none
|
||||||
|
self.suites = :all
|
||||||
|
@paths = PTK::PathSet.instance
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.run(command = :process)
|
||||||
|
initializer = PTK::Initializer.new
|
||||||
|
yield initializer if block_given?
|
||||||
|
initializer.send(command)
|
||||||
|
end
|
||||||
|
|
||||||
|
def process
|
||||||
|
initialize_frameworks
|
||||||
|
initialize_suites
|
||||||
|
initialize_plugin
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_frameworks
|
||||||
|
return if frameworks.include?(:none)
|
||||||
|
self.frameworks = [:rails] if frameworks.include?(:rails)
|
||||||
|
frameworks.each { |lib| require_ptk File.join('gem', lib.to_s) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_suites
|
||||||
|
return if suites.include?(:none)
|
||||||
|
self.suites = all_suites if suites.include?(:all)
|
||||||
|
suites.each { |lib| require_ptk File.join('suite', lib.to_s) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_plugin
|
||||||
|
return unless self.init
|
||||||
|
require File.join(RAILS_ROOT, '..', 'init')
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def all_suites
|
||||||
|
Dir.glob(File.join(RAILS_ROOT, 'lib', 'ptk', 'suite', '*.rb')).inject([]) do |a, file|
|
||||||
|
a << File.basename(file, '.rb').to_sym
|
||||||
|
a
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_ptk(library)
|
||||||
|
file = paths.ptk_prefix.empty? ? library : File.join(paths.ptk_prefix, library)
|
||||||
|
require file
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class LoadPath
|
||||||
|
|
||||||
|
def self.expand(file, *dirs)
|
||||||
|
File.join(*([File.expand_path(File.dirname(file))] << dirs))
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.add(*dirs)
|
||||||
|
path = File.expand_path(File.join(*dirs))
|
||||||
|
$:.unshift path
|
||||||
|
$:.uniq!
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
class Test::Unit::TestCase
|
||||||
|
|
||||||
|
# http://project.ioni.st/post/217#post-217
|
||||||
|
#
|
||||||
|
# def test_new_publication
|
||||||
|
# assert_difference(Publication, :count) do
|
||||||
|
# post :create, :publication => {...}
|
||||||
|
# # ...
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# modified by mabs29 to include arguments
|
||||||
|
def assert_difference(object, method = nil, difference = 1, *args)
|
||||||
|
initial_value = object.send(method, *args)
|
||||||
|
yield
|
||||||
|
assert_equal initial_value + difference, object.send(method, *args), "#{object}##{method}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_no_difference(object, method, *args, &block)
|
||||||
|
assert_difference object, method, 0, *args, &block
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
require 'action_controller/test_process'
|
||||||
|
require 'ujs/controller_methods'
|
||||||
|
|
||||||
|
class ControllerStub < ActionController::Base
|
||||||
|
def index
|
||||||
|
render :nothing => true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ControllerStub.send(:include, UJS::ControllerMethods)
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Do not comment out this line; it sets the RAILS_ROOT constant and load paths, not Rails itself
|
||||||
|
require File.join(File.dirname(__FILE__), 'lib', 'ptk', 'boot')
|
||||||
|
|
||||||
|
PTK::Initializer.run do |setup|
|
||||||
|
# You can also redefine the paths of certain directories and files, namely:
|
||||||
|
#setup.paths.config = File.join(RAILS_ROOT, 'config')
|
||||||
|
#setup.paths.fixtures = File.join(RAILS_ROOT, 'fixtures')
|
||||||
|
|
||||||
|
#setup.paths.database = File.join(setup.paths.config, 'database.yml')
|
||||||
|
#setup.paths.schema = File.join(setup.paths.config, 'schema.rb')
|
||||||
|
#setup.paths.routes = File.join(setup.paths.config, 'routes.rb')
|
||||||
|
#setup.paths.environment = File.join(setup.paths.config, 'environment.rb')
|
||||||
|
|
||||||
|
# If any of these paths are set to ':ignore', no warnings will appear if they are missing.
|
||||||
|
|
||||||
|
# Frameworks are the gems from Rails which you need PTK to load for your plugin.
|
||||||
|
# The special :rails framework creates a fully-fledged Rails environment and requires
|
||||||
|
# the environment.rb file.
|
||||||
|
# Valid options are: :action_controller, :action_mailer, :active_record, :rails
|
||||||
|
setup.frameworks = :active_record # :active_record, :action_controller
|
||||||
|
|
||||||
|
# Extra libraries of assertions and other common methods that provide more testing
|
||||||
|
# utilities. To hand-pick which suites you want, uncomment the below
|
||||||
|
#setup.suites = :difference
|
||||||
|
|
||||||
|
# If for some particular reason you do not want your plugin's init to be called
|
||||||
|
# at the end of this block, uncomment the below:
|
||||||
|
setup.init = true
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
# Uninstall hook code here
|
Reference in New Issue