bento 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.
- data/README.rdoc +62 -0
- data/lib/bento.rb +6 -0
- data/lib/bento/controllers/account_scopable.rb +22 -0
- data/lib/bento/controllers/helpers.rb +17 -0
- data/lib/bento/models/account.rb +59 -0
- data/lib/bento/rails.rb +4 -0
- data/lib/bento/version.rb +3 -0
- data/lib/generators/active_record/bento_generator.rb +29 -0
- data/lib/generators/active_record/templates/add_migration.rb +9 -0
- data/lib/generators/active_record/templates/create_migration.rb +18 -0
- data/lib/generators/bento/bento_generator.rb +13 -0
- data/lib/generators/bento/orm_helpers.rb +22 -0
- data/lib/generators/bento/views_generator.rb +14 -0
- data/spec/bento/controllers/helpers_spec.rb +4 -0
- data/spec/bento/models/account_spec.rb +125 -0
- data/spec/proof_of_concepts_spec.rb +17 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/controllers/all_projects_controller.rb +6 -0
- data/spec/rails_app/app/controllers/application_controller.rb +4 -0
- data/spec/rails_app/app/controllers/custom_accounts_controller.rb +9 -0
- data/spec/rails_app/app/controllers/home_controller.rb +4 -0
- data/spec/rails_app/app/controllers/projects_controller.rb +6 -0
- data/spec/rails_app/app/models/account.rb +10 -0
- data/spec/rails_app/app/models/project.rb +4 -0
- data/spec/rails_app/app/models/user.rb +8 -0
- data/spec/rails_app/app/views/custom_accounts/new.html.erb +10 -0
- data/spec/rails_app/app/views/home/index.html.erb +1 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +29 -0
- data/spec/rails_app/app/views/projects/_all_projects.html.erb +9 -0
- data/spec/rails_app/app/views/projects/_current_account_projects.html.erb +9 -0
- data/spec/rails_app/app/views/projects/_form.html.erb +5 -0
- data/spec/rails_app/app/views/projects/edit.html.erb +1 -0
- data/spec/rails_app/app/views/projects/index.html.erb +7 -0
- data/spec/rails_app/app/views/projects/new.html.erb +1 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +43 -0
- data/spec/rails_app/config/boot.rb +15 -0
- data/spec/rails_app/config/database.yml +22 -0
- data/spec/rails_app/config/environment.rb +6 -0
- data/spec/rails_app/config/environments/development.rb +28 -0
- data/spec/rails_app/config/environments/production.rb +49 -0
- data/spec/rails_app/config/environments/test.rb +35 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/devise.rb +142 -0
- data/spec/rails_app/config/initializers/inaccessible_attributes.rb +13 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/initializers/session_store.rb +8 -0
- data/spec/rails_app/config/locales/devise.en.yml +39 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +9 -0
- data/spec/rails_app/db/development.sqlite3 +0 -0
- data/spec/rails_app/db/migrate/20100924132032_devise_create_users.rb +19 -0
- data/spec/rails_app/db/migrate/20101015094514_bento_create_accounts.rb +15 -0
- data/spec/rails_app/db/migrate/20101015094515_bento_add_account_id_to_accounts.rb +9 -0
- data/spec/rails_app/db/migrate/20101015143011_create_projects.rb +13 -0
- data/spec/rails_app/db/production.sqlite3 +0 -0
- data/spec/rails_app/db/schema.rb +44 -0
- data/spec/rails_app/db/test.sqlite3 +0 -0
- data/spec/rails_app/log/development.log +1290 -0
- data/spec/rails_app/log/test.log +30285 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +26 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/rails_app/tmp/pids/server.pid +1 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/bento_spec_helper.rb +16 -0
- data/spec/support/blueprints.rb +32 -0
- metadata +403 -0
data/README.rdoc
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= Bento
|
2
|
+
Bento is a Rails account management engine.
|
3
|
+
|
4
|
+
== Disclaimer
|
5
|
+
Don't expect everything to work as described below. The gem is still in early beta.
|
6
|
+
|
7
|
+
== Requirements
|
8
|
+
Currently it has a lot of pre conditions. The goal is to get rid of as many of them as possible but right new Bento expects the following:
|
9
|
+
* Your model for managing accounts is named Account (Bento can generate it for you)
|
10
|
+
* The account model has a name and a plan column (Bento can generate the migrations too)
|
11
|
+
* You have a model named User to keep track of users
|
12
|
+
- The users has a first_name, last_name, email and password fields
|
13
|
+
|
14
|
+
== Installation
|
15
|
+
gem install bento
|
16
|
+
rails generate bento account
|
17
|
+
rake db:migrate
|
18
|
+
|
19
|
+
== Basic Usage
|
20
|
+
Bento sets up the following routes that you can use, complete with views and controllers:
|
21
|
+
* [GET] sign_up_accounts - account and user creation in once
|
22
|
+
* [GET] new_accounts - for just account creation
|
23
|
+
* [GET] accounts - show
|
24
|
+
* [GET] account - index
|
25
|
+
* [POST] accounts - create
|
26
|
+
* [PUT] account - update
|
27
|
+
* [DELETE] account - destroy
|
28
|
+
|
29
|
+
== Views and controllers
|
30
|
+
=== Access the current account:
|
31
|
+
There is a current_account helper in your controllers and views which returns the currently logged in user's account.
|
32
|
+
|
33
|
+
=== Automatic scoping
|
34
|
+
If you're using inherited_resources in your controllers you can do the below to scope your resource to the current account.
|
35
|
+
class MyController < ApplicationController
|
36
|
+
scoped_to_account
|
37
|
+
end
|
38
|
+
|
39
|
+
=== Admin managed account
|
40
|
+
Bento can give you a complete CRUD for creating accounts. Use
|
41
|
+
rails generate bento:views
|
42
|
+
If your current_user respond to admin? These actions will only be accessible by users that respond with true.
|
43
|
+
|
44
|
+
== Configuring controllers
|
45
|
+
|
46
|
+
You can customize the accounts controller by following these steps:
|
47
|
+
|
48
|
+
1) Create your custom accounts controller, AccountsController
|
49
|
+
class AccountsController < Bento::AccountsController
|
50
|
+
end
|
51
|
+
|
52
|
+
2) Tell the router to use this controller:
|
53
|
+
resources :accounts
|
54
|
+
|
55
|
+
3) And since we changed the controller, it won't use the gem's views so make sure to make some and put in app/views/accounts.
|
56
|
+
|
57
|
+
== Credits and contributors
|
58
|
+
|
59
|
+
* The contributors of the awesome Devise gem from which I've borrowed a lot of ideas and patterns.
|
60
|
+
* Elabs for sponsoring
|
61
|
+
* Jonas Nicklas for helping me boot strap the engine.
|
62
|
+
* All the gems that this gem depends on.
|
data/lib/bento.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Bento
|
2
|
+
module Controllers
|
3
|
+
module AccountScopable
|
4
|
+
def scoped_to_account(*args, &block)
|
5
|
+
inherit_resources(*args)
|
6
|
+
include AccountScopable::InstanceMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def begin_of_association_chain
|
11
|
+
current_user.account
|
12
|
+
end
|
13
|
+
|
14
|
+
private :begin_of_association_chain
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ActionController::Base
|
21
|
+
extend Bento::Controllers::AccountScopable
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Bento
|
2
|
+
module Controllers
|
3
|
+
module Helpers
|
4
|
+
def self.included(base)
|
5
|
+
base.helper_method :current_account
|
6
|
+
end
|
7
|
+
|
8
|
+
def current_account
|
9
|
+
current_user.account if current_user
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ActionController::Base
|
16
|
+
include Bento::Controllers::Helpers
|
17
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Bento
|
2
|
+
module Models
|
3
|
+
module Account
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
USER_ACCESSORS = [:first_name, :last_name, :email, :password, :password_confirmation]
|
10
|
+
|
11
|
+
def bento_account(*options)
|
12
|
+
if extend_with_validations?(options)
|
13
|
+
validates_presence_of :name
|
14
|
+
end
|
15
|
+
|
16
|
+
if extend_with_user_accessors?(options)
|
17
|
+
attr_accessor *USER_ACCESSORS
|
18
|
+
end
|
19
|
+
|
20
|
+
if extend_with_user_association?(options)
|
21
|
+
has_many :users
|
22
|
+
end
|
23
|
+
|
24
|
+
if extend_with_trial?(options)
|
25
|
+
define_method("trial_days_remaining") do
|
26
|
+
created_at.advance(:days => 30).to_date - Date.today
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if extend_with_user_association?(options) and extend_with_user_accessors?(options)
|
31
|
+
before_validation :build_user, :on => :create
|
32
|
+
|
33
|
+
define_method("build_user") do
|
34
|
+
user_attributes = USER_ACCESSORS.inject({}) { |h, key| h.merge(key => send(key)) }
|
35
|
+
if user_attributes.values.any?
|
36
|
+
@user ||= users.build(user_attributes)
|
37
|
+
@user.tap(&:valid?).errors.each { |attribute, message| errors.add(attribute, message) }
|
38
|
+
@user
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def method_missing(*args)
|
47
|
+
option = args.shift.to_s.sub(/^extend_with_/, '').chop.to_sym
|
48
|
+
available = args.flatten
|
49
|
+
|
50
|
+
(available.is_a?(Array) and available.empty?) or available.include?(:all) or available.include?(option)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ActiveRecord::Base
|
58
|
+
include Bento::Models::Account
|
59
|
+
end
|
data/lib/bento/rails.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
require 'generators/bento/orm_helpers'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module Generators
|
6
|
+
class BentoGenerator < ActiveRecord::Generators::Base
|
7
|
+
argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
|
8
|
+
|
9
|
+
include Bento::Generators::OrmHelpers
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
11
|
+
|
12
|
+
def generate_model
|
13
|
+
invoke "active_record:model", [name], :migration => false unless model_exists?
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_bento_migration
|
17
|
+
migration_template "create_migration.rb", "db/migrate/bento_create_#{table_name}"
|
18
|
+
migration_template "add_migration.rb", "db/migrate/bento_add_account_id_to_#{table_name}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def inject_bento_content
|
22
|
+
inject_into_class model_path, class_name, model_contents + <<-CONTENT
|
23
|
+
# Setup accessible (or protected) attributes for your model
|
24
|
+
attr_accessible :name, :plan, :first_name, :last_name, :email, :password_confirmation, :password
|
25
|
+
CONTENT
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class BentoCreateAccounts < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :accounts do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :plan
|
6
|
+
<% for attribute in attributes -%>
|
7
|
+
t.<%= attribute.type %> :<%= attribute.name %>
|
8
|
+
<% end -%>
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :accounts, :name, :unique => true
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :accounts
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Bento
|
2
|
+
module Generators
|
3
|
+
class BentoGenerator < Rails::Generators::NamedBase
|
4
|
+
namespace "bento"
|
5
|
+
source_root File.expand_path("../templates", __FILE__)
|
6
|
+
|
7
|
+
desc "Generates a model with the given NAME (if one does not exist) with bento " <<
|
8
|
+
"configuration plus a migration file."
|
9
|
+
|
10
|
+
hook_for :orm
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Bento
|
2
|
+
module Generators
|
3
|
+
module OrmHelpers
|
4
|
+
def model_contents
|
5
|
+
<<-CONTENT
|
6
|
+
# Include all bento modules. Others available are:
|
7
|
+
# :all, :validations, :user_accessors, :user_association, :user_accessors, :trial
|
8
|
+
bento_account
|
9
|
+
|
10
|
+
CONTENT
|
11
|
+
end
|
12
|
+
|
13
|
+
def model_exists?
|
14
|
+
File.exists?(File.join(destination_root, model_path))
|
15
|
+
end
|
16
|
+
|
17
|
+
def model_path
|
18
|
+
@model_path ||= File.join("app", "models", "#{file_path}.rb")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Bento
|
2
|
+
module Generators
|
3
|
+
class ViewsGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path("../../../../app/views", __FILE__)
|
5
|
+
desc "Copies all Bento views to your application."
|
6
|
+
|
7
|
+
argument :scope, :required => false, :default => nil, :desc => "The scope to copy views to"
|
8
|
+
|
9
|
+
def copy_views
|
10
|
+
directory "bento", "app/views/#{scope}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Bento::AccountTest < ActiveRecord::Base
|
4
|
+
set_table_name("accounts")
|
5
|
+
attr_accessible :name, :plan, :first_name, :last_name, :email, :password_confirmation, :password, :created_at
|
6
|
+
end
|
7
|
+
class NoOptionsTestAccount < Bento::AccountTest; bento_account; end
|
8
|
+
class AllTestAccount < Bento::AccountTest; bento_account(:all); end
|
9
|
+
class ValidationsTestAccount < Bento::AccountTest; bento_account(:validations); end
|
10
|
+
class UserAccessorsTestAccount < Bento::AccountTest; bento_account(:user_accessors); end
|
11
|
+
class UserAssociationTestAccount < Bento::AccountTest; bento_account(:user_association); end
|
12
|
+
class AllUserTestAccount < Bento::AccountTest; bento_account(:user_accessors, :user_association); end
|
13
|
+
class ServeralOptionsAccessorsTestAccount < Bento::AccountTest; bento_account(:user_accessors, :validations); end
|
14
|
+
class TrialTestAccount < Bento::AccountTest; bento_account(:trial); end
|
15
|
+
|
16
|
+
describe Bento::Models::Account do
|
17
|
+
let(:account_params) do
|
18
|
+
{ :name => "Hashrocket", :first_name => "Obie", :last_name => "Fernandez", :email => "obie@hashrocket.com", :password => "test1234" }
|
19
|
+
end
|
20
|
+
|
21
|
+
context "all" do
|
22
|
+
subject { AllTestAccount.new }
|
23
|
+
it { should be_invalid_without(:name) }
|
24
|
+
it { should have_user_accessors }
|
25
|
+
it { should respond_to(:users) }
|
26
|
+
it { should respond_to(:trial_days_remaining) }
|
27
|
+
it { should respond_to(:build_user) }
|
28
|
+
|
29
|
+
describe ".build_user" do
|
30
|
+
context "all user attributes are blank" do
|
31
|
+
it "creates the account without the user" do
|
32
|
+
account = AllTestAccount.new(:name => "Elabs")
|
33
|
+
account.save.should be_true
|
34
|
+
User.find_by_account_id(account.id).should be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "at least one user attributes is present" do
|
39
|
+
context "successfully creating a user" do
|
40
|
+
subject do
|
41
|
+
account = AllTestAccount.create!(account_params)
|
42
|
+
account.users.first
|
43
|
+
end
|
44
|
+
|
45
|
+
its(:email) { should == "obie@hashrocket.com" }
|
46
|
+
its(:first_name) { should == "Obie" }
|
47
|
+
its(:last_name) { should == "Fernandez" }
|
48
|
+
end
|
49
|
+
|
50
|
+
context "unsuccessfully creating a user" do
|
51
|
+
subject { AllTestAccount.new(account_params.merge(:email => "")).tap(&:valid?) }
|
52
|
+
it { subject.errors.should include(:email) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#trial_days_remaining" do
|
58
|
+
subject { TrialTestAccount.create!(:name => "Account", :created_at => 6.days.ago).trial_days_remaining }
|
59
|
+
it("returns the number of days that are left in the trial") { should == 24 }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "no options" do
|
64
|
+
subject { NoOptionsTestAccount.new }
|
65
|
+
it { should be_invalid_without(:name) }
|
66
|
+
it { should have_user_accessors }
|
67
|
+
it { should respond_to(:users) }
|
68
|
+
it { should respond_to(:trial_days_remaining) }
|
69
|
+
it { should respond_to(:build_user) }
|
70
|
+
end
|
71
|
+
|
72
|
+
context "validations" do
|
73
|
+
subject { ValidationsTestAccount.new }
|
74
|
+
it { should be_invalid_without(:name) }
|
75
|
+
it { should_not have_user_accessors }
|
76
|
+
it { should_not respond_to(:users) }
|
77
|
+
it { should_not respond_to(:trial_days_remaining) }
|
78
|
+
it { should_not respond_to(:build_user) }
|
79
|
+
end
|
80
|
+
|
81
|
+
context "user accessors" do
|
82
|
+
subject { UserAccessorsTestAccount.new }
|
83
|
+
it { should_not be_invalid_without(:name) }
|
84
|
+
it { should have_user_accessors }
|
85
|
+
it { should_not respond_to(:users) }
|
86
|
+
it { should_not respond_to(:trial_days_remaining) }
|
87
|
+
it { should_not respond_to(:build_user) }
|
88
|
+
end
|
89
|
+
|
90
|
+
context "several options" do
|
91
|
+
subject { ServeralOptionsAccessorsTestAccount.new }
|
92
|
+
it { should be_invalid_without(:name) }
|
93
|
+
it { should have_user_accessors }
|
94
|
+
it { should_not respond_to(:users) }
|
95
|
+
it { should_not respond_to(:trial_days_remaining) }
|
96
|
+
it { should_not respond_to(:build_user) }
|
97
|
+
end
|
98
|
+
|
99
|
+
context "user association" do
|
100
|
+
subject { UserAssociationTestAccount.new }
|
101
|
+
it { should_not be_invalid_without(:name) }
|
102
|
+
it { should_not have_user_accessors }
|
103
|
+
it { should respond_to(:users) }
|
104
|
+
it { should_not respond_to(:trial_days_remaining) }
|
105
|
+
it { should_not respond_to(:build_user) }
|
106
|
+
end
|
107
|
+
|
108
|
+
context "trial" do
|
109
|
+
subject { TrialTestAccount.new }
|
110
|
+
it { should_not be_invalid_without(:name) }
|
111
|
+
it { should_not have_user_accessors }
|
112
|
+
it { should_not respond_to(:users) }
|
113
|
+
it { should respond_to(:trial_days_remaining) }
|
114
|
+
it { should_not respond_to(:build_user) }
|
115
|
+
end
|
116
|
+
|
117
|
+
context "user accessors and user association" do
|
118
|
+
subject { AllUserTestAccount.new }
|
119
|
+
it { should_not be_invalid_without(:name) }
|
120
|
+
it { should have_user_accessors }
|
121
|
+
it { should respond_to(:users) }
|
122
|
+
it { should_not respond_to(:trial_days_remaining) }
|
123
|
+
it { should respond_to(:build_user) }
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RailsApp do
|
4
|
+
it "reach it's own files" do
|
5
|
+
visit "/"
|
6
|
+
page.body.should include("Welcome to the app that's using Bento")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "loads app" do
|
10
|
+
Bento::AccountsController.should == Bento::AccountsController
|
11
|
+
end
|
12
|
+
|
13
|
+
it "mounts the new accounts route" do
|
14
|
+
visit "/accounts/new"
|
15
|
+
page.status_code.should == 200
|
16
|
+
end
|
17
|
+
end
|