poly_belongs_to 0.2.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56d5f029c2d24b2474440dc487ca11d9da0592bd
4
- data.tar.gz: 626db50369225abcd48137ece11615554ea8ad77
3
+ metadata.gz: ab96e43c85c346057343a22a9e06b8cf532eef4c
4
+ data.tar.gz: af27f3c4ec9facc1577fdace270ebd3bbbb9f9ff
5
5
  SHA512:
6
- metadata.gz: 5344d97c85fb343ef385e647f1c2c043ea881e9a727d63774ca46de9e90ebccf1fcff5c24084ff8c9d058938f0587b57da8e76c4f47da854908fa4abd5709e03
7
- data.tar.gz: 7d0c57c277948b7d336ac2e882d2f1fee2e9330f653a9370265f2b1d7ca3158b62c850a2371c21ce6c95dd6aa94086339e8a579204c89b5ca1b4bd2c374c4182
6
+ metadata.gz: 4b3fb493356381a13b006f927f2c13c28bde9ae004c0bdff342f7ad5cd224a080e76776d21cb28317e707ca419afba8cf41e3aee66c2357cda753eb4b58746fc
7
+ data.tar.gz: 778008ce2888fc121386f93bb71242d33f92048164451dda3ee5db436894235334bfab5c15dcd09c3ea2de15bf2e133199ac8387e272bb133fa318b3e3418bb2
data/README.md CHANGED
@@ -3,6 +3,7 @@
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
  [![Test Coverage](https://codeclimate.com/github/danielpclark/PolyBelongsTo/badges/coverage.svg)](https://codeclimate.com/github/danielpclark/PolyBelongsTo)
6
+ [![Inline docs](http://inch-ci.org/github/danielpclark/PolyBelongsTo.svg?branch=master)](http://inch-ci.org/github/danielpclark/PolyBelongsTo)
6
7
 
7
8
  **Original Purpose:** 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
9
 
@@ -1,6 +1,3 @@
1
- # Poly Belongs To
2
- # The MIT License (MIT)
3
- # Copyright (C) 2015 by Daniel P. Clark
4
1
  $: << File.join(File.dirname(__FILE__), "/poly_belongs_to")
5
2
  require 'active_support/concern'
6
3
  require 'poly_belongs_to/version'
@@ -12,3 +9,19 @@ require 'poly_belongs_to/faked_collection'
12
9
  ActiveRecord::Base.send(:include, PolyBelongsTo::Core )
13
10
  ActiveRecord::Base.send(:include, PolyBelongsTo::Dup )
14
11
 
12
+
13
+
14
+ ##
15
+ #
16
+ # PolyBelongsTo
17
+ # @author Daniel P. Clark
18
+ #
19
+ # Creates uniform helper methods on all ActiveModel & ActiveRecord instances
20
+ # to allow universal access and control over (most) any kind of database
21
+ # relationship.
22
+ #
23
+ # PolyBelongsTo::Core and PolyBelongsTo::Dup methods are included on each instance.
24
+ # @see PolyBelongsTo::Core
25
+ # @see PolyBelongsTo::Dup
26
+ #
27
+ module PolyBelongsTo end
@@ -1,20 +1,29 @@
1
1
  module PolyBelongsTo
2
+ ##
3
+ # PolyBelongsTo::Core are the core set of methods included on all ActiveModel & ActiveRecord instances.
2
4
  module Core
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  included do
8
+ # @return [Symbol] first belongs_to relation
6
9
  def self.pbt
7
10
  reflect_on_all_associations(:belongs_to).first.try(:name)
8
11
  end
9
12
 
13
+ # @return [Array<Symbol>] belongs_to relations
10
14
  def self.pbts
11
15
  reflect_on_all_associations(:belongs_to).map(&:name)
12
16
  end
13
17
 
18
+ # Boolean reponse of current class being polymorphic
19
+ # @return [true, false]
14
20
  def self.poly?
15
21
  !!reflect_on_all_associations(:belongs_to).first.try {|i| i.options[:polymorphic] }
16
22
  end
17
23
 
24
+ # Symbol for html form params
25
+ # @param allow_as_nested [true, false] Allow parameter name to be nested attribute symbol
26
+ # @return [Symbol] The symbol for the form in the view
18
27
  def self.pbt_params_name(allow_as_nested = true)
19
28
  if poly? && allow_as_nested
20
29
  "#{table_name}_attributes".to_sym
@@ -23,37 +32,51 @@ module PolyBelongsTo
23
32
  end
24
33
  end
25
34
 
35
+ # The symbol as an id field for the first belongs_to relation
36
+ # @return [Symbol, nil] :`belongs_to_object`_id or nil
26
37
  def self.pbt_id_sym
27
38
  val = pbt
28
39
  val ? "#{val}_id".to_sym : nil
29
40
  end
30
41
 
42
+ # The symbol as an sym field for the polymorphic belongs_to relation, or nil
43
+ # @return [Symbol, nil] :`belongs_to_object`_sym or nil
31
44
  def self.pbt_type_sym
32
45
  poly? ? "#{pbt}_type".to_sym : nil
33
46
  end
34
47
  end
35
48
 
49
+ # @return [Symbol] first belongs_to relation
36
50
  def pbt
37
51
  self.class.pbt
38
52
  end
39
53
 
54
+ # @return [Array<Symbol>] belongs_to relations
40
55
  def pbts
41
56
  self.class.pbts
42
57
  end
43
58
 
59
+ # Boolean reponse of current class being polymorphic
60
+ # @return [true, false]
44
61
  def poly?
45
62
  self.class.poly?
46
63
  end
47
64
 
65
+ # Value of parent id. nil if no parent
66
+ # @return [Integer, nil]
48
67
  def pbt_id
49
68
  val = pbt
50
69
  val ? self.send("#{val}_id") : nil
51
70
  end
52
71
 
72
+ # Value of polymorphic relation type. nil if not polymorphic.
73
+ # @return [String, nil]
53
74
  def pbt_type
54
75
  poly? ? self.send("#{pbt}_type") : nil
55
76
  end
56
77
 
78
+ # Get the parent relation. Polymorphic relations are prioritized first.
79
+ # @return [Object] ActiveRecord object instasnce
57
80
  def pbt_parent
58
81
  val = pbt
59
82
  if val && !pbt_id.nil?
@@ -67,6 +90,9 @@ module PolyBelongsTo
67
90
  end
68
91
  end
69
92
 
93
+ # Climb up each parent object in the hierarchy until the top is reached.
94
+ # This has a no-repeat safety built in. Polymorphic parents have priority.
95
+ # @return [Object] top parent ActiveRecord object instace
70
96
  def pbt_top_parent
71
97
  record = self
72
98
  return nil unless record.pbt_parent
@@ -78,6 +104,8 @@ module PolyBelongsTo
78
104
  record
79
105
  end
80
106
 
107
+ # All belongs_to parents as class objects. One if polymorphic.
108
+ # @return [Array<Object>] ActiveRecord classes of parent objects.
81
109
  def pbt_parents
82
110
  if poly?
83
111
  Array[pbt_parent].compact
@@ -88,14 +116,21 @@ module PolyBelongsTo
88
116
  end
89
117
  end
90
118
 
119
+ # The symbol as an id field for the first belongs_to relation
120
+ # @return [Symbol, nil] :`belongs_to_object`_id or nil
91
121
  def pbt_id_sym
92
122
  self.class.pbt_id_sym
93
123
  end
94
124
 
125
+ # The symbol as an sym field for the polymorphic belongs_to relation, or nil
126
+ # @return [Symbol, nil] :`belongs_to_object`_sym or nil
95
127
  def pbt_type_sym
96
128
  self.class.pbt_type_sym
97
129
  end
98
130
 
131
+ # Symbol for html form params
132
+ # @param allow_as_nested [true, false] Allow parameter name to be nested attribute symbol
133
+ # @return [Symbol] The symbol for the form in the view
99
134
  def pbt_params_name(allow_as_nested = true)
100
135
  self.class.pbt_params_name(allow_as_nested)
101
136
  end
@@ -1,8 +1,13 @@
1
1
  module PolyBelongsTo
2
+ ##
3
+ # PolyBelongsTo::Dup contains duplication methods which is included on all ActiveModel & ActiveRecord instances
2
4
  module Dup
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  included do
8
+ # Build child object by dup'ing its attributes
9
+ # @param item_to_build_on [Object] Object on which to build on
10
+ # @param item_to_duplicate [Object] Object to duplicate as new child
6
11
  def self.pbt_dup_build(item_to_build_on, item_to_duplicate)
7
12
  singleton_record = yield if block_given?
8
13
  if PolyBelongsTo::Pbt::IsReflected[item_to_build_on, item_to_duplicate]
@@ -12,6 +17,9 @@ module PolyBelongsTo
12
17
  end
13
18
  end
14
19
 
20
+ # Build child object by dup'ing its attributes. Recursive for ancestry tree.
21
+ # @param item_to_build_on [Object] Object on which to build on
22
+ # @param item_to_duplicate [Object] Object to duplicate as new child along with child ancestors
15
23
  def self.pbt_deep_dup_build(item_to_build_on, item_to_duplicate)
16
24
  singleton_record = (block_given? ? yield : PolyBelongsTo::SingletonSet.new)
17
25
  pbt_dup_build(item_to_build_on, item_to_duplicate) {singleton_record}
@@ -29,10 +37,14 @@ module PolyBelongsTo
29
37
 
30
38
  end
31
39
 
40
+ # Build child object by dup'ing its attributes
41
+ # @param item_to_duplicate [Object] Object to duplicate as new child
32
42
  def pbt_dup_build(item_to_duplicate, &block)
33
43
  self.class.pbt_dup_build(self, item_to_duplicate, &block)
34
44
  end
35
45
 
46
+ # Build child object by dup'ing its attributes. Recursive for ancestry tree.
47
+ # @param item_to_duplicate [Object] Object to duplicate as new child along with child ancestors
36
48
  def pbt_deep_dup_build(item_to_duplicate, &block)
37
49
  self.class.pbt_deep_dup_build(self, item_to_duplicate, &block)
38
50
  end
@@ -1,5 +1,8 @@
1
1
  module PolyBelongsTo
2
+ ##
3
+ # PolyBelongsTo::FakedCollection emulates an ActiveRecord::CollectionProxy of exactly one or zero items
2
4
  class FakedCollection
5
+ # Create the Faked Colllection Proxy
3
6
  def initialize(obj = nil, child = nil)
4
7
  @obj = obj
5
8
  @child = child
@@ -12,75 +15,109 @@ module PolyBelongsTo
12
15
  self
13
16
  end
14
17
 
18
+ # @return [Integer, nil]
15
19
  def id
16
20
  @instance.try(:id)
17
21
  end
18
22
 
23
+ # @return [Array]
19
24
  def all
20
25
  Array[@instance].compact
21
26
  end
22
27
 
28
+ # Boolean of empty?
29
+ # @return [true, false]
23
30
  def empty?
24
31
  all.empty?
25
32
  end
26
33
 
34
+ # Boolean of not empty?
35
+ # @return [true, false]
27
36
  def present?
28
37
  !empty?
29
38
  end
30
39
 
40
+ # @return [self, nil]
31
41
  def presence
32
42
  all.first ? self : nil
33
43
  end
34
44
 
45
+ # Boolean of validity
46
+ # @return [true, false]
35
47
  def valid?
36
48
  !!@instance.try(&:valid?)
37
49
  end
38
50
 
51
+ # @return [Object, nil]
39
52
  def first
40
53
  @instance
41
54
  end
42
55
 
56
+ # @return [Object, nil]
43
57
  def last
44
58
  @instance
45
59
  end
46
60
 
61
+ # @return [Integer] 1 or 0
47
62
  def size
48
63
  all.size
49
64
  end
50
65
 
66
+ # @return [Array<Object>]
51
67
  def ancestors
52
68
  klass.ancestors.unshift(PolyBelongsTo::FakedCollection)
53
69
  end
54
-
70
+
71
+ # Boolean of kind match
72
+ # @param thing [Object] object class
73
+ # @return [true, false]
55
74
  def kind_of?(thing)
56
75
  ancestors.include? thing
57
76
  end
58
77
 
78
+ # Boolean of kind match
79
+ # @param thing [Object] object class
80
+ # @return [true, false]
59
81
  def is_a?(thing)
60
82
  kind_of?(thing)
61
83
  end
62
84
 
85
+ # alias for size
86
+ # @return [Integer] 1 or 0
63
87
  def count
64
88
  size
65
89
  end
66
90
 
91
+ # @return [Object] object class
67
92
  def klass
68
93
  @instance.class
69
94
  end
70
95
 
96
+ # build preforms calculated build command and returns self
97
+ # @param args [Array<Symbol>] arguments
98
+ # @return [self]
71
99
  def build(*args)
72
100
  @instance = @obj.send(PolyBelongsTo::Pbt::BuildCmd[@obj, @child], *args)
73
101
  self
74
102
  end
75
103
 
104
+ # @param args [Array<Symbol>] arguments
105
+ # @return [Enumerator]
76
106
  def each(*args, &block)
77
107
  all.each(*args, &block)
78
108
  end
79
109
 
110
+ # Returns whether this or super responds to method
111
+ # @param method_name [Symbol]
112
+ # @return [true, false]
80
113
  def respond_to?(method_name)
81
114
  @instance.respond_to?(method_name) || super
82
115
  end
83
116
 
117
+ # Hands method and argument on to instance if it responds to method, otherwise calls super
118
+ # @param method_name [Symbol]
119
+ # @param args [Array<Symbol>] arguments
120
+ # @return [true, false]
84
121
  def method_missing(method_name, *args, &block)
85
122
  if @instance.respond_to?(method_name)
86
123
  @instance.send method_name, *args, &block
@@ -1,5 +1,11 @@
1
1
  module PolyBelongsTo
2
+ ##
3
+ # PolyBelongsTo::Pbt is where internal methods are for implementing core and duplication methods; available on all instances.
4
+ # They are available for other use if the need should arise.
2
5
  module Pbt
6
+ # Strips date and id fields dup'd attributes
7
+ # @param obj [Object] ActiveRecord instance that has attributes
8
+ # @return [Hash] attributes
3
9
  AttrSanitizer = lambda {|obj|
4
10
  return {} unless obj
5
11
  obj.dup.attributes.delete_if {|ky,vl|
@@ -7,12 +13,19 @@ module PolyBelongsTo
7
13
  }
8
14
  }
9
15
 
16
+ # Discerns which type of build command is used between obj and child
17
+ # @param obj [Object] ActiveRecord instance to build on
18
+ # @param child [Object] ActiveRecord child relation as class or instance
19
+ # @return [String, nil] build command for relation, or nil
10
20
  BuildCmd = lambda {|obj, child|
11
21
  return nil unless obj && child
12
22
  dup_name = CollectionProxy[obj, child]
13
23
  IsSingular[obj, child] ? "build_#{dup_name}" : IsPlural[obj, child] ? "#{dup_name}.build" : nil
14
24
  }
15
25
 
26
+ # Returns all has_one and has_many relationships as symbols (reflec_on_all_associations)
27
+ # @param obj [Object] ActiveRecord instance to reflect on
28
+ # @return [Array<Symbol>]
16
29
  Reflects = lambda {|obj|
17
30
  return [] unless obj
18
31
  [:has_one, :has_many].map { |has|
@@ -20,16 +33,27 @@ module PolyBelongsTo
20
33
  }.flatten
21
34
  }
22
35
 
36
+ # Returns all has_one and has_many relationships as class objects (reflec_on_all_associations)
37
+ # @param obj [Object] ActiveRecord instance to reflect on
38
+ # @return [Array<Object>]
23
39
  ReflectsAsClasses = lambda {|obj|
24
40
  Reflects[obj].map {|ref|
25
41
  (obj.send(ref).try(:klass) || obj.send(ref).class).name.constantize
26
42
  }
27
43
  }
28
44
 
45
+ # Check if the child object is a kind of child that belongs to obj
46
+ # @param obj [Object] ActiveRecord instance
47
+ # @param child [Object] ActiveRecord instance or model class
48
+ # @return [true, false]
29
49
  IsReflected = lambda {|obj, child|
30
50
  !!CollectionProxy[obj, child]
31
51
  }
32
52
 
53
+ # Plurality of object relationship (plural for has_many)
54
+ # @param obj [Object] ActiveRecord instance
55
+ # @param child [Object] ActiveRecord instance or model class
56
+ # @return [Symbol, nil] :plural, :singular, or nil
33
57
  SingularOrPlural = lambda {|obj, child|
34
58
  return nil unless obj && child
35
59
  reflects = Reflects[obj]
@@ -42,24 +66,44 @@ module PolyBelongsTo
42
66
  end
43
67
  }
44
68
 
69
+ # Singularity of object relationship (for has_one)
70
+ # @param obj [Object] ActiveRecord instance
71
+ # @param child [Object] ActiveRecord instance or model class
72
+ # @return [true, false]
45
73
  IsSingular = lambda {|obj, child|
46
74
  SingularOrPlural[obj, child] == :singular
47
75
  }
48
76
 
77
+ # Plurality of object relationship (for has_many)
78
+ # @param obj [Object] ActiveRecord instance
79
+ # @param child [Object] ActiveRecord instance or model class
80
+ # @return [true, false]
49
81
  IsPlural = lambda {|obj,child|
50
82
  SingularOrPlural[obj, child] == :plural
51
83
  }
52
84
 
85
+ # Returns the symbol of the child relation object
86
+ # @param obj [Object] ActiveRecord instance
87
+ # @param child [Object] ActiveRecord instance or model class
88
+ # @return [Symbol] working relation on obj
53
89
  CollectionProxy = lambda {|obj, child|
54
90
  return nil unless obj && child
55
91
  names = [ActiveModel::Naming.singular(child).to_s, ActiveModel::Naming.plural(child).to_s].uniq
56
92
  Reflects[obj].detect {|ref| "#{ref}"[/(?:#{ names.join('|') }).{,3}/]}
57
93
  }
58
94
 
95
+ # Returns either a ActiveRecord::CollectionProxy or a PolyBelongsTo::FakedCollection as a proxy
96
+ # @param obj [Object] ActiveRecord instance
97
+ # @param child [Object] ActiveRecord instance or model class
98
+ # @return [Object]
59
99
  AsCollectionProxy = lambda {|obj, child|
60
100
  return PolyBelongsTo::FakedCollection.new() unless obj && child
61
101
  return PolyBelongsTo::FakedCollection.new(obj, child) if IsSingular[obj, child]
62
- !!CollectionProxy[obj, child] ? obj.send(PolyBelongsTo::Pbt::CollectionProxy[obj, child]) : []
102
+ if CollectionProxy[obj, child]
103
+ obj.send(PolyBelongsTo::Pbt::CollectionProxy[obj, child])
104
+ else
105
+ PolyBelongsTo::FakedCollection.new()
106
+ end
63
107
  }
64
108
  end
65
109
 
@@ -1,16 +1,26 @@
1
1
  require 'set'
2
2
  module PolyBelongsTo
3
+ ##
4
+ # PolyBelongsTo::SingletonSet is a modified instance of Set for maintaining singletons of ActiveRecord objects during any recursive flow.
3
5
  class SingletonSet
6
+ # Creates two internal Sets
7
+ # @return [self]
4
8
  def initialize
5
9
  @set = Set.new
6
10
  @flagged = Set.new
7
11
  self
8
12
  end
9
13
 
14
+ # Takes ActiveRecord object and returns string of class name and object id
15
+ # @param record [Object] ActiveRecord object instance
16
+ # @return [String]
10
17
  def formatted_name(record)
11
18
  "#{record.class.name}-#{record.id}"
12
19
  end
13
20
 
21
+ # Add record to set. Flag if covered already.
22
+ # @param record [Object] ActiveRecord object instance
23
+ # @return [Object, nil] Object if added safely, nil otherwise
14
24
  def add?(record)
15
25
  result = @set.add?( formatted_name( record ) )
16
26
  return result if result
@@ -18,20 +28,31 @@ module PolyBelongsTo
18
28
  result
19
29
  end
20
30
 
31
+ # Boolean of record inclusion
32
+ # @param record [Object] ActiveRecord object instance
33
+ # @return [true, false]
21
34
  def include?(record)
22
35
  return nil unless record
23
36
  @set.include?(formatted_name(record))
24
37
  end
25
38
 
39
+ # Add record to flagged Set list
40
+ # @param record [Object] ActiveRecord object instance
41
+ # @return [Set]
26
42
  def flag(record)
27
43
  @flagged << formatted_name(record)
28
44
  end
29
45
 
46
+ # Boolean of record inclusion in flagged Set
47
+ # @param record [Object] ActiveRecord object instance
48
+ # @return [true, false]
30
49
  def flagged?(record)
31
50
  return nil unless record
32
51
  @flagged.include?(formatted_name(record))
33
52
  end
34
53
 
54
+ # method_missing will transform any record argument into a formatted string and pass the
55
+ # method and arguments on to the internal Set. Also will flag any existing records covered.
35
56
  def method_missing(mthd, *args, &block)
36
57
  new_recs = args.reduce([]) {|a, i| a.push(formatted_name(i)) if i.class.ancestors.include?(ActiveRecord::Base); a}
37
58
  result = @set.send(mthd,