kristopher-scoped-associations 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/Manifest +13 -0
- data/README.rdoc +34 -0
- data/Rakefile +22 -0
- data/TODO +9 -0
- data/init.rb +1 -0
- data/lib/scope_reflection.rb +32 -0
- data/lib/scoped_associations.rb +18 -0
- data/scoped-associations.gemspec +34 -0
- data/spec/models.rb +30 -0
- data/spec/scoped_associations_spec.rb +68 -0
- data/spec/spec_helper.rb +57 -0
- metadata +83 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Kristopher Chambers and Elijah Miller
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
= ScopedAssociations
|
2
|
+
|
3
|
+
Use your named scopes to add conditions to associations! Keep your code DRY
|
4
|
+
and allows eager loading of scoped associations.
|
5
|
+
|
6
|
+
Currently this only works for has_many associations, and probably only on
|
7
|
+
Rails 2.2.
|
8
|
+
|
9
|
+
== Example
|
10
|
+
|
11
|
+
class User < ActiveRecord::Base
|
12
|
+
has_many :published_posts, :scope => :published
|
13
|
+
has_many :published_and_approved_posts, :scope => [:published, :approved]
|
14
|
+
end
|
15
|
+
|
16
|
+
class Post < ActiveRecord::Base
|
17
|
+
named_scope :published, :conditions => { :published => true }
|
18
|
+
named_scope :approved, :conditions => { :approved => true }
|
19
|
+
end
|
20
|
+
|
21
|
+
== Install
|
22
|
+
|
23
|
+
As a Rails plugin:
|
24
|
+
|
25
|
+
./script_plugin install git://github.com/kristopher/scoped-associations.git
|
26
|
+
|
27
|
+
Prefer gems?
|
28
|
+
|
29
|
+
gem sources -a http://gems.github.com
|
30
|
+
gem install kristopher-scoped-associations
|
31
|
+
|
32
|
+
|
33
|
+
Homepage:: http://github.com/kristopher/scoped-associations
|
34
|
+
License:: Copyright (c) 2008 Kristopher Chambers <mailto:kristopher.chambers@gmail.com> and Elijah Miller <mailto:elijah.miller@gmail.com>, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
|
3
|
+
require 'echoe'
|
4
|
+
Echoe.new 'scoped-associations' do |p|
|
5
|
+
p.description = "DRY up your associations with named_scope conditions!"
|
6
|
+
# p.url = "http://scoped-associations.rubyforge.org"
|
7
|
+
p.author = "Kristopher Chambers"
|
8
|
+
p.email = "kristopher.chambers@gmail.com"
|
9
|
+
p.retain_gemspec = true
|
10
|
+
p.need_tar_gz = false
|
11
|
+
p.extra_deps = [
|
12
|
+
]
|
13
|
+
p.ignore_pattern = ['spec/test.sqlite3']
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Default: run specs'
|
17
|
+
task :default => :spec
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_files = FileList["spec/**/*_spec.rb"]
|
20
|
+
end
|
21
|
+
|
22
|
+
task :test => :spec
|
data/TODO
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
A shortcut for generating multiple associations that are scoped:
|
2
|
+
has_many :posts
|
3
|
+
has_many :published_posts, :scope => :published
|
4
|
+
has_many :archived_posts, :scope => :archived
|
5
|
+
|
6
|
+
Could be shortened to:
|
7
|
+
|
8
|
+
has_many :posts, :and_scopes => [:published, :archived]
|
9
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'scoped_associations'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
module ScopeReflection
|
4
|
+
def initialize(owner, reflection)
|
5
|
+
scope_reflection(reflection)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def scope_reflection(reflection)
|
10
|
+
|
11
|
+
source_reflection_class =
|
12
|
+
if reflection.is_a? ActiveRecord::Reflection::ThroughReflection
|
13
|
+
reflection.source_reflection.klass
|
14
|
+
else
|
15
|
+
reflection.klass
|
16
|
+
end
|
17
|
+
|
18
|
+
scope_names = [reflection.options[:scope]].flatten.compact
|
19
|
+
final_scope = scope_names.inject(source_reflection_class) do |accumulated_scope, scope|
|
20
|
+
if accumulated_scope.respond_to?(scope)
|
21
|
+
accumulated_scope.send(scope)
|
22
|
+
else
|
23
|
+
raise ActiveRecord::UndefinedScopeReflectionError.new(self, scope)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
current_scoped_methods = final_scope.send(:current_scoped_methods) || {}
|
28
|
+
reflection.options.merge!(current_scoped_methods[:find] || {})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
class UndefinedScopeReflectionError < ActiveRecordError # :nodoc:
|
3
|
+
def initialize(reflection, scope)
|
4
|
+
super("Undefined Scope '#{scope}' for #{reflection.class_name}.")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Associations # :nodoc:
|
9
|
+
module ClassMethods # :nodoc:
|
10
|
+
@@valid_keys_for_has_many_association << :scope
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require File.join(File.dirname(__FILE__), 'scope_reflection')
|
16
|
+
|
17
|
+
ActiveRecord::Associations::HasManyAssociation.send(:include, ActiveRecord::Associations::ScopeReflection)
|
18
|
+
ActiveRecord::Associations::HasManyThroughAssociation.send(:include, ActiveRecord::Associations::ScopeReflection)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{scoped-associations}
|
5
|
+
s.version = "0.0.3"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Kristopher Chambers"]
|
9
|
+
s.date = %q{2008-12-07}
|
10
|
+
s.description = %q{DRY up your associations with named_scope conditions!}
|
11
|
+
s.email = %q{kristopher.chambers@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/scope_reflection.rb", "lib/scoped_associations.rb", "LICENSE", "README.rdoc", "TODO"]
|
13
|
+
s.files = ["CHANGELOG", "init.rb", "lib/scope_reflection.rb", "lib/scoped_associations.rb", "LICENSE", "Manifest", "Rakefile", "README.rdoc", "scoped-associations.gemspec", "spec/models.rb", "spec/scoped_associations_spec.rb", "spec/spec_helper.rb", "TODO"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Scoped-associations", "--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{scoped-associations}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{DRY up your associations with named_scope conditions!}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_development_dependency(%q<echoe>, [">= 0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
33
|
+
end
|
34
|
+
end
|
data/spec/models.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class GrandChild < ActiveRecord::Base
|
2
|
+
belongs_to :child
|
3
|
+
|
4
|
+
named_scope :alive, :conditions => { :alive => true }
|
5
|
+
end
|
6
|
+
|
7
|
+
class Child < ActiveRecord::Base
|
8
|
+
belongs_to :parent
|
9
|
+
has_many :grand_children
|
10
|
+
|
11
|
+
named_scope :alive, :conditions => { :alive => true }
|
12
|
+
end
|
13
|
+
|
14
|
+
class Parent < ActiveRecord::Base
|
15
|
+
# Standard
|
16
|
+
has_many :children
|
17
|
+
has_many :grand_children, :through => :children
|
18
|
+
|
19
|
+
# Scope on direct assocation
|
20
|
+
has_many :children_direct, :scope => 'alive', :class_name => 'Child'
|
21
|
+
has_many :grand_children_direct, :through => :children_direct, :source => :grand_children
|
22
|
+
|
23
|
+
# Scope on inderect assocation
|
24
|
+
has_many :children_indirect, :class_name => 'Child'
|
25
|
+
has_many :grand_children_indirect, :through => :children_indirect, :scope => 'alive', :source => :grand_children
|
26
|
+
|
27
|
+
# Scope on direct and indirect assocation
|
28
|
+
has_many :children_direct_and_indirect, :scope => 'alive', :class_name => 'Child'
|
29
|
+
has_many :grand_children_direct_and_indirect, :through => :children_direct_and_indirect, :scope => 'alive', :source => :grand_children
|
30
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "ScopedAssociation" do
|
4
|
+
before(:each) do
|
5
|
+
@parent = Parent.first
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "standard assocations" do
|
9
|
+
describe "has_many" do
|
10
|
+
it "should return all children" do
|
11
|
+
@parent.children.collect(&:name).should == ['aa', 'ab']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "has_many :through" do
|
16
|
+
it "should return all grand children" do
|
17
|
+
@parent.grand_children.collect(&:name).should == ['aaa', 'aab', 'aba', 'abb']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
describe "scope on direct assocation" do
|
24
|
+
describe "has_many" do
|
25
|
+
it "should should return all live children" do
|
26
|
+
@parent.children_direct.collect(&:name).should == ['aa']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "has_many :through" do
|
31
|
+
it "should should return all grand children with a live parent" do
|
32
|
+
@parent.grand_children_direct.collect(&:name).should == ['aaa', 'aab']
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
describe "scope on indrect assocation" do
|
39
|
+
describe "has_many" do
|
40
|
+
it "should return all children" do
|
41
|
+
@parent.children_indirect.collect(&:name).should == ['aa', 'ab']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "has_many :through" do
|
46
|
+
it "should should return all live grand children" do
|
47
|
+
@parent.grand_children_indirect.collect(&:name).should == ['aaa', 'aba']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
describe "scope on direct and indrect assocation" do
|
54
|
+
describe "has_many" do
|
55
|
+
it "should return all live children" do
|
56
|
+
@parent.children_direct_and_indirect.collect(&:name).should == ['aa']
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "has_many :through" do
|
61
|
+
it "should return all live grandchildren with a live parent" do
|
62
|
+
@parent.grand_children_direct_and_indirect.collect(&:name).should == ['aaa']
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/test_case'
|
4
|
+
|
5
|
+
require 'active_record'
|
6
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'scoped_associations')
|
7
|
+
|
8
|
+
require File.join(File.dirname(__FILE__), 'models')
|
9
|
+
|
10
|
+
TEST_DATABASE_FILE = File.join(File.dirname(__FILE__), 'test.sqlite3')
|
11
|
+
|
12
|
+
def setup_database
|
13
|
+
File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
|
14
|
+
ActiveRecord::Base.establish_connection(
|
15
|
+
"adapter" => "sqlite3", "timeout" => 5000, "database" => TEST_DATABASE_FILE
|
16
|
+
)
|
17
|
+
create_tables
|
18
|
+
load_fixtures
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_tables
|
22
|
+
c = ActiveRecord::Base.connection
|
23
|
+
|
24
|
+
c.create_table :parents, :force => true do |t|
|
25
|
+
t.string :name
|
26
|
+
t.boolean :alive, :default => false
|
27
|
+
t.timestamps
|
28
|
+
end
|
29
|
+
|
30
|
+
c.create_table :children, :force => true do |t|
|
31
|
+
t.string :name
|
32
|
+
t.references :parent
|
33
|
+
t.boolean :alive, :default => false
|
34
|
+
t.timestamps
|
35
|
+
end
|
36
|
+
|
37
|
+
c.create_table :grand_children, :force => true do |t|
|
38
|
+
t.string :name
|
39
|
+
t.references :child
|
40
|
+
t.boolean :alive, :default => false
|
41
|
+
t.timestamps
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_fixtures
|
46
|
+
a = Parent.create(:name => 'a', :alive => true)
|
47
|
+
|
48
|
+
a_a = a.children.create(:name => 'aa', :alive => true)
|
49
|
+
a_a_a = a_a.grand_children.create(:name => 'aaa', :alive => true)
|
50
|
+
a_a_b = a_a.grand_children.create(:name => 'aab', :alive => false)
|
51
|
+
|
52
|
+
a_b = a.children.create(:name => 'ab', :alive => false)
|
53
|
+
a_b_a = a_b.grand_children.create(:name => 'aba', :alive => true)
|
54
|
+
a_b_b = a_b.grand_children.create(:name => 'abb', :alive => false)
|
55
|
+
end
|
56
|
+
|
57
|
+
setup_database
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kristopher-scoped-associations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kristopher Chambers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-07 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: echoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
description: DRY up your associations with named_scope conditions!
|
25
|
+
email: kristopher.chambers@gmail.com
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- CHANGELOG
|
32
|
+
- lib/scope_reflection.rb
|
33
|
+
- lib/scoped_associations.rb
|
34
|
+
- LICENSE
|
35
|
+
- README.rdoc
|
36
|
+
- TODO
|
37
|
+
files:
|
38
|
+
- CHANGELOG
|
39
|
+
- init.rb
|
40
|
+
- lib/scope_reflection.rb
|
41
|
+
- lib/scoped_associations.rb
|
42
|
+
- LICENSE
|
43
|
+
- Manifest
|
44
|
+
- Rakefile
|
45
|
+
- README.rdoc
|
46
|
+
- scoped-associations.gemspec
|
47
|
+
- spec/models.rb
|
48
|
+
- spec/scoped_associations_spec.rb
|
49
|
+
- spec/spec_helper.rb
|
50
|
+
- TODO
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: ""
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --line-numbers
|
56
|
+
- --inline-source
|
57
|
+
- --title
|
58
|
+
- Scoped-associations
|
59
|
+
- --main
|
60
|
+
- README.rdoc
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "1.2"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project: scoped-associations
|
78
|
+
rubygems_version: 1.2.0
|
79
|
+
signing_key:
|
80
|
+
specification_version: 2
|
81
|
+
summary: DRY up your associations with named_scope conditions!
|
82
|
+
test_files: []
|
83
|
+
|