bento 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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