classy-inheritance 0.4.4 → 0.5.0
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/History.txt +3 -0
- data/lib/classy-inheritance.rb +33 -4
- data/lib/classy-inheritance/version.rb +2 -2
- data/test/test_classy-inheritance.rb +52 -2
- data/test/test_helper.rb +134 -0
- data/test/test_polymorphic_associations.rb +64 -0
- data/test/test_with_optional_dependency.rb +59 -0
- data/test/test_with_standard_attributes.rb +82 -0
- data/website/index.html +2 -2
- metadata +5 -2
data/History.txt
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
== 0.5.0 2008-07-08
|
2
|
+
* Add: new options: validates_presence_if, validates_associated_if to allow for greater control over validation use.
|
3
|
+
* Add: Basic test cases of classy-inheritance depends_on functionality.
|
1
4
|
== 0.4.4 2008-06-24
|
2
5
|
* Fix: :class_name attribute was not being recognized
|
3
6
|
== 0.4.3 2008-06-24
|
data/lib/classy-inheritance.rb
CHANGED
@@ -26,8 +26,22 @@ module Stonean
|
|
26
26
|
def depends_on(model_sym, options = {})
|
27
27
|
define_relationship(model_sym,options)
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
# Optional presence of handling
|
30
|
+
if options.has_key?(:validates_presence_if) && options[:validates_presence_if] != true
|
31
|
+
if [Symbol, String, Proc].include?(options[:validates_presence_if].class)
|
32
|
+
validates_presence_of model_sym, :if => options[:validates_presence_if]
|
33
|
+
end
|
34
|
+
else
|
35
|
+
validates_presence_of model_sym
|
36
|
+
end
|
37
|
+
|
38
|
+
if options.has_key?(:validates_associated_if) && options[:validates_associated_if] != true
|
39
|
+
if [Symbol, String, Proc].include?(options[:validates_assoicated_if].class)
|
40
|
+
validates_associated model_sym, :if => options[:validates_associated_if]
|
41
|
+
end
|
42
|
+
else
|
43
|
+
validates_associated model_sym
|
44
|
+
end
|
31
45
|
|
32
46
|
# Before save functionality to create/update the requisite object
|
33
47
|
define_save_method(model_sym, options[:as])
|
@@ -63,9 +77,19 @@ module Stonean
|
|
63
77
|
|
64
78
|
private
|
65
79
|
|
80
|
+
def classy_options
|
81
|
+
[:as, :attrs, :prefix, :validates_presence_if, :validates_associated_if]
|
82
|
+
end
|
83
|
+
|
84
|
+
def delete_classy_options(options, *keepers)
|
85
|
+
options.delete_if do |key,value|
|
86
|
+
classy_options.include?(key) && !keepers.include?(key)
|
87
|
+
end
|
88
|
+
options
|
89
|
+
end
|
90
|
+
|
66
91
|
def define_relationship(model_sym, options)
|
67
|
-
opts = options.dup
|
68
|
-
[:attrs, :prefix].each{|key| opts.delete(key)}
|
92
|
+
opts = delete_classy_options(options.dup, :as)
|
69
93
|
if opts[:as]
|
70
94
|
as_opt = opts.delete(:as)
|
71
95
|
opts = polymorphic_constraints(as_opt).merge(opts)
|
@@ -77,12 +101,17 @@ module Stonean
|
|
77
101
|
|
78
102
|
def define_save_method(model_sym, polymorphic_name = nil)
|
79
103
|
define_method "save_requisite_#{model_sym}" do
|
104
|
+
# Return unless the association exists
|
105
|
+
eval("return unless self.#{model_sym}")
|
106
|
+
|
107
|
+
# Set the polymorphic type and id before saving
|
80
108
|
if polymorphic_name
|
81
109
|
eval("self.#{model_sym}.#{polymorphic_name}_type = self.class.name")
|
82
110
|
eval("self.#{model_sym}.#{polymorphic_name}_id = self.id")
|
83
111
|
end
|
84
112
|
|
85
113
|
if polymorphic_name
|
114
|
+
# Save only if it's an update, has_one creates automatically
|
86
115
|
eval <<-SAVEIT
|
87
116
|
unless self.#{model_sym}.new_record?
|
88
117
|
self.#{model_sym}.save
|
@@ -1,11 +1,61 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
2
|
|
3
|
+
User.depends_on :profile, :attrs => [:first_name, :last_name, :email]
|
4
|
+
|
3
5
|
class TestClassyInheritance < Test::Unit::TestCase
|
4
6
|
|
5
7
|
def setup
|
8
|
+
@user = User.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_active_record_should_have_classy_inheritance_included
|
12
|
+
assert ActiveRecord::Base.included_modules.include?(Stonean::ClassyInheritance)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_active_record_should_respond_to_depends_on
|
16
|
+
assert ActiveRecord::Base.respond_to?(:depends_on)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_user_should_respond_to_find_with_profile
|
20
|
+
assert User.respond_to?(:find_with_profile)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_user_should_respond_to_first_name
|
24
|
+
assert @user.respond_to?(:first_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_user_should_respond_to_first_name=
|
28
|
+
assert @user.respond_to?(:first_name=)
|
6
29
|
end
|
7
30
|
|
8
|
-
def
|
9
|
-
assert
|
31
|
+
def test_user_should_respond_to_last_name
|
32
|
+
assert @user.respond_to?(:last_name)
|
10
33
|
end
|
34
|
+
|
35
|
+
def test_user_should_respond_to_last_name=
|
36
|
+
assert @user.respond_to?(:last_name=)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_user_should_respond_to_email
|
40
|
+
assert @user.respond_to?(:email)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_user_should_respond_to_email=
|
44
|
+
assert @user.respond_to?(:email=)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_user_should_be_invalid_without_profile_attributes
|
48
|
+
@user.login = 'joe'
|
49
|
+
assert !@user.valid?
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_user_should_valid_with_profile_attributes
|
53
|
+
@user.login = 'joe'
|
54
|
+
@user.first_name = 'joe'
|
55
|
+
@user.last_name = 'bloggs'
|
56
|
+
@user.email = 'joe@bloggs.co.uk'
|
57
|
+
|
58
|
+
assert @user.valid?
|
59
|
+
end
|
60
|
+
|
11
61
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,2 +1,136 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'activerecord'
|
3
|
+
|
1
4
|
require 'test/unit'
|
2
5
|
require File.dirname(__FILE__) + '/../lib/classy-inheritance'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection({
|
8
|
+
:adapter => "sqlite3",
|
9
|
+
:dbfile => "test/database.sqlite3"
|
10
|
+
})
|
11
|
+
|
12
|
+
class SetupTestTables < ActiveRecord::Migration
|
13
|
+
def self.up
|
14
|
+
create_table :profiles, :force => true do |t|
|
15
|
+
t.string :first_name
|
16
|
+
t.string :last_name
|
17
|
+
t.string :email
|
18
|
+
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :users, :force => true do |t|
|
23
|
+
t.string :login
|
24
|
+
t.integer :profile_id
|
25
|
+
|
26
|
+
t.timestamps
|
27
|
+
end
|
28
|
+
|
29
|
+
create_table :authors, :force => true do |t|
|
30
|
+
t.string :login
|
31
|
+
t.integer :profile_id
|
32
|
+
|
33
|
+
t.timestamps
|
34
|
+
end
|
35
|
+
|
36
|
+
create_table :artists, :force => true do |t|
|
37
|
+
t.string :login
|
38
|
+
t.integer :profile_id
|
39
|
+
|
40
|
+
t.timestamps
|
41
|
+
end
|
42
|
+
|
43
|
+
create_table :addresses, :force => true do |t|
|
44
|
+
t.string :line_one
|
45
|
+
t.string :line_two
|
46
|
+
t.string :city
|
47
|
+
t.string :state_code
|
48
|
+
t.string :postal_code
|
49
|
+
|
50
|
+
t.timestamps
|
51
|
+
end
|
52
|
+
|
53
|
+
create_table :offices, :force => true do |t|
|
54
|
+
t.string :name
|
55
|
+
t.integer :billing_address_id
|
56
|
+
t.integer :shipping_address_id
|
57
|
+
|
58
|
+
t.timestamps
|
59
|
+
end
|
60
|
+
|
61
|
+
create_table :contents, :force => true do |t|
|
62
|
+
t.string :name
|
63
|
+
t.string :presentable_type
|
64
|
+
t.integer :presentable_id
|
65
|
+
|
66
|
+
t.timestamps
|
67
|
+
end
|
68
|
+
|
69
|
+
create_table :pages, :force => true do |t|
|
70
|
+
t.text :body
|
71
|
+
|
72
|
+
t.timestamps
|
73
|
+
end
|
74
|
+
|
75
|
+
create_table :documents, :force => true do |t|
|
76
|
+
t.string :version_file_name
|
77
|
+
t.string :version_content_type
|
78
|
+
t.integer :version_file_size
|
79
|
+
|
80
|
+
t.timestamps
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.down
|
85
|
+
drop_table :authors
|
86
|
+
drop_table :artists
|
87
|
+
drop_table :users
|
88
|
+
drop_table :profiles
|
89
|
+
drop_table :offices
|
90
|
+
drop_table :addresses
|
91
|
+
drop_table :pages
|
92
|
+
drop_table :documents
|
93
|
+
drop_table :contents
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
SetupTestTables.migrate(:up)
|
98
|
+
|
99
|
+
class Profile < ActiveRecord::Base
|
100
|
+
validates_presence_of :first_name, :last_name, :email
|
101
|
+
end
|
102
|
+
|
103
|
+
class User < ActiveRecord::Base
|
104
|
+
validates_presence_of :login
|
105
|
+
end
|
106
|
+
|
107
|
+
class Author < ActiveRecord::Base
|
108
|
+
validates_presence_of :login
|
109
|
+
end
|
110
|
+
|
111
|
+
# to test optional dependency
|
112
|
+
class Artist < ActiveRecord::Base
|
113
|
+
validates_presence_of :login
|
114
|
+
end
|
115
|
+
|
116
|
+
# to test using standard relationship options (class_name, etc..)
|
117
|
+
class Address < ActiveRecord::Base
|
118
|
+
validates_presence_of :line_one, :city, :postal_code
|
119
|
+
end
|
120
|
+
|
121
|
+
class Office < ActiveRecord::Base
|
122
|
+
validates_presence_of :name
|
123
|
+
end
|
124
|
+
|
125
|
+
# Polymorphic classes
|
126
|
+
class Content < ActiveRecord::Base
|
127
|
+
validates_presence_of :name
|
128
|
+
end
|
129
|
+
|
130
|
+
class Page < ActiveRecord::Base
|
131
|
+
validates_presence_of :body
|
132
|
+
end
|
133
|
+
|
134
|
+
# Would typically use PaperClip for this...
|
135
|
+
class Document < ActiveRecord::Base
|
136
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
Content.can_be :document, :as => :presentable
|
4
|
+
|
5
|
+
Page.depends_on :content, :attrs => [:name], :as => :presentable
|
6
|
+
|
7
|
+
class TestPolymorphicAssociations < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@page = Page.new
|
11
|
+
|
12
|
+
@test_page = Page.create(:name => 'My Page', :body => 'Something interesting')
|
13
|
+
@test_content = @test_page.content
|
14
|
+
end
|
15
|
+
|
16
|
+
# from Content.can_be declaration
|
17
|
+
def test_document_should_respond_to_find_with_content
|
18
|
+
assert Document.respond_to?(:find_with_content)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_page_should_respond_to_name
|
22
|
+
assert @page.respond_to?(:name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_page_should_fail_validation_without_content_name
|
26
|
+
@page.body = 'Some really informative stuff that would be a lot longer'
|
27
|
+
assert !@page.valid?
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_page_should_pass_validation_with_content_name
|
31
|
+
@page.name = 'First Page'
|
32
|
+
@page.body = 'Some really informative stuff that would be a lot longer'
|
33
|
+
assert @page.valid?
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_page_should_create_content
|
37
|
+
@page.name = 'First Page'
|
38
|
+
@page.body = 'Some really informative stuff that would be a lot longer'
|
39
|
+
@page.save
|
40
|
+
|
41
|
+
assert @page.content.id != nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_content_should_respond_to_is_a_document
|
45
|
+
assert @test_content.respond_to?(:is_a_document?)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_content_should_respond_to_as_a_document
|
49
|
+
assert @test_content.respond_to?(:as_a_document)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_content_should_respond_to_is_a_page
|
53
|
+
assert @test_content.respond_to?(:is_a_page?)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_content_should_respond_to_as_a_page
|
57
|
+
assert @test_content.respond_to?(:as_a_page)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_content_should_return_page_object
|
61
|
+
assert @test_content.as_a_page.is_a?(Page)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestWithOptionalDependency < Test::Unit::TestCase
|
4
|
+
# Turn off the validates_presence_of call
|
5
|
+
Author.depends_on :profile, :validates_presence_if => false,
|
6
|
+
:attrs => [:first_name, :last_name, :email]
|
7
|
+
|
8
|
+
# Turn off the validates_presence_of and the validates_associated calls
|
9
|
+
Artist.depends_on :profile, :validates_presence_if => false,
|
10
|
+
:validates_associated_if => false,
|
11
|
+
:attrs => [:first_name, :last_name, :email]
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@author = Author.new
|
15
|
+
@artist = Artist.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_author_should_be_valid_without_profile
|
19
|
+
@author.login = 'joe'
|
20
|
+
|
21
|
+
@author.valid?
|
22
|
+
puts @author.errors.full_messages.to_sentence
|
23
|
+
|
24
|
+
assert @author.valid?
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_author_should_be_invalid_with_invalid_profile
|
28
|
+
@author.login = 'joe'
|
29
|
+
@author.first_name = 'joe'
|
30
|
+
|
31
|
+
assert !@author.valid?
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_author_should_be_valid_with_profile_attributes
|
35
|
+
@author.login = 'joe'
|
36
|
+
@author.first_name = 'joe'
|
37
|
+
@author.last_name = 'bloggs'
|
38
|
+
@author.email = 'joe@bloggs.co.uk'
|
39
|
+
|
40
|
+
assert @author.valid?
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_artist_should_not_save_with_invalid_profile
|
44
|
+
@artist.login = 'joe'
|
45
|
+
@artist.first_name = 'joe'
|
46
|
+
|
47
|
+
assert !@artist.save
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_artist_should_save_with_valid_profile
|
51
|
+
@artist.login = 'joe'
|
52
|
+
@artist.first_name = 'joe'
|
53
|
+
@artist.last_name = 'bloggs'
|
54
|
+
@artist.email = 'joe@bloggs.co.uk'
|
55
|
+
|
56
|
+
assert @artist.save
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
Office.depends_on :billing_address,
|
4
|
+
:attrs => [:line_one, :line_two, :city, :state_code, :postal_code],
|
5
|
+
:class_name => "Address",
|
6
|
+
:foreign_key => "billing_address_id",
|
7
|
+
:prefix => true
|
8
|
+
|
9
|
+
Office.depends_on :shipping_address,
|
10
|
+
:attrs => [:line_one, :line_two, :city, :state_code, :postal_code],
|
11
|
+
:class_name => "Address",
|
12
|
+
:foreign_key => "shipping_address_id",
|
13
|
+
:prefix => true
|
14
|
+
|
15
|
+
class TestWithStandardAttributes < Test::Unit::TestCase
|
16
|
+
def setup
|
17
|
+
@office = Office.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_office_should_respond_to_billing_address_line_one
|
21
|
+
assert_respond_to(@office, :billing_address_line_one)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_office_should_respond_to_billing_address_line_two
|
25
|
+
assert_respond_to(@office, :billing_address_line_two)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_office_should_respond_to_billing_address_city
|
29
|
+
assert_respond_to(@office, :billing_address_city)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_office_should_respond_to_billing_address_state_code
|
33
|
+
assert_respond_to(@office, :billing_address_state_code)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_office_should_respond_to_billing_address_postal_code
|
37
|
+
assert_respond_to(@office, :billing_address_postal_code)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_office_should_respond_to_shipping_address_line_one
|
41
|
+
assert_respond_to(@office, :shipping_address_line_one)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_office_should_respond_to_shipping_address_line_two
|
45
|
+
assert_respond_to(@office, :shipping_address_line_two)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_office_should_respond_to_shipping_address_city
|
49
|
+
assert_respond_to(@office, :shipping_address_city)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_office_should_respond_to_shipping_address_state_code
|
53
|
+
assert_respond_to(@office, :shipping_address_state_code)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_office_should_respond_to_shipping_address_postal_code
|
57
|
+
assert_respond_to(@office, :shipping_address_postal_code)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_office_should_create_billing_and_shipping_address_records
|
61
|
+
@office.name = 'Initest'
|
62
|
+
|
63
|
+
@office.billing_address_line_one = '123 Somewhere'
|
64
|
+
@office.billing_address_city = 'Somecity'
|
65
|
+
@office.billing_address_postal_code = '12345'
|
66
|
+
|
67
|
+
@office.shipping_address_line_one = '999 MyHouse'
|
68
|
+
@office.shipping_address_city = 'Mycity'
|
69
|
+
@office.shipping_address_postal_code = '98765'
|
70
|
+
|
71
|
+
@office.save
|
72
|
+
|
73
|
+
@billing_address = Address.find(@office.billing_address_id)
|
74
|
+
@shipping_address = Address.find(@office.shipping_address_id)
|
75
|
+
|
76
|
+
assert_equal @billing_address, @office.billing_address
|
77
|
+
|
78
|
+
assert_equal @shipping_address, @office.shipping_address
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
end
|
data/website/index.html
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
<h1>Classy Inheritance</h1>
|
34
34
|
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/classyinherit"; return false'>
|
35
35
|
<p>Get Version</p>
|
36
|
-
<a href="http://rubyforge.org/projects/classyinherit" class="numbers">0.
|
36
|
+
<a href="http://rubyforge.org/projects/classyinherit" class="numbers">0.5.0</a>
|
37
37
|
</div>
|
38
38
|
<p><i>“You stay classy, inheritance” – Gibson</i></p>
|
39
39
|
|
@@ -56,7 +56,7 @@
|
|
56
56
|
<p>Thanks for your interest in Classy Inheritance,<br/>
|
57
57
|
-andy</p>
|
58
58
|
<p class="coda">
|
59
|
-
<a href="http://blog.stonean.com">Andrew Stone</a>,
|
59
|
+
<a href="http://blog.stonean.com">Andrew Stone</a>, 8th July 2008<br>
|
60
60
|
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
|
61
61
|
</p>
|
62
62
|
</div>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: classy-inheritance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Stone
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-07-08 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -84,3 +84,6 @@ summary: Adds a depends_on class method to your ActiveRecord model so that you c
|
|
84
84
|
test_files:
|
85
85
|
- test/test_classy-inheritance.rb
|
86
86
|
- test/test_helper.rb
|
87
|
+
- test/test_polymorphic_associations.rb
|
88
|
+
- test/test_with_optional_dependency.rb
|
89
|
+
- test/test_with_standard_attributes.rb
|