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.
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