advanced_relationship_management 0.1.0
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 +7 -0
- data/lib/advanced_relationship_management/advanced_scoping.rb +25 -0
- data/lib/advanced_relationship_management/configuration.rb +16 -0
- data/lib/advanced_relationship_management/counts.rb +14 -0
- data/lib/advanced_relationship_management/cycle_detection.rb +16 -0
- data/lib/advanced_relationship_management/filter_scope.rb +12 -0
- data/lib/advanced_relationship_management/lazy_loading.rb +19 -0
- data/lib/advanced_relationship_management/path_to_root.rb +33 -0
- data/lib/advanced_relationship_management/railtie.rb +7 -0
- data/lib/advanced_relationship_management/recursive_relationships.rb +98 -0
- data/lib/advanced_relationship_management/relationship_visualizations.rb +11 -0
- data/lib/advanced_relationship_management/sibling_relationships.rb +12 -0
- data/lib/advanced_relationship_management/version.rb +5 -0
- data/lib/advanced_relationship_management.rb +88 -0
- data/lib/generators/advanced_relationship_management/install/install_generator.rb +23 -0
- data/lib/generators/advanced_relationship_management/install/templates/create_graph_edges_migration.rb +10 -0
- data/lib/tasks/advanced_relationship_management_tasks.rake +7 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8f221df65f0856c3c8b946e80ecda01dd2a32557497ea66d9e3bee45e9fe7b24
|
4
|
+
data.tar.gz: e32baec528fab08a83c1f84cec9b844e65fc76e7ee7bea63d5e5100f778edc7e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 513d6327b555cde3e42101a92ddf79e8fd0b7fa00edc3e190c042733b59ac6c2d9257c76b690b430fe2b39cd672a302fc4e8662bfb00041d34f87464823ebda3
|
7
|
+
data.tar.gz: c9a7f3608959234358bde548208b5541cb2d8f82aac5c8e5650131e0a6743473073a76cb22d89c61aa52f97ffd22b5e74802ead2995837e575a5d6b10fbb62bb
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module AdvancedScoping
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
scope :roots, -> { where(parent_id: nil) }
|
7
|
+
scope :by_depth, ->(depth) { where("depth <= ?", depth) }
|
8
|
+
|
9
|
+
def depth
|
10
|
+
if AdvancedRelationshipManagement.enable_caching
|
11
|
+
Rails.cache.fetch([self, "depth"]) { calculate_depth }
|
12
|
+
else
|
13
|
+
calculate_depth
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def calculate_depth
|
18
|
+
ancestors.size
|
19
|
+
end
|
20
|
+
|
21
|
+
scope :with_min_descendants, ->(min) { select { |record| record.descendants.size >= min } }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module Configuration
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
mattr_accessor :default_parent_column, :default_child_column, :enable_caching
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def configure
|
11
|
+
yield self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module CycleDetection
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
validate :no_cycles
|
7
|
+
end
|
8
|
+
|
9
|
+
def no_cycles
|
10
|
+
if ancestors.include?(self)
|
11
|
+
errors.add(:base, "Cycle detected in the hierarchy")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module LazyLoading
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
scope :lazy_load_children, -> { includes(:children) }
|
7
|
+
scope :lazy_load_parent, -> { includes(:parent) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def lazy_descendants
|
11
|
+
self.class.lazy_load_children.where("#{self.class.parent_column_name} = ?", self.id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def lazy_ancestors
|
15
|
+
self.class.lazy_load_parent.where("#{self.class.child_column_name} = ?", self.id)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module PathToRoot
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def path_to_root(format: :array, attribute: :id)
|
6
|
+
path = []
|
7
|
+
current_node = self
|
8
|
+
while current_node
|
9
|
+
path << current_node
|
10
|
+
current_node = current_node.parent
|
11
|
+
end
|
12
|
+
path.reverse!
|
13
|
+
|
14
|
+
case format
|
15
|
+
when :array
|
16
|
+
path
|
17
|
+
when :symbolic
|
18
|
+
path.map { |node| node.public_send(attribute) }.join(' -> ')
|
19
|
+
when :json
|
20
|
+
path.map { |node| { id: node.id, attribute => node.public_send(attribute) } }.to_json
|
21
|
+
when :html
|
22
|
+
path.map { |node| "<a href='/users/#{node.id}'>#{node.public_send(attribute)}</a>" }.join(' > ')
|
23
|
+
when :reverse_symbolic
|
24
|
+
path.map { |node| node.public_send(attribute) }.reverse.join(' -> ')
|
25
|
+
when :nested_hash
|
26
|
+
path.inject(nil) { |acc, node| { id: node.id, attribute => node.public_send(attribute), parent: acc } }
|
27
|
+
else
|
28
|
+
raise ArgumentError, "Unsupported format: #{format}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module RecursiveRelationships
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
after_initialize :setup_recursive_relationships
|
7
|
+
after_create :clear_cache
|
8
|
+
after_update :clear_cache
|
9
|
+
after_destroy :clear_cache
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup_recursive_relationships
|
13
|
+
return if self.class.reflect_on_association(:children) && self.class.reflect_on_association(:parent)
|
14
|
+
|
15
|
+
parent_column = self.class.parent_column_name
|
16
|
+
self.class.setup_relationships(parent_column)
|
17
|
+
end
|
18
|
+
|
19
|
+
def descendants(*scopes)
|
20
|
+
if AdvancedRelationshipManagement.enable_caching
|
21
|
+
Rails.cache.fetch([self, "descendants"] + scopes) { fetch_descendants(*scopes) }
|
22
|
+
else
|
23
|
+
fetch_descendants(*scopes)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_descendants(*scopes)
|
28
|
+
relation = self.class.find_by_sql([<<-SQL, id])
|
29
|
+
WITH RECURSIVE descendants_cte AS (
|
30
|
+
SELECT *
|
31
|
+
FROM #{self.class.table_name}
|
32
|
+
WHERE #{self.class.child_column_name} = ?
|
33
|
+
UNION ALL
|
34
|
+
SELECT #{self.class.table_name}.*
|
35
|
+
FROM #{self.class.table_name}
|
36
|
+
INNER JOIN descendants_cte ON descendants_cte.#{self.class.child_column_name} = #{self.class.table_name}.#{self.class.parent_column_name}
|
37
|
+
)
|
38
|
+
SELECT *
|
39
|
+
FROM descendants_cte
|
40
|
+
SQL
|
41
|
+
|
42
|
+
scopes.each do |scope|
|
43
|
+
relation = relation.public_send(scope) if scope.is_a?(Symbol)
|
44
|
+
relation = relation.merge(scope) if scope.is_a?(ActiveRecord::Relation)
|
45
|
+
end
|
46
|
+
|
47
|
+
relation
|
48
|
+
end
|
49
|
+
|
50
|
+
def ancestors
|
51
|
+
if AdvancedRelationshipManagement.enable_caching
|
52
|
+
Rails.cache.fetch([self, "ancestors"]) { fetch_ancestors }
|
53
|
+
else
|
54
|
+
fetch_ancestors
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def fetch_ancestors
|
59
|
+
all_ancestors = []
|
60
|
+
ActiveRecord::Base.silence do
|
61
|
+
current_node = self
|
62
|
+
while current_node.parent
|
63
|
+
all_ancestors << current_node.parent
|
64
|
+
current_node = current_node.parent
|
65
|
+
end
|
66
|
+
end
|
67
|
+
all_ancestors
|
68
|
+
end
|
69
|
+
|
70
|
+
def depth_of_descendants
|
71
|
+
descendants_with_depth = {}
|
72
|
+
descendants.each do |descendant|
|
73
|
+
descendants_with_depth[descendant] = depth(descendant)
|
74
|
+
end
|
75
|
+
descendants_with_depth
|
76
|
+
end
|
77
|
+
|
78
|
+
def depth_of_ancestors
|
79
|
+
ancestors_with_depth = {}
|
80
|
+
ancestors.each do |ancestor|
|
81
|
+
ancestors_with_depth[ancestor] = depth(ancestor)
|
82
|
+
end
|
83
|
+
ancestors_with_depth
|
84
|
+
end
|
85
|
+
|
86
|
+
def depth(node)
|
87
|
+
node.ancestors.size
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def clear_cache
|
93
|
+
Rails.cache.delete([self, "descendants"])
|
94
|
+
Rails.cache.delete([self, "ancestors"])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_record'
|
3
|
+
require_relative 'advanced_relationship_management/recursive_relationships'
|
4
|
+
require_relative 'advanced_relationship_management/relationship_visualizations'
|
5
|
+
require_relative 'advanced_relationship_management/advanced_scoping'
|
6
|
+
require_relative 'advanced_relationship_management/cycle_detection'
|
7
|
+
require_relative 'advanced_relationship_management/sibling_relationships'
|
8
|
+
require_relative 'advanced_relationship_management/counts'
|
9
|
+
require_relative 'advanced_relationship_management/path_to_root'
|
10
|
+
require_relative 'advanced_relationship_management/filter_scope'
|
11
|
+
require_relative 'advanced_relationship_management/lazy_loading'
|
12
|
+
|
13
|
+
module AdvancedRelationshipManagement
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
mattr_accessor :default_parent_column, :default_child_column, :enable_caching
|
17
|
+
|
18
|
+
self.default_parent_column = :parent_id
|
19
|
+
self.default_child_column = :id
|
20
|
+
self.enable_caching = true
|
21
|
+
|
22
|
+
included do
|
23
|
+
include AdvancedRelationshipManagement::RecursiveRelationships
|
24
|
+
include AdvancedRelationshipManagement::RelationshipVisualizations
|
25
|
+
include AdvancedRelationshipManagement::AdvancedScoping
|
26
|
+
include AdvancedRelationshipManagement::CycleDetection
|
27
|
+
include AdvancedRelationshipManagement::SiblingRelationships
|
28
|
+
include AdvancedRelationshipManagement::Counts
|
29
|
+
include AdvancedRelationshipManagement::PathToRoot
|
30
|
+
include AdvancedRelationshipManagement::FilterScope
|
31
|
+
include AdvancedRelationshipManagement::LazyLoading
|
32
|
+
end
|
33
|
+
|
34
|
+
class_methods do
|
35
|
+
def setup_relationships(parent_column = nil, child_column = nil)
|
36
|
+
parent_column ||= AdvancedRelationshipManagement.default_parent_column
|
37
|
+
child_column ||= AdvancedRelationshipManagement.default_child_column
|
38
|
+
|
39
|
+
has_many :children, class_name: name, foreign_key: parent_column, inverse_of: :parent
|
40
|
+
belongs_to :parent, class_name: name, foreign_key: parent_column, optional: true
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure_relationships(parent_column: :parent_id, child_column: :id, enable_caching: true)
|
44
|
+
@parent_column = parent_column
|
45
|
+
@child_column = child_column
|
46
|
+
AdvancedRelationshipManagement.enable_caching = enable_caching
|
47
|
+
setup_relationships(parent_column, child_column)
|
48
|
+
end
|
49
|
+
|
50
|
+
def parent_column(column_name)
|
51
|
+
@parent_column = column_name
|
52
|
+
setup_relationships(column_name, @child_column)
|
53
|
+
end
|
54
|
+
|
55
|
+
def child_column(column_name)
|
56
|
+
@child_column = column_name
|
57
|
+
setup_relationships(@parent_column, column_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def enable_caching?
|
61
|
+
AdvancedRelationshipManagement.enable_caching
|
62
|
+
end
|
63
|
+
|
64
|
+
def enable_caching(enable)
|
65
|
+
AdvancedRelationshipManagement.enable_caching = enable
|
66
|
+
end
|
67
|
+
|
68
|
+
def parent_column_name
|
69
|
+
@parent_column || AdvancedRelationshipManagement.default_parent_column
|
70
|
+
end
|
71
|
+
|
72
|
+
def child_column_name
|
73
|
+
@child_column || AdvancedRelationshipManagement.default_child_column
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module ActiveRecord
|
79
|
+
class Base
|
80
|
+
def self.silence
|
81
|
+
old_logger = ActiveRecord::Base.logger
|
82
|
+
ActiveRecord::Base.logger = nil
|
83
|
+
yield
|
84
|
+
ensure
|
85
|
+
ActiveRecord::Base.logger = old_logger
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module AdvancedRelationshipManagement
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
9
|
+
|
10
|
+
def copy_migration
|
11
|
+
migration_template "create_graph_edges_migration.rb", "db/migrate/create_graph_edges.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.next_migration_number(dirname)
|
15
|
+
if ActiveRecord::Base.timestamped_migrations
|
16
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
17
|
+
else
|
18
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: advanced_relationship_management
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jana
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-05-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rgl
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.5
|
41
|
+
description: A gem to manage complex relationships, including recursive and graph-based
|
42
|
+
relationships, for ActiveRecord models in Rails.
|
43
|
+
email:
|
44
|
+
- shanmugamjanarthan24@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- lib/advanced_relationship_management.rb
|
50
|
+
- lib/advanced_relationship_management/advanced_scoping.rb
|
51
|
+
- lib/advanced_relationship_management/configuration.rb
|
52
|
+
- lib/advanced_relationship_management/counts.rb
|
53
|
+
- lib/advanced_relationship_management/cycle_detection.rb
|
54
|
+
- lib/advanced_relationship_management/filter_scope.rb
|
55
|
+
- lib/advanced_relationship_management/lazy_loading.rb
|
56
|
+
- lib/advanced_relationship_management/path_to_root.rb
|
57
|
+
- lib/advanced_relationship_management/railtie.rb
|
58
|
+
- lib/advanced_relationship_management/recursive_relationships.rb
|
59
|
+
- lib/advanced_relationship_management/relationship_visualizations.rb
|
60
|
+
- lib/advanced_relationship_management/sibling_relationships.rb
|
61
|
+
- lib/advanced_relationship_management/version.rb
|
62
|
+
- lib/generators/advanced_relationship_management/install/install_generator.rb
|
63
|
+
- lib/generators/advanced_relationship_management/install/templates/create_graph_edges_migration.rb
|
64
|
+
- lib/tasks/advanced_relationship_management_tasks.rake
|
65
|
+
homepage: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata:
|
69
|
+
allowed_push_host: https://rubygems.org
|
70
|
+
homepage_uri: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
71
|
+
source_code_uri: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
72
|
+
changelog_uri: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.6.0
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubygems_version: 3.4.20
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Advanced Relationship Management for ActiveRecord Models
|
92
|
+
test_files: []
|