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