saucy 0.7.0 → 0.7.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.
- 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
|