jsmestad-merb-auth-slice-activation 1.0.0

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.
Files changed (30) hide show
  1. data/README.textile +205 -0
  2. data/Rakefile +57 -0
  3. data/app/controllers/activations.rb +24 -0
  4. data/app/controllers/application.rb +5 -0
  5. data/app/helpers/application_helper.rb +64 -0
  6. data/app/helpers/mailer_helper.rb +29 -0
  7. data/app/mailers/activation_mailer.rb +18 -0
  8. data/app/mailers/views/activation_mailer/activation.text.erb +1 -0
  9. data/app/mailers/views/activation_mailer/signup.text.erb +7 -0
  10. data/app/views/layout/merb_auth_slice_activation.html.erb +16 -0
  11. data/config/init.rb +84 -0
  12. data/lib/merb-auth-slice-activation.rb +91 -0
  13. data/lib/merb-auth-slice-activation/merbtasks.rb +112 -0
  14. data/lib/merb-auth-slice-activation/mixins/activated_user.rb +118 -0
  15. data/lib/merb-auth-slice-activation/mixins/activated_user/ar_activated_user.rb +21 -0
  16. data/lib/merb-auth-slice-activation/mixins/activated_user/dm_activated_user.rb +24 -0
  17. data/lib/merb-auth-slice-activation/mixins/activated_user/sq_activated_user.rb +23 -0
  18. data/lib/merb-auth-slice-activation/slicetasks.rb +18 -0
  19. data/lib/merb-auth-slice-activation/spectasks.rb +75 -0
  20. data/merb-auth-slice-activation.gemspec +48 -0
  21. data/public/javascripts/master.js +0 -0
  22. data/public/stylesheets/master.css +2 -0
  23. data/spec/controllers/activations_spec.rb +83 -0
  24. data/spec/mailers/activation_mailer_spec.rb +68 -0
  25. data/spec/mixins/activated_user_spec.rb +122 -0
  26. data/spec/spec_helper.rb +61 -0
  27. data/stubs/app/controllers/activations.rb +8 -0
  28. data/stubs/app/mailers/views/activation_mailer/activation.text.erb +1 -0
  29. data/stubs/app/mailers/views/activation_mailer/signup.text.erb +7 -0
  30. metadata +99 -0
@@ -0,0 +1,91 @@
1
+ if defined?(Merb::Plugins)
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+
5
+ load_dependency 'merb-slices'
6
+ load_dependency 'merb-auth-core'
7
+ load_dependency 'merb-auth-more'
8
+ load_dependency 'merb-mailer'
9
+ require(File.expand_path(File.dirname(__FILE__) / "merb-auth-slice-activation" / "mixins") / "activated_user")
10
+
11
+ Merb::Plugins.add_rakefiles "merb-auth-slice-activation/merbtasks", "merb-auth-slice-activation/slicetasks", "merb-auth-slice-activation/spectasks"
12
+
13
+ # Register the Slice for the current host application
14
+ Merb::Slices::register(__FILE__)
15
+
16
+ # Slice configuration - set this in a before_app_loads callback.
17
+ # By default a Slice uses its own layout, so you can swicht to
18
+ # the main application layout or no layout at all if needed.
19
+ #
20
+ # Configuration options:
21
+ # :layout - the layout to use; defaults to :merb-auth-slice-activation
22
+ # :mirror - which path component types to use on copy operations; defaults to all
23
+ Merb::Slices::config[:merb_auth_slice_activation][:layout] ||= :application
24
+
25
+ # All Slice code is expected to be namespaced inside a module
26
+ module MerbAuthSliceActivation
27
+
28
+ # Slice metadata
29
+ self.description = "MerbAuthSliceActivation is a merb slice that adds basic activation for merb-auth-based merb applications."
30
+ self.version = "1.0"
31
+ self.author = "Daniel Neighman, Christian Kebekus"
32
+
33
+ # Stub classes loaded hook - runs before LoadClasses BootLoader
34
+ # right after a slice's classes have been loaded internally.
35
+ def self.loaded
36
+ end
37
+
38
+ # Initialization hook - runs before AfterAppLoads BootLoader
39
+ def self.init
40
+ # Actually check if the user is active
41
+ ::Merb::Authentication.after_authentication do |user, *rest|
42
+ if user.respond_to?(:active?)
43
+ user.active? ? user : nil
44
+ else
45
+ user
46
+ end
47
+ end
48
+ end
49
+
50
+ # Activation hook - runs after AfterAppLoads BootLoader
51
+ def self.activate
52
+ end
53
+
54
+ # Deactivation hook - triggered by Merb::Slices.deactivate(MerbAuthSliceActivation)
55
+ def self.deactivate
56
+ end
57
+
58
+ # Setup routes inside the host application
59
+ #
60
+ # @param scope<Merb::Router::Behaviour>
61
+ # Routes will be added within this scope (namespace). In fact, any
62
+ # router behaviour is a valid namespace, so you can attach
63
+ # routes at any level of your router setup.
64
+ #
65
+ # @note prefix your named routes with :merb_auth_slice_activation_
66
+ # to avoid potential conflicts with global named routes.
67
+ def self.setup_router(scope)
68
+ scope.match("/activate/:activation_code").to(:controller => "activations", :action => "activate").name(:activate)
69
+ end
70
+
71
+ end
72
+
73
+ # Setup the slice layout for MerbAuthSliceActivation
74
+ #
75
+ # Use MerbAuthSliceActivation.push_path and MerbAuthSliceActivation.push_app_path
76
+ # to set paths to merb-auth-slice-activation-level and app-level paths. Example:
77
+ #
78
+ # MerbAuthSliceActivation.push_path(:application, MerbAuthSliceActivation.root)
79
+ # MerbAuthSliceActivation.push_app_path(:application, Merb.root / 'slices' / 'merb-auth-slice-activation')
80
+ # ...
81
+ #
82
+ # Any component path that hasn't been set will default to MerbAuthSliceActivation.root
83
+ #
84
+ # Or just call setup_default_structure! to setup a basic Merb MVC structure.
85
+ MerbAuthSliceActivation.setup_default_structure!
86
+ MaSA = MerbAuthSliceActivation unless defined?(MaSA)
87
+
88
+ # Add dependencies for other MerbAuthSliceActivation classes below. Example:
89
+ # dependency "merb-auth-slice-activation/other"
90
+
91
+ end
@@ -0,0 +1,112 @@
1
+ namespace :slices do
2
+ namespace :merb_auth_slice_activation do
3
+
4
+ desc "Install MerbAuthSliceActivation"
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
+ MerbAuthSliceActivation.mirrored_components.each do |type|
15
+ if File.directory?(MerbAuthSliceActivation.dir_for(type))
16
+ if !File.directory?(dst_path = MerbAuthSliceActivation.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 MerbAuthSliceActivation - resolves any collisions"
28
+ copied, preserved = MerbAuthSliceActivation.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 MerbAuthSliceActivation - resolves any collisions"
40
+ copied, preserved = MerbAuthSliceActivation.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 MerbAuthSliceActivation into your app (only merb-auth-slice-activation/app)"
51
+ task :freeze => [ "freeze:app" ]
52
+
53
+ namespace :freeze do
54
+
55
+ desc "Freezes MerbAuthSliceActivation by installing the gem into application/gems"
56
+ task :gem do
57
+ ENV["GEM"] ||= "merb-auth-slice-activation"
58
+ Rake::Task['slices:install_as_gem'].invoke
59
+ end
60
+
61
+ desc "Freezes MerbAuthSliceActivation by copying all files from merb-auth-slice-activation/app to your application"
62
+ task :app do
63
+ puts "Copying all merb-auth-slice-activation/app files to your application - resolves any collisions"
64
+ copied, preserved = MerbAuthSliceActivation.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 = MerbAuthSliceActivation.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 mailers into your application for easy modification"
80
+ task :mailers do
81
+ puts "Copying all mailer templates to your application - resolves any collisions"
82
+ copied, preserved = MerbAuthSliceActivation.mirror_files_for :mailer
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 "Freeze all models into your application for easy modification"
89
+ task :models do
90
+ puts "Copying all models to your application - resolves any collisions"
91
+ copied, preserved = MerbAuthSliceActivation.mirror_files_for :model
92
+ puts "- no files to copy" if copied.empty? && preserved.empty?
93
+ copied.each { |f| puts "- copied #{f}" }
94
+ preserved.each { |f| puts "! preserved override as #{f}" }
95
+ end
96
+
97
+ desc "Freezes MerbAuthSliceActivation as a gem and copies over merb-auth-slice-activation/app"
98
+ task :app_with_gem => [:gem, :app]
99
+
100
+ desc "Freezes MerbAuthSliceActivation by unpacking all files into your application"
101
+ task :unpack do
102
+ puts "Unpacking MerbAuthSliceActivation files to your application - resolves any collisions"
103
+ copied, preserved = MerbAuthSliceActivation.unpack_slice!
104
+ puts "- no files to copy" if copied.empty? && preserved.empty?
105
+ copied.each { |f| puts "- copied #{f}" }
106
+ preserved.each { |f| puts "! preserved override as #{f}" }
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,118 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ # This mixin provides basic user activation.
5
+ #
6
+ # Added properties:
7
+ # :activated_at, DateTime
8
+ # :activation_code, String
9
+ #
10
+ # To use it simply require it and include it into your user class.
11
+ #
12
+ # class User
13
+ # include Authentication::Mixins::ActivatedUser
14
+ #
15
+ # end
16
+ #
17
+ module ActivatedUser
18
+ def self.included(base)
19
+ base.class_eval do
20
+ include Merb::Authentication::Mixins::ActivatedUser::InstanceMethods
21
+ extend Merb::Authentication::Mixins::ActivatedUser::ClassMethods
22
+
23
+ path = File.expand_path(File.dirname(__FILE__)) / "activated_user"
24
+ if defined?(DataMapper) && DataMapper::Resource > self
25
+ require path / "dm_activated_user"
26
+ extend(Merb::Authentication::Mixins::ActivatedUser::DMClassMethods)
27
+ elsif defined?(ActiveRecord) && ancestors.include?(ActiveRecord::Base)
28
+ require path / "ar_activated_user"
29
+ extend(Merb::Authentication::Mixins::ActivatedUser::ARClassMethods)
30
+ elsif defined?(Sequel) && ancestors.include?(Sequel::Model)
31
+ require path / "sq_activated_user"
32
+ extend(Merb::Authentication::Mixins::ActivatedUser::SQClassMethods)
33
+ end
34
+
35
+ end # base.class_eval
36
+ end # self.included
37
+
38
+
39
+ module ClassMethods
40
+ # Create random key.
41
+ #
42
+ # ==== Returns
43
+ # String:: The generated key
44
+ def make_key
45
+ Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
46
+ end
47
+ end # ClassMethods
48
+
49
+ module InstanceMethods
50
+
51
+ # Activates and saves the user.
52
+ def activate!
53
+ self.reload unless self.new_record? # Make sure the model is up to speed before we try to save it
54
+ set_activated_data!
55
+ self.save
56
+
57
+ # send mail for activation
58
+ send_activation_notification
59
+ end
60
+
61
+ # Checks if the user has just been activated. Where 'just' means within the current request.
62
+ # Note that a user can be activate, but the method returns +false+!
63
+ #
64
+ # ==== Returns
65
+ # Boolean:: +true+ is the user has been activated, otherwise +false+.
66
+ def recently_activated?
67
+ @activated
68
+ end
69
+
70
+ # Checks to see if a user is active
71
+ def active?
72
+ return false if self.new_record?
73
+ !! activation_code.nil?
74
+ end
75
+
76
+ alias_method :activated?, :active?
77
+
78
+ # Creates and sets the activation code for the user.
79
+ #
80
+ # ==== Returns
81
+ # String:: The activation code.
82
+ def make_activation_code
83
+ self.activation_code = self.class.make_key
84
+ end
85
+
86
+ # Sends out the activation notification.
87
+ # Used 'Welcome' as subject if +MaSA[:activation_subject]+ is not set.
88
+ def send_activation_notification
89
+ deliver_activation_email(:activation, :subject => (MaSA[:welcome_subject] || "Welcome" ))
90
+ end
91
+
92
+ # Sends out the signup notification.
93
+ # Used 'Please Activate Your Account' as subject if +MaSA[:activation_subject]+ is not set.
94
+ def send_signup_notification
95
+ deliver_activation_email(:signup, :subject => (MaSA[:activation_subject] || "Please Activate Your Account") )
96
+ end
97
+
98
+ private
99
+
100
+ # Helper method delivering the email.
101
+ def deliver_activation_email(action, params)
102
+ from = MaSA[:from_email]
103
+ raise "No :from_email option set for Merb::Slices::config[:merb_auth_slice_activation][:from_email]" unless from
104
+ MaSA::ActivationMailer.dispatch_and_deliver(action, params.merge(:from => from, :to => self.email), :user => self)
105
+ end
106
+
107
+ def set_activated_data!
108
+ @activated = true
109
+ self.activated_at = DateTime.now
110
+ self.activation_code = nil
111
+ true
112
+ end
113
+
114
+ end # InstanceMethods
115
+ end # ActivatedUser
116
+ end # Mixins
117
+ end # Authentication
118
+ end # Merb
@@ -0,0 +1,21 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module ActivatedUser
5
+ module ARClassMethods
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ before :create, :make_activation_code
9
+ after :create, :send_signup_notification
10
+ end # base.class_eval
11
+
12
+ def find_with_activation_code(ac)
13
+ find(:first, :conditions => ["activation_code =?", ac])
14
+ end
15
+
16
+ end # self.extended
17
+ end # ARClassMethods
18
+ end # ActivatedUser
19
+ end # Mixins
20
+ end # Authentication
21
+ end # Merb
@@ -0,0 +1,24 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module ActivatedUser
5
+ module DMClassMethods
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ property :activated_at, DateTime
9
+ property :activation_code, String
10
+
11
+ before :create, :make_activation_code
12
+ after :create, :send_signup_notification
13
+ end # base.class_eval
14
+
15
+ def find_with_activation_code(ac)
16
+ first(:activation_code => ac)
17
+ end
18
+
19
+ end # self.extended
20
+ end # DMClassMethods
21
+ end # ActivatedUser
22
+ end # Mixins
23
+ end # Authentication
24
+ end #Merb
@@ -0,0 +1,23 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module ActivatedUser
5
+ module SQClassMethods
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ before_save :make_activation_code
9
+ after_save :send_signup_notification
10
+ end # base.class_eval
11
+
12
+ def find_with_activation_code(ac)
13
+ self[:activation_code => ac]
14
+ end
15
+ end # self.extended
16
+ end # SQClassMethods
17
+ module SQInstanceMethods
18
+ end # SQInstanceMethods
19
+
20
+ end # ActivatedUser
21
+ end # Mixins
22
+ end # Authentication
23
+ end # Merb
@@ -0,0 +1,18 @@
1
+ namespace :slices do
2
+ namespace :merb_auth_slice_activation do
3
+
4
+ # add your own merb-auth-slice-activation 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,75 @@
1
+ namespace :slices do
2
+ namespace :merb_auth_slice_activation do
3
+
4
+ desc "Run slice specs within the host application context"
5
+ task :spec => [ "spec:explain", "spec:default" ]
6
+
7
+ namespace :spec do
8
+
9
+ slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
10
+
11
+ task :explain do
12
+ puts "\nNote: By running MerbAuthSliceActivation specs inside the application context any\n" +
13
+ "overrides could break existing specs. This isn't always a problem,\n" +
14
+ "especially in the case of views. Use these spec tasks to check how\n" +
15
+ "well your application conforms to the original slice implementation."
16
+ end
17
+
18
+ Spec::Rake::SpecTask.new('default') do |t|
19
+ t.spec_opts = ["--format", "specdoc", "--colour"]
20
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
21
+ end
22
+
23
+ desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
24
+ Spec::Rake::SpecTask.new('model') do |t|
25
+ t.spec_opts = ["--format", "specdoc", "--colour"]
26
+ if(ENV['MODEL'])
27
+ t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
28
+ else
29
+ t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
30
+ end
31
+ end
32
+
33
+ desc "Run all controller specs, run a spec for a specific Controller with CONTROLLER=MyController"
34
+ Spec::Rake::SpecTask.new('controller') do |t|
35
+ t.spec_opts = ["--format", "specdoc", "--colour"]
36
+ if(ENV['CONTROLLER'])
37
+ t.spec_files = Dir["#{slice_root}/spec/controllers/**/#{ENV['CONTROLLER']}_spec.rb"].sort
38
+ else
39
+ t.spec_files = Dir["#{slice_root}/spec/controllers/**/*_spec.rb"].sort
40
+ end
41
+ end
42
+
43
+ desc "Run all view specs, run specs for a specific controller (and view) with CONTROLLER=MyController (VIEW=MyView)"
44
+ Spec::Rake::SpecTask.new('view') do |t|
45
+ t.spec_opts = ["--format", "specdoc", "--colour"]
46
+ if(ENV['CONTROLLER'] and ENV['VIEW'])
47
+ t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/#{ENV['VIEW']}*_spec.rb"].sort
48
+ elsif(ENV['CONTROLLER'])
49
+ t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/*_spec.rb"].sort
50
+ else
51
+ t.spec_files = Dir["#{slice_root}/spec/views/**/*_spec.rb"].sort
52
+ end
53
+ end
54
+
55
+ desc "Run all mailer specs, run a spec for a specific Mailer with MAILER=MyMailer"
56
+ Spec::Rake::SpecTask.new('mailer') do |t|
57
+ t.spec_opts = ["--format", "specdoc", "--colour"]
58
+ if(ENV['MAILER'])
59
+ t.spec_files = Dir["#{slice_root}/spec/mailer/**/#{ENV['MAILER']}_spec.rb"].sort
60
+ else
61
+ t.spec_files = Dir["#{slice_root}/spec/mailers/**/*_spec.rb"].sort
62
+ end
63
+ end
64
+
65
+ desc "Run all specs and output the result in html"
66
+ Spec::Rake::SpecTask.new('html') do |t|
67
+ t.spec_opts = ["--format", "html"]
68
+ t.libs = ['lib', 'server/lib' ]
69
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end