scottmotte-merb-auth-slice-password-reset 0.9.14

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 (32) hide show
  1. data/LICENSE +20 -0
  2. data/README.textile +219 -0
  3. data/Rakefile +57 -0
  4. data/TODO +2 -0
  5. data/app/controllers/application.rb +5 -0
  6. data/app/controllers/passwords.rb +37 -0
  7. data/app/helpers/application_helper.rb +64 -0
  8. data/app/helpers/mailer_helper.rb +28 -0
  9. data/app/mailers/password_reset_mailer.rb +19 -0
  10. data/app/mailers/views/password_reset_mailer/new_password.html.erb +3 -0
  11. data/app/mailers/views/password_reset_mailer/password_reset.text.erb +5 -0
  12. data/app/views/layout/merb_auth_slice_password_reset.html.erb +16 -0
  13. data/app/views/passwords/forgot_password.html.erb +7 -0
  14. data/lib/merb-auth-slice-password-reset.rb +85 -0
  15. data/lib/merb-auth-slice-password-reset/merbtasks.rb +112 -0
  16. data/lib/merb-auth-slice-password-reset/mixins/senile_user.rb +93 -0
  17. data/lib/merb-auth-slice-password-reset/mixins/senile_user/ar_senile_user.rb +19 -0
  18. data/lib/merb-auth-slice-password-reset/mixins/senile_user/dm_senile_user.rb +22 -0
  19. data/lib/merb-auth-slice-password-reset/mixins/senile_user/mm_senile_user.rb +22 -0
  20. data/lib/merb-auth-slice-password-reset/mixins/senile_user/sq_senile_user.rb +20 -0
  21. data/lib/merb-auth-slice-password-reset/slicetasks.rb +18 -0
  22. data/lib/merb-auth-slice-password-reset/spectasks.rb +75 -0
  23. data/public/javascripts/master.js +0 -0
  24. data/public/stylesheets/master.css +2 -0
  25. data/spec/mailers/password_reset_mailer_spec.rb +49 -0
  26. data/spec/mixins/senile_user_spec.rb +111 -0
  27. data/spec/spec_helper.rb +61 -0
  28. data/stubs/app/controllers/passwords.rb +13 -0
  29. data/stubs/app/mailers/views/password_reset_mailer/new_password.html.erb +3 -0
  30. data/stubs/app/mailers/views/password_reset_mailer/password_reset.text.erb +5 -0
  31. data/stubs/app/views/passwords/forgot_password.html.erb +7 -0
  32. metadata +146 -0
@@ -0,0 +1,3 @@
1
+ Your new password
2
+
3
+ <%= @user.password %>
@@ -0,0 +1,5 @@
1
+ Your password has been reset.
2
+
3
+ Please visit:
4
+
5
+ <%= password_reset_url(@user) %>
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5
+ <title>Fresh MerbAuthSlicePasswordReset Slice</title>
6
+ <link href="<%= public_path_for :stylesheet, 'master.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
7
+ <script src="<%= public_path_for :javascript, 'master.js' %>" type="text/javascript" charset="utf-8"></script>
8
+ </head>
9
+ <!-- you can override this layout at slices/merb-auth-slice-password-reset/app/views/layout/merb-auth-slice-password-reset.html.erb -->
10
+ <body class="merb-auth-slice-password-reset-slice">
11
+ <div id="container">
12
+ <h1>MerbAuthSlicePasswordReset Slice</h1>
13
+ <div id="main"><%= catch_content :for_layout %></div>
14
+ </div>
15
+ </body>
16
+ </html>
@@ -0,0 +1,7 @@
1
+ <form action="" method="post">
2
+ <p>
3
+ <label for="<%= @login_param_name %>"><%= @login_param_name.to_s.capitalize.t %></label>
4
+ <input type="text" class="text" name="<%= @login_param_name %>" id="<%= @login_param_name %>" />
5
+ </p>
6
+ <input type="submit" value="Reset password" />
7
+ </form>
@@ -0,0 +1,85 @@
1
+ if defined?(Merb::Plugins)
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+
5
+ dependency 'merb-slices'
6
+ dependency 'merb-auth-core'
7
+ dependency 'merb-auth-more'
8
+ dependency 'merb-mailer'
9
+ require(File.expand_path(File.dirname(__FILE__) / "merb-auth-slice-password-reset" / "mixins") / "senile_user")
10
+
11
+ Merb::Plugins.add_rakefiles "merb-auth-slice-password-reset/merbtasks", "merb-auth-slice-password-reset/slicetasks", "merb-auth-slice-password-reset/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-password-reset
22
+ # :mirror - which path component types to use on copy operations; defaults to all
23
+ Merb::Slices::config[:merb_auth_slice_password_reset][:layout] ||= :application
24
+
25
+ # All Slice code is expected to be namespaced inside a module
26
+ module MerbAuthSlicePasswordReset
27
+
28
+ # Slice metadata
29
+ self.description = "MerbAuthSlicePasswordReset is a merb slice that adds password-reset functionality for merb-auth-based merb applications."
30
+ self.version = "0.9.10"
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
+ end
41
+
42
+ # Activation hook - runs after AfterAppLoads BootLoader
43
+ def self.activate
44
+ end
45
+
46
+ # Deactivation hook - triggered by Merb::Slices.deactivate(MerbAuthSlicePasswordReset)
47
+ def self.deactivate
48
+ end
49
+
50
+ # Setup routes inside the host application
51
+ #
52
+ # @param scope<Merb::Router::Behaviour>
53
+ # Routes will be added within this scope (namespace). In fact, any
54
+ # router behaviour is a valid namespace, so you can attach
55
+ # routes at any level of your router setup.
56
+ #
57
+ # @note prefix your named routes with :merb_auth_slice_password_reset_
58
+ # to avoid potential conflicts with global named routes.
59
+ def self.setup_router(scope)
60
+ scope.match("/reset_password/:password_reset_code").to(:controller => "passwords", :action => "reset").name(:reset_password)
61
+ scope.match("/forgot_password", :method => :get).to(:controller => "passwords", :action => "forgot_password").name(:forgot_password)
62
+ scope.match("/forgot_password", :method => :post).to(:controller => "passwords", :action => "send_confirmation")
63
+ end
64
+
65
+ end
66
+
67
+ # Setup the slice layout for MerbAuthSlicePasswordReset
68
+ #
69
+ # Use MerbAuthSlicePasswordReset.push_path and MerbAuthSlicePasswordReset.push_app_path
70
+ # to set paths to merb-auth-slice-password-reset-level and app-level paths. Example:
71
+ #
72
+ # MerbAuthSlicePasswordReset.push_path(:application, MerbAuthSlicePasswordReset.root)
73
+ # MerbAuthSlicePasswordReset.push_app_path(:application, Merb.root / 'slices' / 'merb-auth-slice-password-reset')
74
+ # ...
75
+ #
76
+ # Any component path that hasn't been set will default to MerbAuthSlicePasswordReset.root
77
+ #
78
+ # Or just call setup_default_structure! to setup a basic Merb MVC structure.
79
+ MerbAuthSlicePasswordReset.setup_default_structure!
80
+ MaSPR = MerbAuthSlicePasswordReset unless defined?(MaSPR)
81
+
82
+ # Add dependencies for other MerbAuthSlicePasswordReset classes below. Example:
83
+ # dependency "merb-auth-slice-password-reset/other"
84
+
85
+ end
@@ -0,0 +1,112 @@
1
+ namespace :slices do
2
+ namespace :merb_auth_slice_password_reset do
3
+
4
+ desc "Install MerbAuthSlicePasswordReset"
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
+ MerbAuthSlicePasswordReset.mirrored_components.each do |type|
15
+ if File.directory?(MerbAuthSlicePasswordReset.dir_for(type))
16
+ if !File.directory?(dst_path = MerbAuthSlicePasswordReset.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 MerbAuthSlicePasswordReset - resolves any collisions"
28
+ copied, preserved = MerbAuthSlicePasswordReset.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 MerbAuthSlicePasswordReset - resolves any collisions"
40
+ copied, preserved = MerbAuthSlicePasswordReset.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 MerbAuthSlicePasswordReset into your app (only merb-auth-slice-password-reset/app)"
51
+ task :freeze => [ "freeze:app" ]
52
+
53
+ namespace :freeze do
54
+
55
+ desc "Freezes MerbAuthSlicePasswordReset by installing the gem into application/gems"
56
+ task :gem do
57
+ ENV["GEM"] ||= "merb-auth-slice-password-reset"
58
+ Rake::Task['slices:install_as_gem'].invoke
59
+ end
60
+
61
+ desc "Freezes MerbAuthSlicePasswordReset by copying all files from merb-auth-slice-password-reset/app to your application"
62
+ task :app do
63
+ puts "Copying all merb-auth-slice-password-reset/app files to your application - resolves any collisions"
64
+ copied, preserved = MerbAuthSlicePasswordReset.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 = MerbAuthSlicePasswordReset.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 = MerbAuthSlicePasswordReset.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 = MerbAuthSlicePasswordReset.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 MerbAuthSlicePasswordReset as a gem and copies over merb-auth-slice-password-reset/app"
98
+ task :app_with_gem => [:gem, :app]
99
+
100
+ desc "Freezes MerbAuthSlicePasswordReset by unpacking all files into your application"
101
+ task :unpack do
102
+ puts "Unpacking MerbAuthSlicePasswordReset files to your application - resolves any collisions"
103
+ copied, preserved = MerbAuthSlicePasswordReset.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,93 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ # This mixin provides basic password-reset functionality for senile users.
5
+ #
6
+ # Added properties:
7
+ # :password_reset_code, String
8
+ #
9
+ # To use it simply require it and include it into your user class.
10
+ #
11
+ # class User
12
+ # include Merb::Authentication::Mixins::SenileUser
13
+ #
14
+ # end
15
+ #
16
+ module SenileUser
17
+ def self.included(base)
18
+ base.class_eval do
19
+ include Merb::Authentication::Mixins::SenileUser::InstanceMethods
20
+ extend Merb::Authentication::Mixins::SenileUser::ClassMethods
21
+
22
+ path = File.expand_path(File.dirname(__FILE__)) / "senile_user"
23
+ if defined?(DataMapper) && DataMapper::Resource > self
24
+ require path / "dm_senile_user"
25
+ extend(Merb::Authentication::Mixins::SenileUser::DMClassMethods)
26
+ elsif defined?(ActiveRecord) && ancestors.include?(ActiveRecord::Base)
27
+ require path / "ar_senile_user"
28
+ extend(Merb::Authentication::Mixins::SenileUser::ARClassMethods)
29
+ elsif defined?(Sequel) && ancestors.include?(Sequel::Model)
30
+ require path / "sq_senile_user"
31
+ extend(Merb::Authentication::Mixins::SenileUser::SQClassMethods)
32
+ elsif MongoMapper::Document > self
33
+ require path / "mm_senile_user"
34
+ extend(Merb::Authentication::Mixins::SenileUser::MMClassMethods)
35
+ end
36
+
37
+ end # base.class_eval
38
+ end # self.included
39
+
40
+ module ClassMethods
41
+ def make_key
42
+ Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
43
+ end
44
+ end # ClassMethods
45
+
46
+ module InstanceMethods
47
+
48
+ def reset_password!
49
+ self.password = self.password_confirmation = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )[0, 7]
50
+ send_new_password
51
+ self.password_reset_code = nil
52
+ self.save
53
+ end
54
+
55
+ def generate_password_reset_code
56
+ pwreset_key_success = false
57
+ until pwreset_key_success
58
+ self.password_reset_code = self.class.make_key
59
+ self.save
60
+ pwreset_key_success = self.errors.on(:password_reset_code).nil? ? true : false
61
+ end
62
+ end
63
+
64
+ def password_reset?
65
+ ! self.password_reset_code.nil?
66
+ end
67
+
68
+ # Sends out the password reset notification.
69
+ # Used 'Request to change your password' as subject if +MaSFP[:password_reset_subject]+ is not set.
70
+ def send_password_reset_notification
71
+ generate_password_reset_code
72
+ deliver_password_reset_email(:password_reset, :subject => (MaSPR[:password_reset_subject] || "Request to change your password"))
73
+ end
74
+
75
+ def send_new_password
76
+ deliver_password_reset_email(:new_password, :subject => (MaSPR[:new_password_subject] || "Your new password"))
77
+ end
78
+
79
+ private
80
+
81
+ # Helper method delivering the email.
82
+ def deliver_password_reset_email(action, params)
83
+ from = MaSPR[:from_email]
84
+ raise "No :from_email option set for Merb::Slices::config[:merb_auth_slice_password_reset][:from_email]" unless from
85
+ MaSPR::PasswordResetMailer.dispatch_and_deliver(action, params.merge(:from => from, :to => self.email), :user => self)
86
+ end
87
+
88
+ end # InstanceMethods
89
+
90
+ end # SenileUser
91
+ end # Mixins
92
+ end # Authentication
93
+ end # Merb
@@ -0,0 +1,19 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module SenileUser
5
+ module ARClassMethods
6
+ def self.extended(base)
7
+ def find_with_password_reset_code(code)
8
+ find(:first, :conditions => ["password_reset_code = ?", code])
9
+ end
10
+
11
+ def find_with_login_param(param_name, value)
12
+ find(:first, :conditions => ["#{param_name} = ?", value])
13
+ end
14
+ end # self.extended
15
+ end # ARClassMethods
16
+ end # SenileUser
17
+ end # Mixins
18
+ end # Authentication
19
+ end # Merb
@@ -0,0 +1,22 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module SenileUser
5
+ module DMClassMethods
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ property :password_reset_code, String, :writer => :protected
9
+ end # base.class_eval
10
+ def find_with_password_reset_code(code)
11
+ first(:password_reset_code => code)
12
+ end
13
+
14
+ def find_with_login_param(param_name, value)
15
+ first(param_name => value)
16
+ end
17
+ end # self.extended
18
+ end # DMClassMethods
19
+ end # SenileUser
20
+ end # Mixins
21
+ end # Authentication
22
+ end #Merb
@@ -0,0 +1,22 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module SenileUser
5
+ module MMClassMethods
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ key :password_reset_code, String
9
+ end # base.class_eval
10
+ def find_with_password_reset_code(code)
11
+ find(:first, :conditions => {:password_reset_code => code})
12
+ end
13
+
14
+ def find_with_login_param(param_name, value)
15
+ find(:first, :conditions => {param_name => value})
16
+ end
17
+ end # self.extended
18
+ end # MMClassMethods
19
+ end # SenileUser
20
+ end # Mixins
21
+ end # Authentication
22
+ end #Merb
@@ -0,0 +1,20 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module SenileUser
5
+ module SQClassMethods
6
+ def self.extended(base)
7
+ def find_with_password_reset_code(code)
8
+ self[:password_reset_code => code]
9
+ end
10
+ def find_with_login_param(param_name, value)
11
+ self[param_name => value]
12
+ end
13
+ end # self.extended
14
+ end # SQClassMethods
15
+ module SQInstanceMethods
16
+ end # SQInstanceMethods
17
+ end # SenileUser
18
+ end # Mixins
19
+ end # Authentication
20
+ end # Merb
@@ -0,0 +1,18 @@
1
+ namespace :slices do
2
+ namespace :merb_auth_slice_password_reset do
3
+
4
+ # add your own merb-auth-slice-password-reset 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_password_reset 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 MerbAuthSlicePasswordReset 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