merb_auth_slice_multisite 0.8.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|