Skip to content

Commit 1e95eba

Browse files
author
Amol Kelkar
committedApr 27, 2011
Fixed active? method logic
Now a real gem on rubygems.org Basic tests
1 parent 77debd3 commit 1e95eba

15 files changed

+367
-26
lines changed
 

‎Gemfile

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
source "http://rubygems.org"
2-
# Add dependencies required to use your gem here.
3-
# Example:
4-
# gem "activesupport", ">= 2.3.5"
52

6-
# Add dependencies to develop your gem here.
7-
# Include everything needed to run rake, tests, features, etc.
3+
gem 'rails', ">= 3.0.4"
4+
gem 'warden'
5+
gem 'devise'
6+
87
group :development do
9-
gem "rspec", "~> 2.3.0"
8+
gem "rspec"
109
gem "bundler", "~> 1.0.0"
1110
gem "jeweler", "~> 1.5.2"
1211
gem "rcov", ">= 0"

‎Gemfile.lock

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
GEM
2+
remote: http://rubygems.org/
3+
specs:
4+
abstract (1.0.0)
5+
actionmailer (3.0.7)
6+
actionpack (= 3.0.7)
7+
mail (~> 2.2.15)
8+
actionpack (3.0.7)
9+
activemodel (= 3.0.7)
10+
activesupport (= 3.0.7)
11+
builder (~> 2.1.2)
12+
erubis (~> 2.6.6)
13+
i18n (~> 0.5.0)
14+
rack (~> 1.2.1)
15+
rack-mount (~> 0.6.14)
16+
rack-test (~> 0.5.7)
17+
tzinfo (~> 0.3.23)
18+
activemodel (3.0.7)
19+
activesupport (= 3.0.7)
20+
builder (~> 2.1.2)
21+
i18n (~> 0.5.0)
22+
activerecord (3.0.7)
23+
activemodel (= 3.0.7)
24+
activesupport (= 3.0.7)
25+
arel (~> 2.0.2)
26+
tzinfo (~> 0.3.23)
27+
activeresource (3.0.7)
28+
activemodel (= 3.0.7)
29+
activesupport (= 3.0.7)
30+
activesupport (3.0.7)
31+
arel (2.0.9)
32+
bcrypt-ruby (2.1.4)
33+
builder (2.1.2)
34+
devise (1.3.3)
35+
bcrypt-ruby (~> 2.1.2)
36+
orm_adapter (~> 0.0.3)
37+
warden (~> 1.0.3)
38+
diff-lcs (1.1.2)
39+
erubis (2.6.6)
40+
abstract (>= 1.0.0)
41+
git (1.2.5)
42+
i18n (0.5.0)
43+
jeweler (1.5.2)
44+
bundler (~> 1.0.0)
45+
git (>= 1.2.5)
46+
rake
47+
mail (2.2.19)
48+
activesupport (>= 2.3.6)
49+
i18n (>= 0.4.0)
50+
mime-types (~> 1.16)
51+
treetop (~> 1.4.8)
52+
mime-types (1.16)
53+
orm_adapter (0.0.4)
54+
polyglot (0.3.1)
55+
rack (1.2.2)
56+
rack-mount (0.6.14)
57+
rack (>= 1.0.0)
58+
rack-test (0.5.7)
59+
rack (>= 1.0)
60+
rails (3.0.7)
61+
actionmailer (= 3.0.7)
62+
actionpack (= 3.0.7)
63+
activerecord (= 3.0.7)
64+
activeresource (= 3.0.7)
65+
activesupport (= 3.0.7)
66+
bundler (~> 1.0)
67+
railties (= 3.0.7)
68+
railties (3.0.7)
69+
actionpack (= 3.0.7)
70+
activesupport (= 3.0.7)
71+
rake (>= 0.8.7)
72+
thor (~> 0.14.4)
73+
rake (0.8.7)
74+
rcov (0.9.9)
75+
rspec (2.5.0)
76+
rspec-core (~> 2.5.0)
77+
rspec-expectations (~> 2.5.0)
78+
rspec-mocks (~> 2.5.0)
79+
rspec-core (2.5.1)
80+
rspec-expectations (2.5.0)
81+
diff-lcs (~> 1.1.2)
82+
rspec-mocks (2.5.0)
83+
thor (0.14.6)
84+
treetop (1.4.9)
85+
polyglot (>= 0.3.1)
86+
tzinfo (0.3.27)
87+
warden (1.0.3)
88+
rack (>= 1.0.0)
89+
90+
PLATFORMS
91+
ruby
92+
93+
DEPENDENCIES
94+
bundler (~> 1.0.0)
95+
devise
96+
jeweler (~> 1.5.2)
97+
rails (>= 3.0.4)
98+
rcov
99+
rspec
100+
warden

‎LICENSE.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2011 Amol Kelkar
1+
Copyright (c) 2010, 2011 Josh Kalderimis, Amol Kelkar
22

33
Permission is hereby granted, free of charge, to any person obtaining
44
a copy of this software and associated documentation files (the

‎README.rdoc

+45-13
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,51 @@
11
= devise_suspendable
22

3-
Description goes here.
3+
devise_suspendable is a simple module which uses the [Devise](http://github.com/plataformatec/devise) authentication framework Activatable module hooks to provide a simple clean way to suspend an account.
4+
When an account is suspended the date and time of suspension is recorded, as well as an optional reason, for easy reference.
45

5-
== Contributing to devise_suspendable
6-
7-
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9-
* Fork the project
10-
* Start a feature/bugfix branch
11-
* Commit and push until you are happy with your contribution
12-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
6+
== Setup
7+
(Assumes that you have devise already setup and your device models, e.g. User, are already created)
8+
1. Add `gem devise_suspendable` in your Gemfile
149

15-
== Copyright
10+
2. Create a migration to update each of your device model(s), e.g. User, that you want to be suspendable
11+
rails g devise_suspendable User
1612

17-
Copyright (c) 2011 Amol Kelkar. See LICENSE.txt for
18-
further details.
13+
3. Run the migration
14+
rake db:migrate
1915

16+
4. Mark the model as suspendable
17+
18+
class User < ActiveRecord::Base
19+
20+
devise ..., :activatable, :suspendable
21+
22+
...
23+
end
24+
25+
== Notes
26+
- :suspendable depends on :activatable
27+
28+
- This plugin is similar to Lockable and can be used along side it, as long as Lockable is using token or timeouts as the unlock strategy. Lockable is great for failed login tracking, and subsequent locking, suspendable is best for manual account suspension
29+
30+
- Maintaining your active users is part and parcel of running a web app
31+
32+
33+
== References
34+
35+
* [Devise](http://github.com/plataformatec/devise)
36+
* [Warden](http://github.com/hassox/warden)
37+
38+
39+
== TODO
40+
41+
- tests, tests, tests
42+
43+
== Thanks to
44+
* Josh Kalderimis for most of core logic for this gem
45+
* devise_lastseenable[https://github.com/ctide/devise_lastseenable] for essentially operating as the base for this gem
46+
* Jeweler[https://github.com/technicalpickles/jeweler] for making it dead simple to write this gem
47+
48+
49+
Released under the MIT license
50+
51+
Copyright (c) 2010, 2011 Josh Kalderimis, Amol Kelkar

‎Rakefile

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ Jeweler::Tasks.new do |gem|
1515
gem.name = "devise_suspendable"
1616
gem.homepage = "http://github.com/amolk/devise_suspendable"
1717
gem.license = "MIT"
18-
gem.summary = %Q{TODO: one-line summary of your gem}
19-
gem.description = %Q{TODO: longer description of your gem}
20-
gem.email = "amol@kingofweb.com"
18+
gem.summary = %Q{Device module that provides a simple clean way to suspend an account}
19+
gem.description = %Q{Devise-Suspendable is a simple module which uses the [Devise](http://github.com/plataformatec/devise) authentication framework Activatable module hooks to provide a simple clean way to suspend an account.
20+
When an account is suspended the date and time of suspension is recorded, as well as an optional reason, for easy reference.}
21+
gem.email = "kelkar.amol@gmail.com"
2122
gem.authors = ["Amol Kelkar"]
2223
# Include your dependencies below. Runtime dependencies are required when using your gem,
2324
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)

‎devise_suspendable.gemspec

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Generated by jeweler
2+
# DO NOT EDIT THIS FILE DIRECTLY
3+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4+
# -*- encoding: utf-8 -*-
5+
6+
Gem::Specification.new do |s|
7+
s.name = %q{devise_suspendable}
8+
s.version = "0.6.0"
9+
10+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11+
s.authors = ["Amol Kelkar"]
12+
s.date = %q{2011-04-27}
13+
s.description = %q{Devise-Suspendable is a simple module which uses the [Devise](http://github.com/plataformatec/devise) authentication framework Activatable module hooks to provide a simple clean way to suspend an account.
14+
When an account is suspended the date and time of suspension is recorded, as well as an optional reason, for easy reference.}
15+
s.email = %q{kelkar.amol@gmail.com}
16+
s.extra_rdoc_files = [
17+
"LICENSE.txt",
18+
"README.rdoc"
19+
]
20+
s.files = [
21+
".document",
22+
".rspec",
23+
"Gemfile",
24+
"LICENSE.txt",
25+
"README.rdoc",
26+
"Rakefile",
27+
"VERSION",
28+
"lib/devise_suspendable.rb",
29+
"spec/devise_suspendable_spec.rb",
30+
"spec/spec_helper.rb"
31+
]
32+
s.homepage = %q{http://github.com/amolk/devise_suspendable}
33+
s.licenses = ["MIT"]
34+
s.require_paths = ["lib"]
35+
s.rubygems_version = %q{1.6.2}
36+
s.summary = %q{Device module that provides a simple clean way to suspend an account}
37+
s.test_files = [
38+
"spec/devise_suspendable_spec.rb",
39+
"spec/spec_helper.rb"
40+
]
41+
42+
if s.respond_to? :specification_version then
43+
s.specification_version = 3
44+
45+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46+
s.add_runtime_dependency(%q<rails>, [">= 3.0.4"])
47+
s.add_runtime_dependency(%q<warden>, [">= 0"])
48+
s.add_runtime_dependency(%q<devise>, [">= 0"])
49+
s.add_development_dependency(%q<rspec>, [">= 0"])
50+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
51+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
52+
s.add_development_dependency(%q<rcov>, [">= 0"])
53+
else
54+
s.add_dependency(%q<rails>, [">= 3.0.4"])
55+
s.add_dependency(%q<warden>, [">= 0"])
56+
s.add_dependency(%q<devise>, [">= 0"])
57+
s.add_dependency(%q<rspec>, [">= 0"])
58+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
59+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
60+
s.add_dependency(%q<rcov>, [">= 0"])
61+
end
62+
else
63+
s.add_dependency(%q<rails>, [">= 3.0.4"])
64+
s.add_dependency(%q<warden>, [">= 0"])
65+
s.add_dependency(%q<devise>, [">= 0"])
66+
s.add_dependency(%q<rspec>, [">= 0"])
67+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
68+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
69+
s.add_dependency(%q<rcov>, [">= 0"])
70+
end
71+
end
72+

‎lib/devise_suspendable.rb

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
unless defined?(Devise)
2+
require 'devise'
3+
end
4+
require 'devise_suspendable'
5+
6+
Devise.add_module :suspendable, :model => 'devise_suspendable/model'
7+
8+
module DeviseSuspendable
9+
end
10+
11+
require 'devise_suspendable/rails'

‎lib/devise_suspendable/model.rb

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
module Devise
2+
module Models
3+
# Suspendable Module, responsible for manual deactivation of a user account.
4+
#
5+
# Examples:
6+
#
7+
# User.find(1).suspend!('Left the company')
8+
#
9+
module Suspendable
10+
def self.included(base)
11+
base.class_eval do
12+
validates_length_of :suspension_reason, :maximum => 250
13+
14+
# basic sanitization
15+
before_validation do |acc|
16+
acc.suspension_reason.strip! if acc.suspension_reason
17+
acc.suspension_reason = nil if acc.suspension_reason.blank?
18+
acc.suspension_reason = nil if acc.suspended_at.blank?
19+
end
20+
end
21+
end
22+
23+
def suspended?
24+
self.suspended_at?
25+
end
26+
27+
def suspend!(reason = nil)
28+
return if suspended?
29+
self.suspended_at = Time.zone.now
30+
self.suspension_reason = reason
31+
self.save(:validate => false)
32+
end
33+
34+
def unsuspend!
35+
return if !suspended?
36+
self.suspended_at = nil
37+
self.suspension_reason = nil
38+
self.save(:validate => false) if self.changed?
39+
end
40+
41+
# override Activatable
42+
def active?
43+
super && !suspended?
44+
end
45+
46+
# Overwrites invalid_message from Devise::Models::Authenticatable to define
47+
# the correct reason for blocking the sign in.
48+
def inactive_message
49+
suspended? ? :suspended : super
50+
end
51+
end
52+
end
53+
end

‎lib/devise_suspendable/rails.rb

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
require 'devise_suspendable'
2+
3+
module DeviseSuspendable
4+
class Engine < ::Rails::Engine
5+
end
6+
end

‎lib/devise_suspendable/version.rb

Whitespace-only changes.
6 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
require 'rails/generators/migration'
2+
3+
class DeviseSuspendableGenerator < Rails::Generators::NamedBase
4+
include Rails::Generators::Migration
5+
6+
def self.source_root
7+
@_devise_source_root ||= File.expand_path("../templates", __FILE__)
8+
end
9+
10+
def self.orm_has_migration?
11+
Rails::Generators.options[:rails][:orm] == :active_record
12+
end
13+
14+
def self.next_migration_number(dirname)
15+
if ActiveRecord::Base.timestamped_migrations
16+
Time.now.utc.strftime("%Y%m%d%H%M%S")
17+
else
18+
"%.3d" % (current_migration_number(dirname) + 1)
19+
end
20+
end
21+
22+
class_option :orm
23+
class_option :migration, :type => :boolean, :default => orm_has_migration?
24+
25+
26+
def create_migration_file
27+
migration_template 'migration.rb', "db/migrate/devise_add_suspendable_#{name.downcase}.rb"
28+
end
29+
30+
protected
31+
end
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class DeviseAddSuspendable<%= table_name.camelize.singularize %> < ActiveRecord::Migration
2+
def self.up
3+
add_column :<%= table_name %>, :suspended_at, :datetime, :null => true, :default => nil
4+
add_column :<%= table_name %>, :suspension_reason, :string, :null => true, :default => nil
5+
end
6+
7+
def self.down
8+
remove_column :<%= table_name %>, :suspended_at
9+
remove_column :<%= table_name %>, :suspension_reason
10+
end
11+
end

‎spec/devise_suspendable_spec.rb

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,32 @@
1-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1+
require File.dirname(__FILE__) + '/spec_helper'
22

33
describe "DeviseSuspendable" do
4-
it "fails" do
5-
fail "hey buddy, you should probably rename this file and start specing for real"
4+
it 'cannot run tests here' do
5+
pending "copy this file to your project as /spec/models/suspendable_user_spec.rb and run as part of your project's specs. The spec code depends on FactoryGirl gem and a valid :user factory defined."
66
end
7+
8+
it 'is not in suspended state when created fresh' do
9+
user = Factory(:user)
10+
user.should_not be_suspended
11+
user.should be_active
12+
end
13+
14+
it 'can be suspended' do
15+
user = Factory(:user)
16+
user.suspend!("has too many 'family members'")
17+
user.should be_suspended
18+
user.should_not be_active
19+
end
20+
21+
it 'can be unsuspended' do
22+
user = Factory(:user, :suspended_at => 1.day.ago)
23+
user.should be_suspended
24+
user.should_not be_active
25+
26+
user.unsuspend!
27+
28+
user.should_not be_suspended
29+
user.should be_active
30+
end
31+
732
end

0 commit comments

Comments
 (0)
Please sign in to comment.