account_scopper 0.1.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ *.log
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Sebastien Grosjean
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,71 @@
1
+ = Account Scopper
2
+
3
+ Account Scopper aims to make conversion from a single account to multi-accounts application as easy as possible.
4
+
5
+ Simply set your current_account before each request and the scoping will be done on it's own.
6
+
7
+ == Usage
8
+
9
+ To convert your application, you will need to make few changes :
10
+
11
+ 1. Create an Account model and Accounts table
12
+ 2. Add account_id to your existing models (except Account of course)
13
+ 3. Make relationship between Account and other models
14
+ 4. Add the class variable "current_account" to the model Account
15
+ 5. Initialize Account.current_account before every request (eg: app/controllers/application.rb)
16
+
17
+ eg:
18
+
19
+ # app/model/account.rb
20
+
21
+ class Account < ActiveRecord::Base
22
+ cattr_accessor :current_account
23
+
24
+ has_many :users
25
+ end
26
+
27
+ # app/model/user.rb
28
+
29
+ class User < ActiveRecord::Base
30
+ belongs_to :account
31
+ end
32
+
33
+ # app/controllers/application.rb
34
+
35
+ class ApplicationController < ActionController::Base
36
+ before_filter :set_current_account
37
+ # ...
38
+
39
+ private
40
+ # Don't forget that @current_account should be properly setup to be an Account object
41
+ # for this purpose you can use the plugin AccountLocation from David Heinemeier Hansson
42
+ def set_current_account
43
+ Account.current_account = @current_account
44
+ end
45
+ end
46
+
47
+ == What it does
48
+
49
+ The plugin overwrite few methods from ActiveRecord::Base
50
+
51
+ To have a better understanding of what it does, the best way is still to look
52
+ at the code itself and the tests.
53
+
54
+ == Warning
55
+
56
+ If using manually generated database requests like find_by_sql, ... you'll have to do your own scoping
57
+
58
+ == Note on Patches/Pull Requests
59
+
60
+ * Fork the project.
61
+ * Make your feature addition or bug fix.
62
+ * Add tests for it. This is important so I don't break it in a
63
+ future version unintentionally.
64
+ * Commit, do not mess with rakefile, version, or history.
65
+ (if you want to have your own version, that is fine but
66
+ bump version in a commit by itself I can ignore when I pull)
67
+ * Send me a pull request. Bonus points for topic branches.
68
+
69
+ == Copyright
70
+
71
+ Copyright (c) 2009 Sebastien Grosjean. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "account_scopper"
8
+ gem.summary = "Account Scopper: Automatically scope your ActiveRecord's model by account. Ideal for multi-account applications."
9
+ gem.description = "Account Scopper: Automatically scope your ActiveRecord's model by account. Ideal for multi-account applications."
10
+ gem.email = "public@zencocoon.com"
11
+ gem.homepage = "http://github.com/ZenCocoon/account_scopper"
12
+ gem.authors = ["Sebastien Grosjean"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "account_scopper #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,73 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{account_scopper}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Sebastien Grosjean"]
12
+ s.date = %q{2009-11-11}
13
+ s.description = %q{Account Scopper: Automatically scope your ActiveRecord's model by account. Ideal for multi-account applications.}
14
+ s.email = %q{public@zencocoon.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "account_scopper.gemspec",
27
+ "init.rb",
28
+ "lib/account_scopper.rb",
29
+ "spec/account_scopper_spec.rb",
30
+ "spec/boot.rb",
31
+ "spec/database.yml",
32
+ "spec/fixtures/account.rb",
33
+ "spec/fixtures/accounts.yml",
34
+ "spec/fixtures/user.rb",
35
+ "spec/fixtures/users.yml",
36
+ "spec/lib/load_fixtures.rb",
37
+ "spec/lib/load_models.rb",
38
+ "spec/lib/load_schema.rb",
39
+ "spec/schema.rb",
40
+ "spec/spec.opts",
41
+ "spec/spec_helper.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/ZenCocoon/account_scopper}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.5}
47
+ s.summary = %q{Account Scopper: Automatically scope your ActiveRecord's model by account. Ideal for multi-account applications.}
48
+ s.test_files = [
49
+ "spec/account_scopper_spec.rb",
50
+ "spec/boot.rb",
51
+ "spec/fixtures/account.rb",
52
+ "spec/fixtures/user.rb",
53
+ "spec/lib/load_fixtures.rb",
54
+ "spec/lib/load_models.rb",
55
+ "spec/lib/load_schema.rb",
56
+ "spec/schema.rb",
57
+ "spec/spec_helper.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
65
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
66
+ else
67
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
68
+ end
69
+ else
70
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
71
+ end
72
+ end
73
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'account_scopper'
@@ -0,0 +1,58 @@
1
+ module ActiveRecord # :nodoc:
2
+ class Base # :nodoc:
3
+ class << self # Class methods
4
+ alias_method :orig_delete_all, :delete_all
5
+ def delete_all(conditions = nil)
6
+ if Account.current_account != nil && column_names.include?("account_id")
7
+ with_scope(:find => {:conditions => "account_id = "+Account.current_account.id.to_s}) do
8
+ orig_delete_all(conditions)
9
+ end
10
+ else
11
+ orig_delete_all(conditions)
12
+ end
13
+ end
14
+
15
+ alias_method :orig_calculate, :calculate
16
+ def calculate(operation, column_name, options = {})
17
+ if Account.current_account != nil && column_names.include?("account_id")
18
+ with_scope(:find => {:conditions => ["account_id = ?", Account.current_account.id]}) do
19
+ orig_calculate(operation, column_name, options)
20
+ end
21
+ else
22
+ orig_calculate(operation, column_name, options)
23
+ end
24
+ end
25
+
26
+ private
27
+ alias_method :orig_find_every, :find_every
28
+ def find_every(options)
29
+ if Account.current_account != nil && column_names.include?("account_id")
30
+ with_scope(:find => {:conditions => self.table_name+".`account_id` = "+Account.current_account.id.to_s}) do
31
+ orig_find_every(options)
32
+ end
33
+ else
34
+ orig_find_every(options)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Instance methods, they are called from an instance of a model (an object)
40
+ alias_method :orig_destroy, :destroy
41
+ def destroy
42
+ if !Account.current_account.nil? && respond_to?(:account_id)
43
+ orig_destroy if self.account_id == Account.current_account.id
44
+ else
45
+ orig_destroy
46
+ end
47
+ end
48
+
49
+ private
50
+ alias_method :orig_create, :create
51
+ def create
52
+ if !Account.current_account.nil? && respond_to?(:account_id)
53
+ self.account_id = Account.current_account.id
54
+ end
55
+ orig_create
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,69 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "AccountScopper" do
4
+ # Homemade fixtures method that doesn't depend on active support
5
+ fixtures :accounts, :users
6
+
7
+ it "should proper set fixtures" do
8
+ Account.find_by_name("Localhost").should == accounts(:localhost)
9
+ end
10
+
11
+ it "should have the current account initialized" do
12
+ Account.current_account.should == accounts(:localhost)
13
+ end
14
+
15
+ it "should create user with account" do
16
+ u = User.create
17
+ u.account.should == accounts(:localhost)
18
+ end
19
+
20
+ it "should force account while creating new user" do
21
+ u = User.create(:account_id => 2)
22
+ u.account.should == accounts(:localhost)
23
+ end
24
+
25
+ it "should find user within account" do
26
+ User.find(2).should == users(:marc)
27
+ end
28
+
29
+ it "should not find user having different account" do
30
+ lambda { User.find(3) }.should raise_error(ActiveRecord::RecordNotFound)
31
+ end
32
+
33
+ it "should return only user from account" do
34
+ User.all.each do |user|
35
+ user.account.should == accounts(:localhost)
36
+ end
37
+ end
38
+
39
+ it "should count only within account" do
40
+ User.count.should == User.all.size
41
+ end
42
+
43
+ it "should delete all within account, not others" do
44
+ User.delete_all
45
+ User.count.should == 0
46
+ Account.current_account = nil
47
+ User.count.should > 0
48
+ end
49
+
50
+ it "should delete all if no account defined" do
51
+ Account.current_account = nil
52
+ User.delete_all
53
+ User.count.should == 0
54
+ end
55
+
56
+ it "should not allow to destroy foreign users" do
57
+ Account.current_account = nil
58
+ user = User.find_by_name('Stephane')
59
+ user.account.should_not == accounts(:localhost)
60
+ Account.current_account = accounts(:localhost)
61
+ user.destroy.should be_nil
62
+ end
63
+
64
+ it "should allow to destroy if not account" do
65
+ Account.current_account = nil
66
+ user = User.find_by_name('Stephane')
67
+ user.destroy.should_not be_nil
68
+ end
69
+ end
data/spec/boot.rb ADDED
@@ -0,0 +1,21 @@
1
+ plugin_root = File.join(File.dirname(__FILE__), '..')
2
+ version = ENV['RAILS_VERSION']
3
+ version = nil if version and version == ""
4
+
5
+ # first look for a symlink to a copy of the framework
6
+ if !version and framework_root = ["#{plugin_root}/rails", "#{plugin_root}/../../rails"].find { |p| File.directory? p }
7
+ puts "found framework root: #{framework_root}"
8
+ # this allows for a plugin to be tested outside of an app and without Rails gems
9
+ $:.unshift "#{framework_root}/activesupport/lib", "#{framework_root}/activerecord/lib", "#{framework_root}/actionpack/lib"
10
+ else
11
+ # simply use installed gems if available
12
+ puts "using Rails#{version ? ' ' + version : nil} gems"
13
+ require 'rubygems'
14
+
15
+ if version
16
+ gem 'rails', version
17
+ else
18
+ gem 'actionpack'
19
+ gem 'activerecord'
20
+ end
21
+ end
data/spec/database.yml ADDED
@@ -0,0 +1,21 @@
1
+ sqlite2:
2
+ :adapter: sqlite
3
+ :database: ":memory:"
4
+
5
+ sqlite3:
6
+ :adapter: sqlite3
7
+ :database: ":memory:"
8
+
9
+ postgresql:
10
+ :adapter: postgresql
11
+ :username: postgres
12
+ :password: postgres
13
+ :database: account_scopper_test
14
+ :min_messages: ERROR
15
+
16
+ mysql:
17
+ :adapter: mysql
18
+ :host: localhost
19
+ :username: root
20
+ :password: password
21
+ :database: account_scopper_test
@@ -0,0 +1,5 @@
1
+ class Account < ActiveRecord::Base
2
+ cattr_accessor :current_account
3
+
4
+ has_many :users
5
+ end
@@ -0,0 +1,6 @@
1
+ localhost:
2
+ id: 1
3
+ name: Localhost
4
+ demo:
5
+ id: 2
6
+ name: Demo
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ belongs_to :account
3
+ end
@@ -0,0 +1,12 @@
1
+ seb:
2
+ id: 1
3
+ account_id: 1
4
+ name: Seb
5
+ marc:
6
+ id: 2
7
+ account_id: 1
8
+ name: Marc
9
+ stephane:
10
+ id: 3
11
+ account_id: 2
12
+ name: Stephane
@@ -0,0 +1,13 @@
1
+ require 'active_record/fixtures'
2
+
3
+ def load_fixtures
4
+ Fixtures.create_fixtures(File.dirname(__FILE__) + "/../fixtures/", ActiveRecord::Base.connection.tables)
5
+ end
6
+
7
+ def fixtures(*args)
8
+ args.each do |fixture|
9
+ define_method fixture.to_s do |symbol|
10
+ eval(fixture.to_s.singularize.camelize).find(Fixtures.all_loaded_fixtures[fixture.to_s][symbol.to_s]["id"])
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ def load_models
2
+ Dir[File.dirname(__FILE__) + "/../fixtures/*.rb"].entries.compact.each do |str|
3
+ load(str)
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ require 'active_record'
2
+
3
+ def load_schema
4
+ ActiveRecord::Schema.verbose = false
5
+
6
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/../database.yml'))
7
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/../debug.log")
8
+
9
+ db_adapter = ENV['DB']
10
+
11
+ # no db passed, try one of these fine config-free DBs before bombing.
12
+ db_adapter ||=
13
+ begin
14
+ require 'rubygems'
15
+ require 'sqlite'
16
+ 'sqlite'
17
+ rescue MissingSourceFile
18
+ begin
19
+ require 'sqlite3'
20
+ 'sqlite3'
21
+ rescue MissingSourceFile
22
+ end
23
+ end
24
+
25
+ if db_adapter.nil?
26
+ raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
27
+ end
28
+
29
+ ActiveRecord::Base.establish_connection(config[db_adapter])
30
+ load(File.dirname(__FILE__) + "/../schema.rb")
31
+ require File.dirname(__FILE__) + '/../../init.rb'
32
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,9 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :accounts, :force => true do |t|
3
+ t.string :name
4
+ end
5
+ create_table :users, :force => true do |t|
6
+ t.string :name
7
+ t.references :account
8
+ end
9
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'spec'
4
+ require 'spec/autorun'
5
+
6
+ require 'boot' unless defined?(ActiveRecord)
7
+
8
+ require File.dirname(__FILE__) + '/lib/load_schema'
9
+ require File.dirname(__FILE__) + '/lib/load_models'
10
+ require File.dirname(__FILE__) + '/lib/load_fixtures'
11
+
12
+ Spec::Runner.configure do |config|
13
+ load_models
14
+
15
+ config.before do
16
+ load_schema
17
+ load_fixtures
18
+ Account.current_account = accounts(:localhost)
19
+ end
20
+
21
+ config.after do
22
+ Account.current_account = nil
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: account_scopper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sebastien Grosjean
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-11 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ description: "Account Scopper: Automatically scope your ActiveRecord's model by account. Ideal for multi-account applications."
26
+ email: public@zencocoon.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - account_scopper.gemspec
42
+ - init.rb
43
+ - lib/account_scopper.rb
44
+ - spec/account_scopper_spec.rb
45
+ - spec/boot.rb
46
+ - spec/database.yml
47
+ - spec/fixtures/account.rb
48
+ - spec/fixtures/accounts.yml
49
+ - spec/fixtures/user.rb
50
+ - spec/fixtures/users.yml
51
+ - spec/lib/load_fixtures.rb
52
+ - spec/lib/load_models.rb
53
+ - spec/lib/load_schema.rb
54
+ - spec/schema.rb
55
+ - spec/spec.opts
56
+ - spec/spec_helper.rb
57
+ has_rdoc: true
58
+ homepage: http://github.com/ZenCocoon/account_scopper
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --charset=UTF-8
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.5
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: "Account Scopper: Automatically scope your ActiveRecord's model by account. Ideal for multi-account applications."
85
+ test_files:
86
+ - spec/account_scopper_spec.rb
87
+ - spec/boot.rb
88
+ - spec/fixtures/account.rb
89
+ - spec/fixtures/user.rb
90
+ - spec/lib/load_fixtures.rb
91
+ - spec/lib/load_models.rb
92
+ - spec/lib/load_schema.rb
93
+ - spec/schema.rb
94
+ - spec/spec_helper.rb