merb_auth_slice_multisite 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +147 -0
- data/VERSION.yml +4 -0
- data/app/controllers/application.rb +5 -0
- data/app/controllers/exceptions.rb +33 -0
- data/app/controllers/passwords.rb +29 -0
- data/app/controllers/sessions.rb +56 -0
- data/app/helpers/application_helper.rb +64 -0
- data/app/mailers/send_password_mailer.rb +11 -0
- data/app/mailers/views/send_password_mailer/send_password.text.erb +3 -0
- data/app/models/site.rb +26 -0
- data/app/views/exceptions/unauthenticated.html.erb +61 -0
- data/app/views/layout/merb_auth_slice_multisite.html.erb +16 -0
- data/config/database.yml +33 -0
- data/config/dependencies.rb +33 -0
- data/config/init.rb +84 -0
- data/config/router.rb +5 -0
- data/lib/merb-auth-more/strategies/multisite/multisite_password_form.rb +77 -0
- data/lib/merb-auth-remember-me/mixins/authenticated_user.rb +97 -0
- data/lib/merb-auth-remember-me/mixins/authenticated_user/dm_authenticated_user.rb +17 -0
- data/lib/merb-auth-remember-me/strategies/remember_me.rb +55 -0
- data/lib/merb_auth_slice_multisite.rb +107 -0
- data/lib/merb_auth_slice_multisite/merbtasks.rb +103 -0
- data/lib/merb_auth_slice_multisite/mixins/user_belongs_to_site.rb +63 -0
- data/lib/merb_auth_slice_multisite/mixins/user_belongs_to_site/dm_user_belongs_to_site.rb +28 -0
- data/lib/merb_auth_slice_multisite/slicetasks.rb +18 -0
- data/lib/merb_auth_slice_multisite/spectasks.rb +54 -0
- data/public/javascripts/master.js +0 -0
- data/public/stylesheets/master.css +2 -0
- data/spec/mailers/send_password_mailer_spec.rb +47 -0
- data/spec/mixins/authenticated_user_spec.rb +33 -0
- data/spec/mixins/user_belongs_to_site_spec.rb +56 -0
- data/spec/models/site_spec.rb +56 -0
- data/spec/spec_helper.rb +101 -0
- data/spec/strategies/remember_me_spec.rb +62 -0
- data/stubs/app/controllers/sessions.rb +19 -0
- data/stubs/app/views/exceptions/unauthenticated.html.erb +61 -0
- metadata +91 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :merb_auth_slice_multisite do
|
3
|
+
|
4
|
+
desc "Install MerbAuthSliceMultisite"
|
5
|
+
task :install => [:preflight, :setup_directories, :copy_assets, :migrate]
|
6
|
+
|
7
|
+
desc "Test for any dependencies"
|
8
|
+
task :preflight do # see slicetasks.rb
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Setup directories"
|
12
|
+
task :setup_directories do
|
13
|
+
puts "Creating directories for host application"
|
14
|
+
MerbAuthSliceMultisite.mirrored_components.each do |type|
|
15
|
+
if File.directory?(MerbAuthSliceMultisite.dir_for(type))
|
16
|
+
if !File.directory?(dst_path = MerbAuthSliceMultisite.app_dir_for(type))
|
17
|
+
relative_path = dst_path.relative_path_from(Merb.root)
|
18
|
+
puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}"
|
19
|
+
mkdir_p(dst_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Copy stub files to host application"
|
26
|
+
task :stubs do
|
27
|
+
puts "Copying stubs for MerbAuthSliceMultisite - resolves any collisions"
|
28
|
+
copied, preserved = MerbAuthSliceMultisite.mirror_stubs!
|
29
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
30
|
+
copied.each { |f| puts "- copied #{f}" }
|
31
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Copy stub files and views to host application"
|
35
|
+
task :patch => [ "stubs", "freeze:views" ]
|
36
|
+
|
37
|
+
desc "Copy public assets to host application"
|
38
|
+
task :copy_assets do
|
39
|
+
puts "Copying assets for MerbAuthSliceMultisite - resolves any collisions"
|
40
|
+
copied, preserved = MerbAuthSliceMultisite.mirror_public!
|
41
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
42
|
+
copied.each { |f| puts "- copied #{f}" }
|
43
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Migrate the database"
|
47
|
+
task :migrate do # see slicetasks.rb
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Freeze MerbAuthSliceMultisite into your app (only merb_auth_slice_multisite/app)"
|
51
|
+
task :freeze => [ "freeze:app" ]
|
52
|
+
|
53
|
+
namespace :freeze do
|
54
|
+
|
55
|
+
desc "Freezes MerbAuthSliceMultisite by installing the gem into application/gems"
|
56
|
+
task :gem do
|
57
|
+
ENV["GEM"] ||= "merb_auth_slice_multisite"
|
58
|
+
Rake::Task['slices:install_as_gem'].invoke
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "Freezes MerbAuthSliceMultisite by copying all files from merb_auth_slice_multisite/app to your application"
|
62
|
+
task :app do
|
63
|
+
puts "Copying all merb_auth_slice_multisite/app files to your application - resolves any collisions"
|
64
|
+
copied, preserved = MerbAuthSliceMultisite.mirror_app!
|
65
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
66
|
+
copied.each { |f| puts "- copied #{f}" }
|
67
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "Freeze all views into your application for easy modification"
|
71
|
+
task :views do
|
72
|
+
puts "Copying all view templates to your application - resolves any collisions"
|
73
|
+
copied, preserved = MerbAuthSliceMultisite.mirror_files_for :view
|
74
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
75
|
+
copied.each { |f| puts "- copied #{f}" }
|
76
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
77
|
+
end
|
78
|
+
|
79
|
+
desc "Freeze all models into your application for easy modification"
|
80
|
+
task :models do
|
81
|
+
puts "Copying all models to your application - resolves any collisions"
|
82
|
+
copied, preserved = MerbAuthSliceMultisite.mirror_files_for :model
|
83
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
84
|
+
copied.each { |f| puts "- copied #{f}" }
|
85
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "Freezes MerbAuthSliceMultisite as a gem and copies over merb_auth_slice_multisite/app"
|
89
|
+
task :app_with_gem => [:gem, :app]
|
90
|
+
|
91
|
+
desc "Freezes MerbAuthSliceMultisite by unpacking all files into your application"
|
92
|
+
task :unpack do
|
93
|
+
puts "Unpacking MerbAuthSliceMultisite files to your application - resolves any collisions"
|
94
|
+
copied, preserved = MerbAuthSliceMultisite.unpack_slice!
|
95
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
96
|
+
copied.each { |f| puts "- copied #{f}" }
|
97
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Merb
|
2
|
+
class Authentication
|
3
|
+
module Mixins
|
4
|
+
# This mixin provides basic user authentication by a site_id.
|
5
|
+
#
|
6
|
+
# Added properties:
|
7
|
+
# :site_id, Integer
|
8
|
+
#
|
9
|
+
# Added Relationships
|
10
|
+
# belongs_to :site
|
11
|
+
#
|
12
|
+
# Added Validations
|
13
|
+
# validates_present :site_id
|
14
|
+
# validates_is_unique :login, :scope => :site_id
|
15
|
+
# validates_is_unique :email, :scope => :site_id
|
16
|
+
#
|
17
|
+
# To use it simply require it and include it into your user class.
|
18
|
+
#
|
19
|
+
# class User
|
20
|
+
# include Authentication::Mixins::UserBelongsToSite
|
21
|
+
#
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
#
|
25
|
+
# OR I RECOMMEND recommend that you put it in your application under merb/merb-auth/setup.rb so it looks like this:
|
26
|
+
# # require 'merb-auth-more/mixins/salted_user'
|
27
|
+
# # Merb::Authentication.user_class.class_eval{
|
28
|
+
# # include Merb::Authentication::Mixins::SaltedUser
|
29
|
+
# # include Merb::Authentication::Mixins::UserBelongsToSite
|
30
|
+
# # }
|
31
|
+
#
|
32
|
+
module UserBelongsToSite
|
33
|
+
def self.included(base)
|
34
|
+
base.class_eval do
|
35
|
+
include Merb::Authentication::Mixins::UserBelongsToSite::InstanceMethods
|
36
|
+
extend Merb::Authentication::Mixins::UserBelongsToSite::ClassMethods
|
37
|
+
|
38
|
+
path = File.expand_path(File.dirname(__FILE__)) / "user_belongs_to_site"
|
39
|
+
if defined?(DataMapper) && DataMapper::Resource > self
|
40
|
+
require path / "dm_user_belongs_to_site"
|
41
|
+
extend(Merb::Authentication::Mixins::UserBelongsToSite::DMClassMethods)
|
42
|
+
elsif defined?(ActiveRecord) && ancestors.include?(ActiveRecord::Base)
|
43
|
+
require path / "ar_user_belongs_to_site"
|
44
|
+
extend(Merb::Authentication::Mixins::UserBelongsToSite::ARClassMethods)
|
45
|
+
elsif defined?(Sequel) && ancestors.include?(Sequel::Model)
|
46
|
+
require path / "sq_user_belongs_to_site"
|
47
|
+
extend(Merb::Authentication::Mixins::UserBelongsToSite::SQClassMethods)
|
48
|
+
end
|
49
|
+
|
50
|
+
end # base.class_eval
|
51
|
+
end # self.included
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
#
|
55
|
+
end # ClassMethods
|
56
|
+
|
57
|
+
module InstanceMethods
|
58
|
+
#
|
59
|
+
end # InstanceMethods
|
60
|
+
end # UserBelongsToSite
|
61
|
+
end # Mixins
|
62
|
+
end # Authentication
|
63
|
+
end # Merb
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Merb
|
2
|
+
class Authentication
|
3
|
+
module Mixins
|
4
|
+
module UserBelongsToSite
|
5
|
+
module DMClassMethods
|
6
|
+
def self.extended(base)
|
7
|
+
base.class_eval do
|
8
|
+
# Schema
|
9
|
+
property :site_id, Integer
|
10
|
+
# Validations
|
11
|
+
validates_present :site_id
|
12
|
+
validates_is_unique :login, :scope => :site_id
|
13
|
+
validates_is_unique :email, :scope => :site_id
|
14
|
+
# Relationships/Associations
|
15
|
+
belongs_to :site
|
16
|
+
end # base.class_eval
|
17
|
+
end # self.extended
|
18
|
+
end # DMClassMethods
|
19
|
+
end # UserBelongsToSite
|
20
|
+
end # Mixins
|
21
|
+
end # Authentication
|
22
|
+
|
23
|
+
class Request
|
24
|
+
def first_subdomain
|
25
|
+
subdomains.first #subdomains is a request variable array so we take the first array entry
|
26
|
+
end #first_subdomain
|
27
|
+
end #Request
|
28
|
+
end #Merb
|
@@ -0,0 +1,18 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :merb_auth_slice_multisite do
|
3
|
+
|
4
|
+
# add your own merb_auth_slice_multisite tasks here
|
5
|
+
|
6
|
+
# implement this to test for structural/code dependencies
|
7
|
+
# like certain directories or availability of other files
|
8
|
+
desc "Test for any dependencies"
|
9
|
+
task :preflight do
|
10
|
+
end
|
11
|
+
|
12
|
+
# implement this to perform any database related setup steps
|
13
|
+
desc "Migrate the database"
|
14
|
+
task :migrate do
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec/rake/spectask"
|
2
|
+
namespace :slices do
|
3
|
+
namespace :merb_auth_slice_multisite do
|
4
|
+
|
5
|
+
desc "Run slice specs within the host application context"
|
6
|
+
task :spec => [ "spec:explain", "spec:default" ]
|
7
|
+
|
8
|
+
namespace :spec do
|
9
|
+
|
10
|
+
slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
11
|
+
|
12
|
+
task :explain do
|
13
|
+
puts "\nNote: By running MerbAuthSliceMultisite specs inside the application context any\n" +
|
14
|
+
"overrides could break existing specs. This isn't always a problem,\n" +
|
15
|
+
"especially in the case of views. Use these spec tasks to check how\n" +
|
16
|
+
"well your application conforms to the original slice implementation."
|
17
|
+
end
|
18
|
+
|
19
|
+
Spec::Rake::SpecTask.new('default') do |t|
|
20
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
21
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
|
25
|
+
Spec::Rake::SpecTask.new('model') do |t|
|
26
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
27
|
+
if(ENV['MODEL'])
|
28
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
|
29
|
+
else
|
30
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Run all request specs, run a spec for a specific request with REQUEST=MyRequest"
|
35
|
+
Spec::Rake::SpecTask.new('request') do |t|
|
36
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
37
|
+
if(ENV['REQUEST'])
|
38
|
+
t.spec_files = Dir["#{slice_root}/spec/requests/**/#{ENV['REQUEST']}_spec.rb"].sort
|
39
|
+
else
|
40
|
+
t.spec_files = Dir["#{slice_root}/spec/requests/**/*_spec.rb"].sort
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Run all specs and output the result in html"
|
45
|
+
Spec::Rake::SpecTask.new('html') do |t|
|
46
|
+
t.spec_opts = ["--format", "html"]
|
47
|
+
t.libs = ['lib', 'server/lib' ]
|
48
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
File without changes
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "SendPasswordMailer" do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
Merb::Router.prepare { add_slice(:merb_auth_slice_password_reset)}
|
7
|
+
User.auto_migrate!
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:all) do
|
11
|
+
Merb::Router.reset!
|
12
|
+
end
|
13
|
+
|
14
|
+
describe MerbAuthSliceMultisite::SendPasswordMailer do
|
15
|
+
|
16
|
+
def deliver(action, mail_opts= {},opts = {})
|
17
|
+
MerbAuthSliceMultisite::SendPasswordMailer.dispatch_and_deliver action, mail_opts, opts
|
18
|
+
@last_delivered_mail = Merb::Mailer.deliveries.last
|
19
|
+
end
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
Merb::Mailer.deliveries.clear
|
23
|
+
Site.all.destroy!
|
24
|
+
User.all.destroy!
|
25
|
+
@site = Site.create(valid_site_attributes)
|
26
|
+
@user = @site.users.build(:email => "homer@simpsons.com", :login => "homer", :password => "donuts" )
|
27
|
+
@mailer_params = { :from => "info@mysite.com", :to => @user.email, :subject => "Welcome to MySite.com" }
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should send mail to homer@simpsons.com for the send password email" do
|
31
|
+
deliver(:send_password, @mailer_params, :user => @user)
|
32
|
+
@last_delivered_mail.assigns(:headers).should include("to: homer@simpsons.com")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should send the mail from 'info@mysite.com' for the the send password email" do
|
36
|
+
deliver(:send_password, @mailer_params, :user => @user)
|
37
|
+
@last_delivered_mail.assigns(:headers).should include("from: info@mysite.com")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should have the link to the subdomain site" do
|
41
|
+
deliver(:send_password, @mailer_params, :user => @user)
|
42
|
+
@last_delivered_mail.text.should include("http://#{@user.site.subdomain}.#{Merb::Slices::config[:merb_auth_slice_multisite][:domain] = "example.com"}")
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "Authenticated user" do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
@user = User.new(valid_user_attributes)
|
7
|
+
@user.remember_token_expires_at.should be_nil
|
8
|
+
@user.remember_token.should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should add the 'remember_token_expires_at' property to the user model" do
|
12
|
+
@user.should respond_to(:remember_token_expires_at)
|
13
|
+
@user.should respond_to(:remember_token_expires_at=)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should add the 'remember_token' property to the user model" do
|
17
|
+
@user.should respond_to(:remember_token)
|
18
|
+
@user.should respond_to(:remember_token=)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should save token and expires_at" do
|
22
|
+
@user.remember_me
|
23
|
+
@user.remember_token_expires_at.should_not be_nil
|
24
|
+
@user.remember_token.should_not be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should save expires_at as 2 weeks later" do
|
28
|
+
@user.remember_me
|
29
|
+
@user.remember_token_expires_at.should eql((Time.now+2.weeks).to_datetime)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "User Belongs To Site" do
|
4
|
+
before(:all) do
|
5
|
+
Merb::Router.prepare { add_slice(:merb_auth_slice_multisite)}
|
6
|
+
end
|
7
|
+
|
8
|
+
after(:all) do
|
9
|
+
Merb::Router.reset!
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "UserBelongsToSite Mixin" do
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
User.all.destroy!
|
16
|
+
@user = User.new(valid_user_attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should add the 'site_id' property to the user model" do
|
20
|
+
@user.should respond_to(:site_id)
|
21
|
+
@user.should respond_to(:site_id=)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be invalid when site_id is nil" do
|
25
|
+
@user.site_id = nil
|
26
|
+
@user.save
|
27
|
+
@user.should_not be_valid
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have a unique login and email" do
|
31
|
+
@user.save
|
32
|
+
@user.should be_valid
|
33
|
+
|
34
|
+
# not unique
|
35
|
+
@user2 = User.new(valid_user_attributes)
|
36
|
+
@user2.should_not be_valid
|
37
|
+
|
38
|
+
# unique email but duplicate login should fail
|
39
|
+
@user2 = User.new(valid_user_attributes)
|
40
|
+
@user2.email = "different@example.org"
|
41
|
+
@user2.should_not be_valid
|
42
|
+
|
43
|
+
# unique login but duplicate email should fail
|
44
|
+
@user2 = User.new(valid_user_attributes)
|
45
|
+
@user2.login = "different"
|
46
|
+
@user2.should_not be_valid
|
47
|
+
|
48
|
+
# unique login & unique email should be valid
|
49
|
+
@user2 = User.new(valid_user_attributes)
|
50
|
+
@user2.login = "unique"
|
51
|
+
@user2.email = "unique@example.org"
|
52
|
+
@user2.should be_valid
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__), '..', "spec_helper" )
|
2
|
+
|
3
|
+
describe Site do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Site.all.destroy!
|
7
|
+
@site = Site.new(valid_site_attributes)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be valid when new" do
|
11
|
+
@site.should be_valid
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be invalid when subdomain is not unique" do
|
15
|
+
@site.save
|
16
|
+
@site.should be_valid
|
17
|
+
|
18
|
+
@site2 = Site.new(valid_site_attributes)
|
19
|
+
@site2.save
|
20
|
+
@site2.should_not be_valid
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not allow use of reserved subdomains" do
|
24
|
+
Site::ReservedSubdomains.each do |reserved|
|
25
|
+
@site.subdomain = reserved
|
26
|
+
@site.save
|
27
|
+
@site.should_not be_valid
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should only allow text and numbers as a subdomain and should not allow any odd characters or whitespace" do
|
32
|
+
@site.save
|
33
|
+
@site.subdomain.should match(/^[a-zA-Z0-9\-]*?$/)
|
34
|
+
|
35
|
+
@site2 = Site.new(valid_site_attributes)
|
36
|
+
@site2.subdomain = "strange-but-legal"
|
37
|
+
@site2.save
|
38
|
+
@site2.subdomain.should match(/^[a-zA-Z0-9\-]*?$/)
|
39
|
+
|
40
|
+
@site3 = Site.new(valid_site_attributes)
|
41
|
+
@site3.subdomain = "i!!egal@ch@racters"
|
42
|
+
@site3.save
|
43
|
+
@site3.subdomain.should_not match(/^[a-zA-Z0-9\-]*?$/)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should respond to users" do
|
47
|
+
@site.save
|
48
|
+
@site.should respond_to(:users)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should have a full created_at field after save" do
|
52
|
+
@site.save
|
53
|
+
@site.created_at.should_not be_nil
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|