poly_belongs_to 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +86 -16
- data/lib/poly_belongs_to.rb +57 -52
- data/lib/poly_belongs_to/dup.rb +43 -0
- data/lib/poly_belongs_to/hierarchy.rb +23 -0
- data/lib/poly_belongs_to/poly_belongs_to.rb +66 -0
- data/lib/poly_belongs_to/version.rb +1 -1
- data/test/dummy/Rakefile +1 -1
- data/test/dummy/app/models/address.rb +3 -0
- data/test/dummy/app/models/contact.rb +5 -0
- data/test/dummy/app/models/photo.rb +3 -0
- data/test/dummy/app/models/profile.rb +7 -0
- data/test/dummy/app/models/ssn.rb +3 -0
- data/test/dummy/app/models/user.rb +3 -1
- data/test/dummy/config.ru +1 -1
- data/test/dummy/config/application.rb +11 -1
- data/test/dummy/config/boot.rb +1 -0
- data/test/dummy/config/environment.rb +3 -3
- data/test/dummy/config/environments/development.rb +1 -1
- data/test/dummy/config/environments/production.rb +1 -1
- data/test/dummy/config/environments/test.rb +1 -1
- data/test/dummy/config/initializers/assets.rb +3 -3
- data/test/dummy/config/initializers/cookies_serializer.rb +1 -1
- data/test/dummy/config/initializers/filter_parameter_logging.rb +1 -1
- data/test/dummy/config/initializers/session_store.rb +1 -1
- data/test/dummy/config/routes.rb +1 -1
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20150211224139_create_users.rb +1 -0
- data/test/dummy/db/migrate/20150211224157_create_tags.rb +1 -0
- data/test/dummy/db/migrate/20150211224225_create_phones.rb +1 -0
- data/test/dummy/db/migrate/20150216092218_create_addresses.rb +10 -0
- data/test/dummy/db/migrate/20150216092338_create_profiles.rb +10 -0
- data/test/dummy/db/migrate/20150216092411_create_photos.rb +10 -0
- data/test/dummy/db/migrate/20150216092449_create_contacts.rb +11 -0
- data/test/dummy/db/migrate/20150216092519_create_ssns.rb +11 -0
- data/test/dummy/db/schema.rb +56 -5
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +50 -51
- data/test/dummy/log/test.log +49835 -1884
- data/test/dup_test.rb +39 -0
- data/test/fixtures/addresses.yml +18 -0
- data/test/fixtures/phones.yml +12 -3
- data/test/fixtures/photos.yml +16 -0
- data/test/fixtures/profiles.yml +15 -0
- data/test/fixtures/ssns.yml +14 -0
- data/test/fixtures/tags.yml +9 -2
- data/test/fixtures/users.yml +3 -1
- data/test/pbt_test.rb +61 -0
- data/test/poly_belongs_to_test.rb +75 -48
- data/test/test_helper.rb +13 -7
- metadata +58 -47
- data/test/dummy/app/assets/javascripts/application.js +0 -13
- data/test/dummy/app/assets/stylesheets/application.css +0 -15
- data/test/dummy/test/models/phone_test.rb +0 -7
- data/test/dummy/test/models/tag_test.rb +0 -7
- data/test/dummy/test/models/user_test.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 295e7bd5fc23bc91323e6bf9d8650e1e4b6f59ab
|
4
|
+
data.tar.gz: 9cfbad7a6779856a5950b7410737ee524b8ef29c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3421897cbce5956b73d3ff9339c2ec3755d36ed03c830eef435dce662e83f2ece03604b9bfa50357023267c66d7fb82dba76a0ac7845712dcaa76a1b8634b93
|
7
|
+
data.tar.gz: babae7be384227bb9ee4b181f08598d97b3963d926bae0da03a80d89ccb2758fccfbabdd07a1ca95b223a2c7bdb11e2a1cc705d7828fb0f9e1f1e2ad645a17c8
|
data/README.md
CHANGED
@@ -3,28 +3,15 @@
|
|
3
3
|
[![Code Climate](https://codeclimate.com/github/danielpclark/PolyBelongsTo/badges/gpa.svg)](https://codeclimate.com/github/danielpclark/PolyBelongsTo)
|
4
4
|
[![Build Status](https://travis-ci.org/danielpclark/PolyBelongsTo.svg)](https://travis-ci.org/danielpclark/PolyBelongsTo)
|
5
5
|
|
6
|
-
|
7
|
-
meta-programatically friendly way.
|
6
|
+
A standard way to check belongs_to relations on any belongs_to Object and let you check your DB Objects polymorphism in a more across-the-board meta-programatically friendly way.
|
8
7
|
|
9
8
|
#Installation
|
10
9
|
|
11
|
-
### Gem
|
12
|
-
|
13
10
|
Just include it in your Gemfile and then run bundle:
|
14
11
|
```ruby
|
15
12
|
gem 'poly_belongs_to'
|
16
13
|
```
|
17
14
|
|
18
|
-
###Merge via git
|
19
|
-
Be in your Rails project directory. Make sure you git is up to date with all your latest changes. Then:
|
20
|
-
|
21
|
-
```shell
|
22
|
-
git fetch git@github.com:danielpclark/PolyBelongsTo.git install:pbt
|
23
|
-
git merge pbt
|
24
|
-
```
|
25
|
-
|
26
|
-
And then enter a description for this merge into your project. Save the message, exit, and you're done!
|
27
|
-
|
28
15
|
##Recommended Usage
|
29
16
|
|
30
17
|
#####On model class
|
@@ -47,7 +34,6 @@ MyObject.pbt_params_name
|
|
47
34
|
MyObject.pbt_params_name(false)
|
48
35
|
# => :my_object
|
49
36
|
User.pbt_params_name
|
50
|
-
User.pbt_params_name
|
51
37
|
# => :user
|
52
38
|
|
53
39
|
# Polymorphic DB field names
|
@@ -101,8 +87,92 @@ MyObject.new.pbt_type_sym # nil for non polymorphic Objects
|
|
101
87
|
# => :my_objectable_type
|
102
88
|
```
|
103
89
|
|
90
|
+
##Internal Methods Available
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
# For cleaning attributs for use with build
|
94
|
+
PolyBelongsTo::Pbt::AttrSanitizer[ obj ]
|
95
|
+
|
96
|
+
# Returns string of either 'child.build' or 'build_child'
|
97
|
+
PolyBelongsTo::Pbt::BuildCmd[ obj, child ]
|
98
|
+
|
99
|
+
# Returns has_one and has_many relationships for obj as an Array of symbols
|
100
|
+
PolyBelongsTo::Pbt::Reflects[ obj ]
|
101
|
+
|
102
|
+
# Returns Class Ojects for each has_one and has_many child associations
|
103
|
+
PolyBelongsTo::Pbt::ReflectsAsClasses[ obj ]
|
104
|
+
|
105
|
+
# Boolean of whether child object/class is a has(one/many) relationship to obj
|
106
|
+
PolyBelongsTo::Pbt::IsReflected[ obj, child ]
|
107
|
+
|
108
|
+
# Returns :singular if obj->child is has_one and :plural if obj->child is has_many
|
109
|
+
PolyBelongsTo::Pbt::SingularOrPlural[ obj, child ]
|
110
|
+
|
111
|
+
# Returns true if obj->child relationship is has_one
|
112
|
+
PolyBelongsTo::Pbt::IsSingular[ obj, child ]
|
113
|
+
|
114
|
+
# Returns true if obj->child relationship is has_many
|
115
|
+
PolyBelongsTo::Pbt::IsPlural[ obj, child ]
|
116
|
+
|
117
|
+
# Returns the simbol for the CollectionProxy the child belongs to in relation to obj
|
118
|
+
PolyBelongsTo::Pbt::CollectionProxy[ obj, child ]
|
119
|
+
|
120
|
+
```
|
121
|
+
##Record Duplication
|
122
|
+
|
123
|
+
**This gives you the advantage of duplicating records regardless of polymorphic associations or
|
124
|
+
otherwise**. You can duplicate a record, or use a self recursive command **pbt_deep_dup_build**
|
125
|
+
to duplicate a record and all of it's has_one/has_many children records at once. Afterwards
|
126
|
+
be sure to use the save method.
|
127
|
+
|
128
|
+
> NOTE: This will need to be included manually. The reason for this is because you need to
|
129
|
+
know what's involved when using this. It's purposefully done this way to lead to reading
|
130
|
+
the documentation for PolyBelongsTo's duplication methods.
|
131
|
+
|
132
|
+
####Known Issues
|
133
|
+
- Carrierwave records won't duplicate. To ensure other records to still save and prevent
|
134
|
+
rollback use .save(validate: false) ... I'm considering possible options to remedy this and
|
135
|
+
other scenarios.
|
136
|
+
- For deep duplication you need to be very aware of the potential for infinite loops with
|
137
|
+
your records if there are any circular references.
|
138
|
+
|
139
|
+
###How To Use
|
140
|
+
|
141
|
+
Include it into ActiveRecord::Base in an initializer /config/initializers/poly_belongs_to.rb
|
142
|
+
```ruby
|
143
|
+
ActiveRecord::Base.send(:include, PolyBelongsTo::Dup)
|
144
|
+
```
|
145
|
+
Then use the dup/build methods as follows
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
# If you were to create a new contact for example
|
149
|
+
contact = User.first.contacts.new
|
150
|
+
|
151
|
+
# This is just like contact.profile.build( { ... user's profile attributes ... } )
|
152
|
+
contact.pbt_dup_build( User.last.profile )
|
153
|
+
|
154
|
+
# Save it!
|
155
|
+
contact.save
|
156
|
+
|
157
|
+
|
158
|
+
# For a fully recursive copy do the same with pbt_deep_dup_build
|
159
|
+
# Keep in mind this is vulnerable to infinite loops!
|
160
|
+
contact.pbt_deep_dup_build( User.last.profile )
|
161
|
+
|
162
|
+
# Remeber to save!
|
163
|
+
contact.save
|
164
|
+
```
|
165
|
+
|
166
|
+
##Planning
|
167
|
+
|
168
|
+
I'm in the process of planning mapping out record hierarchy. Also with this
|
169
|
+
it will add recognition for circular references.
|
170
|
+
|
171
|
+
##Contributing
|
172
|
+
|
173
|
+
Feel free to fork and make pull requests. Please bring up an issue before a pull
|
174
|
+
request if it's a non-fix change. Thank you!
|
104
175
|
|
105
|
-
And that's that!
|
106
176
|
|
107
177
|
#License
|
108
178
|
|
data/lib/poly_belongs_to.rb
CHANGED
@@ -3,79 +3,84 @@
|
|
3
3
|
# Copyright (C) 2015 by Daniel P. Clark
|
4
4
|
$: << File.join(File.dirname(__FILE__), "/poly_belongs_to")
|
5
5
|
require 'poly_belongs_to/version'
|
6
|
+
require 'poly_belongs_to/dup'
|
7
|
+
require 'poly_belongs_to/hierarchy'
|
8
|
+
require 'poly_belongs_to/poly_belongs_to'
|
6
9
|
require 'active_support/concern'
|
7
10
|
|
8
11
|
module PolyBelongsTo
|
9
|
-
|
12
|
+
module Core
|
13
|
+
extend ActiveSupport::Concern
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
included do
|
16
|
+
def self.pbt
|
17
|
+
reflect_on_all_associations(:belongs_to).first.try(:name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.poly?
|
21
|
+
!!reflect_on_all_associations(:belongs_to).first.try {|i| i.options[:polymorphic] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.pbt_params_name(allow_as_nested = true)
|
25
|
+
if poly?
|
26
|
+
allow_as_nested ? "#{table_name}_attributes".to_sym : name.downcase.to_sym
|
27
|
+
else
|
28
|
+
name.downcase.to_sym
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.pbt_id_sym
|
33
|
+
val = pbt
|
34
|
+
val ? "#{val}_id".to_sym : nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.pbt_type_sym
|
38
|
+
poly? ? "#{pbt}_type".to_sym : nil
|
39
|
+
end
|
14
40
|
end
|
15
41
|
|
16
|
-
def
|
17
|
-
|
42
|
+
def pbt
|
43
|
+
self.class.pbt
|
18
44
|
end
|
19
45
|
|
20
|
-
def
|
21
|
-
|
22
|
-
allow_as_nested ? "#{table_name}_attributes".to_sym : name.downcase.to_sym
|
23
|
-
else
|
24
|
-
name.downcase.to_sym
|
25
|
-
end
|
46
|
+
def poly?
|
47
|
+
self.class.poly?
|
26
48
|
end
|
27
49
|
|
28
|
-
def
|
50
|
+
def pbt_id
|
29
51
|
val = pbt
|
30
|
-
val ? "
|
52
|
+
val ? eval("self.#{val}_id") : nil
|
31
53
|
end
|
32
54
|
|
33
|
-
def
|
34
|
-
poly? ? "
|
55
|
+
def pbt_type
|
56
|
+
poly? ? eval("self.#{pbt}_type") : nil
|
35
57
|
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def pbt
|
39
|
-
self.class.pbt
|
40
|
-
end
|
41
58
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
def pbt_type
|
52
|
-
poly? ? eval("self.#{pbt}_type") : nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def pbt_parent
|
56
|
-
val = pbt
|
57
|
-
if pbt
|
58
|
-
if poly?
|
59
|
-
eval "#{pbt_type}.find(#{pbt_id})"
|
59
|
+
def pbt_parent
|
60
|
+
val = pbt
|
61
|
+
if val
|
62
|
+
if poly?
|
63
|
+
eval "#{pbt_type}.find(#{pbt_id})"
|
64
|
+
else
|
65
|
+
eval "#{val.capitalize.to_s}.find(#{pbt_id})"
|
66
|
+
end
|
60
67
|
else
|
61
|
-
|
68
|
+
nil
|
62
69
|
end
|
63
|
-
else
|
64
|
-
nil
|
65
70
|
end
|
66
|
-
end
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
72
|
+
def pbt_id_sym
|
73
|
+
self.class.pbt_id_sym
|
74
|
+
end
|
71
75
|
|
72
|
-
|
73
|
-
|
74
|
-
|
76
|
+
def pbt_type_sym
|
77
|
+
self.class.pbt_type_sym
|
78
|
+
end
|
75
79
|
|
76
|
-
|
77
|
-
|
80
|
+
def pbt_params_name(allow_as_nested = true)
|
81
|
+
self.class.pbt_params_name(allow_as_nested)
|
82
|
+
end
|
78
83
|
end
|
79
84
|
end
|
80
85
|
|
81
|
-
ActiveRecord::Base.send(:include, PolyBelongsTo)
|
86
|
+
ActiveRecord::Base.send(:include, PolyBelongsTo::Core)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module PolyBelongsTo
|
2
|
+
module Dup
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
def self.pbt_dup_build(item_to_build_on, item_to_duplicate)
|
7
|
+
if PolyBelongsTo::Pbt::IsReflected[item_to_build_on, item_to_duplicate]
|
8
|
+
build_cmd = PolyBelongsTo::Pbt::BuildCmd[item_to_build_on, item_to_duplicate]
|
9
|
+
dup_attrs = PolyBelongsTo::Pbt::AttrSanitizer[item_to_duplicate]
|
10
|
+
|
11
|
+
build_cmd ? eval("item_to_build_on.#{build_cmd}(#{dup_attrs})") : nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.pbt_deep_dup_build(item_to_build_on, item_to_duplicate)
|
16
|
+
pbt_dup_build(item_to_build_on, item_to_duplicate)
|
17
|
+
PolyBelongsTo::Pbt::Reflects[item_to_duplicate].each do |ref|
|
18
|
+
child = eval("item_to_duplicate.#{ref}")
|
19
|
+
if child.respond_to?(:build)
|
20
|
+
child.each do |obj|
|
21
|
+
eval("item_to_build_on.#{PolyBelongsTo::Pbt::CollectionProxy[item_to_build_on, item_to_duplicate]}").
|
22
|
+
pbt_deep_dup_build(obj)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
eval("item_to_build_on.#{PolyBelongsTo::Pbt::CollectionProxy[item_to_build_on, item_to_duplicate]}").
|
26
|
+
pbt_deep_dup_build(child)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
item_to_build_on
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def pbt_dup_build(item_to_duplicate)
|
35
|
+
self.class.pbt_dup_build(self, item_to_duplicate)
|
36
|
+
end
|
37
|
+
|
38
|
+
def pbt_deep_dup_build(item_to_duplicate)
|
39
|
+
self.class.pbt_deep_dup_build(self, item_to_duplicate)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PolyBelongsTo
|
2
|
+
module Hierarchy
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
def self.pbt_offspring(obj)
|
7
|
+
# planning phase
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.pbt_children(obj, kind)
|
11
|
+
# planning phase
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def pbt_offspring
|
16
|
+
self.class.pbt_offspring(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def pbt_children(kind)
|
20
|
+
self.class.pbt_children(self, kind)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module PolyBelongsTo
|
2
|
+
module Pbt
|
3
|
+
AttrSanitizer = lambda {|obj|
|
4
|
+
return {} unless obj
|
5
|
+
obj.dup.attributes.delete_if {|ky,vl|
|
6
|
+
[:created_at, :updated_at, :deleted_at, obj.pbt_id_sym, obj.pbt_type_sym].include? ky.to_sym
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
BuildCmd = lambda {|obj, child|
|
11
|
+
dup_name = "#{CollectionProxy[obj,child]}"
|
12
|
+
IsSingular[obj, child] ? "build_#{dup_name}" : IsPlural[obj, child] ? "#{dup_name}.build" : nil
|
13
|
+
}
|
14
|
+
|
15
|
+
Reflects = lambda {|obj|
|
16
|
+
[:has_one, :has_many].map { |has|
|
17
|
+
eval(obj.class.name).reflect_on_all_associations(has).map(&:name).map(&:to_sym)
|
18
|
+
}.flatten
|
19
|
+
}
|
20
|
+
|
21
|
+
ReflectsAsClasses = lambda {|obj|
|
22
|
+
Reflects[obj].map {|ref|
|
23
|
+
eval (eval("obj.#{ref}").try(:klass) || eval("obj.#{ref}").class).name
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
IsReflected = lambda {|obj,child|
|
28
|
+
!!SingularOrPlural[obj, child]
|
29
|
+
}
|
30
|
+
|
31
|
+
SingularOrPlural = lambda {|obj, child|
|
32
|
+
reflects = Reflects[obj]
|
33
|
+
if reflects.include?(ActiveModel::Naming.singular(child).to_sym)
|
34
|
+
:singular
|
35
|
+
elsif reflects.include?(ActiveModel::Naming.plural(child).to_sym)
|
36
|
+
:plural
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
}
|
41
|
+
|
42
|
+
IsSingular = lambda {|obj, child|
|
43
|
+
eval(obj.class.name).reflect_on_all_associations(:has_one).
|
44
|
+
map(&:name).map(&:to_sym).include? ActiveModel::Naming.singular(child).to_sym
|
45
|
+
}
|
46
|
+
|
47
|
+
IsPlural = lambda {|obj,child|
|
48
|
+
eval(obj.class.name).reflect_on_all_associations(:has_many).
|
49
|
+
map(&:name).map(&:to_sym).include? ActiveModel::Naming.plural(child).to_sym
|
50
|
+
}
|
51
|
+
|
52
|
+
CollectionProxy = lambda {|obj, child|
|
53
|
+
reflects = Reflects[obj]
|
54
|
+
proxy = ActiveModel::Naming.singular(child).to_sym
|
55
|
+
if reflects.include? proxy
|
56
|
+
return proxy
|
57
|
+
end
|
58
|
+
proxy = ActiveModel::Naming.plural(child).to_sym
|
59
|
+
if reflects.include? proxy
|
60
|
+
proxy
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
data/test/dummy/Rakefile
CHANGED