acts_as_many_trees 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2e3aaaa29d8492c978bc86b0b61b9cb50eb29c14
4
+ data.tar.gz: 360dfdbedf00f63acb234ebb692104dee7940c83
5
+ SHA512:
6
+ metadata.gz: ebf6c139330b618a017e6254ea52f033e769fff0c49326081ccc3562ca38b6fa5f07a3fa15caedb2bbbcc6c4870140a256d1ccc53ba7db5cd2113bc1aa7ab874
7
+ data.tar.gz: 643a394d45595339c8e48e8a645f96a997773fc3d104293d366bd06c10ba4728ccd789d6a10bc391dab447e2e790407e708c3f44ce1fc51c1107b2a7e7475703
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = ActsAsManyTrees
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ActsAsManyTrees'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+ Bundler::GemHelper.install_tasks
17
+
18
+ begin
19
+ require 'rspec/core/rake_task'
20
+
21
+ RSpec::Core::RakeTask.new(:spec)
22
+
23
+ if defined?(RSpec)
24
+ desc 'Run factory specs.'
25
+ RSpec::Core::RakeTask.new(:factory_specs) do |t|
26
+ t.pattern = './spec/factories_spec.rb'
27
+ end
28
+ end
29
+
30
+ # task spec: :factory_specs
31
+ task :default => :spec
32
+ #http://erniemiller.org/
33
+ desc 'run the console'
34
+ task :console do
35
+ require 'irb'
36
+ require 'irb/completion'
37
+ require 'lib/acts_as_many_trees'
38
+ ARGV.clear
39
+ IRB.start
40
+ end
41
+ rescue LoadError
42
+ # no rspec available
43
+ end
44
+
45
+
@@ -0,0 +1,75 @@
1
+ module ActsAsManyTrees
2
+ module Base
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_many_trees(options = {})
10
+ class_attribute :hierarchy_class
11
+ self.hierarchy_class = (name+'Hierarchy').constantize
12
+ include ActsAsManyTrees::InstanceMethods
13
+ extend ActsAsManyTrees::ClassMethods
14
+ hierarchy_class.send :include,ActsAsManyTrees::HierarchyTable
15
+ end
16
+ end
17
+
18
+ end
19
+ module ClassMethods
20
+ def hierarchy_table_name
21
+ hierarchy_class.table_name
22
+ end
23
+ end
24
+ module InstanceMethods
25
+ extend ActiveSupport::Concern
26
+ included do
27
+ has_many :unscoped_descendant_links,
28
+ class_name:hierarchy_class.to_s,
29
+ foreign_key: 'ancestor_id',
30
+ dependent: :delete_all,
31
+ inverse_of: :unscoped_ancestor
32
+
33
+ has_many :unscoped_ancestor_links,
34
+ class_name: hierarchy_class.to_s,
35
+ foreign_key: 'descendant_id',
36
+ dependent: :delete_all,
37
+ inverse_of: :unscoped_descendant
38
+ has_many :unscoped_ancestors,through: :unscoped_ancestor_links
39
+ has_many :unscoped_descendants, through: :unscoped_descendant_links
40
+ scope :roots , ->(hierarchy=''){
41
+ on = Arel::Nodes::On.new(Arel::Nodes::Equality.new(arel_table[:id],hierarchy_class.arel_table[:descendant_id])
42
+ .and(hierarchy_class.arel_table[:hierarchy_scope].eq(hierarchy))
43
+ )
44
+ outer_join = Arel::Nodes::OuterJoin.new(hierarchy_class.arel_table,on)
45
+ joins(outer_join).merge(hierarchy_class.where(ancestor_id: nil))
46
+ }
47
+ end
48
+ delegate :hierarchy_class, to: :class
49
+ def parent=(new_parent,hierarchy_scope='')
50
+ hierarchy_class.set_parent_of(self,new_parent,hierarchy_scope)
51
+ end
52
+
53
+ def set_parent(new_parent,hierarchy_scope='')
54
+ hierarchy_class.set_parent_of(self,new_parent,hierarchy_scope)
55
+ end
56
+
57
+ def parent(hierarchy_scope='')
58
+ ancestors(hierarchy_scope).where('generation=1').first
59
+ end
60
+
61
+ def children(hierarchy_scope='')
62
+ descendants(hierarchy_scope).where('generation=1')
63
+ end
64
+
65
+ def ancestors(hierarchy='')
66
+ unscoped_ancestors.merge(hierarchy_class.scope_hierarchy(hierarchy))
67
+ end
68
+
69
+ def descendants(hierarchy='')
70
+ unscoped_descendants.merge(hierarchy_class.scope_hierarchy(hierarchy))
71
+ end
72
+ end
73
+
74
+ end
75
+ ActiveRecord::Base.send :include, ActsAsManyTrees::Base
@@ -0,0 +1,91 @@
1
+
2
+ module ActsAsManyTrees
3
+ module HierarchyTable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :item_class_name
8
+ self.item_class_name = self.to_s.gsub('Hierarchy','')
9
+ class_attribute :item_class
10
+ self.item_class = item_class_name.constantize
11
+
12
+ belongs_to :unscoped_ancestor,
13
+ class_name: item_class_name,
14
+ foreign_key: 'ancestor_id',
15
+ inverse_of: :unscoped_descendant_links
16
+
17
+ belongs_to :unscoped_descendant,
18
+ class_name: item_class_name,
19
+ foreign_key: 'descendant_id',
20
+ inverse_of: :unscoped_ancestor_links
21
+
22
+ scope :scope_hierarchy,->(scope_hierarchy=''){ where hierarchy_scope: scope_hierarchy}
23
+ # select t1.* from item_trees t1 left outer join item_trees t2 on t1.ancestor_id = t2.descendant_id and t1.tree_scope = t2.tree_scope where t2.ancestor_id is null
24
+ scope :roots,->do
25
+ t1 = arel_table
26
+ t2 = arel_table.alias
27
+ t1.project(Arel::star).join(t2,Arel::Nodes::OuterJoin)
28
+ .on(t1[:ancestor_id]
29
+ .eq(t2[:descendant_id])
30
+ .and(t1[:hierarchy_scope].eq(t2[:hierarchy_scope])
31
+ )
32
+ )
33
+ .where(t2[:ancestor_id].eq(nil)
34
+ )
35
+
36
+ end
37
+
38
+ def self.set_parent_of(item,new_parent,hierarchy_scope='')
39
+ self.delete_ancestors(item,hierarchy_scope)
40
+ self.fill_in_ancestors_for(new_parent,item,hierarchy_scope)
41
+ self.delete_ancestors_of_item_children(item,hierarchy_scope)
42
+ self.set_new_ancestors_of_item_children(item,hierarchy_scope)
43
+ end
44
+
45
+ private
46
+ def self.delete_ancestors(item,hierarchy_scope)
47
+ delete_all(descendant_id: item.id,hierarchy_scope: hierarchy_scope)
48
+ end
49
+
50
+ def self.delete_ancestors_of_item_children(item,hierarchy_scope)
51
+ sql = <<-SQL
52
+ delete from #{table_name} as p using #{table_name} as p1
53
+ where p.descendant_id = p1.descendant_id
54
+ and p1.ancestor_id = #{item.id}
55
+ and p.generation > p1.generation
56
+ and p.hierarchy_scope = p1.hierarchy_scope
57
+ and p1.hierarchy_scope = '#{hierarchy_scope}'
58
+ SQL
59
+ connection.execute(sql)
60
+ end
61
+
62
+ def self.set_new_ancestors_of_item_children(item,hierarchy_scope)
63
+ sql=<<-SQL
64
+ insert into #{table_name}(ancestor_id,descendant_id,generation,hierarchy_scope)
65
+ select it.ancestor_id,ct.descendant_id,it.generation+ct.generation,it.hierarchy_scope
66
+ from #{table_name} it
67
+ join #{table_name} ct
68
+ on ct.ancestor_id = it.descendant_id
69
+ and ct.hierarchy_scope = it.hierarchy_scope
70
+ where it.descendant_id=#{item.id}
71
+ and it.hierarchy_scope = '#{hierarchy_scope}'
72
+ SQL
73
+ connection.execute(sql)
74
+ end
75
+
76
+ def self.fill_in_ancestors_for(new_parent,item,hierarchy_scope)
77
+ if new_parent
78
+ create(ancestor_id: new_parent.id,descendant_id: item.id,generation: 1,hierarchy_scope: hierarchy_scope)
79
+ sql=<<-SQL
80
+ insert into #{table_name}(ancestor_id,descendant_id,generation,hierarchy_scope)
81
+ select it.ancestor_id,#{item.id},it.generation+1,it.hierarchy_scope
82
+ from #{table_name} it
83
+ where it.descendant_id=#{new_parent.id}
84
+ and it.hierarchy_scope = '#{hierarchy_scope}'
85
+ SQL
86
+ ActiveRecord::Base.connection.execute(sql)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ module ActsAsManyTrees
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_support'
2
+ require 'acts_as_many_trees/version'
3
+ require 'acts_as_many_trees/base'
4
+
5
+ module ActsAsManyTrees
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :Base
9
+ autoload :HierarchyTable
10
+ autoload :Version
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'rails/generators/named_base'
2
+ require 'rails/generators/active_record/migration'
3
+ require 'forwardable'
4
+
5
+ module ActsAsManyTrees
6
+ module Generators # :nodoc:
7
+ class MigrationGenerator < ::Rails::Generators::NamedBase # :nodoc:
8
+ include ActiveRecord::Generators::Migration
9
+
10
+ extend Forwardable
11
+ def_delegators :hierarchy_table_name
12
+
13
+ def self.default_generator_root
14
+ File.dirname(__FILE__)
15
+ end
16
+
17
+ def create_migration_file
18
+ migration_template 'create_hierarchies_table.rb.erb', "db/migrate/create_#{hierarchy_table_name}.rb"
19
+ end
20
+
21
+ def migration_class_name
22
+ "Create#{hierarchy_table_name.camelize}"
23
+ end
24
+
25
+ def hierarchy_table_name
26
+ (class_name+'Hierarchy').tableize
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= hierarchy_table_name %>, id: false do |t|
4
+ t.integer :ancestor_id, null: false
5
+ t.integer :descendant_id, null: false
6
+ t.integer :generation, null: false
7
+ t.string :hierarchy_scope,null: false
8
+ t.decimal :position
9
+ end
10
+
11
+ add_index :<%= hierarchy_table_name %>, [:ancestor_id, :descendant_id,:hierarchy_scope],
12
+ unique: true,
13
+ name:'<%="#{hierarchy_table_name}_anc_desc_scope_idx" %>'
14
+
15
+ add_index :<%= hierarchy_table_name -%>, [:descendant_id,:hierarchy_scope],
16
+ name: '<%="#{hierarchy_table_name}_desc_scope_idx" %>'
17
+
18
+ add_index :<%= hierarchy_table_name -%>, [:ancestor_id,:hierarchy_scope,:position],
19
+ name: '<%="#{hierarchy_table_name}_anc_scope_pos_idx" %>'
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :acts_as_many_trees do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_many_trees
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Small
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-09 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: 4.1.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.1.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: factory_girl_rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Uses the closure tree pattern with a scope field to maintain separate
70
+ hierarchies
71
+ email:
72
+ - jds340+rubygems@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - MIT-LICENSE
78
+ - README.rdoc
79
+ - Rakefile
80
+ - lib/acts_as_many_trees.rb
81
+ - lib/acts_as_many_trees/base.rb
82
+ - lib/acts_as_many_trees/hierarchy_table.rb
83
+ - lib/acts_as_many_trees/version.rb
84
+ - lib/generators/acts_as_many_trees/migration_generator.rb
85
+ - lib/generators/acts_as_many_trees/templates/create_hierarchies_table.rb.erb
86
+ - lib/tasks/acts_as_many_trees_tasks.rake
87
+ homepage: ''
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.2.2
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: ActiveRecord acts as tree, with many trees
111
+ test_files: []