saasy 0.0.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 (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,16 @@
1
+ When /^I choose the "([^"]*)" plan$/ do |plan_name|
2
+ input_id = find(:xpath, "//li[contains(@class, 'plan')]//p[contains(text(), '#{plan_name}')]/../../input")["id"]
3
+ choose(input_id)
4
+ end
5
+
6
+ Then /^the "([^"]*)" plan should be disabled$/ do |plan_name|
7
+ input_id = find(:xpath, "//li[contains(@class, 'plan')]//p[contains(text(), '#{plan_name}')]/../../input")["id"]
8
+ page.should have_css("##{input_id}[disabled='disabled']")
9
+ end
10
+
11
+ When /^I should see the "([^"]*)" plan before the "([^"]*)" plan$/ do |first_plan_name, second_plan_name|
12
+ first_expression = Regexp.escape(first_plan_name)
13
+ second_expression = Regexp.escape(second_plan_name)
14
+ joint_expression = /#{first_expression}.*#{second_expression}/m
15
+ page.body.should =~ joint_expression
16
+ end
@@ -0,0 +1,4 @@
1
+ Given /^a project named "([^"]*)" exists under the "([^"]*)" account$/ do |project_name, account_name|
2
+ account = Account.find_by_name!(account_name)
3
+ Factory(:project, :account => account, :name => project_name)
4
+ end
@@ -0,0 +1,37 @@
1
+ Given /^I (?:am signed in|sign in) as an admin of the "([^"]+)" project$/ do |project_name|
2
+ project = Project.find_by_name!(project_name)
3
+ user = Factory(:user)
4
+ membership = Factory(:membership, :user => user,
5
+ :account => project.account,
6
+ :admin => true)
7
+ Factory(:permission, :membership => membership,
8
+ :project => project)
9
+ When %{I sign in as "#{user.email}"}
10
+ end
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
+
19
+ Given /^I am signed in as an admin of the "([^"]*)" account$/ do |account_name|
20
+ account = Account.find_by_name!(account_name)
21
+ user = Factory(:user)
22
+ Factory(:membership, :user => user,
23
+ :account => account,
24
+ :admin => true)
25
+ When %{I sign in as "#{user.email}"}
26
+ end
27
+
28
+ When /^I sign in as "([^"\/]*)"$/ do |email|
29
+ user = User.find_by_email!(email)
30
+ user.update_attributes!(:password => 'test')
31
+ When %{I sign in as "#{email}/test"}
32
+ end
33
+
34
+ Given /^I am signed in$/ do
35
+ user = Factory(:user)
36
+ When %{I sign in as "#{user.email}"}
37
+ end
@@ -0,0 +1,100 @@
1
+ Given /^"([^"]*)" is a member of the "([^"]*)" project$/ do |email, project_name|
2
+ user = User.find_by_email!(email)
3
+ project = Project.find_by_name!(project_name)
4
+ membership = Membership.find_by_user_id_and_account_id(user, project.account) ||
5
+ Factory(:membership, :user => user, :account => project.account)
6
+ Factory(:permission, :membership => membership, :project => project)
7
+ end
8
+
9
+ Given /^"([^"]*)" is an admin of the "([^"]*)" project$/ do |email, project_name|
10
+ user = User.find_by_email!(email)
11
+ project = Project.find_by_name!(project_name)
12
+ if membership = Membership.find_by_user_id_and_account_id(user, project.account)
13
+ membership.update_attribute(:admin, true)
14
+ else
15
+ membership = Factory(:membership, :user => user,
16
+ :account => project.account,
17
+ :admin => true)
18
+ end
19
+ Factory(:permission, :membership => membership, :project => project)
20
+ end
21
+
22
+ Given /^"([^"]*)" is a member of the "([^"]*)" account/ do |email, account_name|
23
+ user = User.find_by_email!(email)
24
+ account = Account.find_by_name!(account_name)
25
+ Factory(:membership, :user => user, :account => account)
26
+ end
27
+
28
+ Given /^"([^"]*)" is an admin of the "([^"]*)" account/ do |email, account_name|
29
+ user = User.find_by_email!(email)
30
+ account = Account.find_by_name!(account_name)
31
+ Factory(:membership, :user => user, :account => account, :admin => true)
32
+ end
33
+
34
+ Then /^the user "([^"]*)" should be an admin of "([^"]*)"$/ do |email, account_name|
35
+ user = User.find_by_email!(email)
36
+ account = Account.find_by_name!(account_name)
37
+ user.should be_admin_of(account)
38
+ end
39
+
40
+ Given /^the user "([^"]*)" exists under the "([^"]*)" account$/ do |email, account_name|
41
+ Given %{a user exists with an email of "#{email}"}
42
+ Given %{"#{email}" is a member of the "#{account_name}" account}
43
+ end
44
+
45
+ When /^I fill in the following new user:$/ do |table|
46
+ within "fieldset.new_user" do
47
+ table.transpose.hashes.first.each do |field, value|
48
+ fill_in field, :with => value
49
+ end
50
+ end
51
+ end
52
+
53
+ When /^I fill in the following existing user:$/ do |table|
54
+ within "fieldset.existing_user" do
55
+ table.transpose.hashes.first.each do |field, value|
56
+ fill_in field, :with => value
57
+ end
58
+ end
59
+ end
60
+
61
+ Then /^"([^"]*)" should be a member of the "([^"]*)" account$/ do |email, account_name|
62
+ User.find_by_email!(email).should be_member_of(Account.find_by_name!(account_name))
63
+ end
64
+
65
+ Then /^"([^"]*)" should be an admin member of the "([^"]*)" account$/ do |email, account_name|
66
+ User.find_by_email!(email).should be_admin_of(Account.find_by_name!(account_name))
67
+ end
68
+
69
+ Then /^"([^"]*)" should be a member of the "([^"]*)" project/ do |email, project_name|
70
+ User.find_by_email!(email).should be_member_of(Project.find_by_name!(project_name))
71
+ end
72
+
73
+ Then /^"([^"]*)" should not be a member of the "([^"]*)" project/ do |email, project_name|
74
+ User.find_by_email!(email).should_not be_member_of(Project.find_by_name!(project_name))
75
+ end
76
+
77
+ Then /^"([^"]+)" should be listed as an admin$/ do |name|
78
+ within("#project_admins_input") do
79
+ check_box = find_field(name)
80
+ check_box['checked'].should be_true
81
+ check_box['disabled'].should be_true
82
+ end
83
+ end
84
+
85
+ Then /^"([^"]+)" should be listed as a member/ do |name|
86
+ within("#project_users_input") do
87
+ check_box = find_field(name)
88
+ check_box['checked'].should be_true
89
+ check_box['disabled'].should be_false
90
+ end
91
+ end
92
+
93
+ Then /^"([^"]+)" should be listed as a non-member/ do |name|
94
+ within("#project_users_input") do
95
+ check_box = find_field(name)
96
+ check_box['checked'].should be_false
97
+ check_box['disabled'].should be_false
98
+ end
99
+ end
100
+
@@ -0,0 +1,5 @@
1
+ require 'saucy/fake_braintree'
2
+
3
+ After do |s|
4
+ FakeBraintree.clear!
5
+ end
@@ -0,0 +1,40 @@
1
+ require 'generators/saucy/base'
2
+ require 'rails/generators/active_record/migration'
3
+
4
+ module Saucy
5
+ module Generators
6
+ class InstallGenerator < Base
7
+ include Rails::Generators::Migration
8
+ extend ActiveRecord::Generators::Migration
9
+
10
+ desc <<DESC
11
+ Description:
12
+ Copy saucy files to your application.
13
+ DESC
14
+
15
+ def generate_migration
16
+ migration_template "create_saucy_tables.rb", "db/migrate/create_saucy_tables.rb"
17
+ end
18
+
19
+ def create_models
20
+ directory "models", "app/models"
21
+ end
22
+
23
+ def create_controllers
24
+ directory "controllers", "app/controllers"
25
+ end
26
+
27
+ def update_user_model
28
+ insert_into_file "app/models/user.rb",
29
+ "\ninclude Saucy::User",
30
+ :after => "include Clearance::User"
31
+ end
32
+
33
+ def add_account_authorization
34
+ insert_into_file "app/controllers/application_controller.rb",
35
+ "\ninclude Saucy::AccountAuthorization",
36
+ :after => "include Clearance::Authentication"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ class ProjectsController < ApplicationController
2
+ include Saucy::ProjectsController
3
+ end
@@ -0,0 +1,115 @@
1
+ class CreateSaucyTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :memberships do |table|
4
+ table.integer :account_id
5
+ table.integer :user_id
6
+ table.boolean :admin, :null => false, :default => false
7
+ table.datetime :created_at
8
+ table.datetime :updated_at
9
+ end
10
+
11
+ add_index :memberships, [:account_id, :user_id], :unique => true
12
+
13
+ create_table :accounts do |table|
14
+ table.belongs_to :plan
15
+ table.string :name
16
+ table.string :keyword
17
+ table.datetime :created_at
18
+ table.datetime :updated_at
19
+ table.string :customer_token
20
+ table.string :subscription_token
21
+ table.string :subscription_status
22
+ table.datetime :next_billing_date
23
+ table.boolean :notified_of_disabling, :default => false, :null => false
24
+ table.boolean :notified_of_expiration, :default => false, :null => false
25
+ table.boolean :notified_of_completed_trial, :default => false, :null => false
26
+ table.boolean :asked_to_activate, :default => false, :null => false
27
+ table.boolean :activated, :default => false, :null => false
28
+ table.datetime :trial_expires_at
29
+ end
30
+ add_index :accounts, :plan_id
31
+ add_index :accounts, :keyword, :unique => true
32
+ add_index :accounts, :next_billing_date
33
+ add_index :accounts, :created_at
34
+ add_index :accounts, :trial_expires_at
35
+
36
+ create_table :invitations do |table|
37
+ table.string :email
38
+ table.integer :account_id
39
+ table.integer :sender_id
40
+ table.boolean :admin, :null => false, :default => false
41
+ table.string :code
42
+ table.boolean :used, :default => false, :null => false
43
+ table.datetime :created_at
44
+ table.datetime :updated_at
45
+ end
46
+
47
+ add_index :invitations, [:account_id]
48
+
49
+ create_table :permissions do |table|
50
+ table.integer :membership_id
51
+ table.integer :user_id
52
+ table.integer :project_id
53
+ table.datetime :created_at
54
+ table.datetime :updated_at
55
+ end
56
+
57
+ add_index :permissions, [:user_id, :project_id]
58
+ add_index :permissions, [:membership_id, :project_id], :name => [:membership_and_project], :unique => true
59
+
60
+ create_table :projects do |table|
61
+ table.string :name
62
+ table.string :keyword
63
+ table.integer :account_id
64
+ table.boolean :archived, :default => false, :null => false
65
+ table.datetime :created_at
66
+ table.datetime :updated_at
67
+ end
68
+ add_index :projects, [:keyword, :account_id], :unique => true
69
+ add_index :projects, :archived
70
+
71
+ change_table :users do |table|
72
+ table.string :name, :default => ""
73
+ end
74
+
75
+ create_table :plans do |t|
76
+ t.string :name
77
+ t.integer :price, :null => false, :default => 0
78
+ t.boolean :trial, :default => false, :null => false
79
+
80
+ t.timestamps
81
+ end
82
+
83
+ add_index :plans, :name
84
+
85
+ create_table :limits do |t|
86
+ t.belongs_to :plan
87
+ t.string :name
88
+ t.column :value, 'BIGINT UNSIGNED', :null => false, :default => 0
89
+ t.string :value_type, :null => false, :default => "number"
90
+
91
+ t.timestamps
92
+ end
93
+
94
+ add_index :limits, :plan_id
95
+
96
+ create_table :invitations_projects, :id => false do |table|
97
+ table.integer :invitation_id, :null => false
98
+ table.integer :project_id, :null => false
99
+ end
100
+
101
+ add_index :invitations_projects, [:invitation_id, :project_id], :unique => true
102
+ end
103
+
104
+ def self.down
105
+ remove_column :users, :name
106
+ drop_table :invitations_projects
107
+ drop_table :plans
108
+ drop_table :projects
109
+ drop_table :permissions
110
+ drop_table :invitations
111
+ drop_table :accounts
112
+ drop_table :memberships
113
+ end
114
+ end
115
+
@@ -0,0 +1,3 @@
1
+ class Account < ActiveRecord::Base
2
+ include Saucy::Account
3
+ end
@@ -0,0 +1,3 @@
1
+ class Plan < ActiveRecord::Base
2
+ include Saucy::Plan
3
+ end
@@ -0,0 +1,3 @@
1
+ class Project < ActiveRecord::Base
2
+ include Saucy::Project
3
+ end
@@ -0,0 +1,20 @@
1
+ require 'generators/saucy/base'
2
+
3
+ module Saucy
4
+ module Generators
5
+ class SpecsGenerator < Base
6
+
7
+ desc <<DESC
8
+ Description:
9
+ Copy saucy cucumber spec support files to your application.
10
+ DESC
11
+
12
+ def copy_spec_files
13
+ directory "support", "spec/support/saucy"
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+
20
+
@@ -0,0 +1,5 @@
1
+ require 'saucy/fake_braintree'
2
+
3
+ RSpec.configure do |config|
4
+ config.after(:each) { FakeBraintree.clear! }
5
+ end
@@ -0,0 +1,23 @@
1
+ require 'generators/saucy/base'
2
+ require 'rails/generators/active_record/migration'
3
+
4
+ module Saucy
5
+ module Generators
6
+ class ViewsGenerator < Base
7
+ include Rails::Generators::Migration
8
+ extend ActiveRecord::Generators::Migration
9
+
10
+ desc <<-DESC
11
+ Description:
12
+ Copy saucy views to your application.
13
+ DESC
14
+
15
+
16
+ def copy_views
17
+ views_path = File.join(File.dirname(File.expand_path(__FILE__)), '../../../../app/views')
18
+ directory views_path, 'app/views'
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,10 @@
1
+ require 'saucy/user'
2
+ require 'saucy/account'
3
+ require 'saucy/subscription'
4
+ require 'saucy/project'
5
+ require 'saucy/plan'
6
+ require 'saucy/account_authorization'
7
+ require 'saucy/configuration'
8
+ require 'saucy/engine'
9
+ require 'saucy/projects_controller'
10
+
@@ -0,0 +1,132 @@
1
+ module Saucy
2
+ module Account
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Saucy::Subscription
7
+
8
+ has_many :memberships, :dependent => :destroy
9
+ has_many :users, :through => :memberships
10
+ has_many :projects, :dependent => :destroy
11
+ has_many :admins, :through => :memberships,
12
+ :source => :user,
13
+ :conditions => { 'memberships.admin' => true }
14
+ has_many :non_admins, :through => :memberships,
15
+ :source => :user,
16
+ :conditions => { 'memberships.admin' => false }
17
+
18
+ belongs_to :plan
19
+
20
+ delegate :free?, :billed?, :trial?, :to => :plan
21
+
22
+ validates_uniqueness_of :keyword
23
+ validates_presence_of :name, :keyword, :plan_id
24
+
25
+ attr_accessible :name, :keyword
26
+
27
+ validates_format_of :keyword,
28
+ :with => %r{^[a-z0-9_-]+$},
29
+ :message => "must be only lower case letters and underscores."
30
+
31
+ before_create :set_trial_expiration
32
+ end
33
+
34
+ module InstanceMethods
35
+ def to_param
36
+ keyword
37
+ end
38
+
39
+ def has_member?(user)
40
+ memberships.exists?(:user_id => user.id)
41
+ end
42
+
43
+ def users_by_name
44
+ users.by_name
45
+ end
46
+
47
+ def projects_by_name
48
+ projects.by_name
49
+ end
50
+
51
+ def projects_visible_to(user)
52
+ projects.visible_to(user)
53
+ end
54
+
55
+ def memberships_by_name
56
+ memberships.by_name
57
+ end
58
+
59
+ def expired?
60
+ trial? && past_trial?
61
+ end
62
+
63
+ def past_trial?
64
+ trial_expires_at && trial_expires_at < Time.now
65
+ end
66
+
67
+ def admin_emails
68
+ admins.map(&:email)
69
+ end
70
+
71
+ def set_trial_expiration
72
+ self.trial_expires_at = 30.days.from_now(created_at || Time.now)
73
+ end
74
+
75
+ def users_count
76
+ users.count
77
+ end
78
+
79
+ def projects_count
80
+ projects.active.count
81
+ end
82
+ end
83
+
84
+ module ClassMethods
85
+ def deliver_new_unactivated_notifications
86
+ new_unactivated.each do |account|
87
+ BillingMailer.new_unactivated(account).deliver
88
+ account.asked_to_activate = true
89
+ account.save!
90
+ end
91
+ end
92
+
93
+ def deliver_expiring_trial_notifications
94
+ trial_expiring.each do |account|
95
+ BillingMailer.expiring_trial(account).deliver
96
+ account.notified_of_expiration = true
97
+ account.save!
98
+ end
99
+ end
100
+
101
+ def deliver_completed_trial_notifications
102
+ trial_completed.each do |account|
103
+ BillingMailer.completed_trial(account).deliver
104
+ account.notified_of_completed_trial = true
105
+ account.save!
106
+ end
107
+ end
108
+
109
+ def trial_expiring
110
+ trial.
111
+ where(:notified_of_expiration => false).
112
+ where(["accounts.trial_expires_at <= ?", 7.days.from_now])
113
+ end
114
+
115
+ def new_unactivated
116
+ where(["accounts.created_at <= ?", 7.days.ago]).
117
+ where(:asked_to_activate => false, :activated => false)
118
+ end
119
+
120
+ def trial_completed
121
+ trial.
122
+ where(:notified_of_completed_trial => false).
123
+ where(["accounts.trial_expires_at <= ?", Time.now])
124
+ end
125
+
126
+ def trial
127
+ includes(:plan).
128
+ where(:plans => { :trial => true })
129
+ end
130
+ end
131
+ end
132
+ end