acts_as_relation 0.0.4 → 0.0.5
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/README.markdown +30 -11
- data/Rakefile +2 -0
- data/acts_as_relation.gemspec +1 -0
- data/lib/active_record/acts/as_relation.rb +47 -14
- data/lib/active_record/acts/as_relation_superclass_migration.rb +27 -0
- data/lib/acts_as_relation.rb +1 -1
- data/lib/version.rb +1 -1
- data/test/acts_as_relation_test.rb +39 -0
- data/test/schema.rb +10 -6
- metadata +24 -9
data/README.markdown
CHANGED
@@ -19,43 +19,62 @@ parents (via an automatically-created `has_one` associations).
|
|
19
19
|
Example
|
20
20
|
-------
|
21
21
|
|
22
|
-
Required columns on parent model name `parent1` are
|
23
|
-
|
24
|
-
1. `parent1_type`
|
25
|
-
2. `parent1_id`
|
26
|
-
|
27
22
|
generate models
|
28
23
|
|
29
|
-
$ rails g model product name:string price:float
|
24
|
+
$ rails g model product name:string price:float
|
30
25
|
$ rails g model pen color:string
|
31
26
|
|
27
|
+
Add next option to Product Migration:
|
28
|
+
|
29
|
+
create_table :products, :as_relation_superclass => true
|
30
|
+
|
32
31
|
add some validations and instance methods
|
33
32
|
|
34
33
|
class Product < ActiveRecord::Base
|
35
34
|
validates_presence_of :name, :price
|
36
|
-
|
35
|
+
|
37
36
|
def hello
|
38
37
|
puts "Hello, My name is '#{name}', my price is $#{price}."
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
41
|
+
product is superclass for all kind of products:
|
42
|
+
|
43
|
+
class Product < ActiveRecord::Base
|
44
|
+
acts_as_superclass
|
45
|
+
end
|
46
|
+
|
42
47
|
pen inherits from product
|
43
48
|
|
44
49
|
class Pen < ActiveRecord::Base
|
45
50
|
acts_as :product
|
46
51
|
end
|
47
52
|
|
48
|
-
|
53
|
+
to get some specific class(as example: specific product - Pen) from superclass can be used method specific_class:
|
54
|
+
|
55
|
+
Pen.create :name => 'Pen A', :color=> 'black', :price => 0.42
|
56
|
+
product = Product.first
|
57
|
+
product.specific_class # will be instance of Pen class
|
58
|
+
|
59
|
+
to get name of association used between superclass and children can be used method acts_as_association_name:
|
60
|
+
|
61
|
+
Product.acts_as_association_name # 'Productable'
|
62
|
+
|
63
|
+
after deleting specific object will be removed linked superobject:
|
64
|
+
|
65
|
+
Pen.first.destroy # delete as Pen row as linked Product row
|
66
|
+
|
67
|
+
pen inherits products validations and columns
|
49
68
|
|
50
69
|
p = Pen.new
|
51
70
|
p.valid? => false
|
52
71
|
p.errors => {:name=>["can't be blank"], :price=>["can't be blank"]}
|
53
|
-
|
54
|
-
pen inherits
|
72
|
+
|
73
|
+
pen inherits product methods
|
55
74
|
|
56
75
|
pen = Pen.new(:name=>"Red Pen", :color=>:red, :price=>0.99)
|
57
76
|
pen.hello => Hello, My name is 'Red Pen', my price is $0.99.
|
58
|
-
|
77
|
+
|
59
78
|
we can make queries on both models
|
60
79
|
|
61
80
|
Product.where("price <= 1")
|
data/Rakefile
CHANGED
data/acts_as_relation.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
|
25
25
|
# Dependencies (installed via 'bundle install')...
|
26
26
|
s.add_development_dependency("bundler")
|
27
|
+
s.add_development_dependency("rake")
|
27
28
|
s.add_development_dependency("sqlite3")
|
28
29
|
s.add_development_dependency("activerecord")
|
29
30
|
end
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
def self.included(base)
|
5
5
|
base.extend(ClassMethods)
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
module AccessMethods
|
9
9
|
def define_acts_as_accessors(attribs, model_name)
|
10
10
|
attribs.each do |attrib|
|
@@ -24,43 +24,62 @@ module ActiveRecord
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
module ClassMethods
|
29
|
-
|
29
|
+
def acts_as_association_name model_name = nil
|
30
|
+
suffix = 'able'
|
31
|
+
model_name = self.name unless model_name
|
32
|
+
|
33
|
+
name = model_name.to_s.demodulize.singularize
|
34
|
+
if name[-1].chr =~ /[^aeiou]/ || name[-2..-1] =~ /ge|ce/
|
35
|
+
name = name + suffix
|
36
|
+
elsif name[-7..-1] == 'ability'
|
37
|
+
name = name[0..-8] + suffix
|
38
|
+
else
|
39
|
+
name = name[0..-2] + suffix
|
40
|
+
end
|
41
|
+
|
42
|
+
name.underscore
|
43
|
+
end
|
44
|
+
|
30
45
|
def acts_as(model_name)
|
31
46
|
name = model_name.to_s.underscore.singularize
|
32
|
-
|
47
|
+
association_name = acts_as_association_name name
|
48
|
+
|
33
49
|
# Create A AsModel module
|
34
50
|
as_model = Module.new
|
35
51
|
Object.const_set("As#{name.camelcase}", as_model)
|
36
|
-
|
52
|
+
|
37
53
|
as_model.module_eval <<-EndModule
|
38
54
|
def self.included(base)
|
39
|
-
base.has_one :#{name}, :as => :#{
|
55
|
+
base.has_one :#{name}, :as => :#{association_name}, :autosave => true, :validate => false, :dependent => :destroy
|
40
56
|
base.validate :#{name}_must_be_valid
|
41
57
|
base.alias_method_chain :#{name}, :autobuild
|
42
58
|
|
43
59
|
base.extend ActiveRecord::Acts::AsRelation::AccessMethods
|
44
60
|
all_attributes = #{name.camelcase.constantize}.content_columns.map(&:name)
|
45
|
-
ignored_attributes = ["created_at", "updated_at", "#{
|
61
|
+
ignored_attributes = ["created_at", "updated_at", "#{association_name}_type"]
|
46
62
|
associations = #{name.camelcase.constantize}.reflect_on_all_associations(:belongs_to).map! { |assoc| assoc.name }
|
47
63
|
attributes_to_delegate = all_attributes - ignored_attributes + associations
|
48
64
|
base.define_acts_as_accessors(attributes_to_delegate, "#{name}")
|
49
65
|
end
|
50
|
-
|
66
|
+
|
51
67
|
def #{name}_with_autobuild
|
52
68
|
#{name}_without_autobuild || build_#{name}
|
53
69
|
end
|
54
70
|
|
55
|
-
def method_missing
|
71
|
+
def method_missing method, *arg, &block
|
72
|
+
#{name}.send method, *arg, &block
|
73
|
+
rescue NoMethodError
|
74
|
+
super
|
56
75
|
end
|
57
|
-
|
76
|
+
|
58
77
|
def respond_to?(method, include_private_methods = false)
|
59
78
|
super || self.#{name}.respond_to?(method, include_private_methods)
|
60
79
|
end
|
61
|
-
|
80
|
+
|
62
81
|
protected
|
63
|
-
|
82
|
+
|
64
83
|
def #{name}_must_be_valid
|
65
84
|
unless #{name}.valid?
|
66
85
|
#{name}.errors.each do |att, message|
|
@@ -69,13 +88,27 @@ module ActiveRecord
|
|
69
88
|
end
|
70
89
|
end
|
71
90
|
EndModule
|
72
|
-
|
91
|
+
|
73
92
|
class_eval do
|
74
93
|
include "As#{name.camelcase}".constantize
|
75
94
|
end
|
76
95
|
end
|
77
|
-
|
96
|
+
|
97
|
+
def acts_as_superclass
|
98
|
+
association_name = acts_as_association_name
|
99
|
+
|
100
|
+
class_eval <<-CLASS
|
101
|
+
belongs_to :#{association_name}, :polymorphic => true
|
102
|
+
|
103
|
+
def specific_class
|
104
|
+
self.#{association_name}
|
105
|
+
end
|
106
|
+
CLASS
|
107
|
+
end
|
108
|
+
|
78
109
|
end
|
79
110
|
end
|
80
111
|
end
|
81
112
|
end
|
113
|
+
|
114
|
+
ActiveRecord::Base.send :include, ActiveRecord::Acts::AsRelation
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Acts
|
3
|
+
module AsRelationSuperclassMigration
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
alias_method_chain :create_table, :as_relation_superclass
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_table_with_as_relation_superclass(table_name, options = {})
|
12
|
+
association_name = ActiveRecord::Base.acts_as_association_name table_name
|
13
|
+
|
14
|
+
create_table_without_as_relation_superclass(table_name, options) do |td|
|
15
|
+
if options[:as_relation_superclass]
|
16
|
+
td.integer "#{association_name}_id"
|
17
|
+
td.string "#{association_name}_type"
|
18
|
+
end
|
19
|
+
|
20
|
+
yield td if block_given?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ActiveRecord::ConnectionAdapters::SchemaStatements.send :include, ActiveRecord::Acts::AsRelationSuperclassMigration
|
data/lib/acts_as_relation.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'active_record/acts/as_relation'
|
2
|
-
|
2
|
+
require 'active_record/acts/as_relation_superclass_migration'
|
data/lib/version.rb
CHANGED
@@ -74,6 +74,45 @@ class ActsAsRelationTest < ActiveSupport::TestCase
|
|
74
74
|
assert_equal store, pen.store
|
75
75
|
end
|
76
76
|
|
77
|
+
test "call parent methods" do
|
78
|
+
pen = Pen.new(:name=>"RedPen", :price=>0.59, :color=>"red")
|
79
|
+
assert_equal pen.parent_method, "RedPen - 0.59"
|
80
|
+
end
|
81
|
+
|
82
|
+
test "call unexisted method" do
|
83
|
+
assert_raise NoMethodError do
|
84
|
+
pen = Pen.new
|
85
|
+
pen.unexisted_method
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
test "acts as association name" do
|
91
|
+
assert_equal Pencil.acts_as_association_name, 'pencilable'
|
92
|
+
assert_equal Pencil.acts_as_association_name( Pen ), 'penable'
|
93
|
+
end
|
94
|
+
|
95
|
+
test "acts as superclass" do
|
96
|
+
pen = Pen.create(:name => "RedPen", :price => 0.59, :color => "red")
|
97
|
+
product = pen.product
|
98
|
+
|
99
|
+
assert_equal product.specific_class.class, Pen
|
100
|
+
end
|
101
|
+
|
102
|
+
test "destroy action" do
|
103
|
+
pen = Pen.create(:name => "RedPen", :price => 0.59, :color => "red")
|
104
|
+
product = pen.product
|
105
|
+
|
106
|
+
pen.destroy
|
107
|
+
|
108
|
+
assert_raise ActiveRecord::RecordNotFound do
|
109
|
+
Product.find product.id
|
110
|
+
end
|
111
|
+
assert_raise ActiveRecord::RecordNotFound do
|
112
|
+
Pen.find pen.id
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
77
116
|
end
|
78
117
|
|
79
118
|
#ActiveRecord::Base.connection.tables.each do |table|
|
data/test/schema.rb
CHANGED
@@ -13,17 +13,13 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
13
13
|
t.string :store_name
|
14
14
|
end
|
15
15
|
|
16
|
-
create_table :products do |t|
|
16
|
+
create_table :products, :as_relation_superclass => true do |t|
|
17
17
|
t.string :name
|
18
18
|
t.float :price
|
19
|
-
t.string :product_type
|
20
|
-
t.integer :product_id
|
21
19
|
end
|
22
20
|
|
23
|
-
create_table :pens do |t|
|
21
|
+
create_table :pens, :as_relation_superclass => true do |t|
|
24
22
|
t.string :color
|
25
|
-
t.integer :pen_id
|
26
|
-
t.string :pen_type
|
27
23
|
end
|
28
24
|
|
29
25
|
create_table :pencils
|
@@ -34,11 +30,19 @@ class Store < ActiveRecord::Base
|
|
34
30
|
end
|
35
31
|
|
36
32
|
class Product < ActiveRecord::Base
|
33
|
+
acts_as_superclass
|
34
|
+
|
37
35
|
belongs_to :store
|
38
36
|
validates_presence_of :name, :price
|
37
|
+
|
38
|
+
def parent_method
|
39
|
+
"#{name} - #{price}"
|
40
|
+
end
|
39
41
|
end
|
40
42
|
|
41
43
|
class Pen < ActiveRecord::Base
|
44
|
+
acts_as_superclass
|
45
|
+
|
42
46
|
acts_as :product
|
43
47
|
validates_presence_of :color
|
44
48
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_relation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-12-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
|
-
requirement: &
|
16
|
+
requirement: &10491360 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,21 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *10491360
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &10490800 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *10490800
|
25
36
|
- !ruby/object:Gem::Dependency
|
26
37
|
name: sqlite3
|
27
|
-
requirement: &
|
38
|
+
requirement: &10490200 !ruby/object:Gem::Requirement
|
28
39
|
none: false
|
29
40
|
requirements:
|
30
41
|
- - ! '>='
|
@@ -32,10 +43,10 @@ dependencies:
|
|
32
43
|
version: '0'
|
33
44
|
type: :development
|
34
45
|
prerelease: false
|
35
|
-
version_requirements: *
|
46
|
+
version_requirements: *10490200
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: activerecord
|
38
|
-
requirement: &
|
49
|
+
requirement: &10489620 !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
51
|
requirements:
|
41
52
|
- - ! '>='
|
@@ -43,7 +54,7 @@ dependencies:
|
|
43
54
|
version: '0'
|
44
55
|
type: :development
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
57
|
+
version_requirements: *10489620
|
47
58
|
description: This 'acts_as' extension provides multi-table inheritance for rails models.
|
48
59
|
email: hsn.zamani@gmail.com
|
49
60
|
executables: []
|
@@ -57,6 +68,7 @@ files:
|
|
57
68
|
- acts_as_relation.gemspec
|
58
69
|
- init.rb
|
59
70
|
- lib/active_record/acts/as_relation.rb
|
71
|
+
- lib/active_record/acts/as_relation_superclass_migration.rb
|
60
72
|
- lib/acts_as_relation.rb
|
61
73
|
- lib/version.rb
|
62
74
|
- test/acts_as_relation_test.rb
|
@@ -86,4 +98,7 @@ rubygems_version: 1.8.10
|
|
86
98
|
signing_key:
|
87
99
|
specification_version: 3
|
88
100
|
summary: Easy multi-table inheritance for rails
|
89
|
-
test_files:
|
101
|
+
test_files:
|
102
|
+
- test/acts_as_relation_test.rb
|
103
|
+
- test/schema.rb
|
104
|
+
- test/test_helper.rb
|