saucy 0.1.1

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 (86) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +106 -0
  3. data/README +9 -0
  4. data/app/controllers/accounts_controller.rb +57 -0
  5. data/app/controllers/invitations_controller.rb +36 -0
  6. data/app/controllers/memberships_controller.rb +9 -0
  7. data/app/controllers/permissions_controller.rb +17 -0
  8. data/app/controllers/plans_controller.rb +7 -0
  9. data/app/controllers/profiles_controller.rb +17 -0
  10. data/app/controllers/projects_controller.rb +56 -0
  11. data/app/models/account_membership.rb +8 -0
  12. data/app/models/invitation.rb +86 -0
  13. data/app/models/project_membership.rb +16 -0
  14. data/app/models/signup.rb +134 -0
  15. data/app/views/accounts/_account.html.erb +12 -0
  16. data/app/views/accounts/_blank_slate.html.erb +6 -0
  17. data/app/views/accounts/_projects.html.erb +12 -0
  18. data/app/views/accounts/_tab_bar.html.erb +8 -0
  19. data/app/views/accounts/edit.html.erb +17 -0
  20. data/app/views/accounts/index.html.erb +3 -0
  21. data/app/views/accounts/new.html.erb +26 -0
  22. data/app/views/invitation_mailer/invitation.text.erb +6 -0
  23. data/app/views/invitations/new.html.erb +11 -0
  24. data/app/views/invitations/show.html.erb +20 -0
  25. data/app/views/memberships/index.html.erb +16 -0
  26. data/app/views/permissions/edit.html.erb +15 -0
  27. data/app/views/plans/index.html.erb +3 -0
  28. data/app/views/profiles/_inputs.html.erb +6 -0
  29. data/app/views/profiles/edit.html.erb +35 -0
  30. data/app/views/projects/_form.html.erb +4 -0
  31. data/app/views/projects/edit.html.erb +18 -0
  32. data/app/views/projects/index.html.erb +13 -0
  33. data/app/views/projects/new.html.erb +11 -0
  34. data/config/routes.rb +18 -0
  35. data/features/run_features.feature +73 -0
  36. data/features/step_definitions/clearance_steps.rb +45 -0
  37. data/features/step_definitions/rails_steps.rb +70 -0
  38. data/features/support/env.rb +4 -0
  39. data/features/support/file.rb +11 -0
  40. data/lib/generators/saucy/base.rb +18 -0
  41. data/lib/generators/saucy/features/features_generator.rb +68 -0
  42. data/lib/generators/saucy/features/templates/factories.rb +55 -0
  43. data/lib/generators/saucy/features/templates/step_definitions/email_steps.rb +13 -0
  44. data/lib/generators/saucy/features/templates/step_definitions/factory_girl_steps.rb +1 -0
  45. data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +3 -0
  46. data/lib/generators/saucy/features/templates/step_definitions/project_steps.rb +4 -0
  47. data/lib/generators/saucy/features/templates/step_definitions/session_steps.rb +27 -0
  48. data/lib/generators/saucy/features/templates/step_definitions/user_steps.rb +54 -0
  49. data/lib/generators/saucy/install/install_generator.rb +36 -0
  50. data/lib/generators/saucy/install/templates/create_saucy_tables.rb +72 -0
  51. data/lib/generators/saucy/install/templates/models/account.rb +3 -0
  52. data/lib/generators/saucy/install/templates/models/invitation_mailer.rb +9 -0
  53. data/lib/generators/saucy/install/templates/models/plan.rb +3 -0
  54. data/lib/generators/saucy/install/templates/models/project.rb +3 -0
  55. data/lib/generators/saucy/views/views_generator.rb +23 -0
  56. data/lib/saucy.rb +7 -0
  57. data/lib/saucy/account.rb +50 -0
  58. data/lib/saucy/account_authorization.rb +34 -0
  59. data/lib/saucy/configuration.rb +12 -0
  60. data/lib/saucy/engine.rb +9 -0
  61. data/lib/saucy/layouts.rb +36 -0
  62. data/lib/saucy/plan.rb +11 -0
  63. data/lib/saucy/project.rb +39 -0
  64. data/lib/saucy/user.rb +37 -0
  65. data/spec/controllers/accounts_controller_spec.rb +204 -0
  66. data/spec/controllers/application_controller_spec.rb +27 -0
  67. data/spec/controllers/invitations_controller_spec.rb +155 -0
  68. data/spec/controllers/memberships_controller_spec.rb +33 -0
  69. data/spec/controllers/permissions_controller_spec.rb +69 -0
  70. data/spec/controllers/profiles_controller_spec.rb +43 -0
  71. data/spec/controllers/projects_controller_spec.rb +123 -0
  72. data/spec/layouts_spec.rb +21 -0
  73. data/spec/models/account_membership_spec.rb +13 -0
  74. data/spec/models/account_spec.rb +61 -0
  75. data/spec/models/invitation_spec.rb +160 -0
  76. data/spec/models/project_membership_spec.rb +26 -0
  77. data/spec/models/project_spec.rb +80 -0
  78. data/spec/models/signup_spec.rb +175 -0
  79. data/spec/models/user_spec.rb +96 -0
  80. data/spec/saucy_spec.rb +7 -0
  81. data/spec/spec_helper.rb +31 -0
  82. data/spec/support/authentication_helpers.rb +71 -0
  83. data/spec/support/authorization_helpers.rb +56 -0
  84. data/spec/support/clearance_matchers.rb +55 -0
  85. data/spec/views/accounts/_account.html.erb_spec.rb +66 -0
  86. metadata +203 -0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+ gem "cucumber"
3
+ gem "aruba"
4
+ gem "rake"
5
+ gem "rspec", :require => false
6
+ gem "rails", ">= 3.0.3"
7
+ gem "thin"
8
+
data/Gemfile.lock ADDED
@@ -0,0 +1,106 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.3)
6
+ actionpack (= 3.0.3)
7
+ mail (~> 2.2.9)
8
+ actionpack (3.0.3)
9
+ activemodel (= 3.0.3)
10
+ activesupport (= 3.0.3)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.4)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.13)
16
+ rack-test (~> 0.5.6)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.3)
19
+ activesupport (= 3.0.3)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.4)
22
+ activerecord (3.0.3)
23
+ activemodel (= 3.0.3)
24
+ activesupport (= 3.0.3)
25
+ arel (~> 2.0.2)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.3)
28
+ activemodel (= 3.0.3)
29
+ activesupport (= 3.0.3)
30
+ activesupport (3.0.3)
31
+ arel (2.0.6)
32
+ aruba (0.2.6)
33
+ background_process
34
+ cucumber (~> 0.9.4)
35
+ background_process (1.2)
36
+ builder (2.1.2)
37
+ cucumber (0.9.4)
38
+ builder (~> 2.1.2)
39
+ diff-lcs (~> 1.1.2)
40
+ gherkin (~> 2.2.9)
41
+ json (~> 1.4.6)
42
+ term-ansicolor (~> 1.0.5)
43
+ daemons (1.1.0)
44
+ diff-lcs (1.1.2)
45
+ erubis (2.6.6)
46
+ abstract (>= 1.0.0)
47
+ eventmachine (0.12.10)
48
+ gherkin (2.2.9)
49
+ json (~> 1.4.6)
50
+ term-ansicolor (~> 1.0.5)
51
+ i18n (0.5.0)
52
+ json (1.4.6)
53
+ mail (2.2.11)
54
+ activesupport (>= 2.3.6)
55
+ i18n (~> 0.5.0)
56
+ mime-types (~> 1.16)
57
+ treetop (~> 1.4.8)
58
+ mime-types (1.16)
59
+ polyglot (0.3.1)
60
+ rack (1.2.1)
61
+ rack-mount (0.6.13)
62
+ rack (>= 1.0.0)
63
+ rack-test (0.5.6)
64
+ rack (>= 1.0)
65
+ rails (3.0.3)
66
+ actionmailer (= 3.0.3)
67
+ actionpack (= 3.0.3)
68
+ activerecord (= 3.0.3)
69
+ activeresource (= 3.0.3)
70
+ activesupport (= 3.0.3)
71
+ bundler (~> 1.0)
72
+ railties (= 3.0.3)
73
+ railties (3.0.3)
74
+ actionpack (= 3.0.3)
75
+ activesupport (= 3.0.3)
76
+ rake (>= 0.8.7)
77
+ thor (~> 0.14.4)
78
+ rake (0.8.7)
79
+ rspec (2.2.0)
80
+ rspec-core (~> 2.2)
81
+ rspec-expectations (~> 2.2)
82
+ rspec-mocks (~> 2.2)
83
+ rspec-core (2.2.1)
84
+ rspec-expectations (2.2.0)
85
+ diff-lcs (~> 1.1.2)
86
+ rspec-mocks (2.2.0)
87
+ term-ansicolor (1.0.5)
88
+ thin (1.2.7)
89
+ daemons (>= 1.0.9)
90
+ eventmachine (>= 0.12.6)
91
+ rack (>= 1.0.0)
92
+ thor (0.14.6)
93
+ treetop (1.4.9)
94
+ polyglot (>= 0.3.1)
95
+ tzinfo (0.3.23)
96
+
97
+ PLATFORMS
98
+ ruby
99
+
100
+ DEPENDENCIES
101
+ aruba
102
+ cucumber
103
+ rails (>= 3.0.3)
104
+ rake
105
+ rspec
106
+ thin
data/README ADDED
@@ -0,0 +1,9 @@
1
+ script/rails g saucy:install
2
+ script/rails g saucy:features
3
+ script/rails g saucy:views
4
+
5
+ To change the layout for a controller inside of saucy, add a line like this to
6
+ your config/application.rb:
7
+
8
+ config.saucy.layouts.accounts.index = "custom"
9
+
@@ -0,0 +1,57 @@
1
+ class AccountsController < ApplicationController
2
+ skip_before_filter :authenticate, :only => [:new, :create]
3
+ before_filter :authorize_admin, :except => [:new, :create, :index]
4
+ layout Saucy::Layouts.to_proc
5
+
6
+ def new
7
+ @plan = Plan.find(params[:plan_id])
8
+ @signup = Signup.new
9
+ end
10
+
11
+ def create
12
+ @signup = Signup.new(params[:signup])
13
+ @signup.user = current_user
14
+ if @signup.save
15
+ if @signup.email_confirmed?
16
+ flash[:success] = "Account was created."
17
+ sign_in @signup.user
18
+ redirect_to root_url
19
+ else
20
+ flash[:success] = "Account was created. Instructions for confirming your " +
21
+ "account have been sent to the email address you provided."
22
+ redirect_to sign_in_url
23
+ end
24
+ else
25
+ render :action => 'new'
26
+ end
27
+ end
28
+
29
+ def index
30
+ if current_user.projects.size == 1
31
+ flash.keep
32
+ redirect_to project_path(current_user.projects.first)
33
+ else
34
+ @accounts = current_user.accounts
35
+ end
36
+ end
37
+
38
+ def edit
39
+ @account = current_account
40
+ end
41
+
42
+ def update
43
+ @account = current_account
44
+ if @account.update_attributes(params[:account])
45
+ flash[:success] = 'Account was updated.'
46
+ redirect_to edit_profile_url
47
+ else
48
+ render :action => :edit
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def current_account
55
+ Account.find_by_url!(params[:id])
56
+ end
57
+ end
@@ -0,0 +1,36 @@
1
+ class InvitationsController < ApplicationController
2
+ before_filter :authorize_admin, :except => [:show, :update]
3
+ skip_before_filter :authenticate, :only => [:show, :update]
4
+ layout Saucy::Layouts.to_proc
5
+
6
+ def new
7
+ @invitation = Invitation.new
8
+ render
9
+ end
10
+
11
+ def create
12
+ @invitation = Invitation.new(params[:invitation])
13
+ @invitation.account = current_account
14
+ if @invitation.save
15
+ flash[:success] = "User invited."
16
+ redirect_to account_memberships_url(current_account)
17
+ else
18
+ render :action => 'new'
19
+ end
20
+ end
21
+
22
+ def show
23
+ @invitation = Invitation.find(params[:id])
24
+ render
25
+ end
26
+
27
+ def update
28
+ @invitation = Invitation.find(params[:id])
29
+ if @invitation.accept(params[:invitation])
30
+ sign_in @invitation.user
31
+ redirect_to root_url
32
+ else
33
+ render :action => 'show'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ class MembershipsController < ApplicationController
2
+ before_filter :authorize_admin
3
+ layout Saucy::Layouts.to_proc
4
+
5
+ def index
6
+ @users = current_account.users_by_name
7
+ render
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ class PermissionsController < ApplicationController
2
+ before_filter :authorize_admin
3
+ layout Saucy::Layouts.to_proc
4
+
5
+ def edit
6
+ @user = User.find(params[:user_id])
7
+ @projects = current_account.projects_by_name
8
+ render
9
+ end
10
+
11
+ def update
12
+ user = User.find(params[:user_id])
13
+ user.update_permissions_for(current_account, params[:permissions][:project_ids])
14
+ flash[:success] = "Permissions updated."
15
+ redirect_to account_memberships_url(current_account)
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ class PlansController < ApplicationController
2
+ layout Saucy::Layouts.to_proc
3
+
4
+ def index
5
+ @plans = Plan.all
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ class ProfilesController < ApplicationController
2
+ layout Saucy::Layouts.to_proc
3
+
4
+ def edit
5
+ @user = current_user
6
+ end
7
+
8
+ def update
9
+ @user = current_user
10
+ if @user.update_attributes(params[:user])
11
+ flash[:notice] = "Your user account has been updated."
12
+ redirect_to edit_profile_url
13
+ else
14
+ render :action => :edit
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,56 @@
1
+ class ProjectsController < ApplicationController
2
+ before_filter :authorize_admin
3
+ layout Saucy::Layouts.to_proc
4
+
5
+ def new
6
+ @project = current_account.projects.build
7
+ render
8
+ end
9
+
10
+ def create
11
+ @project = current_account.projects.build(params[:project])
12
+ if @project.save
13
+ flash[:notice] = "Project successfully created"
14
+ redirect_to edit_project_url(@project)
15
+ else
16
+ render :action => :new
17
+ end
18
+ end
19
+
20
+ def edit
21
+ @project = Project.find(params[:id])
22
+ render
23
+ end
24
+
25
+ def update
26
+ @project = Project.find(params[:id])
27
+ if @project.update_attributes params[:project]
28
+ flash[:success] = 'Project was updated.'
29
+ redirect_to account_projects_url(current_account)
30
+ else
31
+ render :action => :edit
32
+ end
33
+ end
34
+
35
+ def destroy
36
+ @project = Project.find(params[:id])
37
+ @project.destroy
38
+ flash[:success] = "Project has been deleted"
39
+ redirect_to account_projects_url(@project.account)
40
+ end
41
+
42
+ def index
43
+ @projects = current_account.projects
44
+ render
45
+ end
46
+
47
+ private
48
+
49
+ def current_account
50
+ if params[:id]
51
+ Project.find(params[:id]).account
52
+ else
53
+ super
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,8 @@
1
+ class AccountMembership < ActiveRecord::Base
2
+ belongs_to :user
3
+ belongs_to :account
4
+
5
+ validates_presence_of :user_id
6
+ validates_presence_of :account_id
7
+ validates_uniqueness_of :user_id, :scope => :account_id
8
+ end
@@ -0,0 +1,86 @@
1
+ class Invitation < ActiveRecord::Base
2
+ belongs_to :account
3
+ validates_presence_of :account_id
4
+ validates_presence_of :email
5
+
6
+ after_create :deliver_invitation
7
+
8
+ attr_accessor :new_user_name, :new_user_password,
9
+ :new_user_password_confirmation, :existing_user_password
10
+ attr_writer :new_user_email, :existing_user_email
11
+ attr_reader :user
12
+ attr_protected :account_id
13
+
14
+ validate :validate_accepting_user, :on => :update
15
+
16
+ def account_name
17
+ account.name
18
+ end
19
+
20
+ def accept(attributes)
21
+ self.attributes = attributes
22
+ @user = existing_user || new_user
23
+ if valid?
24
+ @user.save!
25
+ @user.account_memberships.create!(:account => account, :admin => admin)
26
+ end
27
+ end
28
+
29
+ def new_user_email
30
+ @new_user_email ||= email
31
+ end
32
+
33
+ def existing_user_email
34
+ @existing_user_email ||= email
35
+ end
36
+
37
+ private
38
+
39
+ def deliver_invitation
40
+ InvitationMailer.invitation(self).deliver
41
+ end
42
+
43
+ def existing_user
44
+ if existing_user?
45
+ User.find_by_email(existing_user_email)
46
+ end
47
+ end
48
+
49
+ def existing_user?
50
+ existing_user_password.present?
51
+ end
52
+
53
+ def new_user
54
+ User.new(
55
+ :email => new_user_email,
56
+ :password => new_user_password,
57
+ :password_confirmation => new_user_password_confirmation,
58
+ :name => new_user_name
59
+ ).tap do |user|
60
+ user.email_confirmed = true
61
+ end
62
+ end
63
+
64
+ def validate_accepting_user
65
+ if existing_user?
66
+ validate_existing_user
67
+ else
68
+ validate_new_user
69
+ end
70
+ end
71
+
72
+ def validate_new_user
73
+ user.valid?
74
+ user.errors.each do |field, message|
75
+ errors.add("new_user_#{field}", message)
76
+ end
77
+ end
78
+
79
+ def validate_existing_user
80
+ if existing_user.nil?
81
+ errors.add(:existing_user_email, "isn't signed up")
82
+ elsif !existing_user.authenticated?(existing_user_password)
83
+ errors.add(:existing_user_password, "is incorrect")
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,16 @@
1
+ class ProjectMembership < ActiveRecord::Base
2
+ attr_accessible :user, :project, :user_id, :project_id
3
+
4
+ belongs_to :user
5
+ belongs_to :project
6
+
7
+ validate :ensure_account_member
8
+
9
+ private
10
+
11
+ def ensure_account_member
12
+ unless user.member_of?(project.account)
13
+ errors.add(:base, "This user is not a member of #{project.name}'s account")
14
+ end
15
+ end
16
+ end