maiha-scoped_access 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 ADDED
@@ -0,0 +1,4 @@
1
+ ScopedAccess
2
+ ============
3
+
4
+ See http://habtm.com/articles/2006/02/22/nested-with_scope
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the scoped_access plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the scoped_access plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'ScopedAccess'
19
+ rdoc.options << '--line-numbers --inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
data/init.rb ADDED
@@ -0,0 +1,42 @@
1
+ ActionController::Base.instance_eval do
2
+ def scoped_access (*args, &block)
3
+ options = (Hash === args.last && !(args.last.keys & [:only, :except]).empty?) ? args.pop : {}
4
+ send(:around_filter, ScopedAccess::Filter.new(*args, &block), options)
5
+ end
6
+ end
7
+
8
+ require 'dispatcher'
9
+ if ActionController.const_defined?(:Dispatcher)
10
+ # Rails2.1
11
+ ActionController::Dispatcher.class_eval do
12
+ class << self
13
+ def define_dispatcher_callbacks_with_reset(cache_classes)
14
+ define_dispatcher_callbacks_without_reset(cache_classes)
15
+ ScopedAccess.reset
16
+ end
17
+ alias_method_chain :define_dispatcher_callbacks, :reset
18
+ end
19
+ end
20
+
21
+ else
22
+ # Rails1.2 or Rails2.0
23
+ class ::Dispatcher
24
+ app = respond_to?(:prepare_application, true) ? (class << self; self end) : self
25
+ app.class_eval do
26
+ private
27
+ def prepare_application_with_reset
28
+ ScopedAccess.reset
29
+ prepare_application_without_reset
30
+ end
31
+
32
+ alias_method :prepare_application_without_reset, :prepare_application
33
+ alias_method :prepare_application, :prepare_application_with_reset
34
+ end
35
+ end
36
+ end
37
+
38
+ ActiveRecord::Base.instance_eval do
39
+ def reset_scope
40
+ scoped_methods.clear
41
+ end
42
+ end
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,197 @@
1
+ module ScopedAccess
2
+ class SqlCondition
3
+ attr_reader :attributes, :constrains
4
+
5
+ Constrain = Struct.new(:statement, :parameters)
6
+
7
+ def initialize (attributes = nil)
8
+ if attributes.is_a?(SqlCondition)
9
+ constrains = attributes.constrains.dup
10
+ attributes = attributes.attributes
11
+ end
12
+
13
+ @attributes = HashWithIndifferentAccess.new(attributes ? attributes.dup : {})
14
+ @constrains = constrains || []
15
+ @cached = nil
16
+ end
17
+
18
+ def add (statement, *parameters)
19
+ @constrains << Constrain.new(normalize_statement(statement), parameters)
20
+ updated
21
+ end
22
+
23
+ def []= (key, val)
24
+ @attributes[key] = val
25
+ updated
26
+ end
27
+
28
+ def generate
29
+ @cached ||= parse
30
+ end
31
+
32
+ def + (other)
33
+ other.attributes.to_a.inject(self.class.new(@attributes)){|obj, (key, val)| obj[key] = val; obj}
34
+ end
35
+
36
+ def empty?
37
+ @attributes.empty? && @constrains.empty?
38
+ end
39
+
40
+ protected
41
+ def normalize_column_name (column_name)
42
+ column_name
43
+ end
44
+
45
+ def normalize_statement (statement)
46
+ # statement.sub(/\A\s*\(\s*(.*?)\s*\)\s*\Z/) {$2} # strip parentheses
47
+ # this is buggy for the case: "(date1 <= ?) AND (date2 BETWEEN ? AND ?)"
48
+ statement
49
+ end
50
+
51
+ def updated
52
+ @cached = nil
53
+ end
54
+
55
+ def parse
56
+ constrains = @attributes.keys.sort.inject(@constrains.dup) {|array, key| array << parse_attribute(key, @attributes[key])}
57
+ statements = constrains.collect{|constrain| "( %s )" % constrain.statement}
58
+ parameters = constrains.inject([]){|array, constrain| array += constrain.parameters unless constrain.parameters.empty?; array}
59
+ [statements.join(" AND ")] + parameters
60
+ end
61
+
62
+ def parse_attribute (column_name, value)
63
+ column_name = normalize_column_name(column_name)
64
+ if value.nil?
65
+ Constrain.new("#{column_name} is NULL", [])
66
+ elsif value.is_a? Array
67
+ Constrain.new("#{column_name} in (?)", value)
68
+ else
69
+ Constrain.new("#{column_name} = ?", [value])
70
+ end
71
+ end
72
+ end
73
+
74
+ class MethodScoping < SqlCondition
75
+ attr_accessor :find_options, :options
76
+
77
+ def initialize (attributes = nil, options = {}, finder_options = {})
78
+ super(attributes)
79
+ @options = HashWithIndifferentAccess.new(options || {})
80
+ @find_options = finder_options || {}
81
+ end
82
+
83
+ def method_scoping
84
+ constrains = {
85
+ :find => construct_finder,
86
+ :create => @attributes,
87
+ }
88
+ constrains.delete(:find) if constrains[:find] == {:conditions => [""]}
89
+ return constrains
90
+ end
91
+
92
+ protected
93
+ def construct_finder
94
+ (generate == [""]) ? @find_options : @find_options.merge(:conditions => generate)
95
+ end
96
+ end
97
+
98
+ class ClassScoping < MethodScoping
99
+ def initialize (klass, *args)
100
+ (@klass = klass).is_a?(Class) or
101
+ raise ArgumentError, "A subclass of ActiveRecord::Base is required for '#{klass.class}'"
102
+ super(*args)
103
+ end
104
+
105
+ protected
106
+ def normalize_column_name (column_name)
107
+ "#{ @klass.table_name }.#{ column_name }"
108
+ end
109
+
110
+ def column_name_regexp_string
111
+ @column_name_regexp_string ||= "(%s)" % @klass.column_names.join('|')
112
+ end
113
+
114
+ def normalize_statement (statement)
115
+ super.sub(/\A#{column_name_regexp_string}(\s*=|\s+(is|in)\s+)/mi) {
116
+ "%s%s" % [normalize_column_name($1), $2]
117
+ }
118
+ end
119
+
120
+ end
121
+
122
+ class Filter
123
+ @applied_classes = Set.new
124
+ class << self
125
+ def reset
126
+ ActiveRecord::Base.logger.debug("ScopedAccess: reset %s" % @applied_classes.to_a.inspect)
127
+ @applied_classes.each(&:reset_scope)
128
+ @applied_classes.clear
129
+ end
130
+
131
+ def mark(klass)
132
+ @applied_classes << klass
133
+ ActiveRecord::Base.logger.debug("ScopedAccess: mark %s" % klass)
134
+ end
135
+
136
+ def unmark(klass)
137
+ @applied_classes.delete(klass)
138
+ ActiveRecord::Base.logger.debug("ScopedAccess: unmark %s" % klass)
139
+ end
140
+ end
141
+
142
+ def initialize (klass, scoping = :method_scoping, &block)
143
+ @klass = klass
144
+ @scoping = block || scoping
145
+ end
146
+
147
+ def before (controller)
148
+ constrain = self.class.generate_constrain(@klass, @scoping, :table_name=>@klass.table_name, :controller=>controller)
149
+ @klass.logger.debug("ScopedAccessFilter#before (called from %s):\n\t[%s] scope becomes %s" %
150
+ [controller.class, @klass, constrain.inspect])
151
+ @klass.instance_eval do
152
+ Filter.mark(self)
153
+ scoped_methods << with_scope(constrain){current_scoped_methods}
154
+ end
155
+ end
156
+
157
+ def after (controller)
158
+ @klass.instance_eval do
159
+ scoped_methods.pop
160
+ end
161
+ @klass.logger.debug("ScopedAccess::Filter#after (called from %s):\n\t[%s] scope is restored to %s" %
162
+ [controller.class, @klass, @klass.send(:scoped_methods).inspect])
163
+ end
164
+
165
+ class << self
166
+ def generate_constrain (klass, scoping, scoping_options = {})
167
+ case scoping
168
+ when Symbol
169
+ controller = scoping_options[:controller] or raise RuntimeError, "[plugin bug???] missing controller"
170
+ method_scoping = controller.__send__(scoping)
171
+ raise RuntimeError, "generate_constrain got infinite loop!" if method_scoping.is_a?(Symbol)
172
+ return generate_constrain(klass, method_scoping, scoping_options)
173
+ when Proc
174
+ method_scoping = scoping.call(scoping_options[:controller])
175
+ raise RuntimeError, "generate_constrain got infinite loop!" if method_scoping.is_a?(Proc)
176
+ return generate_constrain(klass, method_scoping, scoping_options)
177
+ when Hash, ClassScoping
178
+ return scoping
179
+ when SqlCondition
180
+ method_scoping = ClassScoping.new(klass, scoping, scoping_options.reject{|k,| k==:controller})
181
+ return generate_constrain(klass, method_scoping)
182
+ when Array, String
183
+ method_scoping = ClassScoping.new(klass, {}, scoping_options.reject{|k,| k==:controller})
184
+ method_scoping.add(*scoping)
185
+ return generate_constrain(klass, method_scoping)
186
+ else
187
+ raise TypeError, "cannot generate constrain from this type: (%s)" % scoping.class
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ module_function
194
+ def reset
195
+ Filter.reset
196
+ end
197
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :scoped_access do
3
+ # # Task goes here
4
+ # end
data/test/database.yml ADDED
@@ -0,0 +1,22 @@
1
+ sqlite:
2
+ :adapter: sqlite
3
+ :dbfile: scoped_access_plugin_test.sqlite.db
4
+
5
+ sqlite3:
6
+ :adapter: sqlite3
7
+ :dbfile: scoped_access_plugin_test.sqlite3.db
8
+
9
+ postgresql:
10
+ :adapter: postgresql
11
+ :username: postgres
12
+ :password: postgres
13
+ :database: scoped_access_plugin_test
14
+ :min_messages: ERROR
15
+
16
+ mysql:
17
+ :adapter: mysql
18
+ :host: localhost
19
+ :username: rails
20
+ :password:
21
+ :database: scoped_access_plugin_test
22
+
@@ -0,0 +1,143 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require __DIR__ + '/fixtures/member'
3
+
4
+ class FilterGenerateConstrainTest < Test::Unit::TestCase
5
+ def test_generate_constrain_from_hash
6
+ scoping = Filter.generate_constrain(Member, {:find=>{:limit=>10}})
7
+ expected = {
8
+ :find => { :limit => 10 }
9
+ }
10
+ assert_equal expected, scoping
11
+ end
12
+
13
+ def test_generate_constrain_from_string
14
+ scoping = Filter.generate_constrain(Member, 'name = "Maiha"').method_scoping
15
+ expected = {
16
+ :find => { :conditions => ['( members.name = "Maiha" )'] },
17
+ :create => { },
18
+ }
19
+ assert_equal expected, scoping
20
+ end
21
+
22
+ def test_generate_constrain_from_array
23
+ scoping = Filter.generate_constrain(Member, ['name = ?', "Maiha"]).method_scoping
24
+ expected = {
25
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
26
+ :create => { },
27
+ }
28
+ assert_equal expected, scoping
29
+ end
30
+
31
+ def test_generate_constrain_from_symbol
32
+ assert_raise(NoMethodError) {
33
+ Filter.generate_constrain(Member, :method_scoping, :controller=>:dummy)
34
+ }
35
+ end
36
+
37
+ def test_generate_constrain_from_proc
38
+ expected = {:find=>{}, :create=>{}}
39
+ proc = Proc.new{expected}
40
+ assert_equal expected, Filter.generate_constrain(Member, proc)
41
+ end
42
+
43
+ def test_generate_constrain_from_sql_condition
44
+ condition = SqlCondition.new(:name => "Maiha")
45
+
46
+ scoping = Filter.generate_constrain(Member, condition).method_scoping
47
+ expected = {
48
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
49
+ :create => { 'name' => "Maiha" },
50
+ }
51
+ assert_equal expected, scoping
52
+ end
53
+
54
+ def test_generate_constrain_from_method_scoping
55
+ method_scoping = MethodScoping.new(:name => "Maiha")
56
+
57
+ scoping = Filter.generate_constrain(Member, method_scoping).method_scoping
58
+ expected = {
59
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
60
+ :create => { 'name' => "Maiha" },
61
+ }
62
+ assert_equal expected, scoping
63
+ end
64
+ end
65
+
66
+
67
+
68
+ class FilterWithMethodScopingTest < Test::Unit::TestCase
69
+ def setup
70
+ @controller = Struct.new(:method_scoping).new # faked
71
+ @class = Member
72
+ @filter = Filter.new(@class)
73
+ @nested_depth = @class.instance_eval{scoped_methods}.size
74
+ end
75
+
76
+ def teardown
77
+ current_scopings = @class.instance_eval{scoped_methods}
78
+ nested_depth = current_scopings.size - @nested_depth
79
+ @class.instance_eval{nested_depth.times{scoped_methods.pop}}
80
+ end
81
+
82
+ def test_generate_constrain_from_hash
83
+ @controller.method_scoping = {:find=>{:limit=>10}}
84
+ scoping = @filter.before(@controller).last
85
+ expected = {
86
+ :find => { :limit => 10 }
87
+ }
88
+ assert_equal expected, scoping
89
+ end
90
+
91
+ def test_generate_constrain_from_string
92
+ @controller.method_scoping = 'name = "Maiha"'
93
+ scoping = @filter.before(@controller).last
94
+ expected = {
95
+ :find => { :conditions => ['( members.name = "Maiha" )'] },
96
+ :create => { },
97
+ }
98
+ assert_equal expected, scoping
99
+ end
100
+
101
+ def test_generate_constrain_from_array
102
+ @controller.method_scoping = ['name = ?', "Maiha"]
103
+ scoping = @filter.before(@controller).last
104
+ expected = {
105
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
106
+ :create => { },
107
+ }
108
+ assert_equal expected, scoping
109
+ end
110
+
111
+ def test_generate_constrain_from_symbol
112
+ expected = {:find=>{}, :create=>{}}
113
+ @controller.method_scoping = expected
114
+ assert_equal expected, scoping = @filter.before(@controller).last
115
+ end
116
+
117
+ def test_generate_constrain_from_proc
118
+ expected = {:find=>{}, :create=>{}}
119
+ @controller.method_scoping = Proc.new{expected}
120
+ assert_equal expected, scoping = @filter.before(@controller).last
121
+ end
122
+
123
+ def test_generate_constrain_from_sql_condition
124
+ @controller.method_scoping = SqlCondition.new(:name => "Maiha")
125
+ scoping = @filter.before(@controller).last
126
+ expected = {
127
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
128
+ :create => { 'name' => "Maiha" },
129
+ }
130
+ assert_equal expected, scoping
131
+ end
132
+
133
+ def test_generate_constrain_from_method_scoping
134
+ @controller.method_scoping = MethodScoping.new(:name => "Maiha")
135
+ scoping = @filter.before(@controller).last
136
+ expected = {
137
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
138
+ :create => { 'name' => "Maiha" },
139
+ }
140
+ assert_equal expected, scoping
141
+ end
142
+ end
143
+
@@ -0,0 +1,4 @@
1
+ class Favorite < ActiveRecord::Base
2
+ belongs_to :member
3
+ validates_presence_of :name, :member_id
4
+ end
@@ -0,0 +1,17 @@
1
+ ---
2
+ record_1:
3
+ name: "miso soup"
4
+ member_id: 1
5
+ id: 1
6
+ record_2:
7
+ name: "strawberry"
8
+ member_id: 2
9
+ id: 2
10
+ record_3:
11
+ name: "natto"
12
+ member_id: 3
13
+ id: 3
14
+ record_4:
15
+ name: "golf"
16
+ member_id: 10
17
+ id: 4
@@ -0,0 +1,4 @@
1
+ class Group < ActiveRecord::Base
2
+ has_many :members, :include=>"favorites", :order=>"members", :dependent=>true
3
+ validates_presence_of :name
4
+ end
@@ -0,0 +1,13 @@
1
+ ---
2
+ record_1:
3
+ name: Berryz
4
+ id: 1
5
+ deleted: false
6
+ record_2:
7
+ name: Cute
8
+ id: 2
9
+ deleted: false
10
+ record_5:
11
+ name: ZYX
12
+ id: 5
13
+ deleted: false
@@ -0,0 +1,5 @@
1
+ class Member < ActiveRecord::Base
2
+ belongs_to :group
3
+ has_many :favorites, :dependent=>true
4
+ validates_presence_of :yomi, :name, :comments, :group_id
5
+ end
@@ -0,0 +1,32 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ saki:
3
+ id: 1
4
+ name: Saki
5
+ group_id: 1
6
+ grade: 2
7
+ deleted: false
8
+ maiha:
9
+ id: 2
10
+ name: Maiha
11
+ group_id: 1
12
+ grade: 2
13
+ deleted: true
14
+ yurina:
15
+ id: 3
16
+ name: Yurina
17
+ group_id: 1
18
+ grade: 1
19
+ deleted: false
20
+ risako:
21
+ id: 4
22
+ name: Risako
23
+ group_id: 1
24
+ grade: 1
25
+ deleted: false
26
+ airi:
27
+ id: 10
28
+ name: Airi
29
+ group_id: 2
30
+ grade: 2
31
+ deleted: false
32
+
@@ -0,0 +1,4 @@
1
+ class Song < ActiveRecord::Base
2
+ belongs_to :group
3
+ validates_presence_of :name, :content, :group_id
4
+ end
@@ -0,0 +1,87 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class MethodScopingTest < Test::Unit::TestCase
4
+ def test_no_conditions
5
+ assert_equal({ :find=>{}, :create=>{} }, MethodScoping.new(nil).method_scoping)
6
+ assert_equal({ :find=>{}, :create=>{} }, MethodScoping.new({}).method_scoping)
7
+ end
8
+
9
+ def test_create_method_scoping
10
+ method_scoping = MethodScoping.new(:name => "Maiha")
11
+ expected = {
12
+ :find => { :conditions => ['( name = ? )', "Maiha"] },
13
+ :create => { 'name' => "Maiha" },
14
+ }
15
+ assert_equal expected, method_scoping.method_scoping
16
+ end
17
+
18
+ def test_create_method_scoping_with_attributed_and_conditioned_values
19
+ method_scoping = MethodScoping.new(:name => "Maiha")
20
+ method_scoping.add("grade IN ?", [1,2])
21
+ method_scoping[:group] = 'Berryz'
22
+
23
+ expected = {
24
+ :find => { :conditions => ["( grade IN ? ) AND ( group = ? ) AND ( name = ? )", [1,2], "Berryz", "Maiha"] },
25
+ :create => { 'name' => "Maiha", 'group' => "Berryz" },
26
+ }
27
+ assert_equal expected, method_scoping.method_scoping
28
+ end
29
+
30
+ def test_plus_method_with_merge
31
+ scoping1 = MethodScoping.new(:name => "Maiha")
32
+ scoping2 = MethodScoping.new(:group => "Berryz")
33
+
34
+ expected = {
35
+ "name" => "Maiha",
36
+ "group" => "Berryz",
37
+ }
38
+ assert_equal expected, (scoping1 + scoping2).attributes
39
+ end
40
+
41
+ def test_plus_method_with_overwrite
42
+ scoping1 = MethodScoping.new(:name => "Maiha", :group => "Berryz")
43
+ scoping2 = MethodScoping.new(:name => "Saki")
44
+
45
+ expected = {
46
+ "name" => "Saki",
47
+ "group" => "Berryz",
48
+ }
49
+ assert_equal expected, (scoping1 + scoping2).attributes
50
+ end
51
+ end
52
+
53
+
54
+ class ClassScopingTest < Test::Unit::TestCase
55
+ def test_create_class_scoping
56
+ class_scoping = ClassScoping.new(Member, :name => "Maiha")
57
+ expected = {
58
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
59
+ :create => { 'name' => "Maiha" },
60
+ }
61
+ assert_equal expected, class_scoping.method_scoping
62
+ end
63
+
64
+ def test_create_class_scoping_from_method_scoping
65
+ method_scoping = MethodScoping.new(:name => "Maiha")
66
+ class_scoping = ClassScoping.new(Member, method_scoping.attributes)
67
+
68
+ expected = {
69
+ :find => { :conditions => ['( members.name = ? )', "Maiha"] },
70
+ :create => { 'name' => "Maiha" },
71
+ }
72
+ assert_equal expected, class_scoping.method_scoping
73
+ end
74
+
75
+ def test_create_class_scoping_with_attributed_and_conditioned_values
76
+ class_scoping = ClassScoping.new(Member, :name => "Maiha")
77
+ class_scoping.add("grade IN ?", [1,2])
78
+ class_scoping[:group] = 'Berryz'
79
+
80
+ expected = {
81
+ :find => { :conditions => ["( members.grade IN ? ) AND ( members.group = ? ) AND ( members.name = ? )",
82
+ [1,2], "Berryz", "Maiha"] },
83
+ :create => { 'name' => "Maiha", 'group' => "Berryz" },
84
+ }
85
+ assert_equal expected, class_scoping.method_scoping
86
+ end
87
+ end
data/test/schema.rb ADDED
@@ -0,0 +1,20 @@
1
+ ActiveRecord::Schema.define(:version => 2) do
2
+
3
+ create_table :groups, :force => true do |t|
4
+ t.column :name, :string
5
+ t.column :deleted, :boolean, :default=>0
6
+ end
7
+
8
+ create_table :members, :force => true do |t|
9
+ t.column :name, :string
10
+ t.column :grade, :integer
11
+ t.column :deleted, :boolean, :default=>0
12
+ t.column :group_id,:integer
13
+ end
14
+
15
+ create_table :favorites, :force => true do |t|
16
+ t.column :name, :string
17
+ t.column :member_id,:integer
18
+ end
19
+
20
+ end
@@ -0,0 +1,247 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+ require File.join(File.dirname(__FILE__), '..', 'init')
3
+
4
+ module Scopings
5
+ ActiveMember = MethodScoping.new(:deleted => false)
6
+ ElementarySchool = MethodScoping.new(:grade => 1)
7
+ JuniorHighSchool = MethodScoping.new(:grade => 2)
8
+ end
9
+
10
+ class TestController < ActionController::Base
11
+ attr_reader :members, :member
12
+
13
+ def list
14
+ @members = Member.find(:all, :order=>:id)
15
+ render :text=>''
16
+ end
17
+
18
+ def show
19
+ @member = Member.find(params[:id])
20
+ render :text=>''
21
+ end
22
+ end
23
+
24
+ class ScopedAccessTestCase < Test::Unit::TestCase
25
+ fixtures :members
26
+
27
+ def setup
28
+ Member.instance_eval{scoped_methods.clear}
29
+ @request = ActionController::TestRequest.new
30
+ @response = ActionController::TestResponse.new
31
+ end
32
+
33
+ def test_dummy
34
+ end
35
+
36
+ private
37
+ def test_process(controller, action = "show")
38
+ request = ActionController::TestRequest.new
39
+ request.action = action
40
+ controller.process(request, ActionController::TestResponse.new)
41
+ end
42
+
43
+ end
44
+
45
+
46
+ class WithoutFilterTest < ScopedAccessTestCase
47
+ class WithoutFilterController < TestController
48
+ end
49
+
50
+ def setup
51
+ super
52
+ @controller = WithoutFilterController.new
53
+ end
54
+
55
+ def test_filter_list
56
+ get :list
57
+ assert_response :success
58
+ assert_equal %w( Saki Maiha Yurina Risako Airi ), @controller.members.map{|o| o.name}
59
+ end
60
+
61
+ def test_filter_show
62
+ get :show, {'id' => "2"}
63
+ assert_response :success
64
+ assert_equal members(:maiha), @controller.member
65
+ end
66
+ end
67
+
68
+ class ActiveMemberTest < ScopedAccessTestCase
69
+ class ActiveMemberController < TestController
70
+ around_filter ScopedAccess::Filter.new(Member, Scopings::ActiveMember)
71
+
72
+ def nested_scoping_with_elementary_school
73
+ Member.with_scope(Scopings::ElementarySchool) do
74
+ @members = Member.find(:all)
75
+ end
76
+ render :text=>''
77
+ end
78
+ end
79
+
80
+ def setup
81
+ super
82
+ @controller = ActiveMemberController.new
83
+ end
84
+
85
+ def test_filter_list
86
+ get :list
87
+ assert_response :success
88
+ expected = %w( saki yurina risako airi ).collect{|name| members(name)}
89
+ assert_equal expected, @controller.members
90
+ end
91
+
92
+ def test_filter_show
93
+ assert_raises(ActiveRecord::RecordNotFound) {
94
+ get :show, {'id' => "2"}
95
+ }
96
+ end
97
+
98
+ def test_filter_nested_scoping
99
+ get :nested_scoping_with_elementary_school
100
+ assert_response :success
101
+ assert_equal [members(:yurina), members(:risako)], @controller.members
102
+ end
103
+ end
104
+
105
+
106
+ class DoubleAroundWithMergedScopedFilterTest < ScopedAccessTestCase
107
+ class DoubleAroundWithMergedScopedFilterController < TestController
108
+ scoped_access Member, Scopings::ActiveMember
109
+ scoped_access Member, Scopings::JuniorHighSchool
110
+
111
+ def list_with_exclusive_scope
112
+ Member.with_exclusive_scope(Scopings::ElementarySchool) do
113
+ @members = Member.find(:all)
114
+ end
115
+ render :text=>''
116
+ end
117
+ end
118
+
119
+ def setup
120
+ super
121
+ @controller = DoubleAroundWithMergedScopedFilterController.new
122
+ end
123
+
124
+ def test_filter_list
125
+ get :list
126
+ assert_response :success
127
+ assert_equal [members(:saki), members(:airi)], @controller.members
128
+ end
129
+
130
+ def test_filter_show_active_member
131
+ get :show, {'id' => "1"}
132
+ assert_response :success
133
+ assert_equal members(:saki), @controller.member
134
+ end
135
+
136
+ def test_filter_show_deleted_member
137
+ assert_raises(ActiveRecord::RecordNotFound) {
138
+ get :show, {'id' => "2"}
139
+ }
140
+ end
141
+
142
+ def test_list_with_exclusive_scope
143
+ get :list_with_exclusive_scope
144
+ assert_response :success
145
+ assert_equal [members(:yurina), members(:risako)], @controller.members
146
+ end
147
+ end
148
+
149
+
150
+ class InheritedDoubleAroundFilterTest < ScopedAccessTestCase
151
+ class ParentAroundFilterController < TestController
152
+ scoped_access Member, Scopings::ActiveMember
153
+ end
154
+
155
+ class InheritedDoubleAroundFilterController < ParentAroundFilterController
156
+ scoped_access Member, Scopings::JuniorHighSchool
157
+
158
+ def list_with_conflicted_condition
159
+ Member.with_scope(Scopings::ElementarySchool) do
160
+ @members = Member.find(:all)
161
+ end
162
+ render :text=>''
163
+ end
164
+ end
165
+
166
+ def setup
167
+ super
168
+ @controller = InheritedDoubleAroundFilterController.new
169
+ end
170
+
171
+ def test_filter_list
172
+ get :list
173
+ assert_response :success
174
+ assert_equal [members(:saki), members(:airi)], @controller.members
175
+ end
176
+
177
+ def test_filter_show
178
+ assert_raises(ActiveRecord::RecordNotFound) {
179
+ get :show, {'id' => "2"}
180
+ }
181
+ end
182
+
183
+ def test_filter_list_with_conflicted_condition
184
+ get :list_with_conflicted_condition
185
+ assert_response :success
186
+ assert_equal [], @controller.members
187
+ end
188
+ end
189
+
190
+
191
+ class ConditionalAroundFilterTest < ScopedAccessTestCase
192
+ class ConditionalParentAroundFilterController < TestController
193
+ scoped_access Member, Scopings::ActiveMember, :except=>:all
194
+ scoped_access Member, Scopings::JuniorHighSchool
195
+ end
196
+
197
+ class InheritedDoubleConditionalAroundFilterController < ConditionalParentAroundFilterController
198
+ scoped_access Member, Scopings::ElementarySchool, :only=>:list_with_conflicted_condition
199
+
200
+ def list_with_conflicted_condition
201
+ @members = Member.find(:all)
202
+ render :text=>''
203
+ end
204
+
205
+ def list
206
+ @members = Member.find(:all)
207
+ render :text=>''
208
+ end
209
+
210
+ def all
211
+ @members = Member.find(:all)
212
+ render :text=>''
213
+ end
214
+ end
215
+
216
+ def setup
217
+ super
218
+ @controller = InheritedDoubleConditionalAroundFilterController.new
219
+ end
220
+
221
+ def test_filter_list_out_of_only_condition
222
+ expected = Member.with_exclusive_scope(Scopings::ActiveMember+Scopings::JuniorHighSchool){Member.find(:all)}
223
+ get :list
224
+ assert_response :success
225
+ assert_equal expected, @controller.members
226
+ end
227
+
228
+ def test_filter_show
229
+ assert_raises(ActiveRecord::RecordNotFound) {
230
+ get :show, {'id' => "2"}
231
+ }
232
+ end
233
+
234
+ def test_filter_list_with_conflicted_condition
235
+ get :list_with_conflicted_condition
236
+ assert_response :success
237
+ assert_equal [], @controller.members
238
+ end
239
+
240
+ def test_filter_except_condition
241
+ expected = Member.with_exclusive_scope(Scopings::JuniorHighSchool){Member.find(:all)}
242
+ get :all
243
+ assert_response :success
244
+ assert_equal expected, @controller.members
245
+ end
246
+ end
247
+
@@ -0,0 +1,78 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class SqlConditionTest < Test::Unit::TestCase
4
+ def setup
5
+ attributes = { :name => 'Maiha' }
6
+ @condition = SqlCondition.new(attributes)
7
+ end
8
+
9
+ def test_immutable_attributes
10
+ assert_equal "Maiha", @condition.attributes[:name]
11
+ attributes = { :name => 'Saki' }
12
+ assert_equal "Maiha", @condition.attributes[:name]
13
+ end
14
+
15
+ def test_add_attribute
16
+ @condition[:group] = 'Berryz'
17
+ assert_equal({'name'=>"Maiha", 'group'=>'Berryz'}, @condition.attributes)
18
+ end
19
+
20
+ def test_overwrite_attribute
21
+ @condition[:name] = 'Saki'
22
+ assert_equal({'name'=>"Saki"}, @condition.attributes)
23
+ end
24
+
25
+ def test_generate_with_one_attribute
26
+ assert_equal(["( name = ? )", "Maiha"], @condition.generate)
27
+ end
28
+
29
+ def test_generate_with_one_condition
30
+ @condition = SqlCondition.new
31
+ @condition.add("name = 'Maiha'")
32
+ assert_equal ["( name = 'Maiha' )"], @condition.generate
33
+ end
34
+
35
+ def test_generate_with_one_attribute_and_one_condition
36
+ @condition.add("group = 'Berryz'")
37
+ assert_equal "( group = 'Berryz' ) AND ( name = 'Maiha' )", sanitize_sql(@condition.generate)
38
+ end
39
+
40
+ def test_generate_same_sqls_in_attributed_and_conditioned_ways
41
+ attributed = @condition
42
+ conditioned = SqlCondition.new
43
+
44
+ conditioned.add("name = 'Maiha'")
45
+ assert_equal sanitize_sql(attributed.generate), sanitize_sql(conditioned.generate)
46
+ end
47
+
48
+ def test_generate_with_one_and_two_place_folders
49
+ @condition.add("group = ?", 'Berryz')
50
+ @condition.add("age BETWEEN ? AND ?", 12, 14)
51
+ expected_sql = "( group = 'Berryz' ) AND ( age BETWEEN 12 AND 14 ) AND ( name = 'Maiha' )"
52
+ assert_equal expected_sql, sanitize_sql(@condition.generate)
53
+ end
54
+
55
+ def test_generate_with_complex_constrains
56
+ @condition.add("group = ?", 'Berryz')
57
+ @condition.add("state IN (?)", [1,2])
58
+ @condition[:graduate] = 1
59
+ expected_sql = "( group = 'Berryz' ) AND ( state IN (1,2) ) AND ( graduate = 1 ) AND ( name = 'Maiha' )"
60
+ assert_equal expected_sql, sanitize_sql(@condition.generate)
61
+ end
62
+
63
+ def test_generate_from_sql_condition
64
+ @condition.add("group = ?", 'Berryz')
65
+ cond = SqlCondition.new(@condition)
66
+ assert_equal({"name"=>'Maiha'}, cond.attributes)
67
+ assert_equal([SqlCondition::Constrain.new("group = ?", ['Berryz'])], cond.constrains)
68
+ end
69
+
70
+ private
71
+ def sanitize_sql (sql)
72
+ ActiveRecord::Base.instance_eval do
73
+ sanitize_sql(sql)
74
+ end
75
+ end
76
+
77
+ end
78
+
@@ -0,0 +1,39 @@
1
+ def __DIR__; File.dirname(__FILE__); end
2
+
3
+ $:.unshift(__DIR__ + '/../lib')
4
+ # begin
5
+ # require 'rubygems'
6
+ # rescue LoadError
7
+ $:.unshift(__DIR__ + '/../../../rails/activerecord/lib')
8
+ $:.unshift(__DIR__ + '/../../../rails/activesupport/lib')
9
+ $:.unshift(__DIR__ + '/../../../rails/actionpack/lib')
10
+ #end
11
+ require 'test/unit'
12
+ require 'active_support'
13
+ require 'active_record'
14
+ require 'active_record/fixtures'
15
+
16
+ config = YAML::load_file(__DIR__ + '/database.yml')
17
+ ActiveRecord::Base.logger = Logger.new(__DIR__ + "/debug.log")
18
+ ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
19
+
20
+
21
+ # create tables
22
+ load(__DIR__ + "/schema.rb")
23
+
24
+ # insert sample data to the tables from 'fixtures/*.yml'
25
+ Test::Unit::TestCase.fixture_path = __DIR__ + "/fixtures/"
26
+ $LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
27
+ Test::Unit::TestCase.use_instantiated_fixtures = false
28
+
29
+ # for controller test
30
+ require 'action_pack'
31
+ require 'action_controller'
32
+ require 'action_controller/test_process'
33
+
34
+ ActionController::Base.ignore_missing_templates = true
35
+ ActionController::Routing::Routes.reload rescue nil
36
+ class ActionController::Base; def rescue_action(e) raise e end; end
37
+
38
+ include ScopedAccess
39
+
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maiha-scoped_access
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - maiha
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-03 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: ""
17
+ email: maiha@wota.jp
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README
26
+ - Rakefile
27
+ - init.rb
28
+ - install.rb
29
+ - lib/scoped_access.rb
30
+ - tasks/scoped_access_tasks.rake
31
+ - test/database.yml
32
+ - test/filter_test.rb
33
+ - test/fixtures/favorite.rb
34
+ - test/fixtures/favorites.yml
35
+ - test/fixtures/group.rb
36
+ - test/fixtures/groups.yml
37
+ - test/fixtures/member.rb
38
+ - test/fixtures/members.yml
39
+ - test/fixtures/song.rb
40
+ - test/method_scoping_test.rb
41
+ - test/schema.rb
42
+ - test/scoped_access_test.rb
43
+ - test/sql_condition_test.rb
44
+ - test/test_helper.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/maiha/scoped_access
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.2.0
68
+ signing_key:
69
+ specification_version: 2
70
+ summary: ""
71
+ test_files: []
72
+