poly_belongs_to 0.1.3 → 0.1.4

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +86 -16
  3. data/lib/poly_belongs_to.rb +57 -52
  4. data/lib/poly_belongs_to/dup.rb +43 -0
  5. data/lib/poly_belongs_to/hierarchy.rb +23 -0
  6. data/lib/poly_belongs_to/poly_belongs_to.rb +66 -0
  7. data/lib/poly_belongs_to/version.rb +1 -1
  8. data/test/dummy/Rakefile +1 -1
  9. data/test/dummy/app/models/address.rb +3 -0
  10. data/test/dummy/app/models/contact.rb +5 -0
  11. data/test/dummy/app/models/photo.rb +3 -0
  12. data/test/dummy/app/models/profile.rb +7 -0
  13. data/test/dummy/app/models/ssn.rb +3 -0
  14. data/test/dummy/app/models/user.rb +3 -1
  15. data/test/dummy/config.ru +1 -1
  16. data/test/dummy/config/application.rb +11 -1
  17. data/test/dummy/config/boot.rb +1 -0
  18. data/test/dummy/config/environment.rb +3 -3
  19. data/test/dummy/config/environments/development.rb +1 -1
  20. data/test/dummy/config/environments/production.rb +1 -1
  21. data/test/dummy/config/environments/test.rb +1 -1
  22. data/test/dummy/config/initializers/assets.rb +3 -3
  23. data/test/dummy/config/initializers/cookies_serializer.rb +1 -1
  24. data/test/dummy/config/initializers/filter_parameter_logging.rb +1 -1
  25. data/test/dummy/config/initializers/session_store.rb +1 -1
  26. data/test/dummy/config/routes.rb +1 -1
  27. data/test/dummy/db/development.sqlite3 +0 -0
  28. data/test/dummy/db/migrate/20150211224139_create_users.rb +1 -0
  29. data/test/dummy/db/migrate/20150211224157_create_tags.rb +1 -0
  30. data/test/dummy/db/migrate/20150211224225_create_phones.rb +1 -0
  31. data/test/dummy/db/migrate/20150216092218_create_addresses.rb +10 -0
  32. data/test/dummy/db/migrate/20150216092338_create_profiles.rb +10 -0
  33. data/test/dummy/db/migrate/20150216092411_create_photos.rb +10 -0
  34. data/test/dummy/db/migrate/20150216092449_create_contacts.rb +11 -0
  35. data/test/dummy/db/migrate/20150216092519_create_ssns.rb +11 -0
  36. data/test/dummy/db/schema.rb +56 -5
  37. data/test/dummy/db/test.sqlite3 +0 -0
  38. data/test/dummy/log/development.log +50 -51
  39. data/test/dummy/log/test.log +49835 -1884
  40. data/test/dup_test.rb +39 -0
  41. data/test/fixtures/addresses.yml +18 -0
  42. data/test/fixtures/phones.yml +12 -3
  43. data/test/fixtures/photos.yml +16 -0
  44. data/test/fixtures/profiles.yml +15 -0
  45. data/test/fixtures/ssns.yml +14 -0
  46. data/test/fixtures/tags.yml +9 -2
  47. data/test/fixtures/users.yml +3 -1
  48. data/test/pbt_test.rb +61 -0
  49. data/test/poly_belongs_to_test.rb +75 -48
  50. data/test/test_helper.rb +13 -7
  51. metadata +58 -47
  52. data/test/dummy/app/assets/javascripts/application.js +0 -13
  53. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  54. data/test/dummy/test/models/phone_test.rb +0 -7
  55. data/test/dummy/test/models/tag_test.rb +0 -7
  56. 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: a7bb57e153d2b318ee6809a57f925e3adda3ecea
4
- data.tar.gz: 61110ab7c6b819e37f4c5b82b542f8edb6e123a6
3
+ metadata.gz: 295e7bd5fc23bc91323e6bf9d8650e1e4b6f59ab
4
+ data.tar.gz: 9cfbad7a6779856a5950b7410737ee524b8ef29c
5
5
  SHA512:
6
- metadata.gz: e2ce49d807729c6e49318be14ee20f106b3f9fd971c3586dcddc064c3d29ada5130ba1cf4e81db5924b396cc9c9273ec70372e95a922956e609e6fb40b3120b7
7
- data.tar.gz: d2cd49a7f5a62cbd6f8c2d4de20fcf8675f8a598312bae14d0b565557d8f4690a108b6de7270cefcf212412bc317598afc5828ace0fa71edb013e3db800b5a25
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
- Is an ActiveRecord library which will let you check your DB Objects polymorphism in a more across-the-board
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
 
@@ -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
- extend ActiveSupport::Concern
12
+ module Core
13
+ extend ActiveSupport::Concern
10
14
 
11
- included do
12
- def self.pbt
13
- reflect_on_all_associations(:belongs_to).first.try(:name)
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 self.poly?
17
- !!reflect_on_all_associations(:belongs_to).first.try {|i| i.options[:polymorphic] }
42
+ def pbt
43
+ self.class.pbt
18
44
  end
19
45
 
20
- def self.pbt_params_name(allow_as_nested = true)
21
- if poly?
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 self.pbt_id_sym
50
+ def pbt_id
29
51
  val = pbt
30
- val ? "#{val}_id".to_sym : nil
52
+ val ? eval("self.#{val}_id") : nil
31
53
  end
32
54
 
33
- def self.pbt_type_sym
34
- poly? ? "#{pbt}_type".to_sym : nil
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
- def poly?
43
- self.class.poly?
44
- end
45
-
46
- def pbt_id
47
- val = pbt
48
- val ? eval("self.#{val}_id") : nil
49
- end
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
- eval "#{val.capitalize.to_s}.find(#{pbt_id})"
68
+ nil
62
69
  end
63
- else
64
- nil
65
70
  end
66
- end
67
71
 
68
- def pbt_id_sym
69
- self.class.pbt_id_sym
70
- end
72
+ def pbt_id_sym
73
+ self.class.pbt_id_sym
74
+ end
71
75
 
72
- def pbt_type_sym
73
- self.class.pbt_type_sym
74
- end
76
+ def pbt_type_sym
77
+ self.class.pbt_type_sym
78
+ end
75
79
 
76
- def pbt_params_name(allow_as_nested = true)
77
- self.class.pbt_params_name(allow_as_nested)
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
@@ -1,3 +1,3 @@
1
1
  module PolyBelongsTo
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
data/test/dummy/Rakefile CHANGED
@@ -3,4 +3,4 @@
3
3
 
4
4
  require File.expand_path('../config/application', __FILE__)
5
5
 
6
- Rails.application.load_tasks
6
+ DummyApp.load_tasks
@@ -0,0 +1,3 @@
1
+ class Address < ActiveRecord::Base
2
+ belongs_to :addressable, polymorphic: true
3
+ end
@@ -0,0 +1,5 @@
1
+ class Contact < ActiveRecord::Base
2
+ belongs_to :user
3
+ has_one :profile, as: :profileable, dependent: :destroy
4
+ accepts_nested_attributes_for :profile
5
+ end
@@ -0,0 +1,3 @@
1
+ class Photo < ActiveRecord::Base
2
+ belongs_to :photoable, polymorphic: true
3
+ end