saasy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/CHANGELOG.md +114 -0
  2. data/Gemfile +26 -0
  3. data/README.md +118 -0
  4. data/Rakefile +38 -0
  5. data/app/controllers/accounts_controller.rb +68 -0
  6. data/app/controllers/billings_controller.rb +25 -0
  7. data/app/controllers/invitations_controller.rb +65 -0
  8. data/app/controllers/memberships_controller.rb +45 -0
  9. data/app/controllers/plans_controller.rb +24 -0
  10. data/app/controllers/profiles_controller.rb +19 -0
  11. data/app/helpers/limits_helper.rb +13 -0
  12. data/app/mailers/billing_mailer.rb +53 -0
  13. data/app/mailers/invitation_mailer.rb +18 -0
  14. data/app/models/invitation.rb +113 -0
  15. data/app/models/limit.rb +49 -0
  16. data/app/models/membership.rb +26 -0
  17. data/app/models/permission.rb +19 -0
  18. data/app/models/signup.rb +163 -0
  19. data/app/views/accounts/_account.html.erb +9 -0
  20. data/app/views/accounts/_blank_slate.html.erb +6 -0
  21. data/app/views/accounts/_projects.html.erb +12 -0
  22. data/app/views/accounts/_subnav.html.erb +10 -0
  23. data/app/views/accounts/edit.html.erb +34 -0
  24. data/app/views/accounts/index.html.erb +9 -0
  25. data/app/views/accounts/new.html.erb +36 -0
  26. data/app/views/billing_mailer/completed_trial.text.erb +13 -0
  27. data/app/views/billing_mailer/expiring_trial.text.erb +15 -0
  28. data/app/views/billing_mailer/new_unactivated.text.erb +1 -0
  29. data/app/views/billing_mailer/problem.html.erb +13 -0
  30. data/app/views/billing_mailer/problem.text.erb +14 -0
  31. data/app/views/billing_mailer/receipt.html.erb +41 -0
  32. data/app/views/billing_mailer/receipt.text.erb +25 -0
  33. data/app/views/billings/_form.html.erb +8 -0
  34. data/app/views/billings/edit.html.erb +13 -0
  35. data/app/views/billings/show.html.erb +29 -0
  36. data/app/views/invitation_mailer/invitation.text.erb +6 -0
  37. data/app/views/invitations/new.html.erb +17 -0
  38. data/app/views/invitations/show.html.erb +22 -0
  39. data/app/views/layouts/saucy.html.erb +36 -0
  40. data/app/views/limits/_meter.html.erb +13 -0
  41. data/app/views/memberships/edit.html.erb +21 -0
  42. data/app/views/memberships/index.html.erb +17 -0
  43. data/app/views/plans/_plan.html.erb +32 -0
  44. data/app/views/plans/_terms.html.erb +15 -0
  45. data/app/views/plans/edit.html.erb +33 -0
  46. data/app/views/plans/index.html.erb +12 -0
  47. data/app/views/profiles/_inputs.html.erb +5 -0
  48. data/app/views/profiles/edit.html.erb +36 -0
  49. data/app/views/projects/_form.html.erb +36 -0
  50. data/app/views/projects/edit.html.erb +22 -0
  51. data/app/views/projects/index.html.erb +28 -0
  52. data/app/views/projects/new.html.erb +13 -0
  53. data/app/views/projects/show.html.erb +0 -0
  54. data/app/views/shared/_project_dropdown.html.erb +55 -0
  55. data/app/views/shared/_saucy_javascript.html.erb +33 -0
  56. data/config/locales/en.yml +37 -0
  57. data/config/routes.rb +19 -0
  58. data/features/run_features.feature +83 -0
  59. data/features/step_definitions/clearance_steps.rb +45 -0
  60. data/features/step_definitions/rails_steps.rb +73 -0
  61. data/features/step_definitions/saucy_steps.rb +8 -0
  62. data/features/support/env.rb +4 -0
  63. data/features/support/file.rb +11 -0
  64. data/lib/generators/saucy/base.rb +18 -0
  65. data/lib/generators/saucy/features/features_generator.rb +91 -0
  66. data/lib/generators/saucy/features/templates/README +3 -0
  67. data/lib/generators/saucy/features/templates/factories.rb +71 -0
  68. data/lib/generators/saucy/features/templates/features/edit_profile.feature +9 -0
  69. data/lib/generators/saucy/features/templates/features/edit_project_permissions.feature +37 -0
  70. data/lib/generators/saucy/features/templates/features/edit_user_permissions.feature +47 -0
  71. data/lib/generators/saucy/features/templates/features/manage_account.feature +35 -0
  72. data/lib/generators/saucy/features/templates/features/manage_billing.feature +93 -0
  73. data/lib/generators/saucy/features/templates/features/manage_plan.feature +143 -0
  74. data/lib/generators/saucy/features/templates/features/manage_projects.feature +139 -0
  75. data/lib/generators/saucy/features/templates/features/manage_users.feature +142 -0
  76. data/lib/generators/saucy/features/templates/features/new_account.feature +33 -0
  77. data/lib/generators/saucy/features/templates/features/project_dropdown.feature +77 -0
  78. data/lib/generators/saucy/features/templates/features/sign_up.feature +32 -0
  79. data/lib/generators/saucy/features/templates/features/sign_up_paid.feature +65 -0
  80. data/lib/generators/saucy/features/templates/features/trial_plans.feature +82 -0
  81. data/lib/generators/saucy/features/templates/step_definitions/account_steps.rb +30 -0
  82. data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +25 -0
  83. data/lib/generators/saucy/features/templates/step_definitions/cron_steps.rb +23 -0
  84. data/lib/generators/saucy/features/templates/step_definitions/email_steps.rb +40 -0
  85. data/lib/generators/saucy/features/templates/step_definitions/factory_girl_steps.rb +1 -0
  86. data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +51 -0
  87. data/lib/generators/saucy/features/templates/step_definitions/plan_steps.rb +16 -0
  88. data/lib/generators/saucy/features/templates/step_definitions/project_steps.rb +4 -0
  89. data/lib/generators/saucy/features/templates/step_definitions/session_steps.rb +37 -0
  90. data/lib/generators/saucy/features/templates/step_definitions/user_steps.rb +100 -0
  91. data/lib/generators/saucy/features/templates/support/braintree.rb +5 -0
  92. data/lib/generators/saucy/install/install_generator.rb +40 -0
  93. data/lib/generators/saucy/install/templates/controllers/projects_controller.rb +3 -0
  94. data/lib/generators/saucy/install/templates/create_saucy_tables.rb +115 -0
  95. data/lib/generators/saucy/install/templates/models/account.rb +3 -0
  96. data/lib/generators/saucy/install/templates/models/plan.rb +3 -0
  97. data/lib/generators/saucy/install/templates/models/project.rb +3 -0
  98. data/lib/generators/saucy/specs/specs_generator.rb +20 -0
  99. data/lib/generators/saucy/specs/templates/support/braintree.rb +5 -0
  100. data/lib/generators/saucy/views/views_generator.rb +23 -0
  101. data/lib/saucy.rb +10 -0
  102. data/lib/saucy/account.rb +132 -0
  103. data/lib/saucy/account_authorization.rb +67 -0
  104. data/lib/saucy/configuration.rb +29 -0
  105. data/lib/saucy/engine.rb +35 -0
  106. data/lib/saucy/fake_braintree.rb +134 -0
  107. data/lib/saucy/layouts.rb +36 -0
  108. data/lib/saucy/plan.rb +54 -0
  109. data/lib/saucy/project.rb +125 -0
  110. data/lib/saucy/projects_controller.rb +94 -0
  111. data/lib/saucy/railties/tasks.rake +28 -0
  112. data/lib/saucy/routing_extensions.rb +121 -0
  113. data/lib/saucy/subscription.rb +237 -0
  114. data/lib/saucy/user.rb +30 -0
  115. data/spec/controllers/accounts_controller_spec.rb +228 -0
  116. data/spec/controllers/application_controller_spec.rb +32 -0
  117. data/spec/controllers/invitations_controller_spec.rb +215 -0
  118. data/spec/controllers/memberships_controller_spec.rb +117 -0
  119. data/spec/controllers/plans_controller_spec.rb +13 -0
  120. data/spec/controllers/profiles_controller_spec.rb +48 -0
  121. data/spec/controllers/projects_controller_spec.rb +216 -0
  122. data/spec/environment.rb +95 -0
  123. data/spec/layouts_spec.rb +21 -0
  124. data/spec/mailers/billing_mailer_spec.rb +68 -0
  125. data/spec/mailers/invitiation_mailer_spec.rb +19 -0
  126. data/spec/models/account_spec.rb +218 -0
  127. data/spec/models/invitation_spec.rb +320 -0
  128. data/spec/models/limit_spec.rb +70 -0
  129. data/spec/models/membership_spec.rb +37 -0
  130. data/spec/models/permission_spec.rb +30 -0
  131. data/spec/models/plan_spec.rb +81 -0
  132. data/spec/models/project_spec.rb +223 -0
  133. data/spec/models/signup_spec.rb +177 -0
  134. data/spec/models/subscription_spec.rb +481 -0
  135. data/spec/models/user_spec.rb +72 -0
  136. data/spec/route_extensions_spec.rb +51 -0
  137. data/spec/saucy_spec.rb +62 -0
  138. data/spec/scaffold/config/routes.rb +5 -0
  139. data/spec/spec_helper.rb +39 -0
  140. data/spec/support/authentication_helpers.rb +81 -0
  141. data/spec/support/authorization_helpers.rb +56 -0
  142. data/spec/support/braintree.rb +7 -0
  143. data/spec/support/clearance_matchers.rb +55 -0
  144. data/spec/support/notifications.rb +57 -0
  145. data/spec/views/accounts/_account.html.erb_spec.rb +37 -0
  146. metadata +325 -0
@@ -0,0 +1,114 @@
1
+ 0.8.2
2
+
3
+ Added a uniqueness validation and database constraint for member_id/project_id on permissions. This prevents somebody from being added to the same project twice, which would display it multiple times in the project index and dropdown. When upgrading, make sure this index is in place:
4
+
5
+ remove_index :permissions, :name => [:membership_and_project]
6
+ add_index :permissions, [:membership_id, :project_id], :name => [:membership_and_project], :unique => true
7
+
8
+ You'll need to make sure that no existing duplicates exist before applying the unique index.
9
+
10
+ 0.8.1
11
+
12
+ Fixed a missing translation key.
13
+
14
+ 0.8.0
15
+
16
+ Added a notification for trials that just ended. When upgrading, you'll need to
17
+ add to add a boolean notified_of_completed_trial column to the accounts table,
18
+ which should be NOT NULL and default to false. These notifications go out with
19
+ the daily Saucy jobs.
20
+
21
+ 0.7.3
22
+
23
+ Added a uniqueness constraint for keyword on accounts/projects. When upgrading,
24
+ projects should ensure that there is a unique index on these columns. The
25
+ projects index needs to be scoped to an account ID, so that index should be on
26
+ both columns (account_id and keyword).
27
+
28
+ 0.7.2
29
+
30
+ Don't bother to show the account selection on the project form if the user is only an
31
+ admin on one account
32
+
33
+ 0.7.1
34
+
35
+ Introduce a project dropdown, include it with:
36
+
37
+ render :partial => "shared/project_dropdown"
38
+
39
+ Saucy provides the javascript for this dropdown, but not the CSS or the image
40
+ for the dropdown. The Javascript depends on jQuery.
41
+
42
+ Also, adds the ability to move a project to another project that you are an
43
+ admin on.
44
+
45
+ 0.7.0
46
+
47
+ Split Saucy::Configuration.mailer_sender into support_email_address and manager_email_address.
48
+
49
+ 0.6.1
50
+
51
+ Fix for this known bug in Rails 3.0.7
52
+
53
+ https://rails.lighthouseapp.com/projects/8994/tickets/6757-hashfrom_xml-chokes-on-empty-cdata
54
+
55
+ 0.6.0
56
+
57
+ Upgrading to, and making an explicit dependency on Clearance ~> 0.11.0. You can
58
+ get an overview of the important changes to clearance here:
59
+
60
+ https://github.com/thoughtbot/clearance/wiki/upgrading-clearance
61
+
62
+ 0.5.5
63
+
64
+ Fixes for account and project keywords containing special characters, generated by signup
65
+
66
+ 0.5.4
67
+
68
+ Changes from an archive project link to a checkbox on the project form.
69
+
70
+ 0.5.3
71
+
72
+ Removes uniqueness constraint on account names
73
+
74
+ 0.5.2
75
+
76
+ Simplifies the signup process to asking users for just an email and password.
77
+ Overall this should be seamless upgrade but you may have made other changes in
78
+ your application which might conflict, just pay attention when you upgrade.
79
+
80
+ You'll defintely want to make sure you regerate the saucy features with this upgrade:
81
+
82
+ rails g saucy:features
83
+
84
+ 0.5.1
85
+
86
+ Add an index to the archived column for projects
87
+
88
+ 0.5.0
89
+
90
+ Added the ability to archive projects. Archived projects are not included in
91
+ an accounts `projects_count`.
92
+
93
+ To upgrade to this version add the following column to the projects table:
94
+
95
+ add_column :projects, :archived, :boolean, :default => false, :null => false
96
+ add_index :projects, :archived
97
+
98
+ This version adds a list of archived projects to the projects index of an
99
+ account. An introduced headers to these two lists. Archived projects have
100
+ a "View Project" link, which is how the user can view an archived project.
101
+ You will probably want to style this list, headers, and link.
102
+
103
+ It also adds an Archive/Unarchive link on the project's edit page. You will probably
104
+ want to style these links.
105
+
106
+ It is up to your application to provide any additional logic about what
107
+ limitations are present for archived projects (for example, not being able to
108
+ create new comments on them).
109
+
110
+ 0.4.10
111
+
112
+ A user following an invite link who is already signed in and whose email
113
+ address matches the one in the invite automatically accepts the invite when
114
+ following the link.
data/Gemfile ADDED
@@ -0,0 +1,26 @@
1
+ source "http://rubygems.org"
2
+ gem "cucumber"
3
+ gem "aruba"
4
+ gem "rake"
5
+ gem "rspec-rails", :require => false
6
+ gem "rails", ">= 3.0.3"
7
+ gem "thin"
8
+ gem "clearance", "~> 0.11.0"
9
+ gem "shoulda", :require => false
10
+ gem "bourne", :require => false
11
+ gem "sqlite3", :require => false
12
+ gem "factory_girl", :require => false
13
+ gem "sinatra"
14
+ gem "sham_rack"
15
+ gem "braintree"
16
+ gem "timecop"
17
+
18
+ # used by the rails app in cucumber
19
+ gem "cucumber-rails", :require => false
20
+ gem "capybara", :require => false
21
+ gem "factory_girl_rails", :require => false
22
+ gem "formtastic", :require => false
23
+ gem "database_cleaner", :require => false
24
+ gem "dynamic_form", :require => false
25
+ gem "launchy", :require => false
26
+ gem "jquery-rails", :require => false
@@ -0,0 +1,118 @@
1
+ Saucy
2
+ =====
3
+
4
+ Saucy is a Rails engine for monthly subscription-style SaaS apps.
5
+
6
+ Example scenarios covered by Saucy:
7
+
8
+ * I sign up for "Free" plan under new account "thoughtbot"
9
+ * I am an admin and can be reached at "dan@example.com"
10
+ * I create a project "Hoptoad"
11
+ * I upgrade to the "Basic" plan and my credit card is charged
12
+ * I now have permissions to add users and projects to the "thoughtbot" account
13
+ * I invite "joe@example.com" to "Hoptoad"
14
+ * I create a project "Trajectory"
15
+ * I invite "mike@example.com" to "Trajectory"
16
+
17
+ Installation
18
+ ------------
19
+
20
+ In your Gemfile:
21
+
22
+ gem "saucy"
23
+
24
+ After you bundle, run the generator:
25
+
26
+ rails generate saucy:install
27
+
28
+ You will want to include the `ensure_active_account` `before_filter` in any controller actions that you want to protect if the user is using an past due paid account.
29
+
30
+ You will want to customize the from email addresses.
31
+
32
+ Support email address for your application:
33
+
34
+ Saucy::Configuration.support_email_address = "support@example.com"
35
+
36
+ Personalizable emails such as trial expiration notice and activation encouragement are sent from a product manager personal address:
37
+
38
+ Saucy::Configuration.manager_email_address = "manager@example.com"
39
+
40
+ If you have an account with Braintree with multiple merchant accounts you'll want to configure the merchant account for this application:
41
+
42
+ Saucy::Configuration.merchant_account_id = 'your merchant account id'
43
+
44
+ In addition, there are a number of strings such as application name, support url, automated emails, etc. that are provided and customized with i18n translations. You can customize these in your app, and you can see what they are by looking at config/locales/en.yml in saucy.
45
+
46
+ There is a `saucy:daily` rake task which should be run on a regular basis to send receipts and payment processing problem emails.
47
+
48
+ Saucy accounts become "activated" once an initial setup step is complete. This could be creating the first bug for a bug tracker, or setting up a client gem for a server API. Once the application detects that the account is activate, it should set "activated" to true on the account. This will prevent followup emails being sent to users that have already set up their accounts.
49
+
50
+ Development environment
51
+ -----------------------
52
+
53
+ Plans need to exist for users to sign up for. In db/seeds.rb:
54
+
55
+ %w(free expensive mega-expensive).each do |plan_name|
56
+ Plan.find_or_create_by_name(plan_name)
57
+ end
58
+
59
+ Then run: rake db:seed
60
+
61
+ Test environment
62
+ ----------------
63
+
64
+ Generate the Braintree Fake for your specs:
65
+
66
+ rails generate saucy:specs
67
+
68
+ Generate feature coverage:
69
+
70
+ rails generate saucy:features
71
+
72
+ To use seed data in your Cucumber, add this to features/support/seed.rb:
73
+
74
+ require Rails.root.join('db','seeds')
75
+
76
+ Customization
77
+ -------------
78
+
79
+ By default Saucy uses and provides a `saucy.html.erb` layout. To change the
80
+ layout for a controller inside of saucy, add a line like this to your
81
+ config/application.rb:
82
+
83
+ config.saucy.layouts.accounts.index = "custom"
84
+
85
+ In addition to just the normal yield, your layout should yield the following
86
+ items in order to get everything from saucy views:
87
+
88
+ * :header
89
+ * :subnav
90
+
91
+ To extend the ProjectsController:
92
+
93
+ class ProjectsController < ApplicationController
94
+ include Saucy::ProjectsController
95
+
96
+ def edit
97
+ super
98
+ @deleters = @project.deleters
99
+ end
100
+ end
101
+
102
+ To define additional limit meters, or override existing limit meters, create the
103
+ partials:
104
+
105
+ app/views/limits/_#{limitname}_meter.html.erb
106
+
107
+ You can override all the views by generating them into your app and customizing them there:
108
+
109
+ rails g saucy:views
110
+
111
+ ## Gotchas
112
+
113
+ Make sure you don't do this in ApplicationController:
114
+
115
+ before_filter :authenticate
116
+
117
+ Saucy's internal controllers don't skip any before filters.
118
+
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake'
4
+ require 'rake/gempackagetask'
5
+ require 'cucumber/rake/task'
6
+ require 'rspec/core/rake_task'
7
+
8
+ desc 'Default: run all tests'
9
+ task :default => [:spec, :cucumber]
10
+
11
+ Cucumber::Rake::Task.new(:cucumber) do |t|
12
+ t.fork = true
13
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'progress')]
14
+ end
15
+
16
+ RSpec::Core::RakeTask.new do |t|
17
+ t.pattern = "spec/**/*_spec.rb"
18
+ t.rspec_opts = "--format progress"
19
+ end
20
+
21
+ eval("$specification = begin; #{IO.read('saucy.gemspec')}; end")
22
+ Rake::GemPackageTask.new($specification) do |package|
23
+ package.need_zip = true
24
+ package.need_tar = true
25
+ end
26
+
27
+ gem_file = "pkg/#{$specification.name}-#{$specification.version}.gem"
28
+
29
+ desc "Build and install the latest gem"
30
+ task :install => :gem do
31
+ sh("gem install --local #{gem_file}")
32
+ end
33
+
34
+ desc "Build and release the latest gem"
35
+ task :release => :gem do
36
+ sh("gem push #{gem_file}")
37
+ end
38
+
@@ -0,0 +1,68 @@
1
+ class AccountsController < ApplicationController
2
+ before_filter :authorize, :only => [:index, :edit, :update]
3
+ before_filter :authorize_admin, :except => [:new, :create, :index]
4
+ before_filter :ensure_active_account, :only => [:edit, :update]
5
+ layout Saucy::Layouts.to_proc
6
+
7
+ def new
8
+ @plan = Plan.find(params[:plan_id])
9
+ @signup = Signup.new
10
+ Saucy::Configuration.notify("plan_viewed", :request => request,
11
+ :plan => @plan)
12
+ end
13
+
14
+ def create
15
+ @plan = Plan.find(params[:plan_id])
16
+ @signup = Signup.new(params[:signup])
17
+ @signup.user = current_user
18
+ @signup.plan = @plan
19
+ if @signup.save
20
+ Saucy::Configuration.notify("account_created", :request => request,
21
+ :account => @signup.account)
22
+ flash[:success] = "Account was created."
23
+ sign_in @signup.user
24
+ redirect_to new_account_project_path(@signup.account)
25
+ else
26
+ render :action => 'new'
27
+ end
28
+ end
29
+
30
+ def index
31
+ if current_user.projects.size == 1
32
+ flash.keep
33
+ redirect_to project_path(current_user.projects.first)
34
+ else
35
+ @accounts = current_user.accounts
36
+ end
37
+ end
38
+
39
+ def edit
40
+ @account = current_account
41
+ end
42
+
43
+ def update
44
+ @account = current_account
45
+ if @account.update_attributes(params[:account])
46
+ flash[:success] = 'Account was updated.'
47
+ redirect_to edit_profile_url
48
+ else
49
+ render :action => :edit
50
+ end
51
+ end
52
+
53
+ def destroy
54
+ current_account.destroy
55
+ flash[:success] = "Your account has been deleted."
56
+ redirect_to root_url
57
+ end
58
+
59
+ private
60
+
61
+ def current_account
62
+ Account.find_by_keyword!(params[:id])
63
+ end
64
+
65
+ def current_account?
66
+ params[:id].present?
67
+ end
68
+ end
@@ -0,0 +1,25 @@
1
+ class BillingsController < ApplicationController
2
+ before_filter :authorize_admin, :except => [:show]
3
+ layout Saucy::Layouts.to_proc
4
+
5
+ def show
6
+ end
7
+
8
+ def edit
9
+ @account = current_account
10
+
11
+ @account.cardholder_name = @account.credit_card.cardholder_name
12
+ @account.billing_email = @account.customer.email
13
+ @account.expiration_month = @account.credit_card.expiration_month
14
+ @account.expiration_year = @account.credit_card.expiration_year
15
+ end
16
+
17
+ def update
18
+ @account = current_account
19
+ if @account.save_customer_and_subscription!(params[:account])
20
+ redirect_to account_billing_path(@account), :notice => t('.update.notice', :default => "Billing information updated successfully")
21
+ else
22
+ render :edit
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,65 @@
1
+ class InvitationsController < ApplicationController
2
+ before_filter :authorize_admin, :except => [:show, :update]
3
+ before_filter :ensure_account_within_users_limit, :only => [:new, :create]
4
+ skip_before_filter :authorize, :only => [:show, :update]
5
+ layout Saucy::Layouts.to_proc
6
+
7
+ def new
8
+ assign_projects
9
+ @invitation = Invitation.new
10
+ render
11
+ end
12
+
13
+ def create
14
+ @invitation = Invitation.new(params[:invitation])
15
+ @invitation.account = current_account
16
+ @invitation.sender = current_user
17
+ if @invitation.save
18
+ flash[:success] = "User invited."
19
+ redirect_to account_memberships_url(current_account)
20
+ else
21
+ assign_projects
22
+ render :action => 'new'
23
+ end
24
+ end
25
+
26
+ def show
27
+ with_invitation { render }
28
+ end
29
+
30
+ def update
31
+ with_invitation do
32
+ if @invitation.accept(params[:invitation])
33
+ sign_in @invitation.user
34
+ redirect_to root_url
35
+ else
36
+ render :action => 'show'
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def assign_projects
44
+ @projects = current_account.projects_by_name
45
+ end
46
+
47
+ def with_invitation
48
+ @invitation = Invitation.find_by_code!(params[:id])
49
+ if @invitation.used?
50
+ flash[:error] = t("invitations.show.used",
51
+ :default => "That invitation has already been used.")
52
+ redirect_to root_url
53
+ elsif signed_in? && current_user.email == @invitation.email
54
+ if @invitation.accept({:existing_user => current_user})
55
+ redirect_to root_url
56
+ end
57
+ else
58
+ yield
59
+ end
60
+ end
61
+
62
+ def ensure_account_within_users_limit
63
+ ensure_account_within_limit("users")
64
+ end
65
+ end