bento 0.0.2 → 0.0.3

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 (52) hide show
  1. data/README.rdoc +38 -5
  2. data/lib/bento.rb +1 -0
  3. data/lib/bento/controllers/account_scopable.rb +75 -43
  4. data/lib/bento/models/modules/user_association.rb +2 -1
  5. data/lib/bento/models/user.rb +24 -0
  6. data/lib/bento/rails/routes.rb +7 -2
  7. data/lib/bento/version.rb +1 -1
  8. data/lib/generators/active_record/bento_generator.rb +5 -5
  9. data/lib/generators/active_record/templates/bento_membership_migration.rb +25 -0
  10. data/lib/generators/bento/orm_helpers.rb +50 -0
  11. data/spec/bento/models/modules/user_accessors_spec.rb +12 -2
  12. data/spec/bento/models/user_spec.rb +24 -0
  13. data/spec/controllers/bento_for_custom_routes_spec.rb +38 -0
  14. data/spec/controllers/bento_for_detault_routes_spec.rb +23 -0
  15. data/spec/controllers/bento_for_nested_routes_spec.rb +28 -0
  16. data/spec/rails_app/app/models/account.rb +3 -1
  17. data/spec/rails_app/app/models/bento_membership.rb +6 -0
  18. data/spec/rails_app/app/models/user.rb +8 -3
  19. data/spec/rails_app/app/views/layouts/application.html.erb +6 -1
  20. data/spec/rails_app/app/views/projects/_my_accounts.html.erb +6 -0
  21. data/spec/rails_app/app/views/projects/_my_projects.html.erb +9 -0
  22. data/spec/rails_app/app/views/projects/index.html.erb +4 -0
  23. data/spec/rails_app/config/environment.rb +4 -0
  24. data/spec/rails_app/db/development.sqlite3 +0 -0
  25. data/spec/rails_app/db/migrate/20101015094514_bento_create_accounts.rb +0 -1
  26. data/spec/rails_app/db/migrate/20110821091847_add_plan_to_accounts.rb +9 -0
  27. data/spec/rails_app/db/migrate/20110821095924_bento_create_bento_memberships.rb +25 -0
  28. data/spec/rails_app/{log/production.log → db/production.sqlite3} +0 -0
  29. data/spec/rails_app/db/schema.rb +11 -3
  30. data/spec/rails_app/db/test.sqlite3 +0 -0
  31. data/spec/rails_app/log/development.log +78 -72
  32. data/spec/rails_app/log/test.log +32588 -33752
  33. data/spec/rails_app/tmp/capybara/capybara-201108261408529518409580.html +23 -0
  34. data/spec/rails_app/tmp/capybara/capybara-201108261409354283519857.html +23 -0
  35. data/spec/rails_app/tmp/capybara/capybara-201108261411215267908820.html +36 -0
  36. data/spec/rails_app/tmp/capybara/capybara-201108261411377094743316.html +36 -0
  37. data/spec/rails_app/tmp/capybara/capybara-201108261607322084409756.html +34 -0
  38. data/spec/rails_app/tmp/capybara/capybara-201108261612128889477667.html +34 -0
  39. data/spec/rails_app/tmp/capybara/capybara-201108261614323748273234.html +34 -0
  40. data/spec/rails_app/tmp/capybara/capybara-201108261614562813014420.html +34 -0
  41. data/spec/rails_app/tmp/capybara/capybara-201108261615145359399760.html +34 -0
  42. data/spec/rails_app/tmp/capybara/capybara-201108261616296916249304.html +34 -0
  43. data/spec/rails_app/tmp/capybara/capybara-201108261617171731033403.html +34 -0
  44. data/spec/rails_app/tmp/capybara/capybara-201108261617339533158457.html +36 -0
  45. data/spec/rails_app/tmp/capybara/capybara-201108261620375648320872.html +41 -0
  46. data/spec/spec_helper.rb +7 -3
  47. data/spec/support/bento_spec_helper.rb +2 -2
  48. data/spec/support/blueprints.rb +0 -1
  49. metadata +97 -96
  50. data/spec/bento_spec.rb +0 -4
  51. data/spec/controllers/bento_for_routes_spec.rb +0 -55
  52. data/spec/rails_app/log/server.log +0 -0
@@ -14,6 +14,10 @@ Replace MODEL by the class name you want to use as your account-like model, like
14
14
  This will create a model (if one does not exist) and configure it with default Bento options.
15
15
  The generator will also create a migration file (if you're running ActiveRecord) and configure your routes.
16
16
 
17
+ The migration files will by default set up the following database structure:
18
+ user has_many :accounts, :through => :bento_memberships
19
+ accounts has_many :users, :through => :bento_memberships
20
+
17
21
  == Getting started
18
22
  === Model
19
23
  This is a walkthrough with all steps you need to setup a Bento resource, including model, migration and route files.
@@ -28,6 +32,10 @@ Bento doesn't use attr_accessible or attr_protected inside its modules, so be su
28
32
  Configure your routes after setting up your model. Open your config/routes.rb file and add:
29
33
  bento_for :accounts
30
34
  This will use your Account model to create a set of needed routes (you can see them by running `rake routes`).
35
+ You can nest other routes in bento_for just as you would with the standard resources routes method:
36
+ bento_for :accounts do
37
+ resources :projects
38
+ end
31
39
 
32
40
  This will add bento_for :accounts to your routes file. If you want to add another model just rename or add the argument(s) to bento_for.
33
41
  bento_for :accounts, :sites
@@ -61,8 +69,8 @@ This can be changed by overriding the after_create_url method:
61
69
  end
62
70
 
63
71
  == Navigating the source
64
- The gem has a basic set of cucumber features along with a rails app.
65
- Check them out to see some examples on how to use Bento in your application.
72
+ The gem has a basic set of cucumber features along with a rails app.
73
+ Check them out to see some examples on how to use Bento in your application.
66
74
 
67
75
  == Disclaimer
68
76
  * The gem is still in early beta, be careful.
@@ -76,7 +84,32 @@ This can be changed by overriding the after_create_url method:
76
84
  * All the gems that this gem depends on.
77
85
 
78
86
  == TODO:
79
- * Be able to nest other routes in bento_for.
80
- * Remove the requirement to generate view files when overriding the accounts controller.
87
+ * Make it possible to create new users
88
+ * Make it possible to completely remove users
89
+ * Add inline documentation
81
90
  * Make is possible to have another user model then User.
82
- * Make it possible for a user to belong to more then one account.
91
+
92
+ == License:
93
+
94
+ (The MIT License)
95
+
96
+ Copyright (c) 2011 Nicklas Ramhöj
97
+
98
+ Permission is hereby granted, free of charge, to any person obtaining
99
+ a copy of this software and associated documentation files (the
100
+ 'Software'), to deal in the Software without restriction, including
101
+ without limitation the rights to use, copy, modify, merge, publish,
102
+ distribute, sublicense, and/or sell copies of the Software, and to
103
+ permit persons to whom the Software is furnished to do so, subject to
104
+ the following conditions:
105
+
106
+ The above copyright notice and this permission notice shall be
107
+ included in all copies or substantial portions of the Software.
108
+
109
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
110
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
111
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
112
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
113
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
114
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
115
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,6 +1,7 @@
1
1
  module Bento
2
2
  require "bento/rails"
3
3
  require "bento/models/account"
4
+ require "bento/models/user"
4
5
  require "bento/controllers/helpers"
5
6
  require "bento/controllers/account_scopable"
6
7
  end
@@ -1,50 +1,82 @@
1
1
  module Bento
2
2
  module Controllers
3
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
- private
11
-
12
- def begin_of_association_chain
13
- @account ||= current_account
14
- end
15
-
16
- def current_account
17
- if not_responding_to_admin? or admin?
18
- account_by_param_or_session
19
- else
20
- current_user.account
4
+ mattr_accessor :resource_names
5
+ @@resource_names = []
6
+
7
+ def self.define_methods(resource_name)
8
+ @@resource_names << resource_name.to_s.singularize
9
+ @@name = @@resource_names.first
10
+
11
+ ActiveSupport.on_load(:action_controller) do
12
+ class_eval do
13
+ def admin?
14
+ respond_to_admin? and current_user.admin?
15
+ end
16
+
17
+ def not_responding_to_admin?
18
+ (not respond_to_admin?)
19
+ end
20
+
21
+ def respond_to_admin?
22
+ current_user.respond_to?(:admin?)
23
+ end
21
24
  end
22
- end
23
-
24
- def admin?
25
- respond_to_admin? and current_user.admin?
26
- end
27
-
28
- def not_responding_to_admin?
29
- (not respond_to_admin?)
30
- end
31
-
32
- def respond_to_admin?
33
- current_user.respond_to?(:admin?)
34
- end
35
-
36
- def account_by_param_or_session
37
- if account_id then Account.find(account_id) else current_user.account end
38
- end
39
-
40
- def account_id
41
- key, value = params.find { |key, value| key.to_s.ends_with?("account_id") }
42
- value
43
- end
44
- end
45
- end
46
- end
47
- end
25
+
26
+ @@resource_names.uniq.each do |name|
27
+ (class << self; self; end).send(:define_method, "scoped_to_#{name}") do
28
+ inherit_resources
29
+
30
+ class_eval do
31
+ private
32
+
33
+ # This is where things get scoped.
34
+ # If you're using inherit_resources it will automaticly
35
+ # call your associations on this method.
36
+ # In your own code you would just use current_<my account like model>-method.
37
+ def begin_of_association_chain
38
+ begining = instance_variable_get("@#{@@name}".to_sym)
39
+ return begining if begining
40
+
41
+ begining = send("current_#{@@name}")
42
+ instance_variable_set("@#{@@name}".to_sym, begining)
43
+ begining
44
+ end
45
+
46
+ def bento_resource_by_param_or_session
47
+ if bento_resource_id
48
+ @@name.camelcase.constantize.find(bento_resource_id)
49
+ else
50
+ current_user.send(@@name)
51
+ end
52
+ end
53
+
54
+ def bento_resource_id
55
+ key, value = params.find { |key, value| key.to_s.ends_with?("#{@@name}_id") }
56
+ value
57
+ end
58
+ end
59
+
60
+ class_eval <<-RUBY, __FILE__, __LINE__+1
61
+ private
62
+
63
+ def current_#{name}
64
+ if current_user
65
+ if current_user.#{name}
66
+ current_user.#{name}
67
+ elsif not_responding_to_admin? or admin?
68
+ bento_resource_by_param_or_session
69
+ end
70
+ end
71
+ end
72
+ RUBY
73
+ end # define_method
74
+ end # @@resource_names.uniq.each
75
+ end # ActiveSupport.on_load
76
+ end # def self.define_helpers
77
+ end # module AccountScopable
78
+ end # module Controllers
79
+ end # module Bento
48
80
 
49
81
  class ActionController::Base
50
82
  extend Bento::Controllers::AccountScopable
@@ -4,7 +4,8 @@ module Bento
4
4
  module UserAssociation
5
5
  def self.included(base)
6
6
  base.class_eval do
7
- has_many :users
7
+ has_many :bento_memberships
8
+ has_many :users, :through => :bento_memberships
8
9
  end
9
10
  end
10
11
  end
@@ -0,0 +1,24 @@
1
+ module Bento
2
+ module Models
3
+ module User
4
+ def fake_belongs_to(singular)
5
+ singular = singular.to_s
6
+ plural = singular.pluralize
7
+
8
+ define_method(singular) do
9
+ send(plural).first
10
+ end
11
+
12
+ define_method("#{singular}=") do |record|
13
+ send(plural).each(&:destroy)
14
+ self.send(plural) << record
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # TODO: only include in the user model
22
+ class ActiveRecord::Base
23
+ extend Bento::Models::User
24
+ end
@@ -1,12 +1,17 @@
1
1
  module ActionDispatch::Routing
2
2
  class Mapper
3
- def bento_for(*resource_names)
3
+ def bento_for(*resource_names, &block)
4
+ options = resource_names.extract_options!
4
5
  resource_names.map!(&:to_sym)
6
+
5
7
  resource_names.each do |resource_name|
6
8
  Bento::Controllers::Helpers.define_helpers(resource_name)
9
+ Bento::Controllers::AccountScopable.define_methods(resource_name)
10
+ resource_options = { :controller => account_controller(resource_name) }.merge(options)
7
11
 
8
- resources(resource_name, :controller => account_controller(resource_name)) do
12
+ resources(resource_name, resource_options) do
9
13
  collection { get :sign_up }
14
+ yield if block_given?
10
15
  end
11
16
  end
12
17
  end
@@ -1,3 +1,3 @@
1
1
  module Bento
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -11,18 +11,18 @@ module ActiveRecord
11
11
 
12
12
  def generate_model
13
13
  invoke "active_record:model", [name], :migration => false unless model_exists?
14
+ create_file "app/models/bento_membership.rb", bento_membership_model_content
14
15
  end
15
16
 
16
17
  def copy_bento_migration
17
18
  migration_template "create_migration.rb", "db/migrate/bento_create_#{table_name}"
18
- migration_template "add_migration.rb", "db/migrate/bento_add_#{name}_id_to_#{table_name}"
19
+ migration_template "bento_membership_migration.rb", "db/migrate/bento_create_bento_memberships"
19
20
  end
20
21
 
21
22
  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
23
+ inject_into_class model_path, class_name, model_contents
24
+ inject_into_class user_path, "User", user_model_content
25
+ inject_into_class bento_membership_path, "BentoMembership", bento_membership_model_content
26
26
  end
27
27
  end
28
28
  end
@@ -0,0 +1,25 @@
1
+ class BentoCreateBentoMemberships < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :bento_memberships do |t|
4
+ t.integer :<%= name %>_id
5
+ t.integer :user_id
6
+ t.timestamps
7
+ end
8
+
9
+ add_index :bento_memberships, [:<%= name %>_id, :user_id], :unique => true
10
+
11
+ # If you know for sure that you don't want to be able
12
+ # to have users that belongs to several <%= name.pluralize %>
13
+ # please remove the above lines and uncomment the lines below
14
+
15
+ # add_column :users, :<%= name %>_id, :integer
16
+ # add_index :users, :<%= name %>_id
17
+ end
18
+
19
+ def self.down
20
+ drop_table :bento_memberships
21
+
22
+ # remove_column :users, :<%= name %>_id
23
+ # remove_index :users, :<%= name %>_id
24
+ end
25
+ end
@@ -7,6 +7,44 @@ module Bento
7
7
  # :all, :validations, :user_accessors, :user_association, :user_accessors, :trial
8
8
  bento
9
9
 
10
+ # Setup accessible (or protected) attributes for your model
11
+ attr_accessible :name
12
+ CONTENT
13
+ end
14
+
15
+ def user_model_content
16
+ <<-CONTENT
17
+ has_many :bento_memberships
18
+ has_many :#{table_name}, :through => :bento_memberships
19
+
20
+ #
21
+ # Remove this method call unless you want to work with
22
+ # the has_many :through association as if it was a simple
23
+ # belongs_to :#{class_name.underscore}.
24
+ #
25
+ # If you know for sure that you don't want to be able
26
+ # to have users that belongs to several #{table_name} you
27
+ # can of course edit the generated migration files to
28
+ # not create the join model table "bento_memberships" and instead
29
+ # add a #{class_name.underscore}_id to your User model.
30
+ #
31
+ # You also need to not use the :user_association module in bento
32
+ # for example to use all in your #{class_name} model change the
33
+ # call to bento from `bento :all` to bento `:validations, :user_accessors, :user_accessors, :trial`
34
+ # then you just remove the above relations and remove the "fake_" part below.
35
+ #
36
+ fake_belongs_to :#{class_name.underscore}
37
+ CONTENT
38
+ end
39
+
40
+ def bento_membership_model_content
41
+ <<-CONTENT
42
+ class BentoMembership < ActiveRecord::Base
43
+ belongs_to :user
44
+ belongs_to :#{class_name.underscore}
45
+
46
+ attr_accessible :#{class_name.underscore}_id, :user_id
47
+ end
10
48
  CONTENT
11
49
  end
12
50
 
@@ -14,9 +52,21 @@ CONTENT
14
52
  File.exists?(File.join(destination_root, model_path))
15
53
  end
16
54
 
55
+ def bento_membership_exists?
56
+ File.exists?(File.join(destination_root, bento_membership_path))
57
+ end
58
+
17
59
  def model_path
18
60
  @model_path ||= File.join("app", "models", "#{file_path}.rb")
19
61
  end
62
+
63
+ def bento_membership_path
64
+ @bento_membership_path ||= File.join("app", "models", "bento_membership.rb")
65
+ end
66
+
67
+ def user_path
68
+ @user_path ||= File.join("app", "models", "user.rb")
69
+ end
20
70
  end
21
71
  end
22
72
  end
@@ -7,6 +7,17 @@ class UserAccessorsTestAccount < ActiveRecord::Base
7
7
  end
8
8
 
9
9
  describe Bento::Models::Modules::UserAccessors do
10
+ before do
11
+ # Adding alias to get the relations to work with our custom made class
12
+
13
+ BentoMembership.class_eval do
14
+ attr_accessible :user_accessors_test_account_id
15
+ def user_accessors_test_account_id=(id)
16
+ self.account_id = id
17
+ end
18
+ end
19
+ end
20
+
10
21
  let(:account_params) do
11
22
  { :name => "Hashrocket", :first_name => "Obie", :last_name => "Fernandez", :email => "obie@hashrocket.com", :password => "test1234" }
12
23
  end
@@ -22,8 +33,7 @@ describe Bento::Models::Modules::UserAccessors do
22
33
  context "all user attributes are blank" do
23
34
  it "creates the account without the user" do
24
35
  account = UserAccessorsTestAccount.new(:name => "Elabs")
25
- account.save.should be_true
26
- User.find_by_account_id(account.id).should be_nil
36
+ expect { account.save.should be_true }.to_not change(User, :count)
27
37
  end
28
38
  end
29
39
 
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bento::Models::User do
4
+ describe ".fake_belongs_to" do
5
+ let(:user) { User.make }
6
+ let(:account) { Account.make }
7
+ before do
8
+ User.class_eval { fake_belongs_to(:account) }
9
+ user.accounts << account
10
+ end
11
+
12
+ it "defines #account" do
13
+ user.account.should == account
14
+ end
15
+
16
+ it "defines #account=" do
17
+ other_account = Account.make
18
+ user.account = other_account
19
+
20
+ user.reload.account.should == other_account
21
+ user.accounts.length.should == 1
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+
3
+ class SitesController < ActionController::Base
4
+ def show; render(:text => "s"); end
5
+ def index; render(:text => "i"); end
6
+ def new; render(:text => "n"); end
7
+ def edit; render(:text => "e"); end
8
+ def create; redirect_to sites_url; end
9
+ def update; redirect_to sites_url; end
10
+ def destroy; redirect_to sites_url; end
11
+ end
12
+
13
+ describe SitesController do
14
+ describe "#bento_for" do
15
+ it "defines resource routes" do
16
+ with_routing do |map|
17
+ map.draw { bento_for :sites }
18
+
19
+ get(:show, :id => 1); response.should be_success
20
+ get(:index); response.should be_success
21
+ get(:new, :id => 1); response.should be_success
22
+ get(:edit, :id => 1); response.should be_success
23
+ put(:update, :id => 1); response.should be_redirect
24
+ post(:create); response.should be_redirect
25
+ put(:destroy, :id => 1); response.should be_redirect
26
+ end
27
+ end
28
+
29
+ it "allows the default options to resourcers" do
30
+ with_routing do |map|
31
+ map.draw { bento_for :sites, :only => %w[show new] }
32
+ get(:show, :id => 1); response.should be_success
33
+ get(:new); response.should be_success
34
+ expect { get(:index) }.to raise_error
35
+ end
36
+ end
37
+ end
38
+ end