moro-scope_do 0.1.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/ChangeLog +8 -0
- data/README.rdoc +103 -0
- data/Rakefile +112 -0
- data/lib/scope_do.rb +14 -0
- data/lib/scope_do/chained_scope.rb +35 -0
- data/lib/scope_do/has_children.rb +56 -0
- data/lib/scope_do/named_acl.rb +61 -0
- data/lib/scope_do/named_acl/builder.rb +49 -0
- data/rails/init.rb +5 -0
- data/spec/record_extention_test_util.rb +47 -0
- data/spec/scope_do/chained_scope_spec.rb +41 -0
- data/spec/scope_do/has_children_spec.rb +83 -0
- data/spec/scope_do/named_acl/builder_spec.rb +25 -0
- data/spec/scope_do/named_acl_spec.rb +54 -0
- data/spec/setup_test_model.rb +89 -0
- data/spec/spec_helper.rb +39 -0
- metadata +85 -0
data/ChangeLog
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
= scope_do
|
3
|
+
|
4
|
+
named_scope utilities.
|
5
|
+
|
6
|
+
== Description
|
7
|
+
|
8
|
+
scope_do is a tiny utilities to define useful named_scope.
|
9
|
+
scope_do provides
|
10
|
+
|
11
|
+
- named_acl : enable User - Group - Target style access control list.
|
12
|
+
- chained_scope : enable defined named_scope method chain.
|
13
|
+
- has_children : define scope to load child records
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
=== Archive Installation
|
18
|
+
|
19
|
+
rake install
|
20
|
+
|
21
|
+
=== Gem Installation
|
22
|
+
|
23
|
+
gem install scope_do
|
24
|
+
|
25
|
+
|
26
|
+
== Features/Problems
|
27
|
+
=== named_acl
|
28
|
+
named_acl provides Target.accessible_by(users) scope that load target model accessible by the user.
|
29
|
+
And also provides User#accessible_#{target} methods which call Target.accessible_by(self).
|
30
|
+
|
31
|
+
class User < ActiveRecord::Base
|
32
|
+
scope_do :named_acl
|
33
|
+
named_acl :blogs
|
34
|
+
end
|
35
|
+
|
36
|
+
now you write below in controller.
|
37
|
+
|
38
|
+
class BlogsController < ApplicationCotnroller
|
39
|
+
def index
|
40
|
+
@blogs = current_user.accessible_blogs
|
41
|
+
# or method chain
|
42
|
+
# @blogs = current_user.accessible_blogs.find_by_keyword(params[:keyword])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
=== chained_scope
|
47
|
+
chained_scope provides named_scope macro that skip condition when all args are blank.
|
48
|
+
and it enable named_scope method-chainable.
|
49
|
+
|
50
|
+
class Blog < ActiveRecord::Base
|
51
|
+
scope_do :chained_scope
|
52
|
+
named_scope :title_like, proc{|part|
|
53
|
+
{:conditions => ["#{quoted_table_name}.title LIKE ?", "%#{part}%"]}
|
54
|
+
}
|
55
|
+
...and more...
|
56
|
+
|
57
|
+
chainable_scope :title_like
|
58
|
+
end
|
59
|
+
|
60
|
+
without chained_scope, you must write below (or reject blank in each scope)
|
61
|
+
|
62
|
+
def index # In some controller
|
63
|
+
@blogs = Blog.some_scope
|
64
|
+
@blogs = @blogs.title_like(params[:q]) unless params[:q].blank?
|
65
|
+
@blogs = @blogs.month_in(params[:month]) unless params[:month].blank?
|
66
|
+
...
|
67
|
+
@blogs = scope.find(:all, ...)
|
68
|
+
end
|
69
|
+
|
70
|
+
chained_scope enable below style
|
71
|
+
|
72
|
+
def index # In some controller
|
73
|
+
@blogs = Blog.some_scope.
|
74
|
+
title_like(params[:q]).
|
75
|
+
month_in(params[:month]).
|
76
|
+
find(:all, ...)
|
77
|
+
end
|
78
|
+
|
79
|
+
=== has_children
|
80
|
+
define scope that have children records.
|
81
|
+
|
82
|
+
class Blog < ActiveRecord::Base
|
83
|
+
scope_do :has_children
|
84
|
+
has_many :entries
|
85
|
+
has_children :entries
|
86
|
+
end
|
87
|
+
|
88
|
+
add "has_entries" scope that have one or more entries.
|
89
|
+
|
90
|
+
Blog.has_entries.all?{|b| not b.entries.blank? } # => true
|
91
|
+
|
92
|
+
"has_entires" also accept children numbers
|
93
|
+
|
94
|
+
Blog.has_entries(2).all?{|b| b.entries.size >= 2 } # => true
|
95
|
+
|
96
|
+
== Synopsis
|
97
|
+
see spec/**/*_spec.rb
|
98
|
+
|
99
|
+
== Copyright
|
100
|
+
|
101
|
+
Author:: MOROHASHI Kyosuke <moronatural@>gmail.com
|
102
|
+
Copyright:: Copyright (c) 2009 MOROHASHI Kyosuke
|
103
|
+
License:: MIT
|
data/Rakefile
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'rake/contrib/sshpublisher'
|
10
|
+
require 'fileutils'
|
11
|
+
|
12
|
+
$:.unshift "lib"
|
13
|
+
require 'scope_do'
|
14
|
+
|
15
|
+
require 'spec/rake/spectask'
|
16
|
+
include FileUtils
|
17
|
+
|
18
|
+
NAME = "scope_do"
|
19
|
+
AUTHOR = "MOROHASHI Kyosuke"
|
20
|
+
EMAIL = "moronatural@gmail.com"
|
21
|
+
DESCRIPTION = "named_scope utilities."
|
22
|
+
HOMEPATH = "http://github.com/moro/scope_do/tree/master"
|
23
|
+
BIN_FILES = %w( )
|
24
|
+
|
25
|
+
VERS = ScopeDo::VERSION
|
26
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
27
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
28
|
+
RDOC_OPTS = [
|
29
|
+
'--title', "#{NAME} documentation",
|
30
|
+
"--charset", "utf-8",
|
31
|
+
"--opname", "index.html",
|
32
|
+
"--line-numbers",
|
33
|
+
"--main", "README.rdoc",
|
34
|
+
"--inline-source",
|
35
|
+
]
|
36
|
+
|
37
|
+
task :default => [:test]
|
38
|
+
task :package => [:clean]
|
39
|
+
|
40
|
+
desc "Run all specs in spec directory"
|
41
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
42
|
+
t.spec_opts = %w[--colour --format progress --loadby --reverse]
|
43
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
44
|
+
end
|
45
|
+
|
46
|
+
spec = Gem::Specification.new do |s|
|
47
|
+
s.name = NAME
|
48
|
+
s.version = VERS
|
49
|
+
s.platform = Gem::Platform::RUBY
|
50
|
+
s.has_rdoc = true
|
51
|
+
s.extra_rdoc_files = ["README.rdoc", "ChangeLog"]
|
52
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
53
|
+
s.summary = DESCRIPTION
|
54
|
+
s.description = DESCRIPTION
|
55
|
+
s.author = AUTHOR
|
56
|
+
s.email = EMAIL
|
57
|
+
s.homepage = HOMEPATH
|
58
|
+
s.executables = BIN_FILES
|
59
|
+
s.bindir = "bin" unless BIN_FILES.empty?
|
60
|
+
s.require_path = "lib"
|
61
|
+
s.test_files = Dir["spec/**/*_spec.rb"]
|
62
|
+
|
63
|
+
s.files = %w(README.rdoc ChangeLog Rakefile) +
|
64
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
65
|
+
Dir.glob("spec/**/*.rb") +
|
66
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
67
|
+
Dir.glob("examples/**/*.rb") +
|
68
|
+
Dir.glob("tools/*.rb") +
|
69
|
+
Dir.glob("rails/*.rb")
|
70
|
+
|
71
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
72
|
+
end
|
73
|
+
|
74
|
+
Rake::GemPackageTask.new(spec) do |p|
|
75
|
+
p.need_tar = true
|
76
|
+
p.gem_spec = spec
|
77
|
+
end
|
78
|
+
|
79
|
+
task :install do
|
80
|
+
name = "#{NAME}-#{VERS}.gem"
|
81
|
+
sh %{rake package}
|
82
|
+
sh %{sudo gem install pkg/#{name}}
|
83
|
+
end
|
84
|
+
|
85
|
+
task :uninstall => [:clean] do
|
86
|
+
sh %{sudo gem uninstall #{NAME}}
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
Rake::RDocTask.new do |rdoc|
|
91
|
+
rdoc.rdoc_dir = 'html'
|
92
|
+
rdoc.options += RDOC_OPTS
|
93
|
+
rdoc.template = "resh"
|
94
|
+
#rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
95
|
+
if ENV['DOC_FILES']
|
96
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
97
|
+
else
|
98
|
+
rdoc.rdoc_files.include('README.rdoc', 'ChangeLog')
|
99
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
100
|
+
rdoc.rdoc_files.include('ext/**/*.c')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
desc 'Show information about the gem.'
|
105
|
+
task :debug_gem do
|
106
|
+
puts spec.to_ruby
|
107
|
+
end
|
108
|
+
|
109
|
+
desc 'Update gem spec'
|
110
|
+
task :gemspec do
|
111
|
+
open("#{NAME}.gemspec", 'w').write spec.to_ruby
|
112
|
+
end
|
data/lib/scope_do.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'scope_do/named_acl'
|
2
|
+
require 'scope_do/chained_scope'
|
3
|
+
|
4
|
+
module ScopeDo
|
5
|
+
VERSION = '0.1.1'
|
6
|
+
module Adapter
|
7
|
+
def scope_do *functions
|
8
|
+
functions.each do |function|
|
9
|
+
module_name = function.to_s.camelize
|
10
|
+
include ScopeDo.const_get(module_name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ScopeDo
|
2
|
+
module ChainedScope
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
# Make specified named_scope(s) chainable
|
10
|
+
#
|
11
|
+
# options
|
12
|
+
#
|
13
|
+
# +halt+:: add always false condition (WHERE 1 = 2)
|
14
|
+
def chainable_scope(*scope_names)
|
15
|
+
opts = scope_names.extract_options!
|
16
|
+
opts.symbolize_keys!
|
17
|
+
|
18
|
+
scope_names.each do |scope_name|
|
19
|
+
if defined_scope = scopes[scope_name]
|
20
|
+
scopes[scope_name] = proc{|parent_scope, *args|
|
21
|
+
args.flatten.any?{|x| !x.blank? } ?
|
22
|
+
defined_scope.call(parent_scope, *args) : parent_scope.scoped(chain_condition(opts))
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def chain_condition(options) # nodoc
|
30
|
+
options[:halt] ? {:conditions => "1 = 2"} : {}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# vim:set fileencoding=utf-8 filetype=ruby
|
2
|
+
|
3
|
+
module ScopeDo
|
4
|
+
module HasChildren
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class QueryBuilder
|
11
|
+
def self.has_children_condition(n, q)
|
12
|
+
select_cols, rest = q
|
13
|
+
if n
|
14
|
+
"#{n} <= (SELECT COUNT(#{select_cols}) #{rest})"
|
15
|
+
else
|
16
|
+
"EXISTS(SELECT #{select_cols} #{rest})"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(base, association)
|
21
|
+
@base = base
|
22
|
+
@association = association
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_children_query_parts
|
26
|
+
if through = @association.through_reflection
|
27
|
+
query_parts(@base, through)
|
28
|
+
else
|
29
|
+
query_parts(@base, @association)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def query_parts(parent, association)
|
35
|
+
child_t = association.quoted_table_name
|
36
|
+
parent_t = parent.quoted_table_name
|
37
|
+
[
|
38
|
+
"#{child_t}.#{association.primary_key_name}",
|
39
|
+
"FROM #{child_t} WHERE #{parent_t}.#{parent.primary_key} = #{child_t}.#{association.primary_key_name}"
|
40
|
+
]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
def has_children(target)
|
46
|
+
association = reflections[target]
|
47
|
+
builder = QueryBuilder.new(self, association)
|
48
|
+
qs = builder.has_children_query_parts
|
49
|
+
|
50
|
+
named_scope "has_#{target}", proc{|*n|
|
51
|
+
{:conditions => QueryBuilder.has_children_condition(n.shift, qs)}
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'scope_do/named_acl/builder'
|
2
|
+
|
3
|
+
module ScopeDo
|
4
|
+
module NamedAcl
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
base.cattr_accessor :acl_query_builder
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def named_acl(target, options={})
|
12
|
+
t_klass = target.to_s.classify.constantize
|
13
|
+
self.acl_query_builder = Builder.new(self, target, options)
|
14
|
+
|
15
|
+
define_accessible_scope(t_klass, acl_query_builder.query)
|
16
|
+
class_eval(<<-EOS)
|
17
|
+
def accessible_#{target}; #{t_klass.name}.accessed_by(self) ; end
|
18
|
+
EOS
|
19
|
+
|
20
|
+
if free = t_klass.scopes[:free]
|
21
|
+
define_free_or_accessible_scope(t_klass, free.call(t_klass), acl_query_builder.query)
|
22
|
+
class_eval(<<-EOS)
|
23
|
+
def free_or_accessible_#{target}; #{t_klass.name}.free_or_accessed_by(self); end
|
24
|
+
EOS
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def define_accessible_scope(klass, query)
|
30
|
+
klass.named_scope :accessed_by, proc{|*users|
|
31
|
+
user_ids = users.map(&:id)
|
32
|
+
{:conditions => ["#{klass.quoted_table_name}.id IN (#{query})", {:user_ids => user_ids}]}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_free_or_accessible_scope(klass, free_scope, query)
|
37
|
+
free = merge_conditions( free_scope.proxy_options[:conditions] )
|
38
|
+
|
39
|
+
klass.named_scope :free_or_accessed_by, proc{|*users|
|
40
|
+
accessible = merge_conditions( klass.accessed_by(*users).proxy_options[:conditions] )
|
41
|
+
{:conditions => "(#{free}) OR (#{accessible})"}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def accessible?(record)
|
47
|
+
@accessibilities ||= load_accessibilities
|
48
|
+
|
49
|
+
fk = acl_query_builder.accessibility_target_id(false)
|
50
|
+
@accessibilities.any?{|a| a[fk] == record.id }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def load_accessibilities
|
55
|
+
q = acl_query_builder
|
56
|
+
q.accessibility_klass.find(:all, :joins => "JOIN #{q.join_on_group_id}",
|
57
|
+
:conditions => ["#{q.where}", {q.label.to_sym=> self}])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ScopeDo
|
2
|
+
module NamedAcl
|
3
|
+
class Builder
|
4
|
+
attr_reader :label
|
5
|
+
|
6
|
+
def initialize(user_klass, target, options={})
|
7
|
+
options = {:via => :groups, :label => "user_ids"}.merge(options)
|
8
|
+
@label = options[:label]
|
9
|
+
|
10
|
+
@user = user_klass
|
11
|
+
@group = @user.reflections[options[:via]]
|
12
|
+
@membership = @user.reflections[options[:via]].through_reflection
|
13
|
+
|
14
|
+
@target = @group.klass.reflections[target]
|
15
|
+
@accessibility = @target.through_reflection
|
16
|
+
end
|
17
|
+
|
18
|
+
def accessibility_klass
|
19
|
+
@accessibility.klass
|
20
|
+
end
|
21
|
+
|
22
|
+
def query
|
23
|
+
"SELECT #{accessibility_target_id} FROM #{from} JOIN #{join_on_group_id} WHERE #{where}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def accessibility_target_id(with_table_name = true)
|
27
|
+
if with_table_name
|
28
|
+
"#{@accessibility.quoted_table_name}.#{@target.association_foreign_key}"
|
29
|
+
else
|
30
|
+
@target.association_foreign_key
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def where
|
35
|
+
"#{@membership.quoted_table_name}.#{@membership.primary_key_name} IN (:#{@label})"
|
36
|
+
end
|
37
|
+
|
38
|
+
def from
|
39
|
+
@accessibility.quoted_table_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def join_on_group_id
|
43
|
+
"#{@membership.quoted_table_name} ON " +
|
44
|
+
"#{@membership.quoted_table_name}.#{@group.association_foreign_key} = " +
|
45
|
+
"#{@accessibility.quoted_table_name}.#{@accessibility.primary_key_name}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_record/fixtures'
|
4
|
+
|
5
|
+
RAILS_ENV = "test"
|
6
|
+
|
7
|
+
module RecrodExtention
|
8
|
+
module TestUtil
|
9
|
+
MEMORY_DB_OPTIONS = {
|
10
|
+
:adapter => "sqlite3",
|
11
|
+
:database => ":memory:",
|
12
|
+
}.with_indifferent_access.freeze
|
13
|
+
|
14
|
+
def setup
|
15
|
+
memory_db!
|
16
|
+
log_or_silent!
|
17
|
+
ActiveRecord::Base.extend(ClassMethods)
|
18
|
+
end
|
19
|
+
|
20
|
+
def memory_db!(options = {})
|
21
|
+
ActiveRecord::Base.configurations = {"test" => MEMORY_DB_OPTIONS.merge(options) }
|
22
|
+
ActiveRecord::Base.establish_connection(:test)
|
23
|
+
end
|
24
|
+
|
25
|
+
def log_or_silent!(dir = "log")
|
26
|
+
ActiveRecord::Base.logger = Logger.new(dir && File.directory?(dir) ? "#{dir}/#{RAILS_ENV}.log" : "/dev/null")
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_schema_version
|
30
|
+
begin
|
31
|
+
ActiveRecord::Base.connection.select_one("SELECT version FROM schema_migrations")["version"].succ
|
32
|
+
rescue ActiveRecord::StatementInvalid
|
33
|
+
"1"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
module_function :setup, :memory_db!, :log_or_silent!, :next_schema_version
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
def define_table(table_name = self.name.tableize, &migration)
|
40
|
+
ActiveRecord::Schema.define(:version => TestUtil.next_schema_version) do
|
41
|
+
create_table(table_name, &migration)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:set fileencoding=utf-8 filetype=ruby
|
3
|
+
# $KCODE = 'u'
|
4
|
+
|
5
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
6
|
+
require 'scope_do/chained_scope'
|
7
|
+
|
8
|
+
describe ScopeDo::ChainedScope do
|
9
|
+
before(:all) do
|
10
|
+
::User.class_eval do
|
11
|
+
scope_do :chained_scope
|
12
|
+
named_scope :name_like, proc{|part|
|
13
|
+
{:conditions => ["#{quoted_table_name}.name LIKE ?", "%#{part}%"]}
|
14
|
+
}
|
15
|
+
|
16
|
+
named_scope :name_starts, proc{|part|
|
17
|
+
{:conditions => ["#{quoted_table_name}.name LIKE ?", "#{part}%"]}
|
18
|
+
}
|
19
|
+
|
20
|
+
chainable_scope :name_like
|
21
|
+
chainable_scope :name_starts, :halt => true
|
22
|
+
end
|
23
|
+
|
24
|
+
User.delete_all
|
25
|
+
u = Factory(:user)
|
26
|
+
Factory(:user, :name => "bob")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "name_like('ali') should == [User.find_by_name('alice')]" do
|
30
|
+
User.name_like('ali').should == [User.find_by_name('alice')]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "blank paramters (nil, [], '') on name_like should == User.all" do
|
34
|
+
[nil, [], ''].all?{|param| User.name_like(param) == User.all }.should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "blank paramters (nil, [], '') on name_starts should == []" do
|
38
|
+
[nil, [], ''].all?{|param| User.name_starts(param) == [] }.should be_true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:set fileencoding=utf-8 filetype=ruby
|
3
|
+
# $KCODE = 'u'
|
4
|
+
|
5
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
6
|
+
require 'scope_do/has_children'
|
7
|
+
|
8
|
+
describe ScopeDo::HasChildren do
|
9
|
+
before(:all) do
|
10
|
+
::Blog.class_eval do
|
11
|
+
scope_do :has_children
|
12
|
+
has_children :entries
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
[Blog, Entry, Group, User].each(&:delete_all)
|
18
|
+
@blog = Factory(:blog)
|
19
|
+
@blog.entries << (@entry = Factory(:entry))
|
20
|
+
|
21
|
+
@empty = Factory(:blog)
|
22
|
+
end
|
23
|
+
|
24
|
+
it do
|
25
|
+
Blog.first.should have(1).entries
|
26
|
+
end
|
27
|
+
|
28
|
+
it do
|
29
|
+
Blog.all.should == [@blog, @empty]
|
30
|
+
end
|
31
|
+
|
32
|
+
it ".has_entries" do
|
33
|
+
Blog.has_entries.should == [@blog]
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "specify children numbers" do
|
37
|
+
before do
|
38
|
+
@three_children = Factory(:blog)
|
39
|
+
@three_children.entries = (0..2).map{ Factory(:entry) }
|
40
|
+
end
|
41
|
+
|
42
|
+
it ".has_entries(3)" do
|
43
|
+
Blog.has_entries(3).should == [@three_children]
|
44
|
+
end
|
45
|
+
|
46
|
+
it ".has_entries(4) should be_empty" do
|
47
|
+
Blog.has_entries(4).should be_empty
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "Group / has_many :through children" do
|
52
|
+
before(:all) do
|
53
|
+
Group.class_eval do
|
54
|
+
scope_do :has_children
|
55
|
+
has_children :users
|
56
|
+
end
|
57
|
+
end
|
58
|
+
before do
|
59
|
+
@group = Group.first
|
60
|
+
@group.users = [Factory(:user)]
|
61
|
+
@another_group = Factory(:group)
|
62
|
+
end
|
63
|
+
|
64
|
+
it ".has_users" do
|
65
|
+
Group.has_users.should == [@group]
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "specify n" do
|
69
|
+
before do
|
70
|
+
@group.users << Factory(:user, :name =>"bob")
|
71
|
+
end
|
72
|
+
|
73
|
+
it ".has_users(2).should == [@group]" do
|
74
|
+
Group.has_users(2).should == [@group]
|
75
|
+
end
|
76
|
+
|
77
|
+
it ".has_users(5).should be_empty" do
|
78
|
+
Group.has_users(5).should be_empty
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:set fileencoding=utf-8 filetype=ruby
|
3
|
+
# $KCODE = 'u'
|
4
|
+
|
5
|
+
require File.expand_path("../../spec_helper", File.dirname(__FILE__))
|
6
|
+
require 'scope_do/named_acl/builder'
|
7
|
+
|
8
|
+
describe ScopeDo::NamedAcl::Builder do
|
9
|
+
before do
|
10
|
+
@builder = ScopeDo::NamedAcl::Builder.new(User, :blogs, :via => :groups)
|
11
|
+
end
|
12
|
+
|
13
|
+
it do
|
14
|
+
@builder.should_not be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "#from" do
|
18
|
+
@builder.from.should == Accessibility.quoted_table_name
|
19
|
+
end
|
20
|
+
|
21
|
+
it "#join_on_group_id" do
|
22
|
+
@builder.join_on_group_id.should == "#{Membership.quoted_table_name} ON #{Membership.quoted_table_name}.group_id = #{Accessibility.quoted_table_name}.group_id"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:set fileencoding=utf-8 filetype=ruby
|
3
|
+
# $KCODE = 'u'
|
4
|
+
|
5
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
6
|
+
require 'scope_do/named_acl'
|
7
|
+
|
8
|
+
describe ScopeDo::NamedAcl do
|
9
|
+
before(:all) do
|
10
|
+
::Blog.class_eval do
|
11
|
+
named_scope :free, :conditions => ["public = ?", true]
|
12
|
+
end
|
13
|
+
::User.class_eval do
|
14
|
+
scope_do :named_acl
|
15
|
+
named_acl :blogs
|
16
|
+
end
|
17
|
+
|
18
|
+
[Blog, User].each(&:delete_all)
|
19
|
+
u = Factory(:user)
|
20
|
+
b = Factory(:blog)
|
21
|
+
b.accessibilities.map(&:group).first.users << u
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
@alice = User.find_by_name("alice")
|
26
|
+
@bob = Factory(:user, :name => "bob")
|
27
|
+
@blog = Blog.find(:first)
|
28
|
+
end
|
29
|
+
|
30
|
+
it do
|
31
|
+
Blog.should be_respond_to(:accessed_by)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "alice.accessible_blogs should include @blog" do
|
35
|
+
@alice.accessible_blogs.should include(@blog)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "alice should be accessible @blog" do
|
39
|
+
@alice.should be_accessible(@blog)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "bob.accessible_blogs should not include @blog" do
|
43
|
+
@bob.accessible_blogs.should_not include(@blog)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "bob.free_or_accessible_blogs should not include @blog" do
|
47
|
+
@bob.free_or_accessible_blogs.should include(@blog)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "bob should not be accessible @blog" do
|
51
|
+
@bob.should_not be_accessible(@blog)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_record/fixtures'
|
4
|
+
require 'factory_girl'
|
5
|
+
|
6
|
+
require File.expand_path("record_extention_test_util", File.dirname(__FILE__))
|
7
|
+
RecrodExtention::TestUtil.setup
|
8
|
+
|
9
|
+
class User < ActiveRecord::Base
|
10
|
+
define_table do |t|
|
11
|
+
t.string "name"
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
has_many :memberships
|
15
|
+
has_many :groups, :through => :memberships
|
16
|
+
end
|
17
|
+
|
18
|
+
class Membership < ActiveRecord::Base
|
19
|
+
define_table do |t|
|
20
|
+
t.belongs_to :user
|
21
|
+
t.belongs_to :group
|
22
|
+
end
|
23
|
+
belongs_to :user
|
24
|
+
belongs_to :group
|
25
|
+
end
|
26
|
+
|
27
|
+
class Group < ActiveRecord::Base
|
28
|
+
define_table do |t|
|
29
|
+
t.string :name
|
30
|
+
end
|
31
|
+
has_many :memberships
|
32
|
+
has_many :users, :through => :memberships
|
33
|
+
|
34
|
+
has_many :accessibilities
|
35
|
+
has_many :blogs, :through => :accessibilities
|
36
|
+
end
|
37
|
+
|
38
|
+
class Accessibility < ActiveRecord::Base
|
39
|
+
define_table do |t|
|
40
|
+
t.belongs_to :blog
|
41
|
+
t.belongs_to :group
|
42
|
+
end
|
43
|
+
belongs_to :blog
|
44
|
+
belongs_to :group
|
45
|
+
end
|
46
|
+
|
47
|
+
class Blog < ActiveRecord::Base
|
48
|
+
define_table do |t|
|
49
|
+
t.string "title"
|
50
|
+
t.boolean "public"
|
51
|
+
t.timestamps
|
52
|
+
end
|
53
|
+
has_many :accessibilities
|
54
|
+
has_many :entries
|
55
|
+
end
|
56
|
+
|
57
|
+
class Entry < ActiveRecord::Base
|
58
|
+
define_table do |t|
|
59
|
+
t.string "title"
|
60
|
+
t.text "body"
|
61
|
+
t.belongs_to "blog"
|
62
|
+
end
|
63
|
+
|
64
|
+
belongs_to :blog
|
65
|
+
end
|
66
|
+
|
67
|
+
Factory.define(:group) do |g|
|
68
|
+
g.name "collaborators"
|
69
|
+
end
|
70
|
+
|
71
|
+
Factory.define(:accessibility) do |a|
|
72
|
+
a.group{|g| g.association(:group) }
|
73
|
+
end
|
74
|
+
|
75
|
+
Factory.define(:blog) do |b|
|
76
|
+
b.title "My Blog"
|
77
|
+
b.public true
|
78
|
+
b.accessibilities{|as| [Factory.build(:accessibility)] }
|
79
|
+
end
|
80
|
+
|
81
|
+
Factory.define(:user) do |u|
|
82
|
+
u.name "alice"
|
83
|
+
end
|
84
|
+
|
85
|
+
Factory.define(:entry) do |e|
|
86
|
+
e.title "My Entry"
|
87
|
+
e.body "!!! Body of the entry. !!!"
|
88
|
+
end
|
89
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# ---- requirements
|
2
|
+
$KCODE = 'u' #activate regex unicode
|
3
|
+
require 'rubygems'
|
4
|
+
require 'spec'
|
5
|
+
$LOAD_PATH << File.expand_path("..", File.dirname(__FILE__))
|
6
|
+
$LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
|
7
|
+
|
8
|
+
# ---- bugfix
|
9
|
+
#`exit?': undefined method `run?' for Test::Unit:Module (NoMethodError)
|
10
|
+
#can be solved with require test/unit but this will result in extra test-output
|
11
|
+
unless defined? Test::Unit
|
12
|
+
module Test
|
13
|
+
module Unit
|
14
|
+
def self.run?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# ---- load active record
|
22
|
+
#gem 'activerecord', '2.0.2'
|
23
|
+
if ENV["AR"]
|
24
|
+
gem 'activerecord', ENV["AR"]
|
25
|
+
$stderr.puts("Using ActiveRecord #{ENV["AR"]}")
|
26
|
+
end
|
27
|
+
require 'active_record'
|
28
|
+
require "rails/init"
|
29
|
+
|
30
|
+
load File.expand_path("setup_test_model.rb", File.dirname(__FILE__))
|
31
|
+
|
32
|
+
# ---- fixtures
|
33
|
+
Spec::Example::ExampleGroupMethods.module_eval do
|
34
|
+
def fixtures(*tables)
|
35
|
+
dir = File.expand_path("fixtures", File.dirname(__FILE__))
|
36
|
+
tables.each{|table| Fixtures.create_fixtures(dir, table.to_s) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moro-scope_do
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- MOROHASHI Kyosuke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-18 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: named_scope utilities.
|
17
|
+
email: moronatural@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
- ChangeLog
|
25
|
+
files:
|
26
|
+
- README.rdoc
|
27
|
+
- ChangeLog
|
28
|
+
- Rakefile
|
29
|
+
- lib/scope_do
|
30
|
+
- lib/scope_do/chained_scope.rb
|
31
|
+
- lib/scope_do/has_children.rb
|
32
|
+
- lib/scope_do/named_acl
|
33
|
+
- lib/scope_do/named_acl/builder.rb
|
34
|
+
- lib/scope_do/named_acl.rb
|
35
|
+
- lib/scope_do.rb
|
36
|
+
- spec/record_extention_test_util.rb
|
37
|
+
- spec/scope_do/chained_scope_spec.rb
|
38
|
+
- spec/scope_do/has_children_spec.rb
|
39
|
+
- spec/scope_do/named_acl/builder_spec.rb
|
40
|
+
- spec/scope_do/named_acl_spec.rb
|
41
|
+
- spec/setup_test_model.rb
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
- rails/init.rb
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://github.com/moro/scope_do/tree/master
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --title
|
49
|
+
- scope_do documentation
|
50
|
+
- --charset
|
51
|
+
- utf-8
|
52
|
+
- --opname
|
53
|
+
- index.html
|
54
|
+
- --line-numbers
|
55
|
+
- --main
|
56
|
+
- README.rdoc
|
57
|
+
- --inline-source
|
58
|
+
- --exclude
|
59
|
+
- ^(examples|extras)/
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.2.0
|
78
|
+
signing_key:
|
79
|
+
specification_version: 2
|
80
|
+
summary: named_scope utilities.
|
81
|
+
test_files:
|
82
|
+
- spec/scope_do/chained_scope_spec.rb
|
83
|
+
- spec/scope_do/has_children_spec.rb
|
84
|
+
- spec/scope_do/named_acl/builder_spec.rb
|
85
|
+
- spec/scope_do/named_acl_spec.rb
|