Skip to content

Commit e942026

Browse files
author
Jon Evans
committed
initial import
0 parents  commit e942026

File tree

6 files changed

+254
-0
lines changed

6 files changed

+254
-0
lines changed

MIT-LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2008 Jon Evans <jon@springyweb.com>
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.

README

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
InheritableAttributes
2+
=====================
3+
4+
Useful if model attributes are to be inherited from a parent object if blank.
5+
6+
Example
7+
=======
8+
9+
class Company < ActiveRecord::Base
10+
# company has an attribute "address"
11+
has_many :divisions
12+
end
13+
14+
class Division < ActiveRecord::Base
15+
# division has an attribute "address"
16+
belongs_to :company
17+
18+
inherit_attribute :address, :from => :company
19+
end
20+
21+
class Department < ActiveRecord::Base
22+
# department has an attribute "address"
23+
belongs_to :division
24+
25+
inherit_attribute :address, :from => :division
26+
end
27+
28+
29+
d = Department.first
30+
31+
d.address #=> returns d.address, unless that value is blank, in which case
32+
it returns d.division.address, unless that value is blank,
33+
in which case it returns d.division.company.address
34+
35+
36+
You can also specify more than one attribute to inherit:
37+
38+
class Department
39+
# department has an attribute "address"
40+
belongs_to :division
41+
42+
inherit_attributes [:address, :telephone], :from => :division
43+
end
44+
45+
If the attribute has a different name in the parent, you can use the :as
46+
option to specify it:
47+
48+
class Depot < ActiveRecord::Base
49+
belongs_to :company
50+
51+
inherit_attribute :postal_address, :from => :company, :as => :address
52+
end
53+
54+
d = Depot.first
55+
d.postal_address #=> returns d.postal_address, unless that value is blank,
56+
in which case it returns d.company.address
57+
58+
59+
Copyright (c) 2008 Jon Evans, Springy Web (UK) Ltd. Released under the MIT license
60+
61+
jon@springyweb.com

Rakefile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 inheritable_attributes 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 inheritable_attributes plugin.'
16+
Rake::RDocTask.new(:rdoc) do |rdoc|
17+
rdoc.rdoc_dir = 'rdoc'
18+
rdoc.title = 'InheritableAttributes'
19+
rdoc.options << '--line-numbers' << '--inline-source'
20+
rdoc.rdoc_files.include('README')
21+
rdoc.rdoc_files.include('lib/**/*.rb')
22+
end

init.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ActiveRecord::Base.class_eval { include InheritableAttributes }

lib/inheritable_attributes.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# InheritableAttributes
2+
module InheritableAttributes
3+
module ClassMethods
4+
def inherit_attributes(attributes, options = {})
5+
raise ArgumentError.new("must specify :from") unless options[:from]
6+
parent = options[:from]
7+
[attributes].flatten.each do |attribute|
8+
parent_attribute = options[:as] || attribute
9+
fn = <<-EOV
10+
def #{attribute}_with_inheritance
11+
val = #{attribute}_without_inheritance
12+
13+
(val.blank? && ! parent.nil?) ? #{parent}.#{parent_attribute} : val
14+
end
15+
alias_method_chain :#{attribute}, :inheritance
16+
EOV
17+
class_eval fn
18+
end
19+
end
20+
21+
alias inherit_attribute inherit_attributes
22+
end
23+
24+
def self.included(receiver)
25+
receiver.extend ClassMethods
26+
end
27+
end

test/inheritable_attributes_test.rb

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
require 'rubygems'
2+
require 'test/unit'
3+
require 'active_support/core_ext/blank'
4+
require 'active_support/core_ext/module/aliasing'
5+
require 'inheritable_attributes'
6+
7+
class Thing
8+
include InheritableAttributes
9+
10+
attr_reader :foo
11+
12+
inherit_attribute :foo, :from=>:parent
13+
14+
def initialize(parent, foo=nil)
15+
@parent = parent
16+
@foo = foo
17+
end
18+
19+
def read_attribute(attribute)
20+
instance_variable_get("@#{attribute}")
21+
end
22+
23+
def parent
24+
@parent
25+
end
26+
end
27+
28+
class Thing2 < Thing
29+
attr_reader :bar, :baz
30+
inherit_attributes [:bar, :baz], :from=>:parent
31+
32+
def initialize(parent, foo=nil, bar=nil, baz=nil)
33+
super(parent, foo)
34+
@bar = bar
35+
@baz = baz
36+
end
37+
end
38+
39+
class Thing3 < Thing
40+
attr_reader :bar, :baz
41+
inherit_attribute :bar, :from=>:parent, :as => :foo
42+
inherit_attribute :baz, :from=>:parent
43+
44+
def initialize(parent, foo=nil, bar=nil, baz=nil)
45+
super(parent, foo)
46+
@bar = bar
47+
@baz = baz
48+
end
49+
end
50+
51+
class InheritableAttributesTest < Test::Unit::TestCase
52+
def test_argument_error
53+
assert_raise(ArgumentError) do
54+
eval <<-EOF
55+
class BadThing < Thing
56+
inherit_attribute :bar
57+
end
58+
EOF
59+
end
60+
rescue
61+
62+
end
63+
def test_one_level
64+
thing = Thing.new(nil, nil)
65+
assert_equal(nil, thing.foo)
66+
67+
thing = Thing.new(nil, "")
68+
assert_equal("", thing.foo)
69+
70+
thing = Thing.new(nil, :foo)
71+
assert_equal(:foo, thing.foo)
72+
end
73+
74+
def test_two_levels
75+
nil_parent = Thing.new(nil, nil)
76+
parent = Thing.new(nil, :foo)
77+
78+
# child's foo is nil
79+
thing = Thing.new(nil_parent, nil)
80+
assert_equal(nil, thing.foo)
81+
82+
thing = Thing.new(parent, nil)
83+
assert_equal(:foo, thing.foo)
84+
85+
# child's foo is ""
86+
thing = Thing.new(nil_parent, "")
87+
assert_equal(nil, thing.foo)
88+
89+
thing = Thing.new(parent, "")
90+
assert_equal(:foo, thing.foo)
91+
92+
# child's foo is :bar
93+
thing = Thing.new(nil_parent, :bar)
94+
assert_equal(:bar, thing.foo)
95+
96+
thing = Thing.new(parent, :bar)
97+
assert_equal(:bar, thing.foo)
98+
end
99+
100+
def test_multi_vars
101+
nil_parent = Thing2.new(nil)
102+
parent = Thing2.new(nil, :foo_parent, :bar_parent, :baz_parent)
103+
104+
thing = Thing2.new(parent)
105+
assert_equal(:foo_parent, thing.foo)
106+
assert_equal(:bar_parent, thing.bar)
107+
assert_equal(:baz_parent, thing.baz)
108+
109+
thing = Thing2.new(nil_parent, :foo, :bar, :baz)
110+
assert_equal(:foo, thing.foo)
111+
assert_equal(:bar, thing.bar)
112+
assert_equal(:baz, thing.baz)
113+
end
114+
115+
def test_as
116+
parent = Thing2.new(nil, :foo_parent, :bar_parent, :baz_parent)
117+
118+
thing = Thing3.new(parent)
119+
assert_equal(:foo_parent, thing.foo)
120+
assert_equal(:foo_parent, thing.bar)
121+
assert_equal(:baz_parent, thing.baz)
122+
end
123+
end

0 commit comments

Comments
 (0)