saucy 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +12 -0
- data/app/models/membership.rb +4 -0
- data/app/views/layouts/saucy.html.erb +14 -2
- data/app/views/projects/_form.html.erb +20 -3
- data/app/views/projects/edit.html.erb +2 -4
- data/app/views/projects/new.html.erb +1 -3
- data/app/views/shared/_project_dropdown.html.erb +55 -0
- data/features/run_features.feature +2 -0
- data/lib/generators/saucy/features/features_generator.rb +8 -0
- data/lib/generators/saucy/features/templates/features/manage_projects.feature +34 -3
- data/lib/generators/saucy/features/templates/features/project_dropdown.feature +77 -0
- data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +22 -0
- data/lib/generators/saucy/features/templates/step_definitions/session_steps.rb +7 -0
- data/lib/saucy/project.rb +7 -1
- data/lib/saucy/projects_controller.rb +29 -3
- data/spec/controllers/projects_controller_spec.rb +61 -1
- data/spec/models/project_spec.rb +17 -0
- data/spec/models/user_spec.rb +2 -0
- metadata +22 -20
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.7.1
|
2
|
+
|
3
|
+
Introduce a project dropdown, include it with:
|
4
|
+
|
5
|
+
render :partial => "shared/project_dropdown"
|
6
|
+
|
7
|
+
Saucy provides the javascript for this dropdown, but not the CSS or the image
|
8
|
+
for the dropdown. The Javascript depends on jQuery.
|
9
|
+
|
10
|
+
Also, adds the ability to move a project to another project that you are an
|
11
|
+
admin on.
|
12
|
+
|
1
13
|
0.7.0
|
2
14
|
|
3
15
|
Split Saucy::Configuration.mailer_sender into support_email_address and manager_email_address.
|
data/app/models/membership.rb
CHANGED
@@ -11,14 +11,26 @@
|
|
11
11
|
<%= value %>
|
12
12
|
<% end -%>
|
13
13
|
|
14
|
+
<% if signed_in? -%>
|
15
|
+
<%= render :partial => 'shared/project_dropdown' %>
|
16
|
+
<% else %>
|
17
|
+
<%= link_to root_url do %>
|
18
|
+
<h1><%= t("app_name") %></h1>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
21
|
+
|
14
22
|
<% if signed_in? -%>
|
15
23
|
<%= link_to 'Sign out', sign_out_path, :method => :delete %>
|
16
24
|
<% else -%>
|
17
25
|
<%= link_to 'Sign in', sign_in_path %>
|
18
26
|
<% end -%>
|
19
27
|
|
20
|
-
<%= yield
|
21
|
-
<%= yield
|
28
|
+
<%= yield :subnav %>
|
29
|
+
<%= yield :header %>
|
30
|
+
|
22
31
|
<%= yield %>
|
32
|
+
|
33
|
+
<%= javascript_include_tag 'jquery' %>
|
34
|
+
<%= yield :javascript %>
|
23
35
|
</body>
|
24
36
|
</html>
|
@@ -1,15 +1,32 @@
|
|
1
1
|
<%= form.inputs do %>
|
2
|
-
<%= form.input :
|
2
|
+
<%= form.input :account, :collection => current_user.memberships.admin.collect(&:account) %>
|
3
|
+
<%= form.input :name, :hint => project_url(form.object).html_safe %>
|
3
4
|
<%= form.input :keyword, :wrapper_html => { :style => 'display: none' } %>
|
4
5
|
<% if form.object.persisted? -%>
|
5
6
|
<%= form.input :archived, :hint => t('project.archived.hint', :default => "Archived project don't count against your project total. You cannot create or edit stories or discussions.") %>
|
6
7
|
<% end -%>
|
7
8
|
<%= form.input :users, :label => "Admins",
|
8
9
|
:as => :check_boxes,
|
9
|
-
:collection => admins,
|
10
|
+
:collection => form.object.account.admins,
|
10
11
|
:wrapper_html => { :id => "project_admins_input" },
|
11
12
|
:input_html => { :disabled => true } %>
|
12
|
-
|
13
|
+
<% if form.object.account.non_admins.any? -%>
|
14
|
+
<%= form.input :users, :as => :check_boxes, :collection => form.object.account.non_admins %>
|
15
|
+
<% end -%>
|
13
16
|
<% end %>
|
14
17
|
|
18
|
+
<%= content_for :javascript do -%>
|
19
|
+
<%= javascript_tag do %>
|
20
|
+
$(function() {
|
21
|
+
$('#project_account_id').change(function() {
|
22
|
+
if($('#project_account_id').val() != "") {
|
23
|
+
console.log(window.location);
|
24
|
+
window.location.href = window.location.pathname + '?project[account_id]=' + $('#project_account_id').val();
|
25
|
+
return false;
|
26
|
+
}
|
27
|
+
});
|
28
|
+
});
|
29
|
+
<% end %>
|
30
|
+
<% end -%>
|
31
|
+
|
15
32
|
<%= render :partial => "shared/saucy_javascript" %>
|
@@ -11,10 +11,8 @@
|
|
11
11
|
|
12
12
|
<%= render :partial => 'accounts/subnav' %>
|
13
13
|
|
14
|
-
<%= semantic_form_for @project do |form| %>
|
15
|
-
<%= render 'form', :form
|
16
|
-
:non_admins => current_account.non_admins,
|
17
|
-
:admins => current_account.admins %>
|
14
|
+
<%= semantic_form_for @project, :url => account_project_url(current_account, @project) do |form| %>
|
15
|
+
<%= render 'form', :form => form %>
|
18
16
|
|
19
17
|
<%= form.buttons do %>
|
20
18
|
<%= form.commit_button %>
|
@@ -5,9 +5,7 @@
|
|
5
5
|
<%= render :partial => 'accounts/subnav' %>
|
6
6
|
|
7
7
|
<%= semantic_form_for(@project, :url => account_projects_url(current_account)) do |form| %>
|
8
|
-
<%= render 'form', :form
|
9
|
-
:non_admins => current_account.non_admins,
|
10
|
-
:admins => current_account.admins %>
|
8
|
+
<%= render 'form', :form => form %>
|
11
9
|
<%= form.buttons do -%>
|
12
10
|
<%= form.commit_button %>
|
13
11
|
<li><%= link_to 'Cancel', account_projects_path(current_account) %></li>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<% if current_user.projects.active.any? -%>
|
2
|
+
<ul id="project-selection">
|
3
|
+
<li>
|
4
|
+
<h1>
|
5
|
+
<%= link_to current_project? ? project_path(current_project) : root_url do -%>
|
6
|
+
<% if current_project? -%>
|
7
|
+
<span><%= current_project.name %></span>
|
8
|
+
<% else %>
|
9
|
+
<span>Projects</span>
|
10
|
+
<% end -%>
|
11
|
+
<% if current_user.memberships.admin.any? || current_user.projects.active.any? -%>
|
12
|
+
<img src="/images/arrow-down.png" alt="arrow down" />
|
13
|
+
<% end -%>
|
14
|
+
<% end %>
|
15
|
+
</h1>
|
16
|
+
<ul>
|
17
|
+
<% current_user.projects.active.each do |project| %>
|
18
|
+
<li><%= link_to project.name, project_path(project) -%></li>
|
19
|
+
<% end %>
|
20
|
+
<% if current_user.memberships.admin.any? -%>
|
21
|
+
<li>
|
22
|
+
<%= link_to current_account? && current_user.admin_of?(current_account) ? new_project_path(current_account) : new_project_path(current_user.memberships.admin.first.account) do %>
|
23
|
+
+ Create a new project
|
24
|
+
<% end %>
|
25
|
+
</li>
|
26
|
+
<% end -%>
|
27
|
+
</ul>
|
28
|
+
</li>
|
29
|
+
</ul>
|
30
|
+
<% end -%>
|
31
|
+
|
32
|
+
<%= content_for :javascript do -%>
|
33
|
+
<%= javascript_tag do %>
|
34
|
+
$(function() {
|
35
|
+
$('#project-selection h1 a').click(function() {
|
36
|
+
var dropdownbutton = $('#project-selection h1 a');
|
37
|
+
var dropdownlist = $('#project-selection li ul');
|
38
|
+
if(dropdownbutton.hasClass("expanded")) {
|
39
|
+
dropdownbutton.removeClass("expanded");
|
40
|
+
dropdownlist.removeClass("expanded");
|
41
|
+
} else {
|
42
|
+
dropdownbutton.addClass("expanded");
|
43
|
+
dropdownlist.addClass("expanded");
|
44
|
+
}
|
45
|
+
return false;
|
46
|
+
});
|
47
|
+
|
48
|
+
$('html *').live('click', function() {
|
49
|
+
if($(this).parents('.expanded').length == 0) {
|
50
|
+
$('.expanded').removeClass('expanded');
|
51
|
+
}
|
52
|
+
});
|
53
|
+
});
|
54
|
+
<% end %>
|
55
|
+
<% end -%>
|
@@ -19,9 +19,11 @@ Feature: generate a saucy application and run rake
|
|
19
19
|
gem "shoulda"
|
20
20
|
gem "launchy"
|
21
21
|
gem "timecop"
|
22
|
+
gem "jquery-rails"
|
22
23
|
"""
|
23
24
|
When I add the "saucy" gem from this project as a dependency
|
24
25
|
And I successfully run "bundle install --local"
|
26
|
+
And I successfully run "rails generate jquery:install --force"
|
25
27
|
And I bootstrap the application for clearance
|
26
28
|
And I bootstrap the application for saucy
|
27
29
|
|
@@ -58,6 +58,14 @@ DESC
|
|
58
58
|
user = User.find_by_email!($1)
|
59
59
|
account = user.accounts.order("created_at desc").first
|
60
60
|
new_account_project_path(account)
|
61
|
+
when /^the "([^"]*)" project page$/
|
62
|
+
project = Project.find_by_name!($1)
|
63
|
+
account_project_path(project.account, project)
|
64
|
+
when /^the new project page for the "([^"]+)" account$/
|
65
|
+
account = Account.find_by_name!($1)
|
66
|
+
new_account_project_path(account)
|
67
|
+
|
68
|
+
|
61
69
|
PATHS
|
62
70
|
|
63
71
|
replace_in_file "features/support/paths.rb",
|
@@ -4,13 +4,15 @@ Feature: Manage Projects
|
|
4
4
|
In order to have a project for each of my software applications
|
5
5
|
|
6
6
|
Background:
|
7
|
-
Given the following
|
8
|
-
| name
|
9
|
-
| Test
|
7
|
+
Given the following accounts exist:
|
8
|
+
| name | keyword |
|
9
|
+
| Test | test |
|
10
|
+
| Another | another |
|
10
11
|
And the following user exists:
|
11
12
|
| name | email |
|
12
13
|
| Joe User | joe@example.com |
|
13
14
|
And "joe@example.com" is an admin of the "Test" account
|
15
|
+
And "joe@example.com" is an admin of the "Another" account
|
14
16
|
And I sign in as "joe@example.com"
|
15
17
|
|
16
18
|
Scenario: Create new project
|
@@ -34,6 +36,35 @@ Feature: Manage Projects
|
|
34
36
|
And I press "Update"
|
35
37
|
Then I should see "Name Change"
|
36
38
|
|
39
|
+
@javascript
|
40
|
+
Scenario: Move a project
|
41
|
+
Given the following projects exist:
|
42
|
+
| account | name |
|
43
|
+
| name: Test | Project 1 |
|
44
|
+
When I go to the projects page for the "Test" account
|
45
|
+
And I follow "Project 1" within "ul.projects"
|
46
|
+
And I select "Another" from "Account"
|
47
|
+
And I press "Update"
|
48
|
+
And I should see "Project 1" within "ul.projects"
|
49
|
+
Then I should be on the projects page for the "Another" account
|
50
|
+
|
51
|
+
@javascript
|
52
|
+
Scenario: Move a project when account being moved to is at the account limit
|
53
|
+
Given the following limit exists for the "Another" account:
|
54
|
+
| name | value |
|
55
|
+
| projects | 1 |
|
56
|
+
And the following projects exist:
|
57
|
+
| account | name |
|
58
|
+
| name: Test | Project 1 |
|
59
|
+
| name: Another | Project 2 |
|
60
|
+
When I go to the projects page for the "Test" account
|
61
|
+
And I follow "Project 1" within "ul.projects"
|
62
|
+
And I select "Another" from "Account"
|
63
|
+
And I press "Update"
|
64
|
+
Then I should see "at your limit"
|
65
|
+
When I go to the projects page for the "Test" account
|
66
|
+
Then I should see "Project 1"
|
67
|
+
|
37
68
|
Scenario: Archive a project
|
38
69
|
Given the following project exists:
|
39
70
|
| account | name |
|
@@ -0,0 +1,77 @@
|
|
1
|
+
Feature: Project Dropdown
|
2
|
+
As a user
|
3
|
+
So that I can work on several projects
|
4
|
+
I can easily switch between projects
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I am signed in as "user@example.com" under the "Werther's" account
|
8
|
+
And the following projects exist:
|
9
|
+
| name | account |
|
10
|
+
| Pacelog | name: Werther's |
|
11
|
+
| Narrator | name: Werther's |
|
12
|
+
| Flapper | name: Werther's |
|
13
|
+
And "user@example.com" is a member of the "Pacelog" project
|
14
|
+
|
15
|
+
@javascript
|
16
|
+
Scenario: Navigate from one project to another
|
17
|
+
Given "user@example.com" is a member of the "Narrator" project
|
18
|
+
When I go to the "Pacelog" project page
|
19
|
+
Then the page should not include "#project-selection ul.expanded"
|
20
|
+
When I click "#project-selection h1 a img"
|
21
|
+
Then the page should include "#project-selection ul.expanded"
|
22
|
+
And I should not see "Flapper"
|
23
|
+
When I follow "Narrator" within "#project-selection ul"
|
24
|
+
Then I should be on the "Narrator" project page
|
25
|
+
|
26
|
+
@javascript
|
27
|
+
Scenario: Click outside the projects dropdown to hide it
|
28
|
+
Given "user@example.com" is a member of the "Narrator" project
|
29
|
+
When I go to the "Narrator" project page
|
30
|
+
Then the page should not include "#project-selection ul.expanded"
|
31
|
+
When I click "#project-selection h1 a img"
|
32
|
+
Then the page should include "#project-selection ul.expanded"
|
33
|
+
When I click "body"
|
34
|
+
Then the page should not include "#project-selection ul.expanded"
|
35
|
+
|
36
|
+
Scenario: On project pages, the project name is rendered in the header
|
37
|
+
When I go to the "Pacelog" project page
|
38
|
+
Then I should see "Pacelog" within "#project-selection h1 a"
|
39
|
+
|
40
|
+
Scenario: On project pages with multiple projects, the project name and chooser are rendered in the header
|
41
|
+
Given "user@example.com" is a member of the "Narrator" project
|
42
|
+
When I go to the "Pacelog" project page
|
43
|
+
Then I should see "Pacelog" within "#project-selection h1 a"
|
44
|
+
And the page should include "#project-selection h1 a img"
|
45
|
+
|
46
|
+
Scenario: On non-project pages, the account name is rendered in the header with the project chooser
|
47
|
+
Given "user@example.com" is an admin of the "Narrator" project
|
48
|
+
When I am on the memberships page for the "Werther's" account
|
49
|
+
Then I should see "Projects" within "#project-selection h1 a"
|
50
|
+
And the page should include "#project-selection h1 a img"
|
51
|
+
|
52
|
+
When I am on the edit profile page
|
53
|
+
Then I should see "Projects" within "#project-selection h1 a"
|
54
|
+
And the page should include "#project-selection h1 a img"
|
55
|
+
|
56
|
+
Scenario: An admin on non-project pages, the new project link goes to the new project page for the right account
|
57
|
+
Given "user@example.com" is an admin of the "Narrator" project
|
58
|
+
When I am on the edit profile page
|
59
|
+
Then I should see "Create a new project" within "#project-selection"
|
60
|
+
When I follow "Create a new project"
|
61
|
+
Then I should be on the new project page for the "Werther's" account
|
62
|
+
|
63
|
+
Scenario: An admin on project pages, the new project link goes to the new project page for the right account
|
64
|
+
Given "user@example.com" is an admin of the "Narrator" project
|
65
|
+
When I go to the "Pacelog" project page
|
66
|
+
Then I should see "Create a new project" within "#project-selection"
|
67
|
+
When I follow "Create a new project"
|
68
|
+
Then I should be on the new project page for the "Werther's" account
|
69
|
+
|
70
|
+
Scenario: A non-admin on non-project pages, should not see the new project link
|
71
|
+
Given "user@example.com" is a member of the "Narrator" project
|
72
|
+
When I am on the edit profile page
|
73
|
+
Then I should not see "Create a new project" within "#project-selection"
|
74
|
+
|
75
|
+
Scenario: A non-admin on project pages, should not see the new project link
|
76
|
+
When I go to the "Pacelog" project page
|
77
|
+
Then I should not see "Create a new project" within "#project-selection"
|
@@ -27,3 +27,25 @@ end
|
|
27
27
|
Then /^I there should be a link to the help site$/ do
|
28
28
|
page.should have_css("a[href*='help.example.com']")
|
29
29
|
end
|
30
|
+
|
31
|
+
Then /^"([^"]*)" should not be expanded$/ do |selector|
|
32
|
+
page.has_css?("#{selector}")
|
33
|
+
page.has_no_css?("#{selector}.expanded")
|
34
|
+
end
|
35
|
+
|
36
|
+
Then /^"([^"]*)" should be expanded/ do |selector|
|
37
|
+
page.has_css?("#{selector}")
|
38
|
+
page.has_css?("#{selector}.expanded")
|
39
|
+
end
|
40
|
+
|
41
|
+
When %r{^I click "([^"]*)"$} do |selector|
|
42
|
+
find(selector).click
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^the page should (not )?include "([^"]*)"$/ do |should_not_contain, selector|
|
46
|
+
if should_not_contain.present?
|
47
|
+
page.should have_no_css(selector)
|
48
|
+
else
|
49
|
+
page.should have_css(selector)
|
50
|
+
end
|
51
|
+
end
|
@@ -9,6 +9,13 @@ Given /^I (?:am signed in|sign in) as an admin of the "([^"]+)" project$/ do |pr
|
|
9
9
|
When %{I sign in as "#{user.email}"}
|
10
10
|
end
|
11
11
|
|
12
|
+
Given /^I am signed in as "([^"]*)" under the "([^"]*)" account$/ do |email, account_name|
|
13
|
+
account = Factory(:account, :name => account_name)
|
14
|
+
user = Factory(:user, :email => email)
|
15
|
+
membership = Factory(:membership, :user => user, :account => account)
|
16
|
+
When %{I sign in as "#{user.email}"}
|
17
|
+
end
|
18
|
+
|
12
19
|
Given /^I am signed in as an admin of the "([^"]*)" account$/ do |account_name|
|
13
20
|
account = Account.find_by_name!(account_name)
|
14
21
|
user = Factory(:user)
|
data/lib/saucy/project.rb
CHANGED
@@ -21,6 +21,8 @@ module Saucy
|
|
21
21
|
after_create :setup_memberships
|
22
22
|
after_update :update_memberships
|
23
23
|
|
24
|
+
attr_protected :account, :account_id
|
25
|
+
|
24
26
|
# We have to define these here instead of mixing them in,
|
25
27
|
# because ActiveRecord does the same.
|
26
28
|
|
@@ -110,8 +112,12 @@ module Saucy
|
|
110
112
|
end
|
111
113
|
|
112
114
|
def ensure_account_within_limit
|
115
|
+
message = "You are at your limit of %{name} for your current plan."
|
113
116
|
if archived_changed? && !archived? && !Limit.can_add_one?("projects", account)
|
114
|
-
errors.add(:archived, I18n.t("saucy.errors.limited", :default =>
|
117
|
+
errors.add(:archived, I18n.t("saucy.errors.limited", :default => message, :name => 'projects'))
|
118
|
+
end
|
119
|
+
if account_id_changed? && !Limit.can_add_one?("projects", account)
|
120
|
+
errors.add(:account_id, I18n.t("saucy.errors.limited", :default => message, :name => 'projects'))
|
115
121
|
end
|
116
122
|
end
|
117
123
|
end
|
@@ -3,17 +3,22 @@ module Saucy
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
+
helper_method :current_project?
|
6
7
|
before_filter :authorize
|
7
8
|
before_filter :authorize_member, :only => :show
|
8
9
|
before_filter :authorize_admin, :except => [:show]
|
9
10
|
before_filter :ensure_active_account, :only => [:show, :destroy, :index]
|
10
11
|
before_filter :ensure_account_within_projects_limit, :only => [:new, :create]
|
12
|
+
before_filter :ensure_admin_for_project_account, :only => [:edit, :create, :update]
|
11
13
|
layout Saucy::Layouts.to_proc
|
12
14
|
end
|
13
15
|
|
14
16
|
module InstanceMethods
|
15
17
|
def new
|
16
18
|
@project = current_account.projects.build_with_default_permissions
|
19
|
+
if @project.keyword.blank?
|
20
|
+
@project.keyword = 'keyword'
|
21
|
+
end
|
17
22
|
end
|
18
23
|
|
19
24
|
def create
|
@@ -27,13 +32,16 @@ module Saucy
|
|
27
32
|
end
|
28
33
|
|
29
34
|
def edit
|
30
|
-
current_project
|
35
|
+
@project = current_project
|
36
|
+
set_project_account_if_moving
|
31
37
|
end
|
32
38
|
|
33
39
|
def update
|
34
|
-
|
40
|
+
@project = current_project
|
41
|
+
set_project_account_if_moving
|
42
|
+
if @project.update_attributes params[:project]
|
35
43
|
flash[:success] = 'Project was updated.'
|
36
|
-
redirect_to account_projects_url(
|
44
|
+
redirect_to account_projects_url(@project.account)
|
37
45
|
else
|
38
46
|
render :action => :edit
|
39
47
|
end
|
@@ -56,13 +64,31 @@ module Saucy
|
|
56
64
|
|
57
65
|
private
|
58
66
|
|
67
|
+
def set_project_account_if_moving
|
68
|
+
if params[:project] && params[:project][:account_id]
|
69
|
+
@project.account_id = params[:project][:account_id]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
59
73
|
def current_project
|
60
74
|
@project ||= current_account.projects.find_by_keyword!(params[:id])
|
61
75
|
end
|
62
76
|
|
77
|
+
def current_project?
|
78
|
+
params[:id].present?
|
79
|
+
end
|
80
|
+
|
63
81
|
def ensure_account_within_projects_limit
|
64
82
|
ensure_account_within_limit("projects")
|
65
83
|
end
|
84
|
+
|
85
|
+
def ensure_admin_for_project_account
|
86
|
+
if params[:project] && params[:project][:account_id]
|
87
|
+
if !current_user.admin_of?(::Account.find(params[:project][:account_id]))
|
88
|
+
redirect_to account_projects_url(current_account), :alert => t('account.permission_denied', :default => "You do not have permission to this account.")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
66
92
|
end
|
67
93
|
end
|
68
94
|
end
|
@@ -87,13 +87,73 @@ describe ProjectsController, "update", :as => :project_admin do
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
describe ProjectsController, "update with account that you don't have access to", :as => :project_admin do
|
91
|
+
before do
|
92
|
+
put :update, :project => Factory.attributes_for(:project, :account_id => Factory(:account).id),
|
93
|
+
:id => project.to_param,
|
94
|
+
:account_id => account.to_param
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should show an error" do
|
98
|
+
should set_the_flash.to(/permission/i)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should redirect to account_projects_url" do
|
102
|
+
should redirect_to(account_projects_url(account))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe ProjectsController, "create with account that you don't have access to", :as => :account_admin do
|
107
|
+
before do
|
108
|
+
post :create,
|
109
|
+
:project => Factory.attributes_for(:project, :account_id => Factory(:account).id),
|
110
|
+
:account_id => account.to_param
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should show an error" do
|
114
|
+
should set_the_flash.to(/permission/i)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should redirect to account_projects_url" do
|
118
|
+
should redirect_to(account_projects_url(account))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe ProjectsController, "edit with account that you don't have access to", :as => :project_admin do
|
123
|
+
before do
|
124
|
+
get :edit, :id => project.to_param, :account_id => account.to_param, :project => { :account_id => Factory(:account).id }
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should show an error" do
|
128
|
+
should set_the_flash.to(/permission/i)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should redirect to account_projects_url" do
|
132
|
+
should redirect_to(account_projects_url(account))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe ProjectsController, "edit with account that you have access to", :as => :project_admin do
|
137
|
+
before do
|
138
|
+
@other_account = Factory(:account)
|
139
|
+
Factory(:membership, :user => current_user, :account => @other_account, :admin => true)
|
140
|
+
get :edit, :id => project.to_param, :account_id => account.to_param, :project => { :account_id => @other_account.id }
|
141
|
+
end
|
142
|
+
|
143
|
+
it { should assign_to(:project) }
|
144
|
+
|
145
|
+
it "sets the new account on the project" do
|
146
|
+
assigns(:project).account.should == @other_account
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
90
150
|
describe ProjectsController, "destroy", :as => :project_admin do
|
91
151
|
before do
|
92
152
|
delete :destroy, :id => project.to_param, :account_id => account.to_param
|
93
153
|
end
|
94
154
|
|
95
155
|
it { should set_the_flash.to(/deleted/) }
|
96
|
-
it { should assign_to(
|
156
|
+
it { should assign_to(:project) }
|
97
157
|
|
98
158
|
it "should redirect to account_projects_url" do
|
99
159
|
should redirect_to(account_projects_url(account))
|
data/spec/models/project_spec.rb
CHANGED
@@ -14,6 +14,9 @@ describe Project do
|
|
14
14
|
it { should_not allow_value("HELLO").for(:keyword) }
|
15
15
|
it { should_not allow_value("hello world").for(:keyword) }
|
16
16
|
|
17
|
+
it { should_not allow_mass_assignment_of(:account_id) }
|
18
|
+
it { should_not allow_mass_assignment_of(:account) }
|
19
|
+
|
17
20
|
it "finds projects visible to a user" do
|
18
21
|
account = Factory(:account)
|
19
22
|
user = Factory(:user)
|
@@ -66,6 +69,20 @@ describe Project do
|
|
66
69
|
end
|
67
70
|
end
|
68
71
|
|
72
|
+
context "project moving to an account at its project limit" do
|
73
|
+
before do
|
74
|
+
@project = Factory(:project)
|
75
|
+
@account = Factory(:account)
|
76
|
+
Limit.stubs(:can_add_one?).with("projects", @account).returns(false)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "cannot be moved" do
|
80
|
+
@project.account = @account
|
81
|
+
@project.save.should_not be
|
82
|
+
@project.errors[:account_id].first.should match(/at your limit/)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
69
86
|
it "should give its keyword for to_param" do
|
70
87
|
project = Factory(:project)
|
71
88
|
project.to_param.should == project.keyword
|
data/spec/models/user_spec.rb
CHANGED
@@ -14,6 +14,8 @@ describe User, "valid" do
|
|
14
14
|
account = Factory(:account)
|
15
15
|
Factory(:membership, :user => subject, :admin => true, :account => account)
|
16
16
|
subject.should be_admin_of(account)
|
17
|
+
|
18
|
+
subject.memberships.admin.first.account.should == account
|
17
19
|
end
|
18
20
|
|
19
21
|
it "isn't an admin of an account with a non admin membership" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saucy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 1
|
5
|
+
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 0.7.
|
9
|
+
- 1
|
10
|
+
version: 0.7.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- thoughtbot, inc.
|
@@ -18,12 +18,12 @@ autorequire:
|
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
20
|
|
21
|
-
date: 2011-05-
|
21
|
+
date: 2011-05-06 00:00:00 -04:00
|
22
22
|
default_executable:
|
23
23
|
dependencies:
|
24
24
|
- !ruby/object:Gem::Dependency
|
25
|
+
name: clearance
|
25
26
|
prerelease: false
|
26
|
-
type: :runtime
|
27
27
|
requirement: &id001 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
@@ -35,11 +35,11 @@ dependencies:
|
|
35
35
|
- 11
|
36
36
|
- 0
|
37
37
|
version: 0.11.0
|
38
|
-
|
38
|
+
type: :runtime
|
39
39
|
version_requirements: *id001
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
|
+
name: formtastic
|
41
42
|
prerelease: false
|
42
|
-
type: :runtime
|
43
43
|
requirement: &id002 !ruby/object:Gem::Requirement
|
44
44
|
none: false
|
45
45
|
requirements:
|
@@ -50,11 +50,11 @@ dependencies:
|
|
50
50
|
- 1
|
51
51
|
- 2
|
52
52
|
version: "1.2"
|
53
|
-
|
53
|
+
type: :runtime
|
54
54
|
version_requirements: *id002
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
+
name: railties
|
56
57
|
prerelease: false
|
57
|
-
type: :runtime
|
58
58
|
requirement: &id003 !ruby/object:Gem::Requirement
|
59
59
|
none: false
|
60
60
|
requirements:
|
@@ -66,11 +66,11 @@ dependencies:
|
|
66
66
|
- 0
|
67
67
|
- 3
|
68
68
|
version: 3.0.3
|
69
|
-
|
69
|
+
type: :runtime
|
70
70
|
version_requirements: *id003
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
|
+
name: braintree
|
72
73
|
prerelease: false
|
73
|
-
type: :runtime
|
74
74
|
requirement: &id004 !ruby/object:Gem::Requirement
|
75
75
|
none: false
|
76
76
|
requirements:
|
@@ -82,11 +82,11 @@ dependencies:
|
|
82
82
|
- 6
|
83
83
|
- 2
|
84
84
|
version: 2.6.2
|
85
|
-
|
85
|
+
type: :runtime
|
86
86
|
version_requirements: *id004
|
87
87
|
- !ruby/object:Gem::Dependency
|
88
|
+
name: sham_rack
|
88
89
|
prerelease: false
|
89
|
-
type: :runtime
|
90
90
|
requirement: &id005 !ruby/object:Gem::Requirement
|
91
91
|
none: false
|
92
92
|
requirements:
|
@@ -98,11 +98,11 @@ dependencies:
|
|
98
98
|
- 3
|
99
99
|
- 3
|
100
100
|
version: 1.3.3
|
101
|
-
|
101
|
+
type: :runtime
|
102
102
|
version_requirements: *id005
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
|
+
name: sinatra
|
104
105
|
prerelease: false
|
105
|
-
type: :runtime
|
106
106
|
requirement: &id006 !ruby/object:Gem::Requirement
|
107
107
|
none: false
|
108
108
|
requirements:
|
@@ -114,11 +114,11 @@ dependencies:
|
|
114
114
|
- 1
|
115
115
|
- 2
|
116
116
|
version: 1.1.2
|
117
|
-
|
117
|
+
type: :runtime
|
118
118
|
version_requirements: *id006
|
119
119
|
- !ruby/object:Gem::Dependency
|
120
|
+
name: aruba
|
120
121
|
prerelease: false
|
121
|
-
type: :development
|
122
122
|
requirement: &id007 !ruby/object:Gem::Requirement
|
123
123
|
none: false
|
124
124
|
requirements:
|
@@ -130,7 +130,7 @@ dependencies:
|
|
130
130
|
- 2
|
131
131
|
- 6
|
132
132
|
version: 0.2.6
|
133
|
-
|
133
|
+
type: :development
|
134
134
|
version_requirements: *id007
|
135
135
|
description: Clearance-based Rails engine for Software as a Service (Saas) that provides account and project management
|
136
136
|
email: support@thoughtbot.com
|
@@ -196,6 +196,7 @@ files:
|
|
196
196
|
- app/views/projects/index.html.erb
|
197
197
|
- app/views/projects/new.html.erb
|
198
198
|
- app/views/projects/show.html.erb
|
199
|
+
- app/views/shared/_project_dropdown.html.erb
|
199
200
|
- app/views/shared/_saucy_javascript.html.erb
|
200
201
|
- lib/generators/saucy/base.rb
|
201
202
|
- lib/generators/saucy/features/features_generator.rb
|
@@ -249,6 +250,7 @@ files:
|
|
249
250
|
- lib/generators/saucy/features/templates/features/manage_projects.feature
|
250
251
|
- lib/generators/saucy/features/templates/features/manage_users.feature
|
251
252
|
- lib/generators/saucy/features/templates/features/new_account.feature
|
253
|
+
- lib/generators/saucy/features/templates/features/project_dropdown.feature
|
252
254
|
- lib/generators/saucy/features/templates/features/sign_up.feature
|
253
255
|
- lib/generators/saucy/features/templates/features/sign_up_paid.feature
|
254
256
|
- lib/generators/saucy/features/templates/features/trial_plans.feature
|
@@ -314,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
314
316
|
requirements: []
|
315
317
|
|
316
318
|
rubyforge_project:
|
317
|
-
rubygems_version: 1.
|
319
|
+
rubygems_version: 1.3.7
|
318
320
|
signing_key:
|
319
321
|
specification_version: 3
|
320
322
|
summary: Clearance-based Rails engine for SaaS
|