maiha-scoped_access 0.1

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